puppetlabs_spec_helper 2.6.2 → 2.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 847f2b3b3a676bafcb358cc257baf0cbc9a8600d
4
- data.tar.gz: fa55fb32bbf5445d851105f54c933e18c2a514ae
3
+ metadata.gz: d87d6131bfee20ef40887c452db37042acdab37d
4
+ data.tar.gz: a327772c292504414a79d4fd3784ff82348cad4a
5
5
  SHA512:
6
- metadata.gz: 8b76ba47b2d7f7fd28d2e0b673effb951778475c6653c89c1c67b96c53caf0e076a69baadb0743f945d0f190a14eb77883d52039ae088d7ae04c304bc8b0643f
7
- data.tar.gz: 8b0e09f08ef5febe92178185eb3b24a611f3bf8f8b319df6555a221bb2f0ad828e2f75e4caf94fa31af8bcb6922d1ce95c2575ed8af636cf7e8aae5b28c2e711
6
+ metadata.gz: 6abed6f18fb005b07a38e19c0646716216058d88f425ea1659fd8453ac46bb17cb3135059f4f9deb2f7542609e8348e19121df1d74dc395bfaa94c9a9ec69fd8
7
+ data.tar.gz: c656294475da31a64af15631968a513438291e4f1d1b0e8e3e85d60cb65029ffcfdf63a793d1f2a0982c02706b6f887ed7a0c69d1f30da9d0d7d04d4acdcfb8d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+ ## [2.7.0]
6
+ ### Summary
7
+ Feature release to begin moving away from mocha as a testing (specifically, mocking) framework for modules.
8
+
9
+ ### Added
10
+ - spec/plans/**/*_spec.rb to spec discovery pattern
11
+ - [mocha as default](README.md#mock_with) test framework unless explicitly set by a module
12
+
13
+ ### Fixed
14
+ - parsing for test tiers in beaker rake task
15
+ - module_spec_helper compatibility with mocha 1.5.0
16
+
5
17
  ## [2.6.2]
6
18
  ### Summary
7
19
  A bugfix release to remove dependency on GettextSetup.initialize() in the Rake tasks.
@@ -430,7 +442,8 @@ compatible yet.
430
442
  ### Added
431
443
  * Initial release
432
444
 
433
- [unreleased]: https://github.com/puppetlabs/puppetlabs_spec_helper/compare/v2.6.2...master
445
+ [unreleased]: https://github.com/puppetlabs/puppetlabs_spec_helper/compare/v2.7.0...master
446
+ [2.7.0]: https://github.com/puppetlabs/puppetlabs_spec_helper/compare/v2.6.2...v2.7.0
434
447
  [2.6.2]: https://github.com/puppetlabs/puppetlabs_spec_helper/compare/v2.6.1...v2.6.2
435
448
  [2.6.1]: https://github.com/puppetlabs/puppetlabs_spec_helper/compare/v2.5.1...v2.6.1
436
449
  [2.5.1]: https://github.com/puppetlabs/puppetlabs_spec_helper/compare/v2.5.0...v2.5.1
data/README.md CHANGED
@@ -345,14 +345,14 @@ remembering to duplicate any existing environment variables:
345
345
  env:
346
346
  - FUTURE_PARSER=yes CI_NODE_TOTAL=2 CI_NODE_INDEX=1
347
347
  - FUTURE_PARSER=yes CI_NODE_TOTAL=2 CI_NODE_INDEX=2
348
-
348
+
349
349
  #### Running tests tagged with test tiers
350
- To run tests tagged with risk levels set the ``TEST_TIERS`` environment variable to a comma-separated list of
350
+ To run tests tagged with risk levels set the ``TEST_TIERS`` environment variable to a comma-separated list of
351
351
  the appropriate tiers.
352
352
 
353
- For example: to run tests marked ``tier_high => true`` and ``tier_medium => true`` in the same test run set the
353
+ For example: to run tests marked ``tier_high => true`` and ``tier_medium => true`` in the same test run set the
354
354
  environment variable``TEST_TIERS=high,medium``
355
-
355
+
356
356
  Note, if the ``TEST_TIERS`` environment variable is set to empty string or nil, all tiers will be executed.
357
357
 
358
358
 
@@ -451,7 +451,7 @@ This is related to a registry security setting requiring elevated privileges to
451
451
 
452
452
  Currently, there are two known approaches to get around this problem.
453
453
 
454
- - run your windows shell (cmd) as an Administrator
454
+ - run your windows shell (cmd) as an Administrator
455
455
  or
456
456
  - modify the registry entry settings to allow symbolic links to be created.
457
457
 
@@ -463,5 +463,52 @@ The following links may give you some insight into why...
463
463
 
464
464
  [Microsoft TechNet](https://technet.microsoft.com/en-us/library/cc754077.aspx)
465
465
 
466
+ mock_with
467
+ =========
468
+
469
+ There are two major mocking frameworks in modules test suites today: [mocha](https://rubygems.org/gems/mocha) and [rspec-mocks](https://rubygems.org/gems/rspec-mocks). It is recommended to choose rspec-mocks explicitely by specifying `mock_with` either in your `spec_helper.rb` like so:
470
+
471
+ ```
472
+ RSpec.configure do |c|
473
+ c.mock_with :rspec
474
+ end
475
+ require 'puppetlabs_spec_helper/module_spec_helper'
476
+ ```
477
+
478
+ or by using the PDK's [`mock_with` option in `.sync.yml`](https://github.com/puppetlabs/pdk-templates#specspec_helperrb) and `pdk update`.
479
+
480
+ You can also continue to use mocha by explicitly specifying `:mocha`, following the [mocha documentation](http://gofreerange.com/mocha/docs/).
481
+
482
+ Migration
483
+ ---------
484
+
485
+ To migrate from mocha to rspec-mocks, in many simple cases the following two kinds of changes are all you need:
486
+
487
+ Translate all stubs:
488
+ ```
489
+ context.stubs(:some_method).with(arguments).returns('value')
490
+ ```
491
+ to this:
492
+ ```
493
+ allow(context).to receive(:some_method).with(arguments).and_returns('value')
494
+ ```
495
+
496
+ Translate all expectations:
497
+ ```
498
+ context.expects(:some_method).with(arguments).returns('value')
499
+ ```
500
+ to this:
501
+ ```
502
+ expect(context).to receive(:some_method).with(arguments).and_returns('value')
503
+ ```
504
+
505
+ Rationale
506
+ ---------
507
+
508
+ * As a part of the RSpec project, rspec-mocks integration is better.
509
+ * mocha is extending base ruby objects with its `stubs`, and `expects` methods, polluting the global namespace, with a potential for subtle and gnarly errors.
510
+ * Currently both rspec-mocks and mocha get loaded, leading to test suites that use both.
511
+ * puppetlabs_spec_helper loads and configures mocha unconditionally, causing friction when a different mocking framework is wanted.
512
+ * mocha is an additional dependency to carry.
466
513
 
467
514
  EOF
@@ -26,7 +26,12 @@ RSpec.configure do |c|
26
26
  c.parser = 'future' if ENV['FUTURE_PARSER'] == 'yes'
27
27
 
28
28
  c.before :each do
29
- Puppet.features.stubs(:root?).returns(true)
29
+ if c.mock_framework.framework_name == :rspec
30
+ allow(Puppet.features).to receive(:root?).and_return(true)
31
+ else
32
+ Puppet.features.stubs(:root?).returns(true)
33
+ end
34
+
30
35
  # stringify_facts and trusted_node_data were removed in puppet4
31
36
  if Puppet.version.to_f < 4.0
32
37
  Puppet.settings[:stringify_facts] = false if ENV['STRINGIFY_FACTS'] == 'no'
@@ -3,12 +3,21 @@ require 'puppetlabs_spec_helper/puppetlabs_spec_helper'
3
3
  # Don't want puppet getting the command line arguments for rake or autotest
4
4
  ARGV.clear
5
5
 
6
- # This is needed because we're using mocha with rspec instead of Test::Unit or MiniTest
7
- ENV['MOCHA_OPTIONS']='skip_integration'
8
-
9
6
  require 'puppet'
10
7
  require 'rspec/expectations'
11
- require 'mocha/api'
8
+
9
+ # Detect whether the module is overriding the choice of mocking framework
10
+ # @mock_framework is used since more than seven years, and we need to avoid
11
+ # `mock_framework`'s autoloading to distinguish between the default, and
12
+ # the module's choice.
13
+ # See also below in RSpec.configure
14
+ if RSpec.configuration.instance_variable_get(:@mock_framework).nil?
15
+ # This is needed because we're using mocha with rspec instead of Test::Unit or MiniTest
16
+ ENV['MOCHA_OPTIONS']='skip_integration'
17
+
18
+ # Current versions of RSpec already load this for us, but who knows what's used out there?
19
+ require 'mocha/api'
20
+ end
12
21
 
13
22
  require 'pathname'
14
23
  require 'tmpdir'
@@ -118,8 +127,14 @@ end
118
127
 
119
128
  # And here is where we do the main rspec configuration / setup.
120
129
  RSpec.configure do |config|
121
- # Some modules depend on having mocha set up for them
122
- config.mock_with :mocha
130
+ # Detect whether the module is overriding the choice of mocking framework
131
+ # @mock_framework is used since more than seven years, and we need to avoid
132
+ # `mock_framework`'s autoloading to distinguish between the default, and
133
+ # the module's choice.
134
+ if config.instance_variable_get(:@mock_framework).nil?
135
+ RSpec.warn_deprecation('puppetlabs_spec_helper: defaults `mock_with` to `:mocha`. See https://github.com/puppetlabs/puppetlabs_spec_helper#mock_with to choose a sensible value for you')
136
+ config.mock_with :mocha
137
+ end
123
138
 
124
139
  # determine whether we can use the new API or not, and call the appropriate initializer method.
125
140
  if (defined?(Puppet::Test::TestHelper))
@@ -2,9 +2,10 @@ require 'fileutils'
2
2
  require 'rake'
3
3
  require 'rspec/core/rake_task'
4
4
  require 'tmpdir'
5
- require 'yaml'
6
5
  require 'pathname'
7
6
  require 'puppetlabs_spec_helper/version'
7
+ require 'puppetlabs_spec_helper/tasks/beaker'
8
+ require 'puppetlabs_spec_helper/tasks/fixtures'
8
9
 
9
10
  # optional gems
10
11
  begin
@@ -21,10 +22,9 @@ rescue LoadError
21
22
  # ignore
22
23
  end
23
24
 
24
-
25
25
  task :default => [:help]
26
26
 
27
- pattern = 'spec/{aliases,classes,defines,unit,functions,hosts,integration,type_aliases,types}/**/*_spec.rb'
27
+ pattern = 'spec/{aliases,classes,defines,unit,functions,hosts,integration,plans,type_aliases,types}/**/*_spec.rb'
28
28
 
29
29
  RSpec::Core::RakeTask.new(:spec_standalone) do |t, args|
30
30
  t.rspec_opts = ['--color']
@@ -55,387 +55,6 @@ RSpec::Core::RakeTask.new(:spec_list_json) do |t|
55
55
  t.pattern = pattern
56
56
  end
57
57
 
58
- desc "Run beaker acceptance tests"
59
- RSpec::Core::RakeTask.new(:beaker) do |t|
60
- t.rspec_opts = ['--color']
61
- t.pattern = 'spec/acceptance'
62
- # TEST_TIERS env variable is a comma separated list of tiers to run. e.g. low, medium, high
63
- if ENV['TEST_TIERS']
64
- tiers = '--tag '
65
- test_tiers = ENV['TEST_TIERS'].split(',')
66
- raise 'TEST_TIERS env variable must have at least 1 tier specified. low, medium or high (comma separated).' if test_tiers.count == 0
67
- test_tiers.each do |tier|
68
- tier_to_add = tier.strip
69
- raise "#{tier_to_add} not a valid test tier." unless %w(low medium high).include?(tier_to_add)
70
- tiers += "tier_#{tier_to_add},"
71
- end
72
- tiers = tiers.chomp(',')
73
- t.rspec_opts.push(tiers)
74
- else
75
- puts 'TEST_TIERS env variable not defined. Defaulting to run all tests.'
76
- end
77
- end
78
-
79
- module PuppetlabsSpecHelper::RakeTasks
80
- # This is a helper for the self-symlink entry of fixtures.yml
81
- def source_dir
82
- Dir.pwd
83
- end
84
-
85
- # cache the repositories and return a hash object
86
- def repositories
87
- unless @repositories
88
- @repositories = fixtures('repositories')
89
- end
90
- @repositories
91
- end
92
-
93
- # get the array of Beaker set names
94
- # @return [Array<String>]
95
- def beaker_node_sets
96
- return @beaker_nodes if @beaker_nodes
97
- @beaker_nodes = Dir['spec/acceptance/nodesets/*.yml'].sort.map do |node_set|
98
- node_set.slice!('.yml')
99
- File.basename(node_set)
100
- end
101
- end
102
-
103
- # Use "vagrant ssh" to login to the given node in the node set
104
- # @param set [String] The name of the node set (yml file)
105
- # @param node [String] The name of the node in the set. For multi-node sets.
106
- def vagrant_ssh(set, node = nil)
107
- vagrant_yml_dir = File.join '.vagrant', 'beaker_vagrant_files', "#{set}.yml"
108
- vagrant_file = File.join vagrant_yml_dir, 'Vagrantfile'
109
- unless File.file? vagrant_file
110
- puts "There is no Vagrantfile at: '#{vagrant_file}'. Perhaps, the node is not created or is destroyed."
111
- exit 1
112
- end
113
- Dir.chdir(vagrant_yml_dir) do
114
- command = 'vagrant ssh'
115
- command += " #{node}" if node
116
- # Vagrant is not distributed as a normal gem
117
- # and we should protect it from the current Ruby environment
118
- env = {
119
- 'RUBYLIB' => nil,
120
- 'GEM_PATH' => nil,
121
- 'BUNDLE_BIN_PATH' => nil,
122
- }
123
- system env, command
124
- end
125
- end
126
-
127
- def auto_symlink
128
- { File.basename(Dir.pwd).split('-').last => '#{source_dir}' }
129
- end
130
-
131
- def fixtures(category)
132
- if ENV['FIXTURES_YML']
133
- fixtures_yaml = ENV['FIXTURES_YML']
134
- elsif File.exists?('.fixtures.yml')
135
- fixtures_yaml = '.fixtures.yml'
136
- elsif File.exists?('.fixtures.yaml')
137
- fixtures_yaml = '.fixtures.yaml'
138
- else
139
- fixtures_yaml = false
140
- end
141
-
142
- begin
143
- if fixtures_yaml
144
- fixtures = YAML.load_file(fixtures_yaml) || { 'fixtures' => {} }
145
- else
146
- fixtures = { 'fixtures' => {} }
147
- end
148
- rescue Errno::ENOENT
149
- fail("Fixtures file not found: '#{fixtures_yaml}'")
150
- rescue Psych::SyntaxError => e
151
- fail("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
152
- end
153
-
154
- unless fixtures.include?('fixtures')
155
- # File is non-empty, but does not specify fixtures
156
- fail("No 'fixtures' entries found in '#{fixtures_yaml}'; required")
157
- end
158
-
159
- if fixtures.include? 'defaults'
160
- fixture_defaults = fixtures['defaults']
161
- else
162
- fixture_defaults = {}
163
- end
164
-
165
- fixtures = fixtures['fixtures']
166
-
167
- if fixtures['symlinks'].nil?
168
- fixtures['symlinks'] = auto_symlink
169
- end
170
-
171
- result = {}
172
- if fixtures.include? category and fixtures[category] != nil
173
-
174
- defaults = { "target" => "spec/fixtures/modules" }
175
-
176
- # load defaults from the `.fixtures.yml` `defaults` section
177
- # for the requested category and merge them into my defaults
178
- if fixture_defaults.include? category
179
- defaults = defaults.merge(fixture_defaults[category])
180
- end
181
-
182
- fixtures[category].each do |fixture, opts|
183
- # convert a simple string fixture to a hash, by
184
- # using the string fixture as the `repo` option of the hash.
185
- if opts.instance_of?(String)
186
- opts = { "repo" => opts }
187
- end
188
- # there should be a warning or something if it's not a hash...
189
- if opts.instance_of?(Hash)
190
- # merge our options into the defaults to get the
191
- # final option list
192
- opts = defaults.merge(opts)
193
-
194
- real_target = eval('"'+opts["target"]+'"')
195
- real_source = eval('"'+opts["repo"]+'"')
196
-
197
- result[real_source] = { "target" => File.join(real_target,fixture), "ref" => opts["ref"], "branch" => opts["branch"], "scm" => opts["scm"], "flags" => opts["flags"], "subdir" => opts["subdir"]}
198
- end
199
- end
200
- end
201
- return result
202
- end
203
-
204
- def clone_repo(scm, remote, target, subdir=nil, ref=nil, branch=nil, flags = nil)
205
- args = []
206
- case scm
207
- when 'hg'
208
- args.push('clone')
209
- args.push('-b', branch) if branch
210
- args.push(flags) if flags
211
- args.push(remote, target)
212
- when 'git'
213
- args.push('clone')
214
- args.push('--depth 1') unless ref
215
- args.push('-b', branch) if branch
216
- args.push(flags) if flags
217
- args.push(remote, target)
218
- else
219
- fail "Unfortunately #{scm} is not supported yet"
220
- end
221
- result = system("#{scm} #{args.flatten.join ' '}")
222
- unless File::exists?(target)
223
- fail "Failed to clone #{scm} repository #{remote} into #{target}"
224
- end
225
- result
226
- end
227
-
228
- def revision(scm, target, ref)
229
- args = []
230
- case scm
231
- when 'hg'
232
- args.push('update', '--clean', '-r', ref)
233
- when 'git'
234
- args.push('reset', '--hard', ref)
235
- else
236
- fail "Unfortunately #{scm} is not supported yet"
237
- end
238
- system("cd #{target} && #{scm} #{args.flatten.join ' '}")
239
- end
240
-
241
- def remove_subdirectory(target, subdir)
242
- unless subdir.nil?
243
- Dir.mktmpdir {|tmpdir|
244
- FileUtils.mv(Dir.glob("#{target}/#{subdir}/{.[^\.]*,*}"), tmpdir)
245
- FileUtils.rm_rf("#{target}/#{subdir}")
246
- FileUtils.mv(Dir.glob("#{tmpdir}/{.[^\.]*,*}"), "#{target}")
247
- }
248
- end
249
- end
250
-
251
- # creates a logger so we can log events with certain levels
252
- def logger
253
- unless @logger
254
- require 'logger'
255
- if ENV['ENABLE_LOGGER']
256
- level = Logger::DEBUG
257
- else
258
- level = Logger::INFO
259
- end
260
- @logger = Logger.new(STDERR)
261
- @logger.level = level
262
- end
263
- @logger
264
- end
265
-
266
- def module_working_directory
267
- # The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
268
- # becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
269
- # This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
270
- File.expand_path(ENV['MODULE_WORKING_DIR'] ? ENV['MODULE_WORKING_DIR'] : 'spec/fixtures/work-dir')
271
- end
272
-
273
- # returns the current thread count that is currently active
274
- # a status of false or nil means the thread completed
275
- # so when anything else we count that as a active thread
276
- def current_thread_count(items)
277
- active_threads = items.find_all do |item, opts|
278
- if opts[:thread]
279
- opts[:thread].status
280
- else
281
- false
282
- end
283
- end
284
- logger.debug "Current thread count #{active_threads.count}"
285
- active_threads.count
286
- end
287
-
288
- # returns the max_thread_count
289
- # because we may want to limit ssh or https connections
290
- def max_thread_limit
291
- unless @max_thread_limit
292
- # the default thread count is 10 but can be
293
- # raised by using environment variable MAX_FIXTURE_THREAD_COUNT
294
- if ENV['MAX_FIXTURE_THREAD_COUNT'].to_i > 0
295
- @max_thread_limit = ENV['MAX_FIXTURE_THREAD_COUNT'].to_i
296
- else
297
- @max_thread_limit = 10 # the default
298
- end
299
- end
300
- @max_thread_limit
301
- end
302
-
303
- def check_directory_for_symlinks(dir='.')
304
- dir = Pathname.new(dir) unless dir.is_a?(Pathname)
305
- results = []
306
-
307
- dir.each_child(true) do |child|
308
- if child.symlink?
309
- results << child
310
- elsif child.directory? && child.basename.to_s != '.git'
311
- results.concat(check_directory_for_symlinks(child))
312
- end
313
- end
314
-
315
- results
316
- end
317
- end
318
- include PuppetlabsSpecHelper::RakeTasks
319
-
320
- desc "Create the fixtures directory"
321
- task :spec_prep do
322
- # Ruby only sets File::ALT_SEPARATOR on Windows and Rubys standard library
323
- # uses this to check for Windows
324
- is_windows = !!File::ALT_SEPARATOR
325
- if is_windows
326
- begin
327
- require 'win32/dir'
328
- rescue LoadError
329
- $stderr.puts "win32-dir gem not installed, falling back to executing mklink directly"
330
- end
331
- end
332
-
333
- # git has a race condition creating that directory, that would lead to aborted clone operations
334
- FileUtils::mkdir_p("spec/fixtures/modules")
335
-
336
- repositories.each do |remote, opts|
337
- scm = 'git'
338
- target = opts["target"]
339
- subdir = opts["subdir"]
340
- ref = opts["ref"]
341
- scm = opts["scm"] if opts["scm"]
342
- branch = opts["branch"] if opts["branch"]
343
- flags = opts["flags"]
344
- # get the current active threads that are alive
345
- count = current_thread_count(repositories)
346
- if count < max_thread_limit
347
- logger.debug "New Thread started for #{remote}"
348
- # start up a new thread and store it in the opts hash
349
- opts[:thread] = Thread.new do
350
- clone_repo(scm, remote, target, subdir, ref, branch, flags)
351
- revision(scm, target, ref) if ref
352
- remove_subdirectory(target, subdir) if subdir
353
- end
354
- else
355
- # the last thread started should be the longest wait
356
- item, item_opts = repositories.find_all {|i,o| o.has_key?(:thread)}.last
357
- logger.debug "Waiting on #{item}"
358
- item_opts[:thread].join # wait for the thread to finish
359
- # now that we waited lets try again
360
- redo
361
- end
362
- end
363
-
364
- # wait for all the threads to finish
365
- repositories.each {|remote, opts| opts[:thread].join }
366
-
367
- fixtures("symlinks").each do |target, link|
368
- link = link['target']
369
- unless File.symlink?(link)
370
- logger.info("Creating symlink from #{link} to #{target}")
371
- if is_windows
372
- target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
373
- if Dir.respond_to?(:create_junction)
374
- Dir.create_junction(link, target)
375
- else
376
- system("call mklink /J \"#{link.gsub('/', '\\')}\" \"#{target.gsub('/', '\\')}\"")
377
- end
378
- else
379
- FileUtils::ln_sf(target, link)
380
- end
381
- end
382
- end
383
-
384
- fixtures("forge_modules").each do |remote, opts|
385
- ref = ""
386
- flags = ""
387
- if opts.instance_of?(String)
388
- target = opts
389
- elsif opts.instance_of?(Hash)
390
- target = opts["target"]
391
- ref = " --version #{opts['ref']}" if not opts['ref'].nil?
392
- flags = " #{opts['flags']}" if opts['flags']
393
- end
394
- next if File::exists?(target)
395
-
396
- working_dir = module_working_directory
397
- target_dir = File.expand_path('spec/fixtures/modules')
398
-
399
- command = "puppet module install" + ref + flags + \
400
- " --ignore-dependencies" \
401
- " --force" \
402
- " --module_working_dir \"#{working_dir}\"" \
403
- " --target-dir \"#{target_dir}\" \"#{remote}\""
404
-
405
- unless system(command)
406
- fail "Failed to install module #{remote} to #{target_dir}"
407
- end
408
- end
409
-
410
- FileUtils::mkdir_p("spec/fixtures/manifests")
411
- FileUtils::touch("spec/fixtures/manifests/site.pp")
412
- end
413
-
414
- desc "Clean up the fixtures directory"
415
- task :spec_clean do
416
- fixtures("repositories").each do |remote, opts|
417
- target = opts["target"]
418
- FileUtils::rm_rf(target)
419
- end
420
-
421
- fixtures("forge_modules").each do |remote, opts|
422
- target = opts["target"]
423
- FileUtils::rm_rf(target)
424
- end
425
-
426
- FileUtils::rm_rf(module_working_directory)
427
-
428
- fixtures("symlinks").each do |source, opts|
429
- target = opts["target"]
430
- FileUtils::rm_f(target)
431
- end
432
-
433
- if File.zero?("spec/fixtures/manifests/site.pp")
434
- FileUtils::rm_f("spec/fixtures/manifests/site.pp")
435
- end
436
-
437
- end
438
-
439
58
  desc "Run spec tests and clean the fixtures directory if successful"
440
59
  task :spec do |t, args|
441
60
  begin
@@ -465,38 +84,6 @@ task :parallel_spec do
465
84
  end
466
85
  end
467
86
 
468
- desc "List available beaker nodesets"
469
- task 'beaker:sets' do
470
- beaker_node_sets.each do |set|
471
- puts set
472
- end
473
- end
474
-
475
- # alias for compatibility
476
- task 'beaker_nodes' => 'beaker:sets'
477
-
478
- desc 'Try to use vagrant to login to the Beaker node'
479
- task 'beaker:ssh', [:set, :node] do |_task, args|
480
- set = args[:set] || ENV['BEAKER_set'] || ENV['RS_SET'] || 'default'
481
- node = args[:node]
482
- vagrant_ssh set, node
483
- end
484
-
485
- beaker_node_sets.each do |set|
486
- desc "Run the Beaker acceptance tests for the node set '#{set}'"
487
- task "beaker:#{set}" do
488
- ENV['BEAKER_set'] = set
489
- Rake::Task['beaker'].reenable
490
- Rake::Task['beaker'].invoke
491
- end
492
-
493
- desc "Use vagrant to login to a node from the set '#{set}'"
494
- task "beaker:ssh:#{set}", [:node] do |_task, args|
495
- node = args[:node]
496
- vagrant_ssh set, node
497
- end
498
- end
499
-
500
87
  desc "Build puppet module package"
501
88
  task :build do
502
89
  # This will be deprecated once puppet-module is a face.
@@ -0,0 +1,111 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ module PuppetlabsSpecHelper; end
4
+ module PuppetlabsSpecHelper::Tasks; end
5
+ module PuppetlabsSpecHelper::Tasks::BeakerHelpers
6
+ # This is a helper for the self-symlink entry of fixtures.yml
7
+ def source_dir
8
+ Dir.pwd
9
+ end
10
+
11
+ # cache the repositories and return a hash object
12
+ def repositories
13
+ unless @repositories
14
+ @repositories = fixtures('repositories')
15
+ end
16
+ @repositories
17
+ end
18
+
19
+ # get the array of Beaker set names
20
+ # @return [Array<String>]
21
+ def beaker_node_sets
22
+ return @beaker_nodes if @beaker_nodes
23
+ @beaker_nodes = Dir['spec/acceptance/nodesets/*.yml'].sort.map do |node_set|
24
+ node_set.slice!('.yml')
25
+ File.basename(node_set)
26
+ end
27
+ end
28
+
29
+ # Use "vagrant ssh" to login to the given node in the node set
30
+ # @param set [String] The name of the node set (yml file)
31
+ # @param node [String] The name of the node in the set. For multi-node sets.
32
+ def vagrant_ssh(set, node = nil)
33
+ vagrant_yml_dir = File.join '.vagrant', 'beaker_vagrant_files', "#{set}.yml"
34
+ vagrant_file = File.join vagrant_yml_dir, 'Vagrantfile'
35
+ unless File.file? vagrant_file
36
+ puts "There is no Vagrantfile at: '#{vagrant_file}'. Perhaps, the node is not created or is destroyed."
37
+ exit 1
38
+ end
39
+ Dir.chdir(vagrant_yml_dir) do
40
+ command = 'vagrant ssh'
41
+ command += " #{node}" if node
42
+ # Vagrant is not distributed as a normal gem
43
+ # and we should protect it from the current Ruby environment
44
+ env = {
45
+ 'RUBYLIB' => nil,
46
+ 'GEM_PATH' => nil,
47
+ 'BUNDLE_BIN_PATH' => nil,
48
+ }
49
+ system env, command
50
+ end
51
+ end
52
+ end
53
+ include PuppetlabsSpecHelper::Tasks::BeakerHelpers
54
+
55
+ desc "Run beaker acceptance tests"
56
+ RSpec::Core::RakeTask.new(:beaker) do |t|
57
+ SetupBeaker.setup_beaker(t)
58
+ end
59
+
60
+ class SetupBeaker
61
+ def self.setup_beaker(t)
62
+ t.rspec_opts = ['--color']
63
+ t.pattern = 'spec/acceptance'
64
+ # TEST_TIERS env variable is a comma separated list of tiers to run. e.g. low, medium, high
65
+ if ENV['TEST_TIERS']
66
+ test_tiers = ENV['TEST_TIERS'].split(',')
67
+ raise 'TEST_TIERS env variable must have at least 1 tier specified. low, medium or high (comma separated).' if test_tiers.count == 0
68
+ test_tiers.each do |tier|
69
+ tier_to_add = tier.strip.downcase
70
+ raise "#{tier_to_add} not a valid test tier." unless %w(low medium high).include?(tier_to_add)
71
+ tiers = "--tag tier_#{tier_to_add}"
72
+ t.rspec_opts.push(tiers)
73
+ end
74
+ else
75
+ puts 'TEST_TIERS env variable not defined. Defaulting to run all tests.'
76
+ end
77
+ return t
78
+ end
79
+ end
80
+
81
+ desc "List available beaker nodesets"
82
+ task 'beaker:sets' do
83
+ beaker_node_sets.each do |set|
84
+ puts set
85
+ end
86
+ end
87
+
88
+ # alias for compatibility
89
+ task 'beaker_nodes' => 'beaker:sets'
90
+
91
+ desc 'Try to use vagrant to login to the Beaker node'
92
+ task 'beaker:ssh', [:set, :node] do |_task, args|
93
+ set = args[:set] || ENV['BEAKER_set'] || ENV['RS_SET'] || 'default'
94
+ node = args[:node]
95
+ vagrant_ssh set, node
96
+ end
97
+
98
+ beaker_node_sets.each do |set|
99
+ desc "Run the Beaker acceptance tests for the node set '#{set}'"
100
+ task "beaker:#{set}" do
101
+ ENV['BEAKER_set'] = set
102
+ Rake::Task['beaker'].reenable
103
+ Rake::Task['beaker'].invoke
104
+ end
105
+
106
+ desc "Use vagrant to login to a node from the set '#{set}'"
107
+ task "beaker:ssh:#{set}", [:node] do |_task, args|
108
+ node = args[:node]
109
+ vagrant_ssh set, node
110
+ end
111
+ end
@@ -0,0 +1,328 @@
1
+ require 'yaml'
2
+
3
+ module PuppetlabsSpecHelper; end
4
+ module PuppetlabsSpecHelper::Tasks; end
5
+ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
6
+ # This is a helper for the self-symlink entry of fixtures.yml
7
+ def source_dir
8
+ Dir.pwd
9
+ end
10
+
11
+ # cache the repositories and return a hash object
12
+ def repositories
13
+ unless @repositories
14
+ @repositories = fixtures('repositories')
15
+ end
16
+ @repositories
17
+ end
18
+
19
+ def auto_symlink
20
+ { File.basename(Dir.pwd).split('-').last => '#{source_dir}' }
21
+ end
22
+
23
+ def fixtures(category)
24
+ if ENV['FIXTURES_YML']
25
+ fixtures_yaml = ENV['FIXTURES_YML']
26
+ elsif File.exists?('.fixtures.yml')
27
+ fixtures_yaml = '.fixtures.yml'
28
+ elsif File.exists?('.fixtures.yaml')
29
+ fixtures_yaml = '.fixtures.yaml'
30
+ else
31
+ fixtures_yaml = false
32
+ end
33
+
34
+ begin
35
+ if fixtures_yaml
36
+ fixtures = YAML.load_file(fixtures_yaml) || { 'fixtures' => {} }
37
+ else
38
+ fixtures = { 'fixtures' => {} }
39
+ end
40
+ rescue Errno::ENOENT
41
+ fail("Fixtures file not found: '#{fixtures_yaml}'")
42
+ rescue Psych::SyntaxError => e
43
+ fail("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
44
+ end
45
+
46
+ unless fixtures.include?('fixtures')
47
+ # File is non-empty, but does not specify fixtures
48
+ fail("No 'fixtures' entries found in '#{fixtures_yaml}'; required")
49
+ end
50
+
51
+ if fixtures.include? 'defaults'
52
+ fixture_defaults = fixtures['defaults']
53
+ else
54
+ fixture_defaults = {}
55
+ end
56
+
57
+ fixtures = fixtures['fixtures']
58
+
59
+ if fixtures['symlinks'].nil?
60
+ fixtures['symlinks'] = auto_symlink
61
+ end
62
+
63
+ result = {}
64
+ if fixtures.include? category and fixtures[category] != nil
65
+
66
+ defaults = { "target" => "spec/fixtures/modules" }
67
+
68
+ # load defaults from the `.fixtures.yml` `defaults` section
69
+ # for the requested category and merge them into my defaults
70
+ if fixture_defaults.include? category
71
+ defaults = defaults.merge(fixture_defaults[category])
72
+ end
73
+
74
+ fixtures[category].each do |fixture, opts|
75
+ # convert a simple string fixture to a hash, by
76
+ # using the string fixture as the `repo` option of the hash.
77
+ if opts.instance_of?(String)
78
+ opts = { "repo" => opts }
79
+ end
80
+ # there should be a warning or something if it's not a hash...
81
+ if opts.instance_of?(Hash)
82
+ # merge our options into the defaults to get the
83
+ # final option list
84
+ opts = defaults.merge(opts)
85
+
86
+ real_target = eval('"'+opts["target"]+'"')
87
+ real_source = eval('"'+opts["repo"]+'"')
88
+
89
+ result[real_source] = { "target" => File.join(real_target,fixture), "ref" => opts["ref"], "branch" => opts["branch"], "scm" => opts["scm"], "flags" => opts["flags"], "subdir" => opts["subdir"]}
90
+ end
91
+ end
92
+ end
93
+ return result
94
+ end
95
+
96
+ def clone_repo(scm, remote, target, subdir=nil, ref=nil, branch=nil, flags = nil)
97
+ args = []
98
+ case scm
99
+ when 'hg'
100
+ args.push('clone')
101
+ args.push('-b', branch) if branch
102
+ args.push(flags) if flags
103
+ args.push(remote, target)
104
+ when 'git'
105
+ args.push('clone')
106
+ args.push('--depth 1') unless ref
107
+ args.push('-b', branch) if branch
108
+ args.push(flags) if flags
109
+ args.push(remote, target)
110
+ else
111
+ fail "Unfortunately #{scm} is not supported yet"
112
+ end
113
+ result = system("#{scm} #{args.flatten.join ' '}")
114
+ unless File::exists?(target)
115
+ fail "Failed to clone #{scm} repository #{remote} into #{target}"
116
+ end
117
+ result
118
+ end
119
+
120
+ def revision(scm, target, ref)
121
+ args = []
122
+ case scm
123
+ when 'hg'
124
+ args.push('update', '--clean', '-r', ref)
125
+ when 'git'
126
+ args.push('reset', '--hard', ref)
127
+ else
128
+ fail "Unfortunately #{scm} is not supported yet"
129
+ end
130
+ system("cd #{target} && #{scm} #{args.flatten.join ' '}")
131
+ end
132
+
133
+ def remove_subdirectory(target, subdir)
134
+ unless subdir.nil?
135
+ Dir.mktmpdir {|tmpdir|
136
+ FileUtils.mv(Dir.glob("#{target}/#{subdir}/{.[^\.]*,*}"), tmpdir)
137
+ FileUtils.rm_rf("#{target}/#{subdir}")
138
+ FileUtils.mv(Dir.glob("#{tmpdir}/{.[^\.]*,*}"), "#{target}")
139
+ }
140
+ end
141
+ end
142
+
143
+ # creates a logger so we can log events with certain levels
144
+ def logger
145
+ unless @logger
146
+ require 'logger'
147
+ if ENV['ENABLE_LOGGER']
148
+ level = Logger::DEBUG
149
+ else
150
+ level = Logger::INFO
151
+ end
152
+ @logger = Logger.new(STDERR)
153
+ @logger.level = level
154
+ end
155
+ @logger
156
+ end
157
+
158
+ def module_working_directory
159
+ # The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
160
+ # becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
161
+ # This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
162
+ File.expand_path(ENV['MODULE_WORKING_DIR'] ? ENV['MODULE_WORKING_DIR'] : 'spec/fixtures/work-dir')
163
+ end
164
+
165
+ # returns the current thread count that is currently active
166
+ # a status of false or nil means the thread completed
167
+ # so when anything else we count that as a active thread
168
+ def current_thread_count(items)
169
+ active_threads = items.find_all do |item, opts|
170
+ if opts[:thread]
171
+ opts[:thread].status
172
+ else
173
+ false
174
+ end
175
+ end
176
+ logger.debug "Current thread count #{active_threads.count}"
177
+ active_threads.count
178
+ end
179
+
180
+ # returns the max_thread_count
181
+ # because we may want to limit ssh or https connections
182
+ def max_thread_limit
183
+ unless @max_thread_limit
184
+ # the default thread count is 10 but can be
185
+ # raised by using environment variable MAX_FIXTURE_THREAD_COUNT
186
+ if ENV['MAX_FIXTURE_THREAD_COUNT'].to_i > 0
187
+ @max_thread_limit = ENV['MAX_FIXTURE_THREAD_COUNT'].to_i
188
+ else
189
+ @max_thread_limit = 10 # the default
190
+ end
191
+ end
192
+ @max_thread_limit
193
+ end
194
+
195
+ def check_directory_for_symlinks(dir='.')
196
+ dir = Pathname.new(dir) unless dir.is_a?(Pathname)
197
+ results = []
198
+
199
+ dir.each_child(true) do |child|
200
+ if child.symlink?
201
+ results << child
202
+ elsif child.directory? && child.basename.to_s != '.git'
203
+ results.concat(check_directory_for_symlinks(child))
204
+ end
205
+ end
206
+
207
+ results
208
+ end
209
+ end
210
+ include PuppetlabsSpecHelper::Tasks::FixtureHelpers
211
+
212
+ desc "Create the fixtures directory"
213
+ task :spec_prep do
214
+ # Ruby only sets File::ALT_SEPARATOR on Windows and Rubys standard library
215
+ # uses this to check for Windows
216
+ is_windows = !!File::ALT_SEPARATOR
217
+ if is_windows
218
+ begin
219
+ require 'win32/dir'
220
+ rescue LoadError
221
+ $stderr.puts "win32-dir gem not installed, falling back to executing mklink directly"
222
+ end
223
+ end
224
+
225
+ # git has a race condition creating that directory, that would lead to aborted clone operations
226
+ FileUtils::mkdir_p("spec/fixtures/modules")
227
+
228
+ repositories.each do |remote, opts|
229
+ scm = 'git'
230
+ target = opts["target"]
231
+ subdir = opts["subdir"]
232
+ ref = opts["ref"]
233
+ scm = opts["scm"] if opts["scm"]
234
+ branch = opts["branch"] if opts["branch"]
235
+ flags = opts["flags"]
236
+ # get the current active threads that are alive
237
+ count = current_thread_count(repositories)
238
+ if count < max_thread_limit
239
+ logger.debug "New Thread started for #{remote}"
240
+ # start up a new thread and store it in the opts hash
241
+ opts[:thread] = Thread.new do
242
+ clone_repo(scm, remote, target, subdir, ref, branch, flags)
243
+ revision(scm, target, ref) if ref
244
+ remove_subdirectory(target, subdir) if subdir
245
+ end
246
+ else
247
+ # the last thread started should be the longest wait
248
+ item, item_opts = repositories.find_all {|i,o| o.has_key?(:thread)}.last
249
+ logger.debug "Waiting on #{item}"
250
+ item_opts[:thread].join # wait for the thread to finish
251
+ # now that we waited lets try again
252
+ redo
253
+ end
254
+ end
255
+
256
+ # wait for all the threads to finish
257
+ repositories.each {|remote, opts| opts[:thread].join }
258
+
259
+ fixtures("symlinks").each do |target, link|
260
+ link = link['target']
261
+ unless File.symlink?(link)
262
+ logger.info("Creating symlink from #{link} to #{target}")
263
+ if is_windows
264
+ target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
265
+ if Dir.respond_to?(:create_junction)
266
+ Dir.create_junction(link, target)
267
+ else
268
+ system("call mklink /J \"#{link.gsub('/', '\\')}\" \"#{target.gsub('/', '\\')}\"")
269
+ end
270
+ else
271
+ FileUtils::ln_sf(target, link)
272
+ end
273
+ end
274
+ end
275
+
276
+ fixtures("forge_modules").each do |remote, opts|
277
+ ref = ""
278
+ flags = ""
279
+ if opts.instance_of?(String)
280
+ target = opts
281
+ elsif opts.instance_of?(Hash)
282
+ target = opts["target"]
283
+ ref = " --version #{opts['ref']}" if not opts['ref'].nil?
284
+ flags = " #{opts['flags']}" if opts['flags']
285
+ end
286
+ next if File::exists?(target)
287
+
288
+ working_dir = module_working_directory
289
+ target_dir = File.expand_path('spec/fixtures/modules')
290
+
291
+ command = "puppet module install" + ref + flags + \
292
+ " --ignore-dependencies" \
293
+ " --force" \
294
+ " --module_working_dir \"#{working_dir}\"" \
295
+ " --target-dir \"#{target_dir}\" \"#{remote}\""
296
+
297
+ unless system(command)
298
+ fail "Failed to install module #{remote} to #{target_dir}"
299
+ end
300
+ end
301
+
302
+ FileUtils::mkdir_p("spec/fixtures/manifests")
303
+ FileUtils::touch("spec/fixtures/manifests/site.pp")
304
+ end
305
+
306
+ desc "Clean up the fixtures directory"
307
+ task :spec_clean do
308
+ fixtures("repositories").each do |remote, opts|
309
+ target = opts["target"]
310
+ FileUtils::rm_rf(target)
311
+ end
312
+
313
+ fixtures("forge_modules").each do |remote, opts|
314
+ target = opts["target"]
315
+ FileUtils::rm_rf(target)
316
+ end
317
+
318
+ FileUtils::rm_rf(module_working_directory)
319
+
320
+ fixtures("symlinks").each do |source, opts|
321
+ target = opts["target"]
322
+ FileUtils::rm_f(target)
323
+ end
324
+
325
+ if File.zero?("spec/fixtures/manifests/site.pp")
326
+ FileUtils::rm_f("spec/fixtures/manifests/site.pp")
327
+ end
328
+ end
@@ -1,5 +1,5 @@
1
1
  module PuppetlabsSpecHelper
2
- VERSION = "2.6.2"
2
+ VERSION = "2.7.0"
3
3
 
4
4
  # compat for pre-1.2.0 users; deprecated
5
5
  module Version
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{Standard tasks and configuration for module spec tests.}
13
13
  spec.description = %q{Contains rake tasks and a standard spec_helper for running spec tests on puppet modules.}
14
14
  spec.homepage = "http://github.com/puppetlabs/puppetlabs_spec_helper"
15
+ spec.license = "Apache-2.0"
15
16
 
16
17
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
18
  spec.bindir = "exe"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppetlabs_spec_helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.2
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet, Inc.
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-02-12 00:00:00.000000000 Z
12
+ date: 2018-04-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mocha
@@ -192,12 +192,15 @@ files:
192
192
  - lib/puppetlabs_spec_helper/puppetlabs_spec/puppet_internals.rb
193
193
  - lib/puppetlabs_spec_helper/puppetlabs_spec_helper.rb
194
194
  - lib/puppetlabs_spec_helper/rake_tasks.rb
195
+ - lib/puppetlabs_spec_helper/tasks/beaker.rb
196
+ - lib/puppetlabs_spec_helper/tasks/fixtures.rb
195
197
  - lib/puppetlabs_spec_helper/version.rb
196
198
  - puppet_spec_helper.rb
197
199
  - puppetlabs_spec_helper.gemspec
198
200
  - puppetlabs_spec_helper.rb
199
201
  homepage: http://github.com/puppetlabs/puppetlabs_spec_helper
200
- licenses: []
202
+ licenses:
203
+ - Apache-2.0
201
204
  metadata: {}
202
205
  post_install_message:
203
206
  rdoc_options: []
@@ -215,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
218
  version: '0'
216
219
  requirements: []
217
220
  rubyforge_project:
218
- rubygems_version: 2.5.1
221
+ rubygems_version: 2.6.8
219
222
  signing_key:
220
223
  specification_version: 4
221
224
  summary: Standard tasks and configuration for module spec tests.