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