beaker 3.34.0 → 3.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -1
  3. data/acceptance/tests/base/external_resources_test.rb +18 -3
  4. data/acceptance/tests/base/host/host_test.rb +1 -1
  5. data/docs/concepts/testing_beaker_itself.md +4 -4
  6. data/docs/concepts/ticket_process.md +12 -12
  7. data/docs/concepts/types_puppet_4_and_the_all_in_one_agent.md +1 -1
  8. data/docs/how_to/change_terminal_output_coloring.md +1 -1
  9. data/docs/how_to/confine.md +3 -3
  10. data/docs/how_to/debug_beaker_tests.md +19 -19
  11. data/docs/how_to/enabling_cross_sut_access.md +1 -1
  12. data/docs/how_to/hosts/README.md +1 -1
  13. data/docs/how_to/hosts/cisco.md +1 -1
  14. data/docs/how_to/hypervisors/README.md +1 -1
  15. data/docs/how_to/platform_specific_tag_confines.md +2 -2
  16. data/docs/how_to/preserve_hosts.md +2 -2
  17. data/docs/how_to/rake_tasks.md +3 -4
  18. data/docs/how_to/test_arbitrary_beaker_versions.md +3 -3
  19. data/docs/how_to/upgrade_from_2_to_3.md +4 -4
  20. data/docs/tutorials/README.md +1 -1
  21. data/docs/tutorials/test_suites.md +1 -1
  22. data/lib/beaker/cli.rb +1 -0
  23. data/lib/beaker/command.rb +2 -2
  24. data/lib/beaker/dsl/assertions.rb +1 -1
  25. data/lib/beaker/dsl/helpers/facter_helpers.rb +1 -1
  26. data/lib/beaker/dsl/install_utils/pe_defaults.rb +1 -1
  27. data/lib/beaker/dsl/roles.rb +1 -1
  28. data/lib/beaker/dsl/structure.rb +2 -2
  29. data/lib/beaker/host/cisco.rb +3 -3
  30. data/lib/beaker/host/mac/exec.rb +1 -1
  31. data/lib/beaker/host/windows/exec.rb +1 -1
  32. data/lib/beaker/host_prebuilt_steps.rb +3 -0
  33. data/lib/beaker/options/hosts_file_parser.rb +9 -2
  34. data/lib/beaker/options/parser.rb +1 -1
  35. data/lib/beaker/shared/options_resolver.rb +6 -6
  36. data/lib/beaker/subcommand.rb +13 -8
  37. data/lib/beaker/tasks/quick_start.rb +2 -2
  38. data/lib/beaker/version.rb +1 -1
  39. data/spec/beaker/host/unix/exec_spec.rb +1 -1
  40. data/spec/beaker/host_prebuilt_steps_spec.rb +1 -0
  41. data/spec/beaker/options/hosts_file_parser_spec.rb +15 -7
  42. data/spec/beaker/shared/error_handler_spec.rb +2 -2
  43. data/spec/beaker/shared/repetition_spec.rb +2 -2
  44. data/spec/beaker/subcommand_spec.rb +82 -30
  45. data/spec/helpers.rb +1 -1
  46. data/spec/mock_fission.rb +4 -4
  47. data/spec/mock_vsphere.rb +5 -5
  48. data/spec/mock_vsphere_helper.rb +1 -1
  49. metadata +2 -2
@@ -1,8 +1,8 @@
1
1
  # How To Upgrade from 2.y to 3.0
2
2
 
3
3
  This is a guide detailing all the issues to be aware of, and to help people make
4
- any changes that you might need to move from beaker 2.y to 3.0. To test out
5
- beaker 3.0.0, we recommend implementing the strategy outlined [here](test_arbitrary_beaker_versions.md)
4
+ any changes that you might need to move from beaker 2.y to 3.0. To test out
5
+ beaker 3.0.0, we recommend implementing the strategy outlined [here](test_arbitrary_beaker_versions.md)
6
6
  to ensure this new major release does not break your existing testing.
7
7
 
8
8
  ## Ruby version 1.9.3 no longer supported
@@ -39,10 +39,10 @@ doc for info on how to do this.
39
39
 
40
40
  ## EPEL package update
41
41
 
42
- In beaker < 3.0.0, the epel package names had hardcoded defaults listed in the
42
+ In beaker < 3.0.0, the epel package names had hardcoded defaults listed in the
43
43
  presets default; in beaker >= 3.0.0, beaker utilizes the `release-latest` file
44
44
  provided on epel mirrors for el versions 5, 6, and 7. Since only the latest epel
45
- packages are available on epel mirrors, beaker only supports installation of
45
+ packages are available on epel mirrors, beaker only supports installation of
46
46
  that latest version.
47
47
 
48
48
  ## Solaris and AIX Hypervisors removed
@@ -22,7 +22,7 @@ If you haven't installed beaker yet, your guide to doing
22
22
  so can be found [here](installation.md).
23
23
 
24
24
  ## Quick Start
25
-
25
+
26
26
  As a completely new beaker user, the
27
27
  [quick start rake tasks doc](quick_start_rake_tasks.md)
28
28
  will take you through getting beaker running for the
@@ -47,7 +47,7 @@ failure modes.
47
47
 
48
48
  The pre-suite is for setting up the Systems Under Test (SUTs) for the testing
49
49
  suite. No surprises here, usually these files are filled with the setup and
50
- installation code needed to verify that the operating assumptions of the
50
+ installation code needed to verify that the operating assumptions of the
51
51
  software being tested are true.
52
52
 
53
53
  Pre-suites, since they’re supposed to contain just setup code, will fail-fast
@@ -16,6 +16,7 @@ module Beaker
16
16
  # the options are parsed and replaced with a new logger based on what is passed
17
17
  # in to configure the logger.
18
18
  @logger = Beaker::Logger.new
19
+ @options = {}
19
20
  end
20
21
 
21
22
  def parse_options
@@ -66,7 +66,7 @@ module Beaker
66
66
  # interface.
67
67
  # @param [String] cmd An command to call.
68
68
  # @param [Hash] env An optional hash of environment variables to be used
69
- # @param [String] pc An optional list of commands to prepend
69
+ # @param [String] pc An optional list of commands to prepend
70
70
  #
71
71
  # @return [String] This returns the fully formed command line invocation.
72
72
  def cmd_line host, cmd = @command, env = @environment, pc = @prepend_cmds
@@ -78,7 +78,7 @@ module Beaker
78
78
  end
79
79
 
80
80
  # This will cause things like `puppet -t -v agent` which is maybe bad.
81
- if host[:platform] =~ /cisco_ios_xr/
81
+ if host[:platform] =~ /cisco_ios_xr/
82
82
  cmd_line_array = [prepend_commands, env_string, cmd, options_string, args_string]
83
83
  else
84
84
  cmd_line_array = [env_string, prepend_commands, cmd, options_string, args_string]
@@ -98,7 +98,7 @@ module Beaker
98
98
  # Assert that the provided string does not match the provided regular expression, can pass optional message
99
99
  # @deprecated This is placed her for backwards compatability for tests that used Test::Unit::Assertions,
100
100
  # http://apidock.com/ruby/Test/Unit/Assertions/assert_no_match
101
- #
101
+ #
102
102
  def assert_no_match(regexp, string, msg=nil)
103
103
  assert_instance_of(Regexp, regexp, "The first argument to assert_no_match should be a Regexp.")
104
104
  msg = message(msg) { "<#{mu_pp(regexp)}> expected to not match\n<#{mu_pp(string)}>" }
@@ -13,7 +13,7 @@ module Beaker
13
13
  # (or range) of integer exit codes that should be considered
14
14
  # acceptable. An error will be thrown if the exit code does not
15
15
  # match one of the values in this list.
16
- # @option opts [Boolean] :accept_all_exit_codes (false) Consider all
16
+ # @option opts [Boolean] :accept_all_exit_codes (false) Consider all
17
17
  # exit codes as passing.
18
18
  # @option opts [Boolean] :dry_run (false) Do not actually execute any
19
19
  # commands on the SUT
@@ -72,7 +72,7 @@ module Beaker
72
72
  PE_DEFAULTS[platform].each_pair do |key, val|
73
73
  host[key] = val
74
74
  end
75
- # add the type and group here for backwards compatability
75
+ # add the type and group here for backwards compatability
76
76
  if host['platform'] =~ /windows/
77
77
  host['group'] = 'Administrators'
78
78
  else
@@ -161,7 +161,7 @@ module Beaker
161
161
  end
162
162
 
163
163
  #Create a new role method for a given arbitrary role name. Makes it possible to be able to run
164
- #commands without having to refer to role by String or Symbol. Will not add a new method
164
+ #commands without having to refer to role by String or Symbol. Will not add a new method
165
165
  #definition if the name is already in use.
166
166
  # @param [String, Symbol, Array[String,Symbol]] role The role that you wish to create a definition for, either a String
167
167
  # Symbol or an Array of Strings or Symbols.
@@ -68,7 +68,7 @@ module Beaker
68
68
 
69
69
  set_current_step_name(step_name)
70
70
  # Here we prompt the user to tell us if the step passed or failed
71
- loop do
71
+ loop do
72
72
  input = Readline.readline('Did this step pass, Y/n? ', true).squeeze(" ").strip.downcase
73
73
  if %w(y yes).include?(input)
74
74
  break
@@ -102,7 +102,7 @@ module Beaker
102
102
  def manual_test manual_test_name, &block
103
103
  if(@options.has_key?(:exec_manual_tests) && @options[:exec_manual_tests] == true)
104
104
  # here the option is set so we run the test as normal
105
- test_name manual_test_name, &block
105
+ test_name manual_test_name, &block
106
106
  else
107
107
  # here no option was set so we log the test name and skip it
108
108
  test_name manual_test_name
@@ -119,15 +119,15 @@ module Cisco
119
119
  # this will be raised with the appropriate message
120
120
  def validate_setup
121
121
  msg = nil
122
- if self[:platform] =~ /cisco_nexus/
122
+ if self[:platform] =~ /cisco_nexus/
123
123
  if !self[:vrf]
124
- msg = 'Cisco Nexus hosts must be provided with a :vrf value.'
124
+ msg = 'Cisco Nexus hosts must be provided with a :vrf value.'
125
125
  end
126
126
  if !self[:user]
127
127
  msg = 'Cisco hosts must be provided with a :user value'
128
128
  end
129
129
  end
130
- if self[:platform] =~ /cisco_ios_xr/
130
+ if self[:platform] =~ /cisco_ios_xr/
131
131
  if !self[:user]
132
132
  msg = 'Cisco hosts must be provided with a :user value'
133
133
  end
@@ -30,7 +30,7 @@ module Mac::Exec
30
30
  # Checks if selinux is enabled
31
31
  # selinux is not availble on OS X
32
32
  #
33
- # @return [Boolean] false
33
+ # @return [Boolean] false
34
34
  def selinux_enabled?()
35
35
  false
36
36
  end
@@ -113,7 +113,7 @@ module Windows::Exec
113
113
  #
114
114
  # @return [Boolean]
115
115
  def cygwin_installed?
116
- output = exec(Beaker::Command.new('cygcheck --check-setup cygwin'), :accept_all_exit_codes => true).stdout
116
+ output = exec(Beaker::Command.new('cygcheck --check-setup cygwin'), :accept_all_exit_codes => true).stdout
117
117
  return true if output.match(/cygwin/) && output.match(/OK/)
118
118
  false
119
119
  end
@@ -432,6 +432,9 @@ module Beaker
432
432
  elsif host['platform'] =~ /solaris-11/
433
433
  host.exec(Command.new("if grep \"root::::type=role\" /etc/user_attr; then sudo rolemod -K type=normal root; else echo \"root user already type=normal\"; fi"), {:pty => true} )
434
434
  host.exec(Command.new("sudo gsed -i -e 's/PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config"), {:pty => true} )
435
+ elsif host['platform'] =~ /f5/
436
+ #interacting with f5 should using tmsh
437
+ logger.warn("Attempting to enable root login non-supported platform: #{host.name}: #{host['platform']}")
435
438
  elsif host.is_cygwin?
436
439
  host.exec(Command.new("sed -ri 's/^#?PermitRootLogin /PermitRootLogin yes/' /etc/sshd_config"), {:pty => true})
437
440
  elsif host.is_powershell?
@@ -14,12 +14,17 @@ module Beaker
14
14
  # @raise [ArgumentError] Raises if hosts_file_path is not a valid YAML file
15
15
  # @raise [Errno::ENOENT] File not found error: hosts_file doesn't exist
16
16
  def self.parse_hosts_file(hosts_file_path = nil)
17
+ require 'erb'
18
+
17
19
  host_options = new_host_options
18
20
  return host_options unless hosts_file_path
19
21
  error_message = "#{hosts_file_path} is not a valid YAML file\n\t"
20
22
  host_options = self.merge_hosts_yaml( host_options, error_message ) {
21
23
  hosts_file_path = File.expand_path( hosts_file_path )
22
- YAML.load_file( hosts_file_path )
24
+
25
+ raise "#{hosts_file_path} is not a valid path" unless File.exist?(hosts_file_path)
26
+
27
+ YAML.load(ERB.new(File.read(hosts_file_path), nil, '-').result(binding))
23
28
  }
24
29
  fix_roles_array( host_options )
25
30
  end
@@ -31,11 +36,13 @@ module Beaker
31
36
  # @return [OptionsHash] Contents of the hosts file as an OptionsHash
32
37
  # @raise [ArgumentError] If hosts_def_yaml is not a valid YAML string
33
38
  def self.parse_hosts_string(hosts_def_yaml = nil)
39
+ require 'erb'
40
+
34
41
  host_options = new_host_options
35
42
  return host_options unless hosts_def_yaml
36
43
  error_message = "#{hosts_def_yaml}\nis not a valid YAML string\n\t"
37
44
  host_options = self.merge_hosts_yaml( host_options, error_message ) {
38
- YAML.load( hosts_def_yaml )
45
+ YAML.load(ERB.new(hosts_def_yaml, nil, '-').result(binding))
39
46
  }
40
47
  fix_roles_array( host_options )
41
48
  end
@@ -171,7 +171,7 @@ module Beaker
171
171
 
172
172
  # Update the @attribution hash with the source of each key in the options_hash
173
173
  #
174
- # @param [Hash] options_hash Options hash
174
+ # @param [Hash] options_hash Options hash
175
175
  # @param [String] source Where the options were specified
176
176
  # @return [Hash] hash Hash of sources for each key
177
177
  def tag_sources(options_hash, source)
@@ -3,25 +3,25 @@ module Beaker
3
3
  # Methods for parsing options.
4
4
  module OptionsResolver
5
5
  # parses local and global options to determine if a particular mode should
6
- # be run in parallel. typically, local_options will specify a true/false
6
+ # be run in parallel. typically, local_options will specify a true/false
7
7
  # value, while global_options will specify an array of mode names that should
8
8
  # be run in parallel. the value specified in local_options will take precedence
9
- # over the values specified in global_options.
9
+ # over the values specified in global_options.
10
10
  # @param [Hash] local_options local options for running in parallel
11
11
  # @option local_options [Boolean] :run_in_parallel flag for running in parallel
12
12
  # @param [Hash] global_options global options for running in parallel
13
13
  # @option global_options [Array<String>] :run_in_parallel list of modes to run in parallel
14
14
  # @param [String] mode the mode we want to query global_options for
15
- # @return [Boolean] true if the specified mode is in global_options and :run_in_parallel in local_options is not false,
15
+ # @return [Boolean] true if the specified mode is in global_options and :run_in_parallel in local_options is not false,
16
16
  # or if :run_in_parallel in local_options is true, false otherwise
17
17
  # @example
18
- # run_in_parallel?({:run_in_parallel => true})
18
+ # run_in_parallel?({:run_in_parallel => true})
19
19
  # -> will return true
20
20
  #
21
- # run_in_parallel?({:run_in_parallel => true}, {:run_in_parallel => ['install','configure']}, 'install')
21
+ # run_in_parallel?({:run_in_parallel => true}, {:run_in_parallel => ['install','configure']}, 'install')
22
22
  # -> will return true
23
23
  #
24
- # run_in_parallel?({:run_in_parallel => false}, {:run_in_parallel => ['install','configure']}, 'install')
24
+ # run_in_parallel?({:run_in_parallel => false}, {:run_in_parallel => ['install','configure']}, 'install')
25
25
  # -> will return false
26
26
  def run_in_parallel?(local_options=nil, global_options=nil, mode=nil)
27
27
  run_in_parallel = local_options[:run_in_parallel] unless local_options.nil?
@@ -6,6 +6,7 @@ module Beaker
6
6
  class Subcommand < Thor
7
7
  SubcommandUtil = Beaker::Subcommands::SubcommandUtil
8
8
 
9
+ attr_reader :cli
9
10
 
10
11
  def initialize(*args)
11
12
  super
@@ -171,8 +172,9 @@ module Beaker
171
172
  end
172
173
 
173
174
  beaker_suites = [:pre_suite, :tests, :post_suite, :pre_cleanup]
174
-
175
- if Pathname(resource).exist?
175
+ resources = resource.split(',')
176
+ paths = resources.map { |r| Pathname(r) }
177
+ if paths.all?(&:exist?)
176
178
  # If we determine the resource is a valid file resource, then we empty
177
179
  # all the suites and run that file resource in the tests suite. In the
178
180
  # future, when we have the ability to have custom suites, we should change
@@ -180,12 +182,14 @@ module Beaker
180
182
  beaker_suites.each do |suite|
181
183
  @cli.options[suite] = []
182
184
  end
183
- if Pathname(resource).directory?
184
- @cli.options[:tests] = Dir.glob("#{Pathname(resource)}/**/*.rb")
185
- else
186
- @cli.options[:tests] = [Pathname(resource).to_s]
187
- end
188
- elsif resource.match(/pre-suite|tests|post-suite|pre-cleanup/)
185
+ @cli.options[:tests] = paths.map do |path|
186
+ if path.directory?
187
+ Dir.glob("#{path}/**/*.rb")
188
+ else
189
+ path.to_s
190
+ end
191
+ end.flatten
192
+ elsif resources.all? { |r| r =~ /^(pre-suite|tests|post-suite|pre-cleanup)$/ }
189
193
  # The regex match here is loose so that users can supply multiple suites,
190
194
  # such as `beaker exec pre-suite,tests`.
191
195
  beaker_suites.each do |suite|
@@ -194,6 +198,7 @@ module Beaker
194
198
  else
195
199
  raise ArgumentError, "Unable to parse #{resource} with beaker exec"
196
200
  end
201
+
197
202
  @cli.execute!
198
203
  end
199
204
 
@@ -50,11 +50,11 @@ namespace :beaker_quickstart do
50
50
 
51
51
  desc 'Generate Default Smoke Test'
52
52
  task :gen_smoke_test do
53
- smoke_test_file = "acceptance/setup/default_smoke_test.rb"
53
+ smoke_test_file = "acceptance/tests/default_smoke_test.rb"
54
54
  FileUtils.mkdir_p('acceptance/tests') # -p ignores when dir already exists
55
55
  if !File.exist?(smoke_test_file) then
56
56
  puts "Writing default smoke test to file - #{smoke_test_file}"
57
- File.open("acceptance/tests/default_smoke_test.rb", 'w') do |fh|
57
+ File.open(smoke_test_file, 'w') do |fh|
58
58
  fh.print("test_name 'puppet install smoketest' do
59
59
  step 'puppet install smoketest: verify \\'puppet help\\' can be successfully called on
60
60
  all hosts' do
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '3.34.0'
3
+ STRING = '3.35.0'
4
4
  end
5
5
  end
@@ -102,7 +102,7 @@ module Beaker
102
102
  describe '#ssh_service_restart' do
103
103
  PlatformHelpers::SYSTEMDPLATFORMS.each do |platform|
104
104
  it "calls the correct command for #{platform}" do
105
- opts['platform'] = platform
105
+ opts['platform'] = platform
106
106
  expect(instance).to receive(:exec)
107
107
  expect(Beaker::Command).to receive(:new).with("systemctl restart sshd.service")
108
108
  expect{instance.ssh_service_restart}.to_not raise_error
@@ -38,6 +38,7 @@ describe Beaker do
38
38
  end
39
39
  end
40
40
 
41
+ it_should_behave_like 'enables_root_login', 'f5', []
41
42
  # Non-cygwin Windows
42
43
  it_should_behave_like 'enables_root_login', 'pswindows', [], false
43
44
 
@@ -28,14 +28,14 @@ module Beaker
28
28
 
29
29
  it "raises an error on no file found" do
30
30
  FakeFS.deactivate!
31
- expect{parser.parse_hosts_file("not a valid path")}.to raise_error(Errno::ENOENT)
31
+ expect{parser.parse_hosts_file("not a valid path")}.to raise_error(/is not a valid path/)
32
32
  end
33
33
 
34
34
  it "raises an error on bad yaml file" do
35
35
  FakeFS.deactivate!
36
- allow( YAML ).to receive(:load_file) { raise Psych::SyntaxError }
37
- allow( File ).to receive(:exists?).and_return(true)
38
- expect { parser.parse_hosts_file("not a valid path") }.to raise_error(ArgumentError)
36
+ expect( File ).to receive(:exist?).and_return(true)
37
+
38
+ expect { parser.parse_hosts_file("not a valid path") }.to raise_error(Errno::ENOENT)
39
39
  end
40
40
 
41
41
  it 'returns a #new_host_options hash if given no arguments' do
@@ -43,12 +43,20 @@ module Beaker
43
43
  expect( host_options ).to be === parser.new_host_options
44
44
  end
45
45
 
46
- it 'passes a YAML.load_file call through to #merge_hosts_yaml' do
46
+ it 'passes a YAML.load call through to #merge_hosts_yaml' do
47
47
  yaml_string = 'not actually yaml, but that wont matter'
48
- allow( File ).to receive( :expand_path ).with( yaml_string ) { yaml_string }
49
- expect( YAML ).to receive( :load_file ).with( yaml_string )
48
+ expect( File ).to receive( :exist?).and_return(true)
49
+ expect( File ).to receive( :read ).and_return(yaml_string)
50
50
  parser.parse_hosts_file( yaml_string )
51
51
  end
52
+
53
+ it 'processes ERB in the host YAML successfully' do
54
+ yaml_string = '1 plus 2: <%= 1 + 2 %>'
55
+ expect( File ).to receive( :exist?).and_return(true)
56
+ expect( File ).to receive( :read ).and_return(yaml_string)
57
+ beaker_options_hash = parser.parse_hosts_file( yaml_string )
58
+ expect(beaker_options_hash['1 plus 2']).to eq(3)
59
+ end
52
60
  end
53
61
 
54
62
  describe '#parse_hosts_string' do
@@ -19,14 +19,14 @@ module Beaker
19
19
  ex = Exception.new("ArgumentError")
20
20
  allow( ex ).to receive( :backtrace ).and_return(backtrace)
21
21
  mesg = "I'm the extra message"
22
-
22
+
23
23
  backtrace.each_line do |line|
24
24
  expect( logger ).to receive( :error ).with(line)
25
25
  end
26
26
 
27
27
  expect( subject ).to receive( :raise ).once
28
28
 
29
- subject.report_and_raise(logger, ex, mesg)
29
+ subject.report_and_raise(logger, ex, mesg)
30
30
 
31
31
  end
32
32
 
@@ -10,7 +10,7 @@ module Beaker
10
10
 
11
11
  block = double( 'block' )
12
12
  expect( block ).to receive( :exec ).exactly( 5 ).times.and_return( false )
13
-
13
+
14
14
  subject.repeat_for( 5 ) do
15
15
  block.exec
16
16
  end
@@ -21,7 +21,7 @@ module Beaker
21
21
 
22
22
  block = double( 'block' )
23
23
  expect( block ).to receive( :exec ).once.and_return( true )
24
-
24
+
25
25
  subject.repeat_for( 5 ) do
26
26
  block.exec
27
27
  end
@@ -9,9 +9,9 @@ module Beaker
9
9
 
10
10
  context '#initialize' do
11
11
  it 'creates a cli object' do
12
- expect(Beaker::CLI).to receive(:new).once
13
- subcommand
12
+ expect(subcommand.cli).to be
14
13
  end
14
+
15
15
  describe 'File operation initialization for subcommands' do
16
16
  it 'checks to ensure subcommand file resources exist' do
17
17
  expect(FileUtils).to receive(:mkdir_p).with(SubcommandUtil::CONFIG_DIR)
@@ -28,7 +28,6 @@ module Beaker
28
28
  expect(FileUtils).to receive(:touch).with(SubcommandUtil::SUBCOMMAND_STATE)
29
29
  subcommand
30
30
  end
31
-
32
31
  end
33
32
  end
34
33
 
@@ -80,6 +79,9 @@ module Beaker
80
79
 
81
80
  it 'should not error with valid beaker options' do
82
81
  beaker_options_list.each do |option|
82
+ allow_any_instance_of(Beaker::CLI).to receive(:parse_options)
83
+ allow_any_instance_of(Beaker::CLI).to receive(:configured_options).and_return({})
84
+
83
85
  allow(YAML::Store).to receive(:new).with(SubcommandUtil::SUBCOMMAND_STATE).and_return(yaml_store_mock)
84
86
  allow(yaml_store_mock).to receive(:transaction).and_yield
85
87
  allow(yaml_store_mock).to receive(:[]=).with('provisioned', false)
@@ -87,6 +89,7 @@ module Beaker
87
89
  allow_any_instance_of(Beaker::Logger).to receive(:notify).twice
88
90
  expect(SubcommandUtil::SUBCOMMAND_OPTIONS).to receive(:exist?).and_return(true)
89
91
  expect(SubcommandUtil::SUBCOMMAND_STATE).to receive(:exist?).and_return(true)
92
+
90
93
  expect {Beaker::Subcommand.start(['init', '--hosts', 'centos', "--#{option}"])}.to_not output(/ERROR/).to_stderr
91
94
  end
92
95
  end
@@ -103,13 +106,16 @@ module Beaker
103
106
  end
104
107
 
105
108
  context '#init' do
106
- let( :cli ) { subcommand.instance_variable_get(:@cli) }
109
+ let( :cli ) { subcommand.cli }
107
110
  let( :mock_options ) { {:timestamp => 'noon', :other_key => 'cordite'}}
108
111
  let( :yaml_store_mock ) { double('yaml_store_mock') }
109
- it 'calculates options and writes them to disk and deletes the' do
110
- expect(cli).to receive(:parse_options)
112
+
113
+ before :each do
114
+ allow(cli).to receive(:parse_options)
111
115
  allow(cli).to receive(:configured_options).and_return(mock_options)
116
+ end
112
117
 
118
+ it 'calculates options and writes them to disk and deletes the' do
113
119
  allow(File).to receive(:open)
114
120
  allow(YAML::Store).to receive(:new).with(SubcommandUtil::SUBCOMMAND_STATE).and_return(yaml_store_mock)
115
121
  allow(yaml_store_mock).to receive(:transaction).and_yield
@@ -117,13 +123,14 @@ module Beaker
117
123
  subcommand.init
118
124
  expect(mock_options).not_to have_key(:timestamp)
119
125
  end
126
+
120
127
  it 'requires hosts flag' do
121
128
  expect{subcommand.init}.to raise_error(NotImplementedError)
122
129
  end
123
130
  end
124
131
 
125
132
  context '#provision' do
126
- let ( :cli ) { subcommand.instance_variable_get(:@cli) }
133
+ let ( :cli ) { subcommand.cli }
127
134
  let( :yaml_store_mock ) { double('yaml_store_mock') }
128
135
  let ( :host_hash ) { {'mynode.net' => {:name => 'mynode', :platform => Beaker::Platform.new('centos-6-x86_64')}}}
129
136
  let ( :cleaned_hosts ) {double()}
@@ -133,6 +140,7 @@ module Beaker
133
140
  let ( :hosts) {double('hosts')}
134
141
  let ( :hypervisors) {double('hypervisors')}
135
142
  let (:options) {double ('options')}
143
+
136
144
  it 'provisions the host and saves the host info' do
137
145
  expect(YAML::Store).to receive(:new).with(SubcommandUtil::SUBCOMMAND_STATE).and_return(yaml_store_mock)
138
146
  allow(yaml_store_mock).to receive(:[]).and_return(false)
@@ -157,59 +165,103 @@ module Beaker
157
165
  expect(yaml_store_mock).to receive(:[]=).with('provisioned', true)
158
166
  subcommand.provision
159
167
  end
168
+
160
169
  it 'does not allow hosts to be passed' do
161
170
  subcommand.options = {:hosts => "myhost"}
162
171
  expect{subcommand.provision()}.to raise_error(NotImplementedError)
163
172
  end
164
173
  end
165
174
 
166
-
167
175
  context 'exec' do
176
+ before :each do
177
+ allow(subcommand.cli).to receive(:parse_options)
178
+ allow(subcommand.cli).to receive(:initialize_network_manager)
179
+ end
180
+
168
181
  it 'calls execute! when no resource is given' do
169
182
  expect_any_instance_of(Pathname).to_not receive(:directory?)
170
183
  expect_any_instance_of(Pathname).to_not receive(:exist?)
171
- expect_any_instance_of(Beaker::CLI).to receive(:parse_options).once
172
- expect_any_instance_of(Beaker::CLI).to receive(:initialize_network_manager).once
173
- expect_any_instance_of(Beaker::CLI).to receive(:execute!).once
184
+ expect(subcommand.cli).to receive(:execute!).once
174
185
  expect{subcommand.exec}.to_not raise_error
175
186
  end
176
187
 
177
- it 'checks to to see if the resource is a file_resource' do
188
+ it 'allows hard coded suite names to be specified' do
189
+ subcommand.cli.options[:pre_suite] = %w[step1.rb]
190
+ subcommand.cli.options[:post_suite] = %w[step2.rb]
191
+ subcommand.cli.options[:tests] = %w[tests/1.rb]
192
+
193
+ subcommand.exec('pre-suite,tests')
194
+
195
+ expect(subcommand.cli.options[:pre_suite]).to eq(%w[step1.rb])
196
+ expect(subcommand.cli.options[:post_suite]).to eq([])
197
+ expect(subcommand.cli.options[:tests]).to eq(%w[tests/1.rb])
198
+ end
199
+
200
+ it 'errors when a resource is neither a valid file resource or suite name' do
201
+ allow_any_instance_of(Pathname).to receive(:exist?).and_return(false)
202
+ expect{subcommand.exec('blahblahblah')}.to raise_error(ArgumentError)
203
+ end
178
204
 
179
- expect_any_instance_of(Pathname).to receive(:exist?).and_return(true)
180
- expect_any_instance_of(Pathname).to receive(:directory?).and_return(false)
181
- expect_any_instance_of(Beaker::CLI).to receive(:execute!).once
182
- expect{subcommand.exec('resource')}.to_not raise_error
205
+ it 'accepts a tests directory, clearing all other suites' do
206
+ allow_any_instance_of(Pathname).to receive(:exist?).and_return(true)
207
+ allow_any_instance_of(Pathname).to receive(:directory?).and_return(true)
208
+ allow(Dir).to receive(:glob)
209
+ .with('tests/**/*.rb')
210
+ .and_return(%w[tests/a.rb tests/b/c.rb])
211
+
212
+ subcommand.exec('tests')
213
+
214
+ expect(subcommand.cli.options[:pre_suite]).to eq([])
215
+ expect(subcommand.cli.options[:post_suite]).to eq([])
216
+ expect(subcommand.cli.options[:pre_cleanup]).to eq([])
217
+ expect(subcommand.cli.options[:tests]).to eq(%w[tests/a.rb tests/b/c.rb])
183
218
  end
184
219
 
185
- it 'checks to see if the resource is a directory' do
186
- expect_any_instance_of(Pathname).to receive(:exist?).and_return(true)
187
- expect_any_instance_of(Pathname).to receive(:directory?).and_return(true)
188
- expect(Dir).to receive(:glob)
189
- expect_any_instance_of(Beaker::CLI).to receive(:execute!).once
190
- expect{subcommand.exec('resource')}.to_not raise_error
220
+ it 'accepts comma-separated list of tests, clearing all other suites' do
221
+ allow_any_instance_of(Pathname).to receive(:exist?).and_return(true)
222
+ allow_any_instance_of(Pathname).to receive(:file?).and_return(true)
223
+
224
+ subcommand.exec('tests/1.rb,tests/2.rb')
225
+
226
+ expect(subcommand.cli.options[:pre_suite]).to eq([])
227
+ expect(subcommand.cli.options[:post_suite]).to eq([])
228
+ expect(subcommand.cli.options[:pre_cleanup]).to eq([])
229
+ expect(subcommand.cli.options[:tests]).to eq(%w[tests/1.rb tests/2.rb])
191
230
  end
192
231
 
193
- it 'allows a hard coded suite name to be specified' do
232
+ it 'accepts comma-separated list of directories, recursively scanning each' do
233
+ allow_any_instance_of(Pathname).to receive(:exist?).and_return(true)
234
+ allow_any_instance_of(Pathname).to receive(:directory?).and_return(true)
235
+ allow(Dir).to receive(:glob).with('tests/a/**/*.rb').and_return(%w[tests/a/x.rb])
236
+ allow(Dir).to receive(:glob).with('tests/b/**/*.rb').and_return(%w[tests/b/x/y.rb tests/b/x/z.rb])
194
237
 
195
- allow_any_instance_of(Pathname).to receive(:exist?).and_return(false)
196
- expect_any_instance_of(Beaker::CLI).to receive(:execute!).once
197
- expect{subcommand.exec('tests')}.to_not raise_error
238
+ subcommand.exec('tests/a,tests/b')
239
+
240
+ expect(subcommand.cli.options[:tests]).to eq(%w[tests/a/x.rb tests/b/x/y.rb tests/b/x/z.rb])
198
241
  end
199
242
 
200
- it 'errors when a resource is neither a valid file resource or suite name' do
243
+ it 'rejects comma-separated file and suite name' do
201
244
  allow_any_instance_of(Pathname).to receive(:exist?).and_return(false)
202
- expect{subcommand.exec('blahblahblah')}.to raise_error(ArgumentError)
245
+
246
+ expect {
247
+ subcommand.exec('pre-suite,tests/whoops')
248
+ }.to raise_error(ArgumentError, %r{Unable to parse pre-suite,tests/whoops})
203
249
  end
204
250
  end
205
251
 
206
252
  context 'destroy' do
207
- let( :cli ) { subcommand.instance_variable_get(:@cli) }
253
+ let( :cli ) { subcommand.cli }
208
254
  let( :mock_options ) { {:timestamp => 'noon', :other_key => 'cordite'}}
209
255
  let( :yaml_store_mock ) { double('yaml_store_mock') }
256
+ let( :network_manager) {double('network_manager')}
257
+
210
258
  it 'calls destroy and updates the yaml store' do
259
+ allow(cli).to receive(:parse_options)
260
+ allow(cli).to receive(:initialize_network_manager)
261
+ allow(cli).to receive(:network_manager).and_return(network_manager)
262
+ expect(network_manager).to receive(:cleanup)
263
+
211
264
  expect(YAML::Store).to receive(:new).with(SubcommandUtil::SUBCOMMAND_STATE).and_return(yaml_store_mock)
212
- allow(SubcommandUtil).to receive(:cleanup).with(cli).and_return(true)
213
265
  allow(yaml_store_mock).to receive(:transaction).and_yield
214
266
  allow(yaml_store_mock).to receive(:[]).with('provisioned').and_return(true)
215
267
  allow(yaml_store_mock).to receive(:delete).with('provisioned').and_return(true)