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