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__"
35 from UserDict import UserDict
39 from SCons.Subst import *
42 """Simple node work-alike."""
43 def __init__(self, name):
44 self.name = os.path.normpath(name)
51 def get_subst_proxy(self):
55 def __init__(self, dict={}):
58 def Dictionary(self, key = None):
63 def __getitem__(self, key):
66 def get(self, key, default):
67 return self.dict.get(key, default)
70 dict = self.dict.copy()
71 dict["TARGETS"] = 'tsig'
72 dict["SOURCES"] = 'ssig'
75 def cs(target=None, source=None, env=None, for_signature=None):
78 def cl(target=None, source=None, env=None, for_signature=None):
81 def CmdGen1(target, source, env, for_signature):
82 # Nifty trick...since Environment references are interpolated,
83 # instantiate an instance of a callable class with this one,
84 # which will then get evaluated.
85 assert str(target) == 't', target
86 assert str(source) == 's', source
87 return "${CMDGEN2('foo', %d)}" % for_signature
90 def __init__(self, mystr, forsig):
92 self.expect_for_signature = forsig
94 def __call__(self, target, source, env, for_signature):
95 assert str(target) == 't', target
96 assert str(source) == 's', source
97 assert for_signature == self.expect_for_signature, for_signature
98 return [ self.mystr, env.Dictionary('BAR') ]
105 return str.replace('/', os.sep)
107 class SubstTestCase(unittest.TestCase):
108 class MyNode(DummyNode):
109 """Simple node work-alike with some extra stuff for testing."""
110 def __init__(self, name):
111 DummyNode.__init__(self, name)
114 self.attribute = Attribute()
115 self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
116 self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
117 def get_stuff(self, extra):
118 return self.name + extra
122 def __init__(self, literal):
123 self.literal = literal
126 def is_literal(self):
130 def __init__(self, value):
137 def function_foo(arg):
140 target = [ MyNode("./foo/bar.exe"),
141 MyNode("/bar/baz with spaces.obj"),
142 MyNode("../foo/baz.obj") ]
143 source = [ MyNode("./foo/blah with spaces.cpp"),
144 MyNode("/bar/ack.cpp"),
145 MyNode("../foo/ack.c") ]
147 callable_object_1 = TestCallable('callable-1')
148 callable_object_2 = TestCallable('callable-2')
153 if SCons.Util.is_List(d) or type(d) is types.TupleType:
154 l.append(str(d[0]) + '=' + str(d[1]))
161 'NEWLINE' : 'before\nafter',
175 'DO' : DummyNode('do something'),
176 'FOO' : DummyNode('foo.in'),
177 'BAR' : DummyNode('bar with spaces.out'),
178 'CRAZY' : DummyNode('crazy\nfile.in'),
180 # $XXX$HHH should expand to GGGIII, not BADNEWS.
184 'FFFIII' : 'BADNEWS',
186 'LITERAL' : TestLiteral("$XXX"),
188 # Test that we can expand to and return a function.
189 #'FUNCTION' : function_foo,
194 'LITERALS' : [ Literal('foo\nwith\nnewlines'),
195 Literal('bar\nwith\nnewlines') ],
200 # Test various combinations of strings, lists and functions.
212 'US' : UserString.UserString('us'),
214 # Test function calls within ${}.
215 'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}',
216 'FUNC1' : lambda x: x,
217 'FUNC2' : lambda target, source, env, for_signature: ['x$CCC'],
219 # Various tests refactored from ActionTests.py.
220 'LIST' : [["This", "is", "$(", "$a", "$)", "test"]],
223 'RECURSE' : 'foo $RECURSE bar',
224 'RRR' : 'foo $SSS bar',
227 # Test callables that don't match the calling arguments.
228 'CALLABLE1' : callable_object_1,
229 'CALLABLE2' : callable_object_2,
231 '_defines' : _defines,
232 'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ],
235 def basic_comparisons(self, function, convert):
236 env = DummyEnv(self.loc)
237 cases = self.basic_cases[:]
238 kwargs = {'target' : self.target, 'source' : self.source,
239 'gvars' : env.Dictionary()}
243 input, expect = cases[:2]
244 expect = convert(expect)
246 result = function(input, env, **kwargs)
248 fmt = " input %s generated %s (%s)"
249 print fmt % (repr(input), e.__class__.__name__, repr(e))
253 if failed == 0: print
254 print " input %s => %s did not match %s" % (repr(input), repr(result), repr(expect))
257 fmt = "%d %s() cases failed"
258 assert failed == 0, fmt % (failed, function.__name__)
260 class scons_subst_TestCase(SubstTestCase):
262 # Basic tests of substitution functionality.
264 # Basics: strings without expansions are left alone, and
265 # the simplest possible expansion to a null-string value.
269 # Test expansion of integer values.
270 "test $zero", "test 0",
271 "test $one", "test 1",
273 # Test multiple re-expansion of values.
274 "test $ONE", "test four",
276 # Test a whole bunch of $TARGET[S] and $SOURCE[S] expansions.
277 "test $TARGETS $SOURCES",
278 "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp /bar/ack.cpp ../foo/ack.c",
280 "test ${TARGETS[:]} ${SOURCES[0]}",
281 "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp",
283 "test ${TARGETS[1:]}v",
284 "test /bar/baz with spaces.obj ../foo/baz.objv",
289 "test $TARGET$NO_SUCH_VAR[0]",
290 "test foo/bar.exe[0]",
295 "test ${SOURCES[0:2].foo}",
301 "test ${TARGET.get_stuff('blah')}",
302 "test foo/bar.exeblah",
304 "test ${SOURCES.get_stuff('blah')}",
305 "test foo/blah with spaces.cppblah /bar/ack.cppblah ../foo/ack.cblah",
307 "test ${SOURCES[0:2].get_stuff('blah')}",
308 "test foo/blah with spaces.cppblah /bar/ack.cppblah",
310 "test ${SOURCES[0:2].get_stuff('blah')}",
311 "test foo/blah with spaces.cppblah /bar/ack.cppblah",
313 "test ${SOURCES.attribute.attr1}",
314 "test attr$1-blah with spaces.cpp attr$1-ack.cpp attr$1-ack.c",
316 "test ${SOURCES.attribute.attr2}",
317 "test attr$2-blah with spaces.cpp attr$2-ack.cpp attr$2-ack.c",
319 # Test adjacent expansions.
326 # Test that adjacent expansions don't get re-interpreted
327 # together. The correct disambiguated expansion should be:
328 # $XXX$HHH => ${FFF}III => GGGIII
330 # $XXX$HHH => ${FFFIII} => BADNEWS
331 "$XXX$HHH", "GGGIII",
333 # Test double-dollar-sign behavior.
334 "$$FFF$HHH", "$FFFIII",
336 # Test that a Literal will stop dollar-sign substitution.
337 "$XXX $LITERAL $FFF", "GGG $XXX GGG",
339 # Test that we don't blow up even if they subscript
340 # something in ways they "can't."
345 # Test various combinations of strings and lists.
369 # Various uses of UserString.
370 UserString.UserString('x'), 'x',
371 UserString.UserString('$X'), 'x',
372 UserString.UserString('$US'), 'us',
375 # Test function calls within ${}.
376 '$FUNCCALL', 'a xc b',
378 # Bug reported by Christoph Wiedemann.
379 cvt('$xxx/bin'), '/bin',
381 # Tests callables that don't match our calling arguments.
382 '$CALLABLE1', 'callable-1',
384 # Test handling of quotes.
385 'aaa "bbb ccc" ddd', 'aaa "bbb ccc" ddd',
388 def test_scons_subst(self):
389 """Test scons_subst(): basic substitution"""
390 return self.basic_comparisons(scons_subst, cvt)
408 "$AAA ${AAA}A $BBBB $BBB",
423 # Verify what happens with no target or source nodes.
434 # Various tests refactored from ActionTests.py.
436 "This is $( $) test",
440 ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
441 ["|", "$(", "a", "|", "b", "$)", "|", "c", "1"],
442 ["|", "a", "|", "b", "|", "c", "1"],
443 ["|", "|", "c", "1"],
446 def test_subst_env(self):
447 """Test scons_subst(): expansion dictionary"""
448 # The expansion dictionary no longer comes from the construction
449 # environment automatically.
450 env = DummyEnv(self.loc)
451 s = scons_subst('$AAA', env)
454 def test_subst_SUBST_modes(self):
455 """Test scons_subst(): SUBST_* modes"""
456 env = DummyEnv(self.loc)
457 subst_cases = self.subst_cases[:]
459 gvars = env.Dictionary()
463 input, eraw, ecmd, esig = subst_cases[:4]
464 result = scons_subst(input, env, mode=SUBST_RAW, gvars=gvars)
466 if failed == 0: print
467 print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
469 result = scons_subst(input, env, mode=SUBST_CMD, gvars=gvars)
471 if failed == 0: print
472 print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
474 result = scons_subst(input, env, mode=SUBST_SIG, gvars=gvars)
476 if failed == 0: print
477 print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
480 assert failed == 0, "%d subst() mode cases failed" % failed
482 def test_subst_target_source(self):
483 """Test scons_subst(): target= and source= arguments"""
484 env = DummyEnv(self.loc)
485 t1 = self.MyNode('t1')
486 t2 = self.MyNode('t2')
487 s1 = self.MyNode('s1')
488 s2 = self.MyNode('s2')
489 result = scons_subst("$TARGET $SOURCES", env,
492 assert result == "t1 s1 s2", result
493 result = scons_subst("$TARGET $SOURCES", env,
497 assert result == "t1 s1 s2", result
499 result = scons_subst("$TARGET $SOURCES", env, target=[], source=[])
500 assert result == " ", result
501 result = scons_subst("$TARGETS $SOURCE", env, target=[], source=[])
502 assert result == " ", result
504 def test_subst_callable_expansion(self):
505 """Test scons_subst(): expanding a callable"""
506 env = DummyEnv(self.loc)
507 gvars = env.Dictionary()
508 newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS", env,
509 target=self.MyNode('t'), source=self.MyNode('s'),
511 assert newcom == "test foo bar with spaces.out s t", newcom
513 def test_subst_attribute_errors(self):
514 """Test scons_subst(): handling attribute errors"""
515 env = DummyEnv(self.loc)
519 scons_subst('${foo.bar}', env, gvars={'foo':Foo()})
520 except SCons.Errors.UserError, e:
522 "AttributeError `bar' trying to evaluate `${foo.bar}'",
523 "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
524 "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
526 assert str(e) in expect, e
528 raise AssertionError, "did not catch expected UserError"
530 def test_subst_syntax_errors(self):
531 """Test scons_subst(): handling syntax errors"""
532 env = DummyEnv(self.loc)
534 scons_subst('$foo.bar.3.0', env)
535 except SCons.Errors.UserError, e:
538 "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
539 # Python 2.2, 2.3, 2.4
540 "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
542 "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
544 assert str(e) in expect, e
546 raise AssertionError, "did not catch expected UserError"
548 def test_subst_type_errors(self):
549 """Test scons_subst(): handling type errors"""
550 env = DummyEnv(self.loc)
552 scons_subst("${NONE[2]}", env, gvars={'NONE':None})
553 except SCons.Errors.UserError, e:
555 # Python 1.5, 2.2, 2.3, 2.4
556 "TypeError `unsubscriptable object' trying to evaluate `${NONE[2]}'",
557 # Python 2.5 and later
558 "TypeError `'NoneType' object is unsubscriptable' trying to evaluate `${NONE[2]}'",
560 assert str(e) in expect, e
562 raise AssertionError, "did not catch expected UserError"
567 scons_subst("${func(1)}", env, gvars={'func':func})
568 except SCons.Errors.UserError, e:
571 "TypeError `not enough arguments; expected 3, got 1' trying to evaluate `${func(1)}'",
572 # Python 2.2, 2.3, 2.4, 2.5
573 "TypeError `func() takes exactly 3 arguments (1 given)' trying to evaluate `${func(1)}'"
575 assert str(e) in expect, repr(str(e))
577 raise AssertionError, "did not catch expected UserError"
579 def test_subst_raw_function(self):
580 """Test scons_subst(): fetch function with SUBST_RAW plus conv"""
581 # Test that the combination of SUBST_RAW plus a pass-through
582 # conversion routine allows us to fetch a function through the
583 # dictionary. CommandAction uses this to allow delayed evaluation
584 # of $SPAWN variables.
585 env = DummyEnv(self.loc)
586 gvars = env.Dictionary()
588 r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, conv=x, gvars=gvars)
589 assert r is self.callable_object_1, repr(r)
590 r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, gvars=gvars)
591 assert r == 'callable-1', repr(r)
593 # Test how we handle overriding the internal conversion routines.
597 n1 = self.MyNode('n1')
598 env = DummyEnv({'NODE' : n1})
599 gvars = env.Dictionary()
600 node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
601 assert node is n1, node
602 node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
603 assert node is n1, node
604 node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
605 assert node is n1, node
607 #def test_subst_function_return(self):
608 # """Test scons_subst(): returning a function"""
609 # env = DummyEnv({'FUNCTION' : foo})
610 # gvars = env.Dictionary()
611 # func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None, gvars=gvars)
612 # assert func is function_foo, func
613 # func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None, gvars=gvars)
614 # assert func is function_foo, func
615 # func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None, gvars=gvars)
616 # assert func is function_foo, func
618 def test_subst_overriding_gvars(self):
619 """Test scons_subst(): supplying an overriding gvars dictionary"""
620 env = DummyEnv({'XXX' : 'xxx'})
621 result = scons_subst('$XXX', env, gvars=env.Dictionary())
622 assert result == 'xxx', result
623 result = scons_subst('$XXX', env, gvars={'XXX' : 'yyy'})
624 assert result == 'yyy', result
626 class CLVar_TestCase(unittest.TestCase):
627 def test_CLVar(self):
628 """Test scons_subst() and scons_subst_list() with CLVar objects"""
632 loc['BAR'] = SCons.Util.CLVar('bar')
633 loc['CALL'] = lambda target, source, env, for_signature: 'call'
636 cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
638 newcmd = scons_subst(cmd, env, gvars=env.Dictionary())
639 assert newcmd == ['test', 'foo', 'bar', 'call', 'test'], newcmd
641 cmd_list = scons_subst_list(cmd, env, gvars=env.Dictionary())
642 assert len(cmd_list) == 1, cmd_list
643 assert cmd_list[0][0] == "test", cmd_list[0][0]
644 assert cmd_list[0][1] == "foo", cmd_list[0][1]
645 assert cmd_list[0][2] == "bar", cmd_list[0][2]
646 assert cmd_list[0][3] == "call", cmd_list[0][3]
647 assert cmd_list[0][4] == "test", cmd_list[0][4]
649 class scons_subst_list_TestCase(SubstTestCase):
654 ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
657 "$SOURCES $NEWLINE $TARGETS",
659 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"],
660 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
665 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
679 "test ${SOURCES.attribute.attr1}",
681 ["test", "attr$1-blah with spaces.cpp", "attr$1-ack.cpp", "attr$1-ack.c"],
684 "test ${SOURCES.attribute.attr2}",
686 ["test", "attr$2-blah with spaces.cpp", "attr$2-ack.cpp", "attr$2-ack.c"],
689 "$DO --in=$FOO --out=$BAR",
691 ["do something", "--in=foo.in", "--out=bar with spaces.out"],
694 # This test is now fixed, and works like it should.
695 "$DO --in=$CRAZY --out=$BAR",
697 ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"],
700 # Try passing a list to scons_subst_list().
701 [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"],
703 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
704 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"],
707 # Test against a former bug in scons_subst_list().
713 # Test double-dollar-sign behavior.
719 # Test various combinations of strings, lists and functions.
728 ['x', 'y'], [['x', 'y']],
738 '$S z', [['x', 'y', 'z']],
739 ['$S'], [['x', 'y']],
740 ['$S z'], [['x', 'y z']], # XXX - IS THIS BEST?
741 ['$S', 'z'], [['x', 'y', 'z']],
743 '$LS z', [['x y', 'z']],
745 ['$LS z'], [['x y z']],
746 ['$LS', 'z'], [['x y', 'z']],
748 '$L z', [['x', 'y', 'z']],
749 ['$L'], [['x', 'y']],
750 ['$L z'], [['x', 'y z']], # XXX - IS THIS BEST?
751 ['$L', 'z'], [['x', 'y', 'z']],
761 # Various uses of UserString.
762 UserString.UserString('x'), [['x']],
763 [UserString.UserString('x')], [['x']],
764 UserString.UserString('$X'), [['x']],
765 [UserString.UserString('$X')], [['x']],
766 UserString.UserString('$US'), [['us']],
767 [UserString.UserString('$US')], [['us']],
771 # Test function calls within ${}.
772 '$FUNCCALL', [['a', 'xc', 'b']],
774 # Test handling of newlines in white space.
775 'foo\nbar', [['foo'], ['bar']],
776 'foo\n\nbar', [['foo'], ['bar']],
777 'foo \n \n bar', [['foo'], ['bar']],
778 'foo \nmiddle\n bar', [['foo'], ['middle'], ['bar']],
780 # Bug reported by Christoph Wiedemann.
781 cvt('$xxx/bin'), [['/bin']],
783 # Test variables smooshed together with different prefixes.
784 'foo$AAA', [['fooa']],
785 '<$AAA', [['<', 'a']],
786 '>$AAA', [['>', 'a']],
787 '|$AAA', [['|', 'a']],
789 # Test callables that don't match our calling arguments.
790 '$CALLABLE2', [['callable-2']],
792 # Test handling of quotes.
793 # XXX Find a way to handle this in the future.
794 #'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']],
796 '${_defines(DEFS)}', [['Q1="q1"', 'Q2="a"']],
799 def test_scons_subst_list(self):
800 """Test scons_subst_list(): basic substitution"""
801 def convert_lists(expect):
802 return [list(map(cvt, l)) for l in expect]
803 return self.basic_comparisons(scons_subst_list, convert_lists)
817 [["test", "$(", "$)"]],
821 "$AAA ${AAA}A $BBBB $BBB",
836 # Verify what happens with no target or source nodes.
847 # Various test refactored from ActionTests.py
849 [['This', 'is', '$(', '$)', 'test']],
850 [['This', 'is', 'test']],
851 [['This', 'is', 'test']],
853 ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
854 [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]],
855 [["|", "a", "|", "b", "|", "c", "1"]],
856 [["|", "|", "c", "1"]],
859 def test_subst_env(self):
860 """Test scons_subst_list(): expansion dictionary"""
861 # The expansion dictionary no longer comes from the construction
862 # environment automatically.
864 s = scons_subst_list('$AAA', env)
867 def test_subst_target_source(self):
868 """Test scons_subst_list(): target= and source= arguments"""
869 env = DummyEnv(self.loc)
870 gvars = env.Dictionary()
871 t1 = self.MyNode('t1')
872 t2 = self.MyNode('t2')
873 s1 = self.MyNode('s1')
874 s2 = self.MyNode('s2')
875 result = scons_subst_list("$TARGET $SOURCES", env,
879 assert result == [['t1', 's1', 's2']], result
880 result = scons_subst_list("$TARGET $SOURCES", env,
884 assert result == [['t1', 's1', 's2']], result
886 # Test interpolating a callable.
889 cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
890 env, target=_t, source=_s,
892 assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
894 def test_subst_escape(self):
895 """Test scons_subst_list(): escape functionality"""
896 env = DummyEnv(self.loc)
897 gvars = env.Dictionary()
898 def escape_func(foo):
899 return '**' + foo + '**'
900 cmd_list = scons_subst_list("abc $LITERALS xyz", env, gvars=gvars)
901 assert cmd_list == [['abc',
902 'foo\nwith\nnewlines',
903 'bar\nwith\nnewlines',
905 c = cmd_list[0][0].escape(escape_func)
907 c = cmd_list[0][1].escape(escape_func)
908 assert c == '**foo\nwith\nnewlines**', c
909 c = cmd_list[0][2].escape(escape_func)
910 assert c == '**bar\nwith\nnewlines**', c
911 c = cmd_list[0][3].escape(escape_func)
914 # We used to treat literals smooshed together like the whole
915 # thing was literal and escape it as a unit. The commented-out
916 # asserts below are in case we ever have to find a way to
917 # resurrect that functionality in some way.
918 cmd_list = scons_subst_list("abc${LITERALS}xyz", env, gvars=gvars)
919 c = cmd_list[0][0].escape(escape_func)
920 #assert c == '**abcfoo\nwith\nnewlines**', c
921 assert c == 'abcfoo\nwith\nnewlines', c
922 c = cmd_list[0][1].escape(escape_func)
923 #assert c == '**bar\nwith\nnewlinesxyz**', c
924 assert c == 'bar\nwith\nnewlinesxyz', c
928 cmd_list = scons_subst_list('echo "target: $TARGET"', env,
929 target=_t, gvars=gvars)
930 c = cmd_list[0][0].escape(escape_func)
931 assert c == 'echo', c
932 c = cmd_list[0][1].escape(escape_func)
933 assert c == '"target:', c
934 c = cmd_list[0][2].escape(escape_func)
937 def test_subst_SUBST_modes(self):
938 """Test scons_subst_list(): SUBST_* modes"""
939 env = DummyEnv(self.loc)
940 subst_list_cases = self.subst_list_cases[:]
941 gvars = env.Dictionary()
943 r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW, gvars=gvars)
947 while subst_list_cases:
948 input, eraw, ecmd, esig = subst_list_cases[:4]
949 result = scons_subst_list(input, env, mode=SUBST_RAW, gvars=gvars)
951 if failed == 0: print
952 print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
954 result = scons_subst_list(input, env, mode=SUBST_CMD, gvars=gvars)
956 if failed == 0: print
957 print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
959 result = scons_subst_list(input, env, mode=SUBST_SIG, gvars=gvars)
961 if failed == 0: print
962 print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
964 del subst_list_cases[:4]
965 assert failed == 0, "%d subst() mode cases failed" % failed
967 def test_subst_attribute_errors(self):
968 """Test scons_subst_list(): handling attribute errors"""
973 scons_subst_list('${foo.bar}', env, gvars={'foo':Foo()})
974 except SCons.Errors.UserError, e:
976 "AttributeError `bar' trying to evaluate `${foo.bar}'",
977 "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
978 "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
980 assert str(e) in expect, e
982 raise AssertionError, "did not catch expected UserError"
984 def test_subst_syntax_errors(self):
985 """Test scons_subst_list(): handling syntax errors"""
988 scons_subst_list('$foo.bar.3.0', env)
989 except SCons.Errors.UserError, e:
991 "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
992 "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
993 "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
995 assert str(e) in expect, e
997 raise AssertionError, "did not catch expected SyntaxError"
999 def test_subst_raw_function(self):
1000 """Test scons_subst_list(): fetch function with SUBST_RAW plus conv"""
1001 # Test that the combination of SUBST_RAW plus a pass-through
1002 # conversion routine allows us to fetch a function through the
1004 env = DummyEnv(self.loc)
1005 gvars = env.Dictionary()
1007 r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, conv=x, gvars=gvars)
1008 assert r == [[self.callable_object_2]], repr(r)
1009 r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, gvars=gvars)
1010 assert r == [['callable-2']], repr(r)
1012 def test_subst_list_overriding_gvars(self):
1013 """Test scons_subst_list(): overriding conv()"""
1018 n1 = self.MyNode('n1')
1019 env = DummyEnv({'NODE' : n1})
1020 gvars=env.Dictionary()
1021 node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
1022 assert node == [[n1]], node
1023 node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
1024 assert node == [[n1]], node
1025 node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
1026 assert node == [[n1]], node
1028 def test_subst_list_overriding_gvars(self):
1029 """Test scons_subst_list(): supplying an overriding gvars dictionary"""
1030 env = DummyEnv({'XXX' : 'xxx'})
1031 result = scons_subst_list('$XXX', env, gvars=env.Dictionary())
1032 assert result == [['xxx']], result
1033 result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
1034 assert result == [['yyy']], result
1036 class scons_subst_once_TestCase(unittest.TestCase):
1039 'CCFLAGS' : '-DFOO',
1041 'RECURSE' : 'r $RECURSE r',
1042 'LIST' : ['a', 'b', 'c'],
1070 ['x', '$LIST', 'y'],
1072 ['x', 'a', 'b', 'c', 'y'],
1074 ['x', 'x $LIST y', 'y'],
1076 ['x', 'x a b c y', 'y'],
1078 ['x', 'x $CCFLAGS y', 'y'],
1080 ['x', 'x $CCFLAGS y', 'y'],
1082 ['x', 'x $RECURSE y', 'y'],
1084 ['x', 'x $RECURSE y', 'y'],
1087 def test_subst_once(self):
1088 """Test the scons_subst_once() function"""
1089 env = DummyEnv(self.loc)
1090 cases = self.basic_cases[:]
1094 input, key, expect = cases[:3]
1095 result = scons_subst_once(input, env, key)
1096 if result != expect:
1097 if failed == 0: print
1098 print " input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect))
1101 assert failed == 0, "%d subst() cases failed" % failed
1103 class quote_spaces_TestCase(unittest.TestCase):
1104 def test_quote_spaces(self):
1105 """Test the quote_spaces() method..."""
1106 q = quote_spaces('x')
1109 q = quote_spaces('x x')
1110 assert q == '"x x"', q
1112 q = quote_spaces('x\tx')
1113 assert q == '"x\tx"', q
1116 def __init__(self, name, children=[]):
1117 self.children = children
1125 def has_builder(self):
1127 def has_explicit_builder(self):
1129 def side_effect(self):
1133 def always_build(self):
1138 class LiteralTestCase(unittest.TestCase):
1139 def test_Literal(self):
1140 """Test the Literal() function."""
1141 input_list = [ '$FOO', Literal('$BAR') ]
1142 gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
1144 def escape_func(cmd):
1145 return '**' + cmd + '**'
1147 cmd_list = scons_subst_list(input_list, None, gvars=gvars)
1148 cmd_list = escape_list(cmd_list[0], escape_func)
1149 assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1151 class SpecialAttrWrapperTestCase(unittest.TestCase):
1152 def test_SpecialAttrWrapper(self):
1153 """Test the SpecialAttrWrapper() function."""
1154 input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
1155 gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
1157 def escape_func(cmd):
1158 return '**' + cmd + '**'
1160 cmd_list = scons_subst_list(input_list, None, gvars=gvars)
1161 cmd_list = escape_list(cmd_list[0], escape_func)
1162 assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1164 cmd_list = scons_subst_list(input_list, None, mode=SUBST_SIG, gvars=gvars)
1165 cmd_list = escape_list(cmd_list[0], escape_func)
1166 assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
1168 class subst_dict_TestCase(unittest.TestCase):
1169 def test_subst_dict(self):
1170 """Test substituting dictionary values in an Action
1174 d = subst_dict(target=t, source=s)
1175 assert str(d['TARGETS'][0]) == 't', d['TARGETS']
1176 assert str(d['TARGET']) == 't', d['TARGET']
1177 assert str(d['SOURCES'][0]) == 's', d['SOURCES']
1178 assert str(d['SOURCE']) == 's', d['SOURCE']
1180 t1 = DummyNode('t1')
1181 t2 = DummyNode('t2')
1182 s1 = DummyNode('s1')
1183 s2 = DummyNode('s2')
1184 d = subst_dict(target=[t1, t2], source=[s1, s2])
1185 TARGETS = sorted([str(x) for x in d['TARGETS']])
1186 assert TARGETS == ['t1', 't2'], d['TARGETS']
1187 assert str(d['TARGET']) == 't1', d['TARGET']
1188 SOURCES = sorted([str(x) for x in d['SOURCES']])
1189 assert SOURCES == ['s1', 's2'], d['SOURCES']
1190 assert str(d['SOURCE']) == 's1', d['SOURCE']
1193 # Fake Value node with no rfile() method.
1194 def __init__(self, name):
1197 return 'v-'+self.name
1198 def get_subst_proxy(self):
1203 return self.__class__('rstr-' + self.name)
1206 t4 = DummyNode('t4')
1208 s3 = DummyNode('s3')
1211 d = subst_dict(target=[t3, t4, t5], source=[s3, s4, s5])
1212 TARGETS = sorted([str(x) for x in d['TARGETS']])
1213 assert TARGETS == ['t4', 'v-t3', 'v-t5'], TARGETS
1214 SOURCES = sorted([str(x) for x in d['SOURCES']])
1215 assert SOURCES == ['s3', 'v-rstr-s4', 'v-s5'], SOURCES
1217 if __name__ == "__main__":
1218 suite = unittest.TestSuite()
1222 SpecialAttrWrapperTestCase,
1223 quote_spaces_TestCase,
1224 scons_subst_TestCase,
1225 scons_subst_list_TestCase,
1226 scons_subst_once_TestCase,
1227 subst_dict_TestCase,
1229 for tclass in tclasses:
1230 names = unittest.getTestCaseNames(tclass, 'test_')
1231 suite.addTests(list(map(tclass, names)))
1232 if not unittest.TextTestRunner().run(suite).wasSuccessful():
1237 # indent-tabs-mode:nil
1239 # vim: set expandtab tabstop=4 shiftwidth=4: