Make CommandGenerationAction able to handle subsidiary actions with no strfunction...
[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', """import os, string, sys
62 f = open(sys.argv[1], 'w')
63 f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n")
64 try:
65     if sys.argv[3]:
66         f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
67 except:
68     pass
69 f.close()
70 if os.environ.has_key( 'ACTPY_PIPE' ):
71     if os.environ.has_key( 'PIPE_STDOUT_FILE' ):
72          stdout_msg = open(os.environ['PIPE_STDOUT_FILE'], 'r').read()
73     else:
74          stdout_msg = "act.py: stdout: executed act.py %s\\n" % string.join(sys.argv[1:])
75     sys.stdout.write( stdout_msg )
76     if os.environ.has_key( 'PIPE_STDERR_FILE' ):
77          stderr_msg = open(os.environ['PIPE_STDERR_FILE'], 'r').read()
78     else:
79          stderr_msg = "act.py: stderr: executed act.py %s\\n" % string.join(sys.argv[1:])
80     sys.stderr.write( stderr_msg )
81 sys.exit(0)
82 """)
83
84 act_py = test.workpath('act.py')
85
86 outfile = test.workpath('outfile')
87 outfile2 = test.workpath('outfile2')
88 pipe_file = test.workpath('pipe.out')
89
90 scons_env = SCons.Environment.Environment()
91
92 # Capture all the stuff the Actions will print,
93 # so it doesn't clutter the output.
94 sys.stdout = StringIO.StringIO()
95
96 class CmdStringHolder:
97     def __init__(self, cmd, literal=None):
98         self.data = str(cmd)
99         self.literal = literal
100
101     def is_literal(self):
102         return self.literal
103
104     def escape(self, escape_func):
105         """Escape the string with the supplied function.  The
106         function is expected to take an arbitrary string, then
107         return it with all special characters escaped and ready
108         for passing to the command interpreter.
109
110         After calling this function, the next call to str() will
111         return the escaped string.
112         """
113
114         if self.is_literal():
115             return escape_func(self.data)
116         elif ' ' in self.data or '\t' in self.data:
117             return '"%s"' % self.data
118         else:
119             return self.data
120
121 class Environment:
122     def __init__(self, **kw):
123         self.d = {}
124         self.d['SHELL'] = scons_env['SHELL']
125         self.d['SPAWN'] = scons_env['SPAWN']
126         self.d['PSPAWN'] = scons_env['PSPAWN']
127         self.d['ESCAPE'] = scons_env['ESCAPE']
128         for k, v in kw.items():
129             self.d[k] = v
130     # Just use the underlying scons_subst*() utility methods.
131     def subst(self, strSubst, raw=0, target=[], source=[], dict=None):
132         return SCons.Util.scons_subst(strSubst, self, raw, target, source, dict)
133     subst_target_source = subst
134     def subst_list(self, strSubst, raw=0, target=[], source=[], dict=None):
135         return SCons.Util.scons_subst_list(strSubst, self, raw, target, source, dict)
136     def __getitem__(self, item):
137         return self.d[item]
138     def __setitem__(self, item, value):
139         self.d[item] = value
140     def has_key(self, item):
141         return self.d.has_key(item)
142     def get(self, key, value):
143         return self.d.get(key, value)
144     def items(self):
145         return self.d.items()
146     def Dictionary(self):
147         return self.d
148     def Copy(self, **kw):
149         res = Environment()
150         res.d = SCons.Environment.our_deepcopy(self.d)
151         for k, v in kw.items():
152             res.d[k] = v
153         return res
154     def sig_dict(self):
155         d = {}
156         for k,v in self.items(): d[k] = v
157         d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__']
158         d['TARGET'] = d['TARGETS'][0]
159         d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
160         d['SOURCE'] = d['SOURCES'][0]
161         return d
162
163 class DummyNode:
164     def __init__(self, name):
165         self.name = name
166     def __str__(self):
167         return self.name
168     def rfile(self):
169         return self
170     def get_subst_proxy(self):
171         return self
172
173 if os.name == 'java':
174     python = os.path.join(sys.prefix, 'jython')
175 else:
176     python = sys.executable
177
178 class ActionTestCase(unittest.TestCase):
179     """Test the Action() factory function"""
180
181     def test_FunctionAction(self):
182         """Test the Action() factory's creation of FunctionAction objects
183         """
184         def foo():
185             pass
186         def bar():
187             pass
188         a1 = SCons.Action.Action(foo)
189         assert isinstance(a1, SCons.Action.FunctionAction), a1
190         assert a1.execfunction == foo, a1.execfunction
191
192         a11 = SCons.Action.Action(foo, strfunction=bar)
193         assert isinstance(a11, SCons.Action.FunctionAction), a11
194         assert a11.execfunction == foo, a11.execfunction
195         assert a11.strfunction == bar, a11.strfunction
196
197     def test_CommandAction(self):
198         """Test the Action() factory's creation of CommandAction objects
199         """
200         a1 = SCons.Action.Action("string")
201         assert isinstance(a1, SCons.Action.CommandAction), a1
202         assert a1.cmd_list == "string", a1.cmd_list
203
204         if hasattr(types, 'UnicodeType'):
205             exec "a2 = SCons.Action.Action(u'string')"
206             exec "assert isinstance(a2, SCons.Action.CommandAction), a2"
207
208         a3 = SCons.Action.Action(["a3"])
209         assert isinstance(a3, SCons.Action.CommandAction), a3
210         assert a3.cmd_list == "a3", a3.cmd_list
211
212         a4 = SCons.Action.Action([[ "explicit", "command", "line" ]])
213         assert isinstance(a4, SCons.Action.CommandAction), a4
214         assert a4.cmd_list == [ "explicit", "command", "line" ], a4.cmd_list
215
216         def foo():
217             pass
218
219         a5 = SCons.Action.Action("string", strfunction=foo)
220         assert isinstance(a5, SCons.Action.CommandAction), a5
221         assert a5.cmd_list == "string", a5.cmd_list
222         assert a5.strfunction == foo, a5.strfunction
223
224     def test_ListAction(self):
225         """Test the Action() factory's creation of ListAction objects
226         """
227         a1 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]])
228         assert isinstance(a1, SCons.Action.ListAction), a1
229         assert isinstance(a1.list[0], SCons.Action.CommandAction), a1.list[0]
230         assert a1.list[0].cmd_list == "x", a1.list[0].cmd_list
231         assert isinstance(a1.list[1], SCons.Action.CommandAction), a1.list[1]
232         assert a1.list[1].cmd_list == "y", a1.list[1].cmd_list
233         assert isinstance(a1.list[2], SCons.Action.CommandAction), a1.list[2]
234         assert a1.list[2].cmd_list == "z", a1.list[2].cmd_list
235         assert isinstance(a1.list[3], SCons.Action.CommandAction), a1.list[3]
236         assert a1.list[3].cmd_list == [ "a", "b", "c" ], a1.list[3].cmd_list
237
238         a2 = SCons.Action.Action("x\ny\nz")
239         assert isinstance(a2, SCons.Action.ListAction), a2
240         assert isinstance(a2.list[0], SCons.Action.CommandAction), a2.list[0]
241         assert a2.list[0].cmd_list == "x", a2.list[0].cmd_list
242         assert isinstance(a2.list[1], SCons.Action.CommandAction), a2.list[1]
243         assert a2.list[1].cmd_list == "y", a2.list[1].cmd_list
244         assert isinstance(a2.list[2], SCons.Action.CommandAction), a2.list[2]
245         assert a2.list[2].cmd_list == "z", a2.list[2].cmd_list
246
247         def foo():
248             pass
249
250         a3 = SCons.Action.Action(["x", foo, "z"])
251         assert isinstance(a3, SCons.Action.ListAction), a3
252         assert isinstance(a3.list[0], SCons.Action.CommandAction), a3.list[0]
253         assert a3.list[0].cmd_list == "x", a3.list[0].cmd_list
254         assert isinstance(a3.list[1], SCons.Action.FunctionAction), a3.list[1]
255         assert a3.list[1].execfunction == foo, a3.list[1].execfunction
256         assert isinstance(a3.list[2], SCons.Action.CommandAction), a3.list[2]
257         assert a3.list[2].cmd_list == "z", a3.list[2].cmd_list
258
259         a4 = SCons.Action.Action(["x", "y"], strfunction=foo)
260         assert isinstance(a4, SCons.Action.ListAction), a4
261         assert isinstance(a4.list[0], SCons.Action.CommandAction), a4.list[0]
262         assert a4.list[0].cmd_list == "x", a4.list[0].cmd_list
263         assert isinstance(a4.list[1], SCons.Action.CommandAction), a4.list[1]
264         assert a4.list[1].cmd_list == "y", a4.list[1].cmd_list
265         assert a4.strfunction == foo, a4.strfunction
266
267         a5 = SCons.Action.Action("x\ny", strfunction=foo)
268         assert isinstance(a5, SCons.Action.ListAction), a5
269         assert isinstance(a5.list[0], SCons.Action.CommandAction), a5.list[0]
270         assert a5.list[0].cmd_list == "x", a5.list[0].cmd_list
271         assert isinstance(a5.list[1], SCons.Action.CommandAction), a5.list[1]
272         assert a5.list[1].cmd_list == "y", a5.list[1].cmd_list
273         assert a5.strfunction == foo, a5.strfunction
274
275     def test_CommandGeneratorAction(self):
276         """Test the Action() factory's creation of CommandGeneratorAction objects
277         """
278         def foo():
279             pass
280         def bar():
281             pass
282         cg = SCons.Action.CommandGenerator(foo)
283
284         a1 = SCons.Action.Action(cg)
285         assert isinstance(a1, SCons.Action.CommandGeneratorAction), a1
286         assert a1.generator is foo, a1.generator
287
288         a2 = SCons.Action.Action(cg, strfunction=bar)
289         assert isinstance(a2, SCons.Action.CommandGeneratorAction), a2
290         assert a2.generator is foo, a2.generator
291         assert a2.strfunction is bar, a2.strfunction
292
293     def test_LazyCmdGeneratorAction(self):
294         """Test the Action() factory's creation of lazy CommandGeneratorAction objects
295         """
296         def foo():
297             pass
298
299         a1 = SCons.Action.Action("$FOO")
300         assert isinstance(a1, SCons.Action.CommandGeneratorAction), a1
301         assert isinstance(a1.generator, SCons.Action.LazyCmdGenerator), a1.generator
302
303         a2 = SCons.Action.Action("$FOO", strfunction=foo)
304         assert isinstance(a2, SCons.Action.CommandGeneratorAction), a2
305         assert isinstance(a2.generator, SCons.Action.LazyCmdGenerator), a2.generator
306         assert a2.strfunction is foo, a2.strfunction
307
308     def test_no_action(self):
309         """Test when the Action() factory can't create an action object
310         """
311         a5 = SCons.Action.Action(1)
312         assert a5 is None, a5
313
314     def test_reentrance(self):
315         """Test the Action() factory when the action is already an Action object
316         """
317         a1 = SCons.Action.Action("foo")
318         a2 = SCons.Action.Action(a1)
319         assert a2 is a1, a2
320
321 class ActionBaseTestCase(unittest.TestCase):
322
323     def test__init__(self):
324         """Test creation of ActionBase objects
325         """
326
327         def func():
328             pass
329
330         a = SCons.Action.ActionBase()
331         assert not hasattr(a, 'strfunction')
332
333         assert SCons.Action.ActionBase(kwarg = 1)
334         assert not hasattr(a, 'strfunction')
335         assert not hasattr(a, 'kwarg')
336
337         a = SCons.Action.ActionBase(func)
338         assert a.strfunction is func, a.strfunction
339
340         a = SCons.Action.ActionBase(strfunction=func)
341         assert a.strfunction is func, a.strfunction
342
343     def test___cmp__(self):
344         """Test Action comparison
345         """
346         a1 = SCons.Action.Action("x")
347         a2 = SCons.Action.Action("x")
348         assert a1 == a2
349         a3 = SCons.Action.Action("y")
350         assert a1 != a3
351         assert a2 != a3
352
353     def test___call__(self):
354         """Test calling an Action
355         """
356         save_stdout = sys.stdout
357
358         save_print_actions = SCons.Action.print_actions
359         save_print_actions_presub = SCons.Action.print_actions_presub
360         save_execute_actions = SCons.Action.execute_actions
361         #SCons.Action.print_actions = 0
362
363         try:
364             env = Environment()
365
366             def execfunc(target, source, env):
367                 assert type(target) is type([]), type(target)
368                 assert type(source) is type([]), type(source)
369                 return 7
370             a = SCons.Action.Action(execfunc)
371
372             sio = StringIO.StringIO()
373             sys.stdout = sio
374             result = a("out", "in", env)
375             assert result == 7, result
376             s = sio.getvalue()
377             assert s == 'execfunc("out", "in")\n', s
378
379             SCons.Action.execute_actions = 0
380
381             sio = StringIO.StringIO()
382             sys.stdout = sio
383             result = a("out", "in", env)
384             assert result == 0, result
385             s = sio.getvalue()
386             assert s == 'execfunc("out", "in")\n', s
387
388             SCons.Action.print_actions_presub = 1
389
390             sio = StringIO.StringIO()
391             sys.stdout = sio
392             result = a("out", "in", env)
393             assert result == 0, result
394             s = sio.getvalue()
395             assert s == 'Building out with action(s):\n  execfunc(env, target, source)\nexecfunc("out", "in")\n', s
396
397             sio = StringIO.StringIO()
398             sys.stdout = sio
399             result = a("out", "in", env, presub=0)
400             assert result == 0, result
401             s = sio.getvalue()
402             assert s == 'execfunc("out", "in")\n', s
403
404             sio = StringIO.StringIO()
405             sys.stdout = sio
406             result = a("out", "in", env, presub=0, execute=1, show=0)
407             assert result == 7, result
408             s = sio.getvalue()
409             assert s == '', s
410
411             sys.stdout = save_stdout
412             errfunc_result = []
413
414             def errfunc(stat, result=errfunc_result):
415                 result.append(stat)
416
417             result = a("out", "in", env, errfunc=errfunc)
418             assert result == 0, result
419             assert errfunc_result == [], errfunc_result
420
421             result = a("out", "in", env, execute=1, errfunc=errfunc)
422             assert result == 7, result
423             assert errfunc_result == [7], errfunc_result
424
425         finally:
426             sys.stdout = save_stdout
427             SCons.Action.print_actions = save_print_actions
428             SCons.Action.print_actions_presub = save_print_actions_presub
429             SCons.Action.execute_actions = save_execute_actions
430
431     def test_presub(self):
432         """Test the presub() method
433         """
434         env = Environment()
435         a = SCons.Action.Action("x")
436         s = a.presub(env)
437         assert s == ['x'], s
438
439         a = SCons.Action.Action(["y", "z"])
440         s = a.presub(env)
441         assert s == ['y', 'z'], s
442
443         def func():
444             pass
445         a = SCons.Action.Action(func)
446         s = a.presub(env)
447         assert s == ["func(env, target, source)"], s
448
449         def gen(target, source, env, for_signature):
450             return 'generat' + env.get('GEN', 'or')
451         a = SCons.Action.Action(SCons.Action.CommandGenerator(gen))
452         s = a.presub(env)
453         assert s == ["generator"], s
454         s = a.presub(Environment(GEN = 'ed'))
455         assert s == ["generated"], s
456
457         a = SCons.Action.Action("$ACT")
458         s = a.presub(env)
459         assert s == [''], s
460         s = a.presub(Environment(ACT = 'expanded action'))
461         assert s == ['expanded action'], s
462
463     def test_get_actions(self):
464         """Test the get_actions() method
465         """
466         a = SCons.Action.Action("x")
467         l = a.get_actions()
468         assert l == [a], l
469
470     def test_add(self):
471         """Test adding Actions to stuff."""
472         # Adding actions to other Actions or to stuff that can
473         # be converted into an Action should produce a ListAction
474         # containing all the Actions.
475         def bar():
476             return None
477         baz = SCons.Action.CommandGenerator(bar)
478         act1 = SCons.Action.Action('foo bar')
479         act2 = SCons.Action.Action([ 'foo', bar ])
480
481         sum = act1 + act2
482         assert isinstance(sum, SCons.Action.ListAction), str(sum)
483         assert len(sum.list) == 3, len(sum.list)
484         assert map(lambda x: isinstance(x, SCons.Action.ActionBase),
485                    sum.list) == [ 1, 1, 1 ]
486
487         sum = act1 + act1
488         assert isinstance(sum, SCons.Action.ListAction), str(sum)
489         assert len(sum.list) == 2, len(sum.list)
490
491         sum = act2 + act2
492         assert isinstance(sum, SCons.Action.ListAction), str(sum)
493         assert len(sum.list) == 4, len(sum.list)
494
495         # Should also be able to add command generators to each other
496         # or to actions
497         sum = baz + baz
498         assert isinstance(sum, SCons.Action.ListAction), str(sum)
499         assert len(sum.list) == 2, len(sum.list)
500
501         sum = baz + act1
502         assert isinstance(sum, SCons.Action.ListAction), str(sum)
503         assert len(sum.list) == 2, len(sum.list)
504
505         sum = act2 + baz
506         assert isinstance(sum, SCons.Action.ListAction), str(sum)
507         assert len(sum.list) == 3, len(sum.list)
508
509         # Also should be able to add Actions to anything that can
510         # be converted into an action.
511         sum = act1 + bar
512         assert isinstance(sum, SCons.Action.ListAction), str(sum)
513         assert len(sum.list) == 2, len(sum.list)
514         assert isinstance(sum.list[1], SCons.Action.FunctionAction)
515
516         sum = 'foo bar' + act2
517         assert isinstance(sum, SCons.Action.ListAction), str(sum)
518         assert len(sum.list) == 3, len(sum.list)
519         assert isinstance(sum.list[0], SCons.Action.CommandAction)
520
521         sum = [ 'foo', 'bar' ] + act1
522         assert isinstance(sum, SCons.Action.ListAction), str(sum)
523         assert len(sum.list) == 3, sum.list
524         assert isinstance(sum.list[0], SCons.Action.CommandAction)
525         assert isinstance(sum.list[1], SCons.Action.CommandAction)
526
527         sum = act2 + [ baz, bar ]
528         assert isinstance(sum, SCons.Action.ListAction), str(sum)
529         assert len(sum.list) == 4, len(sum.list)
530         assert isinstance(sum.list[2], SCons.Action.CommandGeneratorAction)
531         assert isinstance(sum.list[3], SCons.Action.FunctionAction)
532
533         try:
534             sum = act2 + 1
535         except TypeError:
536             pass
537         else:
538             assert 0, "Should have thrown a TypeError adding to an int."
539
540         try:
541             sum = 1 + act2
542         except TypeError:
543             pass
544         else:
545             assert 0, "Should have thrown a TypeError adding to an int."
546
547 class CommandActionTestCase(unittest.TestCase):
548
549     def test___init__(self):
550         """Test creation of a command Action
551         """
552         a = SCons.Action.CommandAction(["xyzzy"])
553         assert a.cmd_list == [ "xyzzy" ], a.cmd_list
554
555     def test___str__(self):
556         """Test fetching the pre-substitution string for command Actions
557         """
558         env = Environment()
559         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
560         s = str(act)
561         assert s == 'xyzzy $TARGET $SOURCE', s
562
563         act = SCons.Action.CommandAction(['xyzzy',
564                                           '$TARGET', '$SOURCE',
565                                           '$TARGETS', '$SOURCES'])
566         s = str(act)
567         assert s == "['xyzzy', '$TARGET', '$SOURCE', '$TARGETS', '$SOURCES']", s
568
569     def test_genstring(self):
570         """Test the genstring() method for command Actions
571         """
572
573         env = Environment()
574         t1 = DummyNode('t1')
575         t2 = DummyNode('t2')
576         s1 = DummyNode('s1')
577         s2 = DummyNode('s2')
578         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
579         expect = 'xyzzy $TARGET $SOURCE'
580         s = act.genstring([], [], env)
581         assert s == expect, s
582         s = act.genstring([t1], [s1], env)
583         assert s == expect, s
584         s = act.genstring([t1, t2], [s1, s2], env)
585         assert s == expect, s
586
587         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES')
588         expect = 'xyzzy $TARGETS $SOURCES'
589         s = act.genstring([], [], env)
590         assert s == expect, s
591         s = act.genstring([t1], [s1], env)
592         assert s == expect, s
593         s = act.genstring([t1, t2], [s1, s2], env)
594         assert s == expect, s
595
596         act = SCons.Action.CommandAction(['xyzzy',
597                                           '$TARGET', '$SOURCE',
598                                           '$TARGETS', '$SOURCES'])
599         expect = "['xyzzy', '$TARGET', '$SOURCE', '$TARGETS', '$SOURCES']"
600         s = act.genstring([], [], env)
601         assert s == expect, s
602         s = act.genstring([t1], [s1], env)
603         assert s == expect, s
604         s = act.genstring([t1, t2], [s1, s2], env)
605         assert s == expect, s
606
607     def test_strfunction(self):
608         """Test fetching the string representation of command Actions
609         """
610
611         env = Environment()
612         t1 = DummyNode('t1')
613         t2 = DummyNode('t2')
614         s1 = DummyNode('s1')
615         s2 = DummyNode('s2')
616         act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
617         s = act.strfunction([], [], env)
618         assert s == 'xyzzy', s
619         s = act.strfunction([t1], [s1], env)
620         assert s == 'xyzzy t1 s1', s
621         s = act.strfunction([t1, t2], [s1, s2], env)
622         assert s == 'xyzzy t1 s1', s
623
624         act = SCons.Action.CommandAction('xyzzy $TARGETS $SOURCES')
625         s = act.strfunction([], [], env)
626         assert s == 'xyzzy', s
627         s = act.strfunction([t1], [s1], env)
628         assert s == 'xyzzy t1 s1', s
629         s = act.strfunction([t1, t2], [s1, s2], env)
630         assert s == 'xyzzy t1 t2 s1 s2', s
631
632         act = SCons.Action.CommandAction(['xyzzy',
633                                           '$TARGET', '$SOURCE',
634                                           '$TARGETS', '$SOURCES'])
635         s = act.strfunction([], [], env)
636         assert s == 'xyzzy', s
637         s = act.strfunction([t1], [s1], env)
638         assert s == 'xyzzy t1 s1 t1 s1', s
639         s = act.strfunction([t1, t2], [s1, s2], env)
640         assert s == 'xyzzy t1 s1 t1 t2 s1 s2', s
641
642         def sf(target, source, env):
643             return "sf was called"
644         act = SCons.Action.CommandAction('foo', strfunction=sf)
645         s = act.strfunction([], [], env)
646         assert s == "sf was called", s
647
648     def test_execute(self):
649         """Test execution of command Actions
650
651         """
652         try:
653             env = self.env
654         except AttributeError:
655             env = Environment()
656
657         cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
658
659         act = SCons.Action.CommandAction(cmd1)
660         r = act([], [], env.Copy())
661         assert r == 0
662         c = test.read(outfile, 'r')
663         assert c == "act.py: 'xyzzy'\n", c
664
665         cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
666
667         act = SCons.Action.CommandAction(cmd2)
668         r = act(DummyNode('foo'), [], env.Copy())
669         assert r == 0
670         c = test.read(outfile, 'r')
671         assert c == "act.py: 'foo'\n", c
672
673         cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
674
675         act = SCons.Action.CommandAction(cmd3)
676         r = act(map(DummyNode, ['aaa', 'bbb']), [], env.Copy())
677         assert r == 0
678         c = test.read(outfile, 'r')
679         assert c == "act.py: 'aaa' 'bbb'\n", c
680
681         cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
682
683         act = SCons.Action.CommandAction(cmd4)
684         r = act([], [DummyNode('one'), DummyNode('two')], env.Copy())
685         assert r == 0
686         c = test.read(outfile, 'r')
687         assert c == "act.py: 'one' 'two'\n", c
688
689         cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
690
691         act = SCons.Action.CommandAction(cmd4)
692         sources = [DummyNode('three'), DummyNode('four'), DummyNode('five')]
693         env2 = env.Copy()
694         r = act([], source = sources, env = env2)
695         assert r == 0
696         c = test.read(outfile, 'r')
697         assert c == "act.py: 'three' 'four'\n", c
698
699         cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
700
701         act = SCons.Action.CommandAction(cmd5)
702         env5 = Environment()
703         if scons_env.has_key('ENV'):
704             env5['ENV'] = scons_env['ENV']
705             PATH = scons_env['ENV'].get('PATH', '')
706         else:
707             env5['ENV'] = {}
708             PATH = ''
709
710         env5['ENV']['XYZZY'] = 'xyzzy'
711         r = act(target = DummyNode('out5'), source = [], env = env5)
712
713         act = SCons.Action.CommandAction(cmd5)
714         r = act(target = DummyNode('out5'),
715                 source = [],
716                 env = env.Copy(ENV = {'XYZZY' : 'xyzzy5',
717                                       'PATH' : PATH}))
718         assert r == 0
719         c = test.read(outfile, 'r')
720         assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy5'\n", c
721
722         class Obj:
723             def __init__(self, str):
724                 self._str = str
725             def __str__(self):
726                 return self._str
727             def rfile(self):
728                 return self
729             def get_subst_proxy(self):
730                 return self
731
732         cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (python, act_py, outfile)
733
734         act = SCons.Action.CommandAction(cmd6)
735         r = act(target = [Obj('111'), Obj('222')],
736                         source = [Obj('333'), Obj('444'), Obj('555')],
737                         env = env.Copy())
738         assert r == 0
739         c = test.read(outfile, 'r')
740         assert c == "act.py: '222' '111' '333' '444'\n", c
741
742         if os.name == 'nt':
743             # NT treats execs of directories and non-executable files
744             # as "file not found" errors
745             expect_nonexistent = 1
746             expect_nonexecutable = 1
747         elif sys.platform == 'cygwin':
748             expect_nonexistent = 127
749             expect_nonexecutable = 127
750         else:
751             expect_nonexistent = 127
752             expect_nonexecutable = 126
753
754         # Test that a nonexistent command returns 127
755         act = SCons.Action.CommandAction(python + "_no_such_command_")
756         r = act([], [], env.Copy(out = outfile))
757         assert r == expect_nonexistent, "r == %d" % r
758
759         # Test that trying to execute a directory returns 126
760         dir, tail = os.path.split(python)
761         act = SCons.Action.CommandAction(dir)
762         r = act([], [], env.Copy(out = outfile))
763         assert r == expect_nonexecutable, "r == %d" % r
764
765         # Test that trying to execute a non-executable file returns 126
766         act = SCons.Action.CommandAction(outfile)
767         r = act([], [], env.Copy(out = outfile))
768         assert r == expect_nonexecutable, "r == %d" % r
769
770     def test_pipe_execute(self):
771         """Test capturing piped output from an action
772         """
773         pipe = open( pipe_file, "w" )
774         self.env = Environment(ENV = {'ACTPY_PIPE' : '1'}, PIPE_BUILD = 1,
775                                PSTDOUT = pipe, PSTDERR = pipe)
776         # everything should also work when piping output
777         self.test_execute()
778         self.env['PSTDOUT'].close()
779         pipe_out = test.read( pipe_file )
780
781         act_out = "act.py: stdout: executed act.py"
782         act_err = "act.py: stderr: executed act.py"
783
784         # Since we are now using select(), stdout and stderr can be
785         # intermixed, so count the lines separately.
786         outlines = re.findall(act_out, pipe_out)
787         errlines = re.findall(act_err, pipe_out)
788         assert len(outlines) == 6, pipe_out + repr(outlines)
789         assert len(errlines) == 6, pipe_out + repr(errlines)
790
791         # test redirection operators
792         def test_redirect(self, redir, stdout_msg, stderr_msg):
793             cmd = r'%s %s %s xyzzy %s' % (python, act_py, outfile, redir)
794             # Write the output and error messages to files because Win32
795             # can't handle strings that are too big in its external
796             # environment (os.spawnve() returns EINVAL, "Invalid
797             # argument").
798             stdout_file = test.workpath('stdout_msg')
799             stderr_file = test.workpath('stderr_msg')
800             open(stdout_file, 'w').write(stdout_msg)
801             open(stderr_file, 'w').write(stderr_msg)
802             pipe = open( pipe_file, "w" )
803             act = SCons.Action.CommandAction(cmd)
804             env = Environment( ENV = {'ACTPY_PIPE' : '1',
805                                       'PIPE_STDOUT_FILE' : stdout_file,
806                                       'PIPE_STDERR_FILE' : stderr_file},
807                                PIPE_BUILD = 1,
808                                PSTDOUT = pipe, PSTDERR = pipe )
809             r = act([], [], env)
810             pipe.close()
811             assert r == 0
812             return (test.read(outfile2, 'r'), test.read(pipe_file, 'r'))
813
814         (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
815                                                act_out, act_err)
816         assert redirected == act_out
817         assert pipe_out == act_err
818
819         (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2,
820                                                act_out, act_err)
821         assert redirected == act_err
822         assert pipe_out == act_out
823
824         (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2,
825                                                act_out, act_err)
826         assert (redirected == act_out + act_err or
827                 redirected == act_err + act_out)
828         assert pipe_out == ""
829
830         act_err = "Long Command Output\n"*3000
831         # the size of the string should exceed the system's default block size
832         act_out = ""
833         (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
834                                                act_out, act_err)
835         assert (redirected == act_out)
836         assert (pipe_out == act_err)
837
838     def test_set_handler(self):
839         """Test setting the command handler...
840         """
841         class Test:
842             def __init__(self):
843                 self.executed = 0
844         t=Test()
845         def func(sh, escape, cmd, args, env, test=t):
846             test.executed = args
847             test.shell = sh
848             return 0
849         def escape_func(cmd):
850             return '**' + cmd + '**'
851
852         class LiteralStr:
853             def __init__(self, x):
854                 self.data = x
855             def __str__(self):
856                 return self.data
857             def escape(self, escape_func):
858                 return escape_func(self.data)
859             def is_literal(self):
860                 return 1
861
862         a = SCons.Action.CommandAction(["xyzzy"])
863         e = Environment(SPAWN = func)
864         a([], [], e)
865         assert t.executed == [ 'xyzzy' ], t.executed
866
867         a = SCons.Action.CommandAction(["xyzzy"])
868         e = Environment(SPAWN = func, SHELL = 'fake shell')
869         a([], [], e)
870         assert t.executed == [ 'xyzzy' ], t.executed
871         assert t.shell == 'fake shell', t.shell
872
873         a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ])
874         e = Environment(SPAWN = func, ESCAPE = escape_func)
875         a([], [], e)
876         assert t.executed == [ '**xyzzy**' ], t.executed
877
878     def test_get_contents(self):
879         """Test fetching the contents of a command Action
880         """
881         def CmdGen(target, source, env, for_signature):
882             assert for_signature
883             return "%s %s" % \
884                    (env["foo"], env["bar"])
885
886         # The number 1 is there to make sure all args get converted to strings.
887         a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
888                                         "$)", "|", "$baz", 1])
889         c = a.get_contents(target=[], source=[],
890                            env=Environment(foo = 'FFF', bar = 'BBB',
891                                            baz = CmdGen))
892         assert c == "| | FFF BBB 1", c
893
894         # Make sure that CommandActions use an Environment's
895         # subst_target_source() method for substitution.
896         class SpecialEnvironment(Environment):
897             def subst_target_source(self, strSubst, raw=0, target=[], source=[], dict=None):
898                 return 'subst_target_source: ' + strSubst
899
900         c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'),
901                            env=SpecialEnvironment(foo = 'GGG', bar = 'CCC',
902                                                   baz = 'ZZZ'))
903         assert c == 'subst_target_source: | $( $foo | $bar $) | $baz 1', c
904
905         # We've discussed using the real target and source names in a
906         # CommandAction's signature contents.  This would have have the
907         # advantage of recompiling when a file's name changes (keeping
908         # debug info current), but it would currently break repository
909         # logic that will change the file name based on whether the
910         # files come from a repository or locally.  If we ever move to
911         # that scheme, then all of the '__t1__' and '__s6__' file names
912         # in the asserts below would change to 't1' and 's6' and the
913         # like.
914         t = map(DummyNode, ['t1', 't2', 't3', 't4', 't5', 't6'])
915         s = map(DummyNode, ['s1', 's2', 's3', 's4', 's5', 's6'])
916         env = Environment()
917
918         a = SCons.Action.CommandAction(["$TARGET"])
919         c = a.get_contents(target=t, source=s, env=env)
920         assert c == "t1", c
921         c = a.get_contents(target=t, source=s, env=env, dict={})
922         assert c == "", c
923
924         a = SCons.Action.CommandAction(["$TARGETS"])
925         c = a.get_contents(target=t, source=s, env=env)
926         assert c == "t1 t2 t3 t4 t5 t6", c
927         c = a.get_contents(target=t, source=s, env=env, dict={})
928         assert c == "", c
929
930         a = SCons.Action.CommandAction(["${TARGETS[2]}"])
931         c = a.get_contents(target=t, source=s, env=env)
932         assert c == "t3", c
933
934         a = SCons.Action.CommandAction(["${TARGETS[3:5]}"])
935         c = a.get_contents(target=t, source=s, env=env)
936         assert c == "t4 t5", c
937
938         a = SCons.Action.CommandAction(["$SOURCE"])
939         c = a.get_contents(target=t, source=s, env=env)
940         assert c == "s1", c
941         c = a.get_contents(target=t, source=s, env=env, dict={})
942         assert c == "", c
943
944         a = SCons.Action.CommandAction(["$SOURCES"])
945         c = a.get_contents(target=t, source=s, env=env)
946         assert c == "s1 s2 s3 s4 s5 s6", c
947         c = a.get_contents(target=t, source=s, env=env, dict={})
948         assert c == "", c
949
950         a = SCons.Action.CommandAction(["${SOURCES[2]}"])
951         c = a.get_contents(target=t, source=s, env=env)
952         assert c == "s3", c
953
954         a = SCons.Action.CommandAction(["${SOURCES[3:5]}"])
955         c = a.get_contents(target=t, source=s, env=env)
956         assert c == "s4 s5", c
957
958 class CommandGeneratorActionTestCase(unittest.TestCase):
959
960     def test___init__(self):
961         """Test creation of a command generator Action
962         """
963         def f(target, source, env):
964             pass
965         a = SCons.Action.CommandGeneratorAction(f)
966         assert a.generator == f
967
968     def test___str__(self):
969         """Test the pre-substitution strings for command generator Actions
970         """
971         def f(target, source, env, for_signature, self=self):
972             return "FOO"
973         a = SCons.Action.CommandGeneratorAction(f)
974         s = str(a)
975         assert s == 'FOO', s
976
977     def test_genstring(self):
978         """Test the command generator Action genstring() method
979         """
980         def f(target, source, env, for_signature, self=self):
981             dummy = env['dummy']
982             self.dummy = dummy
983             return "$FOO $TARGET $SOURCE $TARGETS $SOURCES"
984         a = SCons.Action.CommandGeneratorAction(f)
985         self.dummy = 0
986         s = a.genstring([], [], env=Environment(FOO='xyzzy', dummy=1))
987         assert self.dummy == 1, self.dummy
988         assert s == "$FOO $TARGET $SOURCE $TARGETS $SOURCES", s
989
990     def test_strfunction(self):
991         """Test the command generator Action string function
992         """
993         def f(target, source, env, for_signature, self=self):
994             dummy = env['dummy']
995             self.dummy = dummy
996             return "$FOO"
997         a = SCons.Action.CommandGeneratorAction(f)
998         self.dummy = 0
999         s = a.strfunction([], [], env=Environment(FOO='xyzzy', dummy=1))
1000         assert self.dummy == 1, self.dummy
1001         assert s == 'xyzzy', s
1002
1003         def sf(target, source, env):
1004             return "sf was called"
1005         a = SCons.Action.CommandGeneratorAction(f, strfunction=sf)
1006         s = a.strfunction([], [], env=Environment())
1007         assert s == "sf was called", s
1008
1009         def f(target, source, env, for_signature, self=self):
1010             def null(target, source, env):
1011                 pass
1012             return SCons.Action.Action(null, strfunction=None)
1013         a = SCons.Action.CommandGeneratorAction(f)
1014         s = a.strfunction([], [], env=Environment())
1015
1016     def test_execute(self):
1017         """Test executing a command generator Action
1018         """
1019
1020         def f(target, source, env, for_signature, self=self):
1021             dummy = env['dummy']
1022             self.dummy = dummy
1023             s = env.subst("$FOO")
1024             assert s == 'foo baz\nbar ack', s
1025             return "$FOO"
1026         def func_action(target, source, env, self=self):
1027             dummy=env['dummy']
1028             s = env.subst('$foo')
1029             assert s == 'bar', s
1030             self.dummy=dummy
1031         def f2(target, source, env, for_signature, f=func_action):
1032             return f
1033         def ch(sh, escape, cmd, args, env, self=self):
1034             self.cmd.append(cmd)
1035             self.args.append(args)
1036
1037         a = SCons.Action.CommandGeneratorAction(f)
1038         self.dummy = 0
1039         self.cmd = []
1040         self.args = []
1041         a([], [], env=Environment(FOO = 'foo baz\nbar ack',
1042                                           dummy = 1,
1043                                           SPAWN = ch))
1044         assert self.dummy == 1, self.dummy
1045         assert self.cmd == ['foo', 'bar'], self.cmd
1046         assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
1047
1048         b = SCons.Action.CommandGeneratorAction(f2)
1049         self.dummy = 0
1050         b(target=[], source=[], env=Environment(foo =  'bar',
1051                                                         dummy =  2 ))
1052         assert self.dummy==2, self.dummy
1053         del self.dummy
1054
1055         class DummyFile:
1056             def __init__(self, t):
1057                 self.t = t
1058             def rfile(self):
1059                 self.t.rfile_called = 1
1060                 return self
1061             def get_subst_proxy(self):
1062                 return self
1063         def f3(target, source, env, for_signature):
1064             return ''
1065         c = SCons.Action.CommandGeneratorAction(f3)
1066         c(target=[], source=DummyFile(self), env=Environment())
1067         assert self.rfile_called
1068
1069     def test_get_contents(self):
1070         """Test fetching the contents of a command generator Action
1071         """
1072         def f(target, source, env, for_signature):
1073             foo = env['foo']
1074             bar = env['bar']
1075             assert for_signature, for_signature
1076             return [["guux", foo, "$(", "$ignore", "$)", bar,
1077                      '${test("$( foo $bar $)")}' ]]
1078
1079         def test(mystr):
1080             assert mystr == "$( foo $bar $)", mystr
1081             return "test"
1082
1083         env = Environment(foo = 'FFF', bar =  'BBB',
1084                           ignore = 'foo', test=test)
1085         a = SCons.Action.CommandGeneratorAction(f)
1086         c = a.get_contents(target=[], source=[], env=env)
1087         assert c == "guux FFF BBB test", c
1088         c = a.get_contents(target=[], source=[], env=env, dict={})
1089         assert c == "guux FFF BBB test", c
1090
1091
1092 class FunctionActionTestCase(unittest.TestCase):
1093
1094     def test___init__(self):
1095         """Test creation of a function Action
1096         """
1097         def func1():
1098             pass
1099         def func2():
1100             pass
1101         def func3():
1102             pass
1103         def func4():
1104             pass
1105
1106         a = SCons.Action.FunctionAction(func1)
1107         assert a.execfunction == func1, a.execfunction
1108         assert isinstance(a.strfunction, types.MethodType), type(a.strfunction)
1109
1110         a = SCons.Action.FunctionAction(func2, strfunction=func3)
1111         assert a.execfunction == func2, a.execfunction
1112         assert a.strfunction == func3, a.strfunction
1113
1114     def test___str__(self):
1115         """Test the __str__() method for function Actions
1116         """
1117         def func1():
1118             pass
1119         a = SCons.Action.FunctionAction(func1)
1120         s = str(a)
1121         assert s == "func1(env, target, source)", s
1122
1123         class class1:
1124             def __call__(self):
1125                 pass
1126         a = SCons.Action.FunctionAction(class1())
1127         s = str(a)
1128         assert s == "class1(env, target, source)", s
1129
1130     def test_execute(self):
1131         """Test executing a function Action
1132         """
1133         self.inc = 0
1134         def f(target, source, env):
1135             s = env['s']
1136             s.inc = s.inc + 1
1137             s.target = target
1138             s.source=source
1139             assert env.subst("$BAR") == 'foo bar', env.subst("$BAR")
1140             return 0
1141         a = SCons.Action.FunctionAction(f)
1142         a(target=1, source=2, env=Environment(BAR = 'foo bar',
1143                                                       s = self))
1144         assert self.inc == 1, self.inc
1145         assert self.source == [2], self.source
1146         assert self.target == [1], self.target
1147
1148         global count
1149         count = 0
1150         def function1(target, source, env):
1151             global count
1152             count = count + 1
1153             for t in target:
1154                 open(t, 'w').write("function1\n")
1155             return 1
1156
1157         act = SCons.Action.FunctionAction(function1)
1158         r = None
1159         try:
1160             r = act(target = [outfile, outfile2], source=[], env=Environment())
1161         except SCons.Errors.BuildError:
1162             pass
1163         assert r == 1
1164         assert count == 1
1165         c = test.read(outfile, 'r')
1166         assert c == "function1\n", c
1167         c = test.read(outfile2, 'r')
1168         assert c == "function1\n", c
1169
1170         class class1a:
1171             def __init__(self, target, source, env):
1172                 open(env['out'], 'w').write("class1a\n")
1173
1174         act = SCons.Action.FunctionAction(class1a)
1175         r = act([], [], Environment(out = outfile))
1176         assert r.__class__ == class1a
1177         c = test.read(outfile, 'r')
1178         assert c == "class1a\n", c
1179
1180         class class1b:
1181             def __call__(self, target, source, env):
1182                 open(env['out'], 'w').write("class1b\n")
1183                 return 2
1184
1185         act = SCons.Action.FunctionAction(class1b())
1186         r = act([], [], Environment(out = outfile))
1187         assert r == 2
1188         c = test.read(outfile, 'r')
1189         assert c == "class1b\n", c
1190
1191         def build_it(target, source, env, self=self):
1192             self.build_it = 1
1193             return 0
1194         def string_it(target, source, env, self=self):
1195             self.string_it = 1
1196             return None
1197         act = SCons.Action.FunctionAction(build_it, strfunction=string_it)
1198         r = act([], [], Environment())
1199         assert r == 0, r
1200         assert self.build_it
1201         assert self.string_it
1202
1203     def test_get_contents(self):
1204         """Test fetching the contents of a function Action
1205         """
1206
1207         a = SCons.Action.FunctionAction(GlobalFunc)
1208
1209         matches = [
1210             "\177\036\000\177\037\000d\000\000S",
1211             "d\x00\x00S",
1212         ]
1213
1214         c = a.get_contents(target=[], source=[], env=Environment())
1215         assert c in matches, repr(c)
1216         c = a.get_contents(target=[], source=[], env=Environment(), dict={})
1217         assert c in matches, repr(c)
1218
1219         a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
1220
1221         matches_foo = map(lambda x: x + "foo", matches)
1222
1223         c = a.get_contents(target=[], source=[], env=Environment())
1224         assert c in matches, repr(c)
1225         c = a.get_contents(target=[], source=[], env=Environment(XYZ = 'foo'))
1226         assert c in matches_foo, repr(c)
1227
1228         class Foo:
1229             def get_contents(self, target, source, env, dict=None):
1230                 return 'xyzzy'
1231         a = SCons.Action.FunctionAction(Foo())
1232         c = a.get_contents(target=[], source=[], env=Environment())
1233         assert c == 'xyzzy', repr(c)
1234
1235 class ListActionTestCase(unittest.TestCase):
1236
1237     def test___init__(self):
1238         """Test creation of a list of subsidiary Actions
1239         """
1240         def func():
1241             pass
1242         a = SCons.Action.ListAction(["x", func, ["y", "z"]])
1243         assert isinstance(a.list[0], SCons.Action.CommandAction)
1244         assert isinstance(a.list[1], SCons.Action.FunctionAction)
1245         assert isinstance(a.list[2], SCons.Action.ListAction)
1246         assert a.list[2].list[0].cmd_list == 'y'
1247
1248     def test_get_actions(self):
1249         """Test the get_actions() method for ListActions
1250         """
1251         a = SCons.Action.ListAction(["x", "y"])
1252         l = a.get_actions()
1253         assert len(l) == 2, l
1254         assert isinstance(l[0], SCons.Action.CommandAction), l[0]
1255         g = l[0].get_actions()
1256         assert g == [l[0]], g
1257         assert isinstance(l[1], SCons.Action.CommandAction), l[1]
1258         g = l[1].get_actions()
1259         assert g == [l[1]], g
1260
1261     def test___str__(self):
1262         """Test the __str__() method for a list of subsidiary Actions
1263         """
1264         def f(target,source,env):
1265             pass
1266         def g(target,source,env):
1267             pass
1268         a = SCons.Action.ListAction([f, g, "XXX", f])
1269         s = str(a)
1270         assert s == "f(env, target, source)\ng(env, target, source)\nXXX\nf(env, target, source)", s
1271
1272     def test_genstring(self):
1273         """Test the genstring() method for a list of subsidiary Actions
1274         """
1275         def f(target,source,env):
1276             pass
1277         def g(target,source,env):
1278             pass
1279         a = SCons.Action.ListAction([f, g, "XXX", f])
1280         s = a.genstring([], [], Environment())
1281         assert s == "f(env, target, source)\ng(env, target, source)\nXXX\nf(env, target, source)", s
1282
1283     def test_strfunction(self):
1284         """Test the string function for a list of subsidiary Actions
1285         """
1286         def f(target,source,env):
1287             pass
1288         def g(target,source,env):
1289             pass
1290         a = SCons.Action.ListAction([f, g, "XXX", f])
1291         s = a.strfunction([], [], Environment())
1292         assert s == "f([], [])\ng([], [])\nXXX\nf([], [])", s
1293
1294         def sf(target, source, env):
1295             return "sf was called"
1296         act = SCons.Action.ListAction([f, g, "XXX", f], strfunction=sf)
1297         s = act.strfunction([], [], Environment())
1298         assert s == "sf was called", s
1299
1300     def test_execute(self):
1301         """Test executing a list of subsidiary Actions
1302         """
1303         self.inc = 0
1304         def f(target,source,env):
1305             s = env['s']
1306             s.inc = s.inc + 1
1307         a = SCons.Action.ListAction([f, f, f])
1308         a([], [], Environment(s = self))
1309         assert self.inc == 3, self.inc
1310
1311         cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
1312
1313         def function2(target, source, env):
1314             open(env['out'], 'a').write("function2\n")
1315             return 0
1316
1317         class class2a:
1318             def __call__(self, target, source, env):
1319                 open(env['out'], 'a').write("class2a\n")
1320                 return 0
1321
1322         class class2b:
1323             def __init__(self, target, source, env):
1324                 open(env['out'], 'a').write("class2b\n")
1325         act = SCons.Action.ListAction([cmd2, function2, class2a(), class2b])
1326         r = act([], [], Environment(out = outfile))
1327         assert r.__class__ == class2b
1328         c = test.read(outfile, 'r')
1329         assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
1330
1331     def test_get_contents(self):
1332         """Test fetching the contents of a list of subsidiary Actions
1333         """
1334         self.foo=0
1335         def gen(target, source, env, for_signature):
1336             s = env['s']
1337             s.foo=1
1338             return "y"
1339         a = SCons.Action.ListAction(["x",
1340                                      SCons.Action.CommandGenerator(gen),
1341                                      "z"])
1342         c = a.get_contents(target=[], source=[], env=Environment(s = self))
1343         assert self.foo==1, self.foo
1344         assert c == "xyz", c
1345         c = a.get_contents(target=[], source=[], env=Environment(s = self), dict={})
1346         assert self.foo==1, self.foo
1347         assert c == "xyz", c
1348
1349 class LazyActionTestCase(unittest.TestCase):
1350     def test___init__(self):
1351         """Test creation of a lazy-evaluation Action
1352         """
1353         # Environment variable references should create a special
1354         # type of CommandGeneratorAction that lazily evaluates the
1355         # variable.
1356         a9 = SCons.Action.Action('$FOO')
1357         assert isinstance(a9, SCons.Action.CommandGeneratorAction), a9
1358         assert a9.generator.var == 'FOO', a9.generator.var
1359
1360         a10 = SCons.Action.Action('${FOO}')
1361         assert isinstance(a9, SCons.Action.CommandGeneratorAction), a10
1362         assert a10.generator.var == 'FOO', a10.generator.var
1363
1364     def test_strfunction(self):
1365         """Test the lazy-evaluation Action string function
1366         """
1367         def f(target, source, env):
1368             pass
1369         a = SCons.Action.Action('$BAR')
1370         s = a.strfunction([], [], env=Environment(BAR=f, s=self))
1371         assert s == "f([], [])", s
1372
1373     def test_genstring(self):
1374         """Test the lazy-evaluation Action genstring() method
1375         """
1376         def f(target, source, env):
1377             pass
1378         a = SCons.Action.Action('$BAR')
1379         s = a.genstring([], [], env=Environment(BAR=f, s=self))
1380         assert s == "f(env, target, source)", s
1381
1382     def test_execute(self):
1383         """Test executing a lazy-evaluation Action
1384         """
1385         def f(target, source, env):
1386             s = env['s']
1387             s.test=1
1388             return 0
1389         a = SCons.Action.Action('$BAR')
1390         a([], [], env=Environment(BAR = f, s = self))
1391         assert self.test == 1, self.test
1392
1393     def test_get_contents(self):
1394         """Test fetching the contents of a lazy-evaluation Action
1395         """
1396         a = SCons.Action.Action("${FOO}")
1397         env = Environment(FOO = [["This", "is", "a", "test"]])
1398         c = a.get_contents(target=[], source=[], env=env)
1399         assert c == "This is a test", c
1400         c = a.get_contents(target=[], source=[], env=env, dict={})
1401         assert c == "This is a test", c
1402
1403 class ActionCallerTestCase(unittest.TestCase):
1404     def test___init__(self):
1405         """Test creation of an ActionCaller"""
1406         ac = SCons.Action.ActionCaller(1, [2, 3], {'FOO' : 4, 'BAR' : 5})
1407         assert ac.parent == 1, ac.parent
1408         assert ac.args == [2, 3], ac.args
1409         assert ac.kw == {'FOO' : 4, 'BAR' : 5}, ac.kw
1410
1411     def test_get_contents(self):
1412         """Test fetching the contents of an ActionCaller"""
1413         def strfunc():
1414             pass
1415
1416         matches = [
1417             "\177\036\000\177\037\000d\000\000S",
1418             "d\x00\x00S"
1419         ]
1420
1421         af = SCons.Action.ActionFactory(GlobalFunc, strfunc)
1422         ac = SCons.Action.ActionCaller(af, [], {})
1423         c = ac.get_contents([], [], Environment())
1424         assert c in matches, repr(c)
1425
1426         matches = [
1427             '\177"\000\177#\000d\000\000S',
1428             "d\x00\x00S"
1429         ]
1430
1431         af = SCons.Action.ActionFactory(GlobalActFunc(), strfunc)
1432         ac = SCons.Action.ActionCaller(af, [], {})
1433         c = ac.get_contents([], [], Environment())
1434         assert c in matches, repr(c)
1435
1436         matches = [
1437             "<built-in function str>",
1438             "<type 'str'>",
1439         ]
1440
1441         af = SCons.Action.ActionFactory(str, strfunc)
1442         ac = SCons.Action.ActionCaller(af, [], {})
1443         c = ac.get_contents([], [], Environment())
1444         assert c == "<built-in function str>" or \
1445                c == "<type 'str'>", repr(c)
1446
1447     def test___call__(self):
1448         """Test calling an ActionCaller"""
1449         actfunc_args = []
1450         def actfunc(a1, a2, a3, args=actfunc_args):
1451             args.extend([a1, a2, a3])
1452         def strfunc(a1, a2, a3):
1453             pass
1454
1455         af = SCons.Action.ActionFactory(actfunc, strfunc)
1456         ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3], {})
1457         ac([], [], Environment(FOO = 2))
1458         assert actfunc_args == [1, '2', 3], actfunc_args
1459
1460         del actfunc_args[:]
1461         ac = SCons.Action.ActionCaller(af, [], {'a3' : 6, 'a2' : '$BAR', 'a1' : 4})
1462         ac([], [], Environment(BAR = 5))
1463         assert actfunc_args == [4, '5', 6], actfunc_args
1464
1465     def test_strfunction(self):
1466         """Test calling the ActionCaller strfunction() method"""
1467         strfunc_args = []
1468         def actfunc(a1, a2, a3):
1469             pass
1470         def strfunc(a1, a2, a3, args=strfunc_args):
1471             args.extend([a1, a2, a3])
1472
1473         af = SCons.Action.ActionFactory(actfunc, strfunc)
1474         ac = SCons.Action.ActionCaller(af, [1, '$FOO', 3], {})
1475         ac.strfunction([], [], Environment(FOO = 2))
1476         assert strfunc_args == [1, '2', 3], strfunc_args
1477
1478         del strfunc_args[:]
1479         ac = SCons.Action.ActionCaller(af, [], {'a3' : 6, 'a2' : '$BAR', 'a1' : 4})
1480         ac.strfunction([], [], Environment(BAR = 5))
1481         assert strfunc_args == [4, '5', 6], strfunc_args
1482
1483 class ActionFactoryTestCase(unittest.TestCase):
1484     def test___init__(self):
1485         """Test creation of an ActionFactory"""
1486         def actfunc():
1487             pass
1488         def strfunc():
1489             pass
1490         ac = SCons.Action.ActionFactory(actfunc, strfunc)
1491         assert ac.actfunc is actfunc, ac.actfunc
1492         assert ac.strfunc is strfunc, ac.strfunc
1493
1494     def test___call__(self):
1495         """Test calling whatever's returned from an ActionFactory"""
1496         actfunc_args = []
1497         strfunc_args = []
1498         def actfunc(a1, a2, a3, args=actfunc_args):
1499             args.extend([a1, a2, a3])
1500         def strfunc(a1, a2, a3, args=strfunc_args):
1501             args.extend([a1, a2, a3])
1502         af = SCons.Action.ActionFactory(actfunc, strfunc)
1503         af(3, 6, 9)([], [], Environment())
1504         assert actfunc_args == [3, 6, 9], actfunc_args
1505         assert strfunc_args == [3, 6, 9], strfunc_args
1506
1507
1508 if __name__ == "__main__":
1509     suite = unittest.TestSuite()
1510     tclasses = [ ActionTestCase,
1511                  ActionBaseTestCase,
1512                  CommandActionTestCase,
1513                  CommandGeneratorActionTestCase,
1514                  FunctionActionTestCase,
1515                  ListActionTestCase,
1516                  LazyActionTestCase,
1517                  ActionCallerTestCase,
1518                  ActionFactoryTestCase ]
1519     for tclass in tclasses:
1520         names = unittest.getTestCaseNames(tclass, 'test_')
1521         suite.addTests(map(tclass, names))
1522     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1523         sys.exit(1)