# MIT License # # Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import SCons.compat import collections import re import unittest from typing import Optional import SCons.Errors import SCons.Node import SCons.Util from SCons.Util.sctyping import ExecutorType built_it = None built_target = None built_source = None cycle_detected = None built_order = 0 def _actionAppend(a1, a2): all = [] for curr_a in [a1, a2]: if isinstance(curr_a, MyAction): all.append(curr_a) elif isinstance(curr_a, MyListAction): all.extend(curr_a.list) elif isinstance(curr_a, list): all.extend(curr_a) else: raise Exception('Cannot Combine Actions') return MyListAction(all) class MyActionBase: def __add__(self, other): return _actionAppend(self, other) def __radd__(self, other): return _actionAppend(other, self) class MyAction(MyActionBase): def __init__(self) -> None: self.order = 0 def __call__(self, target, source, env, executor: Optional[ExecutorType] = None) -> int: global built_it, built_target, built_source, built_args, built_order if executor: target = executor.get_all_targets() source = executor.get_all_sources() built_it = 1 built_target = target built_source = source built_args = env built_order = built_order + 1 self.order = built_order return 0 def get_implicit_deps(self, target, source, env): return [] class MyExecutor: def __init__(self, env=None, targets=[], sources=[]) -> None: self.env = env self.targets = targets self.sources = sources def get_build_env(self): return self.env def get_build_scanner_path(self, scanner) -> str: return 'executor would call %s' % scanner def cleanup(self) -> None: self.cleaned_up = 1 def scan_targets(self, scanner) -> None: if not scanner: return d = scanner(self.targets) for t in self.targets: t.implicit.extend(d) def scan_sources(self, scanner) -> None: if not scanner: return d = scanner(self.sources) for t in self.targets: t.implicit.extend(d) class MyListAction(MyActionBase): def __init__(self, list) -> None: self.list = list def __call__(self, target, source, env) -> None: for A in self.list: A(target, source, env) class Environment: def __init__(self, **kw) -> None: self._dict = {} self._dict.update(kw) def __getitem__(self, key): return self._dict[key] def get(self, key, default = None): return self._dict.get(key, default) def Dictionary(self, *args): return {} def Override(self, overrides): d = self._dict.copy() d.update(overrides) return Environment(**d) def _update(self, dict) -> None: self._dict.update(dict) def get_factory(self, factory): return factory or MyNode def get_scanner(self, scanner_key): try: return self._dict['SCANNERS'][0] except: pass return [] class Builder: def __init__(self, env=None, is_explicit: bool=True) -> None: if env is None: env = Environment() self.env = env self.overrides = {} self.action = MyAction() self.source_factory = MyNode self.is_explicit = is_explicit self.target_scanner = None self.source_scanner = None def targets(self, t): return [t] def get_actions(self): return [self.action] def get_contents(self, target, source, env) -> int: return 7 class NoneBuilder(Builder): def execute(self, target, source, env): Builder.execute(self, target, source, env) return None class ListBuilder(Builder): def __init__(self, *nodes) -> None: super().__init__() self.nodes = nodes def execute(self, target, source, env): if hasattr(self, 'status'): return self.status for n in self.nodes: n.prepare() target = self.nodes[0] self.status = Builder.execute(self, target, source, env) class FailBuilder: def execute(self, target, source, env) -> int: return 1 class ExceptBuilder: def execute(self, target, source, env): raise SCons.Errors.BuildError class ExceptBuilder2: def execute(self, target, source, env): raise Exception("foo") class Scanner: called = None def __call__(self, node): self.called = 1 return node.GetTag('found_includes') def path(self, env, dir=None, target=None, source=None, kw={}): return () def select(self, node): return self def recurse_nodes(self, nodes): return nodes class MyNode(SCons.Node.Node): """The base Node class contains a number of do-nothing methods that we expect to be overridden by real, functional Node subclasses. So simulate a real, functional Node subclass. """ def __init__(self, name) -> None: super().__init__() self.name = name self.Tag('found_includes', []) def __str__(self) -> str: return self.name def get_found_includes(self, env, scanner, target): return scanner(self) class Calculator: def __init__(self, val) -> None: self.max_drift = 0 class M: def __init__(self, val) -> None: self.val = val def signature(self, args): return self.val def collect(self, args): result = self.val for a in args: result += a return result self.module = M(val) class NodeInfoBaseTestCase(unittest.TestCase): # The abstract class NodeInfoBase has not enough default slots to perform # the merge and format test (arbitrary attributes do not work). Do it with a # derived class that does provide the slots. def test_merge(self) -> None: """Test merging NodeInfoBase attributes""" class TestNodeInfo(SCons.Node.NodeInfoBase): __slots__ = ('a1', 'a2', 'a3') ni1 = TestNodeInfo() ni2 = TestNodeInfo() ni1.a1 = 1 ni1.a2 = 2 ni2.a2 = 222 ni2.a3 = 333 ni1.merge(ni2) assert ni1.a1 == 1, ni1.a1 assert ni1.a2 == 222, ni1.a2 assert ni1.a3 == 333, ni1.a3 def test_update(self) -> None: """Test the update() method""" ni = SCons.Node.NodeInfoBase() ni.update(SCons.Node.Node()) def test_format(self) -> None: """Test the NodeInfoBase.format() method""" class TestNodeInfo(SCons.Node.NodeInfoBase): __slots__ = ('xxx', 'yyy', 'zzz') ni1 = TestNodeInfo() ni1.xxx = 'x' ni1.yyy = 'y' ni1.zzz = 'z' f = ni1.format() assert f == ['x', 'y', 'z'], f field_list = ['xxx', 'zzz', 'aaa'] f = ni1.format(field_list) assert f == ['x', 'z', 'None'], f class BuildInfoBaseTestCase(unittest.TestCase): def test___init__(self) -> None: """Test BuildInfoBase initialization""" n = SCons.Node.Node() bi = SCons.Node.BuildInfoBase() assert bi def test_merge(self) -> None: """Test merging BuildInfoBase attributes""" n1 = SCons.Node.Node() bi1 = SCons.Node.BuildInfoBase() n2 = SCons.Node.Node() bi2 = SCons.Node.BuildInfoBase() bi1.bsources = 1 bi1.bdepends = 2 bi2.bdepends = 222 bi2.bact = 333 bi1.merge(bi2) assert bi1.bsources == 1, bi1.bsources assert bi1.bdepends == 222, bi1.bdepends assert bi1.bact == 333, bi1.bact class NodeTestCase(unittest.TestCase): def test_build(self) -> None: """Test building a node """ global built_it, built_order # Make sure it doesn't blow up if no builder is set. node = MyNode("www") node.build() assert built_it is None node.build(extra_kw_argument = 1) assert built_it is None node = MyNode("xxx") node.builder_set(Builder()) node.env_set(Environment()) node.path = "xxx" node.sources = ["yyy", "zzz"] node.build() assert built_it assert built_target == [node], built_target assert built_source == ["yyy", "zzz"], built_source built_it = None node = MyNode("qqq") node.builder_set(NoneBuilder()) node.env_set(Environment()) node.path = "qqq" node.sources = ["rrr", "sss"] node.builder.overrides = { "foo" : 1, "bar" : 2 } node.build() assert built_it assert built_target == [node], built_target assert built_source == ["rrr", "sss"], built_source assert built_args["foo"] == 1, built_args assert built_args["bar"] == 2, built_args fff = MyNode("fff") ggg = MyNode("ggg") lb = ListBuilder(fff, ggg) e = Environment() fff.builder_set(lb) fff.env_set(e) fff.path = "fff" ggg.builder_set(lb) ggg.env_set(e) ggg.path = "ggg" fff.sources = ["hhh", "iii"] ggg.sources = ["hhh", "iii"] built_it = None fff.build() assert built_it assert built_target == [fff], built_target assert built_source == ["hhh", "iii"], built_source built_it = None ggg.build() assert built_it assert built_target == [ggg], built_target assert built_source == ["hhh", "iii"], built_source built_it = None jjj = MyNode("jjj") b = Builder() jjj.builder_set(b) # NOTE: No env_set()! We should pull the environment from the builder. b.env = Environment() b.overrides = { "on" : 3, "off" : 4 } e.builder = b jjj.build() assert built_it assert built_target[0] == jjj, built_target[0] assert built_source == [], built_source assert built_args["on"] == 3, built_args assert built_args["off"] == 4, built_args def test_get_build_scanner_path(self) -> None: """Test the get_build_scanner_path() method""" n = SCons.Node.Node() x = MyExecutor() n.set_executor(x) p = n.get_build_scanner_path('fake_scanner') assert p == "executor would call fake_scanner", p def test_get_executor(self) -> None: """Test the get_executor() method""" n = SCons.Node.Node() try: n.get_executor(0) except AttributeError: pass else: self.fail("did not catch expected AttributeError") class Builder: action = 'act' env = 'env1' overrides = {} n = SCons.Node.Node() n.builder_set(Builder()) x = n.get_executor() assert x.env == 'env1', x.env n = SCons.Node.Node() n.builder_set(Builder()) n.env_set('env2') x = n.get_executor() assert x.env == 'env2', x.env def test_set_executor(self) -> None: """Test the set_executor() method""" n = SCons.Node.Node() n.set_executor(1) assert n.executor == 1, n.executor def test_executor_cleanup(self) -> None: """Test letting the executor cleanup its cache""" n = SCons.Node.Node() x = MyExecutor() n.set_executor(x) n.executor_cleanup() assert x.cleaned_up def test_reset_executor(self) -> None: """Test the reset_executor() method""" n = SCons.Node.Node() n.set_executor(1) assert n.executor == 1, n.executor n.reset_executor() assert not hasattr(n, 'executor'), "unexpected executor attribute" def test_built(self) -> None: """Test the built() method""" class SubNodeInfo(SCons.Node.NodeInfoBase): __slots__ = ('updated',) def update(self, node) -> None: self.updated = 1 class SubNode(SCons.Node.Node): def clear(self) -> None: self.cleared = 1 n = SubNode() n.ninfo = SubNodeInfo() n.built() assert n.cleared, n.cleared assert n.ninfo.updated, n.ninfo.cleared def test_push_to_cache(self) -> None: """Test the base push_to_cache() method""" n = SCons.Node.Node() r = n.push_to_cache() assert r is None, r def test_retrieve_from_cache(self) -> None: """Test the base retrieve_from_cache() method""" n = SCons.Node.Node() r = n.retrieve_from_cache() assert r == 0, r def test_visited(self) -> None: """Test the base visited() method Just make sure it's there and we can call it. """ n = SCons.Node.Node() n.visited() def test_builder_set(self) -> None: """Test setting a Node's Builder """ node = SCons.Node.Node() b = Builder() node.builder_set(b) assert node.builder == b def test_has_builder(self) -> None: """Test the has_builder() method """ n1 = SCons.Node.Node() assert n1.has_builder() == 0 n1.builder_set(Builder()) assert n1.has_builder() == 1 def test_has_explicit_builder(self) -> None: """Test the has_explicit_builder() method """ n1 = SCons.Node.Node() assert not n1.has_explicit_builder() n1.set_explicit(1) assert n1.has_explicit_builder() n1.set_explicit(None) assert not n1.has_explicit_builder() def test_get_builder(self) -> None: """Test the get_builder() method""" n1 = SCons.Node.Node() b = n1.get_builder() assert b is None, b b = n1.get_builder(777) assert b == 777, b n1.builder_set(888) b = n1.get_builder() assert b == 888, b b = n1.get_builder(999) assert b == 888, b def test_multiple_side_effect_has_builder(self) -> None: """Test the multiple_side_effect_has_builder() method """ n1 = SCons.Node.Node() assert n1.multiple_side_effect_has_builder() == 0 n1.builder_set(Builder()) assert n1.multiple_side_effect_has_builder() == 1 def test_is_derived(self) -> None: """Test the is_derived() method """ n1 = SCons.Node.Node() n2 = SCons.Node.Node() n3 = SCons.Node.Node() n2.builder_set(Builder()) n3.side_effect = 1 assert n1.is_derived() == 0 assert n2.is_derived() == 1 assert n3.is_derived() == 1 def test_alter_targets(self) -> None: """Test the alter_targets() method """ n = SCons.Node.Node() t, m = n.alter_targets() assert t == [], t assert m is None, m def test_is_up_to_date(self) -> None: """Test the default is_up_to_date() method.""" node = SCons.Node.Node() assert not node.is_up_to_date() def test_children_are_up_to_date(self) -> None: """Test the children_are_up_to_date() method used by subclasses """ n1 = SCons.Node.Node() n2 = SCons.Node.Node() n1.add_source([n2]) assert n1.children_are_up_to_date(), "expected up to date" n2.set_state(SCons.Node.executed) assert not n1.children_are_up_to_date(), "expected not up to date" n2.set_state(SCons.Node.up_to_date) assert n1.children_are_up_to_date(), "expected up to date" n1.always_build = 1 assert not n1.children_are_up_to_date(), "expected not up to date" def test_env_set(self) -> None: """Test setting a Node's Environment """ node = SCons.Node.Node() e = Environment() node.env_set(e) assert node.env == e def test_get_actions(self) -> None: """Test fetching a Node's action list """ node = SCons.Node.Node() node.builder_set(Builder()) a = node.builder.get_actions() assert isinstance(a[0], MyAction), a[0] def test_get_csig(self) -> None: """Test generic content signature calculation """ class TestNodeInfo(SCons.Node.NodeInfoBase): __slots__ = ('csig',) try: SCons.Node.Node.NodeInfo = TestNodeInfo def my_contents(obj) -> int: return 444 SCons.Node._get_contents_map[4] = my_contents node = SCons.Node.Node() node._func_get_contents = 4 result = node.get_csig() assert result in ('550a141f12de6341fba65b0ad0433500', '9a3e61b6bcc8abec08f195526c3132d5a4a98cc0', '3538a1ef2e113da64249eea7bd068b585ec7ce5df73b2d1e319d8c9bf47eb314'), result finally: SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase def test_get_cachedir_csig(self) -> None: """Test content signature calculation for CacheDir """ class TestNodeInfo(SCons.Node.NodeInfoBase): __slots__ = ('csig',) try: SCons.Node.Node.NodeInfo = TestNodeInfo def my_contents(obj) -> int: return 555 SCons.Node._get_contents_map[4] = my_contents node = SCons.Node.Node() node._func_get_contents = 4 result = node.get_cachedir_csig() assert result in ('15de21c670ae7c3f6f3f1f37029303c9', 'cfa1150f1787186742a9a884b73a43d8cf219f9b', '91a73fd806ab2c005c13b4dc19130a884e909dea3f72d46e30266fe1a1f588d8'), result finally: SCons.Node.Node.NodeInfo = SCons.Node.NodeInfoBase def test_get_binfo(self) -> None: """Test fetching/creating a build information structure """ class TestNodeInfo(SCons.Node.NodeInfoBase): __slots__ = ('csig',) SCons.Node.Node.NodeInfo = TestNodeInfo node = SCons.Node.Node() binfo = node.get_binfo() assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo node = SCons.Node.Node() d = SCons.Node.Node() ninfo = d.get_ninfo() assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo i = SCons.Node.Node() ninfo = i.get_ninfo() assert isinstance(ninfo, SCons.Node.NodeInfoBase), ninfo node.depends = [d] node.implicit = [i] binfo = node.get_binfo() assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo assert hasattr(binfo, 'bsources') assert hasattr(binfo, 'bsourcesigs') assert binfo.bdepends == [d] assert hasattr(binfo, 'bdependsigs') assert binfo.bimplicit == [i] assert hasattr(binfo, 'bimplicitsigs') def test_explain(self) -> None: """Test explaining why a Node must be rebuilt """ class testNode(SCons.Node.Node): def __str__(self) -> str: return 'xyzzy' node = testNode() node.exists = lambda: None # Can't do this with new-style classes (python bug #1066490) #node.__str__ = lambda: 'xyzzy' result = node.explain() assert result == "building `xyzzy' because it doesn't exist\n", result class testNode2(SCons.Node.Node): def __str__(self) -> str: return 'null_binfo' class FS: pass node = testNode2() node.fs = FS() node.fs.Top = SCons.Node.Node() result = node.explain() assert result is None, result def get_null_info(): class Null_SConsignEntry: class Null_BuildInfo: def prepare_dependencies(self) -> None: pass binfo = Null_BuildInfo() return Null_SConsignEntry() node.get_stored_info = get_null_info #see above: node.__str__ = lambda: 'null_binfo' result = node.explain() assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result # XXX additional tests for the guts of the functionality some day #def test_del_binfo(self): # """Test deleting the build information from a Node # """ # node = SCons.Node.Node() # node.binfo = None # node.del_binfo() # assert not hasattr(node, 'binfo'), node def test_store_info(self) -> None: """Test calling the method to store build information """ node = SCons.Node.Node() SCons.Node.store_info_map[node.store_info](node) def test_get_stored_info(self) -> None: """Test calling the method to fetch stored build information """ node = SCons.Node.Node() result = node.get_stored_info() assert result is None, result def test_set_always_build(self) -> None: """Test setting a Node's always_build value """ node = SCons.Node.Node() node.set_always_build() assert node.always_build node.set_always_build(3) assert node.always_build == 3 def test_set_noclean(self) -> None: """Test setting a Node's noclean value """ node = SCons.Node.Node() node.set_noclean() assert node.noclean == 1, node.noclean node.set_noclean(7) assert node.noclean == 1, node.noclean node.set_noclean(0) assert node.noclean == 0, node.noclean node.set_noclean(None) assert node.noclean == 0, node.noclean def test_set_precious(self) -> None: """Test setting a Node's precious value """ node = SCons.Node.Node() node.set_precious() assert node.precious node.set_precious(7) assert node.precious == 7 def test_set_pseudo(self) -> None: """Test setting a Node's pseudo value """ node = SCons.Node.Node() node.set_pseudo() assert node.pseudo node.set_pseudo(False) assert not node.pseudo def test_exists(self) -> None: """Test evaluating whether a Node exists. """ node = SCons.Node.Node() e = node.exists() assert e == 1, e def test_exists_repo(self) -> None: """Test evaluating whether a Node exists locally or in a repository. """ node = SCons.Node.Node() e = node.rexists() assert e == 1, e class MyNode(SCons.Node.Node): def exists(self) -> str: return 'xyz' node = MyNode() e = node.rexists() assert e == 'xyz', e def test_prepare(self) -> None: """Test preparing a node to be built By extension, this also tests the missing() method. """ node = SCons.Node.Node() n1 = SCons.Node.Node() n1.builder_set(Builder()) node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n1]) node.prepare() # should not throw an exception n2 = SCons.Node.Node() n2.linked = 1 node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n2]) node.prepare() # should not throw an exception n3 = SCons.Node.Node() node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n3]) node.prepare() # should not throw an exception class MyNode(SCons.Node.Node): def rexists(self): return None n4 = MyNode() node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n4]) exc_caught = 0 try: node.prepare() except SCons.Errors.StopError: exc_caught = 1 assert exc_caught, "did not catch expected StopError" def test_add_dependency(self): """Test adding dependencies to a Node's list. """ node = SCons.Node.Node() assert node.depends == [] zero = SCons.Node.Node() one = SCons.Node.Node() two = SCons.Node.Node() three = SCons.Node.Node() four = SCons.Node.Node() five = SCons.Node.Node() six = SCons.Node.Node() node.add_dependency([zero]) assert node.depends == [zero] node.add_dependency([one]) assert node.depends == [zero, one] node.add_dependency([two, three]) assert node.depends == [zero, one, two, three] node.add_dependency([three, four, one]) assert node.depends == [zero, one, two, three, four] try: node.add_depends([[five, six]]) except: pass else: raise Exception("did not catch expected exception") assert node.depends == [zero, one, two, three, four] def test_add_source(self): """Test adding sources to a Node's list. """ node = SCons.Node.Node() assert node.sources == [] zero = SCons.Node.Node() one = SCons.Node.Node() two = SCons.Node.Node() three = SCons.Node.Node() four = SCons.Node.Node() five = SCons.Node.Node() six = SCons.Node.Node() node.add_source([zero]) assert node.sources == [zero] node.add_source([one]) assert node.sources == [zero, one] node.add_source([two, three]) assert node.sources == [zero, one, two, three] node.add_source([three, four, one]) assert node.sources == [zero, one, two, three, four] try: node.add_source([[five, six]]) except: pass else: raise Exception("did not catch expected exception") assert node.sources == [zero, one, two, three, four], node.sources def test_add_ignore(self): """Test adding files whose dependencies should be ignored. """ node = SCons.Node.Node() assert node.ignore == [] zero = SCons.Node.Node() one = SCons.Node.Node() two = SCons.Node.Node() three = SCons.Node.Node() four = SCons.Node.Node() five = SCons.Node.Node() six = SCons.Node.Node() node.add_ignore([zero]) assert node.ignore == [zero] node.add_ignore([one]) assert node.ignore == [zero, one] node.add_ignore([two, three]) assert node.ignore == [zero, one, two, three] node.add_ignore([three, four, one]) assert node.ignore == [zero, one, two, three, four] try: node.add_ignore([[five, six]]) except: pass else: raise Exception("did not catch expected exception") assert node.ignore == [zero, one, two, three, four] def test_get_found_includes(self) -> None: """Test the default get_found_includes() method """ node = SCons.Node.Node() target = SCons.Node.Node() e = Environment() deps = node.get_found_includes(e, None, target) assert deps == [], deps def test_get_implicit_deps(self) -> None: """Test get_implicit_deps() """ node = MyNode("nnn") target = MyNode("ttt") env = Environment() # No scanner at all returns [] deps = node.get_implicit_deps(env, None, target) assert deps == [], deps s = Scanner() d1 = MyNode("d1") d2 = MyNode("d2") node.Tag('found_includes', [d1, d2]) # Simple return of the found includes deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2], deps # By default, our fake scanner recurses e = MyNode("eee") f = MyNode("fff") g = MyNode("ggg") d1.Tag('found_includes', [e, f]) d2.Tag('found_includes', [e, f]) f.Tag('found_includes', [g]) deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2, e, f, g], list(map(str, deps)) # Recursive scanning eliminates duplicates e.Tag('found_includes', [f]) deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2, e, f, g], list(map(str, deps)) # Scanner method can select specific nodes to recurse def no_fff(nodes): return [n for n in nodes if str(n)[0] != 'f'] s.recurse_nodes = no_fff deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2, e, f], list(map(str, deps)) # Scanner method can short-circuit recursing entirely s.recurse_nodes = lambda nodes: [] deps = node.get_implicit_deps(env, s, s.path) assert deps == [d1, d2], list(map(str, deps)) def test_get_env_scanner(self) -> None: """Test fetching the environment scanner for a Node """ node = SCons.Node.Node() scanner = Scanner() env = Environment(SCANNERS = [scanner]) s = node.get_env_scanner(env) assert s == scanner, s s = node.get_env_scanner(env, {'X':1}) assert s == scanner, s def test_get_target_scanner(self) -> None: """Test fetching the target scanner for a Node """ s = Scanner() b = Builder() b.target_scanner = s n = SCons.Node.Node() n.builder = b x = n.get_target_scanner() assert x is s, x def test_get_source_scanner(self) -> None: """Test fetching the source scanner for a Node """ target = SCons.Node.Node() source = SCons.Node.Node() s = target.get_source_scanner(source) assert isinstance(s, SCons.Util.Null), s ts1 = Scanner() ts2 = Scanner() ts3 = Scanner() class Builder1(Builder): def __call__(self, source): r = SCons.Node.Node() r.builder = self return [r] class Builder2(Builder1): def __init__(self, scanner) -> None: self.source_scanner = scanner builder = Builder2(ts1) targets = builder([source]) s = targets[0].get_source_scanner(source) assert s is ts1, s target.builder_set(Builder2(ts1)) target.builder.source_scanner = ts2 s = target.get_source_scanner(source) assert s is ts2, s builder = Builder1(env=Environment(SCANNERS = [ts3])) targets = builder([source]) s = targets[0].get_source_scanner(source) assert s is ts3, s def test_scan(self) -> None: """Test Scanner functionality """ env = Environment() node = MyNode("nnn") node.builder = Builder() node.env_set(env) x = MyExecutor(env, [node]) s = Scanner() d = MyNode("ddd") node.Tag('found_includes', [d]) node.builder.target_scanner = s assert node.implicit is None node.scan() assert s.called assert node.implicit == [d], node.implicit # Check that scanning a node with some stored implicit # dependencies resets internal attributes appropriately # if the stored dependencies need recalculation. class StoredNode(MyNode): def get_stored_implicit(self): return [MyNode('implicit1'), MyNode('implicit2')] save_implicit_cache = SCons.Node.implicit_cache save_implicit_deps_changed = SCons.Node.implicit_deps_changed save_implicit_deps_unchanged = SCons.Node.implicit_deps_unchanged SCons.Node.implicit_cache = 1 SCons.Node.implicit_deps_changed = None SCons.Node.implicit_deps_unchanged = None try: sn = StoredNode("eee") sn.builder_set(Builder()) sn.builder.target_scanner = s sn.scan() assert sn.implicit == [], sn.implicit assert sn.children() == [], sn.children() finally: SCons.Node.implicit_cache = save_implicit_cache SCons.Node.implicit_deps_changed = save_implicit_deps_changed SCons.Node.implicit_deps_unchanged = save_implicit_deps_unchanged def test_scanner_key(self) -> None: """Test that a scanner_key() method exists""" assert SCons.Node.Node().scanner_key() is None def test_children(self) -> None: """Test fetching the non-ignored "children" of a Node. """ node = SCons.Node.Node() n1 = SCons.Node.Node() n2 = SCons.Node.Node() n3 = SCons.Node.Node() n4 = SCons.Node.Node() n5 = SCons.Node.Node() n6 = SCons.Node.Node() n7 = SCons.Node.Node() n8 = SCons.Node.Node() n9 = SCons.Node.Node() n10 = SCons.Node.Node() n11 = SCons.Node.Node() n12 = SCons.Node.Node() node.add_source([n1, n2, n3]) node.add_dependency([n4, n5, n6]) node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n7, n8, n9]) node._add_child(node.implicit, node.implicit_set, [n10, n11, n12]) node.add_ignore([n2, n5, n8, n11]) kids = node.children() for kid in [n1, n3, n4, n6, n7, n9, n10, n12]: assert kid in kids, kid for kid in [n2, n5, n8, n11]: assert kid not in kids, kid def test_all_children(self) -> None: """Test fetching all the "children" of a Node. """ node = SCons.Node.Node() n1 = SCons.Node.Node() n2 = SCons.Node.Node() n3 = SCons.Node.Node() n4 = SCons.Node.Node() n5 = SCons.Node.Node() n6 = SCons.Node.Node() n7 = SCons.Node.Node() n8 = SCons.Node.Node() n9 = SCons.Node.Node() n10 = SCons.Node.Node() n11 = SCons.Node.Node() n12 = SCons.Node.Node() node.add_source([n1, n2, n3]) node.add_dependency([n4, n5, n6]) node.implicit = [] node.implicit_set = set() node._add_child(node.implicit, node.implicit_set, [n7, n8, n9]) node._add_child(node.implicit, node.implicit_set, [n10, n11, n12]) node.add_ignore([n2, n5, n8, n11]) kids = node.all_children() for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12]: assert kid in kids, kid def test_state(self) -> None: """Test setting and getting the state of a node """ node = SCons.Node.Node() assert node.get_state() == SCons.Node.no_state node.set_state(SCons.Node.executing) assert node.get_state() == SCons.Node.executing assert SCons.Node.pending < SCons.Node.executing assert SCons.Node.executing < SCons.Node.up_to_date assert SCons.Node.up_to_date < SCons.Node.executed assert SCons.Node.executed < SCons.Node.failed def test_walker(self) -> None: """Test walking a Node tree. """ n1 = MyNode("n1") nw = SCons.Node.Walker(n1) assert not nw.is_done() assert nw.get_next().name == "n1" assert nw.is_done() assert nw.get_next() is None n2 = MyNode("n2") n3 = MyNode("n3") n1.add_source([n2, n3]) nw = SCons.Node.Walker(n1) n = nw.get_next() assert n.name == "n2", n.name n = nw.get_next() assert n.name == "n3", n.name n = nw.get_next() assert n.name == "n1", n.name n = nw.get_next() assert n is None, n n4 = MyNode("n4") n5 = MyNode("n5") n6 = MyNode("n6") n7 = MyNode("n7") n2.add_source([n4, n5]) n3.add_dependency([n6, n7]) nw = SCons.Node.Walker(n1) assert nw.get_next().name == "n4" assert nw.get_next().name == "n5" assert n2 in nw.history assert nw.get_next().name == "n2" assert nw.get_next().name == "n6" assert nw.get_next().name == "n7" assert n3 in nw.history assert nw.get_next().name == "n3" assert n1 in nw.history assert nw.get_next().name == "n1" assert nw.get_next() is None n8 = MyNode("n8") n8.add_dependency([n3]) n7.add_dependency([n8]) def cycle(node, stack) -> None: global cycle_detected cycle_detected = 1 global cycle_detected nw = SCons.Node.Walker(n3, cycle_func = cycle) n = nw.get_next() assert n.name == "n6", n.name n = nw.get_next() assert n.name == "n8", n.name assert cycle_detected cycle_detected = None n = nw.get_next() assert n.name == "n7", n.name n = nw.get_next() assert nw.get_next() is None def test_abspath(self) -> None: """Test the get_abspath() method.""" n = MyNode("foo") assert n.get_abspath() == str(n), n.get_abspath() def test_for_signature(self) -> None: """Test the for_signature() method.""" n = MyNode("foo") assert n.for_signature() == str(n), n.get_abspath() def test_get_string(self) -> None: """Test the get_string() method.""" class TestNode(MyNode): def __init__(self, name, sig) -> None: super().__init__(name) self.sig = sig def for_signature(self): return self.sig n = TestNode("foo", "bar") assert n.get_string(0) == "foo", n.get_string(0) assert n.get_string(1) == "bar", n.get_string(1) def test_literal(self) -> None: """Test the is_literal() function.""" n=SCons.Node.Node() assert n.is_literal() def test_sconscripts(self) -> None: """Test the is_sconscript() function.""" # check nodes are not sconscript unless added to the list n=SCons.Node.Node() n2=SCons.Node.Node() assert not n.is_sconscript() assert not n2.is_sconscript() # add node to sconscript list and verify SCons.Node.SConscriptNodes.add(n2) assert not n.is_sconscript() assert n2.is_sconscript() def test_conftests(self) -> None: """Test the is_conftest() function.""" # check nodes are not sconscript unless added to the list n=SCons.Node.Node() n2=SCons.Node.Node() assert not n.is_conftest() assert not n2.is_conftest() # add node to sconscript list and verify n2.attributes.conftest_node = 1 assert not n.is_conftest() assert n2.is_conftest() def test_Annotate(self) -> None: """Test using an interface-specific Annotate function.""" def my_annotate(node, self=self) -> None: node.Tag('annotation', self.node_string) save_Annotate = SCons.Node.Annotate SCons.Node.Annotate = my_annotate try: self.node_string = '#1' n = SCons.Node.Node() a = n.GetTag('annotation') assert a == '#1', a self.node_string = '#2' n = SCons.Node.Node() a = n.GetTag('annotation') assert a == '#2', a finally: SCons.Node.Annotate = save_Annotate def test_clear(self) -> None: """Test clearing all cached state information.""" n = SCons.Node.Node() n.set_state(3) n.binfo = 'xyz' n.includes = 'testincludes' n.Tag('found_includes', {'testkey':'testvalue'}) n.implicit = 'testimplicit' n.cached = 1 x = MyExecutor() n.set_executor(x) n.clear() assert n.includes is None, n.includes assert n.cached == 0, n.cached assert x.cleaned_up def test_get_subst_proxy(self) -> None: """Test the get_subst_proxy method.""" n = MyNode("test") assert n.get_subst_proxy() == n, n.get_subst_proxy() def test_new_binfo(self) -> None: """Test the new_binfo() method""" n = SCons.Node.Node() result = n.new_binfo() assert isinstance(result, SCons.Node.BuildInfoBase), result def test_get_suffix(self) -> None: """Test the base Node get_suffix() method""" n = SCons.Node.Node() s = n.get_suffix() assert s == '', s def test_postprocess(self) -> None: """Test calling the base Node postprocess() method""" n = SCons.Node.Node() n.waiting_parents = {'foo', 'bar'} n.postprocess() assert n.waiting_parents == set(), n.waiting_parents def test_add_to_waiting_parents(self) -> None: """Test the add_to_waiting_parents() method""" n1 = SCons.Node.Node() n2 = SCons.Node.Node() assert n1.waiting_parents == set(), n1.waiting_parents r = n1.add_to_waiting_parents(n2) assert r == 1, r assert n1.waiting_parents == {n2}, n1.waiting_parents r = n1.add_to_waiting_parents(n2) assert r == 0, r class NodeListTestCase(unittest.TestCase): def test___str__(self) -> None: """Test""" n1 = MyNode("n1") n2 = MyNode("n2") n3 = MyNode("n3") nl = SCons.Node.NodeList([n3, n2, n1]) l = [1] ul = collections.UserList([2]) s = str(nl) assert s == "['n3', 'n2', 'n1']", s r = repr(nl) r = re.sub(r'at (0[xX])?[0-9a-fA-F]+', 'at 0x', r) # Don't care about ancestry: just leaf value of MyNode r = re.sub(r'<.*?\.MyNode', '"]*3) assert r == '[%s]' % l, r if __name__ == "__main__": unittest.main() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: