rspec-puppet 2.5.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +86 -0
- data/README.md +317 -34
- data/lib/rspec-puppet/adapters.rb +38 -30
- data/lib/rspec-puppet/coverage.rb +31 -6
- data/lib/rspec-puppet/example/function_example_group.rb +53 -8
- data/lib/rspec-puppet/example/type_alias_example_group.rb +14 -0
- data/lib/rspec-puppet/example.rb +16 -33
- data/lib/rspec-puppet/matchers/allow_value.rb +45 -0
- data/lib/rspec-puppet/matchers/compile.rb +7 -0
- data/lib/rspec-puppet/matchers/create_generic.rb +54 -13
- data/lib/rspec-puppet/matchers/parameter_matcher.rb +2 -2
- data/lib/rspec-puppet/matchers/run.rb +6 -1
- data/lib/rspec-puppet/matchers.rb +1 -0
- data/lib/rspec-puppet/monkey_patches.rb +162 -0
- data/lib/rspec-puppet/setup.rb +47 -26
- data/lib/rspec-puppet/spec_helper.rb +4 -3
- data/lib/rspec-puppet/support.rb +183 -34
- data/lib/rspec-puppet.rb +18 -0
- metadata +35 -28
- checksums.yaml +0 -7
@@ -14,9 +14,9 @@ module RSpec::Puppet
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# This method is used by the `run` matcher to trigger the function execution, and provides a uniform interface across all puppet versions.
|
17
|
-
def execute(*args)
|
17
|
+
def execute(*args, &block)
|
18
18
|
Puppet.override(@overrides, "rspec-test scope") do
|
19
|
-
@func.call(@overrides[:global_scope], *args)
|
19
|
+
@func.call(@overrides[:global_scope], *freeze_arg(args), &block)
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -25,6 +25,24 @@ module RSpec::Puppet
|
|
25
25
|
RSpec.deprecate("subject.call", :replacement => "is_expected.to run.with().and_raise_error(), or execute()")
|
26
26
|
execute(*args)
|
27
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Facts, keywords, single-quoted strings etc. are usually frozen in Puppet manifests, so freeze arguments to ensure functions are tested
|
32
|
+
# under worst-case conditions.
|
33
|
+
def freeze_arg(arg)
|
34
|
+
case arg
|
35
|
+
when Array
|
36
|
+
arg.each { |a| freeze_arg(a) }
|
37
|
+
arg.freeze
|
38
|
+
when Hash
|
39
|
+
arg.each { |k,v| freeze_arg(k); freeze_arg(v) }
|
40
|
+
arg.freeze
|
41
|
+
when String
|
42
|
+
arg.freeze
|
43
|
+
end
|
44
|
+
arg
|
45
|
+
end
|
28
46
|
end
|
29
47
|
|
30
48
|
class V3FunctionWrapper
|
@@ -61,18 +79,16 @@ module RSpec::Puppet
|
|
61
79
|
@subject ||= find_function
|
62
80
|
end
|
63
81
|
|
64
|
-
def find_function
|
65
|
-
function_name = self.class.top_level_description.downcase
|
66
|
-
|
82
|
+
def find_function(function_name = self.class.top_level_description)
|
67
83
|
with_vardir do
|
68
84
|
env = adapter.current_environment
|
69
85
|
|
70
86
|
if Puppet.version.to_f >= 4.0
|
71
87
|
context_overrides = compiler.context_overrides
|
72
88
|
func = nil
|
89
|
+
loaders = Puppet.lookup(:loaders)
|
73
90
|
Puppet.override(context_overrides, "rspec-test scope") do
|
74
|
-
|
75
|
-
func = V4FunctionWrapper.new(function_name, loader.private_environment_loader.load(:function, function_name), context_overrides)
|
91
|
+
func = V4FunctionWrapper.new(function_name, loaders.private_environment_loader.load(:function, function_name), context_overrides)
|
76
92
|
@scope = context_overrides[:global_scope]
|
77
93
|
end
|
78
94
|
|
@@ -86,7 +102,11 @@ module RSpec::Puppet
|
|
86
102
|
end
|
87
103
|
end
|
88
104
|
end
|
89
|
-
|
105
|
+
def call_function(function_name, *args)
|
106
|
+
# function = find_function(function_name)
|
107
|
+
# function.execute(*args)
|
108
|
+
scope.call_function(function_name, args)
|
109
|
+
end
|
90
110
|
def scope
|
91
111
|
@scope ||= build_scope(compiler, nodename(:function))
|
92
112
|
end
|
@@ -112,22 +132,47 @@ module RSpec::Puppet
|
|
112
132
|
def build_compiler
|
113
133
|
node_name = nodename(:function)
|
114
134
|
fact_values = facts_hash(node_name)
|
135
|
+
trusted_values = trusted_facts_hash(node_name)
|
136
|
+
|
137
|
+
# Allow different Hiera configurations:
|
138
|
+
HieraPuppet.instance_variable_set('@hiera', nil) if defined? HieraPuppet
|
115
139
|
|
116
140
|
# if we specify a pre_condition, we should ensure that we compile that
|
117
141
|
# code into a catalog that is accessible from the scope where the
|
118
142
|
# function is called
|
119
143
|
Puppet[:code] = pre_cond
|
120
144
|
|
145
|
+
node_facts = Puppet::Node::Facts.new(node_name, fact_values.dup)
|
146
|
+
|
121
147
|
node_options = {
|
122
148
|
:parameters => fact_values,
|
149
|
+
:facts => node_facts
|
123
150
|
}
|
124
151
|
|
125
152
|
stub_facts! fact_values
|
126
153
|
|
127
154
|
node = build_node(node_name, node_options)
|
128
155
|
|
156
|
+
if Puppet::Util::Package.versioncmp(Puppet.version, '4.3.0') >= 0
|
157
|
+
Puppet.push_context(
|
158
|
+
{
|
159
|
+
:trusted_information => Puppet::Context::TrustedInformation.new('remote', node_name, trusted_values)
|
160
|
+
},
|
161
|
+
"Context for spec trusted hash"
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
129
165
|
compiler = Puppet::Parser::Compiler.new(node)
|
130
166
|
compiler.compile
|
167
|
+
if Puppet::Util::Package.versioncmp(Puppet.version, '4.0.0') >= 0
|
168
|
+
loaders = Puppet::Pops::Loaders.new(adapter.current_environment)
|
169
|
+
Puppet.push_context(
|
170
|
+
{
|
171
|
+
:loaders => loaders,
|
172
|
+
:global_scope => compiler.context_overrides[:global_scope]
|
173
|
+
},
|
174
|
+
"set globals")
|
175
|
+
end
|
131
176
|
compiler
|
132
177
|
end
|
133
178
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module RSpec::Puppet
|
2
|
+
module TypeAliasExampleGroup
|
3
|
+
include RSpec::Puppet::TypeAliasMatchers
|
4
|
+
include RSpec::Puppet::Support
|
5
|
+
|
6
|
+
def catalogue(test_value)
|
7
|
+
load_catalogue(:type_alias, false, :test_value => test_value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def subject
|
11
|
+
lambda { |test_value| catalogue(test_value) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/rspec-puppet/example.rb
CHANGED
@@ -4,46 +4,29 @@ require 'rspec-puppet/example/class_example_group'
|
|
4
4
|
require 'rspec-puppet/example/function_example_group'
|
5
5
|
require 'rspec-puppet/example/host_example_group'
|
6
6
|
require 'rspec-puppet/example/type_example_group'
|
7
|
+
require 'rspec-puppet/example/type_alias_example_group'
|
7
8
|
require 'rspec-puppet/example/provider_example_group'
|
8
9
|
require 'rspec-puppet/example/application_example_group'
|
9
10
|
|
10
11
|
RSpec::configure do |c|
|
11
12
|
|
12
|
-
def c.
|
13
|
-
Regexp.compile(
|
13
|
+
def c.rspec_puppet_include(group, type, file_path)
|
14
|
+
escaped_file_path = Regexp.compile(file_path.join('[\\\/]'))
|
15
|
+
if RSpec::Version::STRING < '3'
|
16
|
+
self.include group, :type => type, :example_group => { :file_path => escaped_file_path }, :spec_type => type
|
17
|
+
else
|
18
|
+
self.include group, :type => type, :file_path => lambda { |file_path, metadata| metadata[:type].nil? && escaped_file_path =~ file_path }
|
19
|
+
end
|
14
20
|
end
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
:file_path => c.escaped_path(%w[spec functions])
|
25
|
-
}
|
26
|
-
c.include RSpec::Puppet::HostExampleGroup, :type => :host, :example_group => {
|
27
|
-
:file_path => c.escaped_path(%w[spec hosts])
|
28
|
-
}
|
29
|
-
c.include RSpec::Puppet::TypeExampleGroup, :type => :type, :example_group => {
|
30
|
-
:file_path => c.escaped_path(%w[spec types])
|
31
|
-
}
|
32
|
-
c.include RSpec::Puppet::ProviderExampleGroup, :type => :provider, :example_group => {
|
33
|
-
:file_path => c.escaped_path(%w[spec providers])
|
34
|
-
}
|
35
|
-
c.include RSpec::Puppet::ApplicationExampleGroup, :type => :application, :example_group => {
|
36
|
-
:file_path => c.escaped_path(%w[spec applications])
|
37
|
-
}
|
38
|
-
else
|
39
|
-
c.include RSpec::Puppet::DefineExampleGroup, :type => :define, :file_path => c.escaped_path(%w[spec defines])
|
40
|
-
c.include RSpec::Puppet::ClassExampleGroup, :type => :class, :file_path => c.escaped_path(%w[spec classes])
|
41
|
-
c.include RSpec::Puppet::FunctionExampleGroup, :type => :puppet_function, :file_path => c.escaped_path(%w[spec functions])
|
42
|
-
c.include RSpec::Puppet::HostExampleGroup, :type => :host, :file_path => c.escaped_path(%w[spec hosts])
|
43
|
-
c.include RSpec::Puppet::TypeExampleGroup, :type => :type, :file_path => c.escaped_path(%w[spec types])
|
44
|
-
c.include RSpec::Puppet::ProviderExampleGroup, :type => :provider, :file_path => c.escaped_path(%w[spec providers])
|
45
|
-
c.include RSpec::Puppet::ApplicationExampleGroup, :type => :application, :file_path => c.escaped_path(%w[spec applications])
|
46
|
-
end
|
22
|
+
c.rspec_puppet_include RSpec::Puppet::DefineExampleGroup, :define, %w[spec defines]
|
23
|
+
c.rspec_puppet_include RSpec::Puppet::ClassExampleGroup, :class, %w[spec classes]
|
24
|
+
c.rspec_puppet_include RSpec::Puppet::FunctionExampleGroup, :puppet_function, %w[spec functions]
|
25
|
+
c.rspec_puppet_include RSpec::Puppet::HostExampleGroup, :host, %w[spec hosts]
|
26
|
+
c.rspec_puppet_include RSpec::Puppet::TypeExampleGroup, :type, %w[spec types]
|
27
|
+
c.rspec_puppet_include RSpec::Puppet::TypeAliasExampleGroup, :type_alias, %w[spec type_aliases]
|
28
|
+
c.rspec_puppet_include RSpec::Puppet::ProviderExampleGroup, :provider, %w[spec providers]
|
29
|
+
c.rspec_puppet_include RSpec::Puppet::ApplicationExampleGroup, :application, %w[spec applications]
|
47
30
|
|
48
31
|
# Hook for each example group type to remove any caches or instance variables, since they will remain
|
49
32
|
# and cause a memory leak. Can't be assigned per type by :file_path, so check for its presence.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module RSpec::Puppet
|
2
|
+
module TypeAliasMatchers
|
3
|
+
class AllowValue
|
4
|
+
def initialize(values)
|
5
|
+
@values = values
|
6
|
+
@error_msgs = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def matches?(catalogue)
|
10
|
+
matches = @values.map do |test_value|
|
11
|
+
begin
|
12
|
+
catalogue.call(test_value)
|
13
|
+
true
|
14
|
+
rescue Puppet::Error => e
|
15
|
+
@error_msgs << e.message
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
matches.all?
|
20
|
+
end
|
21
|
+
|
22
|
+
def description
|
23
|
+
if @values.length == 1
|
24
|
+
"match value #{@values.first.inspect}"
|
25
|
+
else
|
26
|
+
"match values #{@values.map(&:inspect).join(', ')}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def failure_message
|
31
|
+
"expected that the type alias would " + description + " but it raised the #{@error_msgs.length == 1 ? 'error' : 'errors'} #{@error_msgs.join(', ')}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def failure_message_when_negated
|
35
|
+
"expected that the type alias would not " + description + " but it does"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def allow_value(*values)
|
40
|
+
RSpec::Puppet::TypeAliasMatchers::AllowValue.new(values)
|
41
|
+
end
|
42
|
+
|
43
|
+
alias_method :allow_values, :allow_value
|
44
|
+
end
|
45
|
+
end
|
@@ -108,6 +108,10 @@ module RSpec::Puppet
|
|
108
108
|
if vertex[:alias]
|
109
109
|
res_hash["#{vertex.type.to_s}[#{vertex[:alias]}]"] = 1
|
110
110
|
end
|
111
|
+
|
112
|
+
if vertex.uniqueness_key != [vertex.title]
|
113
|
+
res_hash["#{vertex.type.to_s}[#{vertex.uniqueness_key.first}]"] = 1
|
114
|
+
end
|
111
115
|
end
|
112
116
|
end
|
113
117
|
res_hash
|
@@ -134,6 +138,7 @@ module RSpec::Puppet
|
|
134
138
|
end
|
135
139
|
|
136
140
|
def cycles_found?
|
141
|
+
Puppet::Type.suppress_provider
|
137
142
|
cat = @catalogue.to_ral.relationship_graph
|
138
143
|
cat.write_graph(:resources)
|
139
144
|
if cat.respond_to? :find_cycles_in_graph
|
@@ -141,6 +146,8 @@ module RSpec::Puppet
|
|
141
146
|
else
|
142
147
|
find_cycles_legacy(cat)
|
143
148
|
end
|
149
|
+
Puppet::Type.unsuppress_provider
|
150
|
+
|
144
151
|
!@cycles.empty?
|
145
152
|
end
|
146
153
|
|
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'rspec-puppet/matchers/parameter_matcher'
|
3
|
+
|
2
4
|
module RSpec::Puppet
|
3
5
|
module ManifestMatchers
|
4
6
|
class CreateGeneric
|
@@ -79,7 +81,7 @@ module RSpec::Puppet
|
|
79
81
|
|
80
82
|
def matches?(catalogue)
|
81
83
|
ret = true
|
82
|
-
@catalogue = catalogue.call
|
84
|
+
@catalogue = catalogue.is_a?(Puppet::Resource::Catalog) ? catalogue : catalogue.call
|
83
85
|
resource = @catalogue.resource(@referenced_type, @title)
|
84
86
|
|
85
87
|
if resource.nil?
|
@@ -87,6 +89,17 @@ module RSpec::Puppet
|
|
87
89
|
else
|
88
90
|
RSpec::Puppet::Coverage.cover!(resource)
|
89
91
|
rsrc_hsh = resource.to_hash
|
92
|
+
|
93
|
+
if resource.builtin_type?
|
94
|
+
namevar = resource.resource_type.key_attributes.first.to_s
|
95
|
+
else
|
96
|
+
namevar = 'name'
|
97
|
+
end
|
98
|
+
|
99
|
+
unless @expected_params.any? { |param| param.first.to_s == namevar }
|
100
|
+
rsrc_hsh.delete(namevar.to_sym) if rsrc_hsh.has_key?(namevar.to_sym)
|
101
|
+
end
|
102
|
+
|
90
103
|
if @expected_params_count
|
91
104
|
unless rsrc_hsh.size == @expected_params_count
|
92
105
|
ret = false
|
@@ -201,7 +214,7 @@ module RSpec::Puppet
|
|
201
214
|
|
202
215
|
def check_befores(catalogue, resource)
|
203
216
|
@befores.each do |ref|
|
204
|
-
unless precedes?(resource,
|
217
|
+
unless precedes?(resource, canonicalize_resource(ref))
|
205
218
|
@errors << BeforeRelationshipError.new(resource.to_ref, ref)
|
206
219
|
end
|
207
220
|
end
|
@@ -209,7 +222,7 @@ module RSpec::Puppet
|
|
209
222
|
|
210
223
|
def check_requires(catalogue, resource)
|
211
224
|
@requires.each do |ref|
|
212
|
-
unless precedes?(
|
225
|
+
unless precedes?(canonicalize_resource(ref), resource)
|
213
226
|
@errors << RequireRelationshipError.new(resource.to_ref, ref)
|
214
227
|
end
|
215
228
|
end
|
@@ -217,7 +230,7 @@ module RSpec::Puppet
|
|
217
230
|
|
218
231
|
def check_notifies(catalogue, resource)
|
219
232
|
@notifies.each do |ref|
|
220
|
-
unless notifies?(resource,
|
233
|
+
unless notifies?(resource, canonicalize_resource(ref))
|
221
234
|
@errors << NotifyRelationshipError.new(resource.to_ref, ref)
|
222
235
|
end
|
223
236
|
end
|
@@ -225,7 +238,7 @@ module RSpec::Puppet
|
|
225
238
|
|
226
239
|
def check_subscribes(catalogue, resource)
|
227
240
|
@subscribes.each do |ref|
|
228
|
-
unless notifies?(
|
241
|
+
unless notifies?(canonicalize_resource(ref), resource)
|
229
242
|
@errors << SubscribeRelationshipError.new(resource.to_ref, ref)
|
230
243
|
end
|
231
244
|
end
|
@@ -240,34 +253,62 @@ module RSpec::Puppet
|
|
240
253
|
end
|
241
254
|
|
242
255
|
def canonicalize_resource(resource)
|
243
|
-
resource_from_ref(resource_ref(resource))
|
256
|
+
res = resource_from_ref(resource_ref(resource))
|
257
|
+
if res.nil?
|
258
|
+
resource = Struct.new(:type, :title).new(*@catalogue.title_key_for_ref(resource)) if resource.is_a?(String)
|
259
|
+
res = @catalogue.resource_keys.select { |type, name|
|
260
|
+
type == resource.type
|
261
|
+
}.map { |type, name|
|
262
|
+
@catalogue.resource(type, name)
|
263
|
+
}.compact.find { |cat_res|
|
264
|
+
cat_res.builtin_type? && cat_res.uniqueness_key.first == resource.title
|
265
|
+
}
|
266
|
+
end
|
267
|
+
res
|
244
268
|
end
|
245
269
|
|
246
270
|
def canonicalize_resource_ref(ref)
|
247
271
|
resource_ref(resource_from_ref(ref))
|
248
272
|
end
|
249
273
|
|
250
|
-
def relationship_refs(resource, type)
|
274
|
+
def relationship_refs(resource, type, visited = Set.new)
|
251
275
|
resource = canonicalize_resource(resource)
|
252
|
-
results =
|
276
|
+
results = Set.new
|
253
277
|
return results unless resource
|
278
|
+
|
279
|
+
# guard to prevent infinite recursion
|
280
|
+
if visited.include?(resource.object_id)
|
281
|
+
return [canonicalize_resource_ref(resource)]
|
282
|
+
else
|
283
|
+
visited << resource.object_id
|
284
|
+
end
|
285
|
+
|
254
286
|
Array[resource[type]].flatten.compact.each do |r|
|
255
287
|
results << canonicalize_resource_ref(r)
|
256
|
-
results << relationship_refs(r, type)
|
288
|
+
results << relationship_refs(r, type, visited)
|
289
|
+
|
290
|
+
res = canonicalize_resource(r)
|
291
|
+
if res && res.builtin_type?
|
292
|
+
results << res.to_ref
|
293
|
+
results << "#{res.type.to_s.capitalize}[#{res.uniqueness_key.first}]"
|
294
|
+
end
|
257
295
|
end
|
258
296
|
|
297
|
+
Puppet::Type.suppress_provider
|
259
298
|
# Add autorequires if any
|
260
299
|
if type == :require and resource.resource_type.respond_to? :eachautorequire
|
261
300
|
resource.resource_type.eachautorequire do |t, b|
|
262
301
|
Array(resource.to_ral.instance_eval(&b)).each do |dep|
|
263
302
|
res = "#{t.to_s.capitalize}[#{dep}]"
|
264
|
-
if r = relationship_refs(res, type)
|
303
|
+
if r = relationship_refs(res, type, visited)
|
265
304
|
results << res
|
266
305
|
results << r
|
267
306
|
end
|
268
307
|
end
|
269
308
|
end
|
270
309
|
end
|
310
|
+
Puppet::Type.unsuppress_provider
|
311
|
+
|
271
312
|
results.flatten
|
272
313
|
end
|
273
314
|
|
@@ -280,8 +321,8 @@ module RSpec::Puppet
|
|
280
321
|
|
281
322
|
self_or_upstream(first).each do |u|
|
282
323
|
self_or_upstream(second).each do |v|
|
283
|
-
before_refs = relationship_refs(u, :before)
|
284
|
-
require_refs = relationship_refs(v, :require)
|
324
|
+
before_refs = relationship_refs(u, :before) + relationship_refs(u, :notify)
|
325
|
+
require_refs = relationship_refs(v, :require) + relationship_refs(u, :subscribe)
|
285
326
|
|
286
327
|
if before_refs.include?(v.to_ref) || require_refs.include?(u.to_ref) || (before_refs & require_refs).any?
|
287
328
|
return true
|
@@ -320,7 +361,7 @@ module RSpec::Puppet
|
|
320
361
|
|
321
362
|
if value.nil? then
|
322
363
|
unless resource[param].nil?
|
323
|
-
@errors << "#{param} undefined"
|
364
|
+
@errors << "#{param} undefined but it is set to #{resource[param].inspect}"
|
324
365
|
end
|
325
366
|
else
|
326
367
|
m = ParameterMatcher.new(param, value, type)
|
@@ -30,7 +30,7 @@ module RSpec::Puppet
|
|
30
30
|
# Puppet flattens an array with a single value into just the value and
|
31
31
|
# this can cause confusion when testing as people expect when you put
|
32
32
|
# an array in, you'll get an array out.
|
33
|
-
actual = [
|
33
|
+
actual = [actual] if expected.is_a?(Array) && !actual.is_a?(Array)
|
34
34
|
|
35
35
|
retval = check(expected, actual)
|
36
36
|
|
@@ -55,7 +55,7 @@ module RSpec::Puppet
|
|
55
55
|
#
|
56
56
|
# @return [true, false] If the resource matched
|
57
57
|
def check(expected, actual)
|
58
|
-
return false if actual.nil? && !expected.nil?
|
58
|
+
return false if !expected.is_a?(Proc) && actual.nil? && !expected.nil?
|
59
59
|
case expected
|
60
60
|
when Proc
|
61
61
|
check_proc(expected, actual)
|
@@ -7,7 +7,7 @@ module RSpec::Puppet
|
|
7
7
|
@has_returned = false
|
8
8
|
begin
|
9
9
|
# `*nil` does not evaluate to "no params" on ruby 1.8 :-(
|
10
|
-
@actual_return = @params.nil? ? @func_obj.execute : @func_obj.execute(*@params)
|
10
|
+
@actual_return = @params.nil? ? @func_obj.execute(&@block) : @func_obj.execute(*@params, &@block)
|
11
11
|
@has_returned = true
|
12
12
|
rescue Exception => e
|
13
13
|
@actual_error = e
|
@@ -52,6 +52,11 @@ module RSpec::Puppet
|
|
52
52
|
self
|
53
53
|
end
|
54
54
|
|
55
|
+
def with_lambda(&block)
|
56
|
+
@block = block
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
55
60
|
def and_return(value)
|
56
61
|
@has_expected_return = true
|
57
62
|
@expected_return = value
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Puppet
|
4
|
+
# Allow rspec-puppet to prevent Puppet::Type from automatically picking
|
5
|
+
# a provider for a resource. We need to do this because in order to fully
|
6
|
+
# resolve the graph edges, we have to convert the Puppet::Resource objects
|
7
|
+
# into Puppet::Type objects so that their autorequires are evaluated. We need
|
8
|
+
# to prevent provider code from being called during this process as it's very
|
9
|
+
# platform specific.
|
10
|
+
class Type
|
11
|
+
old_set_default = instance_method(:set_default)
|
12
|
+
|
13
|
+
define_method(:set_default) do |attr|
|
14
|
+
old_posix = nil
|
15
|
+
old_microsoft_windows = nil
|
16
|
+
|
17
|
+
if attr == :provider
|
18
|
+
old_posix = Puppet.features.posix?
|
19
|
+
old_microsoft_windows = Puppet.features.microsoft_windows?
|
20
|
+
|
21
|
+
if Puppet::Util::Platform.pretend_windows?
|
22
|
+
Puppet.features.add(:posix) { false }
|
23
|
+
Puppet.features.add(:microsoft_windows) { true }
|
24
|
+
else
|
25
|
+
Puppet.features.add(:posix) { true }
|
26
|
+
Puppet.features.add(:microsoft_windows) { false }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
retval = old_set_default.bind(self).call(attr)
|
31
|
+
|
32
|
+
unless old_posix.nil?
|
33
|
+
Puppet.features.add(:posix) { old_posix }
|
34
|
+
end
|
35
|
+
unless old_microsoft_windows.nil?
|
36
|
+
Puppet.features.add(:microsoft_windows) { old_microsoft_windows }
|
37
|
+
end
|
38
|
+
|
39
|
+
retval
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.suppress_provider?
|
43
|
+
@suppress_provider ||= false
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.suppress_provider
|
47
|
+
@suppress_provider = true
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.unsuppress_provider
|
51
|
+
@suppress_provider = false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# If Puppet::Node::Environment has a validate_dirs instance method (i.e.
|
56
|
+
# Puppet < 3.x), wrap the method to check if rspec-puppet is pretending to be
|
57
|
+
# running under windows. The original method uses Puppet::Util.absolute_path?
|
58
|
+
# (which in turn calls Puppet::Util::Platform.windows?) to validate the path
|
59
|
+
# to the manifests on disk during compilation, so we have to temporarily
|
60
|
+
# disable the pretending when running it.
|
61
|
+
class Node::Environment
|
62
|
+
if instance_methods.include?("validate_dirs")
|
63
|
+
old_validate_dirs = instance_method(:validate_dirs)
|
64
|
+
|
65
|
+
define_method(:validate_dirs) do |dirs|
|
66
|
+
pretending = Puppet::Util::Platform.pretend_platform
|
67
|
+
|
68
|
+
if pretending
|
69
|
+
Puppet::Util::Platform.pretend_to_be nil
|
70
|
+
end
|
71
|
+
|
72
|
+
output = old_validate_dirs.bind(self).call(dirs)
|
73
|
+
|
74
|
+
Puppet::Util::Platform.pretend_to_be pretending
|
75
|
+
|
76
|
+
output
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
module Util
|
82
|
+
# Allow rspec-puppet to pretend to be different platforms.
|
83
|
+
module Platform
|
84
|
+
def windows?
|
85
|
+
pretend_platform.nil? ? (actual_platform == :windows) : pretend_windows?
|
86
|
+
end
|
87
|
+
module_function :windows?
|
88
|
+
|
89
|
+
def actual_platform
|
90
|
+
@actual_platform ||= !!File::ALT_SEPARATOR ? :windows : :posix
|
91
|
+
end
|
92
|
+
module_function :actual_platform
|
93
|
+
|
94
|
+
def pretend_windows?
|
95
|
+
pretend_platform == :windows
|
96
|
+
end
|
97
|
+
module_function :pretend_windows?
|
98
|
+
|
99
|
+
def pretend_to_be(platform)
|
100
|
+
# Ensure that we cache the real platform before pretending to be
|
101
|
+
# a different one
|
102
|
+
actual_platform
|
103
|
+
|
104
|
+
@pretend_platform = platform
|
105
|
+
end
|
106
|
+
module_function :pretend_to_be
|
107
|
+
|
108
|
+
def pretend_platform
|
109
|
+
@pretend_platform ||= nil
|
110
|
+
end
|
111
|
+
module_function :pretend_platform
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class Pathname
|
117
|
+
def rspec_puppet_basename(path)
|
118
|
+
raise ArgumentError, 'pathname stubbing not enabled' unless RSpec.configuration.enable_pathname_stubbing
|
119
|
+
|
120
|
+
if path =~ /\A[a-zA-Z]:(#{SEPARATOR_PAT}.*)\z/
|
121
|
+
path = path[2..-1]
|
122
|
+
end
|
123
|
+
path.split(SEPARATOR_PAT).last || path[/(#{SEPARATOR_PAT})/, 1] || path
|
124
|
+
end
|
125
|
+
|
126
|
+
if instance_methods.include?("chop_basename")
|
127
|
+
old_chop_basename = instance_method(:chop_basename)
|
128
|
+
|
129
|
+
define_method(:chop_basename) do |path|
|
130
|
+
if RSpec.configuration.enable_pathname_stubbing
|
131
|
+
base = rspec_puppet_basename(path)
|
132
|
+
if /\A#{SEPARATOR_PAT}?\z/o =~ base
|
133
|
+
return nil
|
134
|
+
else
|
135
|
+
return path[0, path.rindex(base)], base
|
136
|
+
end
|
137
|
+
else
|
138
|
+
old_chop_basename.bind(self).call(path)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Prevent the File type from munging paths (which uses File.expand_path to
|
145
|
+
# normalise paths, which does very bad things to *nix paths on Windows.
|
146
|
+
Puppet::Type.type(:file).paramclass(:path).munge { |value| value }
|
147
|
+
|
148
|
+
# Prevent the Exec type from validating the user. This parameter isn't
|
149
|
+
# supported under Windows at all and only under *nix when the current user is
|
150
|
+
# root.
|
151
|
+
Puppet::Type.type(:exec).paramclass(:user).validate { |value| true }
|
152
|
+
|
153
|
+
# Prevent Puppet from requiring 'puppet/util/windows' if we're pretending to be
|
154
|
+
# windows, otherwise it will require other libraries that probably won't be
|
155
|
+
# available on non-windows hosts.
|
156
|
+
module Kernel
|
157
|
+
alias :old_require :require
|
158
|
+
def require(path)
|
159
|
+
return if path == 'puppet/util/windows' && Puppet::Util::Platform.pretend_windows?
|
160
|
+
old_require(path)
|
161
|
+
end
|
162
|
+
end
|