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