http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / engine / SCons / ActionTests.py
index 06030e3bb4a9575b9b9a6a2e8d4b437108a8331f..052582bd300b009be562383789ee5e483a77f73d 100644 (file)
@@ -37,7 +37,6 @@ class GlobalActFunc:
 import os
 import re
 import StringIO
-import string
 import sys
 import types
 import unittest
@@ -68,16 +67,16 @@ try:
 except:
     pass
 f.close()
-if os.environ.has_key( 'ACTPY_PIPE' ):
-    if os.environ.has_key( 'PIPE_STDOUT_FILE' ):
+if 'ACTPY_PIPE' in os.environ:
+    if 'PIPE_STDOUT_FILE' in os.environ:
          stdout_msg = open(os.environ['PIPE_STDOUT_FILE'], 'r').read()
     else:
-         stdout_msg = "act.py: stdout: executed act.py %s\\n" % string.join(sys.argv[1:])
+         stdout_msg = "act.py: stdout: executed act.py %s\\n" % ' '.join(sys.argv[1:])
     sys.stdout.write( stdout_msg )
-    if os.environ.has_key( 'PIPE_STDERR_FILE' ):
+    if 'PIPE_STDERR_FILE' in os.environ:
          stderr_msg = open(os.environ['PIPE_STDERR_FILE'], 'r').read()
     else:
-         stderr_msg = "act.py: stderr: executed act.py %s\\n" % string.join(sys.argv[1:])
+         stderr_msg = "act.py: stderr: executed act.py %s\\n" % ' '.join(sys.argv[1:])
     sys.stderr.write( stderr_msg )
 sys.exit(0)
 """)
@@ -147,7 +146,7 @@ class Environment:
     def __setitem__(self, item, value):
         self.d[item] = value
     def has_key(self, item):
-        return self.d.has_key(item)
+        return item in self.d
     def get(self, key, value=None):
         return self.d.get(key, value)
     def items(self):
@@ -172,6 +171,8 @@ class Environment:
 class DummyNode:
     def __init__(self, name):
         self.name = name
+    def str_for_display(self):
+        return '"' + self.name + '"'
     def __str__(self):
         return self.name
     def rfile(self):
@@ -186,6 +187,97 @@ else:
 
 _python_ = '"' + python + '"'
 
+_null = SCons.Action._null
+
+def test_varlist(pos_call, str_call, cmd, cmdstrfunc, **kw):
+    def call_action(a, pos_call=pos_call, str_call=str_call, kw=kw):
+        #FUTURE a = SCons.Action.Action(*a, **kw)
+        a = SCons.Action.Action(*a, **kw)
+        # returned object must provide these entry points
+        assert hasattr(a, '__call__')
+        assert hasattr(a, 'get_contents')
+        assert hasattr(a, 'genstring')
+        pos_call(a)
+        str_call(a)
+        return a
+
+    a = call_action((cmd, cmdstrfunc))
+    assert a.varlist == (), a.varlist
+
+    a = call_action((cmd, cmdstrfunc, 'foo'))
+    assert a.varlist == ('foo',), a.varlist
+
+    a = call_action((cmd, cmdstrfunc, 'a', 'b', 'c'))
+    assert a.varlist == ('a', 'b', 'c'), a.varlist
+
+    kw['varlist'] = 'foo'
+    a = call_action((cmd, cmdstrfunc))
+    assert a.varlist == ('foo',), a.varlist
+
+    kw['varlist'] = ['x', 'y', 'z']
+    a = call_action((cmd, cmdstrfunc))
+    assert a.varlist == ('x', 'y', 'z'), a.varlist
+
+    a = call_action((cmd, cmdstrfunc, 'foo'))
+    assert a.varlist == ('foo', 'x', 'y', 'z'), a.varlist
+
+    a = call_action((cmd, cmdstrfunc, 'a', 'b', 'c'))
+    assert a.varlist == ('a', 'b', 'c', 'x', 'y', 'z'), a.varlist
+
+def test_positional_args(pos_callback, cmd, **kw):
+    """Test that Action() returns the expected type and that positional args work.
+    """
+    #FUTURE act = SCons.Action.Action(cmd, **kw)
+    act = SCons.Action.Action(cmd, **kw)
+    pos_callback(act)
+    assert act.varlist is (), act.varlist
+
+    if not isinstance(act, SCons.Action._ActionAction):
+        # only valid cmdstrfunc is None
+        def none(a): pass
+        #FUTURE test_varlist(pos_callback, none, cmd, None, **kw)
+        test_varlist(pos_callback, none, cmd, None, **kw)
+    else:
+        # _ActionAction should have set these
+        assert hasattr(act, 'strfunction')
+        assert act.cmdstr is _null, act.cmdstr
+        assert act.presub is _null, act.presub
+        assert act.chdir is None, act.chdir
+        assert act.exitstatfunc is SCons.Action.default_exitstatfunc, \
+                                        act.exitstatfunc
+
+        def cmdstr(a):
+            assert hasattr(a, 'strfunction')
+            assert a.cmdstr == 'cmdstr', a.cmdstr
+        #FUTURE test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw)
+        test_varlist(pos_callback, cmdstr, cmd, 'cmdstr', **kw)
+
+        def fun(): pass
+        def strfun(a, fun=fun):
+            assert a.strfunction is fun, a.strfunction
+            assert a.cmdstr == _null, a.cmdstr
+        #FUTURE test_varlist(pos_callback, strfun, cmd, fun, **kw)
+        test_varlist(pos_callback, strfun, cmd, fun, **kw)
+
+        def none(a):
+            assert hasattr(a, 'strfunction')
+            assert a.cmdstr is None, a.cmdstr
+        #FUTURE test_varlist(pos_callback, none, cmd, None, **kw)
+        test_varlist(pos_callback, none, cmd, None, **kw)
+
+        """Test handling of bad cmdstrfunc arguments """
+        try:
+            #FUTURE a = SCons.Action.Action(cmd, [], **kw)
+            a = SCons.Action.Action(cmd, [], **kw)
+        except SCons.Errors.UserError, e:
+            s = str(e)
+            m = 'Invalid command display variable'
+            assert s.find(m) != -1, 'Unexpected string:  %s' % s
+        else:
+            raise Exception, "did not catch expected UserError"
+
+    return act
+
 class ActionTestCase(unittest.TestCase):
     """Test the Action() factory function"""
 
@@ -194,49 +286,41 @@ class ActionTestCase(unittest.TestCase):
         """
         def foo():
             pass
-        def bar():
-            pass
-        a1 = SCons.Action.Action(foo)
-        assert isinstance(a1, SCons.Action.FunctionAction), a1
-        assert a1.execfunction == foo, a1.execfunction
 
-        a11 = SCons.Action.Action(foo, strfunction=bar)
-        assert isinstance(a11, SCons.Action.FunctionAction), a11
-        assert a11.execfunction == foo, a11.execfunction
-        assert a11.strfunction == bar, a11.strfunction
+        def func_action(a, foo=foo):
+            assert isinstance(a, SCons.Action.FunctionAction), a
+            assert a.execfunction == foo, a.execfunction
+        test_positional_args(func_action, foo)
+        # a singleton list returns the contained action
+        test_positional_args(func_action, [foo])
 
     def test_CommandAction(self):
         """Test the Action() factory's creation of CommandAction objects
         """
-        a1 = SCons.Action.Action("string")
-        assert isinstance(a1, SCons.Action.CommandAction), a1
-        assert a1.cmd_list == "string", a1.cmd_list
-
-        if hasattr(types, 'UnicodeType'):
-            exec "a2 = SCons.Action.Action(u'string')"
-            exec "assert isinstance(a2, SCons.Action.CommandAction), a2"
-
-        a3 = SCons.Action.Action(["a3"])
-        assert isinstance(a3, SCons.Action.CommandAction), a3
-        assert a3.cmd_list == "a3", a3.cmd_list
-
-        a4 = SCons.Action.Action([[ "explicit", "command", "line" ]])
-        assert isinstance(a4, SCons.Action.CommandAction), a4
-        assert a4.cmd_list == [ "explicit", "command", "line" ], a4.cmd_list
-
-        def foo():
-            pass
+        def cmd_action(a):
+            assert isinstance(a, SCons.Action.CommandAction), a
+            assert a.cmd_list == "string", a.cmd_list
+        test_positional_args(cmd_action, "string")
+        # a singleton list returns the contained action
+        test_positional_args(cmd_action, ["string"])
+
+        try: unicode
+        except NameError: pass
+        else:
+            a2 = eval("SCons.Action.Action(u'string')")
+            assert isinstance(a2, SCons.Action.CommandAction), a2
 
-        a5 = SCons.Action.Action("string", strfunction=foo)
-        assert isinstance(a5, SCons.Action.CommandAction), a5
-        assert a5.cmd_list == "string", a5.cmd_list
-        assert a5.strfunction == foo, a5.strfunction
+        def line_action(a):
+            assert isinstance(a, SCons.Action.CommandAction), a
+            assert a.cmd_list == [ "explicit", "command", "line" ], a.cmd_list
+        test_positional_args(line_action, [[ "explicit", "command", "line" ]])
 
     def test_ListAction(self):
         """Test the Action() factory's creation of ListAction objects
         """
         a1 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]])
         assert isinstance(a1, SCons.Action.ListAction), a1
+        assert a1.varlist is (), a1.varlist
         assert isinstance(a1.list[0], SCons.Action.CommandAction), a1.list[0]
         assert a1.list[0].cmd_list == "x", a1.list[0].cmd_list
         assert isinstance(a1.list[1], SCons.Action.CommandAction), a1.list[1]
@@ -248,6 +332,7 @@ class ActionTestCase(unittest.TestCase):
 
         a2 = SCons.Action.Action("x\ny\nz")
         assert isinstance(a2, SCons.Action.ListAction), a2
+        assert a2.varlist is (), a2.varlist
         assert isinstance(a2.list[0], SCons.Action.CommandAction), a2.list[0]
         assert a2.list[0].cmd_list == "x", a2.list[0].cmd_list
         assert isinstance(a2.list[1], SCons.Action.CommandAction), a2.list[1]
@@ -260,6 +345,7 @@ class ActionTestCase(unittest.TestCase):
 
         a3 = SCons.Action.Action(["x", foo, "z"])
         assert isinstance(a3, SCons.Action.ListAction), a3
+        assert a3.varlist is (), a3.varlist
         assert isinstance(a3.list[0], SCons.Action.CommandAction), a3.list[0]
         assert a3.list[0].cmd_list == "x", a3.list[0].cmd_list
         assert isinstance(a3.list[1], SCons.Action.FunctionAction), a3.list[1]
@@ -269,6 +355,7 @@ class ActionTestCase(unittest.TestCase):
 
         a4 = SCons.Action.Action(["x", "y"], strfunction=foo)
         assert isinstance(a4, SCons.Action.ListAction), a4
+        assert a4.varlist is (), a4.varlist
         assert isinstance(a4.list[0], SCons.Action.CommandAction), a4.list[0]
         assert a4.list[0].cmd_list == "x", a4.list[0].cmd_list
         assert a4.list[0].strfunction == foo, a4.list[0].strfunction
@@ -278,6 +365,7 @@ class ActionTestCase(unittest.TestCase):
 
         a5 = SCons.Action.Action("x\ny", strfunction=foo)
         assert isinstance(a5, SCons.Action.ListAction), a5
+        assert a5.varlist is (), a5.varlist
         assert isinstance(a5.list[0], SCons.Action.CommandAction), a5.list[0]
         assert a5.list[0].cmd_list == "x", a5.list[0].cmd_list
         assert a5.list[0].strfunction == foo, a5.list[0].strfunction
@@ -288,29 +376,22 @@ class ActionTestCase(unittest.TestCase):
     def test_CommandGeneratorAction(self):
         """Test the Action() factory's creation of CommandGeneratorAction objects
         """
-        def foo():
-            pass
-        def bar():
-            pass
-        a1 = SCons.Action.Action(foo, generator=1)
-        assert isinstance(a1, SCons.Action.CommandGeneratorAction), a1
-        assert a1.generator is foo, a1.generator
+        def foo(): pass
 
-        a2 = SCons.Action.Action(foo, strfunction=bar, generator=1)
-        assert isinstance(a2, SCons.Action.CommandGeneratorAction), a2
-        assert a2.generator is foo, a2.generator
+        def gen_action(a, foo=foo):
+            assert isinstance(a, SCons.Action.CommandGeneratorAction), a
+            assert a.generator is foo, a.generator
+        test_positional_args(gen_action, foo, generator=1)
 
     def test_LazyCmdGeneratorAction(self):
         """Test the Action() factory's creation of lazy CommandGeneratorAction objects
         """
-        def foo():
-            pass
-
-        a1 = SCons.Action.Action("$FOO")
-        assert isinstance(a1, SCons.Action.LazyAction), a1
-
-        a2 = SCons.Action.Action("$FOO", strfunction=foo)
-        assert isinstance(a2, SCons.Action.LazyAction), a2
+        def lazy_action(a):
+            assert isinstance(a, SCons.Action.LazyAction), a
+            assert a.var == "FOO", a.var
+            assert a.cmd_list == "${FOO}", a.cmd_list
+        test_positional_args(lazy_action, "$FOO")
+        test_positional_args(lazy_action, "${FOO}")
 
     def test_no_action(self):
         """Test when the Action() factory can't create an action object
@@ -325,13 +406,6 @@ class ActionTestCase(unittest.TestCase):
         a2 = SCons.Action.Action(a1)
         assert a2 is a1, a2
 
-class ActionBaseTestCase(unittest.TestCase):
-    def test_get_executor(self):
-        """Test the ActionBase.get_executor() method"""
-        a = SCons.Action.Action('foo')
-        x = a.get_executor({}, {}, [], [], {})
-        assert not x is None, x
 class _ActionActionTestCase(unittest.TestCase):
 
     def test__init__(self):
@@ -344,26 +418,82 @@ class _ActionActionTestCase(unittest.TestCase):
         def func2():
             pass
 
+        def func3():
+            pass
+
         a = SCons.Action._ActionAction()
         assert not hasattr(a, 'strfunction')
+        assert a.cmdstr is _null, a.cmdstr
+        assert a.varlist == (), a.varlist
+        assert a.presub is _null, a.presub
+        assert a.chdir is None, a.chdir
+        assert a.exitstatfunc is SCons.Action.default_exitstatfunc, a.exitstatfunc
 
         assert SCons.Action._ActionAction(kwarg = 1)
-        assert not hasattr(a, 'strfunction')
         assert not hasattr(a, 'kwarg')
+        assert not hasattr(a, 'strfunction')
+        assert a.cmdstr is _null, a.cmdstr
+        assert a.varlist == (), a.varlist
+        assert a.presub is _null, a.presub
+        assert a.chdir is None, a.chdir
+        assert a.exitstatfunc is SCons.Action.default_exitstatfunc, a.exitstatfunc
 
         a = SCons.Action._ActionAction(strfunction=func1)
         assert a.strfunction is func1, a.strfunction
 
+        a = SCons.Action._ActionAction(strfunction=None)
+        assert not hasattr(a, 'strfunction')
+        assert a.cmdstr is None, a.cmdstr
+
+        a = SCons.Action._ActionAction(cmdstr='cmdstr')
+        assert not hasattr(a, 'strfunction')
+        assert a.cmdstr is 'cmdstr', a.cmdstr
+
+        a = SCons.Action._ActionAction(cmdstr=None)
+        assert not hasattr(a, 'strfunction')
+        assert a.cmdstr is None, a.cmdstr
+
+        t = ('a','b','c')
+        a = SCons.Action._ActionAction(varlist=t)
+        assert a.varlist == t, a.varlist
+
         a = SCons.Action._ActionAction(presub=func1)
         assert a.presub is func1, a.presub
 
         a = SCons.Action._ActionAction(chdir=1)
         assert a.chdir is 1, a.chdir
 
-        a = SCons.Action._ActionAction(func1, func2, 'x')
-        assert a.strfunction is func1, a.strfunction
-        assert a.presub is func2, a.presub
+        a = SCons.Action._ActionAction(exitstatfunc=func1)
+        assert a.exitstatfunc is func1, a.exitstatfunc
+
+        a = SCons.Action._ActionAction(
+                                 # alphabetical order ...
+                                 chdir='x',
+                                 cmdstr='cmdstr',
+                                 exitstatfunc=func3,
+                                 presub=func2,
+                                 strfunction=func1,
+                                 varlist=t,
+                          )
         assert a.chdir is 'x', a.chdir
+        assert a.cmdstr is 'cmdstr', a.cmdstr
+        assert a.exitstatfunc is func3, a.exitstatfunc
+        assert a.presub is func2, a.presub
+        assert a.strfunction is func1, a.strfunction
+        assert a.varlist is t, a.varlist
+
+    def test_dup_keywords(self):
+        """Test handling of both cmdstr and strfunction arguments
+        """
+        def func(): pass
+        try:
+            a = SCons.Action.Action('foo', cmdstr='string', strfunction=func)
+        except SCons.Errors.UserError, e:
+            s = str(e)
+            m = 'Cannot have both strfunction and cmdstr args to Action()'
+            assert s.find(m) != -1, 'Unexpected string:  %s' % s
+        else:
+            raise Exception, "did not catch expected UserError"
 
     def test___cmp__(self):
         """Test Action comparison
@@ -412,18 +542,18 @@ class _ActionActionTestCase(unittest.TestCase):
             env = Environment()
 
             def execfunc(target, source, env):
-                assert type(target) is type([]), type(target)
-                assert type(source) is type([]), type(source)
+                assert isinstance(target, list), type(target)
+                assert isinstance(source, list), type(source)
                 return 7
             a = SCons.Action.Action(execfunc)
 
             def firstfunc(target, source, env):
-                assert type(target) is type([]), type(target)
-                assert type(source) is type([]), type(source)
+                assert isinstance(target, list), type(target)
+                assert isinstance(source, list), type(source)
                 return 0
             def lastfunc(target, source, env):
-                assert type(target) is type([]), type(target)
-                assert type(source) is type([]), type(source)
+                assert isinstance(target, list), type(target)
+                assert isinstance(source, list), type(source)
                 return 9
             b = SCons.Action.Action([firstfunc, execfunc, lastfunc])
             
@@ -432,10 +562,10 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a("out", "in", env)
             assert result.status == 7, result
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             a.chdir = 'xyz'
-            expect = 'os.chdir(%s)\nexecfunc(["out"], ["in"])\nos.chdir(%s)\n'
+            expect = "os.chdir(%s)\nexecfunc(['out'], ['in'])\nos.chdir(%s)\n"
 
             sio = StringIO.StringIO()
             sys.stdout = sio
@@ -458,7 +588,7 @@ class _ActionActionTestCase(unittest.TestCase):
             result = b("out", "in", env)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'firstfunc(["out"], ["in"])\nexecfunc(["out"], ["in"])\n', s
+            assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\n", s
 
             SCons.Action.execute_actions = 0
 
@@ -467,14 +597,14 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a("out", "in", env)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = b("out", "in", env)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'firstfunc(["out"], ["in"])\nexecfunc(["out"], ["in"])\nlastfunc(["out"], ["in"])\n', s
+            assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\nlastfunc(['out'], ['in'])\n", s
 
             SCons.Action.print_actions_presub = 1
             SCons.Action.execute_actions = 1
@@ -484,35 +614,35 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a("out", "in", env)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a("out", "in", env, presub=0)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a("out", "in", env, presub=1)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = b(["out"], "in", env, presub=1)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  firstfunc(target, source, env)\nfirstfunc(["out"], ["in"])\nBuilding out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  firstfunc(target, source, env)\nfirstfunc(['out'], ['in'])\nBuilding out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = b(["out", "list"], "in", env, presub=1)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out and list with action:\n  firstfunc(target, source, env)\nfirstfunc(["out", "list"], ["in"])\nBuilding out and list with action:\n  execfunc(target, source, env)\nexecfunc(["out", "list"], ["in"])\n', s
+            assert s == "Building out and list with action:\n  firstfunc(target, source, env)\nfirstfunc(['out', 'list'], ['in'])\nBuilding out and list with action:\n  execfunc(target, source, env)\nexecfunc(['out', 'list'], ['in'])\n", s
 
             a2 = SCons.Action.Action(execfunc)
 
@@ -521,14 +651,14 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a2("out", "in", env)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'Building out with action:\n  execfunc(target, source, env)\nexecfunc(["out"], ["in"])\n', s
+            assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
             result = a2("out", "in", env, presub=0)
             assert result.status == 7, result.status
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             SCons.Action.execute_actions = 0
 
@@ -537,7 +667,7 @@ class _ActionActionTestCase(unittest.TestCase):
             result = a2("out", "in", env, presub=0)
             assert result == 0, result
             s = sio.getvalue()
-            assert s == 'execfunc(["out"], ["in"])\n', s
+            assert s == "execfunc(['out'], ['in'])\n", s
 
             sio = StringIO.StringIO()
             sys.stdout = sio
@@ -568,7 +698,7 @@ class _ActionActionTestCase(unittest.TestCase):
                 result.append(s)
             env['PRINT_CMD_LINE_FUNC'] = my_print_cmd_line
             a("output", "input", env)
-            assert result == ['execfunc(["output"], ["input"])'], result
+            assert result == ["execfunc(['output'], ['input'])"], result
             
 
         finally:
@@ -623,8 +753,7 @@ class _ActionActionTestCase(unittest.TestCase):
         sum = act1 + act2
         assert isinstance(sum, SCons.Action.ListAction), str(sum)
         assert len(sum.list) == 3, len(sum.list)
-        assert map(lambda x: isinstance(x, SCons.Action.ActionBase),
-                   sum.list) == [ 1, 1, 1 ]
+        assert [isinstance(x, SCons.Action.ActionBase) for x in sum.list] == [ 1, 1, 1 ]
 
         sum = act1 + act1
         assert isinstance(sum, SCons.Action.ListAction), str(sum)
@@ -693,24 +822,12 @@ class CommandActionTestCase(unittest.TestCase):
         """
         a = SCons.Action.CommandAction(["xyzzy"])
         assert a.cmd_list == [ "xyzzy" ], a.cmd_list
-        assert a.cmdstr is None, a.cmdstr
+        assert a.cmdstr is _null, a.cmdstr
 
-        a = SCons.Action.CommandAction(["abra"], "cadabra")
+        a = SCons.Action.CommandAction(["abra"], cmdstr="cadabra")
         assert a.cmd_list == [ "abra" ], a.cmd_list
         assert a.cmdstr == "cadabra", a.cmdstr
 
-    def test_bad_cmdstr(self):
-        """Test handling of bad CommandAction(cmdstr) arguments
-        """
-        try:
-            a = SCons.Action.CommandAction('foo', [])
-        except SCons.Errors.UserError, e:
-            s = str(e)
-            m = 'Invalid command display variable'
-            assert string.find(s, m) != -1, 'Unexpected string:  %s' % s
-        else:
-            raise Exception, "did not catch expected UserError"
-
     def test___str__(self):
         """Test fetching the pre-substitution string for command Actions
         """
@@ -781,7 +898,7 @@ class CommandActionTestCase(unittest.TestCase):
         assert s == 'xyzzy t1 s1', s
 
         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE',
-                                         'cmdstr - $SOURCE - $TARGET -')
+                                         cmdstr='cmdstr - $SOURCE - $TARGET -')
         s = act.strfunction([], [], env)
         assert s == 'cmdstr -  -  -', s
         s = act.strfunction([t1], [s1], env)
@@ -798,7 +915,7 @@ class CommandActionTestCase(unittest.TestCase):
         assert s == 'xyzzy t1 t2 s1 s2', s
 
         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES',
-                                         'cmdstr = $SOURCES = $TARGETS =')
+                                         cmdstr='cmdstr = $SOURCES = $TARGETS =')
         s = act.strfunction([], [], env)
         assert s == 'cmdstr =  =  =', s
         s = act.strfunction([t1], [s1], env)
@@ -817,7 +934,7 @@ class CommandActionTestCase(unittest.TestCase):
         assert s == 'xyzzy t1 s1 t1 t2 s1 s2', s
 
         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES',
-                                         'cmdstr\t$TARGETS\n$SOURCES   ')
+                                         cmdstr='cmdstr\t$TARGETS\n$SOURCES   ')
                     
         s = act.strfunction([], [], env)
         assert s == 'cmdstr\t\n   ', s
@@ -933,7 +1050,7 @@ class CommandActionTestCase(unittest.TestCase):
         cmd3 = r'%s %s %s ${TARGETS}' % (_python_, act_py, outfile)
 
         act = SCons.Action.CommandAction(cmd3)
-        r = act(map(DummyNode, ['aaa', 'bbb']), [], env.Clone())
+        r = act(list(map(DummyNode, ['aaa', 'bbb'])), [], env.Clone())
         assert r == 0
         c = test.read(outfile, 'r')
         assert c == "act.py: 'aaa' 'bbb'\n", c
@@ -960,7 +1077,7 @@ class CommandActionTestCase(unittest.TestCase):
 
         act = SCons.Action.CommandAction(cmd5)
         env5 = Environment()
-        if scons_env.has_key('ENV'):
+        if 'ENV' in scons_env:
             env5['ENV'] = scons_env['ENV']
             PATH = scons_env['ENV'].get('PATH', '')
         else:
@@ -1003,13 +1120,17 @@ class CommandActionTestCase(unittest.TestCase):
             # NT treats execs of directories and non-executable files
             # as "file not found" errors
             expect_nonexistent = 1
-            expect_nonexecutable = 1
+            expect_nonexecutable_file = 1
+            expect_nonexecutable_dir = 1
         elif sys.platform == 'cygwin':
             expect_nonexistent = 127
-            expect_nonexecutable = 127
+            # Newer cygwin seems to return 126 for following
+            expect_nonexecutable_file = 126
+            expect_nonexecutable_dir  = 127
         else:
             expect_nonexistent = 127
-            expect_nonexecutable = 126
+            expect_nonexecutable_file = 126
+            expect_nonexecutable_dir  = 126
 
         # Test that a nonexistent command returns 127
         act = SCons.Action.CommandAction(python + "_no_such_command_")
@@ -1020,12 +1141,12 @@ class CommandActionTestCase(unittest.TestCase):
         dir, tail = os.path.split(python)
         act = SCons.Action.CommandAction(dir)
         r = act([], [], env.Clone(out = outfile))
-        assert r.status == expect_nonexecutable, r.status
+        assert r.status == expect_nonexecutable_file, r.status
 
         # Test that trying to execute a non-executable file returns 126
         act = SCons.Action.CommandAction(outfile)
         r = act([], [], env.Clone(out = outfile))
-        assert r.status == expect_nonexecutable, r.status
+        assert r.status == expect_nonexecutable_dir, r.status
 
         act = SCons.Action.CommandAction('%s %s 1' % (_python_, exit_py))
         r = act([], [], env)
@@ -1211,8 +1332,8 @@ class CommandActionTestCase(unittest.TestCase):
         # that scheme, then all of the '__t1__' and '__s6__' file names
         # in the asserts below would change to 't1' and 's6' and the
         # like.
-        t = map(DummyNode, ['t1', 't2', 't3', 't4', 't5', 't6'])
-        s = map(DummyNode, ['s1', 's2', 's3', 's4', 's5', 's6'])
+        t = list(map(DummyNode, ['t1', 't2', 't3', 't4', 't5', 't6']))
+        s = list(map(DummyNode, ['s1', 's2', 's3', 's4', 's5', 's6']))
         env = Environment()
 
         a = SCons.Action.CommandAction(["$TARGET"])
@@ -1249,12 +1370,16 @@ class CommandActionTestCase(unittest.TestCase):
 
 class CommandGeneratorActionTestCase(unittest.TestCase):
 
+    def factory(self, act, **kw):
+        """Pass any keywords as a dict"""
+        return SCons.Action.CommandGeneratorAction(act, kw)
+
     def test___init__(self):
         """Test creation of a command generator Action
         """
         def f(target, source, env):
             pass
-        a = SCons.Action.CommandGeneratorAction(f)
+        a = self.factory(f)
         assert a.generator == f
 
     def test___str__(self):
@@ -1270,7 +1395,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
 
             env.FindIxes
             return "FOO"
-        a = SCons.Action.CommandGeneratorAction(f)
+        a = self.factory(f)
         s = str(a)
         assert s == 'FOO', s
 
@@ -1281,7 +1406,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
             dummy = env['dummy']
             self.dummy = dummy
             return "$FOO $TARGET $SOURCE $TARGETS $SOURCES"
-        a = SCons.Action.CommandGeneratorAction(f)
+        a = self.factory(f)
         self.dummy = 0
         s = a.genstring([], [], env=Environment(FOO='xyzzy', dummy=1))
         assert self.dummy == 1, self.dummy
@@ -1308,7 +1433,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
             self.cmd.append(cmd)
             self.args.append(args)
 
-        a = SCons.Action.CommandGeneratorAction(f)
+        a = self.factory(f)
         self.dummy = 0
         self.cmd = []
         self.args = []
@@ -1319,7 +1444,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
         assert self.cmd == ['foo', 'bar'], self.cmd
         assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
 
-        b = SCons.Action.CommandGeneratorAction(f2)
+        b = self.factory(f2)
         self.dummy = 0
         b(target=[], source=[], env=Environment(foo =  'bar',
                                                         dummy =  2 ))
@@ -1336,7 +1461,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
                 return self
         def f3(target, source, env, for_signature):
             return ''
-        c = SCons.Action.CommandGeneratorAction(f3)
+        c = self.factory(f3)
         c(target=[], source=DummyFile(self), env=Environment())
         assert self.rfile_called
 
@@ -1356,10 +1481,63 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
 
         env = Environment(foo = 'FFF', bar =  'BBB',
                           ignore = 'foo', test=test)
-        a = SCons.Action.CommandGeneratorAction(f)
+        a = self.factory(f)
         c = a.get_contents(target=[], source=[], env=env)
         assert c == "guux FFF BBB test", c
 
+    def test_get_contents_of_function_action(self):
+        """Test contents of a CommandGeneratorAction-generated FunctionAction
+        """
+
+        def LocalFunc():
+            pass
+
+        func_matches = [
+            "0,0,0,0,(),(),(d\000\000S),(),()",
+            "0,0,0,0,(),(),(d\x00\x00S),(),()",
+            ]
+        
+        meth_matches = [
+            "1,1,0,0,(),(),(d\000\000S),(),()",
+            "1,1,0,0,(),(),(d\x00\x00S),(),()",
+        ]
+
+        def f_global(target, source, env, for_signature):
+            return SCons.Action.Action(GlobalFunc)
+
+        # TODO(1.5):
+        #def f_local(target, source, env, for_signature):
+        def f_local(target, source, env, for_signature, LocalFunc=LocalFunc):
+            return SCons.Action.Action(LocalFunc)
+
+        env = Environment(XYZ = 'foo')
+
+        a = self.factory(f_global)
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in func_matches, repr(c)
+
+        a = self.factory(f_local)
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in func_matches, repr(c)
+
+        def f_global(target, source, env, for_signature):
+            return SCons.Action.Action(GlobalFunc, varlist=['XYZ'])
+
+        # TODO(1.5):
+        #def f_local(target, source, env, for_signature):
+        def f_local(target, source, env, for_signature, LocalFunc=LocalFunc):
+            return SCons.Action.Action(LocalFunc, varlist=['XYZ'])
+
+        matches_foo = [x + "foo" for x in func_matches]
+
+        a = self.factory(f_global)
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in matches_foo, repr(c)
+
+        a = self.factory(f_local)
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in matches_foo, repr(c)
+
 
 class FunctionActionTestCase(unittest.TestCase):
 
@@ -1375,41 +1553,27 @@ class FunctionActionTestCase(unittest.TestCase):
         def func4():
             pass
 
-        a = SCons.Action.FunctionAction(func1)
+        a = SCons.Action.FunctionAction(func1, {})
         assert a.execfunction == func1, a.execfunction
         assert isinstance(a.strfunction, types.MethodType), type(a.strfunction)
 
-        a = SCons.Action.FunctionAction(func2, strfunction=func3)
+        a = SCons.Action.FunctionAction(func2, { 'strfunction' : func3 })
         assert a.execfunction == func2, a.execfunction
         assert a.strfunction == func3, a.strfunction
 
-    def test_cmdstr_bad(self):
-        """Test handling of bad FunctionAction(cmdstr) arguments
-        """
-        def func():
-            pass
-        try:
-            a = SCons.Action.FunctionAction(func, [])
-        except SCons.Errors.UserError, e:
-            s = str(e)
-            m = 'Invalid function display variable'
-            assert string.find(s, m) != -1, 'Unexpected string:  %s' % s
-        else:
-            raise "did not catch expected UserError"
-
     def test___str__(self):
         """Test the __str__() method for function Actions
         """
         def func1():
             pass
-        a = SCons.Action.FunctionAction(func1)
+        a = SCons.Action.FunctionAction(func1, {})
         s = str(a)
         assert s == "func1(target, source, env)", s
 
         class class1:
             def __call__(self):
                 pass
-        a = SCons.Action.FunctionAction(class1())
+        a = SCons.Action.FunctionAction(class1(), {})
         s = str(a)
         assert s == "class1(target, source, env)", s
 
@@ -1424,7 +1588,7 @@ class FunctionActionTestCase(unittest.TestCase):
             s.source=source
             assert env.subst("$BAR") == 'foo bar', env.subst("$BAR")
             return 0
-        a = SCons.Action.FunctionAction(f)
+        a = SCons.Action.FunctionAction(f, {})
         a(target=1, source=2, env=Environment(BAR = 'foo bar',
                                                       s = self))
         assert self.inc == 1, self.inc
@@ -1440,7 +1604,7 @@ class FunctionActionTestCase(unittest.TestCase):
                 open(t, 'w').write("function1\n")
             return 1
 
-        act = SCons.Action.FunctionAction(function1)
+        act = SCons.Action.FunctionAction(function1, {})
         r = act(target = [outfile, outfile2], source=[], env=Environment())
         assert r.status == 1, r.status
 
@@ -1454,7 +1618,7 @@ class FunctionActionTestCase(unittest.TestCase):
             def __init__(self, target, source, env):
                 open(env['out'], 'w').write("class1a\n")
 
-        act = SCons.Action.FunctionAction(class1a)
+        act = SCons.Action.FunctionAction(class1a, {})
         r = act([], [], Environment(out = outfile))
         assert isinstance(r.status, class1a), r.status
         c = test.read(outfile, 'r')
@@ -1465,19 +1629,20 @@ class FunctionActionTestCase(unittest.TestCase):
                 open(env['out'], 'w').write("class1b\n")
                 return 2
 
-        act = SCons.Action.FunctionAction(class1b())
+        act = SCons.Action.FunctionAction(class1b(), {})
         r = act([], [], Environment(out = outfile))
         assert r.status == 2, r.status
         c = test.read(outfile, 'r')
         assert c == "class1b\n", c
 
-        def build_it(target, source, env, self=self):
+        def build_it(target, source, env, executor=None, self=self):
             self.build_it = 1
             return 0
-        def string_it(target, source, env, self=self):
+        def string_it(target, source, env, executor=None, self=self):
             self.string_it = 1
             return None
-        act = SCons.Action.FunctionAction(build_it, strfunction=string_it)
+        act = SCons.Action.FunctionAction(build_it,
+                                          { 'strfunction' : string_it })
         r = act([], [], Environment())
         assert r == 0, r
         assert self.build_it
@@ -1490,32 +1655,47 @@ class FunctionActionTestCase(unittest.TestCase):
         def LocalFunc():
             pass
 
-        matches = [
-            "d\000\000S",
-            "d\x00\x00S",
+        func_matches = [
+            "0,0,0,0,(),(),(d\000\000S),(),()",
+            "0,0,0,0,(),(),(d\x00\x00S),(),()",
+            ]
+        
+        meth_matches = [
+            "1,1,0,0,(),(),(d\000\000S),(),()",
+            "1,1,0,0,(),(),(d\x00\x00S),(),()",
         ]
 
-        a = SCons.Action.FunctionAction(GlobalFunc)
+        def factory(act, **kw):
+            return SCons.Action.FunctionAction(act, kw)
+
+        a = factory(GlobalFunc)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
+        assert c in func_matches, repr(c)
 
-        a = SCons.Action.FunctionAction(LocalFunc)
+        a = factory(LocalFunc)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
+        assert c in func_matches, repr(c)
 
-        a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
+        matches_foo = [x + "foo" for x in func_matches]
 
-        matches_foo = map(lambda x: x + "foo", matches)
+        a = factory(GlobalFunc, varlist=['XYZ'])
+        c = a.get_contents(target=[], source=[], env=Environment())
+        assert c in func_matches, repr(c)
+        c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
+        assert c in matches_foo, repr(c)
 
+        ##TODO: is this set of tests still needed?
+        # Make sure a bare string varlist works
+        a = factory(GlobalFunc, varlist='XYZ')
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
-        c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo'))
+        assert c in func_matches, repr(c)
+        c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo'))
         assert c in matches_foo, repr(c)
 
         class Foo:
             def get_contents(self, target, source, env):
                 return 'xyzzy'
-        a = SCons.Action.FunctionAction(Foo())
+        a = factory(Foo())
         c = a.get_contents(target=[], source=[], env=Environment())
         assert c == 'xyzzy', repr(c)
 
@@ -1523,9 +1703,9 @@ class FunctionActionTestCase(unittest.TestCase):
             def LocalMethod(self):
                 pass
         lc = LocalClass()
-        a = SCons.Action.FunctionAction(lc.LocalMethod)
+        a = factory(lc.LocalMethod)
         c = a.get_contents(target=[], source=[], env=Environment())
-        assert c in matches, repr(c)
+        assert c in meth_matches, repr(c)
 
     def test_strfunction(self):
         """Test the FunctionAction.strfunction() method
@@ -1533,15 +1713,18 @@ class FunctionActionTestCase(unittest.TestCase):
         def func():
             pass
 
-        a = SCons.Action.FunctionAction(func)
+        def factory(act, **kw):
+            return SCons.Action.FunctionAction(act, kw)
+
+        a = factory(func)
         s = a.strfunction(target=[], source=[], env=Environment())
         assert s == 'func([], [])', s
 
-        a = SCons.Action.FunctionAction(func, None)
+        a = factory(func, strfunction=None)
         s = a.strfunction(target=[], source=[], env=Environment())
         assert s is None, s
 
-        a = SCons.Action.FunctionAction(func, 'function')
+        a = factory(func, cmdstr='function')
         s = a.strfunction(target=[], source=[], env=Environment())
         assert s == 'function', s
 
@@ -1678,6 +1861,47 @@ class LazyActionTestCase(unittest.TestCase):
         c = a.get_contents(target=[], source=[], env=env)
         assert c == "This is a test", c
 
+    def test_get_contents_of_function_action(self):
+        """Test fetching the contents of a lazy-evaluation FunctionAction
+        """
+
+        def LocalFunc():
+            pass
+
+        func_matches = [
+            "0,0,0,0,(),(),(d\000\000S),(),()",
+            "0,0,0,0,(),(),(d\x00\x00S),(),()",
+            ]
+        
+        meth_matches = [
+            "1,1,0,0,(),(),(d\000\000S),(),()",
+            "1,1,0,0,(),(),(d\x00\x00S),(),()",
+        ]
+
+        def factory(act, **kw):
+            return SCons.Action.FunctionAction(act, kw)
+
+
+        a = SCons.Action.Action("${FOO}")
+
+        env = Environment(FOO = factory(GlobalFunc))
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in func_matches, repr(c)
+
+        env = Environment(FOO = factory(LocalFunc))
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in func_matches, repr(c)
+
+        matches_foo = [x + "foo" for x in func_matches]
+
+        env = Environment(FOO = factory(GlobalFunc, varlist=['XYZ']))
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in func_matches, repr(c)
+
+        env['XYZ'] = 'foo'
+        c = a.get_contents(target=[], source=[], env=env)
+        assert c in matches_foo, repr(c)
+
 class ActionCallerTestCase(unittest.TestCase):
     def test___init__(self):
         """Test creation of an ActionCaller"""
@@ -1767,20 +1991,21 @@ class ActionCallerTestCase(unittest.TestCase):
     def test_strfunction(self):
         """Test calling the ActionCaller strfunction() method"""
         strfunc_args = []
-        def actfunc(a1, a2, a3):
+        def actfunc(a1, a2, a3, a4):
             pass
-        def strfunc(a1, a2, a3, args=strfunc_args):
-            args.extend([a1, a2, a3])
+        def strfunc(a1, a2, a3, a4, args=strfunc_args):
+            args.extend([a1, a2, a3, a4])
 
         af = SCons.Action.ActionFactory(actfunc, strfunc)
-        ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3], {})
-        ac.strfunction([], [], Environment(FOO = 2))
-        assert strfunc_args == [1, '2', 3], strfunc_args
+        ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3, '$WS'], {})
+        ac.strfunction([], [], Environment(FOO = 2, WS='white   space'))
+        assert strfunc_args == [1, '2', 3, 'white   space'], strfunc_args
 
         del strfunc_args[:]
-        ac = SCons.Action.ActionCaller(af, [], {'a3' : 6, 'a2' : '$BAR', 'a1' : 4})
-        ac.strfunction([], [], Environment(BAR = 5))
-        assert strfunc_args == [4, '5', 6], strfunc_args
+        d = {'a3' : 6, 'a2' : '$BAR', 'a1' : 4, 'a4' : '$WS'}
+        ac = SCons.Action.ActionCaller(af, [], d)
+        ac.strfunction([], [], Environment(BAR = 5, WS='w   s'))
+        assert strfunc_args == [4, '5', 6, 'w   s'], strfunc_args
 
 class ActionFactoryTestCase(unittest.TestCase):
     def test___init__(self):
@@ -1859,9 +2084,8 @@ class ActionCompareTestCase(unittest.TestCase):
 
 if __name__ == "__main__":
     suite = unittest.TestSuite()
-    tclasses = [ ActionTestCase,
-                 ActionBaseTestCase,
-                 _ActionActionTestCase,
+    tclasses = [ _ActionActionTestCase,
+                 ActionTestCase,
                  CommandActionTestCase,
                  CommandGeneratorActionTestCase,
                  FunctionActionTestCase,
@@ -1872,6 +2096,12 @@ if __name__ == "__main__":
                  ActionCompareTestCase ]
     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)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: