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