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