puppetlabs_spec_helper 2.6.2 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
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.