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:
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
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.
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
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.
47 import SCons.Environment
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.
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 = '')
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")
66 f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
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()
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()
79 stderr_msg = "act.py: stderr: executed act.py %s\\n" % string.join(sys.argv[1:])
80 sys.stderr.write( stderr_msg )
84 act_py = test.workpath('act.py')
86 outfile = test.workpath('outfile')
87 outfile2 = test.workpath('outfile2')
88 pipe_file = test.workpath('pipe.out')
90 scons_env = SCons.Environment.Environment()
92 # Capture all the stuff the Actions will print,
93 # so it doesn't clutter the output.
94 sys.stdout = StringIO.StringIO()
96 class CmdStringHolder:
97 def __init__(self, cmd, literal=None):
99 self.literal = literal
101 def is_literal(self):
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.
110 After calling this function, the next call to str() will
111 return the escaped string.
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
122 def __init__(self, **kw):
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():
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):
138 def __setitem__(self, 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)
145 return self.d.items()
146 def Dictionary(self):
148 def Copy(self, **kw):
150 res.d = SCons.Environment.our_deepcopy(self.d)
151 for k, v in kw.items():
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]
164 def __init__(self, name):
170 def get_subst_proxy(self):
173 if os.name == 'java':
174 python = os.path.join(sys.prefix, 'jython')
176 python = sys.executable
178 class ActionTestCase(unittest.TestCase):
180 def test_factory(self):
181 """Test the Action factory
187 a1 = SCons.Action.Action(foo)
188 assert isinstance(a1, SCons.Action.FunctionAction), a1
189 assert a1.execfunction == foo, a1.execfunction
191 a2 = SCons.Action.Action("string")
192 assert isinstance(a2, SCons.Action.CommandAction), a2
193 assert a2.cmd_list == "string", a2.cmd_list
195 if hasattr(types, 'UnicodeType'):
196 exec "a3 = SCons.Action.Action(u'string')"
197 exec "assert isinstance(a3, SCons.Action.CommandAction), a3"
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
210 a5 = SCons.Action.Action(1)
211 assert a5 is None, a5
213 a6 = SCons.Action.Action(a1)
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
220 a8 = SCons.Action.Action(["a8"])
221 assert isinstance(a8, SCons.Action.CommandAction), a8
222 assert a8.cmd_list == "a8", a8.cmd_list
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
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
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
247 class ActionBaseTestCase(unittest.TestCase):
249 def test___cmp__(self):
250 """Test Action comparison
252 a1 = SCons.Action.Action("x")
253 a2 = SCons.Action.Action("x")
255 a3 = SCons.Action.Action("y")
260 """Test the show() method
262 save_stdout = sys.stdout
264 save_print_actions = SCons.Action.print_actions
265 SCons.Action.print_actions = 0
268 a = SCons.Action.Action("x")
270 sio = StringIO.StringIO()
276 SCons.Action.print_actions = 1
278 sio = StringIO.StringIO()
282 assert s == "foobar\n", s
285 SCons.Action.print_actions = save_print_actions
286 sys.stdout = save_stdout
288 def test_presub(self):
289 """Test the presub() method
291 save_stdout = sys.stdout
293 save_print_actions_presub = SCons.Action.print_actions_presub
294 SCons.Action.print_actions_presub = 0
297 a = SCons.Action.Action("x")
300 sio = StringIO.StringIO()
302 a.presub("xyzzy", env)
306 SCons.Action.print_actions_presub = 1
308 sio = StringIO.StringIO()
310 a.presub("foobar", env)
312 assert s == "Building foobar with action(s):\n x\n", s
314 a = SCons.Action.Action(["y", "z"])
316 sio = StringIO.StringIO()
318 a.presub("foobar", env)
320 assert s == "Building foobar with action(s):\n y\n z\n", s
324 a = SCons.Action.Action(func)
326 sio = StringIO.StringIO()
328 a.presub("foobar", env)
330 assert s == "Building foobar with action(s):\n func(env, target, source)\n", s
332 def gen(target, source, env, for_signature):
333 return 'generat' + env.get('GEN', 'or')
334 a = SCons.Action.Action(SCons.Action.CommandGenerator(gen))
336 sio = StringIO.StringIO()
338 a.presub("foobar", env)
340 assert s == "Building foobar with action(s):\n generator\n", s
342 sio = StringIO.StringIO()
344 a.presub("foobar", Environment(GEN = 'ed'))
346 assert s == "Building foobar with action(s):\n generated\n", s
348 a = SCons.Action.Action("$ACT")
350 sio = StringIO.StringIO()
352 a.presub("foobar", env)
354 assert s == "Building foobar with action(s):\n \n", s
356 sio = StringIO.StringIO()
358 a.presub("foobar", Environment(ACT = 'expanded action'))
360 assert s == "Building foobar with action(s):\n expanded action\n", s
363 SCons.Action.print_actions_presub = save_print_actions_presub
364 sys.stdout = save_stdout
366 def test_get_actions(self):
367 """Test the get_actions() method
369 a = SCons.Action.Action("x")
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.
380 baz = SCons.Action.CommandGenerator(bar)
381 act1 = SCons.Action.Action('foo bar')
382 act2 = SCons.Action.Action([ 'foo', bar ])
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 ]
391 assert isinstance(sum, SCons.Action.ListAction), str(sum)
392 assert len(sum.list) == 2, len(sum.list)
395 assert isinstance(sum, SCons.Action.ListAction), str(sum)
396 assert len(sum.list) == 4, len(sum.list)
398 # Should also be able to add command generators to each other
401 assert isinstance(sum, SCons.Action.ListAction), str(sum)
402 assert len(sum.list) == 2, len(sum.list)
405 assert isinstance(sum, SCons.Action.ListAction), str(sum)
406 assert len(sum.list) == 2, len(sum.list)
409 assert isinstance(sum, SCons.Action.ListAction), str(sum)
410 assert len(sum.list) == 3, len(sum.list)
412 # Also should be able to add Actions to anything that can
413 # be converted into an action.
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)
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)
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)
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)
441 assert 0, "Should have thrown a TypeError adding to an int."
448 assert 0, "Should have thrown a TypeError adding to an int."
450 class CommandActionTestCase(unittest.TestCase):
452 def test___init__(self):
453 """Test creation of a command Action
455 a = SCons.Action.CommandAction(["xyzzy"])
456 assert a.cmd_list == [ "xyzzy" ], a.cmd_list
458 def test___str__(self):
459 """Test fetching the pre-substitution string for command Actions
462 act = SCons.Action.CommandAction('xyzzy $TARGET $SOURCE')
464 assert s == 'xyzzy $TARGET $SOURCE', s
466 act = SCons.Action.CommandAction(['xyzzy',
467 '$TARGET', '$SOURCE',
468 '$TARGETS', '$SOURCES'])
470 assert s == "['xyzzy', '$TARGET', '$SOURCE', '$TARGETS', '$SOURCES']", s
472 def test_genstring(self):
473 """Test the genstring() method for command Actions
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
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
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
510 def test_strfunction(self):
511 """Test fetching the string representation of command Actions
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
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
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
545 def test_execute(self):
546 """Test execution of command Actions
551 except AttributeError:
554 cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
556 act = SCons.Action.CommandAction(cmd1)
557 r = act([], [], env.Copy())
559 c = test.read(outfile, 'r')
560 assert c == "act.py: 'xyzzy'\n", c
562 cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
564 act = SCons.Action.CommandAction(cmd2)
565 r = act(DummyNode('foo'), [], env.Copy())
567 c = test.read(outfile, 'r')
568 assert c == "act.py: 'foo'\n", c
570 cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
572 act = SCons.Action.CommandAction(cmd3)
573 r = act(map(DummyNode, ['aaa', 'bbb']), [], env.Copy())
575 c = test.read(outfile, 'r')
576 assert c == "act.py: 'aaa' 'bbb'\n", c
578 cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
580 act = SCons.Action.CommandAction(cmd4)
581 r = act([], [DummyNode('one'), DummyNode('two')], env.Copy())
583 c = test.read(outfile, 'r')
584 assert c == "act.py: 'one' 'two'\n", c
586 cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
588 act = SCons.Action.CommandAction(cmd4)
589 sources = [DummyNode('three'), DummyNode('four'), DummyNode('five')]
591 r = act([], source = sources, env = env2)
593 c = test.read(outfile, 'r')
594 assert c == "act.py: 'three' 'four'\n", c
596 cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
598 act = SCons.Action.CommandAction(cmd5)
600 if scons_env.has_key('ENV'):
601 env5['ENV'] = scons_env['ENV']
602 PATH = scons_env['ENV'].get('PATH', '')
607 env5['ENV']['XYZZY'] = 'xyzzy'
608 r = act(target = DummyNode('out5'), source = [], env = env5)
610 act = SCons.Action.CommandAction(cmd5)
611 r = act(target = DummyNode('out5'),
613 env = env.Copy(ENV = {'XYZZY' : 'xyzzy5',
616 c = test.read(outfile, 'r')
617 assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy5'\n", c
620 def __init__(self, str):
626 def get_subst_proxy(self):
629 cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (python, act_py, outfile)
631 act = SCons.Action.CommandAction(cmd6)
632 r = act(target = [Obj('111'), Obj('222')],
633 source = [Obj('333'), Obj('444'), Obj('555')],
636 c = test.read(outfile, 'r')
637 assert c == "act.py: '222' '111' '333' '444'\n", c
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
648 expect_nonexistent = 127
649 expect_nonexecutable = 126
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
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
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
667 def test_pipe_execute(self):
668 """Test capturing piped output from an action
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
675 self.env['PSTDOUT'].close()
676 pipe_out = test.read( pipe_file )
678 act_out = "act.py: stdout: executed act.py"
679 act_err = "act.py: stderr: executed act.py"
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)
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
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},
705 PSTDOUT = pipe, PSTDERR = pipe )
709 return (test.read(outfile2, 'r'), test.read(pipe_file, 'r'))
711 (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
713 assert redirected == act_out
714 assert pipe_out == act_err
716 (redirected, pipe_out) = test_redirect(self,'2> %s' % outfile2,
718 assert redirected == act_err
719 assert pipe_out == act_out
721 (redirected, pipe_out) = test_redirect(self,'> %s 2>&1' % outfile2,
723 assert (redirected == act_out + act_err or
724 redirected == act_err + act_out)
725 assert pipe_out == ""
727 act_err = "Long Command Output\n"*3000
728 # the size of the string should exceed the system's default block size
730 (redirected, pipe_out) = test_redirect(self,'> %s' % outfile2,
732 assert (redirected == act_out)
733 assert (pipe_out == act_err)
735 def test_set_handler(self):
736 """Test setting the command handler...
742 def func(sh, escape, cmd, args, env, test=t):
746 def escape_func(cmd):
747 return '**' + cmd + '**'
750 def __init__(self, x):
754 def escape(self, escape_func):
755 return escape_func(self.data)
756 def is_literal(self):
759 a = SCons.Action.CommandAction(["xyzzy"])
760 e = Environment(SPAWN = func)
762 assert t.executed == [ 'xyzzy' ], t.executed
764 a = SCons.Action.CommandAction(["xyzzy"])
765 e = Environment(SPAWN = func, SHELL = 'fake shell')
767 assert t.executed == [ 'xyzzy' ], t.executed
768 assert t.shell == 'fake shell', t.shell
770 a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ])
771 e = Environment(SPAWN = func, ESCAPE = escape_func)
773 assert t.executed == [ '**xyzzy**' ], t.executed
775 def test_get_contents(self):
776 """Test fetching the contents of a command Action
778 def CmdGen(target, source, env, for_signature):
781 (env["foo"], env["bar"])
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',
789 assert c == "| | FFF BBB 1", c
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
797 c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'),
798 env=SpecialEnvironment(foo = 'GGG', bar = 'CCC',
800 assert c == 'subst_target_source: | $( $foo | $bar $) | $baz 1', c
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
811 t = map(DummyNode, ['t1', 't2', 't3', 't4', 't5', 't6'])
812 s = map(DummyNode, ['s1', 's2', 's3', 's4', 's5', 's6'])
815 a = SCons.Action.CommandAction(["$TARGET"])
816 c = a.get_contents(target=t, source=s, env=env)
818 c = a.get_contents(target=t, source=s, env=env, dict={})
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={})
827 a = SCons.Action.CommandAction(["${TARGETS[2]}"])
828 c = a.get_contents(target=t, source=s, env=env)
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
835 a = SCons.Action.CommandAction(["$SOURCE"])
836 c = a.get_contents(target=t, source=s, env=env)
838 c = a.get_contents(target=t, source=s, env=env, dict={})
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={})
847 a = SCons.Action.CommandAction(["${SOURCES[2]}"])
848 c = a.get_contents(target=t, source=s, env=env)
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
855 class CommandGeneratorActionTestCase(unittest.TestCase):
857 def test___init__(self):
858 """Test creation of a command generator Action
860 def f(target, source, env):
862 a = SCons.Action.CommandGeneratorAction(f)
863 assert a.generator == f
865 def test___str__(self):
866 """Test the pre-substitution strings for command generator Actions
868 def f(target, source, env, for_signature, self=self):
870 a = SCons.Action.CommandGeneratorAction(f)
874 def test_genstring(self):
875 """Test the command generator Action genstring() method
877 def f(target, source, env, for_signature, self=self):
880 return "$FOO $TARGET $SOURCE $TARGETS $SOURCES"
881 a = SCons.Action.CommandGeneratorAction(f)
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
887 def test_strfunction(self):
888 """Test the command generator Action string function
890 def f(target, source, env, for_signature, self=self):
894 a = SCons.Action.CommandGeneratorAction(f)
896 s = a.strfunction([], [], env=Environment(FOO='xyzzy', dummy=1))
897 assert self.dummy == 1, self.dummy
898 assert s == ['xyzzy'], s
900 def test_execute(self):
901 """Test executing a command generator Action
904 def f(target, source, env, for_signature, self=self):
907 s = env.subst("$FOO")
908 assert s == 'foo baz\nbar ack', s
910 def func_action(target, source, env, self=self):
912 s = env.subst('$foo')
915 def f2(target, source, env, for_signature, f=func_action):
917 def ch(sh, escape, cmd, args, env, self=self):
919 self.args.append(args)
921 a = SCons.Action.CommandGeneratorAction(f)
925 a([], [], env=Environment(FOO = 'foo baz\nbar ack',
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
932 b = SCons.Action.CommandGeneratorAction(f2)
934 b(target=[], source=[], env=Environment(foo = 'bar',
936 assert self.dummy==2, self.dummy
940 def __init__(self, t):
943 self.t.rfile_called = 1
945 def get_subst_proxy(self):
947 def f3(target, source, env, for_signature):
949 c = SCons.Action.CommandGeneratorAction(f3)
950 c(target=[], source=DummyFile(self), env=Environment())
951 assert self.rfile_called
953 def test_get_contents(self):
954 """Test fetching the contents of a command generator Action
956 def f(target, source, env, for_signature):
959 assert for_signature, for_signature
960 return [["guux", foo, "$(", "$ignore", "$)", bar,
961 '${test("$( foo $bar $)")}' ]]
964 assert mystr == "$( foo $bar $)", mystr
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
976 class FunctionActionTestCase(unittest.TestCase):
978 def test___init__(self):
979 """Test creation of a function Action
990 a = SCons.Action.FunctionAction(func1)
991 assert a.execfunction == func1, a.execfunction
992 assert isinstance(a.strfunction, types.FunctionType)
994 a = SCons.Action.FunctionAction(func2, strfunction=func3)
995 assert a.execfunction == func2, a.execfunction
996 assert a.strfunction == func3, a.strfunction
998 a = SCons.Action.FunctionAction(func3, func4)
999 assert a.execfunction == func3, a.execfunction
1000 assert a.strfunction == func4, a.strfunction
1002 a = SCons.Action.FunctionAction(func4, None)
1003 assert a.execfunction == func4, a.execfunction
1004 assert a.strfunction is None, a.strfunction
1006 def test___str__(self):
1007 """Test the __str__() method for function Actions
1011 a = SCons.Action.FunctionAction(func1)
1013 assert s == "func1(env, target, source)", s
1018 a = SCons.Action.FunctionAction(class1())
1020 assert s == "class1(env, target, source)", s
1022 def test_execute(self):
1023 """Test executing a function Action
1026 def f(target, source, env):
1031 assert env.subst("$BAR") == 'foo bar', env.subst("$BAR")
1033 a = SCons.Action.FunctionAction(f)
1034 a(target=1, source=2, env=Environment(BAR = 'foo bar',
1036 assert self.inc == 1, self.inc
1037 assert self.source == [2], self.source
1038 assert self.target == [1], self.target
1042 def function1(target, source, env):
1046 open(t, 'w').write("function1\n")
1049 act = SCons.Action.FunctionAction(function1)
1052 r = act(target = [outfile, outfile2], source=[], env=Environment())
1053 except SCons.Errors.BuildError:
1057 c = test.read(outfile, 'r')
1058 assert c == "function1\n", c
1059 c = test.read(outfile2, 'r')
1060 assert c == "function1\n", c
1063 def __init__(self, target, source, env):
1064 open(env['out'], 'w').write("class1a\n")
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
1073 def __call__(self, target, source, env):
1074 open(env['out'], 'w').write("class1b\n")
1077 act = SCons.Action.FunctionAction(class1b())
1078 r = act([], [], Environment(out = outfile))
1080 c = test.read(outfile, 'r')
1081 assert c == "class1b\n", c
1083 def build_it(target, source, env, self=self):
1086 def string_it(target, source, env, self=self):
1089 act = SCons.Action.FunctionAction(build_it, string_it)
1090 r = act([], [], Environment())
1092 assert self.build_it
1093 assert self.string_it
1095 def test_get_contents(self):
1096 """Test fetching the contents of a function Action
1099 a = SCons.Action.FunctionAction(GlobalFunc)
1102 "\177\036\000\177\037\000d\000\000S",
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)
1111 a = SCons.Action.FunctionAction(GlobalFunc, varlist=['XYZ'])
1113 matches_foo = map(lambda x: x + "foo", matches)
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)
1121 def get_contents(self, target, source, env, dict=None):
1123 a = SCons.Action.FunctionAction(Foo())
1124 c = a.get_contents(target=[], source=[], env=Environment())
1125 assert c == 'xyzzy', repr(c)
1127 class ListActionTestCase(unittest.TestCase):
1129 def test___init__(self):
1130 """Test creation of a list of subsidiary Actions
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'
1140 def test_get_actions(self):
1141 """Test the get_actions() method for ListActions
1143 a = SCons.Action.ListAction(["x", "y"])
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
1153 def test___str__(self):
1154 """Test the __str__() method for a list of subsidiary Actions
1156 def f(target,source,env):
1158 def g(target,source,env):
1160 a = SCons.Action.ListAction([f, g, "XXX", f])
1162 assert s == "f(env, target, source)\ng(env, target, source)\nXXX\nf(env, target, source)", s
1164 def test_genstring(self):
1165 """Test the genstring() method for a list of subsidiary Actions
1167 def f(target,source,env):
1169 def g(target,source,env):
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
1175 def test_strfunction(self):
1176 """Test the string function for a list of subsidiary Actions
1178 def f(target,source,env):
1180 def g(target,source,env):
1182 a = SCons.Action.ListAction([f, g, "XXX", f])
1183 s = a.strfunction([], [], Environment())
1184 assert s == "f([], [])\ng([], [])\nXXX\nf([], [])", s
1186 def test_execute(self):
1187 """Test executing a list of subsidiary Actions
1190 def f(target,source,env):
1193 a = SCons.Action.ListAction([f, f, f])
1194 a([], [], Environment(s = self))
1195 assert self.inc == 3, self.inc
1197 cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
1199 def function2(target, source, env):
1200 open(env['out'], 'a').write("function2\n")
1204 def __call__(self, target, source, env):
1205 open(env['out'], 'a').write("class2a\n")
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
1217 def test_get_contents(self):
1218 """Test fetching the contents of a list of subsidiary Actions
1221 def gen(target, source, env, for_signature):
1225 a = SCons.Action.ListAction(["x",
1226 SCons.Action.CommandGenerator(gen),
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
1235 class LazyActionTestCase(unittest.TestCase):
1236 def test___init__(self):
1237 """Test creation of a lazy-evaluation Action
1239 # Environment variable references should create a special
1240 # type of CommandGeneratorAction that lazily evaluates the
1242 a9 = SCons.Action.Action('$FOO')
1243 assert isinstance(a9, SCons.Action.CommandGeneratorAction), a9
1244 assert a9.generator.var == 'FOO', a9.generator.var
1246 a10 = SCons.Action.Action('${FOO}')
1247 assert isinstance(a9, SCons.Action.CommandGeneratorAction), a10
1248 assert a10.generator.var == 'FOO', a10.generator.var
1250 def test_strfunction(self):
1251 """Test the lazy-evaluation Action string function
1253 def f(target, source, env):
1255 a = SCons.Action.Action('$BAR')
1256 s = a.strfunction([], [], env=Environment(BAR=f, s=self))
1257 assert s == "f([], [])", s
1259 def test_genstring(self):
1260 """Test the lazy-evaluation Action genstring() method
1262 def f(target, source, env):
1264 a = SCons.Action.Action('$BAR')
1265 s = a.genstring([], [], env=Environment(BAR=f, s=self))
1266 assert s == "f(env, target, source)", s
1268 def test_execute(self):
1269 """Test executing a lazy-evaluation Action
1271 def f(target, source, env):
1275 a = SCons.Action.Action('$BAR')
1276 a([], [], env=Environment(BAR = f, s = self))
1277 assert self.test == 1, self.test
1279 def test_get_contents(self):
1280 """Test fetching the contents of a lazy-evaluation Action
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
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
1297 def test_get_contents(self):
1298 """Test fetching the contents of an ActionCaller"""
1303 "\177\036\000\177\037\000d\000\000S",
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)
1313 '\177"\000\177#\000d\000\000S',
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)
1323 "<built-in function str>",
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)
1333 def test___call__(self):
1334 """Test calling an ActionCaller"""
1336 def actfunc(a1, a2, a3, args=actfunc_args):
1337 args.extend([a1, a2, a3])
1338 def strfunc(a1, a2, a3):
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
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
1351 def test_strfunction(self):
1352 """Test calling the ActionCaller strfunction() method"""
1354 def actfunc(a1, a2, a3):
1356 def strfunc(a1, a2, a3, args=strfunc_args):
1357 args.extend([a1, a2, a3])
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
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
1369 class ActionFactoryTestCase(unittest.TestCase):
1370 def test___init__(self):
1371 """Test creation of an ActionFactory"""
1376 ac = SCons.Action.ActionFactory(actfunc, strfunc)
1377 assert ac.actfunc is actfunc, ac.actfunc
1378 assert ac.strfunc is strfunc, ac.strfunc
1380 def test___call__(self):
1381 """Test calling whatever's returned from an ActionFactory"""
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
1394 if __name__ == "__main__":
1395 suite = unittest.TestSuite()
1396 tclasses = [ ActionTestCase,
1398 CommandActionTestCase,
1399 CommandGeneratorActionTestCase,
1400 FunctionActionTestCase,
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():