Add the Precious() method.
[scons.git] / src / engine / SCons / Node / NodeTests.py
1 #
2 # Copyright (c) 2001 Steven Knight
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import os
27 import sys
28 import unittest
29
30 import SCons.Errors
31 import SCons.Node
32
33
34
35 built_it = None
36 built_target =  None
37 built_source =  None
38 cycle_detected = None
39
40 class Builder:
41     def execute(self, **kw):
42         global built_it, built_target, built_source
43         built_it = 1
44         built_target = kw['target']
45         built_source = kw['source']
46         return 0
47     def get_contents(self, env, dir):
48         return 7
49
50 class NoneBuilder(Builder):
51     def execute(self, **kw):
52         apply(Builder.execute, (self,), kw)
53         return None
54
55 class FailBuilder:
56     def execute(self, **kw):
57         return 1
58
59 class ExceptBuilder:
60     def execute(self, **kw):
61         raise SCons.Errors.BuildError
62
63 class ExceptBuilder2:
64     def execute(self, **kw):
65         raise "foo"
66
67 class Environment:
68     def Dictionary(self, *args):
69         pass
70
71
72
73 class NodeTestCase(unittest.TestCase):
74
75     def test_BuildException(self):
76         """Test throwing an exception on build failure.
77         """
78         node = SCons.Node.Node()
79         node.builder_set(FailBuilder())
80         node.env_set(Environment())
81         try:
82             node.build()
83         except SCons.Errors.BuildError:
84             pass
85         else:
86             raise TestFailed, "did not catch expected BuildError"
87
88         node = SCons.Node.Node()
89         node.builder_set(ExceptBuilder())
90         node.env_set(Environment())
91         try:
92             node.build()
93         except SCons.Errors.BuildError:
94             pass
95         else:
96             raise TestFailed, "did not catch expected BuildError"
97
98         node = SCons.Node.Node()
99         node.builder_set(ExceptBuilder2())
100         node.env_set(Environment())
101         try:
102             node.build()
103         except SCons.Errors.BuildError, e:
104             # On a generic (non-BuildError) exception from a Builder,
105             # the Node should throw a BuildError exception with
106             # the args set to the exception value, type, and traceback.
107             assert len(e.args) == 3, `e.args`
108             assert e.args[0] == 'foo', e.args[0]
109             assert e.args[1] is None
110             assert type(e.args[2]) is type(sys.exc_traceback), e.args[2]
111         else:
112             raise TestFailed, "did not catch expected BuildError"
113
114     def test_build(self):
115         """Test building a node
116         """
117         global built_it
118
119         class MyNode(SCons.Node.Node):
120             def __str__(self):
121                 return self.path
122         # Make sure it doesn't blow up if no builder is set.
123         node = MyNode()
124         node.build()
125         assert built_it == None
126
127         node = MyNode()
128         node.builder_set(Builder())
129         node.env_set(Environment())
130         node.path = "xxx"
131         node.sources = ["yyy", "zzz"]
132         node.build()
133         assert built_it
134         assert type(built_target) == type(MyNode()), type(built_target)
135         assert str(built_target) == "xxx", str(built_target)
136         assert built_source == ["yyy", "zzz"], built_source
137
138         built_it = None
139         node = MyNode()
140         node.builder_set(NoneBuilder())
141         node.env_set(Environment())
142         node.path = "qqq"
143         node.sources = ["rrr", "sss"]
144         node.build()
145         assert built_it
146         assert type(built_target) == type(MyNode()), type(built_target)
147         assert str(built_target) == "qqq", str(built_target)
148         assert built_source == ["rrr", "sss"], built_source
149
150     def test_builder_set(self):
151         """Test setting a Node's Builder
152         """
153         node = SCons.Node.Node()
154         b = Builder()
155         node.builder_set(b)
156         assert node.builder == b
157
158     def test_builder_sig_adapter(self):
159         """Test the node's adapter for builder signatures
160         """
161         node = SCons.Node.Node()
162         node.builder_set(Builder())
163         node.env_set(Environment())
164         c = node.builder_sig_adapter().get_contents()
165         assert c == 7, c
166
167     def test_current(self):
168         """Test the default current() method
169         """
170         node = SCons.Node.Node()
171         assert node.current() is None
172
173     def test_env_set(self):
174         """Test setting a Node's Environment
175         """
176         node = SCons.Node.Node()
177         e = Environment()
178         node.env_set(e)
179         assert node.env == e
180
181     def test_set_bsig(self):
182         """Test setting a Node's signature
183         """
184         node = SCons.Node.Node()
185         node.set_bsig('www')
186         assert node.bsig == 'www'
187
188     def test_get_bsig(self):
189         """Test fetching a Node's signature
190         """
191         node = SCons.Node.Node()
192         node.set_bsig('xxx')
193         assert node.get_bsig() == 'xxx'
194
195     def test_set_csig(self):
196         """Test setting a Node's signature
197         """
198         node = SCons.Node.Node()
199         node.set_csig('yyy')
200         assert node.csig == 'yyy'
201
202     def test_get_csig(self):
203         """Test fetching a Node's signature
204         """
205         node = SCons.Node.Node()
206         node.set_csig('zzz')
207         assert node.get_csig() == 'zzz'
208
209     def test_set_precious(self):
210         """Test setting a Node's precious value
211         """
212         node = SCons.Node.Node()
213         node.set_precious()
214         assert node.precious
215         node.set_precious(7)
216         assert node.precious == 7
217
218     def test_add_dependency(self):
219         """Test adding dependencies to a Node's list.
220         """
221         node = SCons.Node.Node()
222         assert node.depends == []
223
224         zero = SCons.Node.Node()
225         try:
226             node.add_dependency(zero)
227         except TypeError:
228             pass
229         else:
230             assert 0
231
232         one = SCons.Node.Node()
233         two = SCons.Node.Node()
234         three = SCons.Node.Node()
235         four = SCons.Node.Node()
236
237         node.add_dependency([one])
238         assert node.depends == [one]
239         node.add_dependency([two, three])
240         assert node.depends == [one, two, three]
241         node.add_dependency([three, four, one])
242         assert node.depends == [one, two, three, four]
243
244         assert zero.get_parents() == []
245         assert one.get_parents() == [node]
246         assert two.get_parents() == [node]
247         assert three.get_parents() == [node]
248         assert four.get_parents() == [node]
249
250
251     def test_add_source(self):
252         """Test adding sources to a Node's list.
253         """
254         node = SCons.Node.Node()
255         assert node.sources == []
256
257         zero = SCons.Node.Node()
258         try:
259             node.add_source(zero)
260         except TypeError:
261             pass
262         else:
263             assert 0
264
265         one = SCons.Node.Node()
266         two = SCons.Node.Node()
267         three = SCons.Node.Node()
268         four = SCons.Node.Node()
269
270         node.add_source([one])
271         assert node.sources == [one]
272         node.add_source([two, three])
273         assert node.sources == [one, two, three]
274         node.add_source([three, four, one])
275         assert node.sources == [one, two, three, four]
276
277         assert zero.get_parents() == []
278         assert one.get_parents() == [node]
279         assert two.get_parents() == [node]
280         assert three.get_parents() == [node]
281         assert four.get_parents() == [node]
282
283     def test_add_implicit(self):
284         """Test adding implicit (scanned) dependencies to a Node's list.
285         """
286         node = SCons.Node.Node()
287         assert node.implicit == {}
288
289         zero = SCons.Node.Node()
290         try:
291             node.add_source(zero)
292         except TypeError:
293             pass
294         else:
295             assert 0
296
297         one = SCons.Node.Node()
298         two = SCons.Node.Node()
299         three = SCons.Node.Node()
300         four = SCons.Node.Node()
301
302         node.add_implicit([one], 1)
303         assert node.implicit[1] == [one]
304         node.add_implicit([two, three], 1)
305         assert node.implicit[1] == [one, two, three]
306         node.add_implicit([three, four, one], 1)
307         assert node.implicit[1] == [one, two, three, four]
308
309         assert zero.get_parents() == []
310         assert one.get_parents() == [node]
311         assert two.get_parents() == [node]
312         assert three.get_parents() == [node]
313         assert four.get_parents() == [node]
314
315         node.add_implicit([one], 2)
316         node.add_implicit([two, three], 3)
317         node.add_implicit([three, four, one], 4)
318
319         assert node.implicit[1] == [one, two, three, four]
320         assert node.implicit[2] == [one]
321         assert node.implicit[3] == [two, three]
322         assert node.implicit[4] == [three, four, one]
323
324     def test_add_ignore(self):
325         """Test adding files whose dependencies should be ignored.
326         """
327         node = SCons.Node.Node()
328         assert node.ignore == []
329
330         zero = SCons.Node.Node()
331         try:
332             node.add_ignore(zero)
333         except TypeError:
334             pass
335         else:
336             assert 0
337
338         one = SCons.Node.Node()
339         two = SCons.Node.Node()
340         three = SCons.Node.Node()
341         four = SCons.Node.Node()
342
343         node.add_ignore([one])
344         assert node.ignore == [one]
345         node.add_ignore([two, three])
346         assert node.ignore == [one, two, three]
347         node.add_ignore([three, four, one])
348         assert node.ignore == [one, two, three, four]
349
350         assert zero.get_parents() == []
351         assert one.get_parents() == [node]
352         assert two.get_parents() == [node]
353         assert three.get_parents() == [node]
354         assert four.get_parents() == [node]
355
356     def test_scan(self):
357         """Test Scanner functionality"""
358         class DummyScanner:
359             pass
360         ds=DummyScanner()
361         node = SCons.Node.Node()
362         assert node.scanners == [], node.scanners
363         node.scanner_set(ds)
364         assert node.scanners == [ ds ], node.scanners
365         node.scan()
366         assert node.scanned[ds] == 1, node.scanned
367
368     def test_children(self):
369         """Test fetching the "children" of a Node.
370         """
371         node = SCons.Node.Node()
372         one = SCons.Node.Node()
373         two = SCons.Node.Node()
374         three = SCons.Node.Node()
375         four = SCons.Node.Node()
376         five = SCons.Node.Node()
377         six = SCons.Node.Node()
378
379         node.add_source([one, two, three])
380         node.add_dependency([four, five, six])
381         kids = node.children()
382         assert len(kids) == 6
383         assert one in kids
384         assert two in kids
385         assert three in kids
386         assert four in kids
387         assert five in kids
388         assert six in kids
389
390     def test_add_parent(self):
391         """Test adding parents to a Node."""
392         node = SCons.Node.Node()
393         parent = SCons.Node.Node()
394         node._add_parent(parent)
395         assert node.get_parents() == [parent]
396         node._add_parent(parent)
397         assert node.get_parents() == [parent]
398
399     def test_state(self):
400         """Test setting and getting the state of a node
401         """
402         node = SCons.Node.Node()
403         assert node.get_state() == None
404         node.set_state(SCons.Node.executing)
405         assert node.get_state() == SCons.Node.executing
406         assert SCons.Node.pending < SCons.Node.executing
407         assert SCons.Node.executing < SCons.Node.up_to_date
408         assert SCons.Node.up_to_date < SCons.Node.executed
409         assert SCons.Node.executed < SCons.Node.failed
410
411     def test_walker(self):
412         """Test walking a Node tree.
413         """
414
415         class MyNode(SCons.Node.Node):
416             def __init__(self, name):
417                 SCons.Node.Node.__init__(self)
418                 self.name = name
419
420         n1 = MyNode("n1")
421
422         nw = SCons.Node.Walker(n1)
423         assert not nw.is_done()
424         assert nw.next().name ==  "n1"
425         assert nw.is_done()
426         assert nw.next() == None
427
428         n2 = MyNode("n2")
429         n3 = MyNode("n3")
430         n1.add_source([n2, n3])
431
432         nw = SCons.Node.Walker(n1)
433         assert nw.next().name ==  "n2"
434         assert nw.next().name ==  "n3"
435         assert nw.next().name ==  "n1"
436         assert nw.next() == None
437
438         n4 = MyNode("n4")
439         n5 = MyNode("n5")
440         n6 = MyNode("n6")
441         n7 = MyNode("n7")
442         n2.add_source([n4, n5])
443         n3.add_dependency([n6, n7])
444
445         nw = SCons.Node.Walker(n1)
446         assert nw.next().name ==  "n4"
447         assert nw.next().name ==  "n5"
448         assert nw.history.has_key(n2)
449         assert nw.next().name ==  "n2"
450         assert nw.next().name ==  "n6"
451         assert nw.next().name ==  "n7"
452         assert nw.history.has_key(n3)
453         assert nw.next().name ==  "n3"
454         assert nw.history.has_key(n1)
455         assert nw.next().name ==  "n1"
456         assert nw.next() == None
457
458         n8 = MyNode("n8")
459         n8.add_dependency([n3])
460         n7.add_dependency([n8])
461
462         def cycle(node, stack):
463             global cycle_detected
464             cycle_detected = 1
465
466         global cycle_detected
467
468         nw = SCons.Node.Walker(n3, cycle_func = cycle)
469         n = nw.next()
470         assert n.name == "n6", n.name
471         n = nw.next()
472         assert n.name == "n8", n.name
473         assert cycle_detected
474         cycle_detected = None
475         n = nw.next()
476         assert n.name == "n7", n.name
477         n = nw.next()
478         assert nw.next() == None
479
480     def test_children_are_executed(self):
481         n1 = SCons.Node.Node()
482         n2 = SCons.Node.Node()
483         n3 = SCons.Node.Node()
484         n4 = SCons.Node.Node()
485
486         n4.add_source([n3])
487         n3.add_source([n1, n2])
488
489         assert not n4.children_are_executed()
490         assert not n3.children_are_executed()
491         assert n2.children_are_executed()
492         assert n1.children_are_executed()
493
494         n1.set_state(SCons.Node.executed)
495         assert not n4.children_are_executed()
496         assert not n3.children_are_executed()
497         assert n2.children_are_executed()
498         assert n1.children_are_executed()
499
500         n2.set_state(SCons.Node.executed)
501         assert not n4.children_are_executed()
502         assert n3.children_are_executed()
503         assert n2.children_are_executed()
504         assert n1.children_are_executed()
505
506         n3.set_state(SCons.Node.executed)
507         assert n4.children_are_executed()
508         assert n3.children_are_executed()
509         assert n2.children_are_executed()
510         assert n1.children_are_executed()
511
512     def test_rescan(self):
513         """Test that built nodes are rescanned."""
514         class DummyScanner:
515             pass
516         
517         class TestNode(SCons.Node.Node):
518             def scan(self):
519                 for scn in self.scanners:
520                     if not self.scanned.has_key(scn):
521                         n=SCons.Node.Node()
522                         n.scanner_set(scn)
523                         self.add_implicit([ n ], scn)
524                     self.scanned[scn] = 1
525         tn=TestNode()
526         tn.builder_set(Builder())
527         tn.env_set(Environment())
528         ds = DummyScanner()
529         tn.scanner_set(ds)
530         tn.scan()
531         map(lambda x: x.scan(), tn.depends)
532         assert tn.scanned[ds]
533         assert len(tn.implicit[ds]) == 1, tn.implicit
534         tn.build()
535         assert len(tn.implicit[ds]) == 2, tn.implicit
536         for dep in tn.implicit[ds]:
537             assert dep.scanned[ds] == 1
538
539 if __name__ == "__main__":
540     suite = unittest.makeSuite(NodeTestCase, 'test_')
541     if not unittest.TextTestRunner().run(suite).wasSuccessful():
542         sys.exit(1)