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