puppetlabs_spec_helper 2.6.2 → 4.0.1

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.
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathspec'
4
+
5
+ module PuppetlabsSpecHelper; end
6
+ module PuppetlabsSpecHelper::Tasks; end
7
+
8
+ class PuppetlabsSpecHelper::Tasks::CheckSymlinks
9
+ DEFAULT_IGNORED = [
10
+ '/.git/',
11
+ '/.bundle/',
12
+ '/vendor/',
13
+ ].freeze
14
+
15
+ IGNORE_LIST_FILES = [
16
+ '.pdkignore',
17
+ '.gitignore',
18
+ ].freeze
19
+
20
+ def check(dir = Dir.pwd)
21
+ dir = Pathname.new(dir) unless dir.is_a?(Pathname)
22
+ results = []
23
+
24
+ dir.each_child(true) do |child|
25
+ next if ignored?(child.to_s)
26
+
27
+ if child.symlink?
28
+ results << child
29
+ elsif child.directory? && child.basename.to_s !~ %r{^(\.git|\.?bundle)$}
30
+ results.concat(check(child))
31
+ end
32
+ end
33
+
34
+ results
35
+ end
36
+
37
+ def ignored?(path)
38
+ path = "#{path}/" if File.directory?(path)
39
+
40
+ !ignore_pathspec.match_paths([path], Dir.pwd).empty?
41
+ end
42
+
43
+ def ignore_pathspec
44
+ @ignore_pathspec ||= PathSpec.new(DEFAULT_IGNORED).tap do |pathspec|
45
+ IGNORE_LIST_FILES.each do |f|
46
+ next unless File.file?(f) && File.readable?(f)
47
+
48
+ File.open(f, 'r') { |fd| pathspec.add(fd) }
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,462 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'open3'
5
+ require 'json'
6
+
7
+ module PuppetlabsSpecHelper; end
8
+ module PuppetlabsSpecHelper::Tasks; end
9
+
10
+ module PuppetlabsSpecHelper::Tasks::FixtureHelpers
11
+ # This is a helper for the self-symlink entry of fixtures.yml
12
+ def source_dir
13
+ Dir.pwd
14
+ end
15
+
16
+ # @return [String] - the name of current module
17
+ def module_name
18
+ raise ArgumentError unless File.file?('metadata.json') && File.readable?('metadata.json')
19
+
20
+ metadata = JSON.parse(File.read('metadata.json'))
21
+ metadata_name = metadata.fetch('name', nil) || ''
22
+
23
+ raise ArgumentError if metadata_name.empty?
24
+
25
+ metadata_name.split('-').last
26
+ rescue JSON::ParserError, ArgumentError
27
+ File.basename(Dir.pwd).split('-').last
28
+ end
29
+
30
+ def module_version(path)
31
+ metadata_path = File.join(path, 'metadata.json')
32
+ raise ArgumentError unless File.file?(metadata_path) && File.readable?(metadata_path)
33
+
34
+ metadata = JSON.parse(File.read(metadata_path))
35
+ metadata.fetch('version', nil) || '0.0.1'
36
+ rescue JSON::ParserError, ArgumentError
37
+ logger.warn "Failed to find module version at path #{path}"
38
+ '0.0.1'
39
+ end
40
+
41
+ # @return [Hash] - returns a hash of all the fixture repositories
42
+ # @example
43
+ # {"puppetlabs-stdlib"=>{"target"=>"https://gitlab.com/puppetlabs/puppet-stdlib.git",
44
+ # "ref"=>nil, "branch"=>"main", "scm"=>nil,
45
+ # }}
46
+ def repositories
47
+ @repositories ||= fixtures('repositories') || {}
48
+ end
49
+
50
+ # @return [Hash] - returns a hash of all the fixture forge modules
51
+ # @example
52
+ # {"puppetlabs-stdlib"=>{"target"=>"spec/fixtures/modules/stdlib",
53
+ # "ref"=>nil, "branch"=>nil, "scm"=>nil,
54
+ # "flags"=>"--module_repository=https://myforge.example.com/", "subdir"=>nil}}
55
+ def forge_modules
56
+ @forge_modules ||= fixtures('forge_modules') || {}
57
+ end
58
+
59
+ # @return [Hash] - a hash of symlinks specified in the fixtures file
60
+ def symlinks
61
+ @symlinks ||= fixtures('symlinks') || {}
62
+ end
63
+
64
+ # @return [Hash] - returns a hash with the module name and the source directory
65
+ def auto_symlink
66
+ { module_name => "\#{source_dir}" }
67
+ end
68
+
69
+ # @return [Boolean] - true if the os is a windows system
70
+ def windows?
71
+ !!File::ALT_SEPARATOR
72
+ end
73
+
74
+ def fixtures(category)
75
+ fixtures_yaml = if ENV['FIXTURES_YML']
76
+ ENV['FIXTURES_YML']
77
+ elsif File.exist?('.fixtures.yml')
78
+ '.fixtures.yml'
79
+ elsif File.exist?('.fixtures.yaml')
80
+ '.fixtures.yaml'
81
+ else
82
+ false
83
+ end
84
+
85
+ begin
86
+ fixtures = if fixtures_yaml
87
+ YAML.load_file(fixtures_yaml) || { 'fixtures' => {} }
88
+ else
89
+ { 'fixtures' => {} }
90
+ end
91
+ rescue Errno::ENOENT
92
+ raise("Fixtures file not found: '#{fixtures_yaml}'")
93
+ rescue Psych::SyntaxError => e
94
+ raise("Found malformed YAML in '#{fixtures_yaml}' on line #{e.line} column #{e.column}: #{e.problem}")
95
+ end
96
+
97
+ unless fixtures.include?('fixtures')
98
+ # File is non-empty, but does not specify fixtures
99
+ raise("No 'fixtures' entries found in '#{fixtures_yaml}'; required")
100
+ end
101
+
102
+ fixture_defaults = if fixtures.include? 'defaults'
103
+ fixtures['defaults']
104
+ else
105
+ {}
106
+ end
107
+
108
+ fixtures = fixtures['fixtures']
109
+
110
+ if fixtures['symlinks'].nil?
111
+ fixtures['symlinks'] = auto_symlink
112
+ end
113
+
114
+ result = {}
115
+ if fixtures.include?(category) && !fixtures[category].nil?
116
+ defaults = { 'target' => 'spec/fixtures/modules' }
117
+
118
+ # load defaults from the `.fixtures.yml` `defaults` section
119
+ # for the requested category and merge them into my defaults
120
+ if fixture_defaults.include? category
121
+ defaults = defaults.merge(fixture_defaults[category])
122
+ end
123
+
124
+ fixtures[category].each do |fixture, opts|
125
+ # convert a simple string fixture to a hash, by
126
+ # using the string fixture as the `repo` option of the hash.
127
+ if opts.instance_of?(String)
128
+ opts = { 'repo' => opts }
129
+ end
130
+ # there should be a warning or something if it's not a hash...
131
+ next unless opts.instance_of?(Hash)
132
+
133
+ # merge our options into the defaults to get the
134
+ # final option list
135
+ opts = defaults.merge(opts)
136
+
137
+ next unless include_repo?(opts['puppet_version'])
138
+
139
+ real_target = eval("\"#{opts['target']}\"", binding, __FILE__, __LINE__) # evaluating target reference in this context (see auto_symlink)
140
+ real_source = eval("\"#{opts['repo']}\"", binding, __FILE__, __LINE__) # evaluating repo reference in this context (see auto_symlink)
141
+
142
+ result[real_source] = validate_fixture_hash!(
143
+ 'target' => File.join(real_target, fixture),
144
+ 'ref' => opts['ref'] || opts['tag'],
145
+ 'branch' => opts['branch'],
146
+ 'scm' => opts['scm'],
147
+ 'flags' => opts['flags'],
148
+ 'subdir' => opts['subdir'],
149
+ )
150
+ end
151
+ end
152
+ result
153
+ end
154
+
155
+ def validate_fixture_hash!(hash)
156
+ # Can only validate git based scm
157
+ return hash unless hash['scm'] == 'git'
158
+
159
+ # Forward slashes in the ref aren't allowed. And is probably a branch name.
160
+ raise ArgumentError, "The ref for #{hash['target']} is invalid (Contains a forward slash). If this is a branch name, please use the 'branch' setting instead." if hash['ref'] =~ %r{/}
161
+
162
+ hash
163
+ end
164
+
165
+ def include_repo?(version_range)
166
+ if version_range && defined?(SemanticPuppet)
167
+ puppet_spec = Gem::Specification.find_by_name('puppet')
168
+ puppet_version = SemanticPuppet::Version.parse(puppet_spec.version.to_s)
169
+
170
+ constraint = SemanticPuppet::VersionRange.parse(version_range)
171
+ constraint.include?(puppet_version)
172
+ else
173
+ true
174
+ end
175
+ end
176
+
177
+ def clone_repo(scm, remote, target, _subdir = nil, ref = nil, branch = nil, flags = nil)
178
+ args = []
179
+ case scm
180
+ when 'hg'
181
+ args.push('clone')
182
+ args.push('-b', branch) if branch
183
+ args.push(flags) if flags
184
+ args.push(remote, target)
185
+ when 'git'
186
+ args.push('clone')
187
+ args.push('--depth 1') unless ref
188
+ args.push('-b', branch) if branch
189
+ args.push(flags) if flags
190
+ args.push(remote, target)
191
+ else
192
+ raise "Unfortunately #{scm} is not supported yet"
193
+ end
194
+ result = system("#{scm} #{args.flatten.join ' '}")
195
+ unless File.exist?(target)
196
+ raise "Failed to clone #{scm} repository #{remote} into #{target}"
197
+ end
198
+
199
+ result
200
+ end
201
+
202
+ def update_repo(scm, target)
203
+ args = case scm
204
+ when 'hg'
205
+ ['pull']
206
+ when 'git'
207
+ ['fetch'].tap do |git_args|
208
+ git_args << '--unshallow' if shallow_git_repo?
209
+ end
210
+ else
211
+ raise "Unfortunately #{scm} is not supported yet"
212
+ end
213
+ system("#{scm} #{args.flatten.join(' ')}", chdir: target)
214
+ end
215
+
216
+ def shallow_git_repo?
217
+ File.file?(File.join('.git', 'shallow'))
218
+ end
219
+
220
+ def revision(scm, target, ref)
221
+ args = []
222
+ case scm
223
+ when 'hg'
224
+ args.push('update', '--clean', '-r', ref)
225
+ when 'git'
226
+ args.push('reset', '--hard', ref)
227
+ else
228
+ raise "Unfortunately #{scm} is not supported yet"
229
+ end
230
+ result = system("#{scm} #{args.flatten.join ' '}", chdir: target)
231
+ raise "Invalid ref #{ref} for #{target}" unless result
232
+ end
233
+
234
+ def valid_repo?(scm, target, remote)
235
+ return false unless File.directory?(target)
236
+ return true if scm == 'hg'
237
+
238
+ return true if git_remote_url(target) == remote
239
+
240
+ warn "Git remote for #{target} has changed, recloning repository"
241
+ FileUtils.rm_rf(target)
242
+ false
243
+ end
244
+
245
+ def git_remote_url(target)
246
+ output, status = Open3.capture2e('git', '--git-dir', File.join(target, '.git'), 'ls-remote', '--get-url', 'origin')
247
+ status.success? ? output.strip : nil
248
+ end
249
+
250
+ def remove_subdirectory(target, subdir)
251
+ unless subdir.nil?
252
+ Dir.mktmpdir do |tmpdir|
253
+ FileUtils.mv(Dir.glob("#{target}/#{subdir}/{.[^\.]*,*}"), tmpdir)
254
+ FileUtils.rm_rf("#{target}/#{subdir}")
255
+ FileUtils.mv(Dir.glob("#{tmpdir}/{.[^\.]*,*}"), target.to_s)
256
+ end
257
+ end
258
+ end
259
+
260
+ # creates a logger so we can log events with certain levels
261
+ def logger
262
+ unless @logger
263
+ require 'logger'
264
+ level = if ENV['ENABLE_LOGGER']
265
+ Logger::DEBUG
266
+ else
267
+ Logger::INFO
268
+ end
269
+ @logger = Logger.new($stderr)
270
+ @logger.level = level
271
+ end
272
+ @logger
273
+ end
274
+
275
+ def module_working_directory
276
+ # The problem with the relative path is that PMT doesn't expand the path properly and so passing in a relative path here
277
+ # becomes something like C:\somewhere\backslashes/spec/fixtures/work-dir on Windows, and then PMT barfs itself.
278
+ # This has been reported as https://tickets.puppetlabs.com/browse/PUP-4884
279
+ File.expand_path(ENV['MODULE_WORKING_DIR'] || 'spec/fixtures/work-dir')
280
+ end
281
+
282
+ # returns the current thread count that is currently active
283
+ # a status of false or nil means the thread completed
284
+ # so when anything else we count that as a active thread
285
+ # @return [Integer] - current thread count
286
+ def current_thread_count(items)
287
+ active_threads = items.find_all do |_item, opts|
288
+ if opts[:thread]
289
+ opts[:thread].status
290
+ else
291
+ false
292
+ end
293
+ end
294
+ logger.debug "Current thread count #{active_threads.count}"
295
+ active_threads.count
296
+ end
297
+
298
+ # @summary Set a limit on the amount threads used, defaults to 10
299
+ # MAX_FIXTURE_THREAD_COUNT can be used to set this limit
300
+ # @return [Integer] - returns the max_thread_count
301
+ def max_thread_limit
302
+ @max_thread_limit ||= (ENV['MAX_FIXTURE_THREAD_COUNT'] || 10).to_i
303
+ end
304
+
305
+ # @param items [Hash] - a hash of either repositories or forge modules
306
+ # @param [Block] - the method you wish to use to download the item
307
+ def download_items(items)
308
+ items.each do |remote, opts|
309
+ # get the current active threads that are alive
310
+ count = current_thread_count(items)
311
+ if count < max_thread_limit
312
+ logger.debug "New Thread started for #{remote}"
313
+ # start up a new thread and store it in the opts hash
314
+ opts[:thread] = Thread.new do
315
+ yield(remote, opts)
316
+ end
317
+ else
318
+ # the last thread started should be the longest wait
319
+ item, item_opts = items.find_all { |_i, o| o.key?(:thread) }.last
320
+ logger.debug "Waiting on #{item}"
321
+ item_opts[:thread].join # wait for the thread to finish
322
+ # now that we waited lets try again
323
+ redo
324
+ end
325
+ end
326
+ # wait for all the threads to finish
327
+ items.each { |_remote, opts| opts[:thread].join }
328
+ end
329
+
330
+ # @param target [String] - the target directory
331
+ # @param link [String] - the name of the link you wish to create
332
+ # works on windows and linux
333
+ def setup_symlink(target, link)
334
+ link = link['target']
335
+ return if File.symlink?(link)
336
+
337
+ logger.info("Creating symlink from #{link} to #{target}")
338
+ if windows?
339
+ target = File.join(File.dirname(link), target) unless Pathname.new(target).absolute?
340
+ if Dir.respond_to?(:create_junction)
341
+ Dir.create_junction(link, target)
342
+ else
343
+ system("call mklink /J \"#{link.tr('/', '\\')}\" \"#{target.tr('/', '\\')}\"")
344
+ end
345
+ else
346
+ FileUtils.ln_sf(target, link)
347
+ end
348
+ end
349
+
350
+ # @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
351
+ # @param [String] - the remote url or namespace/name of the module to download
352
+ # @param [Hash] - list of options such as version, branch, ref
353
+ def download_repository(remote, opts)
354
+ scm = 'git'
355
+ target = opts['target']
356
+ subdir = opts['subdir']
357
+ ref = opts['ref']
358
+ scm = opts['scm'] if opts['scm']
359
+ branch = opts['branch'] if opts['branch']
360
+ flags = opts['flags']
361
+ if valid_repo?(scm, target, remote)
362
+ update_repo(scm, target)
363
+ else
364
+ clone_repo(scm, remote, target, subdir, ref, branch, flags)
365
+ end
366
+ revision(scm, target, ref) if ref
367
+ remove_subdirectory(target, subdir) if subdir
368
+ end
369
+
370
+ # @return [String] - the spec/fixtures/modules directory in the module root folder
371
+ def module_target_dir
372
+ @module_target_dir ||= File.expand_path('spec/fixtures/modules')
373
+ end
374
+
375
+ # @return [Boolean] - returns true if the module was downloaded successfully, false otherwise
376
+ # @param [String] - the remote url or namespace/name of the module to download
377
+ # @param [Hash] - list of options such as version
378
+ def download_module(remote, opts)
379
+ ref = ''
380
+ flags = ''
381
+ if opts.instance_of?(String)
382
+ target = opts
383
+ elsif opts.instance_of?(Hash)
384
+ target = opts['target']
385
+ ref = " --version #{opts['ref']}" unless opts['ref'].nil?
386
+ flags = " #{opts['flags']}" if opts['flags']
387
+ end
388
+
389
+ return false if File.directory?(target) && (ref.empty? || opts['ref'] == module_version(target))
390
+
391
+ # The PMT cannot handle multi threaded runs due to cache directory collisons
392
+ # so we randomize the directory instead.
393
+ # Does working_dir even need to be passed?
394
+ Dir.mktmpdir do |working_dir|
395
+ command = "puppet module install#{ref}#{flags} --ignore-dependencies" \
396
+ ' --force' \
397
+ " --module_working_dir \"#{working_dir}\"" \
398
+ " --target-dir \"#{module_target_dir}\" \"#{remote}\""
399
+
400
+ unless system(command)
401
+ raise "Failed to install module #{remote} to #{module_target_dir}"
402
+ end
403
+ end
404
+ $CHILD_STATUS.success?
405
+ end
406
+ end
407
+
408
+ include PuppetlabsSpecHelper::Tasks::FixtureHelpers # DSL include # rubocop:disable Style/MixinUsage
409
+
410
+ desc 'Create the fixtures directory'
411
+ task :spec_prep do
412
+ # Ruby only sets File::ALT_SEPARATOR on Windows and Rubys standard library
413
+ # uses this to check for Windows
414
+ if windows?
415
+ begin
416
+ require 'win32/dir'
417
+ rescue LoadError
418
+ warn 'win32-dir gem not installed, falling back to executing mklink directly'
419
+ end
420
+ end
421
+
422
+ # git has a race condition creating that directory, that would lead to aborted clone operations
423
+ FileUtils.mkdir_p('spec/fixtures/modules')
424
+
425
+ symlinks.each { |target, link| setup_symlink(target, link) }
426
+
427
+ download_items(repositories) { |remote, opts| download_repository(remote, opts) }
428
+
429
+ download_items(forge_modules) { |remote, opts| download_module(remote, opts) }
430
+
431
+ FileUtils.mkdir_p('spec/fixtures/manifests')
432
+ FileUtils.touch('spec/fixtures/manifests/site.pp')
433
+ end
434
+
435
+ desc 'Clean up the fixtures directory'
436
+ task :spec_clean do
437
+ repositories.each do |_remote, opts|
438
+ target = opts['target']
439
+ FileUtils.rm_rf(target)
440
+ end
441
+
442
+ forge_modules.each do |_remote, opts|
443
+ target = opts['target']
444
+ FileUtils.rm_rf(target)
445
+ end
446
+
447
+ FileUtils.rm_rf(module_working_directory)
448
+
449
+ Rake::Task[:spec_clean_symlinks].invoke
450
+
451
+ if File.zero?('spec/fixtures/manifests/site.pp')
452
+ FileUtils.rm_f('spec/fixtures/manifests/site.pp')
453
+ end
454
+ end
455
+
456
+ desc 'Clean up any fixture symlinks'
457
+ task :spec_clean_symlinks do
458
+ fixtures('symlinks').each do |_source, opts|
459
+ target = opts['target']
460
+ FileUtils.rm_f(target)
461
+ end
462
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PuppetlabsSpecHelper
2
- VERSION = "2.6.2"
4
+ VERSION = '4.0.1'
3
5
 
4
6
  # compat for pre-1.2.0 users; deprecated
5
7
  module Version
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
2
4
 
3
5
  require 'puppetlabs_spec_helper/puppet_spec_helper'
4
6
 
@@ -1,33 +1,38 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'puppetlabs_spec_helper/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "puppetlabs_spec_helper"
8
+ spec.name = 'puppetlabs_spec_helper'
8
9
  spec.version = PuppetlabsSpecHelper::VERSION
9
- spec.authors = ["Puppet, Inc.", "Community Contributors"]
10
- spec.email = ["modules-team@puppet.com"]
10
+ spec.authors = ['Puppet, Inc.', 'Community Contributors']
11
+ spec.email = ['modules-team@puppet.com']
11
12
 
12
- spec.summary = %q{Standard tasks and configuration for module spec tests.}
13
- spec.description = %q{Contains rake tasks and a standard spec_helper for running spec tests on puppet modules.}
14
- spec.homepage = "http://github.com/puppetlabs/puppetlabs_spec_helper"
13
+ spec.summary = 'Standard tasks and configuration for module spec tests.'
14
+ spec.description = 'Contains rake tasks and a standard spec_helper for running spec tests on puppet modules.'
15
+ spec.homepage = 'http://github.com/puppetlabs/puppetlabs_spec_helper'
16
+ spec.license = 'Apache-2.0'
15
17
 
16
18
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
- spec.bindir = "exe"
19
+ spec.bindir = 'exe'
18
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
- spec.require_paths = ["lib"]
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.4')
20
24
 
21
- spec.add_runtime_dependency "mocha", "~> 1.0"
22
- spec.add_runtime_dependency "puppet-lint", "~> 2.0"
23
- spec.add_runtime_dependency "puppet-syntax", "~> 2.0"
24
- spec.add_runtime_dependency "rspec-puppet", "~> 2.0"
25
+ spec.add_runtime_dependency 'mocha', '~> 1.0'
26
+ spec.add_runtime_dependency 'pathspec', '>= 0.2.1', '< 1.1.0'
27
+ spec.add_runtime_dependency 'puppet-lint', '~> 2.0'
28
+ spec.add_runtime_dependency 'puppet-syntax', ['>= 2.0', '< 4']
29
+ spec.add_runtime_dependency 'rspec-puppet', '~> 2.0'
25
30
 
26
- spec.add_development_dependency "bundler", "~> 1.12"
27
- spec.add_development_dependency "pry"
28
- spec.add_development_dependency "puppet"
29
- spec.add_development_dependency "rake", "~> 10.0"
30
- spec.add_development_dependency "rspec", "~> 3.0"
31
- spec.add_development_dependency "yard"
32
- spec.add_development_dependency "gettext-setup", "~> 0.29"
31
+ spec.add_development_dependency 'bundler'
32
+ spec.add_development_dependency 'fakefs', ['>= 0.13.3', '< 2']
33
+ spec.add_development_dependency 'pry'
34
+ spec.add_development_dependency 'puppet'
35
+ spec.add_development_dependency 'rake', ['>= 10.0', '< 14']
36
+ spec.add_development_dependency 'rspec', '~> 3.0'
37
+ spec.add_development_dependency 'yard'
33
38
  end
@@ -1,4 +1,6 @@
1
- $:.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
2
4
 
3
5
  require 'puppetlabs_spec_helper/puppetlabs_spec_helper'
4
6