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