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