~ubuntu-branches/ubuntu/oneiric/puppet/oneiric-security

« back to all changes in this revision

Viewing changes to spec/unit/parser/compiler.rb

  • Committer: Bazaar Package Importer
  • Author(s): Micah Anderson
  • Date: 2008-07-26 15:43:45 UTC
  • mfrom: (1.1.8 upstream) (3.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080726154345-c03m49twzxewdwjn
Tags: 0.24.5-2
* Fix puppetlast to work with 0.24.5
* Adjust logcheck to match against new log messages in 0.24.5
* Update standards version to 3.8.0 (no changes)
* Update changelog to reduce length of line to make lintian happy

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env ruby
 
2
 
 
3
require File.dirname(__FILE__) + '/../../spec_helper'
 
4
 
 
5
describe Puppet::Parser::Compiler do
 
6
    before :each do
 
7
        @node = Puppet::Node.new "testnode"
 
8
        @parser = Puppet::Parser::Parser.new :environment => "development"
 
9
 
 
10
        @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]'
 
11
        @scope = stub 'scope', :resource => @scope_resource, :source => mock("source")
 
12
        @compiler = Puppet::Parser::Compiler.new(@node, @parser)
 
13
    end
 
14
 
 
15
    describe Puppet::Parser::Compiler do
 
16
 
 
17
        it "should be able to store references to class scopes" do
 
18
            lambda { @compiler.class_set "myname", "myscope" }.should_not raise_error
 
19
        end
 
20
 
 
21
        it "should be able to retrieve class scopes by name" do
 
22
            @compiler.class_set "myname", "myscope"
 
23
            @compiler.class_scope("myname").should == "myscope"
 
24
        end
 
25
 
 
26
        it "should be able to retrieve class scopes by object" do
 
27
            klass = mock 'ast_class'
 
28
            klass.expects(:classname).returns("myname")
 
29
            @compiler.class_set "myname", "myscope"
 
30
            @compiler.class_scope(klass).should == "myscope"
 
31
        end
 
32
 
 
33
        it "should be able to return a class list containing all set classes" do
 
34
            @compiler.class_set "", "empty"
 
35
            @compiler.class_set "one", "yep"
 
36
            @compiler.class_set "two", "nope"
 
37
 
 
38
            @compiler.classlist.sort.should == %w{one two}.sort
 
39
        end
 
40
    end
 
41
 
 
42
    describe Puppet::Parser::Compiler, " when initializing" do
 
43
 
 
44
        it "should set its node attribute" do
 
45
            @compiler.node.should equal(@node)
 
46
        end
 
47
 
 
48
        it "should set its parser attribute" do
 
49
            @compiler.parser.should equal(@parser)
 
50
        end
 
51
 
 
52
        it "should detect when ast nodes are absent" do
 
53
            @compiler.ast_nodes?.should be_false
 
54
        end
 
55
 
 
56
        it "should detect when ast nodes are present" do
 
57
            @parser.nodes["testing"] = "yay"
 
58
            @compiler.ast_nodes?.should be_true
 
59
        end
 
60
    end
 
61
 
 
62
    describe Puppet::Parser::Compiler, "when managing scopes" do
 
63
 
 
64
        it "should create a top scope" do
 
65
            @compiler.topscope.should be_instance_of(Puppet::Parser::Scope)
 
66
        end
 
67
 
 
68
        it "should be able to create new scopes" do
 
69
            @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope)
 
70
        end
 
71
 
 
72
        it "should correctly set the level of newly created scopes" do
 
73
            @compiler.newscope(@compiler.topscope, :level => 5).level.should == 5
 
74
        end
 
75
 
 
76
        it "should set the parent scope of the new scope to be the passed-in parent" do
 
77
            scope = mock 'scope'
 
78
            newscope = @compiler.newscope(scope)
 
79
 
 
80
            @compiler.parent(newscope).should equal(scope)
 
81
        end
 
82
    end
 
83
 
 
84
    describe Puppet::Parser::Compiler, " when compiling" do
 
85
 
 
86
        def compile_methods
 
87
            [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated,
 
88
                :finish, :store, :extract]
 
89
        end
 
90
 
 
91
        # Stub all of the main compile methods except the ones we're specifically interested in.
 
92
        def compile_stub(*except)
 
93
            (compile_methods - except).each { |m| @compiler.stubs(m) }
 
94
        end
 
95
 
 
96
        it "should set node parameters as variables in the top scope" do
 
97
            params = {"a" => "b", "c" => "d"}
 
98
            @node.stubs(:parameters).returns(params)
 
99
            compile_stub(:set_node_parameters)
 
100
            @compiler.compile
 
101
            @compiler.topscope.lookupvar("a").should == "b"
 
102
            @compiler.topscope.lookupvar("c").should == "d"
 
103
        end
 
104
 
 
105
        it "should evaluate any existing classes named in the node" do
 
106
            classes = %w{one two three four}
 
107
            main = stub 'main'
 
108
            one = stub 'one', :classname => "one"
 
109
            three = stub 'three', :classname => "three"
 
110
            @node.stubs(:name).returns("whatever")
 
111
            @node.stubs(:classes).returns(classes)
 
112
 
 
113
            @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope)
 
114
            @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes }
 
115
        end
 
116
 
 
117
        it "should enable ast_nodes if the parser has any nodes" do
 
118
            @parser.expects(:nodes).returns(:one => :yay)
 
119
            @compiler.ast_nodes?.should be_true
 
120
        end
 
121
 
 
122
        it "should disable ast_nodes if the parser has no nodes" do
 
123
            @parser.expects(:nodes).returns({})
 
124
            @compiler.ast_nodes?.should be_false
 
125
        end
 
126
 
 
127
        it "should evaluate the main class if it exists" do
 
128
            compile_stub(:evaluate_main)
 
129
            main_class = mock 'main_class'
 
130
            main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) }
 
131
            @compiler.topscope.expects(:source=).with(main_class)
 
132
            @parser.stubs(:findclass).with("", "").returns(main_class)
 
133
 
 
134
            @compiler.compile
 
135
        end
 
136
 
 
137
        it "should evaluate any node classes" do
 
138
            @node.stubs(:classes).returns(%w{one two three four})
 
139
            @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope)
 
140
            @compiler.send(:evaluate_node_classes)
 
141
        end
 
142
 
 
143
        it "should evaluate all added collections" do
 
144
            colls = []
 
145
            # And when the collections fail to evaluate.
 
146
            colls << mock("coll1-false")
 
147
            colls << mock("coll2-false")
 
148
            colls.each { |c| c.expects(:evaluate).returns(false) }
 
149
 
 
150
            @compiler.add_collection(colls[0])
 
151
            @compiler.add_collection(colls[1])
 
152
 
 
153
            compile_stub(:evaluate_generators)
 
154
            @compiler.compile
 
155
        end
 
156
 
 
157
        it "should ignore builtin resources" do
 
158
            resource = stub 'builtin', :ref => "File[testing]", :builtin? => true
 
159
 
 
160
            @compiler.add_resource(@scope, resource)
 
161
            resource.expects(:evaluate).never
 
162
        
 
163
            @compiler.compile
 
164
        end
 
165
 
 
166
        it "should evaluate unevaluated resources" do
 
167
            resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => false
 
168
            @compiler.add_resource(@scope, resource)
 
169
 
 
170
            # We have to now mark the resource as evaluated
 
171
            resource.expects(:evaluate).with { |*whatever| resource.stubs(:evaluated?).returns true }
 
172
        
 
173
            @compiler.compile
 
174
        end
 
175
 
 
176
        it "should not evaluate already-evaluated resources" do
 
177
            resource = stub 'already_evaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => true, :virtual? => false
 
178
            @compiler.add_resource(@scope, resource)
 
179
            resource.expects(:evaluate).never
 
180
        
 
181
            @compiler.compile
 
182
        end
 
183
 
 
184
        it "should evaluate unevaluated resources created by evaluating other resources" do
 
185
            resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => false
 
186
            @compiler.add_resource(@scope, resource)
 
187
 
 
188
            resource2 = stub 'created', :ref => "File[other]", :builtin? => false, :evaluated? => false, :virtual? => false
 
189
 
 
190
            # We have to now mark the resource as evaluated
 
191
            resource.expects(:evaluate).with { |*whatever| resource.stubs(:evaluated?).returns(true); @compiler.add_resource(@scope, resource2) }
 
192
            resource2.expects(:evaluate).with { |*whatever| resource2.stubs(:evaluated?).returns(true) }
 
193
 
 
194
        
 
195
            @compiler.compile
 
196
        end
 
197
 
 
198
        it "should call finish() on all resources" do
 
199
            # Add a resource that does respond to :finish
 
200
            resource = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish"
 
201
            resource.expects(:finish)
 
202
 
 
203
            @compiler.add_resource(@scope, resource)
 
204
 
 
205
            # And one that does not
 
206
            dnf = stub "dnf", :ref => "File[dnf]"
 
207
 
 
208
            @compiler.add_resource(@scope, dnf)
 
209
 
 
210
            @compiler.send(:finish)
 
211
        end
 
212
 
 
213
        it "should add resources that do not conflict with existing resources" do
 
214
            resource = stub "noconflict", :ref => "File[yay]"
 
215
            @compiler.add_resource(@scope, resource)
 
216
 
 
217
            @compiler.catalog.should be_vertex(resource)
 
218
        end
 
219
 
 
220
        it "should fail to add resources that conflict with existing resources" do
 
221
            type = stub 'faketype', :isomorphic? => true, :name => "mytype"
 
222
            Puppet::Type.stubs(:type).with("mytype").returns(type)
 
223
 
 
224
            resource1 = stub "iso1conflict", :ref => "Mytype[yay]", :type => "mytype", :file => "eh", :line => 0
 
225
            resource2 = stub "iso2conflict", :ref => "Mytype[yay]", :type => "mytype", :file => "eh", :line => 0
 
226
 
 
227
            @compiler.add_resource(@scope, resource1)
 
228
            lambda { @compiler.add_resource(@scope, resource2) }.should raise_error(ArgumentError)
 
229
        end
 
230
 
 
231
        it "should have a method for looking up resources" do
 
232
            resource = stub 'resource', :ref => "Yay[foo]"
 
233
            @compiler.add_resource(@scope, resource)
 
234
            @compiler.findresource("Yay[foo]").should equal(resource)
 
235
        end
 
236
 
 
237
        it "should be able to look resources up by type and title" do
 
238
            resource = stub 'resource', :ref => "Yay[foo]"
 
239
            @compiler.add_resource(@scope, resource)
 
240
            @compiler.findresource("Yay", "foo").should equal(resource)
 
241
        end
 
242
 
 
243
        it "should not evaluate virtual defined resources" do
 
244
            resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => true
 
245
            @compiler.add_resource(@scope, resource)
 
246
 
 
247
            resource.expects(:evaluate).never
 
248
        
 
249
            @compiler.compile
 
250
        end
 
251
    end
 
252
 
 
253
    describe Puppet::Parser::Compiler, " when evaluating collections" do
 
254
 
 
255
        it "should evaluate each collection" do
 
256
            2.times { |i|
 
257
                coll = mock 'coll%s' % i
 
258
                @compiler.add_collection(coll)
 
259
            
 
260
                # This is the hard part -- we have to emulate the fact that
 
261
                # collections delete themselves if they are done evaluating.
 
262
                coll.expects(:evaluate).with do
 
263
                    @compiler.delete_collection(coll)
 
264
                end
 
265
            }
 
266
 
 
267
            @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections }
 
268
        end
 
269
 
 
270
        it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do
 
271
            coll = stub 'coll', :evaluate => false
 
272
            coll.expects(:resources).returns(nil)
 
273
 
 
274
            @compiler.add_collection(coll)
 
275
 
 
276
            lambda { @compiler.compile }.should_not raise_error
 
277
        end
 
278
 
 
279
        it "should fail when there are unevaluated resource collections that refer to a specific resource" do
 
280
            coll = stub 'coll', :evaluate => false
 
281
            coll.expects(:resources).returns(:something)
 
282
 
 
283
            @compiler.add_collection(coll)
 
284
 
 
285
            lambda { @compiler.compile }.should raise_error(Puppet::ParseError)
 
286
        end
 
287
 
 
288
        it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do
 
289
            coll = stub 'coll', :evaluate => false
 
290
            coll.expects(:resources).returns([:one, :two])
 
291
 
 
292
            @compiler.add_collection(coll)
 
293
 
 
294
            lambda { @compiler.compile }.should raise_error(Puppet::ParseError)
 
295
        end
 
296
    end
 
297
 
 
298
    describe Puppet::Parser::Compiler, "when told to evaluate missing classes" do
 
299
 
 
300
        it "should fail if there's no source listed for the scope" do
 
301
            scope = stub 'scope', :source => nil
 
302
            proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError)
 
303
        end
 
304
 
 
305
        it "should tag the catalog with the name of each not-found class" do
 
306
            @compiler.catalog.expects(:tag).with("notfound")
 
307
            @scope.expects(:findclass).with("notfound").returns(nil)
 
308
            @compiler.evaluate_classes(%w{notfound}, @scope)
 
309
        end
 
310
    end
 
311
 
 
312
    describe Puppet::Parser::Compiler, " when evaluating found classes" do
 
313
 
 
314
        before do
 
315
            @class = stub 'class', :classname => "my::class"
 
316
            @scope.stubs(:findclass).with("myclass").returns(@class)
 
317
 
 
318
            @resource = stub 'resource', :ref => "Class[myclass]"
 
319
        end
 
320
 
 
321
        it "should evaluate each class" do
 
322
            @compiler.catalog.stubs(:tag)
 
323
 
 
324
            @class.expects(:evaluate).with(@scope)
 
325
 
 
326
            @compiler.evaluate_classes(%w{myclass}, @scope)
 
327
        end
 
328
 
 
329
        it "should not evaluate the resources created for found classes unless asked" do
 
330
            @compiler.catalog.stubs(:tag)
 
331
 
 
332
            @resource.expects(:evaluate).never
 
333
 
 
334
            @class.expects(:evaluate).returns(@resource)
 
335
 
 
336
            @compiler.evaluate_classes(%w{myclass}, @scope)
 
337
        end
 
338
 
 
339
        it "should immediately evaluate the resources created for found classes when asked" do
 
340
            @compiler.catalog.stubs(:tag)
 
341
 
 
342
            @resource.expects(:evaluate)
 
343
            @class.expects(:evaluate).returns(@resource)
 
344
 
 
345
            @compiler.evaluate_classes(%w{myclass}, @scope, false)
 
346
        end
 
347
 
 
348
        it "should skip classes that have already been evaluated" do
 
349
            @compiler.catalog.stubs(:tag)
 
350
 
 
351
            @compiler.expects(:class_scope).with(@class).returns("something")
 
352
 
 
353
            @compiler.expects(:add_resource).never
 
354
 
 
355
            @resource.expects(:evaluate).never
 
356
 
 
357
            Puppet::Parser::Resource.expects(:new).never
 
358
            @compiler.evaluate_classes(%w{myclass}, @scope, false)
 
359
        end
 
360
 
 
361
        it "should return the list of found classes" do
 
362
            @compiler.catalog.stubs(:tag)
 
363
 
 
364
            @compiler.stubs(:add_resource)
 
365
            @scope.stubs(:findclass).with("notfound").returns(nil)
 
366
 
 
367
            Puppet::Parser::Resource.stubs(:new).returns(@resource)
 
368
            @class.stubs :evaluate
 
369
            @compiler.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass}
 
370
        end
 
371
    end
 
372
 
 
373
    describe Puppet::Parser::Compiler, " when evaluating AST nodes with no AST nodes present" do
 
374
 
 
375
        it "should do nothing" do
 
376
            @compiler.expects(:ast_nodes?).returns(false)
 
377
            @compiler.parser.expects(:nodes).never
 
378
            Puppet::Parser::Resource.expects(:new).never
 
379
 
 
380
            @compiler.send(:evaluate_ast_node)
 
381
        end
 
382
    end
 
383
 
 
384
    describe Puppet::Parser::Compiler, " when evaluating AST nodes with AST nodes present" do
 
385
 
 
386
        before do
 
387
            @nodes = mock 'node_hash'
 
388
            @compiler.stubs(:ast_nodes?).returns(true)
 
389
            @compiler.parser.stubs(:nodes).returns(@nodes)
 
390
 
 
391
            # Set some names for our test
 
392
            @node.stubs(:names).returns(%w{a b c})
 
393
            @nodes.stubs(:[]).with("a").returns(nil)
 
394
            @nodes.stubs(:[]).with("b").returns(nil)
 
395
            @nodes.stubs(:[]).with("c").returns(nil)
 
396
 
 
397
            # It should check this last, of course.
 
398
            @nodes.stubs(:[]).with("default").returns(nil)
 
399
        end
 
400
 
 
401
        it "should fail if the named node cannot be found" do
 
402
            proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError)
 
403
        end
 
404
 
 
405
        it "should evaluate the first node class matching the node name" do
 
406
            node_class = stub 'node', :classname => "c", :evaluate_code => nil
 
407
            @nodes.stubs(:[]).with("c").returns(node_class)
 
408
 
 
409
            node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil
 
410
            node_class.expects(:evaluate).returns(node_resource)
 
411
 
 
412
            @compiler.compile
 
413
        end
 
414
 
 
415
        it "should match the default node if no matching node can be found" do
 
416
            node_class = stub 'node', :classname => "default", :evaluate_code => nil
 
417
            @nodes.stubs(:[]).with("default").returns(node_class)
 
418
 
 
419
            node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil
 
420
            node_class.expects(:evaluate).returns(node_resource)
 
421
 
 
422
            @compiler.compile
 
423
        end
 
424
 
 
425
        it "should evaluate the node resource immediately rather than using lazy evaluation" do
 
426
            node_class = stub 'node', :classname => "c"
 
427
            @nodes.stubs(:[]).with("c").returns(node_class)
 
428
 
 
429
            node_resource = stub 'node resource', :ref => "Node[c]"
 
430
            node_class.expects(:evaluate).returns(node_resource)
 
431
 
 
432
            node_resource.expects(:evaluate)
 
433
 
 
434
            @compiler.send(:evaluate_ast_node)
 
435
        end
 
436
 
 
437
        it "should set the node's scope as the top scope" do
 
438
            node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil
 
439
            node_class = stub 'node', :classname => "c", :evaluate => node_resource
 
440
 
 
441
            @nodes.stubs(:[]).with("c").returns(node_class)
 
442
 
 
443
            # The #evaluate method normally does this.
 
444
            scope = stub 'scope', :source => "mysource"
 
445
            @compiler.class_set(node_class.classname, scope)
 
446
            node_resource.stubs(:evaluate)
 
447
 
 
448
            @compiler.compile
 
449
 
 
450
            @compiler.topscope.should equal(scope)
 
451
        end
 
452
    end
 
453
 
 
454
    describe Puppet::Parser::Compiler, "when storing compiled resources" do
 
455
 
 
456
        it "should store the resources" do
 
457
            Puppet.features.expects(:rails?).returns(true)
 
458
            Puppet::Rails.expects(:connect)
 
459
 
 
460
            @compiler.catalog.expects(:vertices).returns(:resources)
 
461
 
 
462
            @compiler.expects(:store_to_active_record).with(@node, :resources)
 
463
            @compiler.send(:store)
 
464
        end
 
465
 
 
466
        it "should store to active_record" do
 
467
            @node.expects(:name).returns("myname")
 
468
            Puppet::Rails::Host.stubs(:transaction).yields
 
469
            Puppet::Rails::Host.expects(:store).with(@node, :resources)
 
470
            @compiler.send(:store_to_active_record, @node, :resources)
 
471
        end
 
472
    end
 
473
 
 
474
    describe Puppet::Parser::Compiler, "when managing resource overrides" do
 
475
 
 
476
        before do
 
477
            @override = stub 'override', :ref => "My[ref]"
 
478
            @resource = stub 'resource', :ref => "My[ref]", :builtin? => true
 
479
        end
 
480
 
 
481
        it "should be able to store overrides" do
 
482
            lambda { @compiler.add_override(@override) }.should_not raise_error
 
483
        end
 
484
 
 
485
        it "should apply overrides to the appropriate resources" do
 
486
            @compiler.add_resource(@scope, @resource)
 
487
            @resource.expects(:merge).with(@override)
 
488
 
 
489
            @compiler.add_override(@override)
 
490
 
 
491
            @compiler.compile
 
492
        end
 
493
 
 
494
        it "should accept overrides before the related resource has been created" do
 
495
            @resource.expects(:merge).with(@override)
 
496
 
 
497
            # First store the override
 
498
            @compiler.add_override(@override)
 
499
 
 
500
            # Then the resource
 
501
            @compiler.add_resource(@scope, @resource)
 
502
 
 
503
            # And compile, so they get resolved
 
504
            @compiler.compile
 
505
        end
 
506
 
 
507
        it "should fail if the compile is finished and resource overrides have not been applied" do
 
508
            @compiler.add_override(@override)
 
509
 
 
510
            lambda { @compiler.compile }.should raise_error(Puppet::ParseError)
 
511
        end
 
512
    end
 
513
 
 
514
    # #620 - Nodes and classes should conflict, else classes don't get evaluated
 
515
    describe Puppet::Parser::Compiler, "when evaluating nodes and classes with the same name (#620)" do
 
516
 
 
517
        before do
 
518
            @node = stub :nodescope? => true
 
519
            @class = stub :nodescope? => false
 
520
        end
 
521
 
 
522
        it "should fail if a node already exists with the same name as the class being evaluated" do
 
523
            @compiler.class_set("one", @node)
 
524
            lambda { @compiler.class_set("one", @class) }.should raise_error(Puppet::ParseError)
 
525
        end
 
526
 
 
527
        it "should fail if a class already exists with the same name as the node being evaluated" do
 
528
            @compiler.class_set("one", @class)
 
529
            lambda { @compiler.class_set("one", @node) }.should raise_error(Puppet::ParseError)
 
530
        end
 
531
    end
 
532
end