Fix subtle problems in end cases with using multiple scanners on a Node.
[scons.git] / src / engine / SCons / Node / NodeTests.py
1 #
2 # Copyright (c) 2001, 2002 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):
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 ListBuilder(Builder):
56     def __init__(self, *nodes):
57         self.nodes = nodes
58     def execute(self, **kw):
59         if hasattr(self, 'status'):
60             return self.status
61         for n in self.nodes:
62             n.prepare()
63         kw['target'] = self.nodes[0]
64         self.status = apply(Builder.execute, (self,), kw)
65
66 class FailBuilder:
67     def execute(self, **kw):
68         return 1
69
70 class ExceptBuilder:
71     def execute(self, **kw):
72         raise SCons.Errors.BuildError
73
74 class ExceptBuilder2:
75     def execute(self, **kw):
76         raise "foo"
77
78 class Environment:
79     def Dictionary(self, *args):
80         pass
81
82
83
84 class NodeTestCase(unittest.TestCase):
85
86     def test_BuildException(self):
87         """Test throwing an exception on build failure.
88         """
89         node = SCons.Node.Node()
90         node.builder_set(FailBuilder())
91         node.env_set(Environment())
92         try:
93             node.build()
94         except SCons.Errors.BuildError:
95             pass
96         else:
97             raise TestFailed, "did not catch expected BuildError"
98
99         node = SCons.Node.Node()
100         node.builder_set(ExceptBuilder())
101         node.env_set(Environment())
102         try:
103             node.build()
104         except SCons.Errors.BuildError:
105             pass
106         else:
107             raise TestFailed, "did not catch expected BuildError"
108
109         node = SCons.Node.Node()
110         node.builder_set(ExceptBuilder2())
111         node.env_set(Environment())
112         try:
113             node.build()
114         except SCons.Errors.BuildError, e:
115             # On a generic (non-BuildError) exception from a Builder,
116             # the Node should throw a BuildError exception with
117             # the args set to the exception value, type, and traceback.
118             assert len(e.args) == 3, `e.args`
119             assert e.args[0] == 'foo', e.args[0]
120             assert e.args[1] is None
121             assert type(e.args[2]) is type(sys.exc_traceback), e.args[2]
122         else:
123             raise TestFailed, "did not catch expected BuildError"
124
125     def test_build(self):
126         """Test building a node
127         """
128         global built_it
129
130         class MyNode(SCons.Node.Node):
131             def __init__(self, **kw):
132                 apply(SCons.Node.Node.__init__, (self,), kw)
133                 self.prepare_count = 0
134             def __str__(self):
135                 return self.path
136             def prepare(self):
137                 self.prepare_count = self.prepare_count+ 1
138         # Make sure it doesn't blow up if no builder is set.
139         node = MyNode()
140         node.build()
141         assert built_it == None
142
143         node = MyNode()
144         node.builder_set(Builder())
145         node.env_set(Environment())
146         node.path = "xxx"
147         node.sources = ["yyy", "zzz"]
148         node.build()
149         assert built_it
150         assert type(built_target) == type(MyNode()), type(built_target)
151         assert str(built_target) == "xxx", str(built_target)
152         assert built_source == ["yyy", "zzz"], built_source
153
154         built_it = None
155         node = MyNode()
156         node.builder_set(NoneBuilder())
157         node.env_set(Environment())
158         node.path = "qqq"
159         node.sources = ["rrr", "sss"]
160         node.build()
161         assert built_it
162         assert type(built_target) == type(MyNode()), type(built_target)
163         assert str(built_target) == "qqq", str(built_target)
164         assert built_source == ["rrr", "sss"], built_source
165
166         fff = MyNode()
167         ggg = MyNode()
168         lb = ListBuilder(fff, ggg)
169         e = Environment()
170         fff.builder_set(lb)
171         fff.env_set(e)
172         fff.path = "fff"
173         ggg.builder_set(lb)
174         ggg.env_set(e)
175         ggg.path = "ggg"
176         fff.sources = ["hhh", "iii"]
177         ggg.sources = ["hhh", "iii"]
178
179         built_it = None
180         fff.build()
181         assert built_it
182         ggg.build()
183         assert ggg.prepare_count== 1, ggg.prepare_count
184         assert type(built_target) == type(MyNode()), type(built_target)
185         assert str(built_target) == "fff", str(built_target)
186         assert built_source == ["hhh", "iii"], built_source
187
188         delattr(lb, 'status')
189         fff.prepare_count = 0
190         ggg.prepare_count = 0
191
192         built_it = None
193         ggg.build()
194         #assert built_it
195         fff.build()
196         assert fff.prepare_count== 1, fff.prepare_count
197         assert type(built_target) == type(MyNode()), type(built_target)
198         assert str(built_target) == "fff", str(built_target)
199         assert built_source == ["hhh", "iii"], built_source
200
201     def test_builder_set(self):
202         """Test setting a Node's Builder
203         """
204         node = SCons.Node.Node()
205         b = Builder()
206         node.builder_set(b)
207         assert node.builder == b
208
209     def test_builder_sig_adapter(self):
210         """Test the node's adapter for builder signatures
211         """
212         node = SCons.Node.Node()
213         node.builder_set(Builder())
214         node.env_set(Environment())
215         c = node.builder_sig_adapter().get_contents()
216         assert c == 7, c
217
218     def test_current(self):
219         """Test the default current() method
220         """
221         node = SCons.Node.Node()
222         assert node.current() is None
223
224     def test_env_set(self):
225         """Test setting a Node's Environment
226         """
227         node = SCons.Node.Node()
228         e = Environment()
229         node.env_set(e)
230         assert node.env == e
231
232     def test_set_bsig(self):
233         """Test setting a Node's signature
234         """
235         node = SCons.Node.Node()
236         node.set_bsig('www')
237         assert node.bsig == 'www'
238
239     def test_get_bsig(self):
240         """Test fetching a Node's signature
241         """
242         node = SCons.Node.Node()
243         node.set_bsig('xxx')
244         assert node.get_bsig() == 'xxx'
245
246     def test_set_csig(self):
247         """Test setting a Node's signature
248         """
249         node = SCons.Node.Node()
250         node.set_csig('yyy')
251         assert node.csig == 'yyy'
252
253     def test_get_csig(self):
254         """Test fetching a Node's signature
255         """
256         node = SCons.Node.Node()
257         node.set_csig('zzz')
258         assert node.get_csig() == 'zzz'
259
260     def test_store_sigs(self):
261         """Test calling the method to store signatures
262         """
263         node = SCons.Node.Node()
264         node.store_sigs()
265
266     def test_set_precious(self):
267         """Test setting a Node's precious value
268         """
269         node = SCons.Node.Node()
270         node.set_precious()
271         assert node.precious
272         node.set_precious(7)
273         assert node.precious == 7
274
275     def test_add_dependency(self):
276         """Test adding dependencies to a Node's list.
277         """
278         node = SCons.Node.Node()
279         assert node.depends == []
280
281         zero = SCons.Node.Node()
282         try:
283             node.add_dependency(zero)
284         except TypeError:
285             pass
286         else:
287             assert 0
288
289         one = SCons.Node.Node()
290         two = SCons.Node.Node()
291         three = SCons.Node.Node()
292         four = SCons.Node.Node()
293
294         node.add_dependency([one])
295         assert node.depends == [one]
296         node.add_dependency([two, three])
297         assert node.depends == [one, two, three]
298         node.add_dependency([three, four, one])
299         assert node.depends == [one, two, three, four]
300
301         assert zero.get_parents() == []
302         assert one.get_parents() == [node]
303         assert two.get_parents() == [node]
304         assert three.get_parents() == [node]
305         assert four.get_parents() == [node]
306
307
308     def test_add_source(self):
309         """Test adding sources to a Node's list.
310         """
311         node = SCons.Node.Node()
312         assert node.sources == []
313
314         zero = SCons.Node.Node()
315         try:
316             node.add_source(zero)
317         except TypeError:
318             pass
319         else:
320             assert 0
321
322         one = SCons.Node.Node()
323         two = SCons.Node.Node()
324         three = SCons.Node.Node()
325         four = SCons.Node.Node()
326
327         node.add_source([one])
328         assert node.sources == [one]
329         node.add_source([two, three])
330         assert node.sources == [one, two, three]
331         node.add_source([three, four, one])
332         assert node.sources == [one, two, three, four]
333
334         assert zero.get_parents() == []
335         assert one.get_parents() == [node]
336         assert two.get_parents() == [node]
337         assert three.get_parents() == [node]
338         assert four.get_parents() == [node]
339
340     def test_add_implicit(self):
341         """Test adding implicit (scanned) dependencies to a Node's list.
342         """
343         node = SCons.Node.Node()
344         assert node.implicit == {}
345
346         zero = SCons.Node.Node()
347         try:
348             node.add_source(zero)
349         except TypeError:
350             pass
351         else:
352             assert 0
353
354         one = SCons.Node.Node()
355         two = SCons.Node.Node()
356         three = SCons.Node.Node()
357         four = SCons.Node.Node()
358
359         node.add_implicit([one], 1)
360         assert node.implicit[1] == [one]
361         node.add_implicit([two, three], 1)
362         assert node.implicit[1] == [one, two, three]
363         node.add_implicit([three, four, one], 1)
364         assert node.implicit[1] == [one, two, three, four]
365
366         assert zero.get_parents() == []
367         assert one.get_parents() == [node]
368         assert two.get_parents() == [node]
369         assert three.get_parents() == [node]
370         assert four.get_parents() == [node]
371
372         node.add_implicit([one], 2)
373         node.add_implicit([two, three], 3)
374         node.add_implicit([three, four, one], 4)
375
376         assert node.implicit[1] == [one, two, three, four]
377         assert node.implicit[2] == [one]
378         assert node.implicit[3] == [two, three]
379         assert node.implicit[4] == [three, four, one]
380
381     def test_add_ignore(self):
382         """Test adding files whose dependencies should be ignored.
383         """
384         node = SCons.Node.Node()
385         assert node.ignore == []
386
387         zero = SCons.Node.Node()
388         try:
389             node.add_ignore(zero)
390         except TypeError:
391             pass
392         else:
393             assert 0
394
395         one = SCons.Node.Node()
396         two = SCons.Node.Node()
397         three = SCons.Node.Node()
398         four = SCons.Node.Node()
399
400         node.add_ignore([one])
401         assert node.ignore == [one]
402         node.add_ignore([two, three])
403         assert node.ignore == [one, two, three]
404         node.add_ignore([three, four, one])
405         assert node.ignore == [one, two, three, four]
406
407         assert zero.get_parents() == []
408         assert one.get_parents() == [node]
409         assert two.get_parents() == [node]
410         assert three.get_parents() == [node]
411         assert four.get_parents() == [node]
412
413     def test_scan(self):
414         """Test Scanner functionality"""
415         class DummyScanner:
416             pass
417         ds=DummyScanner()
418         node = SCons.Node.Node()
419         assert node.scanner == None, node.scanner
420         node.scanner_set(ds)
421         assert node.scanner == ds, node.scanner
422         node.scan(ds)
423         assert node.scanned[ds] == 1, node.scanned
424
425     def test_src_scanner_set(self):
426         """Test setting source-file Scanners"""
427         class DummyScanner:
428             pass
429         ds1=DummyScanner()
430         ds2=DummyScanner()
431         node = SCons.Node.Node()
432         assert node.src_scanners == {}, node.src_scanners
433         node.src_scanner_set('a', ds1)
434         assert node.src_scanners['a'] == ds1, node.src_scanners
435         node.src_scanner_set('b', ds2)
436         assert node.src_scanners['b'] == ds2, node.src_scanners
437
438     def test_src_scanner_set(self):
439         """Test setting source-file Scanners"""
440         class DummyScanner:
441             pass
442         ds1=DummyScanner()
443         ds2=DummyScanner()
444         node = SCons.Node.Node()
445         node.src_scanner_set('a', ds1)
446         node.src_scanner_set('b', ds2)
447         s = node.src_scanner_get(None)
448         assert s == None, s
449         s = node.src_scanner_get('a')
450         assert s == ds1, s
451         s = node.src_scanner_get('b')
452         assert s == ds2, s
453         s = node.src_scanner_get('c')
454         assert s == None, s
455
456     def test_scanner_key(self):
457         """Test that a scanner_key() method exists"""
458         assert SCons.Node.Node().scanner_key() == None
459
460     def test_children(self):
461         """Test fetching the non-ignored "children" of a Node.
462         """
463         node = SCons.Node.Node()
464         n1 = SCons.Node.Node()
465         n2 = SCons.Node.Node()
466         n3 = SCons.Node.Node()
467         n4 = SCons.Node.Node()
468         n5 = SCons.Node.Node()
469         n6 = SCons.Node.Node()
470         n7 = SCons.Node.Node()
471         n8 = SCons.Node.Node()
472         n9 = SCons.Node.Node()
473         n10 = SCons.Node.Node()
474         n11 = SCons.Node.Node()
475         n12 = SCons.Node.Node()
476
477         node.add_source([n1, n2, n3])
478         node.add_dependency([n4, n5, n6])
479         node.add_implicit([n7, n8, n9], 'key1')
480         node.add_implicit([n10, n11, n12], 'key2')
481         node.add_ignore([n2, n5, n8, n11])
482
483         kids = node.children(None)
484         for kid in [n1, n3, n4, n6, n7, n9, n10, n12]:
485             assert kid in kids, kid
486         for kid in [n2, n5, n8, n11]:
487             assert not kid in kids, kid
488
489         kids = node.children('key1')
490         for kid in [n1, n3, n4, n6, n7, n9]:
491             assert kid in kids, kid
492         for kid in [n2, n5, n8, n10, n11, n12]:
493             assert not kid in kids, kid
494
495         kids = node.children('key2')
496         for kid in [n1, n3, n4, n6, n10, n12]:
497             assert kid in kids, kid
498         for kid in [n2, n5, n7, n8, n9, n11]:
499             assert not kid in kids, kid
500
501     def test_all_children(self):
502         """Test fetching all the "children" of a Node.
503         """
504         node = SCons.Node.Node()
505         n1 = SCons.Node.Node()
506         n2 = SCons.Node.Node()
507         n3 = SCons.Node.Node()
508         n4 = SCons.Node.Node()
509         n5 = SCons.Node.Node()
510         n6 = SCons.Node.Node()
511         n7 = SCons.Node.Node()
512         n8 = SCons.Node.Node()
513         n9 = SCons.Node.Node()
514         n10 = SCons.Node.Node()
515         n11 = SCons.Node.Node()
516         n12 = SCons.Node.Node()
517
518         node.add_source([n1, n2, n3])
519         node.add_dependency([n4, n5, n6])
520         node.add_implicit([n7, n8, n9], 'key1')
521         node.add_implicit([n10, n11, n12], 'key2')
522         node.add_ignore([n2, n5, n8, n11])
523
524         kids = node.all_children(None)
525         for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12]:
526             assert kid in kids
527
528         kids = node.all_children('key1')
529         for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9]:
530             assert kid in kids
531         for kid in [n10, n11, n12]:
532             assert not kid in kids
533
534         kids = node.all_children('key2')
535         for kid in [n1, n2, n3, n4, n5, n6, n10, n11, n12]:
536             assert kid in kids
537         for kid in [n7, n8, n9]:
538             assert not kid in kids
539
540     def test_state(self):
541         """Test setting and getting the state of a node
542         """
543         node = SCons.Node.Node()
544         assert node.get_state() == None
545         node.set_state(SCons.Node.executing)
546         assert node.get_state() == SCons.Node.executing
547         assert SCons.Node.pending < SCons.Node.executing
548         assert SCons.Node.executing < SCons.Node.up_to_date
549         assert SCons.Node.up_to_date < SCons.Node.executed
550         assert SCons.Node.executed < SCons.Node.failed
551
552     def test_walker(self):
553         """Test walking a Node tree.
554         """
555
556         class MyNode(SCons.Node.Node):
557             def __init__(self, name):
558                 SCons.Node.Node.__init__(self)
559                 self.name = name
560
561         n1 = MyNode("n1")
562
563         nw = SCons.Node.Walker(n1)
564         assert not nw.is_done()
565         assert nw.next().name ==  "n1"
566         assert nw.is_done()
567         assert nw.next() == None
568
569         n2 = MyNode("n2")
570         n3 = MyNode("n3")
571         n1.add_source([n2, n3])
572
573         nw = SCons.Node.Walker(n1)
574         assert nw.next().name ==  "n2"
575         assert nw.next().name ==  "n3"
576         assert nw.next().name ==  "n1"
577         assert nw.next() == None
578
579         n4 = MyNode("n4")
580         n5 = MyNode("n5")
581         n6 = MyNode("n6")
582         n7 = MyNode("n7")
583         n2.add_source([n4, n5])
584         n3.add_dependency([n6, n7])
585
586         nw = SCons.Node.Walker(n1)
587         assert nw.next().name ==  "n4"
588         assert nw.next().name ==  "n5"
589         assert nw.history.has_key(n2)
590         assert nw.next().name ==  "n2"
591         assert nw.next().name ==  "n6"
592         assert nw.next().name ==  "n7"
593         assert nw.history.has_key(n3)
594         assert nw.next().name ==  "n3"
595         assert nw.history.has_key(n1)
596         assert nw.next().name ==  "n1"
597         assert nw.next() == None
598
599         n8 = MyNode("n8")
600         n8.add_dependency([n3])
601         n7.add_dependency([n8])
602
603         def cycle(node, stack):
604             global cycle_detected
605             cycle_detected = 1
606
607         global cycle_detected
608
609         nw = SCons.Node.Walker(n3, cycle_func = cycle)
610         n = nw.next()
611         assert n.name == "n6", n.name
612         n = nw.next()
613         assert n.name == "n8", n.name
614         assert cycle_detected
615         cycle_detected = None
616         n = nw.next()
617         assert n.name == "n7", n.name
618         n = nw.next()
619         assert nw.next() == None
620
621     def test_children_are_executed(self):
622         n1 = SCons.Node.Node()
623         n2 = SCons.Node.Node()
624         n3 = SCons.Node.Node()
625         n4 = SCons.Node.Node()
626
627         n4.add_source([n3])
628         n3.add_source([n1, n2])
629
630         assert not n4.children_are_executed(None)
631         assert not n3.children_are_executed(None)
632         assert n2.children_are_executed(None)
633         assert n1.children_are_executed(None)
634
635         n1.set_state(SCons.Node.executed)
636         assert not n4.children_are_executed(None)
637         assert not n3.children_are_executed(None)
638         assert n2.children_are_executed(None)
639         assert n1.children_are_executed(None)
640
641         n2.set_state(SCons.Node.executed)
642         assert not n4.children_are_executed(None)
643         assert n3.children_are_executed(None)
644         assert n2.children_are_executed(None)
645         assert n1.children_are_executed(None)
646
647         n3.set_state(SCons.Node.executed)
648         assert n4.children_are_executed(None)
649         assert n3.children_are_executed(None)
650         assert n2.children_are_executed(None)
651         assert n1.children_are_executed(None)
652
653     def test_rescan(self):
654         """Test that built node implicit dependencies are cleared
655         to be rescanned."""
656         class DummyScanner:
657             pass
658         
659         class TestNode(SCons.Node.Node):
660             def scan(self, scanner):
661                 if not self.scanned.has_key(scanner):
662                     n=SCons.Node.Node()
663                     n.scanner_set(scanner)
664                     self.add_implicit([ n ], scanner)
665                 self.scanned[scanner] = 1
666         tn=TestNode()
667         tn.builder_set(Builder())
668         tn.env_set(Environment())
669         ds = DummyScanner()
670         tn.scan(ds)
671         map(lambda x: x.scan(), tn.depends)
672         assert tn.scanned[ds]
673         assert len(tn.implicit[ds]) == 1, tn.implicit
674         tn.scan(ds)
675         assert tn.scanned[ds]
676         assert len(tn.implicit[ds]) == 1, tn.implicit
677         tn.build()
678         assert not tn.scanned.has_key(ds)
679         assert len(tn.implicit[ds]) == 1, tn.implicit
680         tn.scan(ds)
681         assert tn.scanned[ds]
682         assert len(tn.implicit[ds]) == 2, tn.implicit
683
684 if __name__ == "__main__":
685     suite = unittest.makeSuite(NodeTestCase, 'test_')
686     if not unittest.TextTestRunner().run(suite).wasSuccessful():
687         sys.exit(1)