f8cd62b7617c4250310a015998f0993f548d08f4
[scons.git] / src / engine / SCons / Node / NodeTests.py
1 #
2 # __COPYRIGHT__
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 types
29 import unittest
30
31 import SCons.Errors
32 import SCons.Node
33
34
35
36 built_it = None
37 built_target =  None
38 built_source =  None
39 cycle_detected = None
40 built_order = 0
41
42 class MyAction:
43     def __init__(self):
44         self.order = 0
45
46     def __call__(self, target, source, env, errfunc):
47         global built_it, built_target, built_source, built_args, built_order
48         built_it = 1
49         built_target = target
50         built_source = source
51         built_args = env
52         built_order = built_order + 1
53         self.order = built_order
54         return 0
55
56     def get_actions(self):
57         return [self]
58
59 class MyNonGlobalAction:
60     def __init__(self):
61         self.order = 0
62         self.built_it = None
63         self.built_target =  None
64         self.built_source =  None
65
66     def __call__(self, target, source, env, errfunc):
67         # Okay, so not ENTIRELY non-global...
68         global built_order
69         self.built_it = 1
70         self.built_target = target
71         self.built_source = source
72         self.built_args = env
73         built_order = built_order + 1
74         self.order = built_order
75         return 0
76
77     def get_actions(self):
78         return [self]
79
80 class Environment:
81     def __init__(self, **kw):
82         self._dict = {}
83         self._dict.update(kw)
84     def __getitem__(self, key):
85         return self._dict[key]
86     def Dictionary(self, *args):
87         return {}
88     def Override(self, overrides):
89         d = self._dict.copy()
90         d.update(overrides)
91         return apply(Environment, (), d)
92     def _update(self, dict):
93         self._dict.update(dict)
94
95 class Builder:
96     def __init__(self):
97         self.env = Environment()
98         self.overrides = {}
99         self.action = MyAction()
100     def targets(self, t):
101         return [t]
102     def get_actions(self):
103         return [self.action]
104     def get_contents(self, target, source, env):
105         return 7
106
107 class NoneBuilder(Builder):
108     def execute(self, target, source, env):
109         Builder.execute(self, target, source, env)
110         return None
111
112 class ListBuilder(Builder):
113     def __init__(self, *nodes):
114         Builder.__init__(self)
115         self.nodes = nodes
116     def execute(self, target, source, env):
117         if hasattr(self, 'status'):
118             return self.status
119         for n in self.nodes:
120             n.prepare()
121         target = self.nodes[0]
122         self.status = Builder.execute(self, target, source, env)
123
124 class FailBuilder:
125     def execute(self, target, source, env):
126         return 1
127
128 class ExceptBuilder:
129     def execute(self, target, source, env):
130         raise SCons.Errors.BuildError
131
132 class ExceptBuilder2:
133     def execute(self, target, source, env):
134         raise "foo"
135
136 class Scanner:
137     called = None
138     def __call__(self, node):
139         self.called = 1
140         return node.found_includes
141     def select(self, node):
142         return self
143
144 class MyNode(SCons.Node.Node):
145     """The base Node class contains a number of do-nothing methods that
146     we expect to be overridden by real, functional Node subclasses.  So
147     simulate a real, functional Node subclass.
148     """
149     def __init__(self, name):
150         SCons.Node.Node.__init__(self)
151         self.name = name
152         self.found_includes = []
153     def __str__(self):
154         return self.name
155     def get_found_includes(self, env, scanner, target):
156         return scanner(self)
157
158
159
160 class NodeTestCase(unittest.TestCase):
161
162     def test_build(self):
163         """Test building a node
164         """
165         global built_it, built_order
166
167         # Make sure it doesn't blow up if no builder is set.
168         node = MyNode("www")
169         node.build()
170         assert built_it == None
171         node.build(extra_kw_argument = 1)
172         assert built_it == None
173
174         node = MyNode("xxx")
175         node.builder_set(Builder())
176         node.env_set(Environment())
177         node.path = "xxx"
178         node.sources = ["yyy", "zzz"]
179         node.build()
180         assert built_it
181         assert built_target == [node], built_target
182         assert built_source == ["yyy", "zzz"], built_source
183
184         built_it = None
185         node = MyNode("qqq")
186         node.builder_set(NoneBuilder())
187         node.env_set(Environment())
188         node.path = "qqq"
189         node.sources = ["rrr", "sss"]
190         node.builder.overrides = { "foo" : 1, "bar" : 2 }
191         node.build()
192         assert built_it
193         assert built_target == [node], built_target
194         assert built_source == ["rrr", "sss"], built_source
195         assert built_args["foo"] == 1, built_args
196         assert built_args["bar"] == 2, built_args
197
198         fff = MyNode("fff")
199         ggg = MyNode("ggg")
200         lb = ListBuilder(fff, ggg)
201         e = Environment()
202         fff.builder_set(lb)
203         fff.env_set(e)
204         fff.path = "fff"
205         ggg.builder_set(lb)
206         ggg.env_set(e)
207         ggg.path = "ggg"
208         fff.sources = ["hhh", "iii"]
209         ggg.sources = ["hhh", "iii"]
210         # [Charles C. 1/7/2002] Uhhh, why are there no asserts here?
211         # [SK, 15 May 2003] I dunno, let's add some...
212         built_it = None
213         fff.build()
214         assert built_it
215         assert built_target == [fff], built_target
216         assert built_source == ["hhh", "iii"], built_source
217         built_it = None
218         ggg.build()
219         assert built_it
220         assert built_target == [ggg], built_target
221         assert built_source == ["hhh", "iii"], built_source
222
223         built_it = None
224         jjj = MyNode("jjj")
225         b = Builder()
226         jjj.builder_set(b)
227         # NOTE:  No env_set()!  We should pull the environment from the builder.
228         b.env = Environment()
229         b.overrides = { "on" : 3, "off" : 4 }
230         e.builder = b
231         jjj.build()
232         assert built_it
233         assert built_target[0] == jjj, built_target[0]
234         assert built_source == [], built_source
235         assert built_args["on"] == 3, built_args
236         assert built_args["off"] == 4, built_args
237
238         built_it = None
239         built_order = 0
240         node = MyNode("xxx")
241         node.builder_set(Builder())
242         node.env_set(Environment())
243         node.sources = ["yyy", "zzz"]
244         pre1 = MyNonGlobalAction()
245         pre2 = MyNonGlobalAction()
246         post1 = MyNonGlobalAction()
247         post2 = MyNonGlobalAction()
248         node.add_pre_action(pre1)
249         node.add_pre_action(pre2)
250         node.add_post_action(post1)
251         node.add_post_action(post2)
252         node.build()
253         assert built_it
254         assert pre1.built_it
255         assert pre2.built_it
256         assert post1.built_it
257         assert post2.built_it
258         assert pre1.order == 1, pre1.order
259         assert pre2.order == 2, pre1.order
260         # The action of the builder itself is order 3...
261         assert post1.order == 4, pre1.order
262         assert post2.order == 5, pre1.order
263
264         for act in [ pre1, pre2, post1, post2 ]:
265             assert type(act.built_target[0]) == type(MyNode("bar")), type(act.built_target[0])
266             assert str(act.built_target[0]) == "xxx", str(act.built_target[0])
267             assert act.built_source == ["yyy", "zzz"], act.built_source
268
269     def test_built(self):
270         """Test the built() method"""
271         class SubNode(SCons.Node.Node):
272             def clear(self):
273                 self.cleared = 1
274
275         n = SubNode()
276         n.built()
277         assert n.cleared, n.cleared
278
279     def test_retrieve_from_cache(self):
280         """Test the base retrieve_from_cache() method"""
281         n = SCons.Node.Node()
282         r = n.retrieve_from_cache()
283         assert r == 0, r
284
285     def test_visited(self):
286         """Test the base visited() method
287
288         Just make sure it's there and we can call it.
289         """
290         n = SCons.Node.Node()
291         n.visited()
292
293     def test_depends_on(self):
294         """Test the depends_on() method
295         """
296         parent = SCons.Node.Node()
297         child = SCons.Node.Node()
298         parent.add_dependency([child])
299         assert parent.depends_on([child])
300
301     def test_builder_set(self):
302         """Test setting a Node's Builder
303         """
304         node = SCons.Node.Node()
305         b = Builder()
306         node.builder_set(b)
307         assert node.builder == b
308
309     def test_has_builder(self):
310         """Test the has_builder() method
311         """
312         n1 = SCons.Node.Node()
313         assert n1.has_builder() == 0
314         n1.builder_set(Builder())
315         assert n1.has_builder() == 1
316
317     def test_multiple_side_effect_has_builder(self):
318         """Test the multiple_side_effect_has_builder() method
319         """
320         n1 = SCons.Node.Node()
321         assert n1.multiple_side_effect_has_builder() == 0
322         n1.builder_set(Builder())
323         assert n1.multiple_side_effect_has_builder() == 1
324
325     def test_is_derived(self):
326         """Test the is_derived() method
327         """
328         n1 = SCons.Node.Node()
329         n2 = SCons.Node.Node()
330         n3 = SCons.Node.Node()
331
332         n2.builder_set(Builder())
333         n3.side_effect = 1
334
335         assert n1.is_derived() == 0
336         assert n2.is_derived() == 1
337         assert n3.is_derived() == 1
338
339     def test_alter_targets(self):
340         """Test the alter_targets() method
341         """
342         n = SCons.Node.Node()
343         t, m = n.alter_targets()
344         assert t == [], t
345         assert m == None, m
346
347     def test_current(self):
348         """Test the default current() method
349         """
350         node = SCons.Node.Node()
351         assert node.current() is None
352
353     def test_env_set(self):
354         """Test setting a Node's Environment
355         """
356         node = SCons.Node.Node()
357         e = Environment()
358         node.env_set(e)
359         assert node.env == e
360
361     def test_get_actions(self):
362         """Test fetching a Node's action list
363         """
364         node = SCons.Node.Node()
365         node.builder_set(Builder())
366         a = node.builder.get_actions()
367         assert isinstance(a[0], MyAction), a[0]
368
369     def test_calc_bsig(self):
370         """Test generic build signature calculation
371         """
372         class Calculator:
373             def __init__(self, val):
374                 self.max_drift = 0
375                 class M:
376                     def __init__(self, val):
377                         self.val = val
378                     def collect(self, args):
379                         return reduce(lambda x, y: x+y, args, self.val)
380                 self.module = M(val)
381         node = SCons.Node.Node()
382         result = node.calc_bsig(Calculator(222))
383         assert result == 222, result
384         result = node.calc_bsig(Calculator(333))
385         assert result == 222, result
386
387     def test_calc_csig(self):
388         """Test generic content signature calculation
389         """
390         class Calculator:
391             def __init__(self, val):
392                 self.max_drift = 0
393                 class M:
394                     def __init__(self, val):
395                         self.val = val
396                     def signature(self, args):
397                         return self.val
398                 self.module = M(val)
399         node = SCons.Node.Node()
400         result = node.calc_csig(Calculator(444))
401         assert result == 444, result
402         result = node.calc_csig(Calculator(555))
403         assert result == 444, result
404
405     def test_gen_binfo(self):
406         """Test generating a build information structure
407         """
408         class Calculator:
409             def __init__(self, val):
410                 self.max_drift = 0
411                 class M:
412                     def __init__(self, val):
413                         self.val = val
414                     def collect(self, args):
415                         return reduce(lambda x, y: x+y, args, self.val)
416                 self.module = M(val)
417
418         node = SCons.Node.Node()
419         binfo = node.gen_binfo(Calculator(666))
420         assert isinstance(binfo, SCons.Node.BuildInfo), binfo
421         assert hasattr(binfo, 'bsources')
422         assert hasattr(binfo, 'bsourcesigs')
423         assert hasattr(binfo, 'bdepends')
424         assert hasattr(binfo, 'bdependsigs')
425         assert hasattr(binfo, 'bimplicit')
426         assert hasattr(binfo, 'bimplicitsigs')
427         assert binfo.bsig == 666, binfo.bsig
428
429     def test_explain(self):
430         """Test explaining why a Node must be rebuilt
431         """
432         node = SCons.Node.Node()
433         node.exists = lambda: None
434         node.__str__ = lambda: 'xyzzy'
435         result = node.explain()
436         assert result == "building `xyzzy' because it doesn't exist\n", result
437
438         node = SCons.Node.Node()
439         result = node.explain()
440         assert result == None, result
441
442         class Null_BInfo:
443             def __init__(self):
444                 pass
445
446         node.get_stored_info = Null_BInfo
447         node.__str__ = lambda: 'null_binfo'
448         result = node.explain()
449         assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result
450
451         # XXX additional tests for the guts of the functionality some day
452
453     def test_del_binfo(self):
454         """Test deleting the build information from a Node
455         """
456         node = SCons.Node.Node()
457         node.binfo = None
458         node.del_binfo()
459         assert not hasattr(node, 'binfo'), node
460
461     def test_store_info(self):
462         """Test calling the method to store build information
463         """
464         class Entry:
465             pass
466         node = SCons.Node.Node()
467         node.store_info(Entry())
468
469     def test_get_stored_info(self):
470         """Test calling the method to fetch stored build information
471         """
472         node = SCons.Node.Node()
473         result = node.get_stored_info()
474         assert result is None, result
475
476     def test_set_always_build(self):
477         """Test setting a Node's always_build value
478         """
479         node = SCons.Node.Node()
480         node.set_always_build()
481         assert node.always_build
482         node.set_always_build(3)
483         assert node.always_build == 3
484
485     def test_set_precious(self):
486         """Test setting a Node's precious value
487         """
488         node = SCons.Node.Node()
489         node.set_precious()
490         assert node.precious
491         node.set_precious(7)
492         assert node.precious == 7
493
494     def test_exists(self):
495         """Test evaluating whether a Node exists.
496         """
497         node = SCons.Node.Node()
498         e = node.exists()
499         assert e == 1, e
500
501     def test_exists(self):
502         """Test evaluating whether a Node exists locally or in a repository.
503         """
504         node = SCons.Node.Node()
505         e = node.rexists()
506         assert e == 1, e
507
508         class MyNode(SCons.Node.Node):
509             def exists(self):
510                 return 'xyz'
511
512         node = MyNode()
513         e = node.rexists()
514         assert e == 'xyz', e
515
516     def test_prepare(self):
517         """Test preparing a node to be built
518         """
519         node = SCons.Node.Node()
520
521         n1 = SCons.Node.Node()
522         n1.builder_set(Builder())
523         node.implicit = []
524         node.implicit_dict = {}
525         node._add_child(node.implicit, node.implicit_dict, [n1])
526
527         node.prepare()  # should not throw an exception
528
529         n2 = SCons.Node.Node()
530         n2.linked = 1
531         node.implicit = []
532         node.implicit_dict = {}
533         node._add_child(node.implicit, node.implicit_dict, [n2])
534
535         node.prepare()  # should not throw an exception
536
537         n3 = SCons.Node.Node()
538         node.implicit = []
539         node.implicit_dict = {}
540         node._add_child(node.implicit, node.implicit_dict, [n3])
541
542         node.prepare()  # should not throw an exception
543
544         class MyNode(SCons.Node.Node):
545             def rexists(self):
546                 return None
547         n4 = MyNode()
548         node.implicit = []
549         node.implicit_dict = {}
550         node._add_child(node.implicit, node.implicit_dict, [n4])
551         exc_caught = 0
552         try:
553             node.prepare()
554         except SCons.Errors.StopError:
555             exc_caught = 1
556         assert exc_caught, "did not catch expected StopError"
557
558     def test_add_dependency(self):
559         """Test adding dependencies to a Node's list.
560         """
561         node = SCons.Node.Node()
562         assert node.depends == []
563
564         zero = SCons.Node.Node()
565
566         one = SCons.Node.Node()
567         two = SCons.Node.Node()
568         three = SCons.Node.Node()
569         four = SCons.Node.Node()
570         five = SCons.Node.Node()
571         six = SCons.Node.Node()
572
573         node.add_dependency(zero)
574         assert node.depends == [zero]
575         node.add_dependency([one])
576         assert node.depends == [zero, one]
577         node.add_dependency([two, three])
578         assert node.depends == [zero, one, two, three]
579         node.add_dependency([three, four, one])
580         assert node.depends == [zero, one, two, three, four]
581
582         try:
583             node.add_depends([[five, six]])
584         except:
585             pass
586         else:
587             raise "did not catch expected exception"
588         assert node.depends == [zero, one, two, three, four]
589
590
591     def test_add_source(self):
592         """Test adding sources to a Node's list.
593         """
594         node = SCons.Node.Node()
595         assert node.sources == []
596
597         zero = SCons.Node.Node()
598         one = SCons.Node.Node()
599         two = SCons.Node.Node()
600         three = SCons.Node.Node()
601         four = SCons.Node.Node()
602         five = SCons.Node.Node()
603         six = SCons.Node.Node()
604
605         node.add_source(zero)
606         assert node.sources == [zero]
607         node.add_source([one])
608         assert node.sources == [zero, one]
609         node.add_source([two, three])
610         assert node.sources == [zero, one, two, three]
611         node.add_source([three, four, one])
612         assert node.sources == [zero, one, two, three, four]
613
614         try:
615             node.add_source([[five, six]])
616         except:
617             pass
618         else:
619             raise "did not catch expected exception"
620         assert node.sources == [zero, one, two, three, four]
621
622     def test_add_ignore(self):
623         """Test adding files whose dependencies should be ignored.
624         """
625         node = SCons.Node.Node()
626         assert node.ignore == []
627
628         zero = SCons.Node.Node()
629         one = SCons.Node.Node()
630         two = SCons.Node.Node()
631         three = SCons.Node.Node()
632         four = SCons.Node.Node()
633         five = SCons.Node.Node()
634         six = SCons.Node.Node()
635
636         node.add_ignore(zero)
637         assert node.ignore == [zero]
638         node.add_ignore([one])
639         assert node.ignore == [zero, one]
640         node.add_ignore([two, three])
641         assert node.ignore == [zero, one, two, three]
642         node.add_ignore([three, four, one])
643         assert node.ignore == [zero, one, two, three, four]
644
645         try:
646             node.add_ignore([[five, six]])
647         except:
648             pass
649         else:
650             raise "did not catch expected exception"
651         assert node.ignore == [zero, one, two, three, four]
652
653     def test_get_found_includes(self):
654         """Test the default get_found_includes() method
655         """
656         node = SCons.Node.Node()
657         target = SCons.Node.Node()
658         e = Environment()
659         deps = node.get_found_includes(e, None, target)
660         assert deps == [], deps
661
662     def test_get_implicit_deps(self):
663         """Test get_implicit_deps()
664         """
665         node = MyNode("nnn")
666         target = MyNode("ttt")
667         env = Environment()
668
669         # No scanner at all returns []
670         deps = node.get_implicit_deps(env, None, target)
671         assert deps == [], deps
672
673         s = Scanner()
674         d = MyNode("ddd")
675         node.found_includes = [d]
676
677         # Simple return of the found includes
678         deps = node.get_implicit_deps(env, s, target)
679         assert deps == [d], deps
680
681         # No "recursive" attribute on scanner doesn't recurse
682         e = MyNode("eee")
683         d.found_includes = [e]
684         deps = node.get_implicit_deps(env, s, target)
685         assert deps == [d], map(str, deps)
686
687         # Explicit "recursive" attribute on scanner doesn't recurse
688         s.recursive = None
689         deps = node.get_implicit_deps(env, s, target)
690         assert deps == [d], map(str, deps)
691
692         # Explicit "recursive" attribute on scanner which does recurse
693         s.recursive = 1
694         deps = node.get_implicit_deps(env, s, target)
695         assert deps == [d, e], map(str, deps)
696
697         # Recursive scanning eliminates duplicates
698         f = MyNode("fff")
699         d.found_includes = [e, f]
700         e.found_includes = [f]
701         deps = node.get_implicit_deps(env, s, target)
702         assert deps == [d, e, f], map(str, deps)
703
704     def test_get_source_scanner(self):
705         """Test fetching the source scanner for a Node
706         """
707         class Builder:
708             pass
709         target = SCons.Node.Node()
710         source = SCons.Node.Node()
711         s = target.get_source_scanner(source)
712         assert s is None, s
713
714         ts1 = Scanner()
715         ts2 = Scanner()
716         ts3 = Scanner()
717
718         source.backup_source_scanner = ts1
719         s = target.get_source_scanner(source)
720         assert s is ts1, s
721
722         target.builder = Builder()
723         target.builder.source_scanner = ts2
724         s = target.get_source_scanner(source)
725         assert s is ts2, s
726
727         target.source_scanner = ts3
728         s = target.get_source_scanner(source)
729         assert s is ts3, s
730
731     def test_scan(self):
732         """Test Scanner functionality
733         """
734         node = MyNode("nnn")
735         node.builder = Builder()
736         node.env_set(Environment())
737         s = Scanner()
738
739         d = MyNode("ddd")
740         node.found_includes = [d]
741
742         assert node.target_scanner == None, node.target_scanner
743         node.target_scanner = s
744         assert node.implicit is None
745
746         node.scan()
747         assert s.called
748         assert node.implicit == [d], node.implicit
749
750         # Check that scanning a node with some stored implicit
751         # dependencies resets internal attributes appropriately
752         # if the stored dependencies need recalculation.
753         class StoredNode(MyNode):
754             def get_stored_implicit(self):
755                 return ['implicit1', 'implicit2']
756
757         class NotCurrent:
758             def current(self, node, sig):
759                 return None
760             def bsig(self, node):
761                 return 0
762
763         import SCons.Sig
764
765         save_default_calc = SCons.Sig.default_calc
766         save_implicit_cache = SCons.Node.implicit_cache
767         save_implicit_deps_changed = SCons.Node.implicit_deps_changed
768         save_implicit_deps_unchanged = SCons.Node.implicit_deps_unchanged
769         SCons.Sig.default_calc = NotCurrent()
770         SCons.Node.implicit_cache = 1
771         SCons.Node.implicit_deps_changed = None
772         SCons.Node.implicit_deps_unchanged = None
773         try:
774             sn = StoredNode("eee")
775             sn._children = ['fake']
776             sn.target_scanner = s
777
778             sn.scan()
779
780             assert sn.implicit == [], sn.implicit
781             assert not hasattr(sn, '_children'), "unexpected _children attribute"
782         finally:
783             SCons.Sig.default_calc = save_default_calc
784             SCons.Node.implicit_cache = save_implicit_cache
785             SCons.Node.implicit_deps_changed = save_implicit_deps_changed
786             SCons.Node.implicit_deps_unchanged = save_implicit_deps_unchanged
787
788     def test_scanner_key(self):
789         """Test that a scanner_key() method exists"""
790         assert SCons.Node.Node().scanner_key() == None
791
792     def test_children(self):
793         """Test fetching the non-ignored "children" of a Node.
794         """
795         node = SCons.Node.Node()
796         n1 = SCons.Node.Node()
797         n2 = SCons.Node.Node()
798         n3 = SCons.Node.Node()
799         n4 = SCons.Node.Node()
800         n5 = SCons.Node.Node()
801         n6 = SCons.Node.Node()
802         n7 = SCons.Node.Node()
803         n8 = SCons.Node.Node()
804         n9 = SCons.Node.Node()
805         n10 = SCons.Node.Node()
806         n11 = SCons.Node.Node()
807         n12 = SCons.Node.Node()
808
809         node.add_source([n1, n2, n3])
810         node.add_dependency([n4, n5, n6])
811         node.implicit = []
812         node.implicit_dict = {}
813         node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
814         node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
815         node.add_ignore([n2, n5, n8, n11])
816
817         kids = node.children()
818         for kid in [n1, n3, n4, n6, n7, n9, n10, n12]:
819             assert kid in kids, kid
820         for kid in [n2, n5, n8, n11]:
821             assert not kid in kids, kid
822
823     def test_all_children(self):
824         """Test fetching all the "children" of a Node.
825         """
826         node = SCons.Node.Node()
827         n1 = SCons.Node.Node()
828         n2 = SCons.Node.Node()
829         n3 = SCons.Node.Node()
830         n4 = SCons.Node.Node()
831         n5 = SCons.Node.Node()
832         n6 = SCons.Node.Node()
833         n7 = SCons.Node.Node()
834         n8 = SCons.Node.Node()
835         n9 = SCons.Node.Node()
836         n10 = SCons.Node.Node()
837         n11 = SCons.Node.Node()
838         n12 = SCons.Node.Node()
839
840         node.add_source([n1, n2, n3])
841         node.add_dependency([n4, n5, n6])
842         node.implicit = []
843         node.implicit_dict = {}
844         node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
845         node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
846         node.add_ignore([n2, n5, n8, n11])
847
848         kids = node.all_children()
849         for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12]:
850             assert kid in kids, kid
851
852     def test_state(self):
853         """Test setting and getting the state of a node
854         """
855         node = SCons.Node.Node()
856         assert node.get_state() == None
857         node.set_state(SCons.Node.executing)
858         assert node.get_state() == SCons.Node.executing
859         assert SCons.Node.pending < SCons.Node.executing
860         assert SCons.Node.executing < SCons.Node.up_to_date
861         assert SCons.Node.up_to_date < SCons.Node.executed
862         assert SCons.Node.executed < SCons.Node.failed
863
864     def test_walker(self):
865         """Test walking a Node tree.
866         """
867
868         n1 = MyNode("n1")
869
870         nw = SCons.Node.Walker(n1)
871         assert not nw.is_done()
872         assert nw.next().name ==  "n1"
873         assert nw.is_done()
874         assert nw.next() == None
875
876         n2 = MyNode("n2")
877         n3 = MyNode("n3")
878         n1.add_source([n2, n3])
879
880         nw = SCons.Node.Walker(n1)
881         n = nw.next()
882         assert n.name ==  "n2", n.name
883         n = nw.next()
884         assert n.name ==  "n3", n.name
885         n = nw.next()
886         assert n.name ==  "n1", n.name
887         n = nw.next()
888         assert n == None, n
889
890         n4 = MyNode("n4")
891         n5 = MyNode("n5")
892         n6 = MyNode("n6")
893         n7 = MyNode("n7")
894         n2.add_source([n4, n5])
895         n3.add_dependency([n6, n7])
896
897         nw = SCons.Node.Walker(n1)
898         assert nw.next().name ==  "n4"
899         assert nw.next().name ==  "n5"
900         assert nw.history.has_key(n2)
901         assert nw.next().name ==  "n2"
902         assert nw.next().name ==  "n6"
903         assert nw.next().name ==  "n7"
904         assert nw.history.has_key(n3)
905         assert nw.next().name ==  "n3"
906         assert nw.history.has_key(n1)
907         assert nw.next().name ==  "n1"
908         assert nw.next() == None
909
910         n8 = MyNode("n8")
911         n8.add_dependency([n3])
912         n7.add_dependency([n8])
913
914         def cycle(node, stack):
915             global cycle_detected
916             cycle_detected = 1
917
918         global cycle_detected
919
920         nw = SCons.Node.Walker(n3, cycle_func = cycle)
921         n = nw.next()
922         assert n.name == "n6", n.name
923         n = nw.next()
924         assert n.name == "n8", n.name
925         assert cycle_detected
926         cycle_detected = None
927         n = nw.next()
928         assert n.name == "n7", n.name
929         n = nw.next()
930         assert nw.next() == None
931
932     def test_abspath(self):
933         """Test the get_abspath() method."""
934         n = MyNode("foo")
935         assert n.get_abspath() == str(n), n.get_abspath()
936
937     def test_for_signature(self):
938         """Test the for_signature() method."""
939         n = MyNode("foo")
940         assert n.for_signature() == str(n), n.get_abspath()
941
942     def test_get_string(self):
943         """Test the get_string() method."""
944         class TestNode(MyNode):
945             def __init__(self, name, sig):
946                 MyNode.__init__(self, name)
947                 self.sig = sig
948
949             def for_signature(self):
950                 return self.sig
951
952         n = TestNode("foo", "bar")
953         assert n.get_string(0) == "foo", n.get_string(0)
954         assert n.get_string(1) == "bar", n.get_string(1)
955
956     def test_literal(self):
957         """Test the is_literal() function."""
958         n=SCons.Node.Node()
959         assert n.is_literal()
960
961     def test_Annotate(self):
962         """Test using an interface-specific Annotate function."""
963         def my_annotate(node, self=self):
964             node.annotation = self.node_string
965
966         save_Annotate = SCons.Node.Annotate
967         SCons.Node.Annotate = my_annotate
968
969         try:
970             self.node_string = '#1'
971             n = SCons.Node.Node()
972             assert n.annotation == '#1', n.annotation
973
974             self.node_string = '#2'
975             n = SCons.Node.Node()
976             assert n.annotation == '#2', n.annotation
977         finally:
978             SCons.Node.Annotate = save_Annotate
979
980     def test_clear(self):
981         """Test clearing all cached state information."""
982         n = SCons.Node.Node()
983
984         n.set_state(3)
985         n.binfo = 'xyz'
986         n.includes = 'testincludes'
987         n.found_include = {'testkey':'testvalue'}
988         n.implicit = 'testimplicit'
989         n.waiting_parents = ['foo', 'bar']
990
991         n.clear()
992
993         assert n.get_state() is None, n.get_state()
994         assert not hasattr(n, 'binfo'), n.bsig
995         assert n.includes is None, n.includes
996         assert n.found_includes == {}, n.found_includes
997         assert n.implicit is None, n.implicit
998         assert n.waiting_parents == [], n.waiting_parents
999
1000     def test_get_subst_proxy(self):
1001         """Test the get_subst_proxy method."""
1002         n = MyNode("test")
1003
1004         assert n.get_subst_proxy() == n, n.get_subst_proxy()
1005
1006     def test_new_binfo(self):
1007         """Test the new_binfo() method"""
1008         n = SCons.Node.Node()
1009         result = n.new_binfo()
1010         assert isinstance(result, SCons.Node.BuildInfo), result
1011
1012     def test_get_suffix(self):
1013         """Test the base Node get_suffix() method"""
1014         n = SCons.Node.Node()
1015         s = n.get_suffix()
1016         assert s == '', s
1017
1018     def test_generate_build_dict(self):
1019         """Test the base Node generate_build_dict() method"""
1020         n = SCons.Node.Node()
1021         dict = n.generate_build_dict()
1022         assert dict == {}, dict
1023
1024     def test_postprocess(self):
1025         """Test calling the base Node postprocess() method"""
1026         n = SCons.Node.Node()
1027         n.postprocess()
1028
1029     def test_add_to_waiting_parents(self):
1030         """Test the add_to_waiting_parents() method"""
1031         n1 = SCons.Node.Node()
1032         n2 = SCons.Node.Node()
1033         assert n1.waiting_parents == [], n1.waiting_parents
1034         n1.add_to_waiting_parents(n2)
1035         assert n1.waiting_parents == [n2], n1.waiting_parents
1036
1037     def test_call_for_all_waiting_parents(self):
1038         """Test the call_for_all_waiting_parents() method"""
1039         n1 = SCons.Node.Node()
1040         n2 = SCons.Node.Node()
1041         n1.add_to_waiting_parents(n2)
1042         result = []
1043         def func(node, result=result):
1044             result.append(node)
1045         n1.call_for_all_waiting_parents(func)
1046         assert result == [n1, n2], result
1047
1048
1049
1050 if __name__ == "__main__":
1051     suite = unittest.makeSuite(NodeTestCase, 'test_')
1052     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1053         sys.exit(1)