Followon to PR #4348: more bool fixes
[scons.git] / SCons / Node / NodeTests.py
blob42fae01927b6b8b88a454bac4388f3e3cfcc1833
1 # MIT License
3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 import SCons.compat
26 import collections
27 import re
28 import unittest
30 import SCons.Errors
31 import SCons.Node
32 import SCons.Util
36 built_it = None
37 built_target = None
38 built_source = None
39 cycle_detected = None
40 built_order = 0
42 def _actionAppend(a1, a2):
43 all = []
44 for curr_a in [a1, a2]:
45 if isinstance(curr_a, MyAction):
46 all.append(curr_a)
47 elif isinstance(curr_a, MyListAction):
48 all.extend(curr_a.list)
49 elif isinstance(curr_a, list):
50 all.extend(curr_a)
51 else:
52 raise Exception('Cannot Combine Actions')
53 return MyListAction(all)
55 class MyActionBase:
56 def __add__(self, other):
57 return _actionAppend(self, other)
59 def __radd__(self, other):
60 return _actionAppend(other, self)
62 class MyAction(MyActionBase):
63 def __init__(self) -> None:
64 self.order = 0
66 def __call__(self, target, source, env, executor=None) -> int:
67 global built_it, built_target, built_source, built_args, built_order
68 if executor:
69 target = executor.get_all_targets()
70 source = executor.get_all_sources()
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
79 def get_implicit_deps(self, target, source, env):
80 return []
82 class MyExecutor:
83 def __init__(self, env=None, targets=[], sources=[]) -> None:
84 self.env = env
85 self.targets = targets
86 self.sources = sources
87 def get_build_env(self):
88 return self.env
89 def get_build_scanner_path(self, scanner) -> str:
90 return 'executor would call %s' % scanner
91 def cleanup(self) -> None:
92 self.cleaned_up = 1
93 def scan_targets(self, scanner) -> None:
94 if not scanner:
95 return
96 d = scanner(self.targets)
97 for t in self.targets:
98 t.implicit.extend(d)
99 def scan_sources(self, scanner) -> None:
100 if not scanner:
101 return
102 d = scanner(self.sources)
103 for t in self.targets:
104 t.implicit.extend(d)
106 class MyListAction(MyActionBase):
107 def __init__(self, list) -> None:
108 self.list = list
109 def __call__(self, target, source, env) -> None:
110 for A in self.list:
111 A(target, source, env)
113 class Environment:
114 def __init__(self, **kw) -> None:
115 self._dict = {}
116 self._dict.update(kw)
117 def __getitem__(self, key):
118 return self._dict[key]
119 def get(self, key, default = None):
120 return self._dict.get(key, default)
121 def Dictionary(self, *args):
122 return {}
123 def Override(self, overrides):
124 d = self._dict.copy()
125 d.update(overrides)
126 return Environment(**d)
127 def _update(self, dict) -> None:
128 self._dict.update(dict)
129 def get_factory(self, factory):
130 return factory or MyNode
131 def get_scanner(self, scanner_key):
132 try:
133 return self._dict['SCANNERS'][0]
134 except:
135 pass
137 return []
139 class Builder:
140 def __init__(self, env=None, is_explicit: bool=True) -> None:
141 if env is None: env = Environment()
142 self.env = env
143 self.overrides = {}
144 self.action = MyAction()
145 self.source_factory = MyNode
146 self.is_explicit = is_explicit
147 self.target_scanner = None
148 self.source_scanner = None
149 def targets(self, t):
150 return [t]
151 def get_actions(self):
152 return [self.action]
153 def get_contents(self, target, source, env) -> int:
154 return 7
156 class NoneBuilder(Builder):
157 def execute(self, target, source, env):
158 Builder.execute(self, target, source, env)
159 return None
161 class ListBuilder(Builder):
162 def __init__(self, *nodes) -> None:
163 super().__init__()
164 self.nodes = nodes
165 def execute(self, target, source, env):
166 if hasattr(self, 'status'):
167 return self.status
168 for n in self.nodes:
169 n.prepare()
170 target = self.nodes[0]
171 self.status = Builder.execute(self, target, source, env)
173 class FailBuilder:
174 def execute(self, target, source, env) -> int:
175 return 1
177 class ExceptBuilder:
178 def execute(self, target, source, env):
179 raise SCons.Errors.BuildError
181 class ExceptBuilder2:
182 def execute(self, target, source, env):
183 raise Exception("foo")
185 class Scanner:
186 called = None
187 def __call__(self, node):
188 self.called = 1
189 return node.GetTag('found_includes')
190 def path(self, env, dir=None, target=None, source=None, kw={}):
191 return ()
192 def select(self, node):
193 return self
194 def recurse_nodes(self, nodes):
195 return nodes
197 class MyNode(SCons.Node.Node):
198 """The base Node class contains a number of do-nothing methods that
199 we expect to be overridden by real, functional Node subclasses. So
200 simulate a real, functional Node subclass.
202 def __init__(self, name) -> None:
203 super().__init__()
204 self.name = name
205 self.Tag('found_includes', [])
206 def __str__(self) -> str:
207 return self.name
208 def get_found_includes(self, env, scanner, target):
209 return scanner(self)
211 class Calculator:
212 def __init__(self, val) -> None:
213 self.max_drift = 0
214 class M:
215 def __init__(self, val) -> None:
216 self.val = val
217 def signature(self, args):
218 return self.val
219 def collect(self, args):
220 result = self.val
221 for a in args:
222 result += a
223 return result
224 self.module = M(val)
228 class NodeInfoBaseTestCase(unittest.TestCase):
229 # The abstract class NodeInfoBase has not enough default slots to perform
230 # the merge and format test (arbitrary attributes do not work). Do it with a
231 # derived class that does provide the slots.
233 def test_merge(self) -> None:
234 """Test merging NodeInfoBase attributes"""
236 class TestNodeInfo(SCons.Node.NodeInfoBase):
237 __slots__ = ('a1', 'a2', 'a3')
239 ni1 = TestNodeInfo()
240 ni2 = TestNodeInfo()
242 ni1.a1 = 1
243 ni1.a2 = 2
245 ni2.a2 = 222
246 ni2.a3 = 333
248 ni1.merge(ni2)
249 assert ni1.a1 == 1, ni1.a1
250 assert ni1.a2 == 222, ni1.a2
251 assert ni1.a3 == 333, ni1.a3
253 def test_update(self) -> None:
254 """Test the update() method"""
255 ni = SCons.Node.NodeInfoBase()
256 ni.update(SCons.Node.Node())
258 def test_format(self) -> None:
259 """Test the NodeInfoBase.format() method"""
261 class TestNodeInfo(SCons.Node.NodeInfoBase):
262 __slots__ = ('xxx', 'yyy', 'zzz')
264 ni1 = TestNodeInfo()
265 ni1.xxx = 'x'
266 ni1.yyy = 'y'
267 ni1.zzz = 'z'
269 f = ni1.format()
270 assert f == ['x', 'y', 'z'], f
272 field_list = ['xxx', 'zzz', 'aaa']
274 f = ni1.format(field_list)
275 assert f == ['x', 'z', 'None'], f
279 class BuildInfoBaseTestCase(unittest.TestCase):
281 def test___init__(self) -> None:
282 """Test BuildInfoBase initialization"""
283 n = SCons.Node.Node()
284 bi = SCons.Node.BuildInfoBase()
285 assert bi
287 def test_merge(self) -> None:
288 """Test merging BuildInfoBase attributes"""
289 n1 = SCons.Node.Node()
290 bi1 = SCons.Node.BuildInfoBase()
291 n2 = SCons.Node.Node()
292 bi2 = SCons.Node.BuildInfoBase()
294 bi1.bsources = 1
295 bi1.bdepends = 2
297 bi2.bdepends = 222
298 bi2.bact = 333
300 bi1.merge(bi2)
301 assert bi1.bsources == 1, bi1.bsources
302 assert bi1.bdepends == 222, bi1.bdepends
303 assert bi1.bact == 333, bi1.bact
306 class NodeTestCase(unittest.TestCase):
308 def test_build(self) -> None:
309 """Test building a node
311 global built_it, built_order
313 # Make sure it doesn't blow up if no builder is set.
314 node = MyNode("www")
315 node.build()
316 assert built_it is None
317 node.build(extra_kw_argument = 1)
318 assert built_it is None
320 node = MyNode("xxx")
321 node.builder_set(Builder())
322 node.env_set(Environment())
323 node.path = "xxx"
324 node.sources = ["yyy", "zzz"]
325 node.build()
326 assert built_it
327 assert built_target == [node], built_target
328 assert built_source == ["yyy", "zzz"], built_source
330 built_it = None
331 node = MyNode("qqq")
332 node.builder_set(NoneBuilder())
333 node.env_set(Environment())
334 node.path = "qqq"
335 node.sources = ["rrr", "sss"]
336 node.builder.overrides = { "foo" : 1, "bar" : 2 }
337 node.build()
338 assert built_it
339 assert built_target == [node], built_target
340 assert built_source == ["rrr", "sss"], built_source
341 assert built_args["foo"] == 1, built_args
342 assert built_args["bar"] == 2, built_args
344 fff = MyNode("fff")
345 ggg = MyNode("ggg")
346 lb = ListBuilder(fff, ggg)
347 e = Environment()
348 fff.builder_set(lb)
349 fff.env_set(e)
350 fff.path = "fff"
351 ggg.builder_set(lb)
352 ggg.env_set(e)
353 ggg.path = "ggg"
354 fff.sources = ["hhh", "iii"]
355 ggg.sources = ["hhh", "iii"]
356 built_it = None
357 fff.build()
358 assert built_it
359 assert built_target == [fff], built_target
360 assert built_source == ["hhh", "iii"], built_source
361 built_it = None
362 ggg.build()
363 assert built_it
364 assert built_target == [ggg], built_target
365 assert built_source == ["hhh", "iii"], built_source
367 built_it = None
368 jjj = MyNode("jjj")
369 b = Builder()
370 jjj.builder_set(b)
371 # NOTE: No env_set()! We should pull the environment from the builder.
372 b.env = Environment()
373 b.overrides = { "on" : 3, "off" : 4 }
374 e.builder = b
375 jjj.build()
376 assert built_it
377 assert built_target[0] == jjj, built_target[0]
378 assert built_source == [], built_source
379 assert built_args["on"] == 3, built_args
380 assert built_args["off"] == 4, built_args
382 def test_get_build_scanner_path(self) -> None:
383 """Test the get_build_scanner_path() method"""
384 n = SCons.Node.Node()
385 x = MyExecutor()
386 n.set_executor(x)
387 p = n.get_build_scanner_path('fake_scanner')
388 assert p == "executor would call fake_scanner", p
390 def test_get_executor(self) -> None:
391 """Test the get_executor() method"""
392 n = SCons.Node.Node()
394 try:
395 n.get_executor(0)
396 except AttributeError:
397 pass
398 else:
399 self.fail("did not catch expected AttributeError")
401 class Builder:
402 action = 'act'
403 env = 'env1'
404 overrides = {}
406 n = SCons.Node.Node()
407 n.builder_set(Builder())
408 x = n.get_executor()
409 assert x.env == 'env1', x.env
411 n = SCons.Node.Node()
412 n.builder_set(Builder())
413 n.env_set('env2')
414 x = n.get_executor()
415 assert x.env == 'env2', x.env
417 def test_set_executor(self) -> None:
418 """Test the set_executor() method"""
419 n = SCons.Node.Node()
420 n.set_executor(1)
421 assert n.executor == 1, n.executor
423 def test_executor_cleanup(self) -> None:
424 """Test letting the executor cleanup its cache"""
425 n = SCons.Node.Node()
426 x = MyExecutor()
427 n.set_executor(x)
428 n.executor_cleanup()
429 assert x.cleaned_up
431 def test_reset_executor(self) -> None:
432 """Test the reset_executor() method"""
433 n = SCons.Node.Node()
434 n.set_executor(1)
435 assert n.executor == 1, n.executor
436 n.reset_executor()
437 assert not hasattr(n, 'executor'), "unexpected executor attribute"
439 def test_built(self) -> None:
440 """Test the built() method"""
441 class SubNodeInfo(SCons.Node.NodeInfoBase):
442 __slots__ = ('updated',)
443 def update(self, node) -> None:
444 self.updated = 1
445 class SubNode(SCons.Node.Node):
446 def clear(self) -> None:
447 self.cleared = 1
449 n = SubNode()
450 n.ninfo = SubNodeInfo()
451 n.built()
452 assert n.cleared, n.cleared
453 assert n.ninfo.updated, n.ninfo.cleared
455 def test_push_to_cache(self) -> None:
456 """Test the base push_to_cache() method"""
457 n = SCons.Node.Node()
458 r = n.push_to_cache()
459 assert r is None, r
461 def test_retrieve_from_cache(self) -> None:
462 """Test the base retrieve_from_cache() method"""
463 n = SCons.Node.Node()
464 r = n.retrieve_from_cache()
465 assert r == 0, r
467 def test_visited(self) -> None:
468 """Test the base visited() method
470 Just make sure it's there and we can call it.
472 n = SCons.Node.Node()
473 n.visited()
475 def test_builder_set(self) -> None:
476 """Test setting a Node's Builder
478 node = SCons.Node.Node()
479 b = Builder()
480 node.builder_set(b)
481 assert node.builder == b
483 def test_has_builder(self) -> None:
484 """Test the has_builder() method
486 n1 = SCons.Node.Node()
487 assert n1.has_builder() == 0
488 n1.builder_set(Builder())
489 assert n1.has_builder() == 1
491 def test_has_explicit_builder(self) -> None:
492 """Test the has_explicit_builder() method
494 n1 = SCons.Node.Node()
495 assert not n1.has_explicit_builder()
496 n1.set_explicit(1)
497 assert n1.has_explicit_builder()
498 n1.set_explicit(None)
499 assert not n1.has_explicit_builder()
501 def test_get_builder(self) -> None:
502 """Test the get_builder() method"""
503 n1 = SCons.Node.Node()
504 b = n1.get_builder()
505 assert b is None, b
506 b = n1.get_builder(777)
507 assert b == 777, b
508 n1.builder_set(888)
509 b = n1.get_builder()
510 assert b == 888, b
511 b = n1.get_builder(999)
512 assert b == 888, b
514 def test_multiple_side_effect_has_builder(self) -> None:
515 """Test the multiple_side_effect_has_builder() method
517 n1 = SCons.Node.Node()
518 assert n1.multiple_side_effect_has_builder() == 0
519 n1.builder_set(Builder())
520 assert n1.multiple_side_effect_has_builder() == 1
522 def test_is_derived(self) -> None:
523 """Test the is_derived() method
525 n1 = SCons.Node.Node()
526 n2 = SCons.Node.Node()
527 n3 = SCons.Node.Node()
529 n2.builder_set(Builder())
530 n3.side_effect = 1
532 assert n1.is_derived() == 0
533 assert n2.is_derived() == 1
534 assert n3.is_derived() == 1
536 def test_alter_targets(self) -> None:
537 """Test the alter_targets() method
539 n = SCons.Node.Node()
540 t, m = n.alter_targets()
541 assert t == [], t
542 assert m is None, m
544 def test_is_up_to_date(self) -> None:
545 """Test the default is_up_to_date() method."""
546 node = SCons.Node.Node()
547 assert not node.is_up_to_date()
549 def test_children_are_up_to_date(self) -> None:
550 """Test the children_are_up_to_date() method used by subclasses
552 n1 = SCons.Node.Node()
553 n2 = SCons.Node.Node()
555 n1.add_source([n2])
556 assert n1.children_are_up_to_date(), "expected up to date"
557 n2.set_state(SCons.Node.executed)
558 assert not n1.children_are_up_to_date(), "expected not up to date"
559 n2.set_state(SCons.Node.up_to_date)
560 assert n1.children_are_up_to_date(), "expected up to date"
561 n1.always_build = 1
562 assert not n1.children_are_up_to_date(), "expected not up to date"
564 def test_env_set(self) -> None:
565 """Test setting a Node's Environment
567 node = SCons.Node.Node()
568 e = Environment()
569 node.env_set(e)
570 assert node.env == e
572 def test_get_actions(self) -> None:
573 """Test fetching a Node's action list
575 node = SCons.Node.Node()
576 node.builder_set(Builder())
577 a = node.builder.get_actions()
578 assert isinstance(a[0], MyAction), a[0]
580 def test_get_csig(self) -> None:
581 """Test generic content signature calculation
584 class TestNodeInfo(SCons.Node.NodeInfoBase):
585 __slots__ = ('csig',)
586 try:
587 SCons.Node.Node.NodeInfo = TestNodeInfo
588 def my_contents(obj) -> int:
589 return 444
590 SCons.Node._get_contents_map[4] = my_contents
591 node = SCons.Node.Node()
592 node._func_get_contents = 4
593 result = node.get_csig()
594 assert result in ('550a141f12de6341fba65b0ad0433500', '9a3e61b6bcc8abec08f195526c3132d5a4a98cc0', '3538a1ef2e113da64249eea7bd068b585ec7ce5df73b2d1e319d8c9bf47eb314'), result
595 finally:
596 SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase
598 def test_get_cachedir_csig(self) -> None:
599 """Test content signature calculation for CacheDir
601 class TestNodeInfo(SCons.Node.NodeInfoBase):
602 __slots__ = ('csig',)
603 try:
604 SCons.Node.Node.NodeInfo = TestNodeInfo
605 def my_contents(obj) -> int:
606 return 555
607 SCons.Node._get_contents_map[4] = my_contents
608 node = SCons.Node.Node()
609 node._func_get_contents = 4
610 result = node.get_cachedir_csig()
611 assert result in ('15de21c670ae7c3f6f3f1f37029303c9', 'cfa1150f1787186742a9a884b73a43d8cf219f9b', '91a73fd806ab2c005c13b4dc19130a884e909dea3f72d46e30266fe1a1f588d8'), result
612 finally:
613 SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase
615 def test_get_binfo(self) -> None:
616 """Test fetching/creating a build information structure
618 class TestNodeInfo(SCons.Node.NodeInfoBase):
619 __slots__ = ('csig',)
620 SCons.Node.Node.NodeInfo = TestNodeInfo
621 node = SCons.Node.Node()
623 binfo = node.get_binfo()
624 assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo
626 node = SCons.Node.Node()
627 d = SCons.Node.Node()
628 ninfo = d.get_ninfo()
629 assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo
630 i = SCons.Node.Node()
631 ninfo = i.get_ninfo()
632 assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo
633 node.depends = [d]
634 node.implicit = [i]
636 binfo = node.get_binfo()
637 assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo
638 assert hasattr(binfo, 'bsources')
639 assert hasattr(binfo, 'bsourcesigs')
640 assert binfo.bdepends == [d]
641 assert hasattr(binfo, 'bdependsigs')
642 assert binfo.bimplicit == [i]
643 assert hasattr(binfo, 'bimplicitsigs')
645 def test_explain(self) -> None:
646 """Test explaining why a Node must be rebuilt
648 class testNode(SCons.Node.Node):
649 def __str__(self) -> str: return 'xyzzy'
650 node = testNode()
651 node.exists = lambda: None
652 # Can't do this with new-style classes (python bug #1066490)
653 #node.__str__ = lambda: 'xyzzy'
654 result = node.explain()
655 assert result == "building `xyzzy' because it doesn't exist\n", result
657 class testNode2(SCons.Node.Node):
658 def __str__(self) -> str: return 'null_binfo'
659 class FS:
660 pass
661 node = testNode2()
662 node.fs = FS()
663 node.fs.Top = SCons.Node.Node()
664 result = node.explain()
665 assert result is None, result
667 def get_null_info():
668 class Null_SConsignEntry:
669 class Null_BuildInfo:
670 def prepare_dependencies(self) -> None:
671 pass
672 binfo = Null_BuildInfo()
673 return Null_SConsignEntry()
675 node.get_stored_info = get_null_info
676 #see above: node.__str__ = lambda: 'null_binfo'
677 result = node.explain()
678 assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result
680 # XXX additional tests for the guts of the functionality some day
682 #def test_del_binfo(self):
683 # """Test deleting the build information from a Node
684 # """
685 # node = SCons.Node.Node()
686 # node.binfo = None
687 # node.del_binfo()
688 # assert not hasattr(node, 'binfo'), node
690 def test_store_info(self) -> None:
691 """Test calling the method to store build information
693 node = SCons.Node.Node()
694 SCons.Node.store_info_map[node.store_info](node)
696 def test_get_stored_info(self) -> None:
697 """Test calling the method to fetch stored build information
699 node = SCons.Node.Node()
700 result = node.get_stored_info()
701 assert result is None, result
703 def test_set_always_build(self) -> None:
704 """Test setting a Node's always_build value
706 node = SCons.Node.Node()
707 node.set_always_build()
708 assert node.always_build
709 node.set_always_build(3)
710 assert node.always_build == 3
712 def test_set_noclean(self) -> None:
713 """Test setting a Node's noclean value
715 node = SCons.Node.Node()
716 node.set_noclean()
717 assert node.noclean == 1, node.noclean
718 node.set_noclean(7)
719 assert node.noclean == 1, node.noclean
720 node.set_noclean(0)
721 assert node.noclean == 0, node.noclean
722 node.set_noclean(None)
723 assert node.noclean == 0, node.noclean
725 def test_set_precious(self) -> None:
726 """Test setting a Node's precious value
728 node = SCons.Node.Node()
729 node.set_precious()
730 assert node.precious
731 node.set_precious(7)
732 assert node.precious == 7
734 def test_set_pseudo(self) -> None:
735 """Test setting a Node's pseudo value
737 node = SCons.Node.Node()
738 node.set_pseudo()
739 assert node.pseudo
740 node.set_pseudo(False)
741 assert not node.pseudo
743 def test_exists(self) -> None:
744 """Test evaluating whether a Node exists.
746 node = SCons.Node.Node()
747 e = node.exists()
748 assert e == 1, e
750 def test_exists_repo(self) -> None:
751 """Test evaluating whether a Node exists locally or in a repository.
753 node = SCons.Node.Node()
754 e = node.rexists()
755 assert e == 1, e
757 class MyNode(SCons.Node.Node):
758 def exists(self) -> str:
759 return 'xyz'
761 node = MyNode()
762 e = node.rexists()
763 assert e == 'xyz', e
765 def test_prepare(self) -> None:
766 """Test preparing a node to be built
768 By extension, this also tests the missing() method.
770 node = SCons.Node.Node()
772 n1 = SCons.Node.Node()
773 n1.builder_set(Builder())
774 node.implicit = []
775 node.implicit_set = set()
776 node._add_child(node.implicit, node.implicit_set, [n1])
778 node.prepare() # should not throw an exception
780 n2 = SCons.Node.Node()
781 n2.linked = 1
782 node.implicit = []
783 node.implicit_set = set()
784 node._add_child(node.implicit, node.implicit_set, [n2])
786 node.prepare() # should not throw an exception
788 n3 = SCons.Node.Node()
789 node.implicit = []
790 node.implicit_set = set()
791 node._add_child(node.implicit, node.implicit_set, [n3])
793 node.prepare() # should not throw an exception
795 class MyNode(SCons.Node.Node):
796 def rexists(self):
797 return None
798 n4 = MyNode()
799 node.implicit = []
800 node.implicit_set = set()
801 node._add_child(node.implicit, node.implicit_set, [n4])
802 exc_caught = 0
803 try:
804 node.prepare()
805 except SCons.Errors.StopError:
806 exc_caught = 1
807 assert exc_caught, "did not catch expected StopError"
809 def test_add_dependency(self):
810 """Test adding dependencies to a Node's list.
812 node = SCons.Node.Node()
813 assert node.depends == []
815 zero = SCons.Node.Node()
817 one = SCons.Node.Node()
818 two = SCons.Node.Node()
819 three = SCons.Node.Node()
820 four = SCons.Node.Node()
821 five = SCons.Node.Node()
822 six = SCons.Node.Node()
824 node.add_dependency([zero])
825 assert node.depends == [zero]
826 node.add_dependency([one])
827 assert node.depends == [zero, one]
828 node.add_dependency([two, three])
829 assert node.depends == [zero, one, two, three]
830 node.add_dependency([three, four, one])
831 assert node.depends == [zero, one, two, three, four]
833 try:
834 node.add_depends([[five, six]])
835 except:
836 pass
837 else:
838 raise Exception("did not catch expected exception")
839 assert node.depends == [zero, one, two, three, four]
842 def test_add_source(self):
843 """Test adding sources to a Node's list.
845 node = SCons.Node.Node()
846 assert node.sources == []
848 zero = SCons.Node.Node()
849 one = SCons.Node.Node()
850 two = SCons.Node.Node()
851 three = SCons.Node.Node()
852 four = SCons.Node.Node()
853 five = SCons.Node.Node()
854 six = SCons.Node.Node()
856 node.add_source([zero])
857 assert node.sources == [zero]
858 node.add_source([one])
859 assert node.sources == [zero, one]
860 node.add_source([two, three])
861 assert node.sources == [zero, one, two, three]
862 node.add_source([three, four, one])
863 assert node.sources == [zero, one, two, three, four]
865 try:
866 node.add_source([[five, six]])
867 except:
868 pass
869 else:
870 raise Exception("did not catch expected exception")
871 assert node.sources == [zero, one, two, three, four], node.sources
873 def test_add_ignore(self):
874 """Test adding files whose dependencies should be ignored.
876 node = SCons.Node.Node()
877 assert node.ignore == []
879 zero = SCons.Node.Node()
880 one = SCons.Node.Node()
881 two = SCons.Node.Node()
882 three = SCons.Node.Node()
883 four = SCons.Node.Node()
884 five = SCons.Node.Node()
885 six = SCons.Node.Node()
887 node.add_ignore([zero])
888 assert node.ignore == [zero]
889 node.add_ignore([one])
890 assert node.ignore == [zero, one]
891 node.add_ignore([two, three])
892 assert node.ignore == [zero, one, two, three]
893 node.add_ignore([three, four, one])
894 assert node.ignore == [zero, one, two, three, four]
896 try:
897 node.add_ignore([[five, six]])
898 except:
899 pass
900 else:
901 raise Exception("did not catch expected exception")
902 assert node.ignore == [zero, one, two, three, four]
904 def test_get_found_includes(self) -> None:
905 """Test the default get_found_includes() method
907 node = SCons.Node.Node()
908 target = SCons.Node.Node()
909 e = Environment()
910 deps = node.get_found_includes(e, None, target)
911 assert deps == [], deps
913 def test_get_implicit_deps(self) -> None:
914 """Test get_implicit_deps()
916 node = MyNode("nnn")
917 target = MyNode("ttt")
918 env = Environment()
920 # No scanner at all returns []
921 deps = node.get_implicit_deps(env, None, target)
922 assert deps == [], deps
924 s = Scanner()
925 d1 = MyNode("d1")
926 d2 = MyNode("d2")
927 node.Tag('found_includes', [d1, d2])
929 # Simple return of the found includes
930 deps = node.get_implicit_deps(env, s, s.path)
931 assert deps == [d1, d2], deps
933 # By default, our fake scanner recurses
934 e = MyNode("eee")
935 f = MyNode("fff")
936 g = MyNode("ggg")
937 d1.Tag('found_includes', [e, f])
938 d2.Tag('found_includes', [e, f])
939 f.Tag('found_includes', [g])
940 deps = node.get_implicit_deps(env, s, s.path)
941 assert deps == [d1, d2, e, f, g], list(map(str, deps))
943 # Recursive scanning eliminates duplicates
944 e.Tag('found_includes', [f])
945 deps = node.get_implicit_deps(env, s, s.path)
946 assert deps == [d1, d2, e, f, g], list(map(str, deps))
948 # Scanner method can select specific nodes to recurse
949 def no_fff(nodes):
950 return [n for n in nodes if str(n)[0] != 'f']
951 s.recurse_nodes = no_fff
952 deps = node.get_implicit_deps(env, s, s.path)
953 assert deps == [d1, d2, e, f], list(map(str, deps))
955 # Scanner method can short-circuit recursing entirely
956 s.recurse_nodes = lambda nodes: []
957 deps = node.get_implicit_deps(env, s, s.path)
958 assert deps == [d1, d2], list(map(str, deps))
960 def test_get_env_scanner(self) -> None:
961 """Test fetching the environment scanner for a Node
963 node = SCons.Node.Node()
964 scanner = Scanner()
965 env = Environment(SCANNERS = [scanner])
966 s = node.get_env_scanner(env)
967 assert s == scanner, s
968 s = node.get_env_scanner(env, {'X':1})
969 assert s == scanner, s
971 def test_get_target_scanner(self) -> None:
972 """Test fetching the target scanner for a Node
974 s = Scanner()
975 b = Builder()
976 b.target_scanner = s
977 n = SCons.Node.Node()
978 n.builder = b
979 x = n.get_target_scanner()
980 assert x is s, x
982 def test_get_source_scanner(self) -> None:
983 """Test fetching the source scanner for a Node
985 target = SCons.Node.Node()
986 source = SCons.Node.Node()
987 s = target.get_source_scanner(source)
988 assert isinstance(s, SCons.Util.Null), s
990 ts1 = Scanner()
991 ts2 = Scanner()
992 ts3 = Scanner()
994 class Builder1(Builder):
995 def __call__(self, source):
996 r = SCons.Node.Node()
997 r.builder = self
998 return [r]
999 class Builder2(Builder1):
1000 def __init__(self, scanner) -> None:
1001 self.source_scanner = scanner
1003 builder = Builder2(ts1)
1005 targets = builder([source])
1006 s = targets[0].get_source_scanner(source)
1007 assert s is ts1, s
1009 target.builder_set(Builder2(ts1))
1010 target.builder.source_scanner = ts2
1011 s = target.get_source_scanner(source)
1012 assert s is ts2, s
1014 builder = Builder1(env=Environment(SCANNERS = [ts3]))
1016 targets = builder([source])
1018 s = targets[0].get_source_scanner(source)
1019 assert s is ts3, s
1022 def test_scan(self) -> None:
1023 """Test Scanner functionality
1025 env = Environment()
1026 node = MyNode("nnn")
1027 node.builder = Builder()
1028 node.env_set(env)
1029 x = MyExecutor(env, [node])
1031 s = Scanner()
1032 d = MyNode("ddd")
1033 node.Tag('found_includes', [d])
1035 node.builder.target_scanner = s
1036 assert node.implicit is None
1038 node.scan()
1039 assert s.called
1040 assert node.implicit == [d], node.implicit
1042 # Check that scanning a node with some stored implicit
1043 # dependencies resets internal attributes appropriately
1044 # if the stored dependencies need recalculation.
1045 class StoredNode(MyNode):
1046 def get_stored_implicit(self):
1047 return [MyNode('implicit1'), MyNode('implicit2')]
1049 save_implicit_cache = SCons.Node.implicit_cache
1050 save_implicit_deps_changed = SCons.Node.implicit_deps_changed
1051 save_implicit_deps_unchanged = SCons.Node.implicit_deps_unchanged
1052 SCons.Node.implicit_cache = 1
1053 SCons.Node.implicit_deps_changed = None
1054 SCons.Node.implicit_deps_unchanged = None
1055 try:
1056 sn = StoredNode("eee")
1057 sn.builder_set(Builder())
1058 sn.builder.target_scanner = s
1060 sn.scan()
1062 assert sn.implicit == [], sn.implicit
1063 assert sn.children() == [], sn.children()
1065 finally:
1066 SCons.Node.implicit_cache = save_implicit_cache
1067 SCons.Node.implicit_deps_changed = save_implicit_deps_changed
1068 SCons.Node.implicit_deps_unchanged = save_implicit_deps_unchanged
1070 def test_scanner_key(self) -> None:
1071 """Test that a scanner_key() method exists"""
1072 assert SCons.Node.Node().scanner_key() is None
1074 def test_children(self) -> None:
1075 """Test fetching the non-ignored "children" of a Node.
1077 node = SCons.Node.Node()
1078 n1 = SCons.Node.Node()
1079 n2 = SCons.Node.Node()
1080 n3 = SCons.Node.Node()
1081 n4 = SCons.Node.Node()
1082 n5 = SCons.Node.Node()
1083 n6 = SCons.Node.Node()
1084 n7 = SCons.Node.Node()
1085 n8 = SCons.Node.Node()
1086 n9 = SCons.Node.Node()
1087 n10 = SCons.Node.Node()
1088 n11 = SCons.Node.Node()
1089 n12 = SCons.Node.Node()
1091 node.add_source([n1, n2, n3])
1092 node.add_dependency([n4, n5, n6])
1093 node.implicit = []
1094 node.implicit_set = set()
1095 node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
1096 node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
1097 node.add_ignore([n2, n5, n8, n11])
1099 kids = node.children()
1100 for kid in [n1, n3, n4, n6, n7, n9, n10, n12]:
1101 assert kid in kids, kid
1102 for kid in [n2, n5, n8, n11]:
1103 assert kid not in kids, kid
1105 def test_all_children(self) -> None:
1106 """Test fetching all the "children" of a Node.
1108 node = SCons.Node.Node()
1109 n1 = SCons.Node.Node()
1110 n2 = SCons.Node.Node()
1111 n3 = SCons.Node.Node()
1112 n4 = SCons.Node.Node()
1113 n5 = SCons.Node.Node()
1114 n6 = SCons.Node.Node()
1115 n7 = SCons.Node.Node()
1116 n8 = SCons.Node.Node()
1117 n9 = SCons.Node.Node()
1118 n10 = SCons.Node.Node()
1119 n11 = SCons.Node.Node()
1120 n12 = SCons.Node.Node()
1122 node.add_source([n1, n2, n3])
1123 node.add_dependency([n4, n5, n6])
1124 node.implicit = []
1125 node.implicit_set = set()
1126 node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
1127 node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
1128 node.add_ignore([n2, n5, n8, n11])
1130 kids = node.all_children()
1131 for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12]:
1132 assert kid in kids, kid
1134 def test_state(self) -> None:
1135 """Test setting and getting the state of a node
1137 node = SCons.Node.Node()
1138 assert node.get_state() == SCons.Node.no_state
1139 node.set_state(SCons.Node.executing)
1140 assert node.get_state() == SCons.Node.executing
1141 assert SCons.Node.pending < SCons.Node.executing
1142 assert SCons.Node.executing < SCons.Node.up_to_date
1143 assert SCons.Node.up_to_date < SCons.Node.executed
1144 assert SCons.Node.executed < SCons.Node.failed
1146 def test_walker(self) -> None:
1147 """Test walking a Node tree.
1150 n1 = MyNode("n1")
1152 nw = SCons.Node.Walker(n1)
1153 assert not nw.is_done()
1154 assert nw.get_next().name == "n1"
1155 assert nw.is_done()
1156 assert nw.get_next() is None
1158 n2 = MyNode("n2")
1159 n3 = MyNode("n3")
1160 n1.add_source([n2, n3])
1162 nw = SCons.Node.Walker(n1)
1163 n = nw.get_next()
1164 assert n.name == "n2", n.name
1165 n = nw.get_next()
1166 assert n.name == "n3", n.name
1167 n = nw.get_next()
1168 assert n.name == "n1", n.name
1169 n = nw.get_next()
1170 assert n is None, n
1172 n4 = MyNode("n4")
1173 n5 = MyNode("n5")
1174 n6 = MyNode("n6")
1175 n7 = MyNode("n7")
1176 n2.add_source([n4, n5])
1177 n3.add_dependency([n6, n7])
1179 nw = SCons.Node.Walker(n1)
1180 assert nw.get_next().name == "n4"
1181 assert nw.get_next().name == "n5"
1182 assert n2 in nw.history
1183 assert nw.get_next().name == "n2"
1184 assert nw.get_next().name == "n6"
1185 assert nw.get_next().name == "n7"
1186 assert n3 in nw.history
1187 assert nw.get_next().name == "n3"
1188 assert n1 in nw.history
1189 assert nw.get_next().name == "n1"
1190 assert nw.get_next() is None
1192 n8 = MyNode("n8")
1193 n8.add_dependency([n3])
1194 n7.add_dependency([n8])
1196 def cycle(node, stack) -> None:
1197 global cycle_detected
1198 cycle_detected = 1
1200 global cycle_detected
1202 nw = SCons.Node.Walker(n3, cycle_func = cycle)
1203 n = nw.get_next()
1204 assert n.name == "n6", n.name
1205 n = nw.get_next()
1206 assert n.name == "n8", n.name
1207 assert cycle_detected
1208 cycle_detected = None
1209 n = nw.get_next()
1210 assert n.name == "n7", n.name
1211 n = nw.get_next()
1212 assert nw.get_next() is None
1214 def test_abspath(self) -> None:
1215 """Test the get_abspath() method."""
1216 n = MyNode("foo")
1217 assert n.get_abspath() == str(n), n.get_abspath()
1219 def test_for_signature(self) -> None:
1220 """Test the for_signature() method."""
1221 n = MyNode("foo")
1222 assert n.for_signature() == str(n), n.get_abspath()
1224 def test_get_string(self) -> None:
1225 """Test the get_string() method."""
1226 class TestNode(MyNode):
1227 def __init__(self, name, sig) -> None:
1228 super().__init__(name)
1229 self.sig = sig
1231 def for_signature(self):
1232 return self.sig
1234 n = TestNode("foo", "bar")
1235 assert n.get_string(0) == "foo", n.get_string(0)
1236 assert n.get_string(1) == "bar", n.get_string(1)
1238 def test_literal(self) -> None:
1239 """Test the is_literal() function."""
1240 n=SCons.Node.Node()
1241 assert n.is_literal()
1243 def test_sconscripts(self) -> None:
1244 """Test the is_sconscript() function."""
1245 # check nodes are not sconscript unless added to the list
1246 n=SCons.Node.Node()
1247 n2=SCons.Node.Node()
1248 assert not n.is_sconscript()
1249 assert not n2.is_sconscript()
1251 # add node to sconscript list and verify
1252 SCons.Node.SConscriptNodes.add(n2)
1253 assert not n.is_sconscript()
1254 assert n2.is_sconscript()
1256 def test_conftests(self) -> None:
1257 """Test the is_conftest() function."""
1258 # check nodes are not sconscript unless added to the list
1259 n=SCons.Node.Node()
1260 n2=SCons.Node.Node()
1261 assert not n.is_conftest()
1262 assert not n2.is_conftest()
1264 # add node to sconscript list and verify
1265 n2.attributes.conftest_node = 1
1266 assert not n.is_conftest()
1267 assert n2.is_conftest()
1269 def test_Annotate(self) -> None:
1270 """Test using an interface-specific Annotate function."""
1271 def my_annotate(node, self=self) -> None:
1272 node.Tag('annotation', self.node_string)
1274 save_Annotate = SCons.Node.Annotate
1275 SCons.Node.Annotate = my_annotate
1277 try:
1278 self.node_string = '#1'
1279 n = SCons.Node.Node()
1280 a = n.GetTag('annotation')
1281 assert a == '#1', a
1283 self.node_string = '#2'
1284 n = SCons.Node.Node()
1285 a = n.GetTag('annotation')
1286 assert a == '#2', a
1287 finally:
1288 SCons.Node.Annotate = save_Annotate
1290 def test_clear(self) -> None:
1291 """Test clearing all cached state information."""
1292 n = SCons.Node.Node()
1294 n.set_state(3)
1295 n.binfo = 'xyz'
1296 n.includes = 'testincludes'
1297 n.Tag('found_includes', {'testkey':'testvalue'})
1298 n.implicit = 'testimplicit'
1299 n.cached = 1
1301 x = MyExecutor()
1302 n.set_executor(x)
1304 n.clear()
1306 assert n.includes is None, n.includes
1307 assert n.cached == 0, n.cached
1308 assert x.cleaned_up
1310 def test_get_subst_proxy(self) -> None:
1311 """Test the get_subst_proxy method."""
1312 n = MyNode("test")
1314 assert n.get_subst_proxy() == n, n.get_subst_proxy()
1316 def test_new_binfo(self) -> None:
1317 """Test the new_binfo() method"""
1318 n = SCons.Node.Node()
1319 result = n.new_binfo()
1320 assert isinstance(result, SCons.Node.BuildInfoBase), result
1322 def test_get_suffix(self) -> None:
1323 """Test the base Node get_suffix() method"""
1324 n = SCons.Node.Node()
1325 s = n.get_suffix()
1326 assert s == '', s
1328 def test_postprocess(self) -> None:
1329 """Test calling the base Node postprocess() method"""
1330 n = SCons.Node.Node()
1331 n.waiting_parents = {'foo', 'bar'}
1333 n.postprocess()
1334 assert n.waiting_parents == set(), n.waiting_parents
1336 def test_add_to_waiting_parents(self) -> None:
1337 """Test the add_to_waiting_parents() method"""
1338 n1 = SCons.Node.Node()
1339 n2 = SCons.Node.Node()
1340 assert n1.waiting_parents == set(), n1.waiting_parents
1341 r = n1.add_to_waiting_parents(n2)
1342 assert r == 1, r
1343 assert n1.waiting_parents == {n2}, n1.waiting_parents
1344 r = n1.add_to_waiting_parents(n2)
1345 assert r == 0, r
1348 class NodeListTestCase(unittest.TestCase):
1349 def test___str__(self) -> None:
1350 """Test"""
1351 n1 = MyNode("n1")
1352 n2 = MyNode("n2")
1353 n3 = MyNode("n3")
1354 nl = SCons.Node.NodeList([n3, n2, n1])
1356 l = [1]
1357 ul = collections.UserList([2])
1358 s = str(nl)
1359 assert s == "['n3', 'n2', 'n1']", s
1361 r = repr(nl)
1362 r = re.sub(r'at (0[xX])?[0-9a-fA-F]+', 'at 0x', r)
1363 # Don't care about ancestry: just leaf value of MyNode
1364 r = re.sub(r'<.*?\.MyNode', '<MyNode', r)
1365 # New-style classes report as "object"; classic classes report
1366 # as "instance"...
1367 r = re.sub("object", "instance", r)
1368 l = ", ".join(["<MyNode instance at 0x>"]*3)
1369 assert r == '[%s]' % l, r
1373 if __name__ == "__main__":
1374 unittest.main()
1377 # Local Variables:
1378 # tab-width:4
1379 # indent-tabs-mode:nil
1380 # End:
1381 # vim: set expandtab tabstop=4 shiftwidth=4: