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