08373c3fb5cb96d916bc9770b6917bfc8e55dd4c
[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.Environment.our_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             return "FOO"
1265         a = SCons.Action.CommandGeneratorAction(f)
1266         s = str(a)
1267         assert s == 'FOO', s
1268
1269     def test_genstring(self):
1270         """Test the command generator Action genstring() method
1271         """
1272         def f(target, source, env, for_signature, self=self):
1273             dummy = env['dummy']
1274             self.dummy = dummy
1275             return "$FOO $TARGET $SOURCE $TARGETS $SOURCES"
1276         a = SCons.Action.CommandGeneratorAction(f)
1277         self.dummy = 0
1278         s = a.genstring([], [], env=Environment(FOO='xyzzy', dummy=1))
1279         assert self.dummy == 1, self.dummy
1280         assert s == "$FOO $TARGET $SOURCE $TARGETS $SOURCES", s
1281
1282     def test_execute(self):
1283         """Test executing a command generator Action
1284         """
1285
1286         def f(target, source, env, for_signature, self=self):
1287             dummy = env['dummy']
1288             self.dummy = dummy
1289             s = env.subst("$FOO")
1290             assert s == 'foo baz\nbar ack', s
1291             return "$FOO"
1292         def func_action(target, source, env, self=self):
1293             dummy=env['dummy']
1294             s = env.subst('$foo')
1295             assert s == 'bar', s
1296             self.dummy=dummy
1297         def f2(target, source, env, for_signature, f=func_action):
1298             return f
1299         def ch(sh, escape, cmd, args, env, self=self):
1300             self.cmd.append(cmd)
1301             self.args.append(args)
1302
1303         a = SCons.Action.CommandGeneratorAction(f)
1304         self.dummy = 0
1305         self.cmd = []
1306         self.args = []
1307         a([], [], env=Environment(FOO = 'foo baz\nbar ack',
1308                                           dummy = 1,
1309                                           SPAWN = ch))
1310         assert self.dummy == 1, self.dummy
1311         assert self.cmd == ['foo', 'bar'], self.cmd
1312         assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
1313
1314         b = SCons.Action.CommandGeneratorAction(f2)
1315         self.dummy = 0
1316         b(target=[], source=[], env=Environment(foo =  'bar',
1317                                                         dummy =  2 ))
1318         assert self.dummy==2, self.dummy
1319         del self.dummy
1320
1321         class DummyFile:
1322             def __init__(self, t):
1323                 self.t = t
1324             def rfile(self):
1325                 self.t.rfile_called = 1
1326                 return self
1327             def get_subst_proxy(self):
1328                 return self
1329         def f3(target, source, env, for_signature):
1330             return ''
1331         c = SCons.Action.CommandGeneratorAction(f3)
1332         c(target=[], source=DummyFile(self), env=Environment())
1333         assert self.rfile_called
1334
1335     def test_get_contents(self):
1336         """Test fetching the contents of a command generator Action
1337         """
1338         def f(target, source, env, for_signature):
1339             foo = env['foo']
1340             bar = env['bar']
1341             assert for_signature, for_signature
1342             return [["guux", foo, "$(", "$ignore", "$)", bar,
1343                      '${test("$( foo $bar $)")}' ]]
1344
1345         def test(mystr):
1346             assert mystr == "$( foo $bar $)", mystr
1347             return "test"
1348
1349         env = Environment(foo = 'FFF', bar =  'BBB',
1350                           ignore = 'foo', test=test)
1351         a = SCons.Action.CommandGeneratorAction(f)
1352         c = a.get_contents(target=[], source=[], env=env)
1353         assert c == "guux FFF BBB test", c
1354
1355
1356 class FunctionActionTestCase(unittest.TestCase):
1357
1358     def test___init__(self):
1359         """Test creation of a function Action
1360         """
1361         def func1():
1362             pass
1363         def func2():
1364             pass
1365         def func3():
1366             pass
1367         def func4():
1368             pass
1369
1370         a = SCons.Action.FunctionAction(func1)
1371         assert a.execfunction == func1, a.execfunction
1372         assert isinstance(a.strfunction, types.MethodType), type(a.strfunction)
1373
1374         a = SCons.Action.FunctionAction(func2, strfunction=func3)
1375         assert a.execfunction == func2, a.execfunction
1376         assert a.strfunction == func3, a.strfunction
1377
1378     def test_cmdstr_bad(self):
1379         """Test handling of bad FunctionAction(cmdstr) arguments
1380         """
1381         def func():
1382             pass
1383         try:
1384             a = SCons.Action.FunctionAction(func, [])
1385         except SCons.Errors.UserError, e:
1386             s = str(e)
1387             m = 'Invalid function display variable'
1388             assert string.find(s, m) != -1, 'Unexpected string:  %s' % s
1389         else:
1390             raise "did not catch expected UserError"
1391
1392     def test___str__(self):
1393         """Test the __str__() method for function Actions
1394         """
1395         def func1():
1396             pass
1397         a = SCons.Action.FunctionAction(func1)
1398         s = str(a)
1399         assert s == "func1(target, source, env)", s
1400
1401         class class1:
1402             def __call__(self):
1403                 pass
1404         a = SCons.Action.FunctionAction(class1())
1405         s = str(a)
1406         assert s == "class1(target, source, env)", s
1407
1408     def test_execute(self):
1409         """Test executing a function Action
1410         """
1411         self.inc = 0
1412         def f(target, source, env):
1413             s = env['s']
1414             s.inc = s.inc + 1
1415             s.target = target
1416             s.source=source
1417             assert env.subst("$BAR") == 'foo bar', env.subst("$BAR")
1418             return 0
1419         a = SCons.Action.FunctionAction(f)
1420         a(target=1, source=2, env=Environment(BAR = 'foo bar',
1421                                                       s = self))
1422         assert self.inc == 1, self.inc
1423         assert self.source == [2], self.source
1424         assert self.target == [1], self.target
1425
1426         global count
1427         count = 0
1428         def function1(target, source, env):
1429             global count
1430             count = count + 1
1431             for t in target:
1432                 open(t, 'w').write("function1\n")
1433             return 1
1434
1435         act = SCons.Action.FunctionAction(function1)
1436         r = None
1437         try:
1438             r = act(target = [outfile, outfile2], source=[], env=Environment())
1439         except SCons.Errors.BuildError:
1440             pass
1441         assert r == 1
1442         assert count == 1
1443         c = test.read(outfile, 'r')
1444         assert c == "function1\n", c
1445         c = test.read(outfile2, 'r')
1446         assert c == "function1\n", c
1447
1448         class class1a:
1449             def __init__(self, target, source, env):
1450                 open(env['out'], 'w').write("class1a\n")
1451
1452         act = SCons.Action.FunctionAction(class1a)
1453         r = act([], [], Environment(out = outfile))
1454         assert r.__class__ == class1a
1455         c = test.read(outfile, 'r')
1456         assert c == "class1a\n", c
1457
1458         class class1b:
1459             def __call__(self, target, source, env):
1460                 open(env['out'], 'w').write("class1b\n")
1461                 return 2
1462
1463         act = SCons.Action.FunctionAction(class1b())
1464         r = act([], [], Environment(out = outfile))
1465         assert r == 2
1466         c = test.read(outfile, 'r')
1467         assert c == "class1b\n", c
1468
1469         def build_it(target, source, env, self=self):
1470             self.build_it = 1
1471             return 0
1472         def string_it(target, source, env, self=self):
1473             self.string_it = 1
1474             return None
1475         act = SCons.Action.FunctionAction(build_it, strfunction=string_it)
1476         r = act([], [], Environment())
1477         assert r == 0, r
1478         assert self.build_it
1479         assert self.string_it
1480
1481     def test_get_contents(self):
1482         """Test fetching the contents of a function Action
1483         """
1484
1485         def LocalFunc():
1486             pass
1487
1488         matches = [
1489             "d\000\000S",
1490             "d\x00\x00S",
1491         ]
1492
1493         a = SCons.Action.FunctionAction(GlobalFunc)
1494         c = a.get_contents(target=[], source=[], env=Environment())
1495         assert c in matches, repr(c)
1496
1497         a = SCons.Action.FunctionAction(LocalFunc)
1498         c = a.get_contents(target=[], source=[], env=Environment())
1499         assert c in matches, repr(c)
1500
1501         a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
1502
1503         matches_foo = map(lambda x: x + "foo", matches)
1504
1505         c = a.get_contents(target=[], source=[], env=Environment())
1506         assert c in matches, repr(c)
1507         c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo'))
1508         assert c in matches_foo, repr(c)
1509
1510         class Foo:
1511             def get_contents(self, target, source, env):
1512                 return 'xyzzy'
1513         a = SCons.Action.FunctionAction(Foo())
1514         c = a.get_contents(target=[], source=[], env=Environment())
1515         assert c == 'xyzzy', repr(c)
1516
1517         class LocalClass:
1518             def LocalMethod(self):
1519                 pass
1520         lc = LocalClass()
1521         a = SCons.Action.FunctionAction(lc.LocalMethod)
1522         c = a.get_contents(target=[], source=[], env=Environment())
1523         assert c in matches, repr(c)
1524
1525     def test_strfunction(self):
1526         """Test the FunctionAction.strfunction() method
1527         """
1528         def func():
1529             pass
1530
1531         a = SCons.Action.FunctionAction(func)
1532         s = a.strfunction(target=[], source=[], env=Environment())
1533         assert s == 'func([], [])', s
1534
1535         a = SCons.Action.FunctionAction(func, None)
1536         s = a.strfunction(target=[], source=[], env=Environment())
1537         assert s is None, s
1538
1539         a = SCons.Action.FunctionAction(func, 'function')
1540         s = a.strfunction(target=[], source=[], env=Environment())
1541         assert s == 'function', s
1542
1543 class ListActionTestCase(unittest.TestCase):
1544
1545     def test___init__(self):
1546         """Test creation of a list of subsidiary Actions
1547         """
1548         def func():
1549             pass
1550         a = SCons.Action.ListAction(["x", func, ["y", "z"]])
1551         assert isinstance(a.list[0], SCons.Action.CommandAction)
1552         assert isinstance(a.list[1], SCons.Action.FunctionAction)
1553         assert isinstance(a.list[2], SCons.Action.ListAction)
1554         assert a.list[2].list[0].cmd_list == 'y'
1555
1556     def test___str__(self):
1557         """Test the __str__() method for a list of subsidiary Actions
1558         """
1559         def f(target,source,env):
1560             pass
1561         def g(target,source,env):
1562             pass
1563         a = SCons.Action.ListAction([f, g, "XXX", f])
1564         s = str(a)
1565         assert s == "f(target, source, env)\ng(target, source, env)\nXXX\nf(target, source, env)", s
1566
1567     def test_genstring(self):
1568         """Test the genstring() method for a list of subsidiary Actions
1569         """
1570         def f(target,source,env):
1571             pass
1572         def g(target,source,env,for_signature):
1573             return 'generated %s %s' % (target[0], source[0])
1574         g = SCons.Action.Action(g, generator=1)
1575         a = SCons.Action.ListAction([f, g, "XXX", f])
1576         s = a.genstring(['foo.x'], ['bar.y'], Environment())
1577         assert s == "f(target, source, env)\ngenerated foo.x bar.y\nXXX\nf(target, source, env)", s
1578
1579     def test_execute(self):
1580         """Test executing a list of subsidiary Actions
1581         """
1582         self.inc = 0
1583         def f(target,source,env):
1584             s = env['s']
1585             s.inc = s.inc + 1
1586         a = SCons.Action.ListAction([f, f, f])
1587         a([], [], Environment(s = self))
1588         assert self.inc == 3, self.inc
1589
1590         cmd2 = r'%s %s %s syzygy' % (_python_, act_py, outfile)
1591
1592         def function2(target, source, env):
1593             open(env['out'], 'a').write("function2\n")
1594             return 0
1595
1596         class class2a:
1597             def __call__(self, target, source, env):
1598                 open(env['out'], 'a').write("class2a\n")
1599                 return 0
1600
1601         class class2b:
1602             def __init__(self, target, source, env):
1603                 open(env['out'], 'a').write("class2b\n")
1604         act = SCons.Action.ListAction([cmd2, function2, class2a(), class2b])
1605         r = act([], [], Environment(out = outfile))
1606         assert r.__class__ == class2b
1607         c = test.read(outfile, 'r')
1608         assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
1609
1610     def test_get_contents(self):
1611         """Test fetching the contents of a list of subsidiary Actions
1612         """
1613         self.foo=0
1614         def gen(target, source, env, for_signature):
1615             s = env['s']
1616             s.foo=1
1617             return "y"
1618         a = SCons.Action.ListAction(["x",
1619                                      SCons.Action.Action(gen, generator=1),
1620                                      "z"])
1621         c = a.get_contents(target=[], source=[], env=Environment(s = self))
1622         assert self.foo==1, self.foo
1623         assert c == "xyz", c
1624
1625 class LazyActionTestCase(unittest.TestCase):
1626     def test___init__(self):
1627         """Test creation of a lazy-evaluation Action
1628         """
1629         # Environment variable references should create a special type
1630         # of LazyAction that lazily evaluates the variable for whether
1631         # it's a string or something else before doing anything.
1632         a9 = SCons.Action.Action('$FOO')
1633         assert isinstance(a9, SCons.Action.LazyAction), a9
1634         assert a9.var == 'FOO', a9.var
1635
1636         a10 = SCons.Action.Action('${FOO}')
1637         assert isinstance(a10, SCons.Action.LazyAction), a10
1638         assert a10.var == 'FOO', a10.var
1639
1640     def test_genstring(self):
1641         """Test the lazy-evaluation Action genstring() method
1642         """
1643         def f(target, source, env):
1644             pass
1645         a = SCons.Action.Action('$BAR')
1646         env1 = Environment(BAR=f, s=self)
1647         env2 = Environment(BAR='xxx', s=self)
1648         s = a.genstring([], [], env=env1)
1649         assert s == "f(target, source, env)", s
1650         s = a.genstring([], [], env=env2)
1651         assert s == 'xxx', s
1652
1653     def test_execute(self):
1654         """Test executing a lazy-evaluation Action
1655         """
1656         def f(target, source, env):
1657             s = env['s']
1658             s.test=1
1659             return 0
1660         a = SCons.Action.Action('$BAR')
1661         a([], [], env=Environment(BAR = f, s = self))
1662         assert self.test == 1, self.test
1663         cmd = r'%s %s %s lazy' % (_python_, act_py, outfile)
1664         a([], [], env=Environment(BAR = cmd, s = self))
1665         c = test.read(outfile, 'r')
1666         assert c == "act.py: 'lazy'\n", c
1667
1668     def test_get_contents(self):
1669         """Test fetching the contents of a lazy-evaluation Action
1670         """
1671         a = SCons.Action.Action("${FOO}")
1672         env = Environment(FOO = [["This", "is", "a", "test"]])
1673         c = a.get_contents(target=[], source=[], env=env)
1674         assert c == "This is a test", c
1675
1676 class ActionCallerTestCase(unittest.TestCase):
1677     def test___init__(self):
1678         """Test creation of an ActionCaller"""
1679         ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO' : 4, 'BAR' : 5})
1680         assert ac.parent == 1, ac.parent
1681         assert ac.args == [2, 3], ac.args
1682         assert ac.kw == {'FOO' : 4, 'BAR' : 5}, ac.kw
1683
1684     def test_get_contents(self):
1685         """Test fetching the contents of an ActionCaller"""
1686         def strfunc():
1687             pass
1688
1689         def LocalFunc():
1690             pass
1691
1692         matches = [
1693             "d\000\000S",
1694             "d\x00\x00S"
1695         ]
1696
1697         af = SCons.Action.ActionFactory(GlobalFunc, strfunc)
1698         ac = SCons.Action.ActionCaller(af, [], {})
1699         c = ac.get_contents([], [], Environment())
1700         assert c in matches, repr(c)
1701
1702         af = SCons.Action.ActionFactory(LocalFunc, strfunc)
1703         ac = SCons.Action.ActionCaller(af, [], {})
1704         c = ac.get_contents([], [], Environment())
1705         assert c in matches, repr(c)
1706
1707         matches = [
1708             'd\000\000S',
1709             "d\x00\x00S"
1710         ]
1711
1712         class LocalActFunc:
1713             def __call__(self):
1714                 pass
1715
1716         af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
1717         ac = SCons.Action.ActionCaller(af, [], {})
1718         c = ac.get_contents([], [], Environment())
1719         assert c in matches, repr(c)
1720
1721         af = SCons.Action.ActionFactory(LocalActFunc(), strfunc)
1722         ac = SCons.Action.ActionCaller(af, [], {})
1723         c = ac.get_contents([], [], Environment())
1724         assert c in matches, repr(c)
1725
1726         matches = [
1727             "<built-in function str>",
1728             "<type 'str'>",
1729         ]
1730
1731         af = SCons.Action.ActionFactory(str, strfunc)
1732         ac = SCons.Action.ActionCaller(af, [], {})
1733         c = ac.get_contents([], [], Environment())
1734         assert c == "<built-in function str>" or \
1735                c == "<type 'str'>", repr(c)
1736
1737     def test___call__(self):
1738         """Test calling an ActionCaller"""
1739         actfunc_args = []
1740         def actfunc(a1, a2, a3, args=actfunc_args):
1741             args.extend([a1, a2, a3])
1742         def strfunc(a1, a2, a3):
1743             pass
1744
1745         e = Environment(FOO = 2, BAR = 5)
1746
1747         af = SCons.Action.ActionFactory(actfunc, strfunc)
1748         ac = SCons.Action.ActionCaller(af, ['$__env__', '$FOO', 3], {})
1749         ac([], [], e)
1750         assert actfunc_args[0] is e, actfunc_args
1751         assert actfunc_args[1] == '2', actfunc_args
1752         assert actfunc_args[2] == 3, actfunc_args
1753         del actfunc_args[:]
1754
1755         ac = SCons.Action.ActionCaller(af, [], {'a3' : '$__env__', 'a2' : '$BAR', 'a1' : 4})
1756         ac([], [], e)
1757         assert actfunc_args[0] == 4, actfunc_args
1758         assert actfunc_args[1] == '5', actfunc_args
1759         assert actfunc_args[2] is e, actfunc_args
1760         del actfunc_args[:]
1761
1762     def test_strfunction(self):
1763         """Test calling the ActionCaller strfunction() method"""
1764         strfunc_args = []
1765         def actfunc(a1, a2, a3):
1766             pass
1767         def strfunc(a1, a2, a3, args=strfunc_args):
1768             args.extend([a1, a2, a3])
1769
1770         af = SCons.Action.ActionFactory(actfunc, strfunc)
1771         ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3], {})
1772         ac.strfunction([], [], Environment(FOO = 2))
1773         assert strfunc_args == [1, '2', 3], strfunc_args
1774
1775         del strfunc_args[:]
1776         ac = SCons.Action.ActionCaller(af, [], {'a3' : 6, 'a2' : '$BAR', 'a1' : 4})
1777         ac.strfunction([], [], Environment(BAR = 5))
1778         assert strfunc_args == [4, '5', 6], strfunc_args
1779
1780 class ActionFactoryTestCase(unittest.TestCase):
1781     def test___init__(self):
1782         """Test creation of an ActionFactory"""
1783         def actfunc():
1784             pass
1785         def strfunc():
1786             pass
1787         ac = SCons.Action.ActionFactory(actfunc, strfunc)
1788         assert ac.actfunc is actfunc, ac.actfunc
1789         assert ac.strfunc is strfunc, ac.strfunc
1790
1791     def test___call__(self):
1792         """Test calling whatever's returned from an ActionFactory"""
1793         actfunc_args = []
1794         strfunc_args = []
1795         def actfunc(a1, a2, a3, args=actfunc_args):
1796             args.extend([a1, a2, a3])
1797         def strfunc(a1, a2, a3, args=strfunc_args):
1798             args.extend([a1, a2, a3])
1799         af = SCons.Action.ActionFactory(actfunc, strfunc)
1800         af(3, 6, 9)([], [], Environment())
1801         assert actfunc_args == [3, 6, 9], actfunc_args
1802         assert strfunc_args == [3, 6, 9], strfunc_args
1803
1804
1805 class ActionCompareTestCase(unittest.TestCase):
1806
1807     def test_1_solo_name(self):
1808         """Test Lazy Cmd Generator Action get_name alone.
1809
1810         Basically ensures we can locate the builder, comparing it to
1811         itself along the way."""
1812         bar = SCons.Builder.Builder(action = {})
1813         env = Environment( BUILDERS = {'BAR' : bar} )
1814         name = bar.get_name(env)
1815         assert name == 'BAR', name
1816
1817     def test_2_multi_name(self):
1818         """Test LazyCmdGenerator Action get_name multi builders.
1819
1820         Ensure that we can compare builders (and thereby actions) to
1821         each other safely."""
1822         foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo')
1823         bar = SCons.Builder.Builder(action = {})
1824         assert foo != bar
1825         assert foo.action != bar.action
1826         env = Environment( BUILDERS = {'FOO' : foo,
1827                                        'BAR' : bar} )
1828         name = foo.get_name(env)
1829         assert name == 'FOO', name
1830         name = bar.get_name(env)
1831         assert name == 'BAR', name
1832
1833     def test_3_dict_names(self):
1834         """Test Action/Suffix dicts with get_name.
1835
1836         Verifies that Action/Suffix dictionaries work correctly,
1837         especially two builders that can generate the same suffix,
1838         where one of the builders has a suffix dictionary with a None
1839         key."""
1840         
1841         foo = SCons.Builder.Builder(action = '$FOO', suffix = '.foo')
1842         bar = SCons.Builder.Builder(action = {}, suffix={None:'.bar'})
1843         bar.add_action('.cow', "$MOO")
1844         dog = SCons.Builder.Builder(suffix = '.bar')
1845         
1846         env = Environment( BUILDERS = {'FOO' : foo,
1847                                        'BAR' : bar,
1848                                        'DOG' : dog} )
1849         
1850         assert foo.get_name(env) == 'FOO', foo.get_name(env)
1851         assert bar.get_name(env) == 'BAR', bar.get_name(env)
1852         assert dog.get_name(env) == 'DOG', dog.get_name(env)
1853
1854
1855 if __name__ == "__main__":
1856     suite = unittest.TestSuite()
1857     tclasses = [ ActionTestCase,
1858                  ActionBaseTestCase,
1859                  _ActionActionTestCase,
1860                  CommandActionTestCase,
1861                  CommandGeneratorActionTestCase,
1862                  FunctionActionTestCase,
1863                  ListActionTestCase,
1864                  LazyActionTestCase,
1865                  ActionCallerTestCase,
1866                  ActionFactoryTestCase,
1867                  ActionCompareTestCase ]
1868     for tclass in tclasses:
1869         names = unittest.getTestCaseNames(tclass, 'test_')
1870         suite.addTests(map(tclass, names))
1871     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1872         sys.exit(1)