Evaluate compat module transparently when SCons is instantiated
[scons.git] / src / engine / SCons / ActionTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 # Define a null function and a null class for use as builder actions.
27 # Where these are defined in the file seems to affect their byte-code
28 # contents, so try to minimize changes by defining them here, before we
29 # even import anything.
30 def GlobalFunc():
31     pass
32
33 class GlobalActFunc:
34     def __call__(self):
35         pass
36
37 import os
38 import re
39 import StringIO
40 import string
41 import sys
42 import types
43 import unittest
44 import UserDict
45
46 import SCons.Action
47 import SCons.Environment
48 import SCons.Errors
49
50 import TestCmd
51
52 # Initial setup of the common environment for all tests,
53 # a temporary working directory containing a
54 # script for writing arguments to an output file.
55 #
56 # We don't do this as a setUp() method because it's
57 # unnecessary to create a separate directory and script
58 # for each test, they can just use the one.
59 test = TestCmd.TestCmd(workdir = '')
60
61 test.write('act.py', """\
62 import os, string, sys
63 f = open(sys.argv[1], 'w')
64 f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n")
65 try:
66     if sys.argv[3]:
67         f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
68 except:
69     pass
70 f.close()
71 if os.environ.has_key( 'ACTPY_PIPE' ):
72     if os.environ.has_key( 'PIPE_STDOUT_FILE' ):
73          stdout_msg = open(os.environ['PIPE_STDOUT_FILE'], 'r').read()
74     else:
75          stdout_msg = "act.py: stdout: executed act.py %s\\n" % string.join(sys.argv[1:])
76     sys.stdout.write( stdout_msg )
77     if os.environ.has_key( 'PIPE_STDERR_FILE' ):
78          stderr_msg = open(os.environ['PIPE_STDERR_FILE'], 'r').read()
79     else:
80          stderr_msg = "act.py: stderr: executed act.py %s\\n" % string.join(sys.argv[1:])
81     sys.stderr.write( stderr_msg )
82 sys.exit(0)
83 """)
84
85 test.write('exit.py', """\
86 import sys
87 sys.exit(int(sys.argv[1]))
88 """)
89
90 act_py = test.workpath('act.py')
91 exit_py = test.workpath('exit.py')
92
93 outfile = test.workpath('outfile')
94 outfile2 = test.workpath('outfile2')
95 pipe_file = test.workpath('pipe.out')
96
97 scons_env = SCons.Environment.Environment()
98
99 # Capture all the stuff the Actions will print,
100 # so it doesn't clutter the output.
101 sys.stdout = StringIO.StringIO()
102
103 class CmdStringHolder:
104     def __init__(self, cmd, literal=None):
105         self.data = str(cmd)
106         self.literal = literal
107
108     def is_literal(self):
109         return self.literal
110
111     def escape(self, escape_func):
112         """Escape the string with the supplied function.  The
113         function is expected to take an arbitrary string, then
114         return it with all special characters escaped and ready
115         for passing to the command interpreter.
116
117         After calling this function, the next call to str() will
118         return the escaped string.
119         """
120
121         if self.is_literal():
122             return escape_func(self.data)
123         elif ' ' in self.data or '\t' in self.data:
124             return '"%s"' % self.data
125         else:
126             return self.data
127
128 class Environment:
129     def __init__(self, **kw):
130         self.d = {}
131         self.d['SHELL'] = scons_env['SHELL']
132         self.d['SPAWN'] = scons_env['SPAWN']
133         self.d['PSPAWN'] = scons_env['PSPAWN']
134         self.d['ESCAPE'] = scons_env['ESCAPE']
135         for k, v in kw.items():
136             self.d[k] = v
137     # Just use the underlying scons_subst*() utility methods.
138     def subst(self, strSubst, raw=0, target=[], source=[], conv=None):
139         return SCons.Subst.scons_subst(strSubst, self, raw,
140                                        target, source, self.d, conv=conv)
141     subst_target_source = subst
142     def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None):
143         return SCons.Subst.scons_subst_list(strSubst, self, raw,
144                                        target, source, self.d, conv=conv)
145     def __getitem__(self, item):
146         return self.d[item]
147     def __setitem__(self, item, value):
148         self.d[item] = value
149     def has_key(self, item):
150         return self.d.has_key(item)
151     def get(self, key, value=None):
152         return self.d.get(key, value)
153     def items(self):
154         return self.d.items()
155     def Dictionary(self):
156         return self.d
157     def Clone(self, **kw):
158         res = Environment()
159         res.d = SCons.Util.semi_deepcopy(self.d)
160         for k, v in kw.items():
161             res.d[k] = v
162         return res
163     def sig_dict(self):
164         d = {}
165         for k,v in self.items(): d[k] = v
166         d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__']
167         d['TARGET'] = d['TARGETS'][0]
168         d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
169         d['SOURCE'] = d['SOURCES'][0]
170         return d
171
172 class DummyNode:
173     def __init__(self, name):
174         self.name = name
175     def str_for_display(self):
176         return '"' + self.name + '"'
177     def __str__(self):
178         return self.name
179     def rfile(self):
180         return self
181     def get_subst_proxy(self):
182         return self
183
184 if os.name == 'java':
185     python = os.path.join(sys.prefix, 'jython')
186 else:
187     python = sys.executable
188
189 _python_ = '"' + python + '"'
190
191 class ActionTestCase(unittest.TestCase):
192     """Test the Action() factory function"""
193
194     def test_FunctionAction(self):
195         """Test the Action() factory's creation of FunctionAction objects
196         """
197         def foo():
198             pass
199         def bar():
200             pass
201         a1 = SCons.Action.Action(foo)
202         assert isinstance(a1, SCons.Action.FunctionAction), a1
203         assert a1.execfunction == foo, a1.execfunction
204
205         a11 = SCons.Action.Action(foo, strfunction=bar)
206         assert isinstance(a11, SCons.Action.FunctionAction), a11
207         assert a11.execfunction == foo, a11.execfunction
208         assert a11.strfunction == bar, a11.strfunction
209
210     def test_CommandAction(self):
211         """Test the Action() factory's creation of CommandAction objects
212         """
213         a1 = SCons.Action.Action("string")
214         assert isinstance(a1, SCons.Action.CommandAction), a1
215         assert a1.cmd_list == "string", a1.cmd_list
216
217         if hasattr(types, 'UnicodeType'):
218             exec "a2 = SCons.Action.Action(u'string')"
219             exec "assert isinstance(a2, SCons.Action.CommandAction), a2"
220
221         a3 = SCons.Action.Action(["a3"])
222         assert isinstance(a3, SCons.Action.CommandAction), a3
223         assert a3.cmd_list == "a3", a3.cmd_list
224
225         a4 = SCons.Action.Action([[ "explicit", "command", "line" ]])
226         assert isinstance(a4, SCons.Action.CommandAction), a4
227         assert a4.cmd_list == [ "explicit", "command", "line" ], a4.cmd_list
228
229         def foo():
230             pass
231
232         a5 = SCons.Action.Action("string", strfunction=foo)
233         assert isinstance(a5, SCons.Action.CommandAction), a5
234         assert a5.cmd_list == "string", a5.cmd_list
235         assert a5.strfunction == foo, a5.strfunction
236
237     def test_ListAction(self):
238         """Test the Action() factory's creation of ListAction objects
239         """
240         a1 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]])
241         assert isinstance(a1, SCons.Action.ListAction), a1
242         assert isinstance(a1.list[0], SCons.Action.CommandAction), a1.list[0]
243         assert a1.list[0].cmd_list == "x", a1.list[0].cmd_list
244         assert isinstance(a1.list[1], SCons.Action.CommandAction), a1.list[1]
245         assert a1.list[1].cmd_list == "y", a1.list[1].cmd_list
246         assert isinstance(a1.list[2], SCons.Action.CommandAction), a1.list[2]
247         assert a1.list[2].cmd_list == "z", a1.list[2].cmd_list
248         assert isinstance(a1.list[3], SCons.Action.CommandAction), a1.list[3]
249         assert a1.list[3].cmd_list == [ "a", "b", "c" ], a1.list[3].cmd_list
250
251         a2 = SCons.Action.Action("x\ny\nz")
252         assert isinstance(a2, SCons.Action.ListAction), a2
253         assert isinstance(a2.list[0], SCons.Action.CommandAction), a2.list[0]
254         assert a2.list[0].cmd_list == "x", a2.list[0].cmd_list
255         assert isinstance(a2.list[1], SCons.Action.CommandAction), a2.list[1]
256         assert a2.list[1].cmd_list == "y", a2.list[1].cmd_list
257         assert isinstance(a2.list[2], SCons.Action.CommandAction), a2.list[2]
258         assert a2.list[2].cmd_list == "z", a2.list[2].cmd_list
259
260         def foo():
261             pass
262
263         a3 = SCons.Action.Action(["x", foo, "z"])
264         assert isinstance(a3, SCons.Action.ListAction), a3
265         assert isinstance(a3.list[0], SCons.Action.CommandAction), a3.list[0]
266         assert a3.list[0].cmd_list == "x", a3.list[0].cmd_list
267         assert isinstance(a3.list[1], SCons.Action.FunctionAction), a3.list[1]
268         assert a3.list[1].execfunction == foo, a3.list[1].execfunction
269         assert isinstance(a3.list[2], SCons.Action.CommandAction), a3.list[2]
270         assert a3.list[2].cmd_list == "z", a3.list[2].cmd_list
271
272         a4 = SCons.Action.Action(["x", "y"], strfunction=foo)
273         assert isinstance(a4, SCons.Action.ListAction), a4
274         assert isinstance(a4.list[0], SCons.Action.CommandAction), a4.list[0]
275         assert a4.list[0].cmd_list == "x", a4.list[0].cmd_list
276         assert a4.list[0].strfunction == foo, a4.list[0].strfunction
277         assert isinstance(a4.list[1], SCons.Action.CommandAction), a4.list[1]
278         assert a4.list[1].cmd_list == "y", a4.list[1].cmd_list
279         assert a4.list[1].strfunction == foo, a4.list[1].strfunction
280
281         a5 = SCons.Action.Action("x\ny", strfunction=foo)
282         assert isinstance(a5, SCons.Action.ListAction), a5
283         assert isinstance(a5.list[0], SCons.Action.CommandAction), a5.list[0]
284         assert a5.list[0].cmd_list == "x", a5.list[0].cmd_list
285         assert a5.list[0].strfunction == foo, a5.list[0].strfunction
286         assert isinstance(a5.list[1], SCons.Action.CommandAction), a5.list[1]
287         assert a5.list[1].cmd_list == "y", a5.list[1].cmd_list
288         assert a5.list[1].strfunction == foo, a5.list[1].strfunction
289
290     def test_CommandGeneratorAction(self):
291         """Test the Action() factory's creation of CommandGeneratorAction objects
292         """
293         def foo():
294             pass
295         def bar():
296             pass
297         a1 = SCons.Action.Action(foo, generator=1)
298         assert isinstance(a1, SCons.Action.CommandGeneratorAction), a1
299         assert a1.generator is foo, a1.generator
300
301         a2 = SCons.Action.Action(foo, strfunction=bar, generator=1)
302         assert isinstance(a2, SCons.Action.CommandGeneratorAction), a2
303         assert a2.generator is foo, a2.generator
304
305     def test_LazyCmdGeneratorAction(self):
306         """Test the Action() factory's creation of lazy CommandGeneratorAction objects
307         """
308         def foo():
309             pass
310
311         a1 = SCons.Action.Action("$FOO")
312         assert isinstance(a1, SCons.Action.LazyAction), a1
313
314         a2 = SCons.Action.Action("$FOO", strfunction=foo)
315         assert isinstance(a2, SCons.Action.LazyAction), a2
316
317     def test_no_action(self):
318         """Test when the Action() factory can't create an action object
319         """
320         a5 = SCons.Action.Action(1)
321         assert a5 is None, a5
322
323     def test_reentrance(self):
324         """Test the Action() factory when the action is already an Action object
325         """
326         a1 = SCons.Action.Action("foo")
327         a2 = SCons.Action.Action(a1)
328         assert a2 is a1, a2
329
330 class ActionBaseTestCase(unittest.TestCase):
331     def test_get_executor(self):
332         """Test the ActionBase.get_executor() method"""
333         a = SCons.Action.Action('foo')
334         x = a.get_executor({}, {}, [], [], {})
335         assert not x is None, x
336  
337 class _ActionActionTestCase(unittest.TestCase):
338
339     def test__init__(self):
340         """Test creation of _ActionAction objects
341         """
342
343         def func1():
344             pass
345
346         def func2():
347             pass
348
349         a = SCons.Action._ActionAction()
350         assert not hasattr(a, 'strfunction')
351
352         assert SCons.Action._ActionAction(kwarg = 1)
353         assert not hasattr(a, 'strfunction')
354         assert not hasattr(a, 'kwarg')
355
356         a = SCons.Action._ActionAction(strfunction=func1)
357         assert a.strfunction is func1, a.strfunction
358
359         a = SCons.Action._ActionAction(presub=func1)
360         assert a.presub is func1, a.presub
361
362         a = SCons.Action._ActionAction(chdir=1)
363         assert a.chdir is 1, a.chdir
364
365         a = SCons.Action._ActionAction(func1, func2, 'x')
366         assert a.strfunction is func1, a.strfunction
367         assert a.presub is func2, a.presub
368         assert a.chdir is 'x', a.chdir
369
370     def test___cmp__(self):
371         """Test Action comparison
372         """
373         a1 = SCons.Action.Action("x")
374         a2 = SCons.Action.Action("x")
375         assert a1 == a2
376         a3 = SCons.Action.Action("y")
377         assert a1 != a3
378         assert a2 != a3
379
380     def test_print_cmd_lines(self):
381         """Test the print_cmd_lines() method
382         """
383         save_stdout = sys.stdout
384
385         try:
386             def execfunc(target, source, env):
387                 pass
388             a = SCons.Action.Action(execfunc)
389
390             sio = StringIO.StringIO()
391             sys.stdout = sio
392             a.print_cmd_line("foo bar", None, None, None)
393             s = sio.getvalue()
394             assert s == "foo bar\n", s
395
396         finally:
397             sys.stdout = save_stdout
398
399     def test___call__(self):
400         """Test calling an Action
401         """
402         save_stdout = sys.stdout
403
404         save_print_actions = SCons.Action.print_actions
405         save_print_actions_presub = SCons.Action.print_actions_presub
406         save_execute_actions = SCons.Action.execute_actions
407         #SCons.Action.print_actions = 0
408
409         test = TestCmd.TestCmd(workdir = '')
410         test.subdir('sub', 'xyz')
411         os.chdir(test.workpath())
412
413         try:
414             env = Environment()
415
416             def execfunc(target, source, env):
417                 assert type(target) is type([]), type(target)
418                 assert type(source) is type([]), type(source)
419                 return 7
420             a = SCons.Action.Action(execfunc)
421
422             def firstfunc(target, source, env):
423                 assert type(target) is type([]), type(target)
424                 assert type(source) is type([]), type(source)
425                 return 0
426             def lastfunc(target, source, env):
427                 assert type(target) is type([]), type(target)
428                 assert type(source) is type([]), type(source)
429                 return 9
430             b = SCons.Action.Action([firstfunc, execfunc, lastfunc])
431             
432             sio = StringIO.StringIO()
433             sys.stdout = sio
434             result = a("out", "in", env)
435             assert result.status == 7, result
436             s = sio.getvalue()
437             assert s == "execfunc(['out'], ['in'])\n", s
438
439             a.chdir = 'xyz'
440             expect = "os.chdir(%s)\nexecfunc(['out'], ['in'])\nos.chdir(%s)\n"
441
442             sio = StringIO.StringIO()
443             sys.stdout = sio
444             result = a("out", "in", env)
445             assert result.status == 7, result.status
446             s = sio.getvalue()
447             assert s == expect % (repr('xyz'), repr(test.workpath())), s
448
449             sio = StringIO.StringIO()
450             sys.stdout = sio
451             result = a("out", "in", env, chdir='sub')
452             assert result.status == 7, result.status
453             s = sio.getvalue()
454             assert s == expect % (repr('sub'), repr(test.workpath())), s
455
456             a.chdir = None
457
458             sio = StringIO.StringIO()
459             sys.stdout = sio
460             result = b("out", "in", env)
461             assert result.status == 7, result.status
462             s = sio.getvalue()
463             assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\n", s
464
465             SCons.Action.execute_actions = 0
466
467             sio = StringIO.StringIO()
468             sys.stdout = sio
469             result = a("out", "in", env)
470             assert result == 0, result
471             s = sio.getvalue()
472             assert s == "execfunc(['out'], ['in'])\n", s
473
474             sio = StringIO.StringIO()
475             sys.stdout = sio
476             result = b("out", "in", env)
477             assert result == 0, result
478             s = sio.getvalue()
479             assert s == "firstfunc(['out'], ['in'])\nexecfunc(['out'], ['in'])\nlastfunc(['out'], ['in'])\n", s
480
481             SCons.Action.print_actions_presub = 1
482             SCons.Action.execute_actions = 1
483
484             sio = StringIO.StringIO()
485             sys.stdout = sio
486             result = a("out", "in", env)
487             assert result.status == 7, result.status
488             s = sio.getvalue()
489             assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
490
491             sio = StringIO.StringIO()
492             sys.stdout = sio
493             result = a("out", "in", env, presub=0)
494             assert result.status == 7, result.status
495             s = sio.getvalue()
496             assert s == "execfunc(['out'], ['in'])\n", s
497
498             sio = StringIO.StringIO()
499             sys.stdout = sio
500             result = a("out", "in", env, presub=1)
501             assert result.status == 7, result.status
502             s = sio.getvalue()
503             assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
504
505             sio = StringIO.StringIO()
506             sys.stdout = sio
507             result = b(["out"], "in", env, presub=1)
508             assert result.status == 7, result.status
509             s = sio.getvalue()
510             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
511
512             sio = StringIO.StringIO()
513             sys.stdout = sio
514             result = b(["out", "list"], "in", env, presub=1)
515             assert result.status == 7, result.status
516             s = sio.getvalue()
517             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
518
519             a2 = SCons.Action.Action(execfunc)
520
521             sio = StringIO.StringIO()
522             sys.stdout = sio
523             result = a2("out", "in", env)
524             assert result.status == 7, result.status
525             s = sio.getvalue()
526             assert s == "Building out with action:\n  execfunc(target, source, env)\nexecfunc(['out'], ['in'])\n", s
527
528             sio = StringIO.StringIO()
529             sys.stdout = sio
530             result = a2("out", "in", env, presub=0)
531             assert result.status == 7, result.status
532             s = sio.getvalue()
533             assert s == "execfunc(['out'], ['in'])\n", s
534
535             SCons.Action.execute_actions = 0
536
537             sio = StringIO.StringIO()
538             sys.stdout = sio
539             result = a2("out", "in", env, presub=0)
540             assert result == 0, result
541             s = sio.getvalue()
542             assert s == "execfunc(['out'], ['in'])\n", s
543
544             sio = StringIO.StringIO()
545             sys.stdout = sio
546             result = a("out", "in", env, presub=0, execute=1, show=0)
547             assert result.status == 7, result.status
548             s = sio.getvalue()
549             assert s == '', s
550
551             sys.stdout = save_stdout
552             exitstatfunc_result = []
553
554             def exitstatfunc(stat, result=exitstatfunc_result):
555                 result.append(stat)
556                 return stat
557
558             result = a("out", "in", env, exitstatfunc=exitstatfunc)
559             assert result == 0, result
560             assert exitstatfunc_result == [], exitstatfunc_result
561
562             result = a("out", "in", env, execute=1, exitstatfunc=exitstatfunc)
563             assert result.status == 7, result.status
564             assert exitstatfunc_result == [7], exitstatfunc_result
565
566             SCons.Action.execute_actions = 1
567
568             result = []
569             def my_print_cmd_line(s, target, source, env, result=result):
570                 result.append(s)
571             env['PRINT_CMD_LINE_FUNC'] = my_print_cmd_line
572             a("output", "input", env)
573             assert result == ["execfunc(['output'], ['input'])"], result
574             
575
576         finally:
577             sys.stdout = save_stdout
578             SCons.Action.print_actions = save_print_actions
579             SCons.Action.print_actions_presub = save_print_actions_presub
580             SCons.Action.execute_actions = save_execute_actions
581
582     def test_presub_lines(self):
583         """Test the presub_lines() method
584         """
585         env = Environment()
586         a = SCons.Action.Action("x")
587         s = a.presub_lines(env)
588         assert s == ['x'], s
589
590         a = SCons.Action.Action(["y", "z"])
591         s = a.presub_lines(env)
592         assert s == ['y', 'z'], s
593
594         def func():
595             pass
596         a = SCons.Action.Action(func)
597         s = a.presub_lines(env)
598         assert s == ["func(target, source, env)"], s
599
600         def gen(target, source, env, for_signature):
601             return 'generat' + env.get('GEN', 'or')
602         a = SCons.Action.Action(gen, generator=1)
603         s = a.presub_lines(env)
604         assert s == ["generator"], s
605         s = a.presub_lines(Environment(GEN = 'ed'))
606         assert s == ["generated"], s
607
608         a = SCons.Action.Action("$ACT")
609         s = a.presub_lines(env)
610         assert s == [''], s
611         s = a.presub_lines(Environment(ACT = 'expanded action'))
612         assert s == ['expanded action'], s
613
614     def test_add(self):
615         """Test adding Actions to stuff."""
616         # Adding actions to other Actions or to stuff that can
617         # be converted into an Action should produce a ListAction
618         # containing all the Actions.
619         def bar():
620             return None
621         baz = SCons.Action.Action(bar, generator=1)
622         act1 = SCons.Action.Action('foo bar')
623         act2 = SCons.Action.Action([ 'foo', bar ])
624
625         sum = act1 + act2
626         assert isinstance(sum, SCons.Action.ListAction), str(sum)
627         assert len(sum.list) == 3, len(sum.list)
628         assert map(lambda x: isinstance(x, SCons.Action.ActionBase),
629                    sum.list) == [ 1, 1, 1 ]
630
631         sum = act1 + act1
632         assert isinstance(sum, SCons.Action.ListAction), str(sum)
633         assert len(sum.list) == 2, len(sum.list)
634
635         sum = act2 + act2
636         assert isinstance(sum, SCons.Action.ListAction), str(sum)
637         assert len(sum.list) == 4, len(sum.list)
638
639         # Should also be able to add command generators to each other
640         # or to actions
641         sum = baz + baz
642         assert isinstance(sum, SCons.Action.ListAction), str(sum)
643         assert len(sum.list) == 2, len(sum.list)
644
645         sum = baz + act1
646         assert isinstance(sum, SCons.Action.ListAction), str(sum)
647         assert len(sum.list) == 2, len(sum.list)
648
649         sum = act2 + baz
650         assert isinstance(sum, SCons.Action.ListAction), str(sum)
651         assert len(sum.list) == 3, len(sum.list)
652
653         # Also should be able to add Actions to anything that can
654         # be converted into an action.
655         sum = act1 + bar
656         assert isinstance(sum, SCons.Action.ListAction), str(sum)
657         assert len(sum.list) == 2, len(sum.list)
658         assert isinstance(sum.list[1], SCons.Action.FunctionAction)
659
660         sum = 'foo bar' + act2
661         assert isinstance(sum, SCons.Action.ListAction), str(sum)
662         assert len(sum.list) == 3, len(sum.list)
663         assert isinstance(sum.list[0], SCons.Action.CommandAction)
664
665         sum = [ 'foo', 'bar' ] + act1
666         assert isinstance(sum, SCons.Action.ListAction), str(sum)
667         assert len(sum.list) == 3, sum.list
668         assert isinstance(sum.list[0], SCons.Action.CommandAction)
669         assert isinstance(sum.list[1], SCons.Action.CommandAction)
670
671         sum = act2 + [ baz, bar ]
672         assert isinstance(sum, SCons.Action.ListAction), str(sum)
673         assert len(sum.list) == 4, len(sum.list)
674         assert isinstance(sum.list[2], SCons.Action.CommandGeneratorAction)
675         assert isinstance(sum.list[3], SCons.Action.FunctionAction)
676
677         try:
678             sum = act2 + 1
679         except TypeError:
680             pass
681         else:
682             assert 0, "Should have thrown a TypeError adding to an int."
683
684         try:
685             sum = 1 + act2
686         except TypeError:
687             pass
688         else:
689             assert 0, "Should have thrown a TypeError adding to an int."
690
691 class CommandActionTestCase(unittest.TestCase):
692
693     def test___init__(self):
694         """Test creation of a command Action
695         """
696         a = SCons.Action.CommandAction(["xyzzy"])
697         assert a.cmd_list == [ "xyzzy" ], a.cmd_list
698         assert a.cmdstr is None, a.cmdstr
699
700         a = SCons.Action.CommandAction(["abra"], "cadabra")
701         assert a.cmd_list == [ "abra" ], a.cmd_list
702         assert a.cmdstr == "cadabra", a.cmdstr
703
704     def test_bad_cmdstr(self):
705         """Test handling of bad CommandAction(cmdstr) arguments
706         """
707         try:
708             a = SCons.Action.CommandAction('foo', [])
709         except SCons.Errors.UserError, e:
710             s = str(e)
711             m = 'Invalid command display variable'
712             assert string.find(s, m) != -1, 'Unexpected string:  %s' % s
713         else:
714             raise Exception, "did not catch expected UserError"
715
716     def test___str__(self):
717         """Test fetching the pre-substitution string for command Actions
718         """
719         env = Environment()
720         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
721         s = str(act)
722         assert s == 'xyzzy $TARGET $SOURCE', s
723
724         act = SCons.Action.CommandAction(['xyzzy',
725                                           '$TARGET', '$SOURCE',
726                                           '$TARGETS', '$SOURCES'])
727         s = str(act)
728         assert s == "xyzzy $TARGET $SOURCE $TARGETS $SOURCES", s
729
730     def test_genstring(self):
731         """Test the genstring() method for command Actions
732         """
733
734         env = Environment()
735         t1 = DummyNode('t1')
736         t2 = DummyNode('t2')
737         s1 = DummyNode('s1')
738         s2 = DummyNode('s2')
739         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
740         expect = 'xyzzy $TARGET $SOURCE'
741         s = act.genstring([], [], env)
742         assert s == expect, s
743         s = act.genstring([t1], [s1], env)
744         assert s == expect, s
745         s = act.genstring([t1, t2], [s1, s2], env)
746         assert s == expect, s
747
748         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES')
749         expect = 'xyzzy $TARGETS $SOURCES'
750         s = act.genstring([], [], env)
751         assert s == expect, s
752         s = act.genstring([t1], [s1], env)
753         assert s == expect, s
754         s = act.genstring([t1, t2], [s1, s2], env)
755         assert s == expect, s
756
757         act = SCons.Action.CommandAction(['xyzzy',
758                                           '$TARGET', '$SOURCE',
759                                           '$TARGETS', '$SOURCES'])
760         expect = "xyzzy $TARGET $SOURCE $TARGETS $SOURCES"
761         s = act.genstring([], [], env)
762         assert s == expect, s
763         s = act.genstring([t1], [s1], env)
764         assert s == expect, s
765         s = act.genstring([t1, t2], [s1, s2], env)
766         assert s == expect, s
767
768     def test_strfunction(self):
769         """Test fetching the string representation of command Actions
770         """
771
772         env = Environment()
773         t1 = DummyNode('t1')
774         t2 = DummyNode('t2')
775         s1 = DummyNode('s1')
776         s2 = DummyNode('s2')
777         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
778         s = act.strfunction([], [], env)
779         assert s == 'xyzzy', s
780         s = act.strfunction([t1], [s1], env)
781         assert s == 'xyzzy t1 s1', s
782         s = act.strfunction([t1, t2], [s1, s2], env)
783         assert s == 'xyzzy t1 s1', s
784
785         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE',
786                                          'cmdstr - $SOURCE - $TARGET -')
787         s = act.strfunction([], [], env)
788         assert s == 'cmdstr -  -  -', s
789         s = act.strfunction([t1], [s1], env)
790         assert s == 'cmdstr - s1 - t1 -', s
791         s = act.strfunction([t1, t2], [s1, s2], env)
792         assert s == 'cmdstr - s1 - t1 -', s
793
794         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES')
795         s = act.strfunction([], [], env)
796         assert s == 'xyzzy', s
797         s = act.strfunction([t1], [s1], env)
798         assert s == 'xyzzy t1 s1', s
799         s = act.strfunction([t1, t2], [s1, s2], env)
800         assert s == 'xyzzy t1 t2 s1 s2', s
801
802         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES',
803                                          'cmdstr = $SOURCES = $TARGETS =')
804         s = act.strfunction([], [], env)
805         assert s == 'cmdstr =  =  =', s
806         s = act.strfunction([t1], [s1], env)
807         assert s == 'cmdstr = s1 = t1 =', s
808         s = act.strfunction([t1, t2], [s1, s2], env)
809         assert s == 'cmdstr = s1 s2 = t1 t2 =', s
810
811         act = SCons.Action.CommandAction(['xyzzy',
812                                           '$TARGET', '$SOURCE',
813                                           '$TARGETS', '$SOURCES'])
814         s = act.strfunction([], [], env)
815         assert s == 'xyzzy', s
816         s = act.strfunction([t1], [s1], env)
817         assert s == 'xyzzy t1 s1 t1 s1', s
818         s = act.strfunction([t1, t2], [s1, s2], env)
819         assert s == 'xyzzy t1 s1 t1 t2 s1 s2', s
820
821         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES',
822                                          'cmdstr\t$TARGETS\n$SOURCES   ')
823                     
824         s = act.strfunction([], [], env)
825         assert s == 'cmdstr\t\n   ', s
826         s = act.strfunction([t1], [s1], env)
827         assert s == 'cmdstr\tt1\ns1   ', s
828         s = act.strfunction([t1, t2], [s1, s2], env)
829         assert s == 'cmdstr\tt1 t2\ns1 s2   ', s
830
831         def sf(target, source, env):
832             return "sf was called"
833         act = SCons.Action.CommandAction('foo', strfunction=sf)
834         s = act.strfunction([], [], env)
835         assert s == "sf was called", s
836
837         class actclass1:
838             def __init__(self, targets, sources, env):
839                 pass
840             def __call__(self):
841                 return 1
842         class actclass2:
843             def __init__(self, targets, sources, env):
844                 self.strfunction = 5
845             def __call__(self):
846                 return 2
847         class actclass3:
848             def __init__(self, targets, sources, env):
849                 pass
850             def __call__(self):
851                 return 3
852             def strfunction(self, targets, sources, env):
853                 return 'actclass3 on %s to get %s'%(str(sources[0]),
854                                                     str(targets[0]))
855         class actclass4:
856             def __init__(self, targets, sources, env):
857                 pass
858             def __call__(self):
859                 return 4
860             strfunction = None
861
862         act1 = SCons.Action.Action(actclass1([t1], [s1], env))
863         s = act1.strfunction([t1], [s1], env)
864         assert s == 'actclass1(["t1"], ["s1"])', s
865
866         act2 = SCons.Action.Action(actclass2([t1], [s1], env))
867         s = act2.strfunction([t1], [s1], env)
868         assert s == 'actclass2(["t1"], ["s1"])', s
869
870         act3 = SCons.Action.Action(actclass3([t1], [s1], env))
871         s = act3.strfunction([t1], [s1], env)
872         assert s == 'actclass3 on s1 to get t1', s
873
874         act4 = SCons.Action.Action(actclass4([t1], [s1], env))
875         s = act4.strfunction([t1], [s1], env)
876         assert s is None, s
877
878         act = SCons.Action.CommandAction("@foo bar")
879         s = act.strfunction([], [], env)
880         assert s == "", s
881
882         act = SCons.Action.CommandAction("@-foo bar")
883         s = act.strfunction([], [], env)
884         assert s == "", s
885
886         act = SCons.Action.CommandAction("-@foo bar")
887         s = act.strfunction([], [], env)
888         assert s == "", s
889
890         act = SCons.Action.CommandAction("-foo bar")
891         s = act.strfunction([], [], env)
892         assert s == "foo bar", s
893
894         act = SCons.Action.CommandAction("@ foo bar")
895         s = act.strfunction([], [], env)
896         assert s == "", s
897
898         act = SCons.Action.CommandAction("@- foo bar")
899         s = act.strfunction([], [], env)
900         assert s == "", s
901
902         act = SCons.Action.CommandAction("-@ foo bar")
903         s = act.strfunction([], [], env)
904         assert s == "", s
905
906         act = SCons.Action.CommandAction("- foo bar")
907         s = act.strfunction([], [], env)
908         assert s == "foo bar", s
909
910     def test_execute(self):
911         """Test execution of command Actions
912
913         """
914         try:
915             env = self.env
916         except AttributeError:
917             env = Environment()
918
919         cmd1 = r'%s %s %s xyzzy' % (_python_, act_py, outfile)
920
921         act = SCons.Action.CommandAction(cmd1)
922         r = act([], [], env.Clone())
923         assert r == 0
924         c = test.read(outfile, 'r')
925         assert c == "act.py: 'xyzzy'\n", c
926
927         cmd2 = r'%s %s %s $TARGET' % (_python_, act_py, outfile)
928
929         act = SCons.Action.CommandAction(cmd2)
930         r = act(DummyNode('foo'), [], env.Clone())
931         assert r == 0
932         c = test.read(outfile, 'r')
933         assert c == "act.py: 'foo'\n", c
934
935         cmd3 = r'%s %s %s ${TARGETS}' % (_python_, act_py, outfile)
936
937         act = SCons.Action.CommandAction(cmd3)
938         r = act(map(DummyNode, ['aaa', 'bbb']), [], env.Clone())
939         assert r == 0
940         c = test.read(outfile, 'r')
941         assert c == "act.py: 'aaa' 'bbb'\n", c
942
943         cmd4 = r'%s %s %s $SOURCES' % (_python_, act_py, outfile)
944
945         act = SCons.Action.CommandAction(cmd4)
946         r = act([], [DummyNode('one'), DummyNode('two')], env.Clone())
947         assert r == 0
948         c = test.read(outfile, 'r')
949         assert c == "act.py: 'one' 'two'\n", c
950
951         cmd4 = r'%s %s %s ${SOURCES[:2]}' % (_python_, act_py, outfile)
952
953         act = SCons.Action.CommandAction(cmd4)
954         sources = [DummyNode('three'), DummyNode('four'), DummyNode('five')]
955         env2 = env.Clone()
956         r = act([], source = sources, env = env2)
957         assert r == 0
958         c = test.read(outfile, 'r')
959         assert c == "act.py: 'three' 'four'\n", c
960
961         cmd5 = r'%s %s %s $TARGET XYZZY' % (_python_, act_py, outfile)
962
963         act = SCons.Action.CommandAction(cmd5)
964         env5 = Environment()
965         if scons_env.has_key('ENV'):
966             env5['ENV'] = scons_env['ENV']
967             PATH = scons_env['ENV'].get('PATH', '')
968         else:
969             env5['ENV'] = {}
970             PATH = ''
971
972         env5['ENV']['XYZZY'] = 'xyzzy'
973         r = act(target = DummyNode('out5'), source = [], env = env5)
974
975         act = SCons.Action.CommandAction(cmd5)
976         r = act(target = DummyNode('out5'),
977                 source = [],
978                 env = env.Clone(ENV = {'XYZZY' : 'xyzzy5',
979                                       'PATH' : PATH}))
980         assert r == 0
981         c = test.read(outfile, 'r')
982         assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy5'\n", c
983
984         class Obj:
985             def __init__(self, str):
986                 self._str = str
987             def __str__(self):
988                 return self._str
989             def rfile(self):
990                 return self
991             def get_subst_proxy(self):
992                 return self
993
994         cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (_python_, act_py, outfile)
995
996         act = SCons.Action.CommandAction(cmd6)
997         r = act(target = [Obj('111'), Obj('222')],
998                         source = [Obj('333'), Obj('444'), Obj('555')],
999                         env = env.Clone())
1000         assert r == 0
1001         c = test.read(outfile, 'r')
1002         assert c == "act.py: '222' '111' '333' '444'\n", c
1003
1004         if os.name == 'nt':
1005             # NT treats execs of directories and non-executable files
1006             # as "file not found" errors
1007             expect_nonexistent = 1
1008             expect_nonexecutable_file = 1
1009             expect_nonexecutable_dir = 1
1010         elif sys.platform == 'cygwin':
1011             expect_nonexistent = 127
1012             # Newer cygwin seems to return 126 for following
1013             expect_nonexecutable_file = 126
1014             expect_nonexecutable_dir  = 127
1015         else:
1016             expect_nonexistent = 127
1017             expect_nonexecutable_file = 126
1018             expect_nonexecutable_dir  = 126
1019
1020         # Test that a nonexistent command returns 127
1021         act = SCons.Action.CommandAction(python + "_no_such_command_")
1022         r = act([], [], env.Clone(out = outfile))
1023         assert r.status == expect_nonexistent, r.status
1024
1025         # Test that trying to execute a directory returns 126
1026         dir, tail = os.path.split(python)
1027         act = SCons.Action.CommandAction(dir)
1028         r = act([], [], env.Clone(out = outfile))
1029         assert r.status == expect_nonexecutable_file, r.status
1030
1031         # Test that trying to execute a non-executable file returns 126
1032         act = SCons.Action.CommandAction(outfile)
1033         r = act([], [], env.Clone(out = outfile))
1034         assert r.status == expect_nonexecutable_dir, r.status
1035
1036         act = SCons.Action.CommandAction('%s %s 1' % (_python_, exit_py))
1037         r = act([], [], env)
1038         assert r.status == 1, r.status
1039
1040         act = SCons.Action.CommandAction('@%s %s 1' % (_python_, exit_py))
1041         r = act([], [], env)
1042         assert r.status == 1, r.status
1043
1044         act = SCons.Action.CommandAction('@-%s %s 1' % (_python_, exit_py))
1045         r = act([], [], env)
1046         assert r == 0, r
1047
1048         act = SCons.Action.CommandAction('-%s %s 1' % (_python_, exit_py))
1049         r = act([], [], env)
1050         assert r == 0, r
1051
1052         act = SCons.Action.CommandAction('@ %s %s 1' % (_python_, exit_py))
1053         r = act([], [], env)
1054         assert r.status == 1, r.status
1055
1056         act = SCons.Action.CommandAction('@- %s %s 1' % (_python_, exit_py))
1057         r = act([], [], env)
1058         assert r == 0, r
1059
1060         act = SCons.Action.CommandAction('- %s %s 1' % (_python_, exit_py))
1061         r = act([], [], env)
1062         assert r == 0, r
1063
1064     def _DO_NOT_EXECUTE_test_pipe_execute(self):
1065         """Test capturing piped output from an action
1066
1067         We used to have PIPE_BUILD support built right into
1068         Action.execute() for the benefit of the SConf subsystem, but we've
1069         moved that logic back into SConf itself.  We'll leave this code
1070         here, just in case we ever want to resurrect this functionality
1071         in the future, but change the name of the test so it doesn't
1072         get executed as part of the normal test suite.
1073         """
1074         pipe = open( pipe_file, "w" )
1075         self.env = Environment(ENV = {'ACTPY_PIPE' : '1'}, PIPE_BUILD = 1,
1076                                PSTDOUT = pipe, PSTDERR = pipe)
1077         # everything should also work when piping output
1078         self.test_execute()
1079         self.env['PSTDOUT'].close()
1080         pipe_out = test.read( pipe_file )
1081
1082         act_out = "act.py: stdout: executed act.py"
1083         act_err = "act.py: stderr: executed act.py"
1084
1085         # Since we are now using select(), stdout and stderr can be
1086         # intermixed, so count the lines separately.
1087         outlines = re.findall(act_out, pipe_out)
1088         errlines = re.findall(act_err, pipe_out)
1089         assert len(outlines) == 6, pipe_out + repr(outlines)
1090         assert len(errlines) == 6, pipe_out + repr(errlines)
1091
1092         # test redirection operators
1093         def test_redirect(self, redir, stdout_msg, stderr_msg):
1094             cmd = r'%s %s %s xyzzy %s' % (_python_, act_py, outfile, redir)
1095             # Write the output and error messages to files because
1096             # Windows can't handle strings that are too big in its
1097             # external environment (os.spawnve() returns EINVAL,
1098             # "Invalid argument").
1099             stdout_file = test.workpath('stdout_msg')
1100             stderr_file = test.workpath('stderr_msg')
1101             open(stdout_file, 'w').write(stdout_msg)
1102             open(stderr_file, 'w').write(stderr_msg)
1103             pipe = open( pipe_file, "w" )
1104             act = SCons.Action.CommandAction(cmd)
1105             env = Environment( ENV = {'ACTPY_PIPE' : '1',
1106                                       'PIPE_STDOUT_FILE' : stdout_file,
1107                                       'PIPE_STDERR_FILE' : stderr_file},
1108                                PIPE_BUILD = 1,
1109                                PSTDOUT = pipe, PSTDERR = pipe )
1110             r = act([], [], env)
1111             pipe.close()
1112             assert r == 0
1113             return (test.read(outfile2, 'r'), test.read(pipe_file, 'r'))
1114
1115         (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
1116                                                act_out, act_err)
1117         assert redirected == act_out
1118         assert pipe_out == act_err
1119
1120         (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2,
1121                                                act_out, act_err)
1122         assert redirected == act_err
1123         assert pipe_out == act_out
1124
1125         (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2,
1126                                                act_out, act_err)
1127         assert (redirected == act_out + act_err or
1128                 redirected == act_err + act_out)
1129         assert pipe_out == ""
1130
1131         act_err = "Long Command Output\n"*3000
1132         # the size of the string should exceed the system's default block size
1133         act_out = ""
1134         (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
1135                                                act_out, act_err)
1136         assert (redirected == act_out)
1137         assert (pipe_out == act_err)
1138
1139     def test_set_handler(self):
1140         """Test setting the command handler...
1141         """
1142         class Test:
1143             def __init__(self):
1144                 self.executed = 0
1145         t=Test()
1146         def func(sh, escape, cmd, args, env, test=t):
1147             test.executed = args
1148             test.shell = sh
1149             return 0
1150         def escape_func(cmd):
1151             return '**' + cmd + '**'
1152
1153         class LiteralStr:
1154             def __init__(self, x):
1155                 self.data = x
1156             def __str__(self):
1157                 return self.data
1158             def escape(self, escape_func):
1159                 return escape_func(self.data)
1160             def is_literal(self):
1161                 return 1
1162
1163         a = SCons.Action.CommandAction(["xyzzy"])
1164         e = Environment(SPAWN = func)
1165         a([], [], e)
1166         assert t.executed == [ 'xyzzy' ], t.executed
1167
1168         a = SCons.Action.CommandAction(["xyzzy"])
1169         e = Environment(SPAWN = '$FUNC', FUNC = func)
1170         a([], [], e)
1171         assert t.executed == [ 'xyzzy' ], t.executed
1172
1173         a = SCons.Action.CommandAction(["xyzzy"])
1174         e = Environment(SPAWN = func, SHELL = 'fake shell')
1175         a([], [], e)
1176         assert t.executed == [ 'xyzzy' ], t.executed
1177         assert t.shell == 'fake shell', t.shell
1178
1179         a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ])
1180         e = Environment(SPAWN = func, ESCAPE = escape_func)
1181         a([], [], e)
1182         assert t.executed == [ '**xyzzy**' ], t.executed
1183
1184     def test_get_contents(self):
1185         """Test fetching the contents of a command Action
1186         """
1187         def CmdGen(target, source, env, for_signature):
1188             assert for_signature
1189             return "%s %s" % \
1190                    (env["foo"], env["bar"])
1191
1192         # The number 1 is there to make sure all args get converted to strings.
1193         a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
1194                                         "$)", "|", "$baz", 1])
1195         c = a.get_contents(target=[], source=[],
1196                            env=Environment(foo = 'FFF', bar = 'BBB',
1197                                            baz = CmdGen))
1198         assert c == "| | FFF BBB 1", c
1199
1200         # Make sure that CommandActions use an Environment's
1201         # subst_target_source() method for substitution.
1202         class SpecialEnvironment(Environment):
1203             def subst_target_source(self, strSubst, raw=0, target=[], source=[]):
1204                 return 'subst_target_source: ' + strSubst
1205
1206         c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'),
1207                            env=SpecialEnvironment(foo = 'GGG', bar = 'CCC',
1208                                                   baz = 'ZZZ'))
1209         assert c == 'subst_target_source: | $( $foo | $bar $) | $baz 1', c
1210
1211         # We've discussed using the real target and source names in a
1212         # CommandAction's signature contents.  This would have have the
1213         # advantage of recompiling when a file's name changes (keeping
1214         # debug info current), but it would currently break repository
1215         # logic that will change the file name based on whether the
1216         # files come from a repository or locally.  If we ever move to
1217         # that scheme, then all of the '__t1__' and '__s6__' file names
1218         # in the asserts below would change to 't1' and 's6' and the
1219         # like.
1220         t = map(DummyNode, ['t1', 't2', 't3', 't4', 't5', 't6'])
1221         s = map(DummyNode, ['s1', 's2', 's3', 's4', 's5', 's6'])
1222         env = Environment()
1223
1224         a = SCons.Action.CommandAction(["$TARGET"])
1225         c = a.get_contents(target=t, source=s, env=env)
1226         assert c == "t1", c
1227
1228         a = SCons.Action.CommandAction(["$TARGETS"])
1229         c = a.get_contents(target=t, source=s, env=env)
1230         assert c == "t1 t2 t3 t4 t5 t6", c
1231
1232         a = SCons.Action.CommandAction(["${TARGETS[2]}"])
1233         c = a.get_contents(target=t, source=s, env=env)
1234         assert c == "t3", c
1235
1236         a = SCons.Action.CommandAction(["${TARGETS[3:5]}"])
1237         c = a.get_contents(target=t, source=s, env=env)
1238         assert c == "t4 t5", c
1239
1240         a = SCons.Action.CommandAction(["$SOURCE"])
1241         c = a.get_contents(target=t, source=s, env=env)
1242         assert c == "s1", c
1243
1244         a = SCons.Action.CommandAction(["$SOURCES"])
1245         c = a.get_contents(target=t, source=s, env=env)
1246         assert c == "s1 s2 s3 s4 s5 s6", c
1247
1248         a = SCons.Action.CommandAction(["${SOURCES[2]}"])
1249         c = a.get_contents(target=t, source=s, env=env)
1250         assert c == "s3", c
1251
1252         a = SCons.Action.CommandAction(["${SOURCES[3:5]}"])
1253         c = a.get_contents(target=t, source=s, env=env)
1254         assert c == "s4 s5", c
1255
1256 class CommandGeneratorActionTestCase(unittest.TestCase):
1257
1258     def test___init__(self):
1259         """Test creation of a command generator Action
1260         """
1261         def f(target, source, env):
1262             pass
1263         a = SCons.Action.CommandGeneratorAction(f)
1264         assert a.generator == f
1265
1266     def test___str__(self):
1267         """Test the pre-substitution strings for command generator Actions
1268         """
1269         def f(target, source, env, for_signature, self=self):
1270
1271             # See if "env" is really a construction environment (or
1272             # looks like one) by accessing the FindIxes attribute.
1273             # (The Tool/mingw.py module has a generator that uses this,
1274             # and the __str__() method used to cause problems by passing
1275             # us a regular dictionary as a fallback.)
1276
1277             env.FindIxes
1278             return "FOO"
1279         a = SCons.Action.CommandGeneratorAction(f)
1280         s = str(a)
1281         assert s == 'FOO', s
1282
1283     def test_genstring(self):
1284         """Test the command generator Action genstring() method
1285         """
1286         def f(target, source, env, for_signature, self=self):
1287             dummy = env['dummy']
1288             self.dummy = dummy
1289             return "$FOO $TARGET $SOURCE $TARGETS $SOURCES"
1290         a = SCons.Action.CommandGeneratorAction(f)
1291         self.dummy = 0
1292         s = a.genstring([], [], env=Environment(FOO='xyzzy', dummy=1))
1293         assert self.dummy == 1, self.dummy
1294         assert s == "$FOO $TARGET $SOURCE $TARGETS $SOURCES", s
1295
1296     def test_execute(self):
1297         """Test executing a command generator Action
1298         """
1299
1300         def f(target, source, env, for_signature, self=self):
1301             dummy = env['dummy']
1302             self.dummy = dummy
1303             s = env.subst("$FOO")
1304             assert s == 'foo baz\nbar ack', s
1305             return "$FOO"
1306         def func_action(target, source, env, self=self):
1307             dummy=env['dummy']
1308             s = env.subst('$foo')
1309             assert s == 'bar', s
1310             self.dummy=dummy
1311         def f2(target, source, env, for_signature, f=func_action):
1312             return f
1313         def ch(sh, escape, cmd, args, env, self=self):
1314             self.cmd.append(cmd)
1315             self.args.append(args)
1316
1317         a = SCons.Action.CommandGeneratorAction(f)
1318         self.dummy = 0
1319         self.cmd = []
1320         self.args = []
1321         a([], [], env=Environment(FOO = 'foo baz\nbar ack',
1322                                           dummy = 1,
1323                                           SPAWN = ch))
1324         assert self.dummy == 1, self.dummy
1325         assert self.cmd == ['foo', 'bar'], self.cmd
1326         assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
1327
1328         b = SCons.Action.CommandGeneratorAction(f2)
1329         self.dummy = 0
1330         b(target=[], source=[], env=Environment(foo =  'bar',
1331                                                         dummy =  2 ))
1332         assert self.dummy==2, self.dummy
1333         del self.dummy
1334
1335         class DummyFile:
1336             def __init__(self, t):
1337                 self.t = t
1338             def rfile(self):
1339                 self.t.rfile_called = 1
1340                 return self
1341             def get_subst_proxy(self):
1342                 return self
1343         def f3(target, source, env, for_signature):
1344             return ''
1345         c = SCons.Action.CommandGeneratorAction(f3)
1346         c(target=[], source=DummyFile(self), env=Environment())
1347         assert self.rfile_called
1348
1349     def test_get_contents(self):
1350         """Test fetching the contents of a command generator Action
1351         """
1352         def f(target, source, env, for_signature):
1353             foo = env['foo']
1354             bar = env['bar']
1355             assert for_signature, for_signature
1356             return [["guux", foo, "$(", "$ignore", "$)", bar,
1357                      '${test("$( foo $bar $)")}' ]]
1358
1359         def test(mystr):
1360             assert mystr == "$( foo $bar $)", mystr
1361             return "test"
1362
1363         env = Environment(foo = 'FFF', bar =  'BBB',
1364                           ignore = 'foo', test=test)
1365         a = SCons.Action.CommandGeneratorAction(f)
1366         c = a.get_contents(target=[], source=[], env=env)
1367         assert c == "guux FFF BBB test", c
1368
1369
1370 class FunctionActionTestCase(unittest.TestCase):
1371
1372     def test___init__(self):
1373         """Test creation of a function Action
1374         """
1375         def func1():
1376             pass
1377         def func2():
1378             pass
1379         def func3():
1380             pass
1381         def func4():
1382             pass
1383
1384         a = SCons.Action.FunctionAction(func1)
1385         assert a.execfunction == func1, a.execfunction
1386         assert isinstance(a.strfunction, types.MethodType), type(a.strfunction)
1387
1388         a = SCons.Action.FunctionAction(func2, strfunction=func3)
1389         assert a.execfunction == func2, a.execfunction
1390         assert a.strfunction == func3, a.strfunction
1391
1392     def test_cmdstr_bad(self):
1393         """Test handling of bad FunctionAction(cmdstr) arguments
1394         """
1395         def func():
1396             pass
1397         try:
1398             a = SCons.Action.FunctionAction(func, [])
1399         except SCons.Errors.UserError, e:
1400             s = str(e)
1401             m = 'Invalid function display variable'
1402             assert string.find(s, m) != -1, 'Unexpected string:  %s' % s
1403         else:
1404             raise "did not catch expected UserError"
1405
1406     def test___str__(self):
1407         """Test the __str__() method for function Actions
1408         """
1409         def func1():
1410             pass
1411         a = SCons.Action.FunctionAction(func1)
1412         s = str(a)
1413         assert s == "func1(target, source, env)", s
1414
1415         class class1:
1416             def __call__(self):
1417                 pass
1418         a = SCons.Action.FunctionAction(class1())
1419         s = str(a)
1420         assert s == "class1(target, source, env)", s
1421
1422     def test_execute(self):
1423         """Test executing a function Action
1424         """
1425         self.inc = 0
1426         def f(target, source, env):
1427             s = env['s']
1428             s.inc = s.inc + 1
1429             s.target = target
1430             s.source=source
1431             assert env.subst("$BAR") == 'foo bar', env.subst("$BAR")
1432             return 0
1433         a = SCons.Action.FunctionAction(f)
1434         a(target=1, source=2, env=Environment(BAR = 'foo bar',
1435                                                       s = self))
1436         assert self.inc == 1, self.inc
1437         assert self.source == [2], self.source
1438         assert self.target == [1], self.target
1439
1440         global count
1441         count = 0
1442         def function1(target, source, env):
1443             global count
1444             count = count + 1
1445             for t in target:
1446                 open(t, 'w').write("function1\n")
1447             return 1
1448
1449         act = SCons.Action.FunctionAction(function1)
1450         r = act(target = [outfile, outfile2], source=[], env=Environment())
1451         assert r.status == 1, r.status
1452
1453         assert count == 1, count
1454         c = test.read(outfile, 'r')
1455         assert c == "function1\n", c
1456         c = test.read(outfile2, 'r')
1457         assert c == "function1\n", c
1458
1459         class class1a:
1460             def __init__(self, target, source, env):
1461                 open(env['out'], 'w').write("class1a\n")
1462
1463         act = SCons.Action.FunctionAction(class1a)
1464         r = act([], [], Environment(out = outfile))
1465         assert isinstance(r.status, class1a), r.status
1466         c = test.read(outfile, 'r')
1467         assert c == "class1a\n", c
1468
1469         class class1b:
1470             def __call__(self, target, source, env):
1471                 open(env['out'], 'w').write("class1b\n")
1472                 return 2
1473
1474         act = SCons.Action.FunctionAction(class1b())
1475         r = act([], [], Environment(out = outfile))
1476         assert r.status == 2, r.status
1477         c = test.read(outfile, 'r')
1478         assert c == "class1b\n", c
1479
1480         def build_it(target, source, env, self=self):
1481             self.build_it = 1
1482             return 0
1483         def string_it(target, source, env, self=self):
1484             self.string_it = 1
1485             return None
1486         act = SCons.Action.FunctionAction(build_it, strfunction=string_it)
1487         r = act([], [], Environment())
1488         assert r == 0, r
1489         assert self.build_it
1490         assert self.string_it
1491
1492     def test_get_contents(self):
1493         """Test fetching the contents of a function Action
1494         """
1495
1496         def LocalFunc():
1497             pass
1498
1499         func_matches = [
1500             "0,0,0,0,(),(),(d\000\000S),(),()",
1501             "0,0,0,0,(),(),(d\x00\x00S),(),()",
1502             ]
1503         
1504         meth_matches = [
1505             "1,1,0,0,(),(),(d\000\000S),(),()",
1506             "1,1,0,0,(),(),(d\x00\x00S),(),()",
1507         ]
1508
1509         a = SCons.Action.FunctionAction(GlobalFunc)
1510         c = a.get_contents(target=[], source=[], env=Environment())
1511         assert c in func_matches, repr(c)
1512
1513         a = SCons.Action.FunctionAction(LocalFunc)
1514         c = a.get_contents(target=[], source=[], env=Environment())
1515         assert c in func_matches, repr(c)
1516
1517         a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
1518
1519         matches_foo = map(lambda x: x + "foo", func_matches)
1520
1521         c = a.get_contents(target=[], source=[], env=Environment())
1522         assert c in func_matches, repr(c)
1523         c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo'))
1524         assert c in matches_foo, repr(c)
1525
1526         # Make sure a bare string varlist works
1527         a = SCons.Action.FunctionAction(GlobalFunc, varlist='XYZ')
1528
1529         matches_foo = map(lambda x: x + "foo", func_matches)
1530
1531         c = a.get_contents(target=[], source=[], env=Environment())
1532         assert c in func_matches, repr(c)
1533         c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo'))
1534         assert c in matches_foo, repr(c)
1535
1536         class Foo:
1537             def get_contents(self, target, source, env):
1538                 return 'xyzzy'
1539         a = SCons.Action.FunctionAction(Foo())
1540         c = a.get_contents(target=[], source=[], env=Environment())
1541         assert c == 'xyzzy', repr(c)
1542
1543         class LocalClass:
1544             def LocalMethod(self):
1545                 pass
1546         lc = LocalClass()
1547         a = SCons.Action.FunctionAction(lc.LocalMethod)
1548         c = a.get_contents(target=[], source=[], env=Environment())
1549         assert c in meth_matches, repr(c)
1550
1551     def test_strfunction(self):
1552         """Test the FunctionAction.strfunction() method
1553         """
1554         def func():
1555             pass
1556
1557         a = SCons.Action.FunctionAction(func)
1558         s = a.strfunction(target=[], source=[], env=Environment())
1559         assert s == 'func([], [])', s
1560
1561         a = SCons.Action.FunctionAction(func, None)
1562         s = a.strfunction(target=[], source=[], env=Environment())
1563         assert s is None, s
1564
1565         a = SCons.Action.FunctionAction(func, 'function')
1566         s = a.strfunction(target=[], source=[], env=Environment())
1567         assert s == 'function', s
1568
1569 class ListActionTestCase(unittest.TestCase):
1570
1571     def test___init__(self):
1572         """Test creation of a list of subsidiary Actions
1573         """
1574         def func():
1575             pass
1576         a = SCons.Action.ListAction(["x", func, ["y", "z"]])
1577         assert isinstance(a.list[0], SCons.Action.CommandAction)
1578         assert isinstance(a.list[1], SCons.Action.FunctionAction)
1579         assert isinstance(a.list[2], SCons.Action.ListAction)
1580         assert a.list[2].list[0].cmd_list == 'y'
1581
1582     def test___str__(self):
1583         """Test the __str__() method for a list of subsidiary Actions
1584         """
1585         def f(target,source,env):
1586             pass
1587         def g(target,source,env):
1588             pass
1589         a = SCons.Action.ListAction([f, g, "XXX", f])
1590         s = str(a)
1591         assert s == "f(target, source, env)\ng(target, source, env)\nXXX\nf(target, source, env)", s
1592
1593     def test_genstring(self):
1594         """Test the genstring() method for a list of subsidiary Actions
1595         """
1596         def f(target,source,env):
1597             pass
1598         def g(target,source,env,for_signature):
1599             return 'generated %s %s' % (target[0], source[0])
1600         g = SCons.Action.Action(g, generator=1)
1601         a = SCons.Action.ListAction([f, g, "XXX", f])
1602         s = a.genstring(['foo.x'], ['bar.y'], Environment())
1603         assert s == "f(target, source, env)\ngenerated foo.x bar.y\nXXX\nf(target, source, env)", s
1604
1605     def test_execute(self):
1606         """Test executing a list of subsidiary Actions
1607         """
1608         self.inc = 0
1609         def f(target,source,env):
1610             s = env['s']
1611             s.inc = s.inc + 1
1612         a = SCons.Action.ListAction([f, f, f])
1613         a([], [], Environment(s = self))
1614         assert self.inc == 3, self.inc
1615
1616         cmd2 = r'%s %s %s syzygy' % (_python_, act_py, outfile)
1617
1618         def function2(target, source, env):
1619             open(env['out'], 'a').write("function2\n")
1620             return 0
1621
1622         class class2a:
1623             def __call__(self, target, source, env):
1624                 open(env['out'], 'a').write("class2a\n")
1625                 return 0
1626
1627         class class2b:
1628             def __init__(self, target, source, env):
1629                 open(env['out'], 'a').write("class2b\n")
1630         act = SCons.Action.ListAction([cmd2, function2, class2a(), class2b])
1631         r = act([], [], Environment(out = outfile))
1632         assert isinstance(r.status, class2b), r.status
1633         c = test.read(outfile, 'r')
1634         assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
1635
1636     def test_get_contents(self):
1637         """Test fetching the contents of a list of subsidiary Actions
1638         """
1639         self.foo=0
1640         def gen(target, source, env, for_signature):
1641             s = env['s']
1642             s.foo=1
1643             return "y"
1644         a = SCons.Action.ListAction(["x",
1645                                      SCons.Action.Action(gen, generator=1),
1646                                      "z"])
1647         c = a.get_contents(target=[], source=[], env=Environment(s = self))
1648         assert self.foo==1, self.foo
1649         assert c == "xyz", c
1650
1651 class LazyActionTestCase(unittest.TestCase):
1652     def test___init__(self):
1653         """Test creation of a lazy-evaluation Action
1654         """
1655         # Environment variable references should create a special type
1656         # of LazyAction that lazily evaluates the variable for whether
1657         # it's a string or something else before doing anything.
1658         a9 = SCons.Action.Action('$FOO')
1659         assert isinstance(a9, SCons.Action.LazyAction), a9
1660         assert a9.var == 'FOO', a9.var
1661
1662         a10 = SCons.Action.Action('${FOO}')
1663         assert isinstance(a10, SCons.Action.LazyAction), a10
1664         assert a10.var == 'FOO', a10.var
1665
1666     def test_genstring(self):
1667         """Test the lazy-evaluation Action genstring() method
1668         """
1669         def f(target, source, env):
1670             pass
1671         a = SCons.Action.Action('$BAR')
1672         env1 = Environment(BAR=f, s=self)
1673         env2 = Environment(BAR='xxx', s=self)
1674         s = a.genstring([], [], env=env1)
1675         assert s == "f(target, source, env)", s
1676         s = a.genstring([], [], env=env2)
1677         assert s == 'xxx', s
1678
1679     def test_execute(self):
1680         """Test executing a lazy-evaluation Action
1681         """
1682         def f(target, source, env):
1683             s = env['s']
1684             s.test=1
1685             return 0
1686         a = SCons.Action.Action('$BAR')
1687         a([], [], env=Environment(BAR = f, s = self))
1688         assert self.test == 1, self.test
1689         cmd = r'%s %s %s lazy' % (_python_, act_py, outfile)
1690         a([], [], env=Environment(BAR = cmd, s = self))
1691         c = test.read(outfile, 'r')
1692         assert c == "act.py: 'lazy'\n", c
1693
1694     def test_get_contents(self):
1695         """Test fetching the contents of a lazy-evaluation Action
1696         """
1697         a = SCons.Action.Action("${FOO}")
1698         env = Environment(FOO = [["This", "is", "a", "test"]])
1699         c = a.get_contents(target=[], source=[], env=env)
1700         assert c == "This is a test", c
1701
1702 class ActionCallerTestCase(unittest.TestCase):
1703     def test___init__(self):
1704         """Test creation of an ActionCaller"""
1705         ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO' : 4, 'BAR' : 5})
1706         assert ac.parent == 1, ac.parent
1707         assert ac.args == [2, 3], ac.args
1708         assert ac.kw == {'FOO' : 4, 'BAR' : 5}, ac.kw
1709
1710     def test_get_contents(self):
1711         """Test fetching the contents of an ActionCaller"""
1712         def strfunc():
1713             pass
1714
1715         def LocalFunc():
1716             pass
1717
1718         matches = [
1719             "d\000\000S",
1720             "d\x00\x00S"
1721         ]
1722
1723         af = SCons.Action.ActionFactory(GlobalFunc, strfunc)
1724         ac = SCons.Action.ActionCaller(af, [], {})
1725         c = ac.get_contents([], [], Environment())
1726         assert c in matches, repr(c)
1727
1728         af = SCons.Action.ActionFactory(LocalFunc, strfunc)
1729         ac = SCons.Action.ActionCaller(af, [], {})
1730         c = ac.get_contents([], [], Environment())
1731         assert c in matches, repr(c)
1732
1733         matches = [
1734             'd\000\000S',
1735             "d\x00\x00S"
1736         ]
1737
1738         class LocalActFunc:
1739             def __call__(self):
1740                 pass
1741
1742         af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
1743         ac = SCons.Action.ActionCaller(af, [], {})
1744         c = ac.get_contents([], [], Environment())
1745         assert c in matches, repr(c)
1746
1747         af = SCons.Action.ActionFactory(LocalActFunc(), strfunc)
1748         ac = SCons.Action.ActionCaller(af, [], {})
1749         c = ac.get_contents([], [], Environment())
1750         assert c in matches, repr(c)
1751
1752         matches = [
1753             "<built-in function str>",
1754             "<type 'str'>",
1755         ]
1756
1757         af = SCons.Action.ActionFactory(str, strfunc)
1758         ac = SCons.Action.ActionCaller(af, [], {})
1759         c = ac.get_contents([], [], Environment())
1760         assert c == "<built-in function str>" or \
1761                c == "<type 'str'>", repr(c)
1762
1763     def test___call__(self):
1764         """Test calling an ActionCaller"""
1765         actfunc_args = []
1766         def actfunc(a1, a2, a3, args=actfunc_args):
1767             args.extend([a1, a2, a3])
1768         def strfunc(a1, a2, a3):
1769             pass
1770
1771         e = Environment(FOO = 2, BAR = 5)
1772
1773         af = SCons.Action.ActionFactory(actfunc, strfunc)
1774         ac = SCons.Action.ActionCaller(af, ['$__env__', '$FOO', 3], {})
1775         ac([], [], e)
1776         assert actfunc_args[0] is e, actfunc_args
1777         assert actfunc_args[1] == '2', actfunc_args
1778         assert actfunc_args[2] == 3, actfunc_args
1779         del actfunc_args[:]
1780
1781         ac = SCons.Action.ActionCaller(af, [], {'a3' : '$__env__', 'a2' : '$BAR', 'a1' : 4})
1782         ac([], [], e)
1783         assert actfunc_args[0] == 4, actfunc_args
1784         assert actfunc_args[1] == '5', actfunc_args
1785         assert actfunc_args[2] is e, actfunc_args
1786         del actfunc_args[:]
1787
1788     def test_strfunction(self):
1789         """Test calling the ActionCaller strfunction() method"""
1790         strfunc_args = []
1791         def actfunc(a1, a2, a3, a4):
1792             pass
1793         def strfunc(a1, a2, a3, a4, args=strfunc_args):
1794             args.extend([a1, a2, a3, a4])
1795
1796         af = SCons.Action.ActionFactory(actfunc, strfunc)
1797         ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3, '$WS'], {})
1798         ac.strfunction([], [], Environment(FOO = 2, WS='white   space'))
1799         assert strfunc_args == [1, '2', 3, 'white   space'], strfunc_args
1800
1801         del strfunc_args[:]
1802         d = {'a3' : 6, 'a2' : '$BAR', 'a1' : 4, 'a4' : '$WS'}
1803         ac = SCons.Action.ActionCaller(af, [], d)
1804         ac.strfunction([], [], Environment(BAR = 5, WS='w   s'))
1805         assert strfunc_args == [4, '5', 6, 'w   s'], strfunc_args
1806
1807 class ActionFactoryTestCase(unittest.TestCase):
1808     def test___init__(self):
1809         """Test creation of an ActionFactory"""
1810         def actfunc():
1811             pass
1812         def strfunc():
1813             pass
1814         ac = SCons.Action.ActionFactory(actfunc, strfunc)
1815         assert ac.actfunc is actfunc, ac.actfunc
1816         assert ac.strfunc is strfunc, ac.strfunc
1817
1818     def test___call__(self):
1819         """Test calling whatever's returned from an ActionFactory"""
1820         actfunc_args = []
1821         strfunc_args = []
1822         def actfunc(a1, a2, a3, args=actfunc_args):
1823             args.extend([a1, a2, a3])
1824         def strfunc(a1, a2, a3, args=strfunc_args):
1825             args.extend([a1, a2, a3])
1826         af = SCons.Action.ActionFactory(actfunc, strfunc)
1827         af(3, 6, 9)([], [], Environment())
1828         assert actfunc_args == [3, 6, 9], actfunc_args
1829         assert strfunc_args == [3, 6, 9], strfunc_args
1830
1831
1832 class ActionCompareTestCase(unittest.TestCase):
1833
1834     def test_1_solo_name(self):
1835         """Test Lazy Cmd Generator Action get_name alone.
1836
1837         Basically ensures we can locate the builder, comparing it to
1838         itself along the way."""
1839         bar = SCons.Builder.Builder(action = {})
1840         env = Environment( BUILDERS = {'BAR' : bar} )
1841         name = bar.get_name(env)
1842         assert name == 'BAR', name
1843
1844     def test_2_multi_name(self):
1845         """Test LazyCmdGenerator Action get_name multi builders.
1846
1847         Ensure that we can compare builders (and thereby actions) to
1848         each other safely."""
1849         foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo')
1850         bar = SCons.Builder.Builder(action = {})
1851         assert foo != bar
1852         assert foo.action != bar.action
1853         env = Environment( BUILDERS = {'FOO' : foo,
1854                                        'BAR' : bar} )
1855         name = foo.get_name(env)
1856         assert name == 'FOO', name
1857         name = bar.get_name(env)
1858         assert name == 'BAR', name
1859
1860     def test_3_dict_names(self):
1861         """Test Action/Suffix dicts with get_name.
1862
1863         Verifies that Action/Suffix dictionaries work correctly,
1864         especially two builders that can generate the same suffix,
1865         where one of the builders has a suffix dictionary with a None
1866         key."""
1867         
1868         foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo')
1869         bar = SCons.Builder.Builder(action = {}, suffix={None:'.bar'})
1870         bar.add_action('.cow', "$MOO")
1871         dog = SCons.Builder.Builder(suffix = '.bar')
1872         
1873         env = Environment( BUILDERS = {'FOO' : foo,
1874                                        'BAR' : bar,
1875                                        'DOG' : dog} )
1876         
1877         assert foo.get_name(env) == 'FOO', foo.get_name(env)
1878         assert bar.get_name(env) == 'BAR', bar.get_name(env)
1879         assert dog.get_name(env) == 'DOG', dog.get_name(env)
1880
1881
1882 if __name__ == "__main__":
1883     suite = unittest.TestSuite()
1884     tclasses = [ ActionTestCase,
1885                  ActionBaseTestCase,
1886                  _ActionActionTestCase,
1887                  CommandActionTestCase,
1888                  CommandGeneratorActionTestCase,
1889                  FunctionActionTestCase,
1890                  ListActionTestCase,
1891                  LazyActionTestCase,
1892                  ActionCallerTestCase,
1893                  ActionFactoryTestCase,
1894                  ActionCompareTestCase ]
1895     for tclass in tclasses:
1896         names = unittest.getTestCaseNames(tclass, 'test_')
1897         suite.addTests(map(tclass, names))
1898     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1899         sys.exit(1)