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