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