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