require File.join(File.dirname(__FILE__), 'test_helper') module Boson class ScientistTest < Test::Unit::TestCase before(:all) { unless ScientistTest.const_defined?(:Blah) Boson.send :remove_const, :BinRunner if Boson.const_defined?(:BinRunner) eval <<-EOF module Blah def blah(arg1, options={}) [arg1, options] end def splat_blah(*args) args end def default_blah(arg1, arg2=default, options={}) [arg1, arg2, options] end def default; 'some default'; end def default_option(options={}) options end end EOF end @opt_cmd = Object.new.extend Blah } def command(hash, args) hash = {:name=>'blah', :lib=>'bling', :options=>{:force=>:boolean, :level=>2}}.merge(hash) @cmd = Command.new hash @cmd.instance_variable_set("@file_parsed_args", true) if hash[:file_parsed_args] Scientist.create_option_command(@opt_cmd, @cmd) @opt_cmd.send(hash[:name], *args) end def command_with_arg_size(*args) command({:args=>2}, args) end def command_with_args(*args) command({:args=>[['arg1'],['options', {}]]}, args) end def basic_command(hash, args) command({:name=>'splat_blah', :args=>'*'}.merge(hash), args) end def command_with_splat_args(*args) command({:name=>'splat_blah', :args=>'*'}, args) end def command_with_arg_defaults(*args) arg_defaults = [%w{arg1}, %w{arg2 default}, %w{options {}}] command({:name=>'default_blah', :file_parsed_args=>true, :args=>arg_defaults}, args) end def args_are_equal(args, array) command_with_args(*args).should == array command_with_arg_size(*args).should == array command_with_splat_args(*args).should == array end ALL_COMMANDS = [:command_with_args, :command_with_arg_size, :command_with_splat_args] context "all commands" do test "translate arg and options as one string" do args_are_equal ['a1 -f'], ['a1', {:force=>true, :level=>2}] end test "translate arg and stringified options" do args_are_equal [:cool, '-l3'], [:cool, {:level=>3}] end test "translate arg and normal hash options" do args_are_equal [:cool, {:ok=>true}], [:cool, {:ok=>true, :level=>2}] end test "translate stringified arg without options sets default options" do args_are_equal ['cool'], ['cool', {:level=>2}] end test "translate arg without options sets default options" do args_are_equal [:cool], [:cool, {:level=>2}] end test "with invalid options print errors and delete them" do ALL_COMMANDS.each do |cmd| capture_stderr { send(cmd, 'cool -f -z').should == ['cool', {:force=>true, :level=>2}] }.should =~/invalid.*z/ end end test "print help with help option" do ALL_COMMANDS.each do |cmd| Boson.expects(:invoke).with(:usage, anything, anything) send(cmd, '-h') end end end context "command" do context "with arg defaults" do test "sets defaults with stringified args" do command_with_arg_defaults('1').should == ["1", "some default", {:level=>2}] end test "sets defaults with normal args" do command_with_arg_defaults(1).should == [1, "some default", {:level=>2}] end test "sets default if optional arg is a valid option" do command_with_arg_defaults("cool -f").should == ["cool", "some default", {:level=>2, :force=>true}] end test "doesn't set defaults if not needed" do command_with_arg_defaults(1, 'nada').should == [1, "nada", {:level=>2}] end test "prints error for invalid defaults" do arg_defaults = [%w{arg1}, %w{arg2 invalidzzz}, %w{options {}}] capture_stderr { command({:name=>'default_blah', :file_parsed_args=>true, :args=>arg_defaults}, [1]) }.should =~ /Error.*position 2/ end end context "prints error" do test "with option error" do capture_stderr { command_with_args('a1 -l') }.should =~ /Error.*level/ end test "with unexpected error in translation" do Scientist.expects(:parse_command_options).raises("unexpected") capture_stderr { command_with_args('a1') }.should =~ /Error.*unexpected/ end test "with unexpected error in render" do Scientist.expects(:render?).raises("unexpected") capture_stderr { command_with_args('a1') }.should =~ /Error.*unexpected/ end test "with no argument defined for options" do assert_error(ArgumentError, '2 for 1') { command({:args=>1}, 'ok') } end end test "translates stringfied args + options starting at second arg" do command_with_arg_defaults(1, 'nada -l3').should == [1, "nada", {:level=>3}] end test "with leading option-like args are translated as arguments" do command_with_args('-z -f').should == ["-z", {:force=>true, :level=>2}] command_with_args('--blah -f').should == ['--blah', {:force=>true, :level=>2}] end test "with splat args does not raise error for too few or many args" do [[], [''], [1,2,3], ['1 2 3']].each do |args| assert_nothing_raised { command_with_splat_args *args } end end test "with debug option prints debug" do capture_stdout { command_with_args("-v ok") } =~ /Arguments.*ok/ end test "with pretend option prints arguments and returns early" do Scientist.expects(:render_or_raw).never capture_stdout { command_with_args("-p ok") } =~ /Arguments.*ok/ end test "with not enough args raises ArgumentError" do args = [ArgumentError, '0 for 1'] assert_error(*args) { command_with_args } assert_error(*args) { command_with_args '' } assert_error(*args) { command_with_arg_size } assert_error(*args) { command_with_arg_size '' } end test "with too many args raises ArgumentError" do args3 = [ArgumentError, '3 for 2'] args4 = [ArgumentError, '4 for 2'] assert_error(*args3) { command_with_args 1,2,3 } assert_error(*args4) { command_with_args '1 2 3' } assert_error(*args3) { command_with_arg_size 1,2,3 } assert_error(*args4) { command_with_arg_size '1 2 3' } end end def command_with_render(*args) basic_command({:render_options=>{:fields=>{:values=>['f1', 'f2']}} }, args) end def render_expected(options=nil) View.expects(:render).with(anything, options || anything, false) end context "render" do test "called for command with render_options" do render_expected command_with_render('1') end test "called for command without render_options and --render" do render_expected command_with_args('--render 1') end test "not called for command with render_options and --render" do Boson.expects(:invoke).never command_with_render('--render 1') end test "not called for command without render_options" do Boson.expects(:invoke).never command_with_args('1') end end context "command renders" do test "with basic render options" do render_expected :fields => ['f1', 'f2'] command_with_render("--fields f1,f2 ab") end test "without non-render options" do render_expected :fields=>['f1'] Scientist.expects(:render?).returns(true) args = ["--render --fields f1 ab"] basic_command({:render_options=>{:fields=>{:values=>['f1', 'f2']}} }, args) end test "with user-defined render options" do render_expected :fields=>['f1'], :foo=>true args = ["--foo --fields f1 ab"] basic_command({:render_options=>{:foo=>:boolean, :fields=>{:values=>['f1', 'f2']}} }, args) end test "with non-hash user-defined render options" do render_expected :fields=>['f1'], :foo=>true args = ["--foo --fields f1 ab"] basic_command({:render_options=>{:foo=>:boolean, :fields=>%w{f1 f2 f3}} }, args) end end context "command with default option" do before(:all) { @cmd_attributes = {:name=>'default_option', :default_option=>'level', :args=>1} } test "parses normally from irb" do command(@cmd_attributes, '-f --level=3').should == {:level=>3, :force=>true} end test "parses normally from cmdline" do Boson.expects(:const_defined?).returns true command(@cmd_attributes, ['--force', '--level=3']).should == {:level=>3, :force=>true} end test "parses no arguments normally" do command(@cmd_attributes, '').should == {:level=>2} end test "parses ruby arguments normally" do command(@cmd_attributes, [{:force=>true, :level=>10}]).should == {:level=>10, :force=>true} end test "prepends correctly from irb" do command(@cmd_attributes, '3 -f').should == {:level=>3, :force=>true} end test "prepends correctly from cmdline" do Boson.expects(:const_defined?).returns true command(@cmd_attributes, ['3','-f']).should == {:level=>3, :force=>true} end end test "optionless command renders" do render_expected :fields=>['f1'] command({:args=>2, :options=>nil, :render_options=>{:fields=>:array}}, ["--fields f1 ab ok"]) end context "global options:" do def local_and_global(*args) Scientist.stubs(:render?).returns(false) # turn off rendering caused by :render_options @non_opts = basic_command(@command_options, args) @non_opts.slice!(-1,1) << Scientist.global_options end before(:all) { @command_options = {:options=>{:do=>:boolean, :foo=>:boolean}, :render_options=>{:dude=>:boolean}} @expected_non_opts = [[], ['doh'], ['doh'], [:doh]] } test "local option overrides global one" do ['-d', 'doh -d','-d doh', [:doh, '-d']].each_with_index do |args, i| local_and_global(*args).should == [{:do=>true}, {}] @non_opts.should == @expected_non_opts[i] end end test "global option before local one is valid" do args_arr = ['--dude -f', '--dude doh -f', '--dude -f doh', [:doh, '--dude -f']] args_arr.each_with_index do |args, i| local_and_global(*args).should == [{:foo=>true}, {:dude=>true}] @non_opts.should == @expected_non_opts[i] end end test "global option after local one is invalid" do args_arr = ['-f --dude', '-f doh --dude', '-f --dude doh', [:doh, '-f --dude'] ] args_arr.each_with_index do |args, i| capture_stderr { local_and_global(*args).should == [{:foo=>true}, {}] @non_opts.should == @expected_non_opts[i] }.should =~ /invalid.*dude/ end end test "--global option adds additional global options" do local_and_global('-g=d -d').should == [{:do=>true}, {:dude=>true, :global=>'d'}] local_and_global('-g "r dude" -d').should == [{:do=>true}, {:global=>"r dude", :dude=>true, :render=>true}] end end end end