Merged revisions 2302-2362,2364-2452 via svnmerge from
[scons.git] / src / engine / SCons / UtilTests.py
index 4b93d0f1150c7e394f964ebbc185e2f29c371dc2..3e8085bae8f58d1170d27818dba9cf9712a4ff53 100644 (file)
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2001, 2002 Steven Knight
+# __COPYRIGHT__
 #
 # Permission is hereby granted, free of charge, to any person obtaining
 # a copy of this software and associated documentation files (the
@@ -25,264 +25,81 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os
 import os.path
-import re
 import string
+import StringIO
 import sys
 import types
 import unittest
-import SCons.Node
-import SCons.Node.FS
-from SCons.Util import *
-import TestCmd
-
-class UtilTestCase(unittest.TestCase):
-    def test_subst(self):
-       """Test the subst function."""
-       loc = {}
-        loc['TARGETS'] = PathList(map(os.path.normpath, [ "./foo/bar.exe",
-                                                          "/bar/baz.obj",
-                                                          "../foo/baz.obj" ]))
-        loc['TARGET'] = loc['TARGETS'][0]
-        loc['SOURCES'] = PathList(map(os.path.normpath, [ "./foo/blah.cpp",
-                                                          "/bar/ack.cpp",
-                                                          "../foo/ack.c" ]))
-        loc['SOURCE'] = loc['SOURCES'][0]
-        loc['xxx'] = None
-        loc['zero'] = 0
-        loc['one'] = 1
-
-        if os.sep == '/':
-            def cvt(str):
-                return str
-        else:
-            def cvt(str):
-                return string.replace(str, '/', os.sep)
-
-        newcom = scons_subst("test $TARGETS $SOURCES", loc, {})
-        assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c")
-
-        newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", loc, {})
-        assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp")
-
-        newcom = scons_subst("test ${TARGETS[1:]}v", loc, {})
-        assert newcom == cvt("test /bar/baz.obj ../foo/baz.objv")
-
-        newcom = scons_subst("test $TARGET", loc, {})
-        assert newcom == cvt("test foo/bar.exe")
-
-        newcom = scons_subst("test $TARGET$FOO[0]", loc, {})
-        assert newcom == cvt("test foo/bar.exe[0]")
-
-        newcom = scons_subst("test ${TARGET.file}", loc, {})
-        assert newcom == cvt("test bar.exe")
-
-        newcom = scons_subst("test ${TARGET.filebase}", loc, {})
-        assert newcom == cvt("test bar")
-
-        newcom = scons_subst("test ${TARGET.suffix}", loc, {})
-        assert newcom == cvt("test .exe")
-
-        newcom = scons_subst("test ${TARGET.base}", loc, {})
-        assert newcom == cvt("test foo/bar")
-
-        newcom = scons_subst("test ${TARGET.dir}", loc, {})
-        assert newcom == cvt("test foo")
-
-        newcom = scons_subst("test ${TARGET.abspath}", loc, {})
-        assert newcom == cvt("test %s/foo/bar.exe"%SCons.Util.updrive(os.getcwd())), newcom
-
-        newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
-        assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(SCons.Util.updrive(os.getcwd()),
-                                                                     SCons.Util.updrive(os.path.abspath(os.path.normpath("/bar/ack.cpp"))),
-                                                                     SCons.Util.updrive(os.path.normpath(os.getcwd()+"/..")))), newcom
 
-        newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
-        assert newcom == cvt("test %s/foo/blah.cpp"%SCons.Util.updrive(os.getcwd())), newcom
+from UserDict import UserDict
 
-        newcom = scons_subst("test $xxx", loc, {})
-        assert newcom == cvt("test"), newcom
-
-        newcom = scons_subst("test $($xxx$)", loc, {})
-        assert newcom == cvt("test $($)"), newcom
-
-        newcom = scons_subst("test $( $xxx $)", loc, {})
-        assert newcom == cvt("test $( $)"), newcom
-
-        newcom = scons_subst("test $($xxx$)", loc, {}, re.compile('\$[()]'))
-        assert newcom == cvt("test"), newcom
-
-        newcom = scons_subst("test $( $xxx $)", loc, {}, re.compile('\$[()]'))
-        assert newcom == cvt("test"), newcom
+import TestCmd
 
-        newcom = scons_subst("test $zero", loc, {})
-        assert newcom == cvt("test 0"), newcom
+import SCons.Errors
 
-        newcom = scons_subst("test $one", loc, {})
-        assert newcom == cvt("test 1"), newcom
+from SCons.Util import *
 
-        newcom = scons_subst("test aXbXcXd", loc, {}, re.compile('X'))
-        assert newcom == cvt("test abcd"), newcom
+class OutBuffer:
+    def __init__(self):
+        self.buffer = ""
 
-        glob = { 'a' : 1, 'b' : 2 }
-        loc = {'a' : 3, 'c' : 4 }
-        newcom = scons_subst("test $a $b $c $d test", glob, loc)
-        assert newcom == "test 3 2 4 test", newcom
+    def write(self, str):
+        self.buffer = self.buffer + str
 
-        # Test against a former bug in scons_subst_list()
-        glob = { "FOO" : "$BAR",
-                 "BAR" : "BAZ",
-                 "BLAT" : "XYX",
-                 "BARXYX" : "BADNEWS" }
-        newcom = scons_subst("$FOO$BLAT", glob, {})
-        assert newcom == "BAZXYX", newcom
+class dictifyTestCase(unittest.TestCase):
+    def test_dictify(self):
+        """Test the dictify() function"""
+        r = SCons.Util.dictify(['a', 'b', 'c'], [1, 2, 3])
+        assert r == {'a':1, 'b':2, 'c':3}, r
 
-        # Test for double-dollar-sign behavior
-        glob = { "FOO" : "BAR",
-                 "BAZ" : "BLAT" }
-        newcom = scons_subst("$$FOO$BAZ", glob, {})
-        assert newcom == "$FOOBLAT", newcom
+        r = {}
+        SCons.Util.dictify(['a'], [1], r)
+        SCons.Util.dictify(['b'], [2], r)
+        SCons.Util.dictify(['c'], [3], r)
+        assert r == {'a':1, 'b':2, 'c':3}, r
 
+class UtilTestCase(unittest.TestCase):
     def test_splitext(self):
         assert splitext('foo') == ('foo','')
         assert splitext('foo.bar') == ('foo','.bar')
         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
 
-    def test_subst_list(self):
-        """Testing the scons_subst_list() method..."""
-
-        class Node:
-            def __init__(self, name):
-                self.name = name
-            def __str__(self):
-                return self.name
-            def is_literal(self):
-                return 1
-        
-        loc = {}
-        loc['TARGETS'] = PathList(map(os.path.normpath, [ "./foo/bar.exe",
-                                                          "/bar/baz with spaces.obj",
-                                                          "../foo/baz.obj" ]))
-        loc['TARGET'] = loc['TARGETS'][0]
-        loc['SOURCES'] = PathList(map(os.path.normpath, [ "./foo/blah with spaces.cpp",
-                                                          "/bar/ack.cpp",
-                                                          "../foo/ack.c" ]))
-        loc['xxx'] = None
-        loc['NEWLINE'] = 'before\nafter'
-
-        loc['DO'] = Node('do something')
-        loc['FOO'] = Node('foo.in')
-        loc['BAR'] = Node('bar with spaces.out')
-        loc['CRAZY'] = Node('crazy\nfile.in')
-
-        if os.sep == '/':
-            def cvt(str):
-                return str
-        else:
-            def cvt(str):
-                return string.replace(str, '/', os.sep)
-
-        cmd_list = scons_subst_list("$TARGETS", loc, {})
-        assert cmd_list[0][1] == cvt("/bar/baz with spaces.obj"), cmd_list[0][1]
-
-        cmd_list = scons_subst_list("$SOURCES $NEWLINE $TARGETS", loc, {})
-        assert len(cmd_list) == 2, cmd_list
-        assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
-        assert cmd_list[1][2] == cvt("/bar/baz with spaces.obj"), cmd_list[1]
-
-        cmd_list = scons_subst_list("$SOURCES$NEWLINE", loc, {})
-        assert len(cmd_list) == 2, cmd_list
-        assert cmd_list[1][0] == 'after', cmd_list[1][0]
-        assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
-
-        cmd_list = scons_subst_list("$DO --in=$FOO --out=$BAR", loc, {})
-        assert len(cmd_list) == 1, cmd_list
-        assert len(cmd_list[0]) == 3, cmd_list
-        assert cmd_list[0][0] == 'do something', cmd_list[0][0]
-        assert cmd_list[0][1] == '--in=foo.in', cmd_list[0][1]
-        assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
-
-        # This test is now fixed, and works like it should.
-        cmd_list = scons_subst_list("$DO --in=$CRAZY --out=$BAR", loc, {})
-        assert len(cmd_list) == 1, map(str, cmd_list[0])
-        assert len(cmd_list[0]) == 3, cmd_list
-        assert cmd_list[0][0] == 'do something', cmd_list[0][0]
-        assert cmd_list[0][1] == '--in=crazy\nfile.in', cmd_list[0][1]
-        assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
-        
-        # Test inputting a list to scons_subst_list()
-        cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
-                                        "This is a test" ],
-                                    loc, {})
-        assert len(cmd_list) == 2, len(cmd_list)
-        assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
-        assert cmd_list[1][0] == cvt("after"), cmd_list[1]
-        assert cmd_list[1][4] == "This is a test", cmd_list[1]
-
-        glob = { 'a' : 1, 'b' : 2 }
-        loc = {'a' : 3, 'c' : 4 }
-        cmd_list = scons_subst_list("test $a $b $c $d test", glob, loc)
-        assert len(cmd_list) == 1, cmd_list
-        assert map(str, cmd_list[0]) == ['test', '3', '2', '4', 'test'], map(str, cmd_list[0])
-
-        # Test against a former bug in scons_subst_list()
-        glob = { "FOO" : "$BAR",
-                 "BAR" : "BAZ",
-                 "BLAT" : "XYX",
-                 "BARXYX" : "BADNEWS" }
-        cmd_list = scons_subst_list("$FOO$BLAT", glob, {})
-        assert cmd_list[0][0] == "BAZXYX", cmd_list[0][0]
-
-        # Test for double-dollar-sign behavior
-        glob = { "FOO" : "BAR",
-                 "BAZ" : "BLAT" }
-        cmd_list = scons_subst_list("$$FOO$BAZ", glob, {})
-        assert cmd_list[0][0] == "$FOOBLAT", cmd_list[0][0]
-
-        # Now test escape functionality
-        def escape_func(foo):
-            return '**' + foo + '**'
-        def quote_func(foo):
-            return foo
-        glob = { "FOO" : PathList([ 'foo\nwith\nnewlines',
-                                    'bar\nwith\nnewlines' ]) }
-        cmd_list = scons_subst_list("$FOO", glob, {})
-        assert cmd_list[0][0] == 'foo\nwith\nnewlines', cmd_list[0][0]
-        cmd_list[0][0].escape(escape_func)
-        assert cmd_list[0][0] == '**foo\nwith\nnewlines**', cmd_list[0][0]
-        assert cmd_list[0][1] == 'bar\nwith\nnewlines', cmd_list[0][0]
-        cmd_list[0][1].escape(escape_func)
-        assert cmd_list[0][1] == '**bar\nwith\nnewlines**', cmd_list[0][0]
-
-    def test_quote_spaces(self):
-        """Testing the quote_spaces() method..."""
-       q = quote_spaces('x')
-       assert q == 'x', q
-
-       q = quote_spaces('x x')
-       assert q == '"x x"', q
-
-       q = quote_spaces('x\tx')
-       assert q == '"x\tx"', q
-
-    def test_render_tree(self):
-        class Node:
-            def __init__(self, name, children=[]):
-                self.children = children
-                self.name = name
-            def __str__(self):
-                return self.name
-
-        def get_children(node):
-            return node.children
-
-        windows_h = Node("windows.h")
-        stdlib_h = Node("stdlib.h")
-        stdio_h = Node("stdio.h")
-        bar_c = Node("bar.c", [stdlib_h, windows_h])
-        bar_o = Node("bar.o", [bar_c])
-        foo_c = Node("foo.c", [stdio_h])
-        foo_o = Node("foo.o", [foo_c])
-        foo = Node("foo", [foo_o, bar_o])
+    class Node:
+        def __init__(self, name, children=[]):
+            self.children = children
+            self.name = name
+            self.nocache = None
+        def __str__(self):
+            return self.name
+        def exists(self):
+            return 1
+        def rexists(self):
+            return 1
+        def has_builder(self):
+            return 1
+        def has_explicit_builder(self):
+            return 1
+        def side_effect(self):
+            return 1
+        def precious(self):
+            return 1
+        def always_build(self):
+            return 1
+        def is_up_to_date(self):
+            return 1
+        def noclean(self):
+            return 1
+
+    def tree_case_1(self):
+        """Fixture for the render_tree() and print_tree() tests."""
+        windows_h = self.Node("windows.h")
+        stdlib_h = self.Node("stdlib.h")
+        stdio_h = self.Node("stdio.h")
+        bar_c = self.Node("bar.c", [stdlib_h, windows_h])
+        bar_o = self.Node("bar.o", [bar_c])
+        foo_c = self.Node("foo.c", [stdio_h])
+        foo_o = self.Node("foo.o", [foo_c])
+        foo = self.Node("foo", [foo_o, bar_o])
 
         expect = """\
 +-foo
@@ -295,14 +112,101 @@ class UtilTestCase(unittest.TestCase):
       +-windows.h
 """
 
-        actual = render_tree(foo, get_children)
+        lines = string.split(expect, '\n')[:-1]
+        lines = map(lambda l: '[E BSPACN ]'+l, lines)
+        withtags = string.join(lines, '\n') + '\n'
+
+        return foo, expect, withtags
+
+    def tree_case_2(self, prune=1):
+        """Fixture for the render_tree() and print_tree() tests."""
+
+        stdlib_h = self.Node("stdlib.h")
+        bar_h = self.Node('bar.h', [stdlib_h])
+        blat_h = self.Node('blat.h', [stdlib_h])
+        blat_c = self.Node('blat.c', [blat_h, bar_h])
+        blat_o = self.Node('blat.o', [blat_c])
+
+        expect = """\
++-blat.o
+  +-blat.c
+    +-blat.h
+    | +-stdlib.h
+    +-bar.h
+      +-[stdlib.h]
+"""
+
+        if not prune:
+            expect = string.replace(expect, '[', '')
+            expect = string.replace(expect, ']', '')
+
+        lines = string.split(expect, '\n')[:-1]
+        lines = map(lambda l: '[E BSPACN ]'+l, lines)
+        withtags = string.join(lines, '\n') + '\n'
+
+        return blat_o, expect, withtags
+
+    def test_render_tree(self):
+        """Test the render_tree() function"""
+        def get_children(node):
+            return node.children
+
+        node, expect, withtags = self.tree_case_1()
+        actual = render_tree(node, get_children)
+        assert expect == actual, (expect, actual)
+
+        node, expect, withtags = self.tree_case_2()
+        actual = render_tree(node, get_children, 1)
         assert expect == actual, (expect, actual)
 
+    def test_print_tree(self):
+        """Test the print_tree() function"""
+        def get_children(node):
+            return node.children
+
+        save_stdout = sys.stdout
+
+        try:
+            node, expect, withtags = self.tree_case_1()
+
+            sys.stdout = StringIO.StringIO()
+            print_tree(node, get_children)
+            actual = sys.stdout.getvalue()
+            assert expect == actual, (expect, actual)
+
+            sys.stdout = StringIO.StringIO()
+            print_tree(node, get_children, showtags=1)
+            actual = sys.stdout.getvalue()
+            assert withtags == actual, (withtags, actual)
+
+            node, expect, withtags = self.tree_case_2(prune=0)
+
+            sys.stdout = StringIO.StringIO()
+            print_tree(node, get_children, 1)
+            actual = sys.stdout.getvalue()
+            assert expect == actual, (expect, actual)
+
+            sys.stdout = StringIO.StringIO()
+            # The following call should work here:
+            #    print_tree(node, get_children, 1, showtags=1)
+            # For some reason I don't understand, though, *this*
+            # time that we call print_tree, the visited dictionary
+            # is still populated with the values from the last call!
+            # I can't see why this would be, short of a bug in Python,
+            # and rather than continue banging my head against the
+            # brick wall for a *test*, we're going to going with
+            # the cheap, easy workaround:
+            print_tree(node, get_children, 1, showtags=1, visited={})
+            actual = sys.stdout.getvalue()
+            assert withtags == actual, (withtags, actual)
+        finally:
+            sys.stdout = save_stdout
+
     def test_is_Dict(self):
         assert is_Dict({})
-        import UserDict
-        assert is_Dict(UserDict.UserDict())
+        assert is_Dict(UserDict())
         assert not is_Dict([])
+        assert not is_Dict(())
         assert not is_Dict("")
         if hasattr(types, 'UnicodeType'):
             exec "assert not is_Dict(u'')"
@@ -311,16 +215,12 @@ class UtilTestCase(unittest.TestCase):
         assert is_List([])
         import UserList
         assert is_List(UserList.UserList())
+        assert not is_List(())
         assert not is_List({})
         assert not is_List("")
         if hasattr(types, 'UnicodeType'):
             exec "assert not is_List(u'')"
 
-    def test_Split(self):
-        assert Split("foo bar") == ["foo", "bar"]
-        assert Split(["foo", "bar"]) == ["foo", "bar"]
-        assert Split("foo") == ["foo"]
-
     def test_is_String(self):
         assert is_String("")
         if hasattr(types, 'UnicodeType'):
@@ -333,6 +233,15 @@ class UtilTestCase(unittest.TestCase):
             assert is_String(UserString.UserString(''))
         assert not is_String({})
         assert not is_String([])
+        assert not is_String(())
+
+    def test_is_Tuple(self):
+        assert is_Tuple(())
+        assert not is_Tuple([])
+        assert not is_Tuple({})
+        assert not is_Tuple("")
+        if hasattr(types, 'UnicodeType'):
+            exec "assert not is_Tuple(u'')"
 
     def test_to_String(self):
         """Test the to_String() method."""
@@ -391,51 +300,106 @@ class UtilTestCase(unittest.TestCase):
 
         env_path = os.environ['PATH']
 
-        pathdirs_1234 = [ test.workpath('sub1'),
-                          test.workpath('sub2'),
-                          test.workpath('sub3'),
-                          test.workpath('sub4'),
-                        ] + string.split(env_path, os.pathsep)
-
-        pathdirs_1243 = [ test.workpath('sub1'),
-                          test.workpath('sub2'),
-                          test.workpath('sub4'),
-                          test.workpath('sub3'),
-                        ] + string.split(env_path, os.pathsep)
-
-        os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
-        wi = WhereIs('xxx.exe')
-        assert wi == test.workpath(sub3_xxx_exe), wi
-        wi = WhereIs('xxx.exe', pathdirs_1243)
-        assert wi == test.workpath(sub4_xxx_exe), wi
-        wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
-        assert wi == test.workpath(sub4_xxx_exe), wi
-
-        os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
-        wi = WhereIs('xxx.exe')
-        assert wi == test.workpath(sub4_xxx_exe), wi
-        wi = WhereIs('xxx.exe', pathdirs_1234)
-        assert wi == test.workpath(sub3_xxx_exe), wi
-        wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
-        assert wi == test.workpath(sub3_xxx_exe), wi
-
-       if sys.platform == 'win32':
-           wi = WhereIs('xxx', pathext = '')
-           assert wi is None, wi
-
-           wi = WhereIs('xxx', pathext = '.exe')
-           assert wi == test.workpath(sub4_xxx_exe), wi
-
-           wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
-           assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+        try:
+            pathdirs_1234 = [ test.workpath('sub1'),
+                              test.workpath('sub2'),
+                              test.workpath('sub3'),
+                              test.workpath('sub4'),
+                            ] + string.split(env_path, os.pathsep)
+
+            pathdirs_1243 = [ test.workpath('sub1'),
+                              test.workpath('sub2'),
+                              test.workpath('sub4'),
+                              test.workpath('sub3'),
+                            ] + string.split(env_path, os.pathsep)
+
+            os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
+            wi = WhereIs('xxx.exe')
+            assert wi == test.workpath(sub3_xxx_exe), wi
+            wi = WhereIs('xxx.exe', pathdirs_1243)
+            assert wi == test.workpath(sub4_xxx_exe), wi
+            wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
+            assert wi == test.workpath(sub4_xxx_exe), wi
+
+            wi = WhereIs('xxx.exe',reject = sub3_xxx_exe)
+            assert wi == test.workpath(sub4_xxx_exe), wi
+            wi = WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
+            assert wi == test.workpath(sub4_xxx_exe), wi
+
+            os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
+            wi = WhereIs('xxx.exe')
+            assert wi == test.workpath(sub4_xxx_exe), wi
+            wi = WhereIs('xxx.exe', pathdirs_1234)
+            assert wi == test.workpath(sub3_xxx_exe), wi
+            wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
+            assert wi == test.workpath(sub3_xxx_exe), wi
+
+            if sys.platform == 'win32':
+                wi = WhereIs('xxx', pathext = '')
+                assert wi is None, wi
+
+                wi = WhereIs('xxx', pathext = '.exe')
+                assert wi == test.workpath(sub4_xxx_exe), wi
+
+                wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
+                assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+
+                # Test that we return a normalized path even when
+                # the path contains forward slashes.
+                forward_slash = test.workpath('') + '/sub3'
+                wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
+                assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
+
+            del os.environ['PATH']
+            wi = WhereIs('xxx.exe')
+            assert wi is None, wi
+
+        finally:
+            os.environ['PATH'] = env_path
+            
+
+    def test_is_valid_construction_var(self):
+        """Testing is_valid_construction_var()"""
+        r = is_valid_construction_var("_a")
+        assert not r is None, r
+        r = is_valid_construction_var("z_")
+        assert not r is None, r
+        r = is_valid_construction_var("X_")
+        assert not r is None, r
+        r = is_valid_construction_var("2a")
+        assert r is None, r
+        r = is_valid_construction_var("a2_")
+        assert not r is 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
 
     def test_get_env_var(self):
         """Testing get_environment_var()."""
         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
+        assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
+        assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
+        assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
+        assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
 
     def test_Proxy(self):
         """Test generic Proxy class."""
@@ -462,43 +426,306 @@ class UtilTestCase(unittest.TestCase):
         s.baz = 6
 
         assert p.baz == 5, p.baz
+        assert p.get() == s, p.get()
+
+    def test_display(self):
+        old_stdout = sys.stdout
+        sys.stdout = OutBuffer()
+        display("line1")
+        display.set_mode(0)
+        display("line2")
+        display.set_mode(1)
+        display("line3")
+        display("line4\n", append_newline=0)
+        display.set_mode(0)
+        display("dont print1")
+        display("dont print2\n", append_newline=0)
+        display.set_mode(1)
+        assert sys.stdout.buffer == "line1\nline3\nline4\n"
+        sys.stdout = old_stdout
+
+    def test_get_native_path(self):
+        """Test the get_native_path() function."""
+        import tempfile
+        filename = tempfile.mktemp()
+        str = '1234567890 ' + filename
+        try:
+            open(filename, 'w').write(str)
+            assert open(get_native_path(filename)).read() == str
+        finally:
+            try:
+                os.unlink(filename)
+            except OSError:
+                pass
 
-    def test_Literal(self):
-        """Test the Literal() function."""
-        cmd_list = [ '$FOO', Literal('$BAR') ]
-        cmd_list = scons_subst_list(cmd_list,
-                                    { 'FOO' : 'BAZ',
-                                      'BAR' : 'BLAT' }, {})
-        def escape_func(cmd):
-            return '**' + cmd + '**'
-
-        map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
-        cmd_list = map(str, cmd_list[0])
-        assert cmd_list[0] == 'BAZ', cmd_list[0]
-        assert cmd_list[1] == '**$BAR**', cmd_list[1]
-
-    def test_mapPaths(self):
-        """Test the mapPaths function"""
-        fs = SCons.Node.FS.FS()
-        dir=fs.Dir('foo')
-        file=fs.File('bar/file')
-        
-        class DummyEnv:
-            def subst(self, arg):
-                return 'bar'
-
-        res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
-        assert res[0] == file, res[0]
-        assert res[1] == os.path.normpath('foo/baz'), res[1]
-        assert res[2] == os.path.normpath('foo/blat/boo'), res[2]
-        assert res[3] == '#test', res[3]
-
-        env=DummyEnv()
-        res=mapPaths('bleh', dir, env)
-        assert res[0] == os.path.normpath('foo/bar'), res[1]
+    def test_PrependPath(self):
+        """Test prepending to a path"""
+        p1 = r'C:\dir\num\one;C:\dir\num\two'
+        p2 = r'C:\mydir\num\one;C:\mydir\num\two'
+        # have to include the pathsep here so that the test will work on UNIX too.
+        p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';')
+        p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
+        p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
+        p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
+        assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
+        assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
+
+    def test_AppendPath(self):
+        """Test appending to a path."""
+        p1 = r'C:\dir\num\one;C:\dir\num\two'
+        p2 = r'C:\mydir\num\one;C:\mydir\num\two'
+        # have to include the pathsep here so that the test will work on UNIX too.
+        p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';')
+        p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
+        p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
+        p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
+        assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
+        assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
+
+    def test_NodeList(self):
+        """Test NodeList class"""
+        class TestClass:
+            def __init__(self, name, child=None):
+                self.child = child
+                self.bar = name
+            def foo(self):
+                return self.bar + "foo"
+            def getself(self):
+                return self
+
+        t1 = TestClass('t1', TestClass('t1child'))
+        t2 = TestClass('t2', TestClass('t2child'))
+        t3 = TestClass('t3')
+
+        nl = NodeList([t1, t2, t3])
+        assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
+        assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
+        assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
+        assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
+               nl[0:2].child.foo()
+        assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
+               nl[0:2].child.bar
+
+    def test_CLVar(self):
+        """Test the command-line construction variable class"""
+        f = SCons.Util.CLVar('a b')
+
+        r = f + 'c d'
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + ' c d'
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + ['c d']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c d'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + [' c d']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', ' c d'], r.data
+        assert str(r) == 'a b  c d', str(r)
+
+        r = f + ['c', 'd']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + [' c', 'd']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', ' c', 'd'], r.data
+        assert str(r) == 'a b  c d', str(r)
+
+        f = SCons.Util.CLVar(['a b'])
+
+        r = f + 'c d'
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + ' c d'
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + ['c d']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a b', 'c d'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + [' c d']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a b', ' c d'], r.data
+        assert str(r) == 'a b  c d', str(r)
+
+        r = f + ['c', 'd']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + [' c', 'd']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a b', ' c', 'd'], r.data
+        assert str(r) == 'a b  c d', str(r)
+
+        f = SCons.Util.CLVar(['a', 'b'])
+
+        r = f + 'c d'
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + ' c d'
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + ['c d']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c d'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + [' c d']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', ' c d'], r.data
+        assert str(r) == 'a b  c d', str(r)
+
+        r = f + ['c', 'd']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', 'c', 'd'], r.data
+        assert str(r) == 'a b c d', str(r)
+
+        r = f + [' c', 'd']
+        assert isinstance(r, SCons.Util.CLVar), type(r)
+        assert r.data == ['a', 'b', ' c', 'd'], r.data
+        assert str(r) == 'a b  c d', str(r)
+
+    def test_Selector(self):
+        """Test the Selector class"""
+
+        s = Selector({'a' : 'AAA', 'b' : 'BBB'})
+        assert s['a'] == 'AAA', s['a']
+        assert s['b'] == 'BBB', s['b']
+        exc_caught = None
+        try:
+            x = s['c']
+        except KeyError:
+            exc_caught = 1
+        assert exc_caught, "should have caught a KeyError"
+        s['c'] = 'CCC'
+        assert s['c'] == 'CCC', s['c']
+
+        class DummyEnv(UserDict):
+            def subst(self, key):
+                if key[0] == '$':
+                    return self[key[1:]]
+                return key
+
+        env = DummyEnv()
+
+        s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
+        ret = s(env, [])
+        assert ret == None, ret
+        ret = s(env, ['foo.d'])
+        assert ret == 'DDD', ret
+        ret = s(env, ['bar.e'])
+        assert ret == 'EEE', ret
+        ret = s(env, ['bar.x'])
+        assert ret == None, ret
+        s[None] = 'XXX'
+        ret = s(env, ['bar.x'])
+        assert ret == 'XXX', ret
+
+        env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'})
+
+        s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'})
+        ret = s(env, ['foo.f'])
+        assert ret == 'FFF', ret
+        ret = s(env, ['bar.g'])
+        assert ret == 'GGG', ret
+
+    def test_adjustixes(self):
+        """Test the adjustixes() function"""
+        r = adjustixes('file', 'pre-', '-suf')
+        assert r == 'pre-file-suf', r
+        r = adjustixes('pre-file', 'pre-', '-suf')
+        assert r == 'pre-file-suf', r
+        r = adjustixes('file-suf', 'pre-', '-suf')
+        assert r == 'pre-file-suf', r
+        r = adjustixes('pre-file-suf', 'pre-', '-suf')
+        assert r == 'pre-file-suf', r
+        r = adjustixes('pre-file.xxx', 'pre-', '-suf')
+        assert r == 'pre-file.xxx', r
+        r = adjustixes('dir/file', 'pre-', '-suf')
+        assert r == os.path.join('dir', 'pre-file-suf'), r
+
+    def test_containsAny(self):
+        """Test the containsAny() function"""
+        assert containsAny('*.py', '*?[]')
+        assert not containsAny('file.txt', '*?[]')
+
+    def test_containsAll(self):
+        """Test the containsAll() function"""
+        assert containsAll('43221', '123')
+        assert not containsAll('134', '123')
+
+    def test_containsOnly(self):
+        """Test the containsOnly() function"""
+        assert containsOnly('.83', '0123456789.')
+        assert not containsOnly('43221', '123')
+
+    def test_LogicalLines(self):
+        """Test the LogicalLines class"""
+        fobj = StringIO.StringIO(r"""
+foo \
+bar \
+baz
+foo
+bling \
+bling \ bling
+bling
+""")
+
+        lines = LogicalLines(fobj).readlines()
+        assert lines == [
+            '\n',
+            'foo bar baz\n',
+            'foo\n',
+            'bling bling \\ bling\n',
+            'bling\n',
+        ], lines
+
+class MD5TestCase(unittest.TestCase):
+
+    def test_collect(self):
+        """Test collecting a list of signatures into a new signature value
+        """
+        s = map(MD5signature, ('111', '222', '333'))
         
+        assert '698d51a19d8a121ce581499d7b701668' == MD5collect(s[0:1])
+        assert '8980c988edc2c78cc43ccb718c06efd5' == MD5collect(s[0:2])
+        assert '53fd88c84ff8a285eb6e0a687e55b8c7' == MD5collect(s)
+
+    def test_MD5signature(self):
+        """Test generating a signature"""
+        s = MD5signature('111')
+        assert '698d51a19d8a121ce581499d7b701668' == s, s
+
+        s = MD5signature('222')
+        assert 'bcbe3365e6ac95ea2c0343a2395834dd' == s, s
 
 if __name__ == "__main__":
-    suite = unittest.makeSuite(UtilTestCase, 'test_')
+    suite = unittest.TestSuite()
+    tclasses = [ dictifyTestCase,
+                 MD5TestCase,
+                 UtilTestCase,
+               ]
+    for tclass in tclasses:
+        names = unittest.getTestCaseNames(tclass, 'test_')
+        suite.addTests(map(tclass, names))
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
-       sys.exit(1)
+        sys.exit(1)