# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
+from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
+import collections
import os
+import re
import sys
-import types
import unittest
import SCons.Errors
import SCons.Node
+import SCons.Util
cycle_detected = None
built_order = 0
-class MyAction:
+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):
self.order = 0
-
- def __call__(self, target, source, env):
+
+ def __call__(self, target, source, env, executor=None):
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
self.order = built_order
return 0
- def get_actions(self):
- return [self]
-
-class MyNonGlobalAction:
- def __init__(self):
- self.order = 0
- self.built_it = None
- self.built_target = None
- self.built_source = None
-
+ def get_implicit_deps(self, target, source, env):
+ return []
+
+class MyExecutor:
+ def __init__(self, env=None, targets=[], sources=[]):
+ self.env = env
+ self.targets = targets
+ self.sources = sources
+ def get_build_env(self):
+ return self.env
+ def get_build_scanner_path(self, scanner):
+ return 'executor would call %s' % scanner
+ def cleanup(self):
+ self.cleaned_up = 1
+ def scan_targets(self, scanner):
+ if not scanner:
+ return
+ d = scanner(self.targets)
+ for t in self.targets:
+ t.implicit.extend(d)
+ def scan_sources(self, scanner):
+ if not scanner:
+ return
+ d = scanner(self.sources)
+ for t in self.targets:
+ t.implicit.extend(d)
+
+class MyListAction(MyActionBase):
+ def __init__(self, list):
+ self.list = list
def __call__(self, target, source, env):
- # Okay, so not ENTIRELY non-global...
- global built_order
- self.built_it = 1
- self.built_target = target
- self.built_source = source
- self.built_args = env
- built_order = built_order + 1
- self.order = built_order
- return 0
-
- def get_actions(self):
- return [self]
+ for A in self.list:
+ A(target, source, env)
class Environment:
+ def __init__(self, **kw):
+ self._dict = {}
+ self._dict.update(kw)
+ def __getitem__(self, key):
+ return self._dict[key]
def Dictionary(self, *args):
return {}
def Override(self, overrides):
- return overrides
+ d = self._dict.copy()
+ d.update(overrides)
+ return Environment(**d)
+ def _update(self, dict):
+ self._dict.update(dict)
+ def get_factory(self, factory):
+ return factory or MyNode
+ def get_scanner(self, scanner_key):
+ return self._dict['SCANNERS'][0]
class Builder:
- def __init__(self):
- self.env = Environment()
+ def __init__(self, env=None, is_explicit=1):
+ 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):
class ExceptBuilder2:
def execute(self, target, source, env):
- raise "foo"
+ raise Exception("foo")
class Scanner:
called = None
def __call__(self, node):
self.called = 1
return node.found_includes
+ def path(self, env, dir, target=None, source=None):
+ 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
def get_found_includes(self, env, scanner, target):
return scanner(self)
+class Calculator:
+ def __init__(self, val):
+ self.max_drift = 0
+ class M:
+ def __init__(self, val):
+ self.val = val
+ def signature(self, args):
+ return self.val
+ def collect(self, args):
+ return reduce(lambda x, y: x+y, args, self.val)
+ self.module = M(val)
+
+
+
+class NodeInfoBaseTestCase(unittest.TestCase):
+
+ def test_merge(self):
+ """Test merging NodeInfoBase attributes"""
+ ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni2 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+
+ ni1.a1 = 1
+ ni1.a2 = 2
+
+ ni2.a2 = 222
+ ni2.a3 = 333
+
+ ni1.merge(ni2)
+ expect = {'a1':1, 'a2':222, 'a3':333, '_version_id':1}
+ assert ni1.__dict__ == expect, ni1.__dict__
+
+ def test_update(self):
+ """Test the update() method"""
+ ni = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni.update(SCons.Node.Node())
+
+ def test_format(self):
+ """Test the NodeInfoBase.format() method"""
+ ni1 = SCons.Node.NodeInfoBase(SCons.Node.Node())
+ ni1.xxx = 'x'
+ ni1.yyy = 'y'
+ ni1.zzz = 'z'
+
+ f = ni1.format()
+ assert f == ['1', 'x', 'y', 'z'], f
+
+ ni1.field_list = ['xxx', 'zzz', 'aaa']
+
+ f = ni1.format()
+ assert f == ['x', 'z', 'None'], f
+
+
+
+class BuildInfoBaseTestCase(unittest.TestCase):
+
+ def test___init__(self):
+ """Test BuildInfoBase initialization"""
+ n = SCons.Node.Node()
+ bi = SCons.Node.BuildInfoBase(n)
+ assert bi
+
+ def test_merge(self):
+ """Test merging BuildInfoBase attributes"""
+ n1 = SCons.Node.Node()
+ bi1 = SCons.Node.BuildInfoBase(n1)
+ n2 = SCons.Node.Node()
+ bi2 = SCons.Node.BuildInfoBase(n2)
+
+ bi1.a1 = 1
+ bi1.a2 = 2
+
+ bi2.a2 = 222
+ bi2.a3 = 333
+
+ bi1.merge(bi2)
+ assert bi1.a1 == 1, bi1.a1
+ assert bi1.a2 == 222, bi1.a2
+ assert bi1.a3 == 333, bi1.a3
class NodeTestCase(unittest.TestCase):
# Make sure it doesn't blow up if no builder is set.
node = MyNode("www")
node.build()
- assert built_it == None
+ assert built_it is None
+ node.build(extra_kw_argument = 1)
+ assert built_it is None
node = MyNode("xxx")
node.builder_set(Builder())
assert built_args["on"] == 3, built_args
assert built_args["off"] == 4, built_args
- built_it = None
- built_order = 0
- node = MyNode("xxx")
- node.builder_set(Builder())
- node.env_set(Environment())
- node.sources = ["yyy", "zzz"]
- pre1 = MyNonGlobalAction()
- pre2 = MyNonGlobalAction()
- post1 = MyNonGlobalAction()
- post2 = MyNonGlobalAction()
- node.add_pre_action(pre1)
- node.add_pre_action(pre2)
- node.add_post_action(post1)
- node.add_post_action(post2)
- node.build()
- assert built_it
- assert pre1.built_it
- assert pre2.built_it
- assert post1.built_it
- assert post2.built_it
- assert pre1.order == 1, pre1.order
- assert pre2.order == 2, pre1.order
- # The action of the builder itself is order 3...
- assert post1.order == 4, pre1.order
- assert post2.order == 5, pre1.order
-
- for act in [ pre1, pre2, post1, post2 ]:
- assert type(act.built_target[0]) == type(MyNode("bar")), type(act.built_target[0])
- assert str(act.built_target[0]) == "xxx", str(act.built_target[0])
- assert act.built_source == ["yyy", "zzz"], act.built_source
+ def test_get_build_scanner_path(self):
+ """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):
+ """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):
+ """Test the set_executor() method"""
+ n = SCons.Node.Node()
+ n.set_executor(1)
+ assert n.executor == 1, n.executor
+
+ def test_executor_cleanup(self):
+ """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):
+ """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):
+ """Test the built() method"""
+ class SubNodeInfo(SCons.Node.NodeInfoBase):
+ def update(self, node):
+ self.updated = 1
+ class SubNode(SCons.Node.Node):
+ def clear(self):
+ self.cleared = 1
+
+ n = SubNode()
+ n.ninfo = SubNodeInfo(n)
+ n.built()
+ assert n.cleared, n.cleared
+ assert n.ninfo.updated, n.ninfo.cleared
+
+ def test_push_to_cache(self):
+ """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):
+ """Test the base retrieve_from_cache() method"""
+ n = SCons.Node.Node()
+ r = n.retrieve_from_cache()
+ assert r == 0, r
def test_visited(self):
"""Test the base visited() method
"""
n = SCons.Node.Node()
n.visited()
-
- def test_depends_on(self):
- """Test the depends_on() method
- """
- parent = SCons.Node.Node()
- child = SCons.Node.Node()
- parent.add_dependency([child])
- assert parent.depends_on([child])
-
+
def test_builder_set(self):
"""Test setting a Node's Builder
"""
n1.builder_set(Builder())
assert n1.has_builder() == 1
+ def test_has_explicit_builder(self):
+ """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):
+ """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):
+ """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):
"""Test the is_derived() method
"""
n = SCons.Node.Node()
t, m = n.alter_targets()
assert t == [], t
- assert m == None, m
+ assert m is None, m
- def test_current(self):
- """Test the default current() method
+ def test_is_up_to_date(self):
+ """Test the default is_up_to_date() method
"""
node = SCons.Node.Node()
- assert node.current() is None
+ assert node.is_up_to_date() is None
+
+ def test_children_are_up_to_date(self):
+ """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):
"""Test setting a Node's Environment
a = node.builder.get_actions()
assert isinstance(a[0], MyAction), a[0]
- def test_set_bsig(self):
- """Test setting a Node's signature
+ def test_get_csig(self):
+ """Test generic content signature calculation
"""
node = SCons.Node.Node()
- node.set_bsig('www')
- assert node.bsig == 'www'
+ node.get_contents = lambda: 444
+ result = node.get_csig()
+ assert result == '550a141f12de6341fba65b0ad0433500', result
- def test_get_bsig(self):
- """Test fetching a Node's signature
+ def test_get_cachedir_csig(self):
+ """Test content signature calculation for CacheDir
"""
node = SCons.Node.Node()
- node.set_bsig('xxx')
- assert node.get_bsig() == 'xxx'
+ node.get_contents = lambda: 555
+ result = node.get_cachedir_csig()
+ assert result == '15de21c670ae7c3f6f3f1f37029303c9', result
- def test_set_csig(self):
- """Test setting a Node's signature
+ def test_get_binfo(self):
+ """Test fetching/creating a build information structure
"""
node = SCons.Node.Node()
- node.set_csig('yyy')
- assert node.csig == 'yyy'
- def test_get_csig(self):
- """Test fetching a Node's signature
- """
- node = SCons.Node.Node()
- node.set_csig('zzz')
- assert node.get_csig() == 'zzz'
+ binfo = node.get_binfo()
+ assert isinstance(binfo, SCons.Node.BuildInfoBase), binfo
- def test_store_bsig(self):
- """Test calling the method to store a build signature
- """
node = SCons.Node.Node()
- node.store_bsig()
-
- def test_store_csig(self):
- """Test calling the method to store a content signature
+ d = SCons.Node.Node()
+ d.get_ninfo().csig = 777
+ i = SCons.Node.Node()
+ i.get_ninfo().csig = 888
+ 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):
+ """Test explaining why a Node must be rebuilt
"""
- node = SCons.Node.Node()
- node.store_csig()
-
- def test_get_timestamp(self):
- """Test calling the method to fetch a Node's timestamp
+ class testNode(SCons.Node.Node):
+ def __str__(self): 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): 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):
+ 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):
+ """Test calling the method to store build information
"""
node = SCons.Node.Node()
- assert node.get_timestamp() == 0
+ node.store_info()
- def test_store_timestamp(self):
- """Test calling the method to store a timestamp
+ def test_get_stored_info(self):
+ """Test calling the method to fetch stored build information
"""
node = SCons.Node.Node()
- node.store_timestamp()
+ result = node.get_stored_info()
+ assert result is None, result
def test_set_always_build(self):
"""Test setting a Node's always_build value
node.set_always_build(3)
assert node.always_build == 3
+ def test_set_noclean(self):
+ """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):
"""Test setting a Node's precious value
"""
def test_prepare(self):
"""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_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n1])
+ 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_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n2])
+ 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_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n3])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n3])
node.prepare() # should not throw an exception
return None
n4 = MyNode()
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n4])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n4])
exc_caught = 0
try:
node.prepare()
assert node.depends == []
zero = SCons.Node.Node()
- try:
- node.add_dependency(zero)
- except TypeError:
- pass
- else:
- assert 0
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 == [one]
+ assert node.depends == [zero, one]
node.add_dependency([two, three])
- assert node.depends == [one, two, three]
+ assert node.depends == [zero, one, two, three]
node.add_dependency([three, four, one])
- assert node.depends == [one, two, three, four]
+ assert node.depends == [zero, one, two, three, four]
- assert zero.get_parents() == []
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
+ 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):
assert node.sources == []
zero = SCons.Node.Node()
- try:
- node.add_source(zero)
- except TypeError:
- pass
- else:
- assert 0
-
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 == [one]
+ assert node.sources == [zero, one]
node.add_source([two, three])
- assert node.sources == [one, two, three]
+ assert node.sources == [zero, one, two, three]
node.add_source([three, four, one])
- assert node.sources == [one, two, three, four]
+ assert node.sources == [zero, one, two, three, four]
- assert zero.get_parents() == []
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
+ 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.
assert node.ignore == []
zero = SCons.Node.Node()
- try:
- node.add_ignore(zero)
- except TypeError:
- pass
- else:
- assert 0
-
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 == [one]
+ assert node.ignore == [zero, one]
node.add_ignore([two, three])
- assert node.ignore == [one, two, three]
+ assert node.ignore == [zero, one, two, three]
node.add_ignore([three, four, one])
- assert node.ignore == [one, two, three, four]
+ assert node.ignore == [zero, one, two, three, four]
- assert zero.get_parents() == []
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
+ 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):
"""Test the default get_found_includes() method
assert deps == [], deps
s = Scanner()
- d = MyNode("ddd")
- node.found_includes = [d]
+ d1 = MyNode("d1")
+ d2 = MyNode("d2")
+ node.found_includes = [d1, d2]
# Simple return of the found includes
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d], deps
+ assert deps == [d1, d2], deps
- # No "recursive" attribute on scanner doesn't recurse
+ # By default, our fake scanner recurses
e = MyNode("eee")
- d.found_includes = [e]
+ f = MyNode("fff")
+ g = MyNode("ggg")
+ d1.found_includes = [e, f]
+ d2.found_includes = [e, f]
+ f.found_includes = [g]
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d], map(str, deps)
+ assert deps == [d1, d2, e, f, g], list(map(str, deps))
- # Explicit "recursive" attribute on scanner doesn't recurse
- s.recursive = None
+ # Recursive scanning eliminates duplicates
+ e.found_includes = [f]
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d], map(str, deps)
+ assert deps == [d1, d2, e, f, g], list(map(str, deps))
- # Explicit "recursive" attribute on scanner which does recurse
- s.recursive = 1
+ # 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, target)
- assert deps == [d, e], map(str, deps)
+ assert deps == [d1, d2, e, f], list(map(str, deps))
- # Recursive scanning eliminates duplicates
- f = MyNode("fff")
- d.found_includes = [e, f]
- e.found_includes = [f]
+ # Scanner method can short-circuit recursing entirely
+ s.recurse_nodes = lambda nodes: []
deps = node.get_implicit_deps(env, s, target)
- assert deps == [d, e, f], map(str, deps)
+ assert deps == [d1, d2], list(map(str, deps))
+
+ def test_get_env_scanner(self):
+ """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):
+ """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):
+ """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):
+ 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):
"""Test Scanner functionality
"""
+ env = Environment()
node = MyNode("nnn")
node.builder = Builder()
- node.env_set(Environment())
- s = Scanner()
+ node.env_set(env)
+ x = MyExecutor(env, [node])
+ s = Scanner()
d = MyNode("ddd")
node.found_includes = [d]
- assert node.target_scanner == None, node.target_scanner
- node.target_scanner = s
+ 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):
"""Test that a scanner_key() method exists"""
- assert SCons.Node.Node().scanner_key() == None
+ assert SCons.Node.Node().scanner_key() is None
def test_children(self):
"""Test fetching the non-ignored "children" of a Node.
node.add_source([n1, n2, n3])
node.add_dependency([n4, n5, n6])
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
- node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
+ 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()
node.add_source([n1, n2, n3])
node.add_dependency([n4, n5, n6])
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
- node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
+ 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()
"""Test setting and getting the state of a node
"""
node = SCons.Node.Node()
- assert node.get_state() == None
+ 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
nw = SCons.Node.Walker(n1)
assert not nw.is_done()
- assert nw.next().name == "n1"
+ assert nw.get_next().name == "n1"
assert nw.is_done()
- assert nw.next() == None
+ assert nw.get_next() is None
n2 = MyNode("n2")
n3 = MyNode("n3")
n1.add_source([n2, n3])
nw = SCons.Node.Walker(n1)
- n = nw.next()
+ n = nw.get_next()
assert n.name == "n2", n.name
- n = nw.next()
+ n = nw.get_next()
assert n.name == "n3", n.name
- n = nw.next()
+ n = nw.get_next()
assert n.name == "n1", n.name
- n = nw.next()
- assert n == None, n
+ n = nw.get_next()
+ assert n is None, n
n4 = MyNode("n4")
n5 = MyNode("n5")
n3.add_dependency([n6, n7])
nw = SCons.Node.Walker(n1)
- assert nw.next().name == "n4"
- assert nw.next().name == "n5"
- assert nw.history.has_key(n2)
- assert nw.next().name == "n2"
- assert nw.next().name == "n6"
- assert nw.next().name == "n7"
- assert nw.history.has_key(n3)
- assert nw.next().name == "n3"
- assert nw.history.has_key(n1)
- assert nw.next().name == "n1"
- assert nw.next() == None
+ 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])
global cycle_detected
nw = SCons.Node.Walker(n3, cycle_func = cycle)
- n = nw.next()
+ n = nw.get_next()
assert n.name == "n6", n.name
- n = nw.next()
+ n = nw.get_next()
assert n.name == "n8", n.name
assert cycle_detected
cycle_detected = None
- n = nw.next()
+ n = nw.get_next()
assert n.name == "n7", n.name
- n = nw.next()
- assert nw.next() == None
-
- def test_rstr(self):
- """Test the rstr() method."""
- n1 = MyNode("n1")
- assert n1.rstr() == 'n1', n1.rstr()
+ n = nw.get_next()
+ assert nw.get_next() is None
def test_abspath(self):
"""Test the get_abspath() method."""
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_arg2nodes(self):
- """Test the arg2nodes function."""
- dict = {}
- class X(SCons.Node.Node):
- pass
- def Factory(name, directory = None, create = 1, dict=dict, X=X):
- if not dict.has_key(name):
- dict[name] = X()
- dict[name].name = name
- return dict[name]
-
- nodes = SCons.Node.arg2nodes("Util.py UtilTests.py", Factory)
- assert len(nodes) == 1, nodes
- assert isinstance(nodes[0], X)
- assert nodes[0].name == "Util.py UtilTests.py"
-
- if hasattr(types, 'UnicodeType'):
- code = """if 1:
- nodes = SCons.Node.arg2nodes(u"Util.py UtilTests.py", Factory)
- assert len(nodes) == 1, nodes
- assert isinstance(nodes[0], X)
- assert nodes[0].name == u"Util.py UtilTests.py"
- \n"""
- exec code in globals(), locals()
-
- nodes = SCons.Node.arg2nodes(["Util.py", "UtilTests.py"], Factory)
- assert len(nodes) == 2, nodes
- assert isinstance(nodes[0], X)
- assert isinstance(nodes[1], X)
- assert nodes[0].name == "Util.py"
- assert nodes[1].name == "UtilTests.py"
-
- n1 = Factory("Util.py")
- nodes = SCons.Node.arg2nodes([n1, "UtilTests.py"], Factory)
- assert len(nodes) == 2, nodes
- assert isinstance(nodes[0], X)
- assert isinstance(nodes[1], X)
- assert nodes[0].name == "Util.py"
- assert nodes[1].name == "UtilTests.py"
-
- class SConsNode(SCons.Node.Node):
- pass
- nodes = SCons.Node.arg2nodes(SConsNode())
- assert len(nodes) == 1, nodes
- assert isinstance(nodes[0], SConsNode), node
-
- class OtherNode:
- pass
- nodes = SCons.Node.arg2nodes(OtherNode())
- assert len(nodes) == 1, nodes
- assert isinstance(nodes[0], OtherNode), node
-
- def lookup_a(str, F=Factory):
- if str[0] == 'a':
- n = F(str)
- n.a = 1
- return n
- else:
- return None
-
- def lookup_b(str, F=Factory):
- if str[0] == 'b':
- n = F(str)
- n.b = 1
- return n
- else:
- return None
-
- SCons.Node.arg2nodes_lookups.append(lookup_a)
- SCons.Node.arg2nodes_lookups.append(lookup_b)
-
- nodes = SCons.Node.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
- assert len(nodes) == 3, nodes
-
- assert nodes[0].name == 'aaa', nodes[0]
- assert nodes[0].a == 1, nodes[0]
- assert not hasattr(nodes[0], 'b'), nodes[0]
-
- assert nodes[1].name == 'bbb'
- assert not hasattr(nodes[1], 'a'), nodes[1]
- assert nodes[1].b == 1, nodes[1]
-
- assert nodes[2].name == 'ccc'
- assert not hasattr(nodes[2], 'a'), nodes[1]
- assert not hasattr(nodes[2], 'b'), nodes[1]
-
- def lookup_bbbb(str, F=Factory):
- if str == 'bbbb':
- n = F(str)
- n.bbbb = 1
- return n
- else:
- return None
-
- def lookup_c(str, F=Factory):
- if str[0] == 'c':
- n = F(str)
- n.c = 1
- return n
- else:
- return None
-
- nodes = SCons.Node.arg2nodes(['bbbb', 'ccc'], Factory,
- [lookup_c, lookup_bbbb, lookup_b])
- assert len(nodes) == 2, nodes
-
- assert nodes[0].name == 'bbbb'
- assert not hasattr(nodes[0], 'a'), nodes[1]
- assert not hasattr(nodes[0], 'b'), nodes[1]
- assert nodes[0].bbbb == 1, nodes[1]
- assert not hasattr(nodes[0], 'c'), nodes[0]
-
- assert nodes[1].name == 'ccc'
- assert not hasattr(nodes[1], 'a'), nodes[1]
- assert not hasattr(nodes[1], 'b'), nodes[1]
- assert not hasattr(nodes[1], 'bbbb'), nodes[0]
- assert nodes[1].c == 1, nodes[1]
-
def test_literal(self):
"""Test the is_literal() function."""
n=SCons.Node.Node()
assert n.is_literal()
-
+
def test_Annotate(self):
"""Test using an interface-specific Annotate function."""
def my_annotate(node, self=self):
n = SCons.Node.Node()
n.set_state(3)
- n.set_bsig('bsig')
- n.set_csig('csig')
+ n.binfo = 'xyz'
n.includes = 'testincludes'
n.found_include = {'testkey':'testvalue'}
n.implicit = 'testimplicit'
+ x = MyExecutor()
+ n.set_executor(x)
+
n.clear()
- assert n.get_state() is None, n.get_state()
- assert not hasattr(n, 'bsig'), n.bsig
- assert not hasattr(n, 'csig'), n.csig
assert n.includes is None, n.includes
- assert n.found_includes == {}, n.found_includes
- assert n.implicit is None, n.implicit
+ assert x.cleaned_up
def test_get_subst_proxy(self):
"""Test the get_subst_proxy method."""
assert n.get_subst_proxy() == n, n.get_subst_proxy()
- def test_get_prevsiginfo(self):
- """Test the base Node get_prevsiginfo() method"""
+ def test_new_binfo(self):
+ """Test the new_binfo() method"""
n = SCons.Node.Node()
- siginfo = n.get_prevsiginfo()
- assert siginfo == (None, None, None), siginfo
+ result = n.new_binfo()
+ assert isinstance(result, SCons.Node.BuildInfoBase), result
+
+ def test_get_suffix(self):
+ """Test the base Node get_suffix() method"""
+ n = SCons.Node.Node()
+ s = n.get_suffix()
+ assert s == '', s
+
+ def test_postprocess(self):
+ """Test calling the base Node postprocess() method"""
+ n = SCons.Node.Node()
+ n.waiting_parents = set( ['foo','bar'] )
+
+ n.postprocess()
+ assert n.waiting_parents == set(), n.waiting_parents
+
+ def test_add_to_waiting_parents(self):
+ """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 == set((n2,)), n1.waiting_parents
+ r = n1.add_to_waiting_parents(n2)
+ assert r == 0, r
+
+
+class NodeListTestCase(unittest.TestCase):
+ def test___str__(self):
+ """Test"""
+ n1 = MyNode("n1")
+ n2 = MyNode("n2")
+ n3 = MyNode("n3")
+ nl = SCons.Node.NodeList([n3, n2, n1])
+
+ l = [1]
+ ul = collections.UserList([2])
+ try:
+ l.extend(ul)
+ except TypeError:
+ # An older version of Python (*cough* 1.5.2 *cough*)
+ # that doesn't allow UserList objects to extend lists.
+ pass
+ else:
+ s = str(nl)
+ assert s == "['n3', 'n2', 'n1']", s
+
+ r = repr(nl)
+ r = re.sub('at (0[xX])?[0-9a-fA-F]+', 'at 0x', r)
+ # Don't care about ancestry: just leaf value of MyNode
+ r = re.sub('<.*?\.MyNode', '<MyNode', r)
+ # New-style classes report as "object"; classic classes report
+ # as "instance"...
+ r = re.sub("object", "instance", r)
+ l = ", ".join(["<MyNode instance at 0x>"]*3)
+ assert r == '[%s]' % l, r
if __name__ == "__main__":
- suite = unittest.makeSuite(NodeTestCase, 'test_')
+ suite = unittest.TestSuite()
+ tclasses = [ BuildInfoBaseTestCase,
+ NodeInfoBaseTestCase,
+ NodeTestCase,
+ NodeListTestCase ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(list(map(tclass, names)))
if not unittest.TextTestRunner().run(suite).wasSuccessful():
sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: