beaker 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,7 @@ module Beaker
3
3
  module Validator
4
4
  PACKAGES = ['curl']
5
5
  UNIX_PACKAGES = ['ntpdate']
6
+ SLES_PACKAGES = ['ntp']
6
7
 
7
8
  def self.validate(hosts, logger)
8
9
  hosts.each do |host|
@@ -11,12 +12,20 @@ module Beaker
11
12
  host.install_package pkg
12
13
  end
13
14
  end
14
- if host['platform'] !~ /(windows)|(aix)|(solaris)/
15
- UNIX_PACKAGES.each do |pkg|
16
- if not host.check_for_package pkg
17
- host.install_package pkg
15
+ case
16
+ when host['platform'] =~ /sles-/
17
+ SLES_PACKAGES.each do |pkg|
18
+ if not host.check_for_package pkg
19
+ host.install_package pkg
20
+ end
21
+ end
22
+
23
+ when host['platform'] !~ /(windows)|(aix)|(solaris)/
24
+ UNIX_PACKAGES.each do |pkg|
25
+ if not host.check_for_package pkg
26
+ host.install_package pkg
27
+ end
18
28
  end
19
- end
20
29
  end
21
30
  end
22
31
  rescue => e
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '1.7.0'
3
+ STRING = '1.8.0'
4
4
  end
5
5
  end
@@ -8,6 +8,12 @@ module Beaker
8
8
  basic_hosts }
9
9
  let( :master_certname ) { 'master_certname' }
10
10
 
11
+ it 'generates 3.2 answers for 3.3 hosts' do
12
+ @ver = '3.3'
13
+ Beaker::Answers::Version32.should_receive( :answers ).with( hosts, master_certname, {}).once
14
+ subject.answers( @ver, hosts, master_certname, {} )
15
+ end
16
+
11
17
  it 'generates 3.2 answers for 3.2 hosts' do
12
18
  @ver = '3.2'
13
19
  Beaker::Answers::Version32.should_receive( :answers ).with( hosts, master_certname, {}).once
@@ -63,7 +69,15 @@ module Beaker
63
69
  it 'should add q_upgrade_with_unknown_disk_space to the dashboard on upgrade' do
64
70
  @ver = '3.2'
65
71
  expect( subject.answers( hosts, master_certname, {:type => :upgrade} )['vm2']).to include :q_upgrade_with_unknown_disk_space
66
- end
72
+ end
73
+
74
+ it 'should add answers to the host objects' do
75
+ @ver = '3.2'
76
+ answers = subject.answers( hosts, master_certname, {})
77
+ hosts.each do |host|
78
+ expect( host[:answers] ).to be === answers[host.name]
79
+ end
80
+ end
67
81
  end
68
82
 
69
83
  describe Version30 do
@@ -85,9 +99,18 @@ module Beaker
85
99
  it 'sets correct answers for a master' do
86
100
  expect( subject.answers( hosts, master_certname, {} )['vm1'] ).to be === { :q_install=>"y", :q_vendor_packages_install=>"y", :q_puppetagent_install=>"y", :q_puppet_cloud_install=>"y", :q_verify_packages=>"y", :q_puppet_symlinks_install=>"y", :q_puppetagent_certname=>hosts[0], :q_puppetagent_server=>master_certname, :q_puppetmaster_install=>"y", :q_all_in_one_install=>"y", :q_puppet_enterpriseconsole_install=>"y", :q_puppetdb_install=>"y", :q_database_install=>"y", :q_puppetdb_hostname=>hosts[0], :q_puppetdb_port=>8081, :q_puppetmaster_dnsaltnames=>"master_certname,puppet,#{hosts[0][:ip]}", :q_puppetmaster_enterpriseconsole_hostname=>hosts[0], :q_puppetmaster_enterpriseconsole_port=>443, :q_puppetmaster_certname=>"master_certname", :q_puppetdb_database_name=>"pe-puppetdb", :q_puppetdb_database_user=>"mYpdBu3r", :q_puppetdb_database_password=>"'~!@\#$%^*-/ aZ'", :q_puppet_enterpriseconsole_auth_database_name=>"console_auth", :q_puppet_enterpriseconsole_auth_database_user=>"mYu7hu3r", :q_puppet_enterpriseconsole_auth_database_password=>"'~!@\#$%^*-/ aZ'", :q_puppet_enterpriseconsole_database_name=>"console", :q_puppet_enterpriseconsole_database_user=>"mYc0nS03u3r", :q_puppet_enterpriseconsole_database_password=>"'~!@\#$%^*-/ aZ'", :q_database_host=>hosts[0], :q_database_port=>5432, :q_pe_database=>"y", :q_puppet_enterpriseconsole_inventory_hostname=>hosts[0], :q_puppet_enterpriseconsole_inventory_certname=>hosts[0], :q_puppet_enterpriseconsole_inventory_dnsaltnames=>hosts[0], :q_puppet_enterpriseconsole_inventory_port=>8140, :q_puppet_enterpriseconsole_master_hostname=>hosts[0], :q_puppet_enterpriseconsole_auth_user_email=>"'admin@example.com'", :q_puppet_enterpriseconsole_auth_password=>"'~!@\#$%^*-/ aZ'", :q_puppet_enterpriseconsole_httpd_port=>443, :q_puppet_enterpriseconsole_smtp_host=>"'vm1'", :q_puppet_enterpriseconsole_smtp_use_tls=>"'n'", :q_puppet_enterpriseconsole_smtp_port=>"'25'", :q_database_root_password=>"'=ZYdjiP3jCwV5eo9s1MBd'", :q_database_root_user=>"pe-postgres" }
87
101
  end
102
+
88
103
  it 'generates nil answers for a windows host' do
89
104
  expect( subject.answers( hosts, master_certname, {} )['vm2'] ).to be === nil
90
105
  end
106
+
107
+ it 'should add answers to the host objects' do
108
+ @ver = '3.0'
109
+ answers = subject.answers( hosts, master_certname, {})
110
+ hosts.each do |host|
111
+ expect( host[:answers] ).to be === answers[host.name]
112
+ end
113
+ end
91
114
  end
92
115
 
93
116
  describe Version28 do
@@ -109,6 +132,13 @@ module Beaker
109
132
  expect( subject.answers( hosts, master_certname, {} )['vm2'] ).to be === nil
110
133
  end
111
134
 
135
+ it 'should add answers to the host objects' do
136
+ @ver = '2.8'
137
+ answers = subject.answers( hosts, master_certname, {})
138
+ hosts.each do |host|
139
+ expect( host[:answers] ).to be === answers[host.name]
140
+ end
141
+ end
112
142
 
113
143
  end
114
144
  describe Version20 do
@@ -130,6 +160,14 @@ module Beaker
130
160
  expect( subject.answers( hosts, master_certname, {} )['vm2'] ).to be === nil
131
161
  end
132
162
 
163
+ it 'should add answers to the host objects' do
164
+ @ver = '2.0'
165
+ answers = subject.answers( hosts, master_certname, {})
166
+ hosts.each do |host|
167
+ expect( host[:answers] ).to be === answers[host.name]
168
+ end
169
+ end
170
+
133
171
  end
134
172
 
135
173
  end
@@ -95,10 +95,10 @@ describe ClassMixedWithDSLHelpers do
95
95
  host.should_receive( :exec ).and_return( result )
96
96
  end
97
97
 
98
- it 'yields self' do
98
+ it 'yields result' do
99
99
  subject.on host, command do |containing_class|
100
100
  expect( containing_class ).
101
- to be_an_instance_of( ClassMixedWithDSLHelpers )
101
+ to be_an_instance_of( Beaker::Result )
102
102
  end
103
103
  end
104
104
 
@@ -651,11 +651,17 @@ describe ClassMixedWithDSLHelpers do
651
651
  end
652
652
 
653
653
  describe '#with_puppet_running_on' do
654
- let(:is_pe) { false }
655
- let(:host) { FakeHost.new(:pe => is_pe) }
656
654
  let(:test_case_path) { 'testcase/path' }
657
655
  let(:tmpdir_path) { '/tmp/tmpdir' }
658
656
  let(:puppet_path) { '/puppet/path' }
657
+ let(:is_pe) { false }
658
+ let(:host) do
659
+ FakeHost.new(:pe => is_pe,
660
+ :options => {
661
+ 'puppetpath' => puppet_path,
662
+ 'platform' => 'el'
663
+ })
664
+ end
659
665
 
660
666
  def stub_host_and_subject_to_allow_the_default_testdir_argument_to_be_created
661
667
  subject.instance_variable_set(:@path, test_case_path)
@@ -665,7 +671,6 @@ describe ClassMixedWithDSLHelpers do
665
671
 
666
672
  before do
667
673
  stub_host_and_subject_to_allow_the_default_testdir_argument_to_be_created
668
- host.stub(:[]).and_return(puppet_path)
669
674
  end
670
675
 
671
676
  it "raises an ArgumentError if you try to submit a String instead of a Hash of options" do
@@ -790,6 +795,26 @@ describe ClassMixedWithDSLHelpers do
790
795
  end.to raise_error(RuntimeError, /failed.*because.*Failed while yielding./)
791
796
  end
792
797
 
798
+ it 'dumps the puppet logs if there is an error in the teardown' do
799
+ host.should_receive(:port_open?).with(8140).and_return(true)
800
+
801
+ subject.logger.should_receive(:notify).with(/Dumping master log/)
802
+
803
+ expect do
804
+ subject.with_puppet_running_on(host, {})
805
+ end.to raise_error(RuntimeError, /Also failed in teardown/)
806
+ end
807
+
808
+ it 'does not mask the teardown error with an error from dumping the logs' do
809
+ host.should_receive(:port_open?).with(8140).and_return(true)
810
+
811
+ subject.logger.should_receive(:notify).with(/Dumping master log/).and_raise("Error from dumping logs")
812
+
813
+ expect do
814
+ subject.with_puppet_running_on(host, {})
815
+ end.to raise_error(RuntimeError, /Also failed in teardown/)
816
+ end
817
+
793
818
  it 'does not swallow a teardown exception if no earlier exception was raised' do
794
819
  host.should_receive(:port_open?).with(8140).and_return(true)
795
820
  subject.logger.should_not_receive(:error)
@@ -9,7 +9,6 @@ module Beaker
9
9
  let(:hosts_path) { File.join(File.expand_path(File.dirname(__FILE__)), "data", "hosts.cfg") }
10
10
  let(:badyaml_path) { File.join(File.expand_path(File.dirname(__FILE__)), "data", "badyaml.cfg") }
11
11
  let(:home) { ENV['HOME'] }
12
- let(:platforms_regex) { Parser::PLATFORMS }
13
12
 
14
13
  it "supports usage function" do
15
14
  expect{parser.usage}.to_not raise_error
@@ -21,26 +20,6 @@ module Beaker
21
20
  expect(parser.repo).to be === "#{repo}"
22
21
  end
23
22
 
24
- #read through the file of possible platform values, correctly identify the 50 invalid platform values
25
- describe "recognizes valid platforms" do
26
-
27
- it "accepts correctly formatted platform values" do
28
- expect( 'oracle-version-arch' =~ platforms_regex ).to be === 0
29
- end
30
-
31
- it "rejects non-supported osfamilies" do
32
- expect( 'amazon6-version-arch' =~ platforms_regex ).to be === nil
33
- end
34
-
35
- it "rejects platforms without version/arch" do
36
- expect( 'ubuntu-5' =~ platforms_regex ).to be === nil
37
- end
38
-
39
- it "rejects platforms that do not have osfamily at start of string" do
40
- expect( 'oel-r5-u6-x86-64' =~ platforms_regex ).to be === nil
41
- end
42
- end
43
-
44
23
  #test parse_install_options
45
24
  it "can transform --install PUPPET/3.1 into #{repo}/puppet.git#3.1" do
46
25
  opts = ["PUPPET/3.1"]
@@ -154,7 +133,7 @@ module Beaker
154
133
  it "can correctly combine arguments from different sources" do
155
134
  FakeFS.deactivate!
156
135
  args = ["-h", hosts_path, "--log-level", "debug", "--type", "git", "--install", "PUPPET/1.0,HIERA/hello"]
157
- expect(parser.parse_args(args)).to be === {:log_level=>"debug", :hosts_file=>hosts_path, :options_file=>nil, :type=>"git", :provision=>true, :preserve_hosts=>'never', :root_keys=>false, :quiet=>false, :xml=>false, :color=>true, :dry_run=>false, :timeout=>300, :fail_mode=>'slow', :timesync=>false, :repo_proxy=>false, :add_el_extras=>false, :consoleport=>443, :pe_dir=>"/opt/enterprise/dists", :pe_version_file=>"LATEST", :pe_version_file_win=>"LATEST-win", :dot_fog=>"#{home}/.fog", :help=>false, :ec2_yaml=>"config/image_templates/ec2.yaml", :ssh=>{:config=>false, :paranoid=>false, :timeout=>300, :auth_methods=>["publickey"], :port=>22, :forward_agent=>true, :keys=>["#{home}/.ssh/id_rsa"], :user_known_hosts_file=>"#{home}/.ssh/known_hosts"}, :install=>["git://github.com/puppetlabs/puppet.git#1.0", "git://github.com/puppetlabs/hiera.git#hello"], :HOSTS=>{:"pe-ubuntu-lucid"=>{:roles=>["agent", "dashboard", "database", "master"], :vmname=>"pe-ubuntu-lucid", :platform=>"ubuntu-10.04-i386", :snapshot=>"clean-w-keys", :hypervisor=>"fusion"}, :"pe-centos6"=>{:roles=>["agent"], :vmname=>"pe-centos6", :platform=>"el-6-i386", :hypervisor=>"fusion", :snapshot=>"clean-w-keys"}}, :nfs_server=>"none", :helper=>[], :load_path=>[], :tests=>[], :pre_suite=>[], :post_suite=>[], :modules=>[]}
136
+ expect(parser.parse_args(args)).to be === {:log_level=>"debug", :trace_limit=>10, :hosts_file=>hosts_path, :options_file=>nil, :type=>"git", :provision=>true, :preserve_hosts=>'never', :root_keys=>false, :quiet=>false, :xml=>false, :color=>true, :dry_run=>false, :timeout=>300, :fail_mode=>'slow', :timesync=>false, :repo_proxy=>false, :add_el_extras=>false, :consoleport=>443, :pe_dir=>"/opt/enterprise/dists", :pe_version_file=>"LATEST", :pe_version_file_win=>"LATEST-win", :dot_fog=>"#{home}/.fog", :help=>false, :ec2_yaml=>"config/image_templates/ec2.yaml", :ssh=>{:config=>false, :paranoid=>false, :timeout=>300, :auth_methods=>["publickey"], :port=>22, :forward_agent=>true, :keys=>["#{home}/.ssh/id_rsa"], :user_known_hosts_file=>"#{home}/.ssh/known_hosts"}, :install=>["git://github.com/puppetlabs/puppet.git#1.0", "git://github.com/puppetlabs/hiera.git#hello"], :HOSTS=>{:"pe-ubuntu-lucid"=>{:roles=>["agent", "dashboard", "database", "master"], :vmname=>"pe-ubuntu-lucid", :platform=>"ubuntu-10.04-i386", :snapshot=>"clean-w-keys", :hypervisor=>"fusion"}, :"pe-centos6"=>{:roles=>["agent"], :vmname=>"pe-centos6", :platform=>"el-6-i386", :hypervisor=>"fusion", :snapshot=>"clean-w-keys"}}, :nfs_server=>"none", :helper=>[], :load_path=>[], :tests=>[], :pre_suite=>[], :post_suite=>[], :modules=>[]}
158
137
  end
159
138
 
160
139
  it "ensures that fail-mode is one of fast/slow" do
@@ -174,10 +153,10 @@ module Beaker
174
153
  {
175
154
  'HOSTS' => {
176
155
  :master => {
177
- :roles => ["master","agent"],
156
+ :roles => ["master","agent","arbitrary_role"],
178
157
  },
179
158
  :agent => {
180
- :roles => ["agent"],
159
+ :roles => ["agent","default","other_abitrary_role"],
181
160
  },
182
161
  }
183
162
  }
@@ -198,7 +177,7 @@ module Beaker
198
177
  it "restricts #{platform} hosts to agent for #{type}" do
199
178
  hosts_file = fake_hosts_file_for_platform(hosts, platform)
200
179
  args << "--hosts" << hosts_file
201
- expect { parser.parse_args(args) }.to raise_error(ArgumentError, /#{platform}.*can only have role 'agent'/)
180
+ expect { parser.parse_args(args) }.to raise_error(ArgumentError, /#{platform}.*may not have roles 'master', 'dashboard', or 'database'/)
202
181
  end
203
182
  end
204
183
 
@@ -0,0 +1,97 @@
1
+ require 'spec_helper'
2
+
3
+ module Beaker
4
+ describe Platform do
5
+
6
+ let( :logger ) { double( 'logger' ) }
7
+ let( :platform ) { Platform.new(@name) }
8
+
9
+ context 'initialize' do
10
+
11
+ describe "recognizes valid platforms" do
12
+
13
+ it "accepts correctly formatted platform values" do
14
+ @name = 'oracle-version-arch'
15
+ expect{ platform }.not_to raise_error
16
+ end
17
+
18
+ it "rejects non-supported osfamilies" do
19
+ @name = 'amazon6-version-arch'
20
+ expect{ platform }.to raise_error(ArgumentError)
21
+ end
22
+
23
+ it "rejects platforms without version/arch" do
24
+ @name = 'ubuntu-5'
25
+ expect{ platform }.to raise_error(ArgumentError)
26
+ end
27
+
28
+ it "rejects platforms that do not have osfamily at start of string" do
29
+ @name = 'o3l-r5-u6-x86'
30
+ expect{ platform }.to raise_error(ArgumentError)
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ context 'with_version_codename' do
38
+
39
+ it "can convert debian-7-xxx to debian-wheezy-xxx" do
40
+ @name = 'debian-7-xxx'
41
+ expect( platform.with_version_codename ).to be === 'debian-wheezy-xxx'
42
+ end
43
+
44
+ it "can convert debian-6-xxx to debian-squeeze-xxx" do
45
+ @name = 'debian-6-xxx'
46
+ expect( platform.with_version_codename ).to be === 'debian-squeeze-xxx'
47
+ end
48
+
49
+ it "can convert ubuntu-1310-xxx to ubuntu-saucy-xxx" do
50
+ @name = 'ubuntu-1310-xxx'
51
+ expect( platform.with_version_codename ).to be === 'ubuntu-saucy-xxx'
52
+ end
53
+
54
+ it "can convert ubuntu-12.10-xxx to ubuntu-quantal-xxx" do
55
+ @name = 'ubuntu-12.10-xxx'
56
+ expect( platform.with_version_codename ).to be === 'ubuntu-quantal-xxx'
57
+ end
58
+
59
+ it "leaves centos-7-xxx alone" do
60
+ @name = 'centos-7-xxx'
61
+ expect( platform.with_version_codename ).to be === 'centos-7-xxx'
62
+ end
63
+
64
+ end
65
+
66
+ context 'with_version_number' do
67
+
68
+ it "can convert debian-wheezy-xxx to debian-7-xxx" do
69
+ @name = 'debian-wheezy-xxx'
70
+ expect( platform.with_version_number ).to be === 'debian-7-xxx'
71
+ end
72
+
73
+ it "can convert debian-squeeze-xxx to debian-6-xxx" do
74
+ @name = 'debian-squeeze-xxx'
75
+ expect( platform.with_version_number ).to be === 'debian-6-xxx'
76
+ end
77
+
78
+ it "can convert ubuntu-saucy-xxx to ubuntu-1310-xxx" do
79
+ @name = 'ubuntu-saucy-xxx'
80
+ expect( platform.with_version_number ).to be === 'ubuntu-1310-xxx'
81
+ end
82
+
83
+ it "can convert ubuntu-quantal-xxx to ubuntu-1210-xxx" do
84
+ @name = 'ubuntu-quantal-xxx'
85
+ expect( platform.with_version_number ).to be === 'ubuntu-1210-xxx'
86
+ end
87
+
88
+ it "leaves centos-7-xxx alone" do
89
+ @name = 'centos-7-xxx'
90
+ expect( platform.with_version_number ).to be === 'centos-7-xxx'
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -15,6 +15,24 @@ module Beaker
15
15
 
16
16
  end
17
17
 
18
+ it "can retry on failure on unix hosts" do
19
+ @hosts = make_hosts( { :platform => 'unix', :exit_code => [1, 0] } )
20
+ ntp_control.stub( :sleep ).and_return(true)
21
+
22
+ Command.should_receive( :new ).with("ntpdate -t 20 #{ntpserver}").exactly( 6 ).times
23
+
24
+ ntp_control.timesync
25
+ end
26
+
27
+ it "eventually gives up and raises an error when unix hosts can't be synched" do
28
+ @hosts = make_hosts( { :platform => 'unix', :exit_code => 1 } )
29
+ ntp_control.stub( :sleep ).and_return(true)
30
+
31
+ Command.should_receive( :new ).with("ntpdate -t 20 #{ntpserver}").exactly( 5 ).times
32
+
33
+ expect{ ntp_control.timesync }.to raise_error
34
+ end
35
+
18
36
  it "can sync time on solaris-10 hosts" do
19
37
  @hosts = make_hosts( { :platform => 'solaris-10' } )
20
38
 
@@ -36,6 +54,16 @@ module Beaker
36
54
 
37
55
  end
38
56
 
57
+ it "can sync time on Sles hosts" do
58
+ @hosts = make_hosts( { :platform => 'sles-13.1-x64' } )
59
+
60
+ Command.should_receive( :new ).with("sntp #{ntpserver}").exactly( 3 ).times
61
+
62
+ ntp_control.timesync
63
+
64
+ end
65
+
66
+
39
67
  end
40
68
 
41
69
  end
@@ -6,6 +6,7 @@ module Beaker
6
6
  let( :validator ) { Beaker::Utils::Validator }
7
7
  let( :pkgs ) { Beaker::Utils::Validator::PACKAGES }
8
8
  let( :unix_only_pkgs ) { Beaker::Utils::Validator::UNIX_PACKAGES }
9
+ let( :sles_only_pkgs ) { Beaker::Utils::Validator::SLES_PACKAGES }
9
10
  let( :platform ) { @platform || 'unix' }
10
11
  let( :hosts ) { hosts = make_hosts( { :platform => platform } )
11
12
  hosts[0][:roles] = ['agent']
@@ -26,6 +27,10 @@ module Beaker
26
27
  host.should_receive( :check_for_package ).with( pkg ).once.and_return( false )
27
28
  host.should_receive( :install_package ).with( pkg ).once
28
29
  end
30
+ sles_only_pkgs.each do |pkg|
31
+ host.should_receive( :check_for_package).with( pkg ).never
32
+ host.should_receive( :install_package ).with( pkg ).never
33
+ end
29
34
 
30
35
  end
31
36
 
@@ -45,6 +50,34 @@ module Beaker
45
50
  host.should_receive( :check_for_package).with( pkg ).never
46
51
  host.should_receive( :install_package ).with( pkg ).never
47
52
  end
53
+ sles_only_pkgs.each do |pkg|
54
+ host.should_receive( :check_for_package).with( pkg ).never
55
+ host.should_receive( :install_package ).with( pkg ).never
56
+ end
57
+
58
+ end
59
+
60
+ validator.validate(hosts, logger)
61
+
62
+ end
63
+
64
+ it "can validate SLES hosts" do
65
+ @platform = 'sles-13.1-x64'
66
+
67
+ hosts.each do |host|
68
+ pkgs.each do |pkg|
69
+ host.should_receive( :check_for_package ).with( pkg ).once.and_return( false )
70
+ host.should_receive( :install_package ).with( pkg ).once
71
+ end
72
+ unix_only_pkgs.each do |pkg|
73
+ host.should_receive( :check_for_package).with( pkg ).never
74
+ host.should_receive( :install_package ).with( pkg ).never
75
+ end
76
+ sles_only_pkgs.each do |pkg|
77
+ host.should_receive( :check_for_package).with( pkg ).once.and_return( false )
78
+ host.should_receive( :install_package ).with( pkg ).once
79
+ end
80
+
48
81
  end
49
82
 
50
83
  validator.validate(hosts, logger)
@@ -58,12 +58,13 @@ module HostHelpers
58
58
 
59
59
  def generate_result (name, opts )
60
60
  result = double( 'result' )
61
- stdout = opts.has_key?(:stdout) ? opts[:stdout] : name
61
+ stdout = opts.has_key?(:stdout) ? opts[:stdout] : name
62
62
  stderr = opts.has_key?(:stderr) ? opts[:stderr] : name
63
63
  exit_code = opts.has_key?(:exit_code) ? opts[:exit_code] : 0
64
+ exit_code = [exit_code].flatten
64
65
  result.stub( :stdout ).and_return( stdout )
65
66
  result.stub( :stderr ).and_return( stderr )
66
- result.stub( :exit_code ).and_return( exit_code )
67
+ result.stub( :exit_code ).and_return( *exit_code )
67
68
  result
68
69
  end
69
70