http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / engine / SCons / EnvironmentTests.py
index b2b17e146343336cfeb7db6e518a2d0733994631..25404082a0b6311cf122018ae845e419ca39952e 100644 (file)
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import SCons.compat
+
+import copy
 import os
 import os
-import string
+import StringIO
 import sys
 import TestCmd
 import unittest
 import sys
 import TestCmd
 import unittest
@@ -38,18 +41,16 @@ def diff_env(env1, env2):
     s2 = "env2 = {\n"
     d = {}
     for k in env1._dict.keys() + env2._dict.keys():
     s2 = "env2 = {\n"
     d = {}
     for k in env1._dict.keys() + env2._dict.keys():
-       d[k] = None
-    keys = d.keys()
-    keys.sort()
-    for k in keys:
-        if env1.has_key(k):
-           if env2.has_key(k):
+        d[k] = None
+    for k in sorted(d.keys()):
+        if k in env1:
+           if k in env2:
                if env1[k] != env2[k]:
                    s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
                    s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
            else:
                s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
                if env1[k] != env2[k]:
                    s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
                    s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
            else:
                s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
-        elif env2.has_key(k):
+        elif k in env2:
            s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
     s1 = s1 + "}\n"
     s2 = s2 + "}\n"
            s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
     s1 = s1 + "}\n"
     s2 = s2 + "}\n"
@@ -60,18 +61,16 @@ def diff_dict(d1, d2):
     s2 = "d2 = {\n"
     d = {}
     for k in d1.keys() + d2.keys():
     s2 = "d2 = {\n"
     d = {}
     for k in d1.keys() + d2.keys():
-       d[k] = None
-    keys = d.keys()
-    keys.sort()
-    for k in keys:
-        if d1.has_key(k):
-           if d2.has_key(k):
+        d[k] = None
+    for k in sorted(d.keys()):
+        if k in d1:
+           if k in d2:
                if d1[k] != d2[k]:
                    s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
                    s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
            else:
                s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
                if d1[k] != d2[k]:
                    s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
                    s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
            else:
                s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
-        elif env2.has_key(k):
+        elif k in env2:
            s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
     s1 = s1 + "}\n"
     s2 = s2 + "}\n"
            s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
     s1 = s1 + "}\n"
     s2 = s2 + "}\n"
@@ -80,15 +79,17 @@ def diff_dict(d1, d2):
 called_it = {}
 built_it = {}
 
 called_it = {}
 built_it = {}
 
-class Builder:
+class Builder(SCons.Builder.BuilderBase):
     """A dummy Builder class for testing purposes.  "Building"
     a target is simply setting a value in the dictionary.
     """
     def __init__(self, name = None):
         self.name = name
 
     """A dummy Builder class for testing purposes.  "Building"
     a target is simply setting a value in the dictionary.
     """
     def __init__(self, name = None):
         self.name = name
 
-    def __call__(self, env, **kw):
+    def __call__(self, env, target=None, source=None, **kw):
         global called_it
         global called_it
+        called_it['target'] = target
+        called_it['source'] = source
         called_it.update(kw)
 
     def execute(self, target = None, **kw):
         called_it.update(kw)
 
     def execute(self, target = None, **kw):
@@ -108,18 +109,32 @@ class Scanner:
         self.skeys = skeys
 
     def __call__(self, filename):
         self.skeys = skeys
 
     def __call__(self, filename):
+        global scanned_it
         scanned_it[filename] = 1
 
     def __cmp__(self, other):
         scanned_it[filename] = 1
 
     def __cmp__(self, other):
-        return cmp(self.__dict__, other.__dict__)
+        try:
+            return cmp(self.__dict__, other.__dict__)
+        except AttributeError:
+            return 1
+
+    def get_skeys(self, env):
+        return self.skeys
+
+    def __str__(self):
+        return self.name
 
 
 
 class CLVar(UserList.UserList):
     def __init__(self, seq):
 
 
 
 class CLVar(UserList.UserList):
     def __init__(self, seq):
-        if type(seq) == type(''):
-            seq = string.split(seq)
+        if isinstance(seq, str):
+            seq = seq.split()
         UserList.UserList.__init__(self, seq)
         UserList.UserList.__init__(self, seq)
+    def __add__(self, other):
+        return UserList.UserList.__add__(self, CLVar(other))
+    def __radd__(self, other):
+        return UserList.UserList.__radd__(self, CLVar(other))
     def __coerce__(self, other):
         return (self, CLVar(other))
 
     def __coerce__(self, other):
         return (self, CLVar(other))
 
@@ -135,45 +150,118 @@ class DummyNode:
     def get_subst_proxy(self):
         return self
 
     def get_subst_proxy(self):
         return self
 
+def test_tool( env ):
+    env['_F77INCFLAGS'] = '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
+
+class TestEnvironmentFixture:
+    def TestEnvironment(self, *args, **kw):
+        if not kw or 'tools' not in kw:
+            kw['tools'] = [test_tool]
+        default_keys = { 'CC' : 'cc',
+                         'CCFLAGS' : '-DNDEBUG',
+                         'ENV' : { 'TMP' : '/tmp' } }
+        for key, value in default_keys.items():
+            if key not in kw:
+                kw[key] = value
+        if 'BUILDERS' not in kw:
+            static_obj = SCons.Builder.Builder(action = {},
+                                               emitter = {},
+                                               suffix = '.o',
+                                               single_source = 1)
+            kw['BUILDERS'] = {'Object' : static_obj}
+            static_obj.add_action('.cpp', 'fake action')
+            
+        env = Environment(*args, **kw)
+        return env
+
+class SubstitutionTestCase(unittest.TestCase):
 
 
+    def test___init__(self):
+        """Test initializing a SubstitutionEnvironment
+        """
+        env = SubstitutionEnvironment()
+        assert '__env__' not in env
 
 
-class EnvironmentTestCase(unittest.TestCase):
+    def test___cmp__(self):
+        """Test comparing SubstitutionEnvironments
+        """
 
 
-    def test___init__(self):
-        """Test construction Environment creation
+        env1 = SubstitutionEnvironment(XXX = 'x')
+        env2 = SubstitutionEnvironment(XXX = 'x')
+        env3 = SubstitutionEnvironment(XXX = 'xxx')
+        env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x')
 
 
-        Create two with identical arguments and check that
-        they compare the same.
+        assert env1 == env2
+        assert env1 != env3
+        assert env1 != env4
+
+    def test___delitem__(self):
+        """Test deleting a variable from a SubstitutionEnvironment
         """
         """
-        env1 = Environment(XXX = 'x', YYY = 'y')
-        env2 = Environment(XXX = 'x', YYY = 'y')
-        assert env1 == env2, diff_env(env1, env2)
+        env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+        env2 = SubstitutionEnvironment(XXX = 'x')
+        del env1['YYY']
+        assert env1 == env2
 
 
-        assert env1['__env__'] is env1, env1['__env__']
-        assert env2['__env__'] is env2, env2['__env__']
+    def test___getitem__(self):
+        """Test fetching a variable from a SubstitutionEnvironment
+        """
+        env = SubstitutionEnvironment(XXX = 'x')
+        assert env['XXX'] == 'x', env['XXX']
+
+    def test___setitem__(self):
+        """Test setting a variable in a SubstitutionEnvironment
+        """
+        env1 = SubstitutionEnvironment(XXX = 'x')
+        env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+        env1['YYY'] = 'y'
+        assert env1 == env2
 
     def test_get(self):
 
     def test_get(self):
-        """Test the get() method."""
-        env = Environment(aaa = 'AAA')
+        """Test the SubstitutionEnvironment get() method
+        """
+        env = SubstitutionEnvironment(XXX = 'x')
+        assert env.get('XXX') == 'x', env.get('XXX')
+        assert env.get('YYY') is None, env.get('YYY')
 
 
-        x = env.get('aaa')
-        assert x == 'AAA', x
-        x = env.get('aaa', 'XXX')
-        assert x == 'AAA', x
-        x = env.get('bbb')
-        assert x is None, x
-        x = env.get('bbb', 'XXX')
-        assert x == 'XXX', x
+    def test_has_key(self):
+        """Test the SubstitutionEnvironment has_key() method
+        """
+        env = SubstitutionEnvironment(XXX = 'x')
+        assert 'XXX' in env
+        assert 'YYY' not in env
+
+    def test_contains(self):
+        """Test the SubstitutionEnvironment __contains__() method
+        """
+        try:
+            'x' in {'x':1}
+        except TypeError:
+            # TODO(1.5)
+            # An early version of Python that doesn't support "in"
+            # on dictionaries.  Just pass the test.
+            pass
+        else:
+            env = SubstitutionEnvironment(XXX = 'x')
+            assert 'XXX' in env
+            assert not 'YYY' in env
+
+    def test_items(self):
+        """Test the SubstitutionEnvironment items() method
+        """
+        env = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
+        items = env.items()
+        assert items == [('XXX','x'), ('YYY','y')], items
 
     def test_arg2nodes(self):
         """Test the arg2nodes method
         """
 
     def test_arg2nodes(self):
         """Test the arg2nodes method
         """
-        env = Environment()
+        env = SubstitutionEnvironment()
         dict = {}
         class X(SCons.Node.Node):
             pass
         def Factory(name, directory = None, create = 1, dict=dict, X=X):
         dict = {}
         class X(SCons.Node.Node):
             pass
         def Factory(name, directory = None, create = 1, dict=dict, X=X):
-            if not dict.has_key(name):
+            if name not in dict:
                 dict[name] = X()
                 dict[name].name = name
             return dict[name]
                 dict[name] = X()
                 dict[name].name = name
             return dict[name]
@@ -183,8 +271,9 @@ class EnvironmentTestCase(unittest.TestCase):
         assert isinstance(nodes[0], X)
         assert nodes[0].name == "Util.py UtilTests.py"
 
         assert isinstance(nodes[0], X)
         assert nodes[0].name == "Util.py UtilTests.py"
 
-        import types
-        if hasattr(types, 'UnicodeType'):
+        try: unicode
+        except NameError: pass
+        else:
             code = """if 1:
                 nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory)
                 assert len(nodes) == 1, nodes
             code = """if 1:
                 nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory)
                 assert len(nodes) == 1, nodes
@@ -236,7 +325,7 @@ class EnvironmentTestCase(unittest.TestCase):
             else:
                 return None
 
             else:
                 return None
 
-        env_ll = env.Copy()
+        env_ll = SubstitutionEnvironment()
         env_ll.lookup_list = [lookup_a, lookup_b]
 
         nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
         env_ll.lookup_list = [lookup_a, lookup_b]
 
         nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
@@ -286,53 +375,89 @@ class EnvironmentTestCase(unittest.TestCase):
         assert not hasattr(nodes[1], 'bbbb'), nodes[0]
         assert nodes[1].c == 1, nodes[1]
 
         assert not hasattr(nodes[1], 'bbbb'), nodes[0]
         assert nodes[1].c == 1, nodes[1]
 
+    def test_arg2nodes_target_source(self):
+        """Test the arg2nodes method with target= and source= keywords
+        """
+        targets = [DummyNode('t1'), DummyNode('t2')]
+        sources = [DummyNode('s1'), DummyNode('s2')]
+        env = SubstitutionEnvironment()
+        nodes = env.arg2nodes(['${TARGET}-a',
+                               '${SOURCE}-b',
+                               '${TARGETS[1]}-c',
+                               '${SOURCES[1]}-d'],
+                              DummyNode,
+                              target=targets,
+                              source=sources)
+        names = [n.name for n in nodes]
+        assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names
+
+    def test_gvars(self):
+        """Test the base class gvars() method"""
+        env = SubstitutionEnvironment()
+        gvars = env.gvars()
+        assert gvars == {}, gvars
+
+    def test_lvars(self):
+        """Test the base class lvars() method"""
+        env = SubstitutionEnvironment()
+        lvars = env.lvars()
+        assert lvars == {}, lvars
+
     def test_subst(self):
         """Test substituting construction variables within strings
 
         Check various combinations, including recursive expansion
         of variables into other variables.
         """
     def test_subst(self):
         """Test substituting construction variables within strings
 
         Check various combinations, including recursive expansion
         of variables into other variables.
         """
-        env = Environment(AAA = 'a', BBB = 'b')
+        env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
         mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
         assert mystr == "a aA b", mystr
 
         # Changed the tests below to reflect a bug fix in
         # subst()
         mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
         assert mystr == "a aA b", mystr
 
         # Changed the tests below to reflect a bug fix in
         # subst()
-        env = Environment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
+        env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
         assert mystr == "b bA bB b", mystr
 
         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
         assert mystr == "b bA bB b", mystr
 
-        env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
+        env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
         assert mystr == "c cA cB c", mystr
 
         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
         assert mystr == "c cA cB c", mystr
 
+        # Lists:
+        env = SubstitutionEnvironment(AAA = ['a', 'aa', 'aaa'])
+        mystr = env.subst("$AAA")
+        assert mystr == "a aa aaa", mystr
+
+        # Tuples:
+        env = SubstitutionEnvironment(AAA = ('a', 'aa', 'aaa'))
+        mystr = env.subst("$AAA")
+        assert mystr == "a aa aaa", mystr
+
         t1 = DummyNode('t1')
         t2 = DummyNode('t2')
         s1 = DummyNode('s1')
         s2 = DummyNode('s2')
 
         t1 = DummyNode('t1')
         t2 = DummyNode('t2')
         s1 = DummyNode('s1')
         s2 = DummyNode('s2')
 
-        env = Environment(AAA = 'aaa')
+        env = SubstitutionEnvironment(AAA = 'aaa')
         s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
         assert s == "aaa t1 s1 s2", s
         s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
         assert s == "aaa t1 t2 s1", s
         s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
         assert s == "aaa t1 s1 s2", s
         s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
         assert s == "aaa t1 t2 s1", s
-        s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2], dict={})
-        assert s == "aaa", s
 
 
-        # Test callables in the Environment
+        # Test callables in the SubstitutionEnvironment
         def foo(target, source, env, for_signature):
             assert str(target) == 't', target
             assert str(source) == 's', source
             return env["FOO"]
 
         def foo(target, source, env, for_signature):
             assert str(target) == 't', target
             assert str(source) == 's', source
             return env["FOO"]
 
-        env = Environment(BAR=foo, FOO='baz')
+        env = SubstitutionEnvironment(BAR=foo, FOO='baz')
         t = DummyNode('t')
         s = DummyNode('s')
 
         subst = env.subst('test $BAR', target=t, source=s)
         assert subst == 'test baz', subst
 
         t = DummyNode('t')
         s = DummyNode('s')
 
         subst = env.subst('test $BAR', target=t, source=s)
         assert subst == 'test baz', subst
 
-        # Test not calling callables in the Environment
+        # Test not calling callables in the SubstitutionEnvironment
         if 0:
             # This will take some serious surgery to subst() and
             # subst_list(), so just leave these tests out until we can
         if 0:
             # This will take some serious surgery to subst() and
             # subst_list(), so just leave these tests out until we can
@@ -340,7 +465,7 @@ class EnvironmentTestCase(unittest.TestCase):
             def bar(arg):
                 pass
 
             def bar(arg):
                 pass
 
-            env = Environment(BAR=bar, FOO='$BAR')
+            env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
 
             subst = env.subst('$BAR', call=None)
             assert subst is bar, subst
 
             subst = env.subst('$BAR', call=None)
             assert subst is bar, subst
@@ -349,8 +474,8 @@ class EnvironmentTestCase(unittest.TestCase):
             assert subst is bar, subst
 
     def test_subst_kw(self):
             assert subst is bar, subst
 
     def test_subst_kw(self):
-       """Test substituting construction variables within dictionaries"""
-       env = Environment(AAA = 'a', BBB = 'b')
+        """Test substituting construction variables within dictionaries"""
+        env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
         kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
         assert len(kw) == 2, kw
         assert kw['a'] == 'aaa', kw['a']
         kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
         assert len(kw) == 2, kw
         assert kw['a'] == 'aaa', kw['a']
@@ -359,21 +484,21 @@ class EnvironmentTestCase(unittest.TestCase):
     def test_subst_list(self):
         """Test substituting construction variables in command lists
         """
     def test_subst_list(self):
         """Test substituting construction variables in command lists
         """
-        env = Environment(AAA = 'a', BBB = 'b')
+        env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
         l = env.subst_list("$AAA ${AAA}A $BBBB $BBB")
         assert l == [["a", "aA", "b"]], l
 
         # Changed the tests below to reflect a bug fix in
         # subst()
         l = env.subst_list("$AAA ${AAA}A $BBBB $BBB")
         assert l == [["a", "aA", "b"]], l
 
         # Changed the tests below to reflect a bug fix in
         # subst()
-        env = Environment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
+        env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
         assert l == [["b", "bA", "bB", "b"]], l
 
         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
         assert l == [["b", "bA", "bB", "b"]], l
 
-        env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
+        env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
         assert l == [["c", "cA", "cB", "c"]], mystr
 
         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
         assert l == [["c", "cA", "cB", "c"]], mystr
 
-        env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
+        env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
         lst = env.subst_list([ "$AAA", "B $CCC" ])
         assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
 
         lst = env.subst_list([ "$AAA", "B $CCC" ])
         assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
 
@@ -382,28 +507,26 @@ class EnvironmentTestCase(unittest.TestCase):
         s1 = DummyNode('s1')
         s2 = DummyNode('s2')
 
         s1 = DummyNode('s1')
         s2 = DummyNode('s2')
 
-        env = Environment(AAA = 'aaa')
+        env = SubstitutionEnvironment(AAA = 'aaa')
         s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
         assert s == [["aaa", "t1", "s1", "s2"]], s
         s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
         assert s == [["aaa", "t1", "t2", "s1"]], s
         s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
         assert s == [["aaa", "t1", "s1", "s2"]], s
         s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
         assert s == [["aaa", "t1", "t2", "s1"]], s
-        s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2], dict={})
-        assert s == [["aaa"]], s
 
 
-        # Test callables in the Environment
+        # Test callables in the SubstitutionEnvironment
         def foo(target, source, env, for_signature):
             assert str(target) == 't', target
             assert str(source) == 's', source
             return env["FOO"]
 
         def foo(target, source, env, for_signature):
             assert str(target) == 't', target
             assert str(source) == 's', source
             return env["FOO"]
 
-        env = Environment(BAR=foo, FOO='baz')
+        env = SubstitutionEnvironment(BAR=foo, FOO='baz')
         t = DummyNode('t')
         s = DummyNode('s')
 
         lst = env.subst_list('test $BAR', target=t, source=s)
         assert lst == [['test', 'baz']], lst
 
         t = DummyNode('t')
         s = DummyNode('s')
 
         lst = env.subst_list('test $BAR', target=t, source=s)
         assert lst == [['test', 'baz']], lst
 
-        # Test not calling callables in the Environment
+        # Test not calling callables in the SubstitutionEnvironment
         if 0:
             # This will take some serious surgery to subst() and
             # subst_list(), so just leave these tests out until we can
         if 0:
             # This will take some serious surgery to subst() and
             # subst_list(), so just leave these tests out until we can
@@ -411,7 +534,7 @@ class EnvironmentTestCase(unittest.TestCase):
             def bar(arg):
                 pass
 
             def bar(arg):
                 pass
 
-            env = Environment(BAR=bar, FOO='$BAR')
+            env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
 
             subst = env.subst_list('$BAR', call=None)
             assert subst is bar, subst
 
             subst = env.subst_list('$BAR', call=None)
             assert subst is bar, subst
@@ -428,10 +551,22 @@ class EnvironmentTestCase(unittest.TestCase):
             def get(self):
                 return self.val + '-proxy'
 
             def get(self):
                 return self.val + '-proxy'
 
+        class MyNode:
+            def __init__(self, val):
+                self.val = val
+            def get_subst_proxy(self):
+                return self
+            def __str__(self):
+                return self.val
+
         class MyObj:
         class MyObj:
-            pass
+            def get(self):
+                return self
 
 
-        env = Environment(FOO='foo', BAR='bar', PROXY=MyProxy('my1'))
+        env = SubstitutionEnvironment(FOO='foo',
+                                      BAR='bar',
+                                      LIST=['one', 'two'],
+                                      PROXY=MyProxy('my1'))
 
         r = env.subst_path('$FOO')
         assert r == ['foo'], r
 
         r = env.subst_path('$FOO')
         assert r == ['foo'], r
@@ -439,11 +574,373 @@ class EnvironmentTestCase(unittest.TestCase):
         r = env.subst_path(['$FOO', 'xxx', '$BAR'])
         assert r == ['foo', 'xxx', 'bar'], r
 
         r = env.subst_path(['$FOO', 'xxx', '$BAR'])
         assert r == ['foo', 'xxx', 'bar'], r
 
+        r = env.subst_path(['$FOO', '$LIST', '$BAR'])
+        assert list(map(str, r)) == ['foo', 'one two', 'bar'], r
+
+        r = env.subst_path(['$FOO', '$TARGET', '$SOURCE', '$BAR'])
+        assert r == ['foo', '', '', 'bar'], r
+
+        r = env.subst_path(['$FOO', '$TARGET', '$BAR'], target=MyNode('ttt'))
+        assert list(map(str, r)) == ['foo', 'ttt', 'bar'], r
+
+        r = env.subst_path(['$FOO', '$SOURCE', '$BAR'], source=MyNode('sss'))
+        assert list(map(str, r)) == ['foo', 'sss', 'bar'], r
+
         n = MyObj()
 
         r = env.subst_path(['$PROXY', MyProxy('my2'), n])
         assert r == ['my1-proxy', 'my2-proxy', n], r
 
         n = MyObj()
 
         r = env.subst_path(['$PROXY', MyProxy('my2'), n])
         assert r == ['my1-proxy', 'my2-proxy', n], r
 
+        class StringableObj:
+            def __init__(self, s):
+                self.s = s
+            def __str__(self):
+                return self.s
+
+        env = SubstitutionEnvironment(FOO=StringableObj("foo"),
+                          BAR=StringableObj("bar"))
+
+        r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
+        assert r == [ "foo/bar", "bar/baz" ], r
+
+        r = env.subst_path([ "bar/${FOO}", "baz/${BAR}" ])
+        assert r == [ "bar/foo", "baz/bar" ], r
+
+        r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
+        assert r == [ "bar/foo/bar", "baz/bar/baz" ], r
+
+    def test_subst_target_source(self):
+        """Test the base environment subst_target_source() method"""
+        env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
+        mystr = env.subst_target_source("$AAA ${AAA}A $BBBB $BBB")
+        assert mystr == "a aA b", mystr
+
+    def test_backtick(self):
+        """Test the backtick() method for capturing command output"""
+        env = SubstitutionEnvironment()
+
+        test = TestCmd.TestCmd(workdir = '')
+        test.write('stdout.py', """\
+import sys
+sys.stdout.write('this came from stdout.py\\n')
+sys.exit(0)
+""")
+        test.write('stderr.py', """\
+import sys
+sys.stderr.write('this came from stderr.py\\n')
+sys.exit(0)
+""")
+        test.write('fail.py', """\
+import sys
+sys.exit(1)
+""")
+        test.write('echo.py', """\
+import os, sys
+sys.stdout.write(os.environ['ECHO'] + '\\n')
+sys.exit(0)
+""")
+
+        save_stderr = sys.stderr
+
+        python = '"' + sys.executable + '"'
+
+        try:
+            sys.stderr = StringIO.StringIO()
+            cmd = '%s %s' % (python, test.workpath('stdout.py'))
+            output = env.backtick(cmd)
+            errout = sys.stderr.getvalue()
+            assert output == 'this came from stdout.py\n', output
+            assert errout == '', errout
+
+            sys.stderr = StringIO.StringIO()
+            cmd = '%s %s' % (python, test.workpath('stderr.py'))
+            output = env.backtick(cmd)
+            errout = sys.stderr.getvalue()
+            assert output == '', output
+            assert errout == 'this came from stderr.py\n', errout
+
+            sys.stderr = StringIO.StringIO()
+            cmd = '%s %s' % (python, test.workpath('fail.py'))
+            try:
+                env.backtick(cmd)
+            except OSError, e:
+                assert str(e) == "'%s' exited 1" % cmd, str(e)
+            else:
+                self.fail("did not catch expected OSError")
+
+            sys.stderr = StringIO.StringIO()
+            cmd = '%s %s' % (python, test.workpath('echo.py'))
+            env['ENV'] = os.environ.copy()
+            env['ENV']['ECHO'] = 'this came from ECHO'
+            output = env.backtick(cmd)
+            errout = sys.stderr.getvalue()
+            assert output == 'this came from ECHO\n', output
+            assert errout == '', errout
+
+        finally:
+            sys.stderr = save_stderr
+
+    def test_AddMethod(self):
+        """Test the AddMethod() method"""
+        env = SubstitutionEnvironment(FOO = 'foo')
+
+        def func(self):
+            return 'func-' + self['FOO']
+
+        assert not hasattr(env, 'func')
+        env.AddMethod(func)
+        r = env.func()
+        assert r == 'func-foo', r
+
+        assert not hasattr(env, 'bar')
+        env.AddMethod(func, 'bar')
+        r = env.bar()
+        assert r == 'func-foo', r
+
+        def func2(self, arg=''):
+            return 'func2-' + self['FOO'] + arg
+
+        env.AddMethod(func2)
+        r = env.func2()
+        assert r == 'func2-foo', r
+        r = env.func2('-xxx')
+        assert r == 'func2-foo-xxx', r
+
+        env.AddMethod(func2, 'func')
+        r = env.func()
+        assert r == 'func2-foo', r
+        r = env.func('-yyy')
+        assert r == 'func2-foo-yyy', r
+
+        # Test that clones of clones correctly re-bind added methods.
+        env1 = Environment(FOO = '1')
+        env1.AddMethod(func2)
+        env2 = env1.Clone(FOO = '2')
+        env3 = env2.Clone(FOO = '3')
+        env4 = env3.Clone(FOO = '4')
+        r = env1.func2()
+        assert r == 'func2-1', r
+        r = env2.func2()
+        assert r == 'func2-2', r
+        r = env3.func2()
+        assert r == 'func2-3', r
+        r = env4.func2()
+        assert r == 'func2-4', r
+
+        # Test that clones don't re-bind an attribute that the user
+        env1 = Environment(FOO = '1')
+        env1.AddMethod(func2)
+        def replace_func2():
+            return 'replace_func2'
+        env1.func2 = replace_func2
+        env2 = env1.Clone(FOO = '2')
+        r = env2.func2()
+        assert r == 'replace_func2', r
+
+    def test_Override(self):
+        "Test overriding construction variables"
+        env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4)
+        assert env['ONE'] == 1, env['ONE']
+        assert env['TWO'] == 2, env['TWO']
+        assert env['THREE'] == 3, env['THREE']
+        assert env['FOUR'] == 4, env['FOUR']
+
+        env2 = env.Override({'TWO'   : '10',
+                             'THREE' :'x $THREE y',
+                             'FOUR'  : ['x', '$FOUR', 'y']})
+        assert env2['ONE'] == 1, env2['ONE']
+        assert env2['TWO'] == '10', env2['TWO']
+        assert env2['THREE'] == 'x 3 y', env2['THREE']
+        assert env2['FOUR'] == ['x', 4, 'y'], env2['FOUR']
+
+        assert env['ONE'] == 1, env['ONE']
+        assert env['TWO'] == 2, env['TWO']
+        assert env['THREE'] == 3, env['THREE']
+        assert env['FOUR'] == 4, env['FOUR']
+
+        env2.Replace(ONE = "won")
+        assert env2['ONE'] == "won", env2['ONE']
+        assert env['ONE'] == 1, env['ONE']
+
+    def test_ParseFlags(self):
+        """Test the ParseFlags() method
+        """
+        env = SubstitutionEnvironment()
+
+        empty = {
+            'ASFLAGS'       : [],
+            'CFLAGS'        : [],
+            'CCFLAGS'       : [],
+            'CPPDEFINES'    : [],
+            'CPPFLAGS'      : [],
+            'CPPPATH'       : [],
+            'FRAMEWORKPATH' : [],
+            'FRAMEWORKS'    : [],
+            'LIBPATH'       : [],
+            'LIBS'          : [],
+            'LINKFLAGS'     : [],
+            'RPATH'         : [],
+        }
+
+        d = env.ParseFlags(None)
+        assert d == empty, d
+
+        d = env.ParseFlags('')
+        assert d == empty, d
+
+        d = env.ParseFlags([])
+        assert d == empty, d
+
+        s = "-I/usr/include/fum -I bar -X\n" + \
+            '-I"C:\\Program Files\\ASCEND\\include" ' + \
+            "-L/usr/fax -L foo -lxxx -l yyy " + \
+            '-L"C:\\Program Files\\ASCEND" -lascend ' + \
+            "-Wa,-as -Wl,-link " + \
+            "-Wl,-rpath=rpath1 " + \
+            "-Wl,-R,rpath2 " + \
+            "-Wl,-Rrpath3 " + \
+            "-Wp,-cpp " + \
+            "-std=c99 " + \
+            "-framework Carbon " + \
+            "-frameworkdir=fwd1 " + \
+            "-Ffwd2 " + \
+            "-F fwd3 " + \
+            "-pthread " + \
+            "-mno-cygwin -mwindows " + \
+            "-arch i386 -isysroot /tmp +DD64 " + \
+            "-DFOO -DBAR=value -D BAZ "
+
+        d = env.ParseFlags(s)
+
+        assert d['ASFLAGS'] == ['-as'], d['ASFLAGS']
+        assert d['CFLAGS']  == ['-std=c99']
+        assert d['CCFLAGS'] == ['-X', '-Wa,-as',
+                                  '-pthread', '-mno-cygwin',
+                                  ('-arch', 'i386'), ('-isysroot', '/tmp'),
+                                  '+DD64'], d['CCFLAGS']
+        assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
+        assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
+        assert d['CPPPATH'] == ['/usr/include/fum',
+                                'bar',
+                                'C:\\Program Files\\ASCEND\\include'], d['CPPPATH']
+        assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
+        assert d['FRAMEWORKS'] == ['Carbon'], d['FRAMEWORKS']
+        assert d['LIBPATH'] == ['/usr/fax',
+                                'foo',
+                                'C:\\Program Files\\ASCEND'], d['LIBPATH']
+        LIBS = list(map(str, d['LIBS']))
+        assert LIBS == ['xxx', 'yyy', 'ascend'], (d['LIBS'], LIBS)
+        assert d['LINKFLAGS'] == ['-Wl,-link', '-pthread',
+                                  '-mno-cygwin', '-mwindows',
+                                  ('-arch', 'i386'),
+                                  ('-isysroot', '/tmp'),
+                                  '+DD64'], d['LINKFLAGS']
+        assert d['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d['RPATH']
+
+
+    def test_MergeFlags(self):
+        """Test the MergeFlags() method
+        """
+        env = SubstitutionEnvironment()
+        env.MergeFlags('')
+        assert 'CCFLAGS' not in env, env['CCFLAGS']
+        env.MergeFlags('-X')
+        assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+        env.MergeFlags('-X')
+        assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+
+        env = SubstitutionEnvironment(CCFLAGS=None)
+        env.MergeFlags('-Y')
+        assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
+
+        env = SubstitutionEnvironment()
+        env.MergeFlags({'A':['aaa'], 'B':['bbb']})
+        assert env['A'] == ['aaa'], env['A']
+        assert env['B'] == ['bbb'], env['B']
+
+#     def test_MergeShellPaths(self):
+#         """Test the MergeShellPaths() method
+#         """
+#         env = Environment()
+#         env.MergeShellPaths({})
+#         assert not env['ENV'].has_key('INCLUDE'), env['INCLUDE']
+#         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
+#         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
+#         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
+#         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
+#         env.MergeShellPaths({'INCLUDE': r'xyz'})
+#         assert env['ENV']['INCLUDE'] == r'xyz%sc:\Program Files\Stuff'%os.pathsep, env['ENV']['INCLUDE']
+
+#         env = Environment()
+#         env['ENV']['INCLUDE'] = 'xyz'
+#         env.MergeShellPaths({'INCLUDE':['c:/inc1', 'c:/inc2']} )
+#         assert env['ENV']['INCLUDE'] == r'c:/inc1%sc:/inc2%sxyz'%(os.pathsep, os.pathsep), env['ENV']['INCLUDE']
+
+#         # test prepend=0
+#         env = Environment()
+#         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}, prepend=0)
+#         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
+#         env.MergeShellPaths({'INCLUDE': r'xyz'}, prepend=0)
+#         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff%sxyz'%os.pathsep, env['ENV']['INCLUDE']
+
+
+class BaseTestCase(unittest.TestCase,TestEnvironmentFixture):
+
+    reserved_variables = [
+        'CHANGED_SOURCES',
+        'CHANGED_TARGETS',
+        'SOURCE',
+        'SOURCES',
+        'TARGET',
+        'TARGETS',
+        'UNCHANGED_SOURCES',
+        'UNCHANGED_TARGETS',
+    ]
+
+    def test___init__(self):
+        """Test construction Environment creation
+
+        Create two with identical arguments and check that
+        they compare the same.
+        """
+        env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+        env2 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+        assert env1 == env2, diff_env(env1, env2)
+
+        assert '__env__' not in env1
+        assert '__env__' not in env2
+
+    def test_variables(self):
+        """Test that variables only get applied once."""
+        class FakeOptions:
+            def __init__(self, key, val):
+                self.calls = 0
+                self.key = key
+                self.val = val
+            def keys(self):
+                return [self.key]
+            def Update(self, env):
+                env[self.key] = self.val
+                self.calls = self.calls + 1
+
+        o = FakeOptions('AAA', 'fake_opt')
+        env = Environment(variables=o, AAA='keyword_arg')
+        assert o.calls == 1, o.calls
+        assert env['AAA'] == 'fake_opt', env['AAA']
+
+    def test_get(self):
+        """Test the get() method."""
+        env = self.TestEnvironment(aaa = 'AAA')
+
+        x = env.get('aaa')
+        assert x == 'AAA', x
+        x = env.get('aaa', 'XXX')
+        assert x == 'AAA', x
+        x = env.get('bbb')
+        assert x is None, x
+        x = env.get('bbb', 'XXX')
+        assert x == 'XXX', x
+
     def test_Builder_calls(self):
         """Test Builder calls through different environments
         """
     def test_Builder_calls(self):
         """Test Builder calls through different environments
         """
@@ -456,54 +953,81 @@ class EnvironmentTestCase(unittest.TestCase):
         env.Replace(BUILDERS = { 'builder1' : b1,
                                  'builder2' : b2 })
         called_it = {}
         env.Replace(BUILDERS = { 'builder1' : b1,
                                  'builder2' : b2 })
         called_it = {}
-        env.builder1(target = 'out1')
-        assert called_it['target'] == 'out1', called_it
-        assert not called_it.has_key('source')
+        env.builder1('in1')
+        assert called_it['target'] is None, called_it
+        assert called_it['source'] == ['in1'], called_it
 
         called_it = {}
 
         called_it = {}
-        env.builder2(target = 'out2', xyzzy = 1)
-        assert called_it['target'] == 'out2', called_it
+        env.builder2(source = 'in2', xyzzy = 1)
+        assert called_it['target'] is None, called_it
+        assert called_it['source'] == ['in2'], called_it
         assert called_it['xyzzy'] == 1, called_it
         assert called_it['xyzzy'] == 1, called_it
-        assert not called_it.has_key('source')
 
         called_it = {}
         env.builder1(foo = 'bar')
         assert called_it['foo'] == 'bar', called_it
 
         called_it = {}
         env.builder1(foo = 'bar')
         assert called_it['foo'] == 'bar', called_it
-        assert not called_it.has_key('target')
-        assert not called_it.has_key('source')
+        assert called_it['target'] is None, called_it
+        assert called_it['source'] is None, called_it
 
 
+    def test_BuilderWrapper_attributes(self):
+        """Test getting and setting of BuilderWrapper attributes
+        """
+        b1 = Builder()
+        b2 = Builder()
+        e1 = Environment()
+        e2 = Environment()
+
+        e1.Replace(BUILDERS = {'b' : b1})
+        bw = e1.b
+
+        assert bw.env is e1
+        bw.env = e2
+        assert bw.env is e2
 
 
+        assert bw.builder is b1
+        bw.builder = b2
+        assert bw.builder is b2
 
 
-    def test_Builder_execs(self):
-       """Test Builder execution through different environments
+        self.assertRaises(AttributeError, getattr, bw, 'foobar')
+        bw.foobar = 42
+        assert bw.foobar is 42
 
 
-       One environment is initialized with a single
-       Builder object, one with a list of a single Builder
-       object, and one with a list of two Builder objects.
-       """
-       global built_it
+    # This unit test is currently disabled because we don't think the
+    # underlying method it tests (Environment.BuilderWrapper.execute())
+    # is necessary, but we're leaving the code here for now in case
+    # that's mistaken.
+    def _DO_NOT_test_Builder_execs(self):
+        """Test Builder execution through different environments
+
+        One environment is initialized with a single
+        Builder object, one with a list of a single Builder
+        object, and one with a list of two Builder objects.
+        """
+        global built_it
 
 
-       b1 = Builder()
-       b2 = Builder()
+        b1 = Builder()
+        b2 = Builder()
 
 
-       built_it = {}
+        built_it = {}
         env3 = Environment()
         env3.Replace(BUILDERS = { 'builder1' : b1,
                                   'builder2' : b2 })
         env3 = Environment()
         env3.Replace(BUILDERS = { 'builder1' : b1,
                                   'builder2' : b2 })
-       env3.builder1.execute(target = 'out1')
-       env3.builder2.execute(target = 'out2')
-       env3.builder1.execute(target = 'out3')
-       assert built_it['out1']
-       assert built_it['out2']
-       assert built_it['out3']
+        env3.builder1.execute(target = 'out1')
+        env3.builder2.execute(target = 'out2')
+        env3.builder1.execute(target = 'out3')
+        assert built_it['out1']
+        assert built_it['out2']
+        assert built_it['out3']
 
 
-        env4 = env3.Copy()
-        assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % (env4.builder1.env, env3)
-        assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % (env4.builder1.env, env3)
+        env4 = env3.Clone()
+        assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % (
+env4.builder1.env, env3)
+        assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % (
+env4.builder1.env, env3)
 
         # Now test BUILDERS as a dictionary.
         built_it = {}
 
         # Now test BUILDERS as a dictionary.
         built_it = {}
-        env5 = Environment(BUILDERS={ 'foo' : b1 })
+        env5 = self.TestEnvironment(BUILDERS={ 'foo' : b1 })
         env5['BUILDERS']['bar'] = b2
         env5.foo.execute(target='out1')
         env5.bar.execute(target='out2')
         env5['BUILDERS']['bar'] = b2
         env5.foo.execute(target='out1')
         env5.bar.execute(target='out2')
@@ -519,71 +1043,166 @@ class EnvironmentTestCase(unittest.TestCase):
         assert built_it['out1']
         assert built_it['out2']
 
         assert built_it['out1']
         assert built_it['out2']
 
+
+
     def test_Scanners(self):
     def test_Scanners(self):
-        """Test Scanner execution through different environments
+        """Test setting SCANNERS in various ways
 
         One environment is initialized with a single
         Scanner object, one with a list of a single Scanner
         object, and one with a list of two Scanner objects.
         """
 
         One environment is initialized with a single
         Scanner object, one with a list of a single Scanner
         object, and one with a list of two Scanner objects.
         """
-#        global scanned_it
-#
-#      s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
-#      s2 = Scanner(name = 'scanner2', skeys = [".m4"])
-#
-#      scanned_it = {}
-#      env1 = Environment(SCANNERS = s1)
+        global scanned_it
+
+        s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
+        s2 = Scanner(name = 'scanner2', skeys = [".m4"])
+        s3 = Scanner(name = 'scanner3', skeys = [".m4", ".m5"])
+        s4 = Scanner(name = 'scanner4', skeys = [None])
+
+#        XXX Tests for scanner execution through different environments,
+#        XXX if we ever want to do that some day
+#        scanned_it = {}
+#        env1 = self.TestEnvironment(SCANNERS = s1)
 #        env1.scanner1(filename = 'out1')
 #        env1.scanner1(filename = 'out1')
-#      assert scanned_it['out1']
+#        assert scanned_it['out1']
 #
 #
-#      scanned_it = {}
-#      env2 = Environment(SCANNERS = [s1])
+#        scanned_it = {}
+#        env2 = self.TestEnvironment(SCANNERS = [s1])
 #        env1.scanner1(filename = 'out1')
 #        env1.scanner1(filename = 'out1')
-#      assert scanned_it['out1']
+#        assert scanned_it['out1']
 #
 #
-#      scanned_it = {}
+#        scanned_it = {}
 #        env3 = Environment()
 #        env3 = Environment()
-#        env3.Replace(SCANNERS = [s1, s2])
+#        env3.Replace(SCANNERS = [s1])
 #        env3.scanner1(filename = 'out1')
 #        env3.scanner2(filename = 'out2')
 #        env3.scanner1(filename = 'out3')
 #        env3.scanner1(filename = 'out1')
 #        env3.scanner2(filename = 'out2')
 #        env3.scanner1(filename = 'out3')
-#      assert scanned_it['out1']
-#      assert scanned_it['out2']
-#      assert scanned_it['out3']
-#
-#      s = env3.get_scanner(".c")
-#      assert s == s1, s
-#      s = env3.get_scanner(skey=".m4")
-#      assert s == s2, s
-#      s = env3.get_scanner(".cxx")
-#      assert s == None, s
+#        assert scanned_it['out1']
+#        assert scanned_it['out2']
+#        assert scanned_it['out3']
+
+        suffixes = [".c", ".cc", ".cxx", ".m4", ".m5"]
+
+        env = Environment()
+        try: del env['SCANNERS']
+        except KeyError: pass
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [None, None, None, None, None], s
+
+        env = self.TestEnvironment(SCANNERS = [])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [None, None, None, None, None], s
+
+        env.Replace(SCANNERS = [s1])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [s1, s1, None, None, None], s
+
+        env.Append(SCANNERS = [s2])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [s1, s1, None, s2, None], s
+
+        env.AppendUnique(SCANNERS = [s3])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [s1, s1, None, s2, s3], s
+
+        env = env.Clone(SCANNERS = [s2])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [None, None, None, s2, None], s
+
+        env['SCANNERS'] = [s1]
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [s1, s1, None, None, None], s
+
+        env.PrependUnique(SCANNERS = [s2, s1])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [s1, s1, None, s2, None], s
+
+        env.Prepend(SCANNERS = [s3])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [s1, s1, None, s3, s3], s
+
+        # Verify behavior of case-insensitive suffix matches on Windows.
+        uc_suffixes = [_.upper() for _ in suffixes]
+
+        env = Environment(SCANNERS = [s1, s2, s3],
+                          PLATFORM = 'linux')
+
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [s1, s1, None, s2, s3], s
+
+        s = list(map(env.get_scanner, uc_suffixes))
+        assert s == [None, None, None, None, None], s
+
+        env['PLATFORM'] = 'win32'
+
+        s = list(map(env.get_scanner, uc_suffixes))
+        assert s == [s1, s1, None, s2, s3], s
+
+        # Verify behavior for a scanner returning None (on Windows
+        # where we might try to perform case manipulation on None).
+        env.Replace(SCANNERS = [s4])
+        s = list(map(env.get_scanner, suffixes))
+        assert s == [None, None, None, None, None], s
 
     def test_ENV(self):
 
     def test_ENV(self):
-       """Test setting the external ENV in Environments
-       """
-       env = Environment()
-       assert env.Dictionary().has_key('ENV')
+        """Test setting the external ENV in Environments
+        """
+        env = Environment()
+        assert 'ENV' in env.Dictionary()
 
 
-       env = Environment(ENV = { 'PATH' : '/foo:/bar' })
-       assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
+        env = self.TestEnvironment(ENV = { 'PATH' : '/foo:/bar' })
+        assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
 
     def test_ReservedVariables(self):
 
     def test_ReservedVariables(self):
-        """Test generation of warnings when reserved variable names
-        are set in an environment."""
+        """Test warning generation when reserved variable names are set"""
+
+        reserved_variables = [
+            'CHANGED_SOURCES',
+            'CHANGED_TARGETS',
+            'SOURCE',
+            'SOURCES',
+            'TARGET',
+            'TARGETS',
+            'UNCHANGED_SOURCES',
+            'UNCHANGED_TARGETS',
+        ]
 
 
-        SCons.Warnings.enableWarningClass(SCons.Warnings.ReservedVariableWarning)
+        warning = SCons.Warnings.ReservedVariableWarning
+        SCons.Warnings.enableWarningClass(warning)
         old = SCons.Warnings.warningAsException(1)
 
         try:
             env4 = Environment()
         old = SCons.Warnings.warningAsException(1)
 
         try:
             env4 = Environment()
-            for kw in ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']:
+            for kw in self.reserved_variables:
                 exc_caught = None
                 try:
                     env4[kw] = 'xyzzy'
                 exc_caught = None
                 try:
                     env4[kw] = 'xyzzy'
-                except SCons.Warnings.ReservedVariableWarning:
+                except warning:
                     exc_caught = 1
                 assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw
                     exc_caught = 1
                 assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw
-                assert not env4.has_key(kw), "`%s' variable was incorrectly set" % kw
+                assert kw not in env4, "`%s' variable was incorrectly set" % kw
+        finally:
+            SCons.Warnings.warningAsException(old)
+
+    def test_FutureReservedVariables(self):
+        """Test warning generation when future reserved variable names are set"""
+
+        future_reserved_variables = []
+
+        warning = SCons.Warnings.FutureReservedVariableWarning
+        SCons.Warnings.enableWarningClass(warning)
+        old = SCons.Warnings.warningAsException(1)
+
+        try:
+            env4 = Environment()
+            for kw in future_reserved_variables:
+                exc_caught = None
+                try:
+                    env4[kw] = 'xyzzy'
+                except warning:
+                    exc_caught = 1
+                assert exc_caught, "Did not catch FutureReservedVariableWarning for `%s'" % kw
+                assert kw in env4, "`%s' variable was not set" % kw
         finally:
             SCons.Warnings.warningAsException(old)
 
         finally:
             SCons.Warnings.warningAsException(old)
 
@@ -606,128 +1225,91 @@ class EnvironmentTestCase(unittest.TestCase):
     def test_autogenerate(dict):
         """Test autogenerating variables in a dictionary."""
 
     def test_autogenerate(dict):
         """Test autogenerating variables in a dictionary."""
 
-        def RDirs(pathlist):
-            return SCons.Node.FS.default_fs.Rsearchall(pathlist,
-                                                       clazz=SCons.Node.FS.Dir,
-                                                       must_exist=0,
-                                                       cwd=SCons.Node.FS.default_fs.Dir('xx'))
+        drive, p = os.path.splitdrive(os.getcwd())
+        def normalize_path(path, drive=drive):
+            if path[0] in '\\/':
+                path = drive + path
+            path = os.path.normpath(path)
+            drive, path = os.path.splitdrive(path)
+            return drive.lower() + path
 
 
-        env = Environment(LIBS = [ 'foo', 'bar', 'baz' ],
+        env = dict.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ],
                           LIBLINKPREFIX = 'foo',
                           LIBLINKPREFIX = 'foo',
-                          LIBLINKSUFFIX = 'bar',
-                          RDirs=RDirs)
+                          LIBLINKSUFFIX = 'bar')
+
+        def RDirs(pathlist, fs=env.fs):
+            return fs.Dir('xx').Rfindalldirs(pathlist)
+
+        env['RDirs'] = RDirs
         flags = env.subst_list('$_LIBFLAGS', 1)[0]
         flags = env.subst_list('$_LIBFLAGS', 1)[0]
-        assert len(flags) == 3, flags
-        assert flags[0] == 'foobar', \
-               flags[0]
-        assert flags[1] == 'foobar', \
-               flags[1]
-        assert flags[2] == 'foobazbar', \
-               flags[2]
-
-        blat = SCons.Node.FS.default_fs.Dir('blat')
-
-        env = Environment(CPPPATH = [ 'foo', '$FOO/bar', blat ],
-                          INCPREFIX = 'foo ',
-                          INCSUFFIX = 'bar',
-                          FOO = 'baz',
-                          RDirs=RDirs)
+        assert flags == ['foobar', 'foobar', 'foobazbar'], flags
+
+        blat = env.fs.Dir('blat')
+
+        env.Replace(CPPPATH = [ 'foo', '$FOO/bar', blat ],
+                    INCPREFIX = 'foo ',
+                    INCSUFFIX = 'bar',
+                    FOO = 'baz')
         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
-        assert len(flags) == 8, flags
-        assert flags[0] == '$(', \
-               flags[0]
-        assert flags[1] == os.path.normpath('foo'), \
-               flags[1]
-        assert flags[2] == os.path.normpath('xx/foobar'), \
-               flags[2]
-        assert flags[3] == os.path.normpath('foo'), \
-               flags[3]
-        assert flags[4] == os.path.normpath('xx/baz/bar'), \
-               flags[4]
-        assert flags[5] == os.path.normpath('foo'), \
-               flags[5]
-        assert flags[6] == os.path.normpath('blatbar'), \
-               flags[6]
-        assert flags[7] == '$)', \
-               flags[7]
-
-        env = Environment(F77PATH = [ 'foo', '$FOO/bar', blat ],
-                          INCPREFIX = 'foo ',
-                          INCSUFFIX = 'bar',
-                          FOO = 'baz',
-                          RDirs=RDirs)
+        expect = [ '$(',
+                   normalize_path('foo'),
+                   normalize_path('xx/foobar'),
+                   normalize_path('foo'),
+                   normalize_path('xx/baz/bar'),
+                   normalize_path('foo'),
+                   normalize_path('blatbar'),
+                   '$)',
+        ]
+        assert flags == expect, flags
+
+        env.Replace(F77PATH = [ 'foo', '$FOO/bar', blat ],
+                    INCPREFIX = 'foo ',
+                    INCSUFFIX = 'bar',
+                    FOO = 'baz')
         flags = env.subst_list('$_F77INCFLAGS', 1)[0]
         flags = env.subst_list('$_F77INCFLAGS', 1)[0]
-        assert len(flags) == 8, flags
-        assert flags[0] == '$(', \
-               flags[0]
-        assert flags[1] == os.path.normpath('foo'), \
-               flags[1]
-        assert flags[2] == os.path.normpath('xx/foobar'), \
-               flags[2]
-        assert flags[3] == os.path.normpath('foo'), \
-               flags[3]
-        assert flags[4] == os.path.normpath('xx/baz/bar'), \
-               flags[4]
-        assert flags[5] == os.path.normpath('foo'), \
-               flags[5]
-        assert flags[6] == os.path.normpath('blatbar'), \
-               flags[6]
-        assert flags[7] == '$)', \
-               flags[7]
-
-        env = Environment(CPPPATH = '', F77PATH = '', LIBPATH = '',
-                          RDirs=RDirs)
+        expect = [ '$(',
+                   normalize_path('foo'),
+                   normalize_path('xx/foobar'),
+                   normalize_path('foo'),
+                   normalize_path('xx/baz/bar'),
+                   normalize_path('foo'),
+                   normalize_path('blatbar'),
+                   '$)',
+        ]
+        assert flags == expect, flags
+
+        env.Replace(CPPPATH = '', F77PATH = '', LIBPATH = '')
         l = env.subst_list('$_CPPINCFLAGS')
         l = env.subst_list('$_CPPINCFLAGS')
-        assert len(l[0]) == 0, l[0]
+        assert l == [[]], l
         l = env.subst_list('$_F77INCFLAGS')
         l = env.subst_list('$_F77INCFLAGS')
-        assert len(l[0]) == 0, l[0]
+        assert l == [[]], l
         l = env.subst_list('$_LIBDIRFLAGS')
         l = env.subst_list('$_LIBDIRFLAGS')
-        assert len(l[0]) == 0, l[0]
-
-        SCons.Node.FS.default_fs.Repository('/rep1')
-        SCons.Node.FS.default_fs.Repository('/rep2')
-        env = Environment(CPPPATH = [ 'foo', '/a/b', '$FOO/bar', blat],
-                          INCPREFIX = '-I ',
-                          INCSUFFIX = 'XXX',
-                          FOO = 'baz',
-                          RDirs=RDirs)
+        assert l == [[]], l
+
+        env.fs.Repository('/rep1')
+        env.fs.Repository('/rep2')
+        env.Replace(CPPPATH = [ 'foo', '/a/b', '$FOO/bar', blat],
+                    INCPREFIX = '-I ',
+                    INCSUFFIX = 'XXX',
+                    FOO = 'baz')
         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
-        assert flags[0] == '$(', \
-               flags[0]
-        assert flags[1] == '-I', \
-               flags[1]
-        assert flags[2] == os.path.normpath('xx/fooXXX'), \
-               flags[2]
-        assert flags[3] == '-I', \
-               flags[3]
-        assert flags[4] == os.path.normpath('/rep1/xx/fooXXX'), \
-               flags[4]
-        assert flags[5] == '-I', \
-               flags[5]
-        assert flags[6] == os.path.normpath('/rep2/xx/fooXXX'), \
-               flags[6]
-        assert flags[7] == '-I', \
-               flags[7]
-        assert flags[8] == os.path.normpath('/a/bXXX'), \
-               flags[8]
-        assert flags[9] == '-I', \
-               flags[9]
-        assert flags[10] == os.path.normpath('xx/baz/barXXX'), \
-               flags[10]
-        assert flags[11] == '-I', \
-               flags[11]
-        assert flags[12] == os.path.normpath('/rep1/xx/baz/barXXX'), \
-               flags[12]
-        assert flags[13] == '-I', \
-               flags[13]
-        assert flags[14] == os.path.normpath('/rep2/xx/baz/barXXX'), \
-               flags[14]
-        assert flags[15] == '-I', \
-               flags[15]
-        assert flags[16] == os.path.normpath('blatXXX'), \
-               flags[16]
-        assert flags[17] == '$)', \
-               flags[17]
+        expect = [ '$(',
+                   '-I', normalize_path('xx/fooXXX'),
+                   '-I', normalize_path('/rep1/xx/fooXXX'),
+                   '-I', normalize_path('/rep2/xx/fooXXX'),
+                   '-I', normalize_path('/a/bXXX'),
+                   '-I', normalize_path('xx/baz/barXXX'),
+                   '-I', normalize_path('/rep1/xx/baz/barXXX'),
+                   '-I', normalize_path('/rep2/xx/baz/barXXX'),
+                   '-I', normalize_path('blatXXX'),
+                   '$)'
+        ]
+        def normalize_if_path(arg, np=normalize_path):
+            if arg not in ('$(','$)','-I'):
+                return np(str(arg))
+            return arg
+        flags = list(map(normalize_if_path, flags))
+        assert flags == expect, flags
 
     def test_platform(self):
         """Test specifying a platform callable when instantiating."""
 
     def test_platform(self):
         """Test specifying a platform callable when instantiating."""
@@ -739,7 +1321,7 @@ class EnvironmentTestCase(unittest.TestCase):
             env['SET_TOOL'] = 'initialized'
             assert env['PLATFORM'] == "TestPlatform"
 
             env['SET_TOOL'] = 'initialized'
             assert env['PLATFORM'] == "TestPlatform"
 
-        env = Environment(platform = platform(), tools = [tool])
+        env = self.TestEnvironment(platform = platform(), tools = [tool])
         assert env['XYZZY'] == 777, env
         assert env['PLATFORM'] == "TestPlatform"
         assert env['SET_TOOL'] == "initialized"
         assert env['XYZZY'] == 777, env
         assert env['PLATFORM'] == "TestPlatform"
         assert env['SET_TOOL'] == "initialized"
@@ -761,7 +1343,7 @@ class EnvironmentTestCase(unittest.TestCase):
             SCons.Defaults.ConstructionEnvironment.update({
                 'PLATFORM' : platform(),
             })
             SCons.Defaults.ConstructionEnvironment.update({
                 'PLATFORM' : platform(),
             })
-            env = Environment(tools = [tool])
+            env = self.TestEnvironment(tools = [tool])
             assert env['XYZZY'] == 888, env
             assert env['PLATFORM'] == "DefaultTestPlatform"
             assert env['SET_TOOL'] == "abcde"
             assert env['XYZZY'] == 888, env
             assert env['PLATFORM'] == "DefaultTestPlatform"
             assert env['SET_TOOL'] == "abcde"
@@ -778,13 +1360,29 @@ class EnvironmentTestCase(unittest.TestCase):
             env['AAA'] = env['XYZ']
         def t4(env):
             env['TOOL4'] = 444
             env['AAA'] = env['XYZ']
         def t4(env):
             env['TOOL4'] = 444
-        env = Environment(tools = [t1, t2, t3], XYZ = 'aaa')
+        env = self.TestEnvironment(tools = [t1, t2, t3], XYZ = 'aaa')
         assert env['TOOL1'] == 111, env['TOOL1']
         assert env['TOOL2'] == 222, env
         assert env['AAA'] == 'aaa', env
         t4(env)
         assert env['TOOL4'] == 444, env
 
         assert env['TOOL1'] == 111, env['TOOL1']
         assert env['TOOL2'] == 222, env
         assert env['AAA'] == 'aaa', env
         t4(env)
         assert env['TOOL4'] == 444, env
 
+        test = TestCmd.TestCmd(workdir = '')
+        test.write('faketool.py', """\
+def generate(env, **kw):
+    for k, v in kw.items():
+        env[k] = v
+
+def exists(env):
+    return 1
+""")
+
+        env = self.TestEnvironment(tools = [('faketool', {'a':1, 'b':2, 'c':3})],
+                          toolpath = [test.workpath('')])
+        assert env['a'] == 1, env['a']
+        assert env['b'] == 2, env['b']
+        assert env['c'] == 3, env['c']
+
     def test_Default_TOOLS(self):
         """Test overriding the default TOOLS variable"""
         def t5(env):
     def test_Default_TOOLS(self):
         """Test overriding the default TOOLS variable"""
         def t5(env):
@@ -811,9 +1409,26 @@ class EnvironmentTestCase(unittest.TestCase):
         finally:
             SCons.Defaults.ConstructionEnvironment = save
 
         finally:
             SCons.Defaults.ConstructionEnvironment = save
 
+    def test_null_tools(self):
+        """Test specifying a tool of None is OK."""
+        def t1(env):
+            env['TOOL1'] = 111
+        def t2(env):
+            env['TOOL2'] = 222
+        env = self.TestEnvironment(tools = [t1, None, t2], XYZ = 'aaa')
+        assert env['TOOL1'] == 111, env['TOOL1']
+        assert env['TOOL2'] == 222, env
+        assert env['XYZ'] == 'aaa', env
+        env = self.TestEnvironment(tools = [None], XYZ = 'xyz')
+        assert env['XYZ'] == 'xyz', env
+        env = self.TestEnvironment(tools = [t1, '', t2], XYZ = 'ddd')
+        assert env['TOOL1'] == 111, env['TOOL1']
+        assert env['TOOL2'] == 222, env
+        assert env['XYZ'] == 'ddd', env
+
     def test_concat(self):
         "Test _concat()"
     def test_concat(self):
         "Test _concat()"
-        e1 = Environment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
+        e1 = self.TestEnvironment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
         s = e1.subst
         x = s("${_concat('', '', '', __env__)}")
         assert x == '', x
         s = e1.subst
         x = s("${_concat('', '', '', __env__)}")
         assert x == '', x
@@ -826,9 +1441,17 @@ class EnvironmentTestCase(unittest.TestCase):
         x = s("${_concat(PRE, LIST, SUF, __env__)}")
         assert x == 'preasuf prebsuf', x
 
         x = s("${_concat(PRE, LIST, SUF, __env__)}")
         assert x == 'preasuf prebsuf', x
 
+    def test_gvars(self):
+        """Test the Environment gvars() method"""
+        env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
+        gvars = env.gvars()
+        assert gvars['XXX'] == 'x', gvars['XXX']
+        assert gvars['YYY'] == 'y', gvars['YYY']
+        assert gvars['ZZZ'] == 'z', gvars['ZZZ']
+
     def test__update(self):
         """Test the _update() method"""
     def test__update(self):
         """Test the _update() method"""
-        env = Environment(X = 'x', Y = 'y', Z = 'z')
+        env = self.TestEnvironment(X = 'x', Y = 'y', Z = 'z')
         assert env['X'] == 'x', env['X']
         assert env['Y'] == 'y', env['Y']
         assert env['Z'] == 'z', env['Z']
         assert env['X'] == 'x', env['X']
         assert env['Y'] == 'y', env['Y']
         assert env['Z'] == 'z', env['Z']
@@ -856,6 +1479,8 @@ class EnvironmentTestCase(unittest.TestCase):
         b2 = Environment()['BUILDERS']
         assert b1 == b2, diff_dict(b1, b2)
 
         b2 = Environment()['BUILDERS']
         assert b1 == b2, diff_dict(b1, b2)
 
+        import UserDict
+        UD = UserDict.UserDict
         import UserList
         UL = UserList.UserList
 
         import UserList
         UL = UserList.UserList
 
@@ -887,6 +1512,18 @@ class EnvironmentTestCase(unittest.TestCase):
             UL(['i7']), [''],           UL(['i7', '']),
             UL(['i8']), UL(['']),       UL(['i8', '']),
 
             UL(['i7']), [''],           UL(['i7', '']),
             UL(['i8']), UL(['']),       UL(['i8', '']),
 
+            {'d1':1},   'D1',           {'d1':1, 'D1':None},
+            {'d2':1},   ['D2'],         {'d2':1, 'D2':None},
+            {'d3':1},   UL(['D3']),     {'d3':1, 'D3':None},
+            {'d4':1},   {'D4':1},       {'d4':1, 'D4':1},
+            {'d5':1},   UD({'D5':1}),   UD({'d5':1, 'D5':1}),
+
+            UD({'u1':1}), 'U1',         UD({'u1':1, 'U1':None}),
+            UD({'u2':1}), ['U2'],       UD({'u2':1, 'U2':None}),
+            UD({'u3':1}), UL(['U3']),   UD({'u3':1, 'U3':None}),
+            UD({'u4':1}), {'U4':1},     UD({'u4':1, 'U4':1}),
+            UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
+
             '',         'M1',           'M1',
             '',         ['M2'],         ['M2'],
             '',         UL(['M3']),     UL(['M3']),
             '',         'M1',           'M1',
             '',         ['M2'],         ['M2'],
             '',         UL(['M3']),     UL(['M3']),
@@ -937,14 +1574,21 @@ class EnvironmentTestCase(unittest.TestCase):
         failed = 0
         while cases:
             input, append, expect = cases[:3]
         failed = 0
         while cases:
             input, append, expect = cases[:3]
-            env['XXX'] = input
-            env.Append(XXX = append)
-            result = env['XXX']
-            if result != expect:
+            env['XXX'] = copy.copy(input)
+            try:
+                env.Append(XXX = append)
+            except Exception, e:
                 if failed == 0: print
                 if failed == 0: print
-                print "    %s Append %s => %s did not match %s" % \
-                      (repr(input), repr(append), repr(result), repr(expect))
+                print "    %s Append %s exception: %s" % \
+                      (repr(input), repr(append), e)
                 failed = failed + 1
                 failed = failed + 1
+            else:
+                result = env['XXX']
+                if result != expect:
+                    if failed == 0: print
+                    print "    %s Append %s => %s did not match %s" % \
+                          (repr(input), repr(append), repr(result), repr(expect))
+                    failed = failed + 1
             del cases[:3]
         assert failed == 0, "%d Append() cases failed" % failed
 
             del cases[:3]
         assert failed == 0, "%d Append() cases failed" % failed
 
@@ -970,121 +1614,190 @@ class EnvironmentTestCase(unittest.TestCase):
 
         ccc = C('ccc')
 
 
         ccc = C('ccc')
 
-        env2 = Environment(CCC1 = ['c1'], CCC2 = ccc)
+        env2 = self.TestEnvironment(CCC1 = ['c1'], CCC2 = ccc)
         env2.Append(CCC1 = ccc, CCC2 = ['c2'])
         assert env2['CCC1'][0] == 'c1', env2['CCC1']
         assert env2['CCC1'][1] is ccc, env2['CCC1']
         assert env2['CCC2'][0] is ccc, env2['CCC2']
         assert env2['CCC2'][1] == 'c2', env2['CCC2']
 
         env2.Append(CCC1 = ccc, CCC2 = ['c2'])
         assert env2['CCC1'][0] == 'c1', env2['CCC1']
         assert env2['CCC1'][1] is ccc, env2['CCC1']
         assert env2['CCC2'][0] is ccc, env2['CCC2']
         assert env2['CCC2'][1] == 'c2', env2['CCC2']
 
-        env3 = Environment(X = {'x1' : 7})
+        env3 = self.TestEnvironment(X = {'x1' : 7})
         env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
         assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X']
         assert env3['Y'] == {'y1': 10}, env3['Y']
 
         env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
         assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X']
         assert env3['Y'] == {'y1': 10}, env3['Y']
 
-        env4 = Environment(BUILDERS = {'z1' : 11})
-        env4.Append(BUILDERS = {'z2' : 12})
-        assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
+        z1 = Builder()
+        z2 = Builder()
+        env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
+        env4.Append(BUILDERS = {'z2' : z2})
+        assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
         assert hasattr(env4, 'z1')
         assert hasattr(env4, 'z2')
 
     def test_AppendENVPath(self):
         """Test appending to an ENV path."""
         assert hasattr(env4, 'z1')
         assert hasattr(env4, 'z2')
 
     def test_AppendENVPath(self):
         """Test appending to an ENV path."""
-        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+        env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
         # have to include the pathsep here so that the test will work on UNIX too.
         env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
         env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
         env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
         env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
         # have to include the pathsep here so that the test will work on UNIX too.
         env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
         env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
         env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
         env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
+        # this should do nothing since delete_existing is 0
+        env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
         assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
 
         assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
 
+        test = TestCmd.TestCmd(workdir = '')
+        test.subdir('sub1', 'sub2')
+        p=env1['ENV']['PATH']
+        env1.AppendENVPath('PATH','#sub1', sep = ';')
+        env1.AppendENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
+        assert env1['ENV']['PATH'] == p + ';sub1;sub2', env1['ENV']['PATH']
+
     def test_AppendUnique(self):
         """Test appending to unique values to construction variables
 
         This strips values that are already present when lists are
         involved."""
     def test_AppendUnique(self):
         """Test appending to unique values to construction variables
 
         This strips values that are already present when lists are
         involved."""
-        env = Environment(AAA1 = 'a1',
+        env = self.TestEnvironment(AAA1 = 'a1',
                           AAA2 = 'a2',
                           AAA3 = 'a3',
                           AAA2 = 'a2',
                           AAA3 = 'a3',
+                          AAA4 = 'a4',
+                          AAA5 = 'a5',
                           BBB1 = ['b1'],
                           BBB2 = ['b2'],
                           BBB1 = ['b1'],
                           BBB2 = ['b2'],
-                          BBB3 = ['b3'])
+                          BBB3 = ['b3'],
+                          BBB4 = ['b4'],
+                          BBB5 = ['b5'],
+                          CCC1 = '',
+                          CCC2 = '',
+                          DDD1 = ['a', 'b', 'c'])
         env.AppendUnique(AAA1 = 'a1',
                          AAA2 = ['a2'],
         env.AppendUnique(AAA1 = 'a1',
                          AAA2 = ['a2'],
-                         AAA3 = ['a3', 'b', 'c', 'a3'],
+                         AAA3 = ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups
+                         AAA4 = 'a4.new',
+                         AAA5 = ['a5.new'],
                          BBB1 = 'b1',
                          BBB2 = ['b2'],
                          BBB1 = 'b1',
                          BBB2 = ['b2'],
-                         BBB3 = ['b3', 'c', 'd', 'b3'])
+                         BBB3 = ['b3', 'c', 'd', 'c', 'b3'],
+                         BBB4 = 'b4.new',
+                         BBB5 = ['b5.new'],
+                         CCC1 = 'c1',
+                         CCC2 = ['c2'],
+                         DDD1 = 'b')
+
         assert env['AAA1'] == 'a1a1', env['AAA1']
         assert env['AAA2'] == ['a2'], env['AAA2']
         assert env['AAA3'] == ['a3', 'b', 'c'], env['AAA3']
         assert env['AAA1'] == 'a1a1', env['AAA1']
         assert env['AAA2'] == ['a2'], env['AAA2']
         assert env['AAA3'] == ['a3', 'b', 'c'], env['AAA3']
+        assert env['AAA4'] == 'a4a4.new', env['AAA4']
+        assert env['AAA5'] == ['a5', 'a5.new'], env['AAA5']
         assert env['BBB1'] == ['b1'], env['BBB1']
         assert env['BBB2'] == ['b2'], env['BBB2']
         assert env['BBB3'] == ['b3', 'c', 'd'], env['BBB3']
         assert env['BBB1'] == ['b1'], env['BBB1']
         assert env['BBB2'] == ['b2'], env['BBB2']
         assert env['BBB3'] == ['b3', 'c', 'd'], env['BBB3']
+        assert env['BBB4'] == ['b4', 'b4.new'], env['BBB4']
+        assert env['BBB5'] == ['b5', 'b5.new'], env['BBB5']
+        assert env['CCC1'] == 'c1', env['CCC1']
+        assert env['CCC2'] == ['c2'], env['CCC2']
+        assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
+
+        env.AppendUnique(DDD1 = 'b', delete_existing=1)
+        assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end
+        env.AppendUnique(DDD1 = ['a','b'], delete_existing=1)
+        assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
+        env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
+        assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
+        
+        env['CLVar'] = CLVar([])
+        env.AppendUnique(CLVar = 'bar')
+        result = env['CLVar']
+        if sys.version[0] == '1' or sys.version[:3] == '2.0':
+            # Python 2.0 and before have a quirky behavior where CLVar([])
+            # actually matches '' and [] due to different __coerce__()
+            # semantics in the UserList implementation.  It isn't worth a
+            # lot of effort to get this corner case to work identically
+            # (support for Python 1.5 support will die soon anyway),
+            # so just treat it separately for now.
+            assert result == 'bar', result
+        else:
+            assert isinstance(result, CLVar), repr(result)
+            assert result == ['bar'], result
 
 
-    def test_Copy(self):
-       """Test construction Environment copying
-
-       Update the copy independently afterwards and check that
-       the original remains intact (that is, no dangling
-       references point to objects in the copied environment).
-       Copy the original with some construction variable
-       updates and check that the original remains intact
-       and the copy has the updated values.
-       """
-       env1 = Environment(XXX = 'x', YYY = 'y')
-       env2 = env1.Copy()
-       env1copy = env1.Copy()
-       env2.Replace(YYY = 'yyy')
-       assert env1 != env2
-       assert env1 == env1copy
-
-       env3 = env1.Copy(XXX = 'x3', ZZZ = 'z3')
-       assert env3.Dictionary('XXX') == 'x3'
-       assert env3.Dictionary('YYY') == 'y'
-       assert env3.Dictionary('ZZZ') == 'z3'
-       assert env1 == env1copy
-
-        assert env1['__env__'] is env1, env1['__env__']
-        assert env2['__env__'] is env2, env2['__env__']
-        assert env3['__env__'] is env3, env3['__env__']
+        env['CLVar'] = CLVar(['abc'])
+        env.AppendUnique(CLVar = 'bar')
+        result = env['CLVar']
+        assert isinstance(result, CLVar), repr(result)
+        assert result == ['abc', 'bar'], result
+
+        env['CLVar'] = CLVar(['bar'])
+        env.AppendUnique(CLVar = 'bar')
+        result = env['CLVar']
+        assert isinstance(result, CLVar), repr(result)
+        assert result == ['bar'], result
+
+    def test_Clone(self):
+        """Test construction environment copying
+
+        Update the copy independently afterwards and check that
+        the original remains intact (that is, no dangling
+        references point to objects in the copied environment).
+        Clone the original with some construction variable
+        updates and check that the original remains intact
+        and the copy has the updated values.
+        """
+        env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+        env2 = env1.Clone()
+        env1copy = env1.Clone()
+        assert env1copy == env1copy
+        assert env2 == env2
+        env2.Replace(YYY = 'yyy')
+        assert env2 == env2
+        assert env1 != env2
+        assert env1 == env1copy
+
+        env3 = env1.Clone(XXX = 'x3', ZZZ = 'z3')
+        assert env3 == env3
+        assert env3.Dictionary('XXX') == 'x3'
+        assert env3.Dictionary('YYY') == 'y'
+        assert env3.Dictionary('ZZZ') == 'z3'
+        assert env1 == env1copy
 
         # Ensure that lists and dictionaries are
         # deep copied, but not instances.
         class TestA:
             pass
 
         # Ensure that lists and dictionaries are
         # deep copied, but not instances.
         class TestA:
             pass
-        env1 = Environment(XXX=TestA(), YYY = [ 1, 2, 3 ],
+        env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ],
                            ZZZ = { 1:2, 3:4 })
                            ZZZ = { 1:2, 3:4 })
-        env2=env1.Copy()
+        env2=env1.Clone()
         env2.Dictionary('YYY').append(4)
         env2.Dictionary('ZZZ')[5] = 6
         assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
         assert 4 in env2.Dictionary('YYY')
         assert not 4 in env1.Dictionary('YYY')
         env2.Dictionary('YYY').append(4)
         env2.Dictionary('ZZZ')[5] = 6
         assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
         assert 4 in env2.Dictionary('YYY')
         assert not 4 in env1.Dictionary('YYY')
-        assert env2.Dictionary('ZZZ').has_key(5)
-        assert not env1.Dictionary('ZZZ').has_key(5)
+        assert 5 in env2.Dictionary('ZZZ')
+        assert 5 not in env1.Dictionary('ZZZ')
 
         #
 
         #
-        env1 = Environment(BUILDERS = {'b1' : 1})
+        env1 = self.TestEnvironment(BUILDERS = {'b1' : Builder()})
         assert hasattr(env1, 'b1'), "env1.b1 was not set"
         assert hasattr(env1, 'b1'), "env1.b1 was not set"
-        assert env1.b1.env == env1, "b1.env doesn't point to env1"
-        env2 = env1.Copy(BUILDERS = {'b2' : 2})
+        assert env1.b1.object == env1, "b1.object doesn't point to env1"
+        env2 = env1.Clone(BUILDERS = {'b2' : Builder()})
+        assert env2 is env2
+        assert env2 == env2
         assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
         assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
-        assert env1.b1.env == env1, "b1.env was changed"
+        assert env1.b1.object == env1, "b1.object was changed"
         assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
         assert hasattr(env2, 'b2'), "env2.b2 was not set"
         assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
         assert hasattr(env2, 'b2'), "env2.b2 was not set"
-        assert env2.b2.env == env2, "b2.env doesn't point to env2"
+        assert env2.b2.object == env2, "b2.object doesn't point to env2"
 
         # Ensure that specifying new tools in a copied environment
         # works.
         def foo(env): env['FOO'] = 1
         def bar(env): env['BAR'] = 2
         def baz(env): env['BAZ'] = 3
 
         # Ensure that specifying new tools in a copied environment
         # works.
         def foo(env): env['FOO'] = 1
         def bar(env): env['BAR'] = 2
         def baz(env): env['BAZ'] = 3
-        env1 = Environment(tools=[foo])
-        env2 = env1.Copy()
-        env3 = env1.Copy(tools=[bar, baz])
+        env1 = self.TestEnvironment(tools=[foo])
+        env2 = env1.Clone()
+        env3 = env1.Clone(tools=[bar, baz])
 
         assert env1.get('FOO') is 1
         assert env1.get('BAR') is None
 
         assert env1.get('FOO') is 1
         assert env1.get('BAR') is None
@@ -1098,8 +1811,8 @@ class EnvironmentTestCase(unittest.TestCase):
 
         # Ensure that recursive variable substitution when copying
         # environments works properly.
 
         # Ensure that recursive variable substitution when copying
         # environments works properly.
-        env1 = Environment(CCFLAGS = '-DFOO', XYZ = '-DXYZ')
-        env2 = env1.Copy(CCFLAGS = '$CCFLAGS -DBAR',
+        env1 = self.TestEnvironment(CCFLAGS = '-DFOO', XYZ = '-DXYZ')
+        env2 = env1.Clone(CCFLAGS = '$CCFLAGS -DBAR',
                          XYZ = ['-DABC', 'x $XYZ y', '-DDEF'])
         x = env2.get('CCFLAGS')
         assert x == '-DFOO -DBAR', x
                          XYZ = ['-DABC', 'x $XYZ y', '-DDEF'])
         x = env2.get('CCFLAGS')
         assert x == '-DFOO -DBAR', x
@@ -1108,14 +1821,68 @@ class EnvironmentTestCase(unittest.TestCase):
 
         # Ensure that special properties of a class don't get
         # lost on copying.
 
         # Ensure that special properties of a class don't get
         # lost on copying.
-        env1 = Environment(FLAGS = CLVar('flag1 flag2'))
+        env1 = self.TestEnvironment(FLAGS = CLVar('flag1 flag2'))
         x = env1.get('FLAGS')
         assert x == ['flag1', 'flag2'], x
         x = env1.get('FLAGS')
         assert x == ['flag1', 'flag2'], x
-        env2 = env1.Copy()
+        env2 = env1.Clone()
         env2.Append(FLAGS = 'flag3 flag4')
         x = env2.get('FLAGS')
         assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x
 
         env2.Append(FLAGS = 'flag3 flag4')
         x = env2.get('FLAGS')
         assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x
 
+        # Test that the environment stores the toolpath and
+        # re-uses it for copies.
+        test = TestCmd.TestCmd(workdir = '')
+
+        test.write('xxx.py', """\
+def exists(env):
+    1
+def generate(env):
+    env['XXX'] = 'one'
+""")
+
+        test.write('yyy.py', """\
+def exists(env):
+    1
+def generate(env):
+    env['YYY'] = 'two'
+""")
+
+        env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
+        assert env['XXX'] == 'one', env['XXX']
+        env = env.Clone(tools=['yyy'])
+        assert env['YYY'] == 'two', env['YYY']
+
+
+        # Test that
+        real_value = [4]
+
+        def my_tool(env, rv=real_value):
+            assert env['KEY_THAT_I_WANT'] == rv[0]
+            env['KEY_THAT_I_WANT'] = rv[0] + 1
+
+        env = self.TestEnvironment()
+
+        real_value[0] = 5
+        env = env.Clone(KEY_THAT_I_WANT=5, tools=[my_tool])
+        assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
+
+        real_value[0] = 6
+        env = env.Clone(KEY_THAT_I_WANT=6, tools=[my_tool])
+        assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
+
+
+    def test_Copy(self):
+        """Test copying using the old env.Copy() method"""
+        env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
+        env2 = env1.Copy()
+        env1copy = env1.Copy()
+        assert env1copy == env1copy
+        assert env2 == env2
+        env2.Replace(YYY = 'yyy')
+        assert env2 == env2
+        assert env1 != env2
+        assert env1 == env1copy
+
     def test_Detect(self):
         """Test Detect()ing tools"""
         test = TestCmd.TestCmd(workdir = '')
     def test_Detect(self):
         """Test Detect()ing tools"""
         test = TestCmd.TestCmd(workdir = '')
@@ -1127,14 +1894,14 @@ class EnvironmentTestCase(unittest.TestCase):
             test.write(['sub1', 'xxx'], "sub1/xxx\n")
             test.write(['sub2', 'xxx'], "sub2/xxx\n")
 
             test.write(['sub1', 'xxx'], "sub1/xxx\n")
             test.write(['sub2', 'xxx'], "sub2/xxx\n")
 
-            env = Environment(ENV = { 'PATH' : [sub1, sub2] })
+            env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
 
             x = env.Detect('xxx.exe')
             assert x is None, x
 
             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
 
 
             x = env.Detect('xxx.exe')
             assert x is None, x
 
             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
 
-            env = Environment(ENV = { 'PATH' : [sub1, sub2] })
+            env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
 
             x = env.Detect('xxx.exe')
             assert x == 'xxx.exe', x
 
             x = env.Detect('xxx.exe')
             assert x == 'xxx.exe', x
@@ -1148,7 +1915,7 @@ class EnvironmentTestCase(unittest.TestCase):
             test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
 
             test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
 
-            env = Environment(ENV = { 'PATH' : [sub1, sub2] })
+            env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
 
             x = env.Detect('xxx.exe')
             assert x is None, x
 
             x = env.Detect('xxx.exe')
             assert x is None, x
@@ -1156,7 +1923,7 @@ class EnvironmentTestCase(unittest.TestCase):
             sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
             os.chmod(sub2_xxx_exe, 0755)
 
             sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
             os.chmod(sub2_xxx_exe, 0755)
 
-            env = Environment(ENV = { 'PATH' : [sub1, sub2] })
+            env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
 
             x = env.Detect('xxx.exe')
             assert x == 'xxx.exe', x
 
             x = env.Detect('xxx.exe')
             assert x == 'xxx.exe', x
@@ -1167,37 +1934,37 @@ class EnvironmentTestCase(unittest.TestCase):
             x = env.Detect('xxx.exe')
             assert x == 'xxx.exe', x
 
             x = env.Detect('xxx.exe')
             assert x == 'xxx.exe', x
 
-        env = Environment(ENV = { 'PATH' : [] })
+        env = self.TestEnvironment(ENV = { 'PATH' : [] })
         x = env.Detect('xxx.exe')
         assert x is None, x
 
     def test_Dictionary(self):
         x = env.Detect('xxx.exe')
         assert x is None, x
 
     def test_Dictionary(self):
-       """Test retrieval of known construction variables
-
-       Fetch them from the Dictionary and check for well-known
-       defaults that get inserted.
-       """
-       env = Environment(XXX = 'x', YYY = 'y', ZZZ = 'z')
-       assert env.Dictionary('XXX') == 'x'
-       assert env.Dictionary('YYY') == 'y'
-       assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
-       xxx, zzz = env.Dictionary('XXX', 'ZZZ')
-       assert xxx == 'x'
-       assert zzz == 'z'
-       assert env.Dictionary().has_key('BUILDERS')
-       assert env.Dictionary().has_key('CC')
-       assert env.Dictionary().has_key('CCFLAGS')
-       assert env.Dictionary().has_key('ENV')
-
-       assert env['XXX'] == 'x'
-       env['XXX'] = 'foo'
-       assert env.Dictionary('XXX') == 'foo'
-       del env['XXX']
-       assert not env.Dictionary().has_key('XXX')
+        """Test retrieval of known construction variables
+
+        Fetch them from the Dictionary and check for well-known
+        defaults that get inserted.
+        """
+        env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
+        assert env.Dictionary('XXX') == 'x'
+        assert env.Dictionary('YYY') == 'y'
+        assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
+        xxx, zzz = env.Dictionary('XXX', 'ZZZ')
+        assert xxx == 'x'
+        assert zzz == 'z'
+        assert 'BUILDERS' in env.Dictionary()
+        assert 'CC' in env.Dictionary()
+        assert 'CCFLAGS' in env.Dictionary()
+        assert 'ENV' in env.Dictionary()
+
+        assert env['XXX'] == 'x'
+        env['XXX'] = 'foo'
+        assert env.Dictionary('XXX') == 'foo'
+        del env['XXX']
+        assert 'XXX' not in env.Dictionary()
 
     def test_FindIxes(self):
         "Test FindIxes()"
 
     def test_FindIxes(self):
         "Test FindIxes()"
-        env = Environment(LIBPREFIX='lib',
+        env = self.TestEnvironment(LIBPREFIX='lib',
                           LIBSUFFIX='.a',
                           SHLIBPREFIX='lib',
                           SHLIBSUFFIX='.so',
                           LIBSUFFIX='.a',
                           SHLIBPREFIX='lib',
                           SHLIBSUFFIX='.so',
@@ -1209,73 +1976,157 @@ class EnvironmentTestCase(unittest.TestCase):
 
         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
         assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
 
         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
         assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
-        assert None == env.FindIxes(paths, 'PREFIX', 'POST')
+        assert None is env.FindIxes(paths, 'PREFIX', 'POST')
 
         paths = ['libfoo.a', 'prefoopost']
 
         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
 
         paths = ['libfoo.a', 'prefoopost']
 
         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
-        assert None == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+        assert None is env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
         assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
 
         assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
 
-    def test_Override(self):
-        "Test overriding construction variables"
-        env = Environment(ONE=1, TWO=2, THREE=3, FOUR=4)
-        assert env['ONE'] == 1, env['ONE']
-        assert env['TWO'] == 2, env['TWO']
-        assert env['THREE'] == 3, env['THREE']
-        assert env['FOUR'] == 4, env['FOUR']
-
-        env2 = env.Override({'TWO'   : '10',
-                             'THREE' :'x $THREE y',
-                             'FOUR'  : ['x', '$FOUR', 'y']})
-        assert env2['ONE'] == 1, env2['ONE']
-        assert env2['TWO'] == '10', env2['TWO']
-        assert env2['THREE'] == 'x 3 y', env2['THREE']
-        assert env2['FOUR'] == ['x', 4, 'y'], env2['FOUR']
-
-        assert env['ONE'] == 1, env['ONE']
-        assert env['TWO'] == 2, env['TWO']
-        assert env['THREE'] == 3, env['THREE']
-        assert env['FOUR'] == 4, env['FOUR']
-
-        env2.Replace(ONE = "won")
-        assert env2['ONE'] == "won", env2['ONE']
-        assert env['ONE'] == 1, env['ONE']
-
-        assert env['__env__'] is env, env['__env__']
-        assert env2['__env__'] is env2, env2['__env__']
-
     def test_ParseConfig(self):
         """Test the ParseConfig() method"""
     def test_ParseConfig(self):
         """Test the ParseConfig() method"""
-        env = Environment(COMMAND='command',
+        env = self.TestEnvironment(COMMAND='command',
+                          ASFLAGS='assembler',
+                          CCFLAGS=[''],
+                          CPPDEFINES=[],
+                          CPPFLAGS=[''],
                           CPPPATH='string',
                           CPPPATH='string',
+                          FRAMEWORKPATH=[],
+                          FRAMEWORKS=[],
                           LIBPATH=['list'],
                           LIBS='',
                           LIBPATH=['list'],
                           LIBS='',
-                          CCFLAGS=[''])
-        save_command = []
-        orig_popen = os.popen
-        def my_popen(command, save_command=save_command):
-            save_command.append(command)
-            class fake_file:
-                def read(self):
-                    return "-I/usr/include/fum -Ibar -X\n" + \
-                           "-L/usr/fax -Lfoo -lxxx abc"
-            return fake_file()
+                          LINKFLAGS=[''],
+                          RPATH=[])
+
+        orig_backtick = env.backtick
+        class my_backtick:
+            def __init__(self, save_command, output):
+                self.save_command = save_command
+                self.output = output
+            def __call__(self, command):
+                self.save_command.append(command)
+                return self.output
+
         try:
         try:
-            os.popen = my_popen
-            libs = env.ParseConfig("fake $COMMAND")
+            save_command = []
+            env.backtick = my_backtick(save_command, 
+                                 "-I/usr/include/fum -I bar -X\n" + \
+                                 "-L/usr/fax -L foo -lxxx -l yyy " + \
+                                 "-Wa,-as -Wl,-link " + \
+                                 "-Wl,-rpath=rpath1 " + \
+                                 "-Wl,-R,rpath2 " + \
+                                 "-Wl,-Rrpath3 " + \
+                                 "-Wp,-cpp abc " + \
+                                 "-framework Carbon " + \
+                                 "-frameworkdir=fwd1 " + \
+                                 "-Ffwd2 " + \
+                                 "-F fwd3 " + \
+                                 "-pthread " + \
+                                 "-mno-cygwin -mwindows " + \
+                                 "-arch i386 -isysroot /tmp +DD64 " + \
+                                 "-DFOO -DBAR=value")
+            env.ParseConfig("fake $COMMAND")
             assert save_command == ['fake command'], save_command
             assert save_command == ['fake command'], save_command
-            assert libs == ['abc'], libs
+            assert env['ASFLAGS'] == ['assembler', '-as'], env['ASFLAGS']
+            assert env['CCFLAGS'] == ['', '-X', '-Wa,-as',
+                                      '-pthread', '-mno-cygwin',
+                                      ('-arch', 'i386'), ('-isysroot', '/tmp'),
+                                      '+DD64'], env['CCFLAGS']
+            assert env['CPPDEFINES'] == ['FOO', ['BAR', 'value']], env['CPPDEFINES']
+            assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS']
             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
+            assert env['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], env['FRAMEWORKPATH']
+            assert env['FRAMEWORKS'] == ['Carbon'], env['FRAMEWORKS']
             assert env['LIBPATH'] == ['list', '/usr/fax', 'foo'], env['LIBPATH']
             assert env['LIBPATH'] == ['list', '/usr/fax', 'foo'], env['LIBPATH']
-            assert env['LIBS'] == ['xxx'], env['LIBS']
-            assert env['CCFLAGS'] == ['', '-X'], env['CCFLAGS']
+            assert env['LIBS'] == ['xxx', 'yyy', env.File('abc')], env['LIBS']
+            assert env['LINKFLAGS'] == ['', '-Wl,-link', '-pthread',
+                                        '-mno-cygwin', '-mwindows',
+                                        ('-arch', 'i386'),
+                                        ('-isysroot', '/tmp'),
+                                        '+DD64'], env['LINKFLAGS']
+            assert env['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], env['RPATH']
+
+            env.backtick = my_backtick([], "-Ibar")
+            env.ParseConfig("fake2")
+            assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
+            env.ParseConfig("fake2", unique=0)
+            assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar', 'bar'], env['CPPPATH']
         finally:
         finally:
-            os.popen = orig_popen
+            env.backtick = orig_backtick
+
+    def test_ParseDepends(self):
+        """Test the ParseDepends() method"""
+        test = TestCmd.TestCmd(workdir = '')
+
+        test.write('single', """
+#file: dependency
+
+f0: \
+   d1 \
+   d2 \
+   d3 \
+
+""")
+
+        test.write('multiple', """
+f1: foo
+f2 f3: bar
+f4: abc def
+#file: dependency
+f5: \
+   ghi \
+   jkl \
+   mno \
+""")
+
+        env = self.TestEnvironment(SINGLE = test.workpath('single'))
+
+        tlist = []
+        dlist = []
+        def my_depends(target, dependency, tlist=tlist, dlist=dlist):
+            tlist.extend(target)
+            dlist.extend(dependency)
+
+        env.Depends = my_depends
+
+        env.ParseDepends(test.workpath('does_not_exist'))
+
+        exc_caught = None
+        try:
+            env.ParseDepends(test.workpath('does_not_exist'), must_exist=1)
+        except IOError:
+            exc_caught = 1
+        assert exc_caught, "did not catch expected IOError"
+
+        del tlist[:]
+        del dlist[:]
+
+        env.ParseDepends('$SINGLE', only_one=1)
+        t = list(map(str, tlist))
+        d = list(map(str, dlist))
+        assert t == ['f0'], t
+        assert d == ['d1', 'd2', 'd3'], d
+
+        del tlist[:]
+        del dlist[:]
+
+        env.ParseDepends(test.workpath('multiple'))
+        t = list(map(str, tlist))
+        d = list(map(str, dlist))
+        assert t == ['f1', 'f2', 'f3', 'f4', 'f5'], t
+        assert d == ['foo', 'bar', 'abc', 'def', 'ghi', 'jkl', 'mno'], d
+
+        exc_caught = None
+        try:
+            env.ParseDepends(test.workpath('multiple'), only_one=1)
+        except SCons.Errors.UserError:
+            exc_caught = 1
+        assert exc_caught, "did not catch expected UserError"
 
     def test_Platform(self):
         """Test the Platform() method"""
 
     def test_Platform(self):
         """Test the Platform() method"""
-        env = Environment(WIN32='win32', NONE='no-such-platform')
+        env = self.TestEnvironment(WIN32='win32', NONE='no-such-platform')
 
         exc_caught = None
         try:
 
         exc_caught = None
         try:
@@ -1300,6 +2151,8 @@ class EnvironmentTestCase(unittest.TestCase):
     def test_Prepend(self):
         """Test prepending to construction variables in an Environment
         """
     def test_Prepend(self):
         """Test prepending to construction variables in an Environment
         """
+        import UserDict
+        UD = UserDict.UserDict
         import UserList
         UL = UserList.UserList
 
         import UserList
         UL = UserList.UserList
 
@@ -1331,6 +2184,18 @@ class EnvironmentTestCase(unittest.TestCase):
             UL(['i7']), [''],           UL(['', 'i7']),
             UL(['i8']), UL(['']),       UL(['', 'i8']),
 
             UL(['i7']), [''],           UL(['', 'i7']),
             UL(['i8']), UL(['']),       UL(['', 'i8']),
 
+            {'d1':1},   'D1',           {'d1':1, 'D1':None},
+            {'d2':1},   ['D2'],         {'d2':1, 'D2':None},
+            {'d3':1},   UL(['D3']),     {'d3':1, 'D3':None},
+            {'d4':1},   {'D4':1},       {'d4':1, 'D4':1},
+            {'d5':1},   UD({'D5':1}),   UD({'d5':1, 'D5':1}),
+
+            UD({'u1':1}), 'U1',         UD({'u1':1, 'U1':None}),
+            UD({'u2':1}), ['U2'],       UD({'u2':1, 'U2':None}),
+            UD({'u3':1}), UL(['U3']),   UD({'u3':1, 'U3':None}),
+            UD({'u4':1}), {'U4':1},     UD({'u4':1, 'U4':1}),
+            UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
+
             '',         'M1',           'M1',
             '',         ['M2'],         ['M2'],
             '',         UL(['M3']),     UL(['M3']),
             '',         'M1',           'M1',
             '',         ['M2'],         ['M2'],
             '',         UL(['M3']),     UL(['M3']),
@@ -1381,14 +2246,21 @@ class EnvironmentTestCase(unittest.TestCase):
         failed = 0
         while cases:
             input, prepend, expect = cases[:3]
         failed = 0
         while cases:
             input, prepend, expect = cases[:3]
-            env['XXX'] = input
-            env.Prepend(XXX = prepend)
-            result = env['XXX']
-            if result != expect:
+            env['XXX'] = copy.copy(input)
+            try:
+                env.Prepend(XXX = prepend)
+            except Exception, e:
                 if failed == 0: print
                 if failed == 0: print
-                print "    %s Prepend %s => %s did not match %s" % \
-                      (repr(input), repr(prepend), repr(result), repr(expect))
+                print "    %s Prepend %s exception: %s" % \
+                      (repr(input), repr(prepend), e)
                 failed = failed + 1
                 failed = failed + 1
+            else:
+                result = env['XXX']
+                if result != expect:
+                    if failed == 0: print
+                    print "    %s Prepend %s => %s did not match %s" % \
+                          (repr(input), repr(prepend), repr(result), repr(expect))
+                    failed = failed + 1
             del cases[:3]
         assert failed == 0, "%d Prepend() cases failed" % failed
 
             del cases[:3]
         assert failed == 0, "%d Prepend() cases failed" % failed
 
@@ -1404,85 +2276,142 @@ class EnvironmentTestCase(unittest.TestCase):
         assert isinstance(result, CLVar), repr(result)
         assert result == ['bar', 'foo'], result
 
         assert isinstance(result, CLVar), repr(result)
         assert result == ['bar', 'foo'], result
 
-        env3 = Environment(X = {'x1' : 7})
+        env3 = self.TestEnvironment(X = {'x1' : 7})
         env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
         assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
         assert env3['Y'] == {'y1': 10}, env3['Y']
 
         env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
         assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
         assert env3['Y'] == {'y1': 10}, env3['Y']
 
-        env4 = Environment(BUILDERS = {'z1' : 11})
-        env4.Prepend(BUILDERS = {'z2' : 12})
-        assert env4['BUILDERS'] == {'z1' : 11, 'z2' : 12}, env4['BUILDERS']
+        z1 = Builder()
+        z2 = Builder()
+        env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
+        env4.Prepend(BUILDERS = {'z2' : z2})
+        assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
         assert hasattr(env4, 'z1')
         assert hasattr(env4, 'z2')
 
     def test_PrependENVPath(self):
         """Test prepending to an ENV path."""
         assert hasattr(env4, 'z1')
         assert hasattr(env4, 'z2')
 
     def test_PrependENVPath(self):
         """Test prepending to an ENV path."""
-        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+        env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
         # have to include the pathsep here so that the test will work on UNIX too.
         env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';')
         env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
         env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
         env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
         # have to include the pathsep here so that the test will work on UNIX too.
         env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';')
         env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
         env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
         env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
+        # this should do nothing since delete_existing is 0
+        env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
         assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
 
         assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
 
-    def test_PrependENVPath(self):
-        """Test prepending to an ENV path."""
-        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
-                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
-        # have to include the pathsep here so that the test will work on UNIX too.
-        env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';')
-        env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
-        env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
-        env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
-        assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
-        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
+        test = TestCmd.TestCmd(workdir = '')
+        test.subdir('sub1', 'sub2')
+        p=env1['ENV']['PATH']
+        env1.PrependENVPath('PATH','#sub1', sep = ';')
+        env1.PrependENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
+        assert env1['ENV']['PATH'] == 'sub2;sub1;' + p, env1['ENV']['PATH']
 
     def test_PrependUnique(self):
         """Test prepending unique values to construction variables
 
         This strips values that are already present when lists are
         involved."""
 
     def test_PrependUnique(self):
         """Test prepending unique values to construction variables
 
         This strips values that are already present when lists are
         involved."""
-        env = Environment(AAA1 = 'a1',
+        env = self.TestEnvironment(AAA1 = 'a1',
                           AAA2 = 'a2',
                           AAA3 = 'a3',
                           AAA2 = 'a2',
                           AAA3 = 'a3',
+                          AAA4 = 'a4',
+                          AAA5 = 'a5',
                           BBB1 = ['b1'],
                           BBB2 = ['b2'],
                           BBB1 = ['b1'],
                           BBB2 = ['b2'],
-                          BBB3 = ['b3'])
+                          BBB3 = ['b3'],
+                          BBB4 = ['b4'],
+                          BBB5 = ['b5'],
+                          CCC1 = '',
+                          CCC2 = '',
+                          DDD1 = ['a', 'b', 'c'])
         env.PrependUnique(AAA1 = 'a1',
                           AAA2 = ['a2'],
         env.PrependUnique(AAA1 = 'a1',
                           AAA2 = ['a2'],
-                          AAA3 = ['a3', 'b', 'c', 'a3'],
+                          AAA3 = ['a3', 'b', 'c', 'b', 'a3'], # ignore dups
+                          AAA4 = 'a4.new',
+                          AAA5 = ['a5.new'],
                           BBB1 = 'b1',
                           BBB2 = ['b2'],
                           BBB1 = 'b1',
                           BBB2 = ['b2'],
-                          BBB3 = ['b3', 'b', 'c', 'b3'])
+                          BBB3 = ['b3', 'b', 'c', 'b3'],
+                          BBB4 = 'b4.new',
+                          BBB5 = ['b5.new'],
+                          CCC1 = 'c1',
+                          CCC2 = ['c2'],
+                          DDD1 = 'b')
         assert env['AAA1'] == 'a1a1', env['AAA1']
         assert env['AAA2'] == ['a2'], env['AAA2']
         assert env['AAA1'] == 'a1a1', env['AAA1']
         assert env['AAA2'] == ['a2'], env['AAA2']
-        assert env['AAA3'] == ['b', 'c', 'a3'], env['AAA3']
+        assert env['AAA3'] == ['c', 'b', 'a3'], env['AAA3']
+        assert env['AAA4'] == 'a4.newa4', env['AAA4']
+        assert env['AAA5'] == ['a5.new', 'a5'], env['AAA5']
         assert env['BBB1'] == ['b1'], env['BBB1']
         assert env['BBB2'] == ['b2'], env['BBB2']
         assert env['BBB3'] == ['b', 'c', 'b3'], env['BBB3']
         assert env['BBB1'] == ['b1'], env['BBB1']
         assert env['BBB2'] == ['b2'], env['BBB2']
         assert env['BBB3'] == ['b', 'c', 'b3'], env['BBB3']
+        assert env['BBB4'] == ['b4.new', 'b4'], env['BBB4']
+        assert env['BBB5'] == ['b5.new', 'b5'], env['BBB5']
+        assert env['CCC1'] == 'c1', env['CCC1']
+        assert env['CCC2'] == ['c2'], env['CCC2']
+        assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
+
+        env.PrependUnique(DDD1 = 'b', delete_existing=1)
+        assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front
+        env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
+        assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
+        env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
+        assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1'] 
+
+
+        env['CLVar'] = CLVar([])
+        env.PrependUnique(CLVar = 'bar')
+        result = env['CLVar']
+        if sys.version[0] == '1' or sys.version[:3] == '2.0':
+            # Python 2.0 and before have a quirky behavior where CLVar([])
+            # actually matches '' and [] due to different __coerce__()
+            # semantics in the UserList implementation.  It isn't worth a
+            # lot of effort to get this corner case to work identically
+            # (support for Python 1.5 support will die soon anyway),
+            # so just treat it separately for now.
+            assert result == 'bar', result
+        else:
+            assert isinstance(result, CLVar), repr(result)
+            assert result == ['bar'], result
+
+        env['CLVar'] = CLVar(['abc'])
+        env.PrependUnique(CLVar = 'bar')
+        result = env['CLVar']
+        assert isinstance(result, CLVar), repr(result)
+        assert result == ['bar', 'abc'], result
+
+        env['CLVar'] = CLVar(['bar'])
+        env.PrependUnique(CLVar = 'bar')
+        result = env['CLVar']
+        assert isinstance(result, CLVar), repr(result)
+        assert result == ['bar'], result
 
     def test_Replace(self):
         """Test replacing construction variables in an Environment
 
         After creation of the Environment, of course.
         """
 
     def test_Replace(self):
         """Test replacing construction variables in an Environment
 
         After creation of the Environment, of course.
         """
-        env1 = Environment(AAA = 'a', BBB = 'b')
+        env1 = self.TestEnvironment(AAA = 'a', BBB = 'b')
         env1.Replace(BBB = 'bbb', CCC = 'ccc')
 
         env1.Replace(BBB = 'bbb', CCC = 'ccc')
 
-        env2 = Environment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
+        env2 = self.TestEnvironment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
         assert env1 == env2, diff_env(env1, env2)
 
         assert env1 == env2, diff_env(env1, env2)
 
-        env3 = Environment(BUILDERS = {'b1' : 1})
+        b1 = Builder()
+        b2 = Builder()
+        env3 = self.TestEnvironment(BUILDERS = {'b1' : b1})
         assert hasattr(env3, 'b1'), "b1 was not set"
         assert hasattr(env3, 'b1'), "b1 was not set"
-        env3.Replace(BUILDERS = {'b2' : 2})
+        env3.Replace(BUILDERS = {'b2' : b2})
         assert not hasattr(env3, 'b1'), "b1 was not cleared"
         assert hasattr(env3, 'b2'), "b2 was not set"
 
     def test_ReplaceIxes(self):
         "Test ReplaceIxes()"
         assert not hasattr(env3, 'b1'), "b1 was not cleared"
         assert hasattr(env3, 'b2'), "b2 was not set"
 
     def test_ReplaceIxes(self):
         "Test ReplaceIxes()"
-        env = Environment(LIBPREFIX='lib',
+        env = self.TestEnvironment(LIBPREFIX='lib',
                           LIBSUFFIX='.a',
                           SHLIBPREFIX='lib',
                           SHLIBSUFFIX='.so',
                           LIBSUFFIX='.a',
                           SHLIBPREFIX='lib',
                           SHLIBSUFFIX='.so',
@@ -1501,23 +2430,33 @@ class EnvironmentTestCase(unittest.TestCase):
                                              'PREFIX', 'SUFFIX',
                                              'LIBPREFIX', 'LIBSUFFIX')
 
                                              'PREFIX', 'SUFFIX',
                                              'LIBPREFIX', 'LIBSUFFIX')
 
+    def test_SetDefault(self):
+        """Test the SetDefault method"""
+        env = self.TestEnvironment(tools = [])
+        env.SetDefault(V1 = 1)
+        env.SetDefault(V1 = 2)
+        assert env['V1'] == 1
+        env['V2'] = 2
+        env.SetDefault(V2 = 1)
+        assert env['V2'] == 2
+
     def test_Tool(self):
         """Test the Tool() method"""
     def test_Tool(self):
         """Test the Tool() method"""
-        env = Environment(LINK='link', NONE='no-such-tool')
+        env = self.TestEnvironment(LINK='link', NONE='no-such-tool')
 
         exc_caught = None
         try:
             env.Tool('does_not_exist')
 
         exc_caught = None
         try:
             env.Tool('does_not_exist')
-        except SCons.Errors.UserError:
+        except SCons.Errors.EnvironmentError:
             exc_caught = 1
             exc_caught = 1
-        assert exc_caught, "did not catch expected UserError"
+        assert exc_caught, "did not catch expected EnvironmentError"
 
         exc_caught = None
         try:
             env.Tool('$NONE')
 
         exc_caught = None
         try:
             env.Tool('$NONE')
-        except SCons.Errors.UserError:
+        except SCons.Errors.EnvironmentError:
             exc_caught = 1
             exc_caught = 1
-        assert exc_caught, "did not catch expected UserError"
+        assert exc_caught, "did not catch expected EnvironmentError"
 
         # Use a non-existent toolpath directory just to make sure we
         # can call Tool() with the keyword argument.
 
         # Use a non-existent toolpath directory just to make sure we
         # can call Tool() with the keyword argument.
@@ -1527,6 +2466,29 @@ class EnvironmentTestCase(unittest.TestCase):
         env.Tool('$LINK')
         assert env['LINK'] == '$SMARTLINK', env['LINK']
 
         env.Tool('$LINK')
         assert env['LINK'] == '$SMARTLINK', env['LINK']
 
+        # Test that the environment stores the toolpath and
+        # re-uses it for later calls.
+        test = TestCmd.TestCmd(workdir = '')
+
+        test.write('xxx.py', """\
+def exists(env):
+    1
+def generate(env):
+    env['XXX'] = 'one'
+""")
+
+        test.write('yyy.py', """\
+def exists(env):
+    1
+def generate(env):
+    env['YYY'] = 'two'
+""")
+
+        env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
+        assert env['XXX'] == 'one', env['XXX']
+        env.Tool('yyy')
+        assert env['YYY'] == 'two', env['YYY']
+
     def test_WhereIs(self):
         """Test the WhereIs() method"""
         test = TestCmd.TestCmd(workdir = '')
     def test_WhereIs(self):
         """Test the WhereIs() method"""
         test = TestCmd.TestCmd(workdir = '')
@@ -1555,30 +2517,35 @@ class EnvironmentTestCase(unittest.TestCase):
                           test.workpath('sub2'),
                           test.workpath('sub3'),
                           test.workpath('sub4'),
                           test.workpath('sub2'),
                           test.workpath('sub3'),
                           test.workpath('sub4'),
-                        ] + string.split(env_path, os.pathsep)
+                        ] + env_path.split(os.pathsep)
 
         pathdirs_1243 = [ test.workpath('sub1'),
                           test.workpath('sub2'),
                           test.workpath('sub4'),
                           test.workpath('sub3'),
 
         pathdirs_1243 = [ test.workpath('sub1'),
                           test.workpath('sub2'),
                           test.workpath('sub4'),
                           test.workpath('sub3'),
-                        ] + string.split(env_path, os.pathsep)
+                        ] + env_path.split(os.pathsep)
 
 
-        path = string.join(pathdirs_1234, os.pathsep)
-        env = Environment(ENV = {'PATH' : path})
+        path = os.pathsep.join(pathdirs_1234)
+        env = self.TestEnvironment(ENV = {'PATH' : path})
         wi = env.WhereIs('xxx.exe')
         assert wi == test.workpath(sub3_xxx_exe), wi
         wi = env.WhereIs('xxx.exe', pathdirs_1243)
         assert wi == test.workpath(sub4_xxx_exe), wi
         wi = env.WhereIs('xxx.exe')
         assert wi == test.workpath(sub3_xxx_exe), wi
         wi = env.WhereIs('xxx.exe', pathdirs_1243)
         assert wi == test.workpath(sub4_xxx_exe), wi
-        wi = env.WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
+        wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1243))
+        assert wi == test.workpath(sub4_xxx_exe), wi
+
+        wi = env.WhereIs('xxx.exe', reject = sub3_xxx_exe)
+        assert wi == test.workpath(sub4_xxx_exe), wi
+        wi = env.WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
         assert wi == test.workpath(sub4_xxx_exe), wi
 
         assert wi == test.workpath(sub4_xxx_exe), wi
 
-        path = string.join(pathdirs_1243, os.pathsep)
-        env = Environment(ENV = {'PATH' : path})
+        path = os.pathsep.join(pathdirs_1243)
+        env = self.TestEnvironment(ENV = {'PATH' : path})
         wi = env.WhereIs('xxx.exe')
         assert wi == test.workpath(sub4_xxx_exe), wi
         wi = env.WhereIs('xxx.exe', pathdirs_1234)
         assert wi == test.workpath(sub3_xxx_exe), wi
         wi = env.WhereIs('xxx.exe')
         assert wi == test.workpath(sub4_xxx_exe), wi
         wi = env.WhereIs('xxx.exe', pathdirs_1234)
         assert wi == test.workpath(sub3_xxx_exe), wi
-        wi = env.WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
+        wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1234))
         assert wi == test.workpath(sub3_xxx_exe), wi
 
         if sys.platform == 'win32':
         assert wi == test.workpath(sub3_xxx_exe), wi
 
         if sys.platform == 'win32':
@@ -1589,13 +2556,13 @@ class EnvironmentTestCase(unittest.TestCase):
             assert wi == test.workpath(sub4_xxx_exe), wi
 
             wi = env.WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
             assert wi == test.workpath(sub4_xxx_exe), wi
 
             wi = env.WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
-            assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+            assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
 
             # Test that we return a normalized path even when
             # the path contains forward slashes.
             forward_slash = test.workpath('') + '/sub3'
             wi = env.WhereIs('xxx', path = forward_slash, pathext = '.EXE')
 
             # Test that we return a normalized path even when
             # the path contains forward slashes.
             forward_slash = test.workpath('') + '/sub3'
             wi = env.WhereIs('xxx', path = forward_slash, pathext = '.EXE')
-            assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+            assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
 
 
 
 
 
 
@@ -1603,29 +2570,33 @@ class EnvironmentTestCase(unittest.TestCase):
         """Test the Action() method"""
         import SCons.Action
 
         """Test the Action() method"""
         import SCons.Action
 
-        env = Environment(FOO = 'xyzzy')
+        env = self.TestEnvironment(FOO = 'xyzzy')
 
         a = env.Action('foo')
         assert a, a
 
         a = env.Action('foo')
         assert a, a
-        assert a.__class__ is SCons.Action.CommandAction, a
+        assert a.__class__ is SCons.Action.CommandAction, a.__class__
 
         a = env.Action('$FOO')
         assert a, a
 
         a = env.Action('$FOO')
         assert a, a
-        assert a.__class__ is SCons.Action.CommandGeneratorAction, a
+        assert a.__class__ is SCons.Action.CommandAction, a.__class__
+
+        a = env.Action('$$FOO')
+        assert a, a
+        assert a.__class__ is SCons.Action.LazyAction, a.__class__
 
         a = env.Action(['$FOO', 'foo'])
         assert a, a
 
         a = env.Action(['$FOO', 'foo'])
         assert a, a
-        assert a.__class__ is SCons.Action.ListAction, a
+        assert a.__class__ is SCons.Action.ListAction, a.__class__
 
         def func(arg):
             pass
         a = env.Action(func)
         assert a, a
 
         def func(arg):
             pass
         a = env.Action(func)
         assert a, a
-        assert a.__class__ is SCons.Action.FunctionAction, a
+        assert a.__class__ is SCons.Action.FunctionAction, a.__class__
 
     def test_AddPostAction(self):
         """Test the AddPostAction() method"""
 
     def test_AddPostAction(self):
         """Test the AddPostAction() method"""
-        env = Environment(FOO='fff', BAR='bbb')
+        env = self.TestEnvironment(FOO='fff', BAR='bbb')
 
         n = env.AddPostAction('$FOO', lambda x: x)
         assert str(n[0]) == 'fff', n[0]
 
         n = env.AddPostAction('$FOO', lambda x: x)
         assert str(n[0]) == 'fff', n[0]
@@ -1636,7 +2607,7 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_AddPreAction(self):
         """Test the AddPreAction() method"""
 
     def test_AddPreAction(self):
         """Test the AddPreAction() method"""
-        env = Environment(FOO='fff', BAR='bbb')
+        env = self.TestEnvironment(FOO='fff', BAR='bbb')
 
         n = env.AddPreAction('$FOO', lambda x: x)
         assert str(n[0]) == 'fff', n[0]
 
         n = env.AddPreAction('$FOO', lambda x: x)
         assert str(n[0]) == 'fff', n[0]
@@ -1647,128 +2618,149 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_Alias(self):
         """Test the Alias() method"""
 
     def test_Alias(self):
         """Test the Alias() method"""
-        env = Environment(FOO='kkk', BAR='lll', EA='export_alias')
+        env = self.TestEnvironment(FOO='kkk', BAR='lll', EA='export_alias')
 
 
-        tgt = env.Alias('new_alias')
+        tgt = env.Alias('new_alias')[0]
         assert str(tgt) == 'new_alias', tgt
         assert tgt.sources == [], tgt.sources
         assert str(tgt) == 'new_alias', tgt
         assert tgt.sources == [], tgt.sources
+        assert not hasattr(tgt, 'builder'), tgt.builder
 
 
-        tgt = env.Alias('None_alias', None)
+        tgt = env.Alias('None_alias', None)[0]
         assert str(tgt) == 'None_alias', tgt
         assert tgt.sources == [], tgt.sources
 
         assert str(tgt) == 'None_alias', tgt
         assert tgt.sources == [], tgt.sources
 
-        tgt = env.Alias('empty_list', [])
+        tgt = env.Alias('empty_list', [])[0]
         assert str(tgt) == 'empty_list', tgt
         assert tgt.sources == [], tgt.sources
 
         assert str(tgt) == 'empty_list', tgt
         assert tgt.sources == [], tgt.sources
 
-        tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])
+        tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])[0]
         assert str(tgt) == 'export_alias', tgt
         assert str(tgt) == 'export_alias', tgt
-        assert len(tgt.sources) == 2, map(str, tgt.sources)
-        assert str(tgt.sources[0]) == 'asrc1', map(str, tgt.sources)
-        assert str(tgt.sources[1]) == 'kkk', map(str, tgt.sources)
+        assert len(tgt.sources) == 2, list(map(str, tgt.sources))
+        assert str(tgt.sources[0]) == 'asrc1', list(map(str, tgt.sources))
+        assert str(tgt.sources[1]) == 'kkk', list(map(str, tgt.sources))
 
 
-        n = env.Alias(tgt, source = ['$BAR', 'asrc4'])
+        n = env.Alias(tgt, source = ['$BAR', 'asrc4'])[0]
         assert n is tgt, n
         assert n is tgt, n
-        assert len(tgt.sources) == 4, map(str, tgt.sources)
-        assert str(tgt.sources[2]) == 'lll', map(str, tgt.sources)
-        assert str(tgt.sources[3]) == 'asrc4', map(str, tgt.sources)
+        assert len(tgt.sources) == 4, list(map(str, tgt.sources))
+        assert str(tgt.sources[2]) == 'lll', list(map(str, tgt.sources))
+        assert str(tgt.sources[3]) == 'asrc4', list(map(str, tgt.sources))
 
 
-        n = env.Alias('$EA', 'asrc5')
+        n = env.Alias('$EA', 'asrc5')[0]
         assert n is tgt, n
         assert n is tgt, n
-        assert len(tgt.sources) == 5, map(str, tgt.sources)
-        assert str(tgt.sources[4]) == 'asrc5', map(str, tgt.sources)
+        assert len(tgt.sources) == 5, list(map(str, tgt.sources))
+        assert str(tgt.sources[4]) == 'asrc5', list(map(str, tgt.sources))
 
         t1, t2 = env.Alias(['t1', 't2'], ['asrc6', 'asrc7'])
         assert str(t1) == 't1', t1
         assert str(t2) == 't2', t2
 
         t1, t2 = env.Alias(['t1', 't2'], ['asrc6', 'asrc7'])
         assert str(t1) == 't1', t1
         assert str(t2) == 't2', t2
-        assert len(t1.sources) == 2, map(str, t1.sources)
-        assert str(t1.sources[0]) == 'asrc6', map(str, t1.sources)
-        assert str(t1.sources[1]) == 'asrc7', map(str, t1.sources)
-        assert len(t2.sources) == 2, map(str, t2.sources)
-        assert str(t2.sources[0]) == 'asrc6', map(str, t2.sources)
-        assert str(t2.sources[1]) == 'asrc7', map(str, t2.sources)
+        assert len(t1.sources) == 2, list(map(str, t1.sources))
+        assert str(t1.sources[0]) == 'asrc6', list(map(str, t1.sources))
+        assert str(t1.sources[1]) == 'asrc7', list(map(str, t1.sources))
+        assert len(t2.sources) == 2, list(map(str, t2.sources))
+        assert str(t2.sources[0]) == 'asrc6', list(map(str, t2.sources))
+        assert str(t2.sources[1]) == 'asrc7', list(map(str, t2.sources))
+
+        tgt = env.Alias('add', 's1')
+        tgt = env.Alias('add', 's2')[0]
+        s = list(map(str, tgt.sources))
+        assert s == ['s1', 's2'], s
+        tgt = env.Alias(tgt, 's3')[0]
+        s = list(map(str, tgt.sources))
+        assert s == ['s1', 's2', 's3'], s
+
+        tgt = env.Alias('act', None, "action1")[0]
+        s = str(tgt.builder.action)
+        assert s == "action1", s
+        tgt = env.Alias('act', None, "action2")[0]
+        s = str(tgt.builder.action)
+        assert s == "action1\naction2", s
+        tgt = env.Alias(tgt, None, "action3")[0]
+        s = str(tgt.builder.action)
+        assert s == "action1\naction2\naction3", s
 
     def test_AlwaysBuild(self):
         """Test the AlwaysBuild() method"""
 
     def test_AlwaysBuild(self):
         """Test the AlwaysBuild() method"""
-        env = Environment(FOO='fff', BAR='bbb')
-        t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR')
-        assert t[0].__class__.__name__ == 'File'
+        env = self.TestEnvironment(FOO='fff', BAR='bbb')
+        t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
+                            env.fs.Dir('dir'), env.fs.File('file'))
+        assert t[0].__class__.__name__ == 'Entry'
         assert t[0].path == 'a'
         assert t[0].always_build
         assert t[0].path == 'a'
         assert t[0].always_build
-        assert t[1].__class__.__name__ == 'File'
+        assert t[1].__class__.__name__ == 'Entry'
         assert t[1].path == 'bfff'
         assert t[1].always_build
         assert t[1].path == 'bfff'
         assert t[1].always_build
-        assert t[2].__class__.__name__ == 'File'
+        assert t[2].__class__.__name__ == 'Entry'
         assert t[2].path == 'c'
         assert t[2].always_build
         assert t[2].path == 'c'
         assert t[2].always_build
-        assert t[3].__class__.__name__ == 'File'
+        assert t[3].__class__.__name__ == 'Entry'
         assert t[3].path == 'd'
         assert t[3].always_build
         assert t[3].path == 'd'
         assert t[3].always_build
-        assert t[4].__class__.__name__ == 'File'
+        assert t[4].__class__.__name__ == 'Entry'
         assert t[4].path == 'bbb'
         assert t[4].always_build
         assert t[4].path == 'bbb'
         assert t[4].always_build
-
-    def test_BuildDir(self):
-        """Test the BuildDir() method"""
+        assert t[5].__class__.__name__ == 'Dir'
+        assert t[5].path == 'dir'
+        assert t[5].always_build
+        assert t[6].__class__.__name__ == 'File'
+        assert t[6].path == 'file'
+        assert t[6].always_build
+
+    def test_VariantDir(self):
+        """Test the VariantDir() method"""
         class MyFS:
              def Dir(self, name):
                  return name
         class MyFS:
              def Dir(self, name):
                  return name
-             def BuildDir(self, build_dir, src_dir, duplicate):
-                 self.build_dir = build_dir
+             def VariantDir(self, variant_dir, src_dir, duplicate):
+                 self.variant_dir = variant_dir
                  self.src_dir = src_dir
                  self.duplicate = duplicate
 
                  self.src_dir = src_dir
                  self.duplicate = duplicate
 
-        env = Environment(FOO = 'fff', BAR = 'bbb')
+        env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
         env.fs = MyFS()
 
         env.fs = MyFS()
 
-        env.BuildDir('build', 'src')
-        assert env.fs.build_dir == 'build', env.fs.build_dir
+        env.VariantDir('build', 'src')
+        assert env.fs.variant_dir == 'build', env.fs.variant_dir
         assert env.fs.src_dir == 'src', env.fs.src_dir
         assert env.fs.duplicate == 1, env.fs.duplicate
 
         assert env.fs.src_dir == 'src', env.fs.src_dir
         assert env.fs.duplicate == 1, env.fs.duplicate
 
-        env.BuildDir('build${FOO}', '${BAR}src', 0)
-        assert env.fs.build_dir == 'buildfff', env.fs.build_dir
+        env.VariantDir('build${FOO}', '${BAR}src', 0)
+        assert env.fs.variant_dir == 'buildfff', env.fs.variant_dir
         assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir
         assert env.fs.duplicate == 0, env.fs.duplicate
 
     def test_Builder(self):
         """Test the Builder() method"""
         assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir
         assert env.fs.duplicate == 0, env.fs.duplicate
 
     def test_Builder(self):
         """Test the Builder() method"""
-        env = Environment(FOO = 'xyzzy')
+        env = self.TestEnvironment(FOO = 'xyzzy')
 
         b = env.Builder(action = 'foo')
 
         b = env.Builder(action = 'foo')
-        assert not b is None, b
+        assert b is not None, b
 
         b = env.Builder(action = '$FOO')
 
         b = env.Builder(action = '$FOO')
-        assert not b is None, b
+        assert b is not None, b
 
         b = env.Builder(action = ['$FOO', 'foo'])
 
         b = env.Builder(action = ['$FOO', 'foo'])
-        assert not b is None, b
+        assert b is not None, b
 
         def func(arg):
             pass
         b = env.Builder(action = func)
 
         def func(arg):
             pass
         b = env.Builder(action = func)
-        assert not b is None, b
+        assert b is not None, b
         b = env.Builder(generator = func)
         b = env.Builder(generator = func)
-        assert not b is None, b
+        assert b is not None, b
 
     def test_CacheDir(self):
         """Test the CacheDir() method"""
 
     def test_CacheDir(self):
         """Test the CacheDir() method"""
-        class MyFS:
-            def CacheDir(self, path):
-                self.CD = path
-
-        env = Environment(CD = 'CacheDir')
-        env.fs = MyFS()
+        env = self.TestEnvironment(CD = 'CacheDir')
 
         env.CacheDir('foo')
 
         env.CacheDir('foo')
-        assert env.fs.CD == 'foo', env.fs.CD
+        assert env._CacheDir_path == 'foo', env._CacheDir_path
 
         env.CacheDir('$CD')
 
         env.CacheDir('$CD')
-        assert env.fs.CD == 'CacheDir', env.fs.CD
+        assert env._CacheDir_path == 'CacheDir', env._CacheDir_path
 
     def test_Clean(self):
         """Test the Clean() method"""
 
     def test_Clean(self):
         """Test the Clean() method"""
-        env = Environment(FOO = 'fff', BAR = 'bbb')
+        env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
 
         CT = SCons.Environment.CleanTargets
 
 
         CT = SCons.Environment.CleanTargets
 
@@ -1776,84 +2768,93 @@ class EnvironmentTestCase(unittest.TestCase):
         fff = env.arg2nodes('fff')[0]
 
         t = env.Clean('foo', 'aaa')
         fff = env.arg2nodes('fff')[0]
 
         t = env.Clean('foo', 'aaa')
-        l = map(str, CT[foo])
+        l = list(map(str, CT[foo]))
         assert l == ['aaa'], l
 
         t = env.Clean(foo, ['$BAR', 'ccc'])
         assert l == ['aaa'], l
 
         t = env.Clean(foo, ['$BAR', 'ccc'])
-        l = map(str, CT[foo])
+        l = list(map(str, CT[foo]))
         assert l == ['aaa', 'bbb', 'ccc'], l
 
         eee = env.arg2nodes('eee')[0]
 
         t = env.Clean('$FOO', 'ddd')
         assert l == ['aaa', 'bbb', 'ccc'], l
 
         eee = env.arg2nodes('eee')[0]
 
         t = env.Clean('$FOO', 'ddd')
-        l = map(str, CT[fff])
+        l = list(map(str, CT[fff]))
         assert l == ['ddd'], l
         t = env.Clean(fff, [eee, 'fff'])
         assert l == ['ddd'], l
         t = env.Clean(fff, [eee, 'fff'])
-        l = map(str, CT[fff])
+        l = list(map(str, CT[fff]))
         assert l == ['ddd', 'eee', 'fff'], l
 
     def test_Command(self):
         """Test the Command() method."""
         env = Environment()
         t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
         assert l == ['ddd', 'eee', 'fff'], l
 
     def test_Command(self):
         """Test the Command() method."""
         env = Environment()
         t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
-                        action='buildfoo $target $source')
-        assert not t.builder is None
+                        action='buildfoo $target $source')[0]
+        assert t.builder is not None
         assert t.builder.action.__class__.__name__ == 'CommandAction'
         assert t.builder.action.cmd_list == 'buildfoo $target $source'
         assert t.builder.action.__class__.__name__ == 'CommandAction'
         assert t.builder.action.cmd_list == 'buildfoo $target $source'
-        assert 'foo1.in' in map(lambda x: x.path, t.sources)
-        assert 'foo2.in' in map(lambda x: x.path, t.sources)
+        assert 'foo1.in' in [x.path for x in t.sources]
+        assert 'foo2.in' in [x.path for x in t.sources]
 
 
-        sub = SCons.Node.FS.default_fs.Dir('sub')
+        sub = env.fs.Dir('sub')
         t = env.Command(target='bar.out', source='sub',
         t = env.Command(target='bar.out', source='sub',
-                        action='buildbar $target $source')
-        assert 'sub' in map(lambda x: x.path, t.sources)
+                        action='buildbar $target $source')[0]
+        assert 'sub' in [x.path for x in t.sources]
 
         def testFunc(env, target, source):
             assert str(target[0]) == 'foo.out'
 
         def testFunc(env, target, source):
             assert str(target[0]) == 'foo.out'
-            assert 'foo1.in' in map(str, source) and 'foo2.in' in map(str, source), map(str, source)
+            assert 'foo1.in' in list(map(str, source)) and 'foo2.in' in list(map(str, source)), list(map(str, source))
             return 0
         t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
             return 0
         t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
-                        action=testFunc)
-        assert not t.builder is None
+                        action=testFunc)[0]
+        assert t.builder is not None
         assert t.builder.action.__class__.__name__ == 'FunctionAction'
         t.build()
         assert t.builder.action.__class__.__name__ == 'FunctionAction'
         t.build()
-        assert 'foo1.in' in map(lambda x: x.path, t.sources)
-        assert 'foo2.in' in map(lambda x: x.path, t.sources)
+        assert 'foo1.in' in [x.path for x in t.sources]
+        assert 'foo2.in' in [x.path for x in t.sources]
 
         x = []
         def test2(baz, x=x):
             x.append(baz)
 
         x = []
         def test2(baz, x=x):
             x.append(baz)
-        env = Environment(TEST2 = test2)
+        env = self.TestEnvironment(TEST2 = test2)
         t = env.Command(target='baz.out', source='baz.in',
                         action='${TEST2(XYZ)}',
         t = env.Command(target='baz.out', source='baz.in',
                         action='${TEST2(XYZ)}',
-                        XYZ='magic word')
-        assert not t.builder is None
+                        XYZ='magic word')[0]
+        assert t.builder is not None
         t.build()
         assert x[0] == 'magic word', x
 
         t.build()
         assert x[0] == 'magic word', x
 
+        t = env.Command(target='${X}.out', source='${X}.in',
+                        action = 'foo',
+                        X = 'xxx')[0]
+        assert str(t) == 'xxx.out', str(t)
+        assert 'xxx.in' in [x.path for x in t.sources]
+
+        env = self.TestEnvironment(source_scanner = 'should_not_find_this')
+        t = env.Command(target='file.out', source='file.in',
+                        action = 'foo',
+                        source_scanner = 'fake')[0]
+        assert t.builder.source_scanner == 'fake', t.builder.source_scanner
+
     def test_Configure(self):
         """Test the Configure() method"""
         # Configure() will write to a local temporary file.
         test = TestCmd.TestCmd(workdir = '')
         save = os.getcwd()
     def test_Configure(self):
         """Test the Configure() method"""
         # Configure() will write to a local temporary file.
         test = TestCmd.TestCmd(workdir = '')
         save = os.getcwd()
-        # Configure() will test, if we are reading a SConscript file
-        import SCons.Script.SConscript
-        SCons.Script.SConscript.sconscript_reading = 1
 
         try:
             os.chdir(test.workpath())
 
 
         try:
             os.chdir(test.workpath())
 
-            env = Environment(FOO = 'xyzzy')
+            env = self.TestEnvironment(FOO = 'xyzzy')
 
             def func(arg):
                 pass
 
             c = env.Configure()
 
             def func(arg):
                 pass
 
             c = env.Configure()
-            assert not c is None, c
+            assert c is not None, c
             c.Finish()
 
             c = env.Configure(custom_tests = {'foo' : func, '$FOO' : func})
             c.Finish()
 
             c = env.Configure(custom_tests = {'foo' : func, '$FOO' : func})
-            assert not c is None, c
+            assert c is not None, c
             assert hasattr(c, 'foo')
             assert hasattr(c, 'xyzzy')
             c.Finish()
             assert hasattr(c, 'foo')
             assert hasattr(c, 'xyzzy')
             c.Finish()
@@ -1862,12 +2863,13 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_Depends(self):
         """Test the explicit Depends method."""
 
     def test_Depends(self):
         """Test the explicit Depends method."""
-        env = Environment(FOO = 'xxx', BAR='yyy')
+        env = self.TestEnvironment(FOO = 'xxx', BAR='yyy')
         env.Dir('dir1')
         env.Dir('dir2')
         env.File('xxx.py')
         env.File('yyy.py')
         env.Dir('dir1')
         env.Dir('dir2')
         env.File('xxx.py')
         env.File('yyy.py')
-        t = env.Depends(target='EnvironmentTest.py', dependency='Environment.py')
+        t = env.Depends(target='EnvironmentTest.py',
+                        dependency='Environment.py')[0]
         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
         assert t.path == 'EnvironmentTest.py'
         assert len(t.depends) == 1
         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
         assert t.path == 'EnvironmentTest.py'
         assert len(t.depends) == 1
@@ -1875,7 +2877,7 @@ class EnvironmentTestCase(unittest.TestCase):
         assert d.__class__.__name__ == 'Entry', d.__class__.__name__
         assert d.path == 'Environment.py'
 
         assert d.__class__.__name__ == 'Entry', d.__class__.__name__
         assert d.path == 'Environment.py'
 
-        t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')
+        t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0]
         assert t.__class__.__name__ == 'File', t.__class__.__name__
         assert t.path == 'xxx.py'
         assert len(t.depends) == 1
         assert t.__class__.__name__ == 'File', t.__class__.__name__
         assert t.path == 'xxx.py'
         assert len(t.depends) == 1
@@ -1883,7 +2885,7 @@ class EnvironmentTestCase(unittest.TestCase):
         assert d.__class__.__name__ == 'File', d.__class__.__name__
         assert d.path == 'yyy.py'
 
         assert d.__class__.__name__ == 'File', d.__class__.__name__
         assert d.path == 'yyy.py'
 
-        t = env.Depends(target='dir1', dependency='dir2')
+        t = env.Depends(target='dir1', dependency='dir2')[0]
         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
         assert t.path == 'dir1'
         assert len(t.depends) == 1
         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
         assert t.path == 'dir1'
         assert len(t.depends) == 1
@@ -1897,7 +2899,7 @@ class EnvironmentTestCase(unittest.TestCase):
             def Dir(self, name):
                 return 'Dir(%s)' % name
 
             def Dir(self, name):
                 return 'Dir(%s)' % name
 
-        env = Environment(FOO = 'foodir', BAR = 'bardir')
+        env = self.TestEnvironment(FOO = 'foodir', BAR = 'bardir')
         env.fs = MyFS()
 
         d = env.Dir('d')
         env.fs = MyFS()
 
         d = env.Dir('d')
@@ -1909,9 +2911,45 @@ class EnvironmentTestCase(unittest.TestCase):
         d = env.Dir('${BAR}_$BAR')
         assert d == 'Dir(bardir_bardir)', d
 
         d = env.Dir('${BAR}_$BAR')
         assert d == 'Dir(bardir_bardir)', d
 
+        d = env.Dir(['dir1'])
+        assert d == ['Dir(dir1)'], d
+
+        d = env.Dir(['dir1', 'dir2'])
+        assert d == ['Dir(dir1)', 'Dir(dir2)'], d
+
+    def test_NoClean(self):
+        """Test the NoClean() method"""
+        env = self.TestEnvironment(FOO='ggg', BAR='hhh')
+        env.Dir('p_hhhb')
+        env.File('p_d')
+        t = env.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
+
+        assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
+        assert t[0].path == 'p_a'
+        assert t[0].noclean
+        assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
+        assert t[1].path == 'p_hhhb'
+        assert t[1].noclean
+        assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
+        assert t[2].path == 'p_c'
+        assert t[2].noclean
+        assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
+        assert t[3].path == 'p_d'
+        assert t[3].noclean
+        assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
+        assert t[4].path == 'p_ggg'
+        assert t[4].noclean
+
+    def test_Dump(self):
+        """Test the Dump() method"""
+
+        env = self.TestEnvironment(FOO = 'foo')
+        assert env.Dump('FOO') == "'foo'", env.Dump('FOO')
+        assert len(env.Dump()) > 200, env.Dump()    # no args version
+
     def test_Environment(self):
         """Test the Environment() method"""
     def test_Environment(self):
         """Test the Environment() method"""
-        env = Environment(FOO = 'xxx', BAR = 'yyy')
+        env = self.TestEnvironment(FOO = 'xxx', BAR = 'yyy')
 
         e2 = env.Environment(X = '$FOO', Y = '$BAR')
         assert e2['X'] == 'xxx', e2['X']
 
         e2 = env.Environment(X = '$FOO', Y = '$BAR')
         assert e2['X'] == 'xxx', e2['X']
@@ -1932,13 +2970,37 @@ class EnvironmentTestCase(unittest.TestCase):
         result = env.Execute("foo")
         assert result == "foo executed", result
 
         result = env.Execute("foo")
         assert result == "foo executed", result
 
+    def test_Entry(self):
+        """Test the Entry() method"""
+        class MyFS:
+            def Entry(self, name):
+                return 'Entry(%s)' % name
+
+        env = self.TestEnvironment(FOO = 'fooentry', BAR = 'barentry')
+        env.fs = MyFS()
+
+        e = env.Entry('e')
+        assert e == 'Entry(e)', e
+
+        e = env.Entry('$FOO')
+        assert e == 'Entry(fooentry)', e
+
+        e = env.Entry('${BAR}_$BAR')
+        assert e == 'Entry(barentry_barentry)', e
+
+        e = env.Entry(['entry1'])
+        assert e == ['Entry(entry1)'], e
+
+        e = env.Entry(['entry1', 'entry2'])
+        assert e == ['Entry(entry1)', 'Entry(entry2)'], e
+
     def test_File(self):
         """Test the File() method"""
         class MyFS:
             def File(self, name):
                 return 'File(%s)' % name
 
     def test_File(self):
         """Test the File() method"""
         class MyFS:
             def File(self, name):
                 return 'File(%s)' % name
 
-        env = Environment(FOO = 'foofile', BAR = 'barfile')
+        env = self.TestEnvironment(FOO = 'foofile', BAR = 'barfile')
         env.fs = MyFS()
 
         f = env.File('f')
         env.fs = MyFS()
 
         f = env.File('f')
@@ -1950,18 +3012,32 @@ class EnvironmentTestCase(unittest.TestCase):
         f = env.File('${BAR}_$BAR')
         assert f == 'File(barfile_barfile)', f
 
         f = env.File('${BAR}_$BAR')
         assert f == 'File(barfile_barfile)', f
 
+        f = env.File(['file1'])
+        assert f == ['File(file1)'], f
+
+        f = env.File(['file1', 'file2'])
+        assert f == ['File(file1)', 'File(file2)'], f
+
     def test_FindFile(self):
         """Test the FindFile() method"""
     def test_FindFile(self):
         """Test the FindFile() method"""
-        env = Environment(FOO = 'fff', BAR = 'bbb')
+        env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
 
         r = env.FindFile('foo', ['no_such_directory'])
         assert r is None, r
 
         # XXX
 
 
         r = env.FindFile('foo', ['no_such_directory'])
         assert r is None, r
 
         # XXX
 
+    def test_Flatten(self):
+        """Test the Flatten() method"""
+        env = Environment()
+        l = env.Flatten([1])
+        assert l == [1]
+        l = env.Flatten([1, [2, [3, [4]]]])
+        assert l == [1, 2, 3, 4], l
+
     def test_GetBuildPath(self):
         """Test the GetBuildPath() method."""
     def test_GetBuildPath(self):
         """Test the GetBuildPath() method."""
-        env = Environment(MAGIC = 'xyzzy')
+        env = self.TestEnvironment(MAGIC = 'xyzzy')
 
         p = env.GetBuildPath('foo')
         assert p == 'foo', p
 
         p = env.GetBuildPath('foo')
         assert p == 'foo', p
@@ -1971,13 +3047,13 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_Ignore(self):
         """Test the explicit Ignore method."""
 
     def test_Ignore(self):
         """Test the explicit Ignore method."""
-        env = Environment(FOO='yyy', BAR='zzz')
+        env = self.TestEnvironment(FOO='yyy', BAR='zzz')
         env.Dir('dir1')
         env.Dir('dir2')
         env.File('yyyzzz')
         env.File('zzzyyy')
 
         env.Dir('dir1')
         env.Dir('dir2')
         env.File('yyyzzz')
         env.File('zzzyyy')
 
-        t = env.Ignore(target='targ.py', dependency='dep.py')
+        t = env.Ignore(target='targ.py', dependency='dep.py')[0]
         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
         assert t.path == 'targ.py'
         assert len(t.ignore) == 1
         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
         assert t.path == 'targ.py'
         assert len(t.ignore) == 1
@@ -1985,7 +3061,7 @@ class EnvironmentTestCase(unittest.TestCase):
         assert i.__class__.__name__ == 'Entry', i.__class__.__name__
         assert i.path == 'dep.py'
 
         assert i.__class__.__name__ == 'Entry', i.__class__.__name__
         assert i.path == 'dep.py'
 
-        t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')
+        t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0]
         assert t.__class__.__name__ == 'File', t.__class__.__name__
         assert t.path == 'yyyzzz'
         assert len(t.ignore) == 1
         assert t.__class__.__name__ == 'File', t.__class__.__name__
         assert t.path == 'yyyzzz'
         assert len(t.ignore) == 1
@@ -1993,7 +3069,7 @@ class EnvironmentTestCase(unittest.TestCase):
         assert i.__class__.__name__ == 'File', i.__class__.__name__
         assert i.path == 'zzzyyy'
 
         assert i.__class__.__name__ == 'File', i.__class__.__name__
         assert i.path == 'zzzyyy'
 
-        t = env.Ignore(target='dir1', dependency='dir2')
+        t = env.Ignore(target='dir1', dependency='dir2')[0]
         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
         assert t.path == 'dir1'
         assert len(t.ignore) == 1
         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
         assert t.path == 'dir1'
         assert len(t.ignore) == 1
@@ -2001,75 +3077,9 @@ class EnvironmentTestCase(unittest.TestCase):
         assert i.__class__.__name__ == 'Dir', i.__class__.__name__
         assert i.path == 'dir2'
 
         assert i.__class__.__name__ == 'Dir', i.__class__.__name__
         assert i.path == 'dir2'
 
-    def test_Install(self):
-       """Test the Install method"""
-        env = Environment(FOO='iii', BAR='jjj')
-
-        tgt = env.Install('export', [ 'build/foo1', 'build/foo2' ])
-        paths = map(str, tgt)
-        paths.sort()
-        expect = map(os.path.normpath, [ 'export/foo1', 'export/foo2' ])
-        assert paths == expect, paths
-        for tnode in tgt:
-            assert tnode.builder == InstallBuilder
-
-        tgt = env.Install('$FOO', [ 'build/${BAR}1', 'build/${BAR}2' ])
-        paths = map(str, tgt)
-        paths.sort()
-        expect = map(os.path.normpath, [ 'iii/jjj1', 'iii/jjj2' ])
-        assert paths == expect, paths
-        for tnode in tgt:
-            assert tnode.builder == InstallBuilder
-
-        exc_caught = None
-        try:
-            tgt = env.Install('export', 'export')
-        except SCons.Errors.UserError, e:
-            exc_caught = 1
-        assert exc_caught, "UserError should be thrown when Install() target is not a file."
-        match = str(e) == "Source `export' of Install() is not a file.  Install() source must be one or more files."
-        assert match, e
-
-        exc_caught = None
-        try:
-            tgt = env.Install('export', ['export', 'build/foo1'])
-        except SCons.Errors.UserError, e:
-            exc_caught = 1
-        assert exc_caught, "UserError should be thrown when Install() target containins non-files."
-        match = str(e) == "Source `['export', 'build/foo1']' of Install() contains one or more non-files.  Install() source must be one or more files."
-        assert match, e
-
-        exc_caught = None
-        try:
-            tgt = env.Install('export/foo1', 'build/foo1')
-        except SCons.Errors.UserError, e:
-            exc_caught = 1
-        assert exc_caught, "UserError should be thrown reversing the order of Install() targets."
-        match = str(e) == "Target `export/foo1' of Install() is a file, but should be a directory.  Perhaps you have the Install() arguments backwards?"
-        assert match, e
-
-    def test_InstallAs(self):
-       """Test the InstallAs method"""
-        env = Environment(FOO='iii', BAR='jjj')
-
-        tgt = env.InstallAs(target=string.split('foo1 foo2'),
-                            source=string.split('bar1 bar2'))
-        assert len(tgt) == 2, len(tgt)
-        paths = map(lambda x: str(x.sources[0]), tgt)
-        paths.sort()
-        expect = map(os.path.normpath, [ 'bar1', 'bar2' ])
-        assert paths == expect, paths
-        for tnode in tgt:
-            assert tnode.builder == InstallBuilder
-
-        tgt = env.InstallAs(target='${FOO}.t', source='${BAR}.s')
-        assert tgt.path == 'iii.t'
-        assert tgt.sources[0].path == 'jjj.s'
-        assert tgt.builder == InstallBuilder
-
     def test_Literal(self):
         """Test the Literal() method"""
     def test_Literal(self):
         """Test the Literal() method"""
-        env = Environment(FOO='fff', BAR='bbb')
+        env = self.TestEnvironment(FOO='fff', BAR='bbb')
         list = env.subst_list([env.Literal('$FOO'), '$BAR'])[0]
         assert list == ['$FOO', 'bbb'], list
         list = env.subst_list(['$FOO', env.Literal('$BAR')])[0]
         list = env.subst_list([env.Literal('$FOO'), '$BAR'])[0]
         assert list == ['$FOO', 'bbb'], list
         list = env.subst_list(['$FOO', env.Literal('$BAR')])[0]
@@ -2077,7 +3087,7 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_Local(self):
         """Test the Local() method."""
 
     def test_Local(self):
         """Test the Local() method."""
-        env = Environment(FOO='lll')
+        env = self.TestEnvironment(FOO='lll')
 
         l = env.Local(env.fs.File('fff'))
         assert str(l[0]) == 'fff', l[0]
 
         l = env.Local(env.fs.File('fff'))
         assert str(l[0]) == 'fff', l[0]
@@ -2088,7 +3098,7 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_Precious(self):
         """Test the Precious() method"""
 
     def test_Precious(self):
         """Test the Precious() method"""
-        env = Environment(FOO='ggg', BAR='hhh')
+        env = self.TestEnvironment(FOO='ggg', BAR='hhh')
         env.Dir('p_hhhb')
         env.File('p_d')
         t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
         env.Dir('p_hhhb')
         env.File('p_d')
         t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
@@ -2118,7 +3128,7 @@ class EnvironmentTestCase(unittest.TestCase):
                 self.list.extend(list(dirs))
             def Dir(self, name):
                 return name
                 self.list.extend(list(dirs))
             def Dir(self, name):
                 return name
-        env = Environment(FOO='rrr', BAR='sss')
+        env = self.TestEnvironment(FOO='rrr', BAR='sss')
         env.fs = MyFS()
         env.Repository('/tmp/foo')
         env.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
         env.fs = MyFS()
         env.Repository('/tmp/foo')
         env.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
@@ -2130,31 +3140,32 @@ class EnvironmentTestCase(unittest.TestCase):
         def scan(node, env, target, arg):
             pass
 
         def scan(node, env, target, arg):
             pass
 
-        env = Environment(FOO = scan)
+        env = self.TestEnvironment(FOO = scan)
 
         s = env.Scanner('foo')
 
         s = env.Scanner('foo')
-        assert not s is None, s
+        assert s is not None, s
 
         s = env.Scanner(function = 'foo')
 
         s = env.Scanner(function = 'foo')
-        assert not s is None, s
+        assert s is not None, s
 
         if 0:
             s = env.Scanner('$FOO')
 
         if 0:
             s = env.Scanner('$FOO')
-            assert not s is None, s
+            assert s is not None, s
 
             s = env.Scanner(function = '$FOO')
 
             s = env.Scanner(function = '$FOO')
-            assert not s is None, s
+            assert s is not None, s
 
     def test_SConsignFile(self):
         """Test the SConsignFile() method"""
 
     def test_SConsignFile(self):
         """Test the SConsignFile() method"""
-        import SCons.Sig
+        import SCons.SConsign
 
         class MyFS:
             SConstruct_dir = os.sep + 'dir'
 
 
         class MyFS:
             SConstruct_dir = os.sep + 'dir'
 
-        env = Environment(FOO = 'SConsign',
+        env = self.TestEnvironment(FOO = 'SConsign',
                           BAR = os.path.join(os.sep, 'File'))
         env.fs = MyFS()
                           BAR = os.path.join(os.sep, 'File'))
         env.fs = MyFS()
+        env.Execute = lambda action: None
 
         try:
             fnames = []
 
         try:
             fnames = []
@@ -2163,92 +3174,100 @@ class EnvironmentTestCase(unittest.TestCase):
                 fnames.append(name)
                 dbms.append(dbm_module)
 
                 fnames.append(name)
                 dbms.append(dbm_module)
 
-            save_Sig_SConsignFile = SCons.Sig.SConsignFile
-            SCons.Sig.SConsignFile = capture
+            save_SConsign_File = SCons.SConsign.File
+            SCons.SConsign.File = capture
 
             env.SConsignFile('foo')
 
             env.SConsignFile('foo')
-            assert fnames[0] == os.path.join(os.sep, 'dir', 'foo'), fnames
-            assert dbms[0] == None, dbms
+            assert fnames[-1] == os.path.join(os.sep, 'dir', 'foo'), fnames
+            assert dbms[-1] is None, dbms
 
             env.SConsignFile('$FOO')
 
             env.SConsignFile('$FOO')
-            assert fnames[1] == os.path.join(os.sep, 'dir', 'SConsign'), fnames
-            assert dbms[1] == None, dbms
+            assert fnames[-1] == os.path.join(os.sep, 'dir', 'SConsign'), fnames
+            assert dbms[-1] is None, dbms
 
             env.SConsignFile('/$FOO')
 
             env.SConsignFile('/$FOO')
-            assert fnames[2] == '/SConsign', fnames
-            assert dbms[2] == None, dbms
+            assert fnames[-1] == os.sep + 'SConsign', fnames
+            assert dbms[-1] is None, dbms
+
+            env.SConsignFile(os.sep + '$FOO')
+            assert fnames[-1] == os.sep + 'SConsign', fnames
+            assert dbms[-1] is None, dbms
 
             env.SConsignFile('$BAR', 'x')
 
             env.SConsignFile('$BAR', 'x')
-            assert fnames[3] == os.path.join(os.sep, 'File'), fnames
-            assert dbms[3] == 'x', dbms
+            assert fnames[-1] == os.path.join(os.sep, 'File'), fnames
+            assert dbms[-1] == 'x', dbms
 
             env.SConsignFile('__$BAR', 7)
 
             env.SConsignFile('__$BAR', 7)
-            assert fnames[4] == os.path.join(os.sep, 'dir', '__', 'File'), fnames
-            assert dbms[4] == 7, dbms
+            assert fnames[-1] == os.path.join(os.sep, 'dir', '__', 'File'), fnames
+            assert dbms[-1] == 7, dbms
+
+            env.SConsignFile()
+            assert fnames[-1] == os.path.join(os.sep, 'dir', '.sconsign'), fnames
+            assert dbms[-1] is None, dbms
+
+            env.SConsignFile(None)
+            assert fnames[-1] is None, fnames
+            assert dbms[-1] is None, dbms
         finally:
         finally:
-            SCons.Sig.SConsignFile = save_Sig_SConsignFile
+            SCons.SConsign.File = save_SConsign_File
 
     def test_SideEffect(self):
         """Test the SideEffect() method"""
 
     def test_SideEffect(self):
         """Test the SideEffect() method"""
-        env = Environment(LIB='lll', FOO='fff', BAR='bbb')
+        env = self.TestEnvironment(LIB='lll', FOO='fff', BAR='bbb')
         env.File('mylll.pdb')
         env.Dir('mymmm.pdb')
 
         env.File('mylll.pdb')
         env.Dir('mymmm.pdb')
 
-        foo = env.Object('foo.obj', 'foo.cpp')
-        bar = env.Object('bar.obj', 'bar.cpp')
-        s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])
+        foo = env.Object('foo.obj', 'foo.cpp')[0]
+        bar = env.Object('bar.obj', 'bar.cpp')[0]
+        s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])[0]
         assert s.__class__.__name__ == 'Entry', s.__class__.__name__
         assert s.path == 'mylib.pdb'
         assert s.side_effect
         assert foo.side_effects == [s]
         assert bar.side_effects == [s]
         assert s.__class__.__name__ == 'Entry', s.__class__.__name__
         assert s.path == 'mylib.pdb'
         assert s.side_effect
         assert foo.side_effects == [s]
         assert bar.side_effects == [s]
-        assert s.depends_on([bar])
-        assert s.depends_on([foo])
 
 
-        fff = env.Object('fff.obj', 'fff.cpp')
-        bbb = env.Object('bbb.obj', 'bbb.cpp')
-        s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])
+        fff = env.Object('fff.obj', 'fff.cpp')[0]
+        bbb = env.Object('bbb.obj', 'bbb.cpp')[0]
+        s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])[0]
         assert s.__class__.__name__ == 'File', s.__class__.__name__
         assert s.path == 'mylll.pdb'
         assert s.side_effect
         assert fff.side_effects == [s], fff.side_effects
         assert bbb.side_effects == [s], bbb.side_effects
         assert s.__class__.__name__ == 'File', s.__class__.__name__
         assert s.path == 'mylll.pdb'
         assert s.side_effect
         assert fff.side_effects == [s], fff.side_effects
         assert bbb.side_effects == [s], bbb.side_effects
-        assert s.depends_on([bbb])
-        assert s.depends_on([fff])
 
 
-        ggg = env.Object('ggg.obj', 'ggg.cpp')
-        ccc = env.Object('ccc.obj', 'ccc.cpp')
-        s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])
+        ggg = env.Object('ggg.obj', 'ggg.cpp')[0]
+        ccc = env.Object('ccc.obj', 'ccc.cpp')[0]
+        s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])[0]
         assert s.__class__.__name__ == 'Dir', s.__class__.__name__
         assert s.path == 'mymmm.pdb'
         assert s.side_effect
         assert ggg.side_effects == [s], ggg.side_effects
         assert ccc.side_effects == [s], ccc.side_effects
         assert s.__class__.__name__ == 'Dir', s.__class__.__name__
         assert s.path == 'mymmm.pdb'
         assert s.side_effect
         assert ggg.side_effects == [s], ggg.side_effects
         assert ccc.side_effects == [s], ccc.side_effects
-        assert s.depends_on([ccc])
-        assert s.depends_on([ggg])
 
     def test_SourceCode(self):
         """Test the SourceCode() method."""
 
     def test_SourceCode(self):
         """Test the SourceCode() method."""
-        env = Environment(FOO='mmm', BAR='nnn')
-        e = env.SourceCode('foo', None)
+        env = self.TestEnvironment(FOO='mmm', BAR='nnn')
+        e = env.SourceCode('foo', None)[0]
         assert e.path == 'foo'
         s = e.src_builder()
         assert s is None, s
 
         b = Builder()
         assert e.path == 'foo'
         s = e.src_builder()
         assert s is None, s
 
         b = Builder()
-        e = env.SourceCode(e, b)
+        e = env.SourceCode(e, b)[0]
         assert e.path == 'foo'
         s = e.src_builder()
         assert s is b, s
 
         assert e.path == 'foo'
         s = e.src_builder()
         assert s is b, s
 
-        e = env.SourceCode('$BAR$FOO', None)
+        e = env.SourceCode('$BAR$FOO', None)[0]
         assert e.path == 'nnnmmm'
         s = e.src_builder()
         assert s is None, s
 
     def test_SourceSignatures(type):
         """Test the SourceSignatures() method"""
         assert e.path == 'nnnmmm'
         s = e.src_builder()
         assert s is None, s
 
     def test_SourceSignatures(type):
         """Test the SourceSignatures() method"""
-        env = Environment(M = 'MD5', T = 'timestamp')
+        import SCons.Errors
+
+        env = type.TestEnvironment(M = 'MD5', T = 'timestamp')
 
         exc_caught = None
         try:
 
         exc_caught = None
         try:
@@ -2256,23 +3275,35 @@ class EnvironmentTestCase(unittest.TestCase):
         except SCons.Errors.UserError:
             exc_caught = 1
         assert exc_caught, "did not catch expected UserError"
         except SCons.Errors.UserError:
             exc_caught = 1
         assert exc_caught, "did not catch expected UserError"
-        assert not hasattr(env, '_calc_module')
 
         env.SourceSignatures('MD5')
 
         env.SourceSignatures('MD5')
-        m = env._calc_module
+        assert env.src_sig_type == 'MD5', env.src_sig_type
 
         env.SourceSignatures('$M')
 
         env.SourceSignatures('$M')
-        assert env._calc_module is m
+        assert env.src_sig_type == 'MD5', env.src_sig_type
 
         env.SourceSignatures('timestamp')
 
         env.SourceSignatures('timestamp')
-        t = env._calc_module
+        assert env.src_sig_type == 'timestamp', env.src_sig_type
 
         env.SourceSignatures('$T')
 
         env.SourceSignatures('$T')
-        assert env._calc_module is t
+        assert env.src_sig_type == 'timestamp', env.src_sig_type
+
+        try:
+            import SCons.Util
+            save_md5 = SCons.Util.md5
+            SCons.Util.md5 = None
+            try:
+                env.SourceSignatures('MD5')
+            except SCons.Errors.UserError:
+                pass
+            else:
+                self.fail('Did not catch expected UserError')
+        finally:
+            SCons.Util.md5 = save_md5
 
     def test_Split(self):
         """Test the Split() method"""
 
     def test_Split(self):
         """Test the Split() method"""
-        env = Environment(FOO='fff', BAR='bbb')
+        env = self.TestEnvironment(FOO='fff', BAR='bbb')
         s = env.Split("foo bar")
         assert s == ["foo", "bar"], s
         s = env.Split("$FOO bar")
         s = env.Split("foo bar")
         assert s == ["foo", "bar"], s
         s = env.Split("$FOO bar")
@@ -2288,7 +3319,9 @@ class EnvironmentTestCase(unittest.TestCase):
 
     def test_TargetSignatures(type):
         """Test the TargetSignatures() method"""
 
     def test_TargetSignatures(type):
         """Test the TargetSignatures() method"""
-        env = Environment(B = 'build', C = 'content')
+        import SCons.Errors
+
+        env = type.TestEnvironment(B = 'build', C = 'content')
 
         exc_caught = None
         try:
 
         exc_caught = None
         try:
@@ -2299,16 +3332,41 @@ class EnvironmentTestCase(unittest.TestCase):
         assert not hasattr(env, '_build_signature')
 
         env.TargetSignatures('build')
         assert not hasattr(env, '_build_signature')
 
         env.TargetSignatures('build')
-        assert env._build_signature == 1, env._build_signature
-
-        env.TargetSignatures('content')
-        assert env._build_signature == 0, env._build_signature
+        assert env.tgt_sig_type == 'build', env.tgt_sig_type
 
         env.TargetSignatures('$B')
 
         env.TargetSignatures('$B')
-        assert env._build_signature == 1, env._build_signature
+        assert env.tgt_sig_type == 'build', env.tgt_sig_type
+
+        env.TargetSignatures('content')
+        assert env.tgt_sig_type == 'content', env.tgt_sig_type
 
         env.TargetSignatures('$C')
 
         env.TargetSignatures('$C')
-        assert env._build_signature == 0, env._build_signature
+        assert env.tgt_sig_type == 'content', env.tgt_sig_type
+
+        env.TargetSignatures('MD5')
+        assert env.tgt_sig_type == 'MD5', env.tgt_sig_type
+
+        env.TargetSignatures('timestamp')
+        assert env.tgt_sig_type == 'timestamp', env.tgt_sig_type
+
+        try:
+            import SCons.Util
+            save_md5 = SCons.Util.md5
+            SCons.Util.md5 = None
+            try:
+                env.TargetSignatures('MD5')
+            except SCons.Errors.UserError:
+                pass
+            else:
+                self.fail('Did not catch expected UserError')
+            try:
+                env.TargetSignatures('content')
+            except SCons.Errors.UserError:
+                pass
+            else:
+                self.fail('Did not catch expected UserError')
+        finally:
+            SCons.Util.md5 = save_md5
 
     def test_Value(self):
         """Test creating a Value() object
 
     def test_Value(self):
         """Test creating a Value() object
@@ -2325,6 +3383,9 @@ class EnvironmentTestCase(unittest.TestCase):
         assert not v1 is v2
         assert v1.value == v2.value
 
         assert not v1 is v2
         assert v1.value == v2.value
 
+        v3 = env.Value('c', 'build-c')
+        assert v3.value == 'c', v3.value
+
 
 
     def test_Environment_global_variable(type):
 
 
     def test_Environment_global_variable(type):
@@ -2343,13 +3404,440 @@ class EnvironmentTestCase(unittest.TestCase):
         f = env.xxx('$FOO')
         assert f == 'foo', f
 
         f = env.xxx('$FOO')
         assert f == 'foo', f
 
+    def test_bad_keywords(self):
+        """Test trying to use reserved keywords in an Environment"""
+        added = []
+
+        env = self.TestEnvironment(TARGETS = 'targets',
+                                   SOURCES = 'sources',
+                                   SOURCE = 'source',
+                                   TARGET = 'target',
+                                   CHANGED_SOURCES = 'changed_sources',
+                                   CHANGED_TARGETS = 'changed_targets',
+                                   UNCHANGED_SOURCES = 'unchanged_sources',
+                                   UNCHANGED_TARGETS = 'unchanged_targets',
+                                   INIT = 'init')
+        bad_msg = '%s is not reserved, but got omitted; see Environment.construction_var_name_ok'
+        added.append('INIT')
+        for x in self.reserved_variables:
+            assert x not in env, env[x]
+        for x in added:
+            assert x in env, bad_msg % x
+
+        env.Append(TARGETS = 'targets',
+                   SOURCES = 'sources',
+                   SOURCE = 'source',
+                   TARGET = 'target',
+                   CHANGED_SOURCES = 'changed_sources',
+                   CHANGED_TARGETS = 'changed_targets',
+                   UNCHANGED_SOURCES = 'unchanged_sources',
+                   UNCHANGED_TARGETS = 'unchanged_targets',
+                   APPEND = 'append')
+        added.append('APPEND')
+        for x in self.reserved_variables:
+            assert x not in env, env[x]
+        for x in added:
+            assert x in env, bad_msg % x
+
+        env.AppendUnique(TARGETS = 'targets',
+                         SOURCES = 'sources',
+                         SOURCE = 'source',
+                         TARGET = 'target',
+                         CHANGED_SOURCES = 'changed_sources',
+                         CHANGED_TARGETS = 'changed_targets',
+                         UNCHANGED_SOURCES = 'unchanged_sources',
+                         UNCHANGED_TARGETS = 'unchanged_targets',
+                         APPENDUNIQUE = 'appendunique')
+        added.append('APPENDUNIQUE')
+        for x in self.reserved_variables:
+            assert x not in env, env[x]
+        for x in added:
+            assert x in env, bad_msg % x
+
+        env.Prepend(TARGETS = 'targets',
+                    SOURCES = 'sources',
+                    SOURCE = 'source',
+                    TARGET = 'target',
+                    CHANGED_SOURCES = 'changed_sources',
+                    CHANGED_TARGETS = 'changed_targets',
+                    UNCHANGED_SOURCES = 'unchanged_sources',
+                    UNCHANGED_TARGETS = 'unchanged_targets',
+                    PREPEND = 'prepend')
+        added.append('PREPEND')
+        for x in self.reserved_variables:
+            assert x not in env, env[x]
+        for x in added:
+            assert x in env, bad_msg % x
+
+        env.Prepend(TARGETS = 'targets',
+                    SOURCES = 'sources',
+                    SOURCE = 'source',
+                    TARGET = 'target',
+                    CHANGED_SOURCES = 'changed_sources',
+                    CHANGED_TARGETS = 'changed_targets',
+                    UNCHANGED_SOURCES = 'unchanged_sources',
+                    UNCHANGED_TARGETS = 'unchanged_targets',
+                    PREPENDUNIQUE = 'prependunique')
+        added.append('PREPENDUNIQUE')
+        for x in self.reserved_variables:
+            assert x not in env, env[x]
+        for x in added:
+            assert x in env, bad_msg % x
+
+        env.Replace(TARGETS = 'targets',
+                    SOURCES = 'sources',
+                    SOURCE = 'source',
+                    TARGET = 'target',
+                    CHANGED_SOURCES = 'changed_sources',
+                    CHANGED_TARGETS = 'changed_targets',
+                    UNCHANGED_SOURCES = 'unchanged_sources',
+                    UNCHANGED_TARGETS = 'unchanged_targets',
+                    REPLACE = 'replace')
+        added.append('REPLACE')
+        for x in self.reserved_variables:
+            assert x not in env, env[x]
+        for x in added:
+            assert x in env, bad_msg % x
+
+        copy = env.Clone(TARGETS = 'targets',
+                         SOURCES = 'sources',
+                         SOURCE = 'source',
+                         TARGET = 'target',
+                         CHANGED_SOURCES = 'changed_sources',
+                         CHANGED_TARGETS = 'changed_targets',
+                         UNCHANGED_SOURCES = 'unchanged_sources',
+                         UNCHANGED_TARGETS = 'unchanged_targets',
+                         COPY = 'copy')
+        for x in self.reserved_variables:
+            assert x not in copy, env[x]
+        for x in added + ['COPY']:
+            assert x in copy, bad_msg % x
+
+        over = env.Override({'TARGETS' : 'targets',
+                             'SOURCES' : 'sources',
+                             'SOURCE' : 'source',
+                             'TARGET' : 'target',
+                             'CHANGED_SOURCES' : 'changed_sources',
+                             'CHANGED_TARGETS' : 'changed_targets',
+                             'UNCHANGED_SOURCES' : 'unchanged_sources',
+                             'UNCHANGED_TARGETS' : 'unchanged_targets',
+                             'OVERRIDE' : 'override'})
+        for x in self.reserved_variables:
+            assert x not in over, over[x]
+        for x in added + ['OVERRIDE']:
+            assert x in over, bad_msg % x
+
+    def test_parse_flags(self):
+        '''Test the Base class parse_flags argument'''
+        # all we have to show is that it gets to MergeFlags internally
+        env = Environment(tools=[], parse_flags = '-X')
+        assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+
+        env = Environment(tools=[], CCFLAGS=None, parse_flags = '-Y')
+        assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
+
+        env = Environment(tools=[], CPPDEFINES = 'FOO', parse_flags = '-std=c99 -X -DBAR')
+        assert env['CFLAGS']  == ['-std=c99'], env['CFLAGS']
+        assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
+        assert env['CPPDEFINES'] == ['FOO', 'BAR'], env['CPPDEFINES']
+
+    def test_clone_parse_flags(self):
+        '''Test the env.Clone() parse_flags argument'''
+        # all we have to show is that it gets to MergeFlags internally
+        env = Environment(tools = [])
+        env2 = env.Clone(parse_flags = '-X')
+        assert 'CCFLAGS' not in env
+        assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+
+        env = Environment(tools = [], CCFLAGS=None)
+        env2 = env.Clone(parse_flags = '-Y')
+        assert env['CCFLAGS'] is None, env['CCFLAGS']
+        assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
+
+        env = Environment(tools = [], CPPDEFINES = 'FOO')
+        env2 = env.Clone(parse_flags = '-std=c99 -X -DBAR')
+        assert 'CFLAGS' not in env
+        assert env2['CFLAGS']  == ['-std=c99'], env2['CFLAGS']
+        assert 'CCFLAGS' not in env
+        assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+        assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
+        assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
+
+
+
+class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture):
+
+    def setUp(self):
+        env = Environment()
+        env._dict = {'XXX' : 'x', 'YYY' : 'y'}
+        env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
+        env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
+        self.envs = [ env, env2, env3 ]
+
+    def checkpath(self, node, expect):
+        return str(node) == os.path.normpath(expect)
+
+    def test___init__(self):
+        """Test OverrideEnvironment initialization"""
+        env, env2, env3 = self.envs
+        assert env['XXX'] == 'x', env['XXX']
+        assert env2['XXX'] == 'x2', env2['XXX']
+        assert env3['XXX'] == 'x3', env3['XXX']
+        assert env['YYY'] == 'y', env['YYY']
+        assert env2['YYY'] == 'y', env2['YYY']
+        assert env3['YYY'] == 'y3', env3['YYY']
+
+    def test___delitem__(self):
+        """Test deleting variables from an OverrideEnvironment"""
+        env, env2, env3 = self.envs
 
 
+        del env3['XXX']
+        assert 'XXX' not in env, "env has XXX?"
+        assert 'XXX' not in env2, "env2 has XXX?"
+        assert 'XXX' not in env3, "env3 has XXX?"
 
 
-class NoSubstitutionProxyTestCase(unittest.TestCase):
+        del env3['YYY']
+        assert 'YYY' not in env, "env has YYY?"
+        assert 'YYY' not in env2, "env2 has YYY?"
+        assert 'YYY' not in env3, "env3 has YYY?"
+
+        del env3['ZZZ']
+        assert 'ZZZ' not in env, "env has ZZZ?"
+        assert 'ZZZ' not in env2, "env2 has ZZZ?"
+        assert 'ZZZ' not in env3, "env3 has ZZZ?"
+
+    def test_get(self):
+        """Test the OverrideEnvironment get() method"""
+        env, env2, env3 = self.envs
+        assert env.get('XXX') == 'x', env.get('XXX')
+        assert env2.get('XXX') == 'x2', env2.get('XXX')
+        assert env3.get('XXX') == 'x3', env3.get('XXX')
+        assert env.get('YYY') == 'y', env.get('YYY')
+        assert env2.get('YYY') == 'y', env2.get('YYY')
+        assert env3.get('YYY') == 'y3', env3.get('YYY')
+        assert env.get('ZZZ') is None, env.get('ZZZ')
+        assert env2.get('ZZZ') is None, env2.get('ZZZ')
+        assert env3.get('ZZZ') == 'z3', env3.get('ZZZ')
+
+    def test_has_key(self):
+        """Test the OverrideEnvironment has_key() method"""
+        env, env2, env3 = self.envs
+        assert 'XXX' in env, 'XXX' in env
+        assert 'XXX' in env2, 'XXX' in env2
+        assert 'XXX' in env3, 'XXX' in env3
+        assert 'YYY' in env, 'YYY' in env
+        assert 'YYY' in env2, 'YYY' in env2
+        assert 'YYY' in env3, 'YYY' in env3
+        assert 'ZZZ' not in env, 'ZZZ' in env
+        assert 'ZZZ' not in env2, 'ZZZ' in env2
+        assert 'ZZZ' in env3, 'ZZZ' in env3
+
+    def test_contains(self):
+        """Test the OverrideEnvironment __contains__() method"""
+        try:
+            'x' in {'x':1}
+        except TypeError:
+            # TODO(1.5)
+            # An early version of Python that doesn't support "in"
+            # on dictionaries.  Just pass the test.
+            pass
+        else:
+            env, env2, env3 = self.envs
+            assert 'XXX' in env
+            assert 'XXX' in env2
+            assert 'XXX' in env3
+            assert 'YYY' in env
+            assert 'YYY' in env2
+            assert 'YYY' in env3
+            assert not 'ZZZ' in env
+            assert not 'ZZZ' in env2
+            assert 'ZZZ' in env3
+
+    def test_items(self):
+        """Test the OverrideEnvironment Dictionary() method"""
+        env, env2, env3 = self.envs
+        items = env.Dictionary()
+        assert items == {'XXX' : 'x', 'YYY' : 'y'}, items
+        items = env2.Dictionary()
+        assert items == {'XXX' : 'x2', 'YYY' : 'y'}, items
+        items = env3.Dictionary()
+        assert items == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items
+
+    def test_items(self):
+        """Test the OverrideEnvironment items() method"""
+        env, env2, env3 = self.envs
+        items = sorted(env.items())
+        assert items == [('XXX', 'x'), ('YYY', 'y')], items
+        items = sorted(env2.items())
+        assert items == [('XXX', 'x2'), ('YYY', 'y')], items
+        items = sorted(env3.items())
+        assert items == [('XXX', 'x3'), ('YYY', 'y3'), ('ZZZ', 'z3')], items
+
+    def test_gvars(self):
+        """Test the OverrideEnvironment gvars() method"""
+        env, env2, env3 = self.envs
+        gvars = env.gvars()
+        assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
+        gvars = env2.gvars()
+        assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
+        gvars = env3.gvars()
+        assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
+
+    def test_lvars(self):
+        """Test the OverrideEnvironment lvars() method"""
+        env, env2, env3 = self.envs
+        lvars = env.lvars()
+        assert lvars == {}, lvars
+        lvars = env2.lvars()
+        assert lvars == {'XXX' : 'x2'}, lvars
+        lvars = env3.lvars()
+        assert lvars == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, lvars
+
+    def test_Replace(self):
+        """Test the OverrideEnvironment Replace() method"""
+        env, env2, env3 = self.envs
+        assert env['XXX'] == 'x', env['XXX']
+        assert env2['XXX'] == 'x2', env2['XXX']
+        assert env3['XXX'] == 'x3', env3['XXX']
+        assert env['YYY'] == 'y', env['YYY']
+        assert env2['YYY'] == 'y', env2['YYY']
+        assert env3['YYY'] == 'y3', env3['YYY']
+
+        env.Replace(YYY = 'y4')
+
+        assert env['XXX'] == 'x', env['XXX']
+        assert env2['XXX'] == 'x2', env2['XXX']
+        assert env3['XXX'] == 'x3', env3['XXX']
+        assert env['YYY'] == 'y4', env['YYY']
+        assert env2['YYY'] == 'y4', env2['YYY']
+        assert env3['YYY'] == 'y3', env3['YYY']
+
+    # Tests a number of Base methods through an OverrideEnvironment to
+    # make sure they handle overridden constructionv variables properly.
+    #
+    # The following Base methods also call self.subst(), and so could
+    # theoretically be subject to problems with evaluating overridden
+    # variables, but they're never really called that way in the rest
+    # of our code, so we won't worry about them (at least for now):
+    #
+    # ParseConfig()
+    # ParseDepends()
+    # Platform()
+    # Tool()
+    #
+    # Action()
+    # Alias()
+    # Builder()
+    # CacheDir()
+    # Configure()
+    # Environment()
+    # FindFile()
+    # Scanner()
+    # SourceSignatures()
+    # TargetSignatures()
+
+    # It's unlikely Clone() will ever be called this way, so let the
+    # other methods test that handling overridden values works.
+    #def test_Clone(self):
+    #    """Test the OverrideEnvironment Clone() method"""
+    #    pass
+
+    def test_FindIxes(self):
+        """Test the OverrideEnvironment FindIxes() method"""
+        env, env2, env3 = self.envs
+        x = env.FindIxes(['xaaay'], 'XXX', 'YYY')
+        assert x == 'xaaay', x
+        x = env2.FindIxes(['x2aaay'], 'XXX', 'YYY')
+        assert x == 'x2aaay', x
+        x = env3.FindIxes(['x3aaay3'], 'XXX', 'YYY')
+        assert x == 'x3aaay3', x
+
+    def test_ReplaceIxes(self):
+        """Test the OverrideEnvironment ReplaceIxes() method"""
+        env, env2, env3 = self.envs
+        x = env.ReplaceIxes('xaaay', 'XXX', 'YYY', 'YYY', 'XXX')
+        assert x == 'yaaax', x
+        x = env2.ReplaceIxes('x2aaay', 'XXX', 'YYY', 'YYY', 'XXX')
+        assert x == 'yaaax2', x
+        x = env3.ReplaceIxes('x3aaay3', 'XXX', 'YYY', 'YYY', 'XXX')
+        assert x == 'y3aaax3', x
+
+    # It's unlikely WhereIs() will ever be called this way, so let the
+    # other methods test that handling overridden values works.
+    #def test_WhereIs(self):
+    #    """Test the OverrideEnvironment WhereIs() method"""
+    #    pass
+
+    def test_Dir(self):
+        """Test the OverrideEnvironment Dir() method"""
+        env, env2, env3 = self.envs
+        x = env.Dir('ddir/$XXX')
+        assert self.checkpath(x, 'ddir/x'), str(x)
+        x = env2.Dir('ddir/$XXX')
+        assert self.checkpath(x, 'ddir/x2'), str(x)
+        x = env3.Dir('ddir/$XXX')
+        assert self.checkpath(x, 'ddir/x3'), str(x)
+
+    def test_Entry(self):
+        """Test the OverrideEnvironment Entry() method"""
+        env, env2, env3 = self.envs
+        x = env.Entry('edir/$XXX')
+        assert self.checkpath(x, 'edir/x'), str(x)
+        x = env2.Entry('edir/$XXX')
+        assert self.checkpath(x, 'edir/x2'), str(x)
+        x = env3.Entry('edir/$XXX')
+        assert self.checkpath(x, 'edir/x3'), str(x)
+
+    def test_File(self):
+        """Test the OverrideEnvironment File() method"""
+        env, env2, env3 = self.envs
+        x = env.File('fdir/$XXX')
+        assert self.checkpath(x, 'fdir/x'), str(x)
+        x = env2.File('fdir/$XXX')
+        assert self.checkpath(x, 'fdir/x2'), str(x)
+        x = env3.File('fdir/$XXX')
+        assert self.checkpath(x, 'fdir/x3'), str(x)
+
+    def test_Split(self):
+        """Test the OverrideEnvironment Split() method"""
+        env, env2, env3 = self.envs
+        env['AAA'] = '$XXX $YYY $ZZZ'
+        x = env.Split('$AAA')
+        assert x == ['x', 'y'], x
+        x = env2.Split('$AAA')
+        assert x == ['x2', 'y'], x
+        x = env3.Split('$AAA')
+        assert x == ['x3', 'y3', 'z3'], x
+
+    def test_parse_flags(self):
+        '''Test the OverrideEnvironment parse_flags argument'''
+        # all we have to show is that it gets to MergeFlags internally
+        env = SubstitutionEnvironment()
+        env2 = env.Override({'parse_flags' : '-X'})
+        assert 'CCFLAGS' not in env
+        assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+
+        env = SubstitutionEnvironment(CCFLAGS=None)
+        env2 = env.Override({'parse_flags' : '-Y'})
+        assert env['CCFLAGS'] is None, env['CCFLAGS']
+        assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
+
+        env = SubstitutionEnvironment(CPPDEFINES = 'FOO')
+        env2 = env.Override({'parse_flags' : '-std=c99 -X -DBAR'})
+        assert 'CFLAGS' not in env
+        assert env2['CFLAGS']  == ['-std=c99'], env2['CFLAGS']
+        assert 'CCFLAGS' not in env
+        assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
+        assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
+        assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
+
+
+
+class NoSubstitutionProxyTestCase(unittest.TestCase,TestEnvironmentFixture):
 
     def test___init__(self):
         """Test NoSubstitutionProxy initialization"""
 
     def test___init__(self):
         """Test NoSubstitutionProxy initialization"""
-        env = Environment(XXX = 'x', YYY = 'y')
+        env = self.TestEnvironment(XXX = 'x', YYY = 'y')
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
@@ -2377,7 +3865,7 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
 
     def test_subst(self):
         """Test the NoSubstitutionProxy.subst() method"""
 
     def test_subst(self):
         """Test the NoSubstitutionProxy.subst() method"""
-        env = Environment(XXX = 'x', YYY = 'y')
+        env = self.TestEnvironment(XXX = 'x', YYY = 'y')
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
@@ -2391,13 +3879,13 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
         assert x == '$XXX', x
 
         x = proxy.subst('$YYY', raw=7, target=None, source=None,
         assert x == '$XXX', x
 
         x = proxy.subst('$YYY', raw=7, target=None, source=None,
-                        dict=None, conv=None,
+                        conv=None,
                         extra_meaningless_keyword_argument=None)
         assert x == '$YYY', x
 
     def test_subst_kw(self):
         """Test the NoSubstitutionProxy.subst_kw() method"""
                         extra_meaningless_keyword_argument=None)
         assert x == '$YYY', x
 
     def test_subst_kw(self):
         """Test the NoSubstitutionProxy.subst_kw() method"""
-        env = Environment(XXX = 'x', YYY = 'y')
+        env = self.TestEnvironment(XXX = 'x', YYY = 'y')
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
@@ -2412,7 +3900,7 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
 
     def test_subst_list(self):
         """Test the NoSubstitutionProxy.subst_list() method"""
 
     def test_subst_list(self):
         """Test the NoSubstitutionProxy.subst_list() method"""
-        env = Environment(XXX = 'x', YYY = 'y')
+        env = self.TestEnvironment(XXX = 'x', YYY = 'y')
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
@@ -2425,13 +3913,12 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
         x = proxy.subst_list('$XXX')
         assert x == [[]], x
 
         x = proxy.subst_list('$XXX')
         assert x == [[]], x
 
-        x = proxy.subst_list('$YYY', raw=0, target=None, source=None,
-                             dict=None, conv=None)
+        x = proxy.subst_list('$YYY', raw=0, target=None, source=None, conv=None)
         assert x == [[]], x
 
     def test_subst_target_source(self):
         """Test the NoSubstitutionProxy.subst_target_source() method"""
         assert x == [[]], x
 
     def test_subst_target_source(self):
         """Test the NoSubstitutionProxy.subst_target_source() method"""
-        env = Environment(XXX = 'x', YYY = 'y')
+        env = self.TestEnvironment(XXX = 'x', YYY = 'y')
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
         assert env['XXX'] == 'x', env['XXX']
         assert env['YYY'] == 'y', env['YYY']
 
@@ -2441,19 +3928,61 @@ class NoSubstitutionProxyTestCase(unittest.TestCase):
 
         args = ('$XXX $TARGET $SOURCE $YYY',)
         kw = {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')}
 
         args = ('$XXX $TARGET $SOURCE $YYY',)
         kw = {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')}
-        x = apply(env.subst_target_source, args, kw)
+        x = env.subst_target_source(*args, **kw)
         assert x == 'x ttt sss y', x
         assert x == 'x ttt sss y', x
-        x = apply(proxy.subst_target_source, args, kw)
+        x = proxy.subst_target_source(*args, **kw)
         assert x == ' ttt sss ', x
 
         assert x == ' ttt sss ', x
 
+class EnvironmentVariableTestCase(unittest.TestCase):
+
+    def test_is_valid_construction_var(self):
+        """Testing is_valid_construction_var()"""
+        r = is_valid_construction_var("_a")
+        assert r is not None, r
+        r = is_valid_construction_var("z_")
+        assert r is not None, r
+        r = is_valid_construction_var("X_")
+        assert r is not None, r
+        r = is_valid_construction_var("2a")
+        assert r is None, r
+        r = is_valid_construction_var("a2_")
+        assert r is not None, r
+        r = is_valid_construction_var("/")
+        assert r is None, r
+        r = is_valid_construction_var("_/")
+        assert r is None, r
+        r = is_valid_construction_var("a/")
+        assert r is None, r
+        r = is_valid_construction_var(".b")
+        assert r is None, r
+        r = is_valid_construction_var("_.b")
+        assert r is None, r
+        r = is_valid_construction_var("b1._")
+        assert r is None, r
+        r = is_valid_construction_var("-b")
+        assert r is None, r
+        r = is_valid_construction_var("_-b")
+        assert r is None, r
+        r = is_valid_construction_var("b1-_")
+        assert r is None, r
+
 
 
 if __name__ == "__main__":
     suite = unittest.TestSuite()
 
 
 if __name__ == "__main__":
     suite = unittest.TestSuite()
-    tclasses = [ EnvironmentTestCase,
-                 NoSubstitutionProxyTestCase ]
+    tclasses = [ SubstitutionTestCase,
+                 BaseTestCase,
+                 OverrideEnvironmentTestCase,
+                 NoSubstitutionProxyTestCase,
+                 EnvironmentVariableTestCase ]
     for tclass in tclasses:
         names = unittest.getTestCaseNames(tclass, 'test_')
     for tclass in tclasses:
         names = unittest.getTestCaseNames(tclass, 'test_')
-        suite.addTests(map(tclass, names))
+        suite.addTests(list(map(tclass, names)))
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
     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: