X-Git-Url: https://review.fuel-infra.org/gitweb?a=blobdiff_plain;f=spec%2Funit%2Fapplication_spec.rb;fp=spec%2Funit%2Fapplication_spec.rb;h=9b1c434b5930c7fe2527a722f20b261caebe4135;hb=b87d2f4e68281062df1913440ca5753ae63314a9;hp=0000000000000000000000000000000000000000;hpb=ab0ea530b8ac956091f17b104ab2311336cfc250;p=packages%2Fprecise%2Fmcollective.git diff --git a/spec/unit/application_spec.rb b/spec/unit/application_spec.rb new file mode 100755 index 0000000..9b1c434 --- /dev/null +++ b/spec/unit/application_spec.rb @@ -0,0 +1,653 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +module MCollective + describe Application do + before do + Application.intialize_application_options + @argv_backup = ARGV.clone + end + + describe "#application_options" do + it "should return the application options" do + Application.application_options.should == {:description => nil, + :usage => [], + :cli_arguments => [], + :exclude_arg_sections => []} + end + end + + describe "#[]=" do + it "should set the application option" do + Application["foo"] = "bar" + Application.application_options["foo"].should == "bar" + end + end + + describe "#[]" do + it "should set the application option" do + Application[:cli_arguments].should == [] + end + end + + describe "#intialize_application_options" do + it "should initialize application options correctly" do + Application.intialize_application_options.should == {:description => nil, + :usage => [], + :cli_arguments => [], + :exclude_arg_sections => []} + end + end + + describe "#description" do + it "should set the description correctly" do + Application.description "meh" + Application[:description].should == "meh" + end + end + + describe "#usage" do + it "should set the usage correctly" do + Application.usage "meh" + Application.usage "foo" + + Application[:usage].should == ["meh", "foo"] + end + end + + describe "#exclude_argument_sections" do + it "should set the excluded sections correctly" do + Application.exclude_argument_sections "common", "rpc", "filter" + Application[:exclude_arg_sections].should == ["common", "rpc", "filter"] + Application.exclude_argument_sections ["common", "rpc", "filter"] + Application[:exclude_arg_sections].should == ["common", "rpc", "filter"] + end + + it "should detect unknown sections" do + expect { Application.exclude_argument_sections "rspec" }.to raise_error("Unknown CLI argument section rspec") + end + end + + describe "#option" do + it "should add an option correctly" do + Application.option :test, + :description => "description", + :arguments => "--config CONFIG", + :type => Integer, + :required => true + + args = Application[:cli_arguments].first + args.delete(:validate) + + args.should == {:name=>:test, + :arguments=>"--config CONFIG", + :required=>true, + :type=>Integer, + :description=>"description"} + end + + it "should set correct defaults" do + Application.option :test, {} + + args = Application[:cli_arguments].first + args.delete(:validate) + + args.should == {:name=>:test, + :arguments=>[], + :required=>false, + :type=>String, + :description=>nil} + end + end + + describe "#validate_option" do + it "should pass validations" do + a = Application.new + a.validate_option(Proc.new {|v| v == 1}, "key", 1) + end + + it "should print an error to STDERR on error" do + IO.any_instance.expects(:puts).with("Validation of key failed: failed").at_least_once + Application.any_instance.stubs("exit").returns(true) + + a = Application.new + a.validate_option(Proc.new {|v| "failed"}, "key", 1) + end + + it "should exit on valdation error" do + IO.any_instance.expects(:puts).at_least_once + Application.any_instance.stubs("exit").returns(true) + + a = Application.new + a.validate_option(Proc.new {|v| "failed"}, "key", 1) + end + end + + describe "#application_parse_options" do + it "should pass the requested help value to the clioptions method" do + ARGV.clear + + app = Application.new + app.expects(:clioptions).with(true) + app.application_parse_options(true) + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should support creating arrays of values" do + Application.any_instance.stubs("main").returns(true) + + Application.option :foo, + :description => "meh", + :arguments => "--foo [FOO]", + :type => :array + + ARGV.clear + ARGV << "--foo=bar" << "--foo=baz" + + a = Application.new + a.run + a.configuration.should == {:foo=>["bar", "baz"]} + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should support boolean options" do + Application.any_instance.stubs("main").returns(true) + + Application.option :foo, + :description => "meh", + :arguments => "--foo", + :type => :boolean + + ARGV.clear + ARGV << "--foo" + + a = Application.new + a.run + a.configuration.should == {:foo=>true} + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should support unsetting boolean options" do + Application.any_instance.stubs("main").returns(true) + + Application.option :foo, + :description => "meh", + :arguments => "--[no-]foo", + :type => :boolean + + ARGV.clear + ARGV << "--no-foo" + + a = Application.new + a.run + a.configuration.should == {:foo=>false} + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should set the application description as head" do + OptionParser.any_instance.stubs(:define_head).with("meh") + + ARGV.clear + + Application.description "meh" + Application.new.application_parse_options + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should set the application usage as a banner" do + OptionParser.any_instance.stubs(:banner).with("meh") + + ARGV.clear + + Application.usage "meh" + Application.new.application_parse_options + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should support validation" do + IO.any_instance.expects(:puts).with("Validation of foo failed: failed").at_least_once + Application.any_instance.stubs("exit").returns(true) + Application.any_instance.stubs("main").returns(true) + + Application.option :foo, + :description => "meh", + :required => true, + :default => "meh", + :arguments => "--foo [FOO]", + :validate => Proc.new {|v| "failed"} + + ARGV.clear + ARGV << "--foo=bar" + + a = Application.new + a.run + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should support default values" do + Application.any_instance.stubs("main").returns(true) + + Application.option :foo, + :description => "meh", + :required => true, + :default => "meh", + :arguments => "--foo [FOO]" + + a = Application.new + a.run + a.configuration.should == {:foo => "meh"} + end + + it "should enforce required options" do + Application.any_instance.stubs("exit").returns(true) + Application.any_instance.stubs("main").returns(true) + OptionParser.any_instance.stubs("parse!").returns(true) + IO.any_instance.expects(:puts).with(anything).at_least_once + IO.any_instance.expects(:puts).with("The foo option is mandatory").at_least_once + + ARGV.clear + ARGV << "--foo=bar" + + Application.option :foo, + :description => "meh", + :required => true, + :arguments => "--foo [FOO]" + + Application.new.run + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should call post_option_parser" do + OptionParser.any_instance.stubs("parse!").returns(true) + Application.any_instance.stubs("post_option_parser").returns(true).at_least_once + Application.any_instance.stubs("main").returns(true) + + ARGV.clear + ARGV << "--foo=bar" + + Application.option :foo, + :description => "meh", + :arguments => "--foo [FOO]" + + Application.new.run + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + + it "should create an application option" do + OptionParser.any_instance.stubs("parse!").returns(true) + OptionParser.any_instance.expects(:on).with(anything, anything, anything, anything).at_least_once + OptionParser.any_instance.expects(:on).with('--foo [FOO]', String, 'meh').at_least_once + Application.any_instance.stubs("main").returns(true) + + ARGV.clear + ARGV << "--foo=bar" + + Application.option :foo, + :description => "meh", + :arguments => "--foo [FOO]" + + Application.new.run + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + end + + describe "#initialize" do + it "should parse the command line options at application run" do + Application.any_instance.expects("application_parse_options").once + Application.any_instance.stubs("main").returns(true) + + Application.new.run + end + end + + describe "#application_options" do + it "sshould return the application options" do + Application.new.application_options.should == Application.application_options + end + end + + describe "#application_description" do + it "should provide the right description" do + Application.description "Foo" + Application.new.application_description.should == "Foo" + end + end + + describe "#application_usage" do + it "should provide the right usage" do + Application.usage "Foo" + Application.new.application_usage.should == ["Foo"] + end + end + + describe "#application_cli_arguments" do + it "should provide the right usage" do + Application.option :foo, + :description => "meh", + :arguments => "--foo [FOO]" + + args = Application.new.application_cli_arguments.first + + # need to remove this cos we cant validate procs for equality afaik + args.delete(:validate) + + args.should == {:description=>"meh", + :name=>:foo, + :arguments=>"--foo [FOO]", + :type=>String, + :required=>false} + end + end + + describe "#help" do + it "should generate help using the full user supplied options" do + app = Application.new + app.expects(:clioptions).with(true).once + app.help + end + end + + describe "#main" do + it "should detect applications without a #main" do + IO.any_instance.expects(:puts).with("Applications need to supply a 'main' method") + + expect { + Application.new.run + }.to raise_error(SystemExit) + end + + it "should raise SystemExit exceptions for exit events" do + connector = mock + connector.expects(:disconnect) + PluginManager.expects("[]").with("connector_plugin").returns(connector) + + a = Application.new + a.expects(:main).raises(SystemExit) + + expect { + a.run + }.to raise_error(SystemExit) + end + end + + describe "#configuration" do + it "should return the correct configuration" do + Application.any_instance.stubs("main").returns(true) + + ARGV.clear + ARGV << "--foo=bar" + + Application.option :foo, + :description => "meh", + :arguments => "--foo [FOO]" + + a = Application.new + a.run + + a.configuration.should == {:foo => "bar"} + + ARGV.clear + @argv_backup.each{|a| ARGV << a} + end + end + + describe "#halt" do + before do + @stats = {:discoverytime => 0, :discovered => 0, :failcount => 0, :responses => 0} + end + + it "should exit with code 0 if discovery was done and all responses passed" do + app = Application.new + + @stats[:discoverytime] = 2 + @stats[:discovered] = 2 + @stats[:responses] = 2 + + app.expects(:exit).with(0) + + app.halt(@stats) + end + + it "should exit with code 0 if no discovery were done but responses were received" do + app = Application.new + + @stats[:responses] = 1 + + app.expects(:exit).with(0) + + app.halt(@stats) + end + + it "should exit with code 0 if discovery info is missing" do + app = Application.new + + app.expects(:exit).with(0) + + app.halt({}) + end + + it "should exit with code 1 if no nodes were discovered and discovery was done" do + app = Application.new + + @stats[:discoverytime] = 2 + + app.expects(:exit).with(1) + + app.halt(@stats) + end + + it "should exit with code 2 if a request failed for some nodes" do + app = Application.new + + @stats[:discovered] = 1 + @stats[:failcount] = 1 + @stats[:discoverytime] = 2 + @stats[:responses] = 1 + + app.expects(:exit).with(2) + + app.halt(@stats) + end + + it "should exit with code 3 if no responses were received after discovery" do + app = Application.new + + @stats[:discovered] = 1 + @stats[:discoverytime] = 2 + + app.expects(:exit).with(3) + + app.halt(@stats) + end + + it "should exit with code 4 if no discovery was done and no responses were received" do + app = Application.new + + app.expects(:exit).with(4) + + app.halt(@stats) + end + end + + describe "#disconnect" do + it "should disconnect from the connector plugin" do + connector = mock + connector.expects(:disconnect) + PluginManager.expects("[]").with("connector_plugin").returns(connector) + + Application.new.disconnect + end + end + + describe "#clioptions" do + it "should pass the excluded argument section" do + oparser = mock + oparser.stubs(:parse) + + Application.exclude_argument_sections "rpc" + + Optionparser.expects(:new).with({:verbose => false, :progress_bar => true}, "filter", ["rpc"]).returns(oparser) + + Application.new.clioptions(false) + end + + it "should add the RPC options" do + oparser = mock + oparser.stubs(:parse).yields(oparser, {}) + oparser.stubs(:banner=) + oparser.stubs(:define_tail) + + Optionparser.stubs(:new).with({:verbose => false, :progress_bar => true}, "filter", []).returns(oparser) + RPC::Helpers.expects(:add_simplerpc_options).with(oparser, {}) + + Application.new.clioptions(false) + end + + it "should support bypassing the RPC options" do + oparser = mock + oparser.stubs(:parse).yields(oparser, {}) + oparser.stubs(:banner=) + oparser.stubs(:define_tail) + + Application.exclude_argument_sections "rpc" + + Optionparser.stubs(:new).with({:verbose => false, :progress_bar => true}, "filter", ["rpc"]).returns(oparser) + RPC::Helpers.expects(:add_simplerpc_options).never + + Application.new.clioptions(false) + end + + it "should return the help text if requested" do + parser = mock + parser.expects(:help) + + oparser = mock + oparser.stubs(:parse).yields(oparser, {}) + oparser.stubs(:banner=) + oparser.stubs(:define_tail) + oparser.expects(:parser).returns(parser) + + Optionparser.stubs(:new).with({:verbose => false, :progress_bar => true}, "filter", []).returns(oparser) + RPC::Helpers.expects(:add_simplerpc_options).with(oparser, {}) + + Application.new.clioptions(true) + end + end + + describe "#application_failure" do + before do + @app = Application.new + end + + it "on SystemExit it should disconnect and exit without backtraces or error messages" do + @app.expects(:disconnect) + expect { @app.application_failure(SystemExit.new) }.to raise_error(SystemExit) + end + + it "should print a single line error message" do + out = StringIO.new + @app.stubs(:disconnect) + @app.stubs(:exit).with(1) + @app.stubs(:options).returns({}) + + Config.instance.stubs(:color).returns(false) + e = mock + e.stubs(:backtrace).returns([]) + e.stubs(:to_s).returns("rspec") + + out.expects(:puts).with(regexp_matches(/rspec application failed to run/)) + + @app.application_failure(e, out) + end + + it "should print a backtrace if options are unset or verbose is enabled" do + out = StringIO.new + @app.stubs(:disconnect) + @app.stubs(:exit).with(1) + @app.stubs(:options).returns(nil) + + Config.instance.stubs(:color).returns(false) + e = mock + e.stubs(:backtrace).returns(["rspec"]) + e.stubs(:to_s).returns("rspec") + + @app.expects(:options).returns({:verbose => true}).times(3) + out.expects(:puts).with(regexp_matches(/ application failed to run/)) + out.expects(:puts).with(regexp_matches(/from rspec <---/)) + out.expects(:puts).with(regexp_matches(/rspec.+Mocha::Mock/)) + + @app.application_failure(e, out) + end + end + + describe "#run" do + before do + @app = Application.new + end + + it "should parse the application options, run main and disconnect" do + @app.expects(:application_parse_options) + @app.expects(:main) + @app.expects(:disconnect) + + @app.run + end + + it "should allow the application plugin to validate configuration variables" do + @app.expects("respond_to?").with(:validate_configuration).returns(true) + @app.expects(:validate_configuration).once + + @app.stubs(:application_parse_options) + @app.stubs(:main) + @app.stubs(:disconnect) + + @app.run + end + + it "should start the sleeper thread on windows" do + Util.expects("windows?").returns(true) + Util.expects(:setup_windows_sleeper).once + + @app.stubs(:application_parse_options) + @app.stubs(:main) + @app.stubs(:disconnect) + + @app.run + end + + it "should catch handle exit() correctly" do + @app.expects(:main).raises(SystemExit) + @app.expects(:disconnect).once + + expect { @app.run }.to raise_error(SystemExit) + end + + it "should catch all exceptions and process them correctly" do + @app.expects(:main).raises("rspec") + @app.expects(:application_failure).once + @app.run + end + end + end +end