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