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__"
34 from collections import UserDict
38 from SCons.Subst import *
41 """Simple node work-alike."""
42 def __init__(self, name):
43 self.name = os.path.normpath(name)
50 def get_subst_proxy(self):
54 def __init__(self, dict={}):
57 def Dictionary(self, key = None):
62 def __getitem__(self, key):
65 def get(self, key, default):
66 return self.dict.get(key, default)
69 dict = self.dict.copy()
70 dict["TARGETS"] = 'tsig'
71 dict["SOURCES"] = 'ssig'
74 def cs(target=None, source=None, env=None, for_signature=None):
77 def cl(target=None, source=None, env=None, for_signature=None):
80 def CmdGen1(target, source, env, for_signature):
81 # Nifty trick...since Environment references are interpolated,
82 # instantiate an instance of a callable class with this one,
83 # which will then get evaluated.
84 assert str(target) == 't', target
85 assert str(source) == 's', source
86 return "${CMDGEN2('foo', %d)}" % for_signature
89 def __init__(self, mystr, forsig):
91 self.expect_for_signature = forsig
93 def __call__(self, target, source, env, for_signature):
94 assert str(target) == 't', target
95 assert str(source) == 's', source
96 assert for_signature == self.expect_for_signature, for_signature
97 return [ self.mystr, env.Dictionary('BAR') ]
104 return str.replace('/', os.sep)
106 class SubstTestCase(unittest.TestCase):
107 class MyNode(DummyNode):
108 """Simple node work-alike with some extra stuff for testing."""
109 def __init__(self, name):
110 DummyNode.__init__(self, name)
113 self.attribute = Attribute()
114 self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
115 self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
116 def get_stuff(self, extra):
117 return self.name + extra
121 def __init__(self, literal):
122 self.literal = literal
125 def is_literal(self):
129 def __init__(self, value):
136 def function_foo(arg):
139 target = [ MyNode("./foo/bar.exe"),
140 MyNode("/bar/baz with spaces.obj"),
141 MyNode("../foo/baz.obj") ]
142 source = [ MyNode("./foo/blah with spaces.cpp"),
143 MyNode("/bar/ack.cpp"),
144 MyNode("../foo/ack.c") ]
146 callable_object_1 = TestCallable('callable-1')
147 callable_object_2 = TestCallable('callable-2')
152 if SCons.Util.is_List(d) or isinstance(d, tuple):
153 l.append(str(d[0]) + '=' + str(d[1]))
160 'NEWLINE' : 'before\nafter',
174 'DO' : DummyNode('do something'),
175 'FOO' : DummyNode('foo.in'),
176 'BAR' : DummyNode('bar with spaces.out'),
177 'CRAZY' : DummyNode('crazy\nfile.in'),
179 # $XXX$HHH should expand to GGGIII, not BADNEWS.
183 'FFFIII' : 'BADNEWS',
185 'LITERAL' : TestLiteral("$XXX"),
187 # Test that we can expand to and return a function.
188 #'FUNCTION' : function_foo,
193 'LITERALS' : [ Literal('foo\nwith\nnewlines'),
194 Literal('bar\nwith\nnewlines') ],
199 # Test various combinations of strings, lists and functions.
211 'US' : collections.UserString('us'),
213 # Test function calls within ${}.
214 'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}',
215 'FUNC1' : lambda x: x,
216 'FUNC2' : lambda target, source, env, for_signature: ['x$CCC'],
218 # Various tests refactored from ActionTests.py.
219 'LIST' : [["This", "is", "$(", "$a", "$)", "test"]],
222 'RECURSE' : 'foo $RECURSE bar',
223 'RRR' : 'foo $SSS bar',
226 # Test callables that don't match the calling arguments.
227 'CALLABLE1' : callable_object_1,
228 'CALLABLE2' : callable_object_2,
230 '_defines' : _defines,
231 'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ],
234 def basic_comparisons(self, function, convert):
235 env = DummyEnv(self.loc)
236 cases = self.basic_cases[:]
237 kwargs = {'target' : self.target, 'source' : self.source,
238 'gvars' : env.Dictionary()}
242 input, expect = cases[:2]
243 expect = convert(expect)
245 result = function(input, env, **kwargs)
247 fmt = " input %s generated %s (%s)"
248 print fmt % (repr(input), e.__class__.__name__, repr(e))
252 if failed == 0: print
253 print " input %s => %s did not match %s" % (repr(input), repr(result), repr(expect))
256 fmt = "%d %s() cases failed"
257 assert failed == 0, fmt % (failed, function.__name__)
259 class scons_subst_TestCase(SubstTestCase):
261 # Basic tests of substitution functionality.
263 # Basics: strings without expansions are left alone, and
264 # the simplest possible expansion to a null-string value.
268 # Test expansion of integer values.
269 "test $zero", "test 0",
270 "test $one", "test 1",
272 # Test multiple re-expansion of values.
273 "test $ONE", "test four",
275 # Test a whole bunch of $TARGET[S] and $SOURCE[S] expansions.
276 "test $TARGETS $SOURCES",
277 "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp /bar/ack.cpp ../foo/ack.c",
279 "test ${TARGETS[:]} ${SOURCES[0]}",
280 "test foo/bar.exe /bar/baz with spaces.obj ../foo/baz.obj foo/blah with spaces.cpp",
282 "test ${TARGETS[1:]}v",
283 "test /bar/baz with spaces.obj ../foo/baz.objv",
288 "test $TARGET$NO_SUCH_VAR[0]",
289 "test foo/bar.exe[0]",
294 "test ${SOURCES[0:2].foo}",
300 "test ${TARGET.get_stuff('blah')}",
301 "test foo/bar.exeblah",
303 "test ${SOURCES.get_stuff('blah')}",
304 "test foo/blah with spaces.cppblah /bar/ack.cppblah ../foo/ack.cblah",
306 "test ${SOURCES[0:2].get_stuff('blah')}",
307 "test foo/blah with spaces.cppblah /bar/ack.cppblah",
309 "test ${SOURCES[0:2].get_stuff('blah')}",
310 "test foo/blah with spaces.cppblah /bar/ack.cppblah",
312 "test ${SOURCES.attribute.attr1}",
313 "test attr$1-blah with spaces.cpp attr$1-ack.cpp attr$1-ack.c",
315 "test ${SOURCES.attribute.attr2}",
316 "test attr$2-blah with spaces.cpp attr$2-ack.cpp attr$2-ack.c",
318 # Test adjacent expansions.
325 # Test that adjacent expansions don't get re-interpreted
326 # together. The correct disambiguated expansion should be:
327 # $XXX$HHH => ${FFF}III => GGGIII
329 # $XXX$HHH => ${FFFIII} => BADNEWS
330 "$XXX$HHH", "GGGIII",
332 # Test double-dollar-sign behavior.
333 "$$FFF$HHH", "$FFFIII",
335 # Test that a Literal will stop dollar-sign substitution.
336 "$XXX $LITERAL $FFF", "GGG $XXX GGG",
338 # Test that we don't blow up even if they subscript
339 # something in ways they "can't."
344 # Test various combinations of strings and lists.
368 # Various uses of UserString.
369 collections.UserString('x'), 'x',
370 collections.UserString('$X'), 'x',
371 collections.UserString('$US'), 'us',
374 # Test function calls within ${}.
375 '$FUNCCALL', 'a xc b',
377 # Bug reported by Christoph Wiedemann.
378 cvt('$xxx/bin'), '/bin',
380 # Tests callables that don't match our calling arguments.
381 '$CALLABLE1', 'callable-1',
383 # Test handling of quotes.
384 'aaa "bbb ccc" ddd', 'aaa "bbb ccc" ddd',
387 def test_scons_subst(self):
388 """Test scons_subst(): basic substitution"""
389 return self.basic_comparisons(scons_subst, cvt)
407 "$AAA ${AAA}A $BBBB $BBB",
422 # Verify what happens with no target or source nodes.
433 # Various tests refactored from ActionTests.py.
435 "This is $( $) test",
439 ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
440 ["|", "$(", "a", "|", "b", "$)", "|", "c", "1"],
441 ["|", "a", "|", "b", "|", "c", "1"],
442 ["|", "|", "c", "1"],
445 def test_subst_env(self):
446 """Test scons_subst(): expansion dictionary"""
447 # The expansion dictionary no longer comes from the construction
448 # environment automatically.
449 env = DummyEnv(self.loc)
450 s = scons_subst('$AAA', env)
453 def test_subst_SUBST_modes(self):
454 """Test scons_subst(): SUBST_* modes"""
455 env = DummyEnv(self.loc)
456 subst_cases = self.subst_cases[:]
458 gvars = env.Dictionary()
462 input, eraw, ecmd, esig = subst_cases[:4]
463 result = scons_subst(input, env, mode=SUBST_RAW, gvars=gvars)
465 if failed == 0: print
466 print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
468 result = scons_subst(input, env, mode=SUBST_CMD, gvars=gvars)
470 if failed == 0: print
471 print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
473 result = scons_subst(input, env, mode=SUBST_SIG, gvars=gvars)
475 if failed == 0: print
476 print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
479 assert failed == 0, "%d subst() mode cases failed" % failed
481 def test_subst_target_source(self):
482 """Test scons_subst(): target= and source= arguments"""
483 env = DummyEnv(self.loc)
484 t1 = self.MyNode('t1')
485 t2 = self.MyNode('t2')
486 s1 = self.MyNode('s1')
487 s2 = self.MyNode('s2')
488 result = scons_subst("$TARGET $SOURCES", env,
491 assert result == "t1 s1 s2", result
492 result = scons_subst("$TARGET $SOURCES", env,
496 assert result == "t1 s1 s2", result
498 result = scons_subst("$TARGET $SOURCES", env, target=[], source=[])
499 assert result == " ", result
500 result = scons_subst("$TARGETS $SOURCE", env, target=[], source=[])
501 assert result == " ", result
503 def test_subst_callable_expansion(self):
504 """Test scons_subst(): expanding a callable"""
505 env = DummyEnv(self.loc)
506 gvars = env.Dictionary()
507 newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS", env,
508 target=self.MyNode('t'), source=self.MyNode('s'),
510 assert newcom == "test foo bar with spaces.out s t", newcom
512 def test_subst_attribute_errors(self):
513 """Test scons_subst(): handling attribute errors"""
514 env = DummyEnv(self.loc)
518 scons_subst('${foo.bar}', env, gvars={'foo':Foo()})
519 except SCons.Errors.UserError, e:
521 "AttributeError `bar' trying to evaluate `${foo.bar}'",
522 "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
523 "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
525 assert str(e) in expect, e
527 raise AssertionError("did not catch expected UserError")
529 def test_subst_syntax_errors(self):
530 """Test scons_subst(): handling syntax errors"""
531 env = DummyEnv(self.loc)
533 scons_subst('$foo.bar.3.0', env)
534 except SCons.Errors.UserError, e:
537 "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
538 # Python 2.2, 2.3, 2.4
539 "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
541 "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
543 assert str(e) in expect, e
545 raise AssertionError("did not catch expected UserError")
547 def test_subst_type_errors(self):
548 """Test scons_subst(): handling type errors"""
549 env = DummyEnv(self.loc)
551 scons_subst("${NONE[2]}", env, gvars={'NONE':None})
552 except SCons.Errors.UserError, e:
554 # Python 1.5, 2.2, 2.3, 2.4
555 "TypeError `unsubscriptable object' trying to evaluate `${NONE[2]}'",
556 # Python 2.5 and later
557 "TypeError `'NoneType' object is unsubscriptable' trying to evaluate `${NONE[2]}'",
559 assert str(e) in expect, e
561 raise AssertionError("did not catch expected UserError")
566 scons_subst("${func(1)}", env, gvars={'func':func})
567 except SCons.Errors.UserError, e:
570 "TypeError `not enough arguments; expected 3, got 1' trying to evaluate `${func(1)}'",
571 # Python 2.2, 2.3, 2.4, 2.5
572 "TypeError `func() takes exactly 3 arguments (1 given)' trying to evaluate `${func(1)}'"
574 assert str(e) in expect, repr(str(e))
576 raise AssertionError("did not catch expected UserError")
578 def test_subst_raw_function(self):
579 """Test scons_subst(): fetch function with SUBST_RAW plus conv"""
580 # Test that the combination of SUBST_RAW plus a pass-through
581 # conversion routine allows us to fetch a function through the
582 # dictionary. CommandAction uses this to allow delayed evaluation
583 # of $SPAWN variables.
584 env = DummyEnv(self.loc)
585 gvars = env.Dictionary()
587 r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, conv=x, gvars=gvars)
588 assert r is self.callable_object_1, repr(r)
589 r = scons_subst("$CALLABLE1", env, mode=SUBST_RAW, gvars=gvars)
590 assert r == 'callable-1', repr(r)
592 # Test how we handle overriding the internal conversion routines.
596 n1 = self.MyNode('n1')
597 env = DummyEnv({'NODE' : n1})
598 gvars = env.Dictionary()
599 node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
600 assert node is n1, node
601 node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
602 assert node is n1, node
603 node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
604 assert node is n1, node
606 #def test_subst_function_return(self):
607 # """Test scons_subst(): returning a function"""
608 # env = DummyEnv({'FUNCTION' : foo})
609 # gvars = env.Dictionary()
610 # func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None, gvars=gvars)
611 # assert func is function_foo, func
612 # func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None, gvars=gvars)
613 # assert func is function_foo, func
614 # func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None, gvars=gvars)
615 # assert func is function_foo, func
617 def test_subst_overriding_gvars(self):
618 """Test scons_subst(): supplying an overriding gvars dictionary"""
619 env = DummyEnv({'XXX' : 'xxx'})
620 result = scons_subst('$XXX', env, gvars=env.Dictionary())
621 assert result == 'xxx', result
622 result = scons_subst('$XXX', env, gvars={'XXX' : 'yyy'})
623 assert result == 'yyy', result
625 class CLVar_TestCase(unittest.TestCase):
626 def test_CLVar(self):
627 """Test scons_subst() and scons_subst_list() with CLVar objects"""
631 loc['BAR'] = SCons.Util.CLVar('bar')
632 loc['CALL'] = lambda target, source, env, for_signature: 'call'
635 cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
637 newcmd = scons_subst(cmd, env, gvars=env.Dictionary())
638 assert newcmd == ['test', 'foo', 'bar', 'call', 'test'], newcmd
640 cmd_list = scons_subst_list(cmd, env, gvars=env.Dictionary())
641 assert len(cmd_list) == 1, cmd_list
642 assert cmd_list[0][0] == "test", cmd_list[0][0]
643 assert cmd_list[0][1] == "foo", cmd_list[0][1]
644 assert cmd_list[0][2] == "bar", cmd_list[0][2]
645 assert cmd_list[0][3] == "call", cmd_list[0][3]
646 assert cmd_list[0][4] == "test", cmd_list[0][4]
648 class scons_subst_list_TestCase(SubstTestCase):
653 ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
656 "$SOURCES $NEWLINE $TARGETS",
658 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"],
659 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
664 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
678 "test ${SOURCES.attribute.attr1}",
680 ["test", "attr$1-blah with spaces.cpp", "attr$1-ack.cpp", "attr$1-ack.c"],
683 "test ${SOURCES.attribute.attr2}",
685 ["test", "attr$2-blah with spaces.cpp", "attr$2-ack.cpp", "attr$2-ack.c"],
688 "$DO --in=$FOO --out=$BAR",
690 ["do something", "--in=foo.in", "--out=bar with spaces.out"],
693 # This test is now fixed, and works like it should.
694 "$DO --in=$CRAZY --out=$BAR",
696 ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"],
699 # Try passing a list to scons_subst_list().
700 [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"],
702 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
703 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"],
706 # Test against a former bug in scons_subst_list().
712 # Test double-dollar-sign behavior.
718 # Test various combinations of strings, lists and functions.
727 ['x', 'y'], [['x', 'y']],
737 '$S z', [['x', 'y', 'z']],
738 ['$S'], [['x', 'y']],
739 ['$S z'], [['x', 'y z']], # XXX - IS THIS BEST?
740 ['$S', 'z'], [['x', 'y', 'z']],
742 '$LS z', [['x y', 'z']],
744 ['$LS z'], [['x y z']],
745 ['$LS', 'z'], [['x y', 'z']],
747 '$L z', [['x', 'y', 'z']],
748 ['$L'], [['x', 'y']],
749 ['$L z'], [['x', 'y z']], # XXX - IS THIS BEST?
750 ['$L', 'z'], [['x', 'y', 'z']],
760 # Various uses of UserString.
761 collections.UserString('x'), [['x']],
762 [collections.UserString('x')], [['x']],
763 collections.UserString('$X'), [['x']],
764 [collections.UserString('$X')], [['x']],
765 collections.UserString('$US'), [['us']],
766 [collections.UserString('$US')], [['us']],
770 # Test function calls within ${}.
771 '$FUNCCALL', [['a', 'xc', 'b']],
773 # Test handling of newlines in white space.
774 'foo\nbar', [['foo'], ['bar']],
775 'foo\n\nbar', [['foo'], ['bar']],
776 'foo \n \n bar', [['foo'], ['bar']],
777 'foo \nmiddle\n bar', [['foo'], ['middle'], ['bar']],
779 # Bug reported by Christoph Wiedemann.
780 cvt('$xxx/bin'), [['/bin']],
782 # Test variables smooshed together with different prefixes.
783 'foo$AAA', [['fooa']],
784 '<$AAA', [['<', 'a']],
785 '>$AAA', [['>', 'a']],
786 '|$AAA', [['|', 'a']],
788 # Test callables that don't match our calling arguments.
789 '$CALLABLE2', [['callable-2']],
791 # Test handling of quotes.
792 # XXX Find a way to handle this in the future.
793 #'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']],
795 '${_defines(DEFS)}', [['Q1="q1"', 'Q2="a"']],
798 def test_scons_subst_list(self):
799 """Test scons_subst_list(): basic substitution"""
800 def convert_lists(expect):
801 return [list(map(cvt, l)) for l in expect]
802 return self.basic_comparisons(scons_subst_list, convert_lists)
816 [["test", "$(", "$)"]],
820 "$AAA ${AAA}A $BBBB $BBB",
835 # Verify what happens with no target or source nodes.
846 # Various test refactored from ActionTests.py
848 [['This', 'is', '$(', '$)', 'test']],
849 [['This', 'is', 'test']],
850 [['This', 'is', 'test']],
852 ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
853 [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]],
854 [["|", "a", "|", "b", "|", "c", "1"]],
855 [["|", "|", "c", "1"]],
858 def test_subst_env(self):
859 """Test scons_subst_list(): expansion dictionary"""
860 # The expansion dictionary no longer comes from the construction
861 # environment automatically.
863 s = scons_subst_list('$AAA', env)
866 def test_subst_target_source(self):
867 """Test scons_subst_list(): target= and source= arguments"""
868 env = DummyEnv(self.loc)
869 gvars = env.Dictionary()
870 t1 = self.MyNode('t1')
871 t2 = self.MyNode('t2')
872 s1 = self.MyNode('s1')
873 s2 = self.MyNode('s2')
874 result = scons_subst_list("$TARGET $SOURCES", env,
878 assert result == [['t1', 's1', 's2']], result
879 result = scons_subst_list("$TARGET $SOURCES", env,
883 assert result == [['t1', 's1', 's2']], result
885 # Test interpolating a callable.
888 cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
889 env, target=_t, source=_s,
891 assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
893 def test_subst_escape(self):
894 """Test scons_subst_list(): escape functionality"""
895 env = DummyEnv(self.loc)
896 gvars = env.Dictionary()
897 def escape_func(foo):
898 return '**' + foo + '**'
899 cmd_list = scons_subst_list("abc $LITERALS xyz", env, gvars=gvars)
900 assert cmd_list == [['abc',
901 'foo\nwith\nnewlines',
902 'bar\nwith\nnewlines',
904 c = cmd_list[0][0].escape(escape_func)
906 c = cmd_list[0][1].escape(escape_func)
907 assert c == '**foo\nwith\nnewlines**', c
908 c = cmd_list[0][2].escape(escape_func)
909 assert c == '**bar\nwith\nnewlines**', c
910 c = cmd_list[0][3].escape(escape_func)
913 # We used to treat literals smooshed together like the whole
914 # thing was literal and escape it as a unit. The commented-out
915 # asserts below are in case we ever have to find a way to
916 # resurrect that functionality in some way.
917 cmd_list = scons_subst_list("abc${LITERALS}xyz", env, gvars=gvars)
918 c = cmd_list[0][0].escape(escape_func)
919 #assert c == '**abcfoo\nwith\nnewlines**', c
920 assert c == 'abcfoo\nwith\nnewlines', c
921 c = cmd_list[0][1].escape(escape_func)
922 #assert c == '**bar\nwith\nnewlinesxyz**', c
923 assert c == 'bar\nwith\nnewlinesxyz', c
927 cmd_list = scons_subst_list('echo "target: $TARGET"', env,
928 target=_t, gvars=gvars)
929 c = cmd_list[0][0].escape(escape_func)
930 assert c == 'echo', c
931 c = cmd_list[0][1].escape(escape_func)
932 assert c == '"target:', c
933 c = cmd_list[0][2].escape(escape_func)
936 def test_subst_SUBST_modes(self):
937 """Test scons_subst_list(): SUBST_* modes"""
938 env = DummyEnv(self.loc)
939 subst_list_cases = self.subst_list_cases[:]
940 gvars = env.Dictionary()
942 r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW, gvars=gvars)
946 while subst_list_cases:
947 input, eraw, ecmd, esig = subst_list_cases[:4]
948 result = scons_subst_list(input, env, mode=SUBST_RAW, gvars=gvars)
950 if failed == 0: print
951 print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
953 result = scons_subst_list(input, env, mode=SUBST_CMD, gvars=gvars)
955 if failed == 0: print
956 print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
958 result = scons_subst_list(input, env, mode=SUBST_SIG, gvars=gvars)
960 if failed == 0: print
961 print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
963 del subst_list_cases[:4]
964 assert failed == 0, "%d subst() mode cases failed" % failed
966 def test_subst_attribute_errors(self):
967 """Test scons_subst_list(): handling attribute errors"""
972 scons_subst_list('${foo.bar}', env, gvars={'foo':Foo()})
973 except SCons.Errors.UserError, e:
975 "AttributeError `bar' trying to evaluate `${foo.bar}'",
976 "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
977 "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
979 assert str(e) in expect, e
981 raise AssertionError("did not catch expected UserError")
983 def test_subst_syntax_errors(self):
984 """Test scons_subst_list(): handling syntax errors"""
987 scons_subst_list('$foo.bar.3.0', env)
988 except SCons.Errors.UserError, e:
990 "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
991 "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
992 "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
994 assert str(e) in expect, e
996 raise AssertionError("did not catch expected SyntaxError")
998 def test_subst_raw_function(self):
999 """Test scons_subst_list(): fetch function with SUBST_RAW plus conv"""
1000 # Test that the combination of SUBST_RAW plus a pass-through
1001 # conversion routine allows us to fetch a function through the
1003 env = DummyEnv(self.loc)
1004 gvars = env.Dictionary()
1006 r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, conv=x, gvars=gvars)
1007 assert r == [[self.callable_object_2]], repr(r)
1008 r = scons_subst_list("$CALLABLE2", env, mode=SUBST_RAW, gvars=gvars)
1009 assert r == [['callable-2']], repr(r)
1011 def test_subst_list_overriding_gvars(self):
1012 """Test scons_subst_list(): overriding conv()"""
1017 n1 = self.MyNode('n1')
1018 env = DummyEnv({'NODE' : n1})
1019 gvars=env.Dictionary()
1020 node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
1021 assert node == [[n1]], node
1022 node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
1023 assert node == [[n1]], node
1024 node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
1025 assert node == [[n1]], node
1027 def test_subst_list_overriding_gvars(self):
1028 """Test scons_subst_list(): supplying an overriding gvars dictionary"""
1029 env = DummyEnv({'XXX' : 'xxx'})
1030 result = scons_subst_list('$XXX', env, gvars=env.Dictionary())
1031 assert result == [['xxx']], result
1032 result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
1033 assert result == [['yyy']], result
1035 class scons_subst_once_TestCase(unittest.TestCase):
1038 'CCFLAGS' : '-DFOO',
1040 'RECURSE' : 'r $RECURSE r',
1041 'LIST' : ['a', 'b', 'c'],
1069 ['x', '$LIST', 'y'],
1071 ['x', 'a', 'b', 'c', 'y'],
1073 ['x', 'x $LIST y', 'y'],
1075 ['x', 'x a b c y', 'y'],
1077 ['x', 'x $CCFLAGS y', 'y'],
1079 ['x', 'x $CCFLAGS y', 'y'],
1081 ['x', 'x $RECURSE y', 'y'],
1083 ['x', 'x $RECURSE y', 'y'],
1086 def test_subst_once(self):
1087 """Test the scons_subst_once() function"""
1088 env = DummyEnv(self.loc)
1089 cases = self.basic_cases[:]
1093 input, key, expect = cases[:3]
1094 result = scons_subst_once(input, env, key)
1095 if result != expect:
1096 if failed == 0: print
1097 print " input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect))
1100 assert failed == 0, "%d subst() cases failed" % failed
1102 class quote_spaces_TestCase(unittest.TestCase):
1103 def test_quote_spaces(self):
1104 """Test the quote_spaces() method..."""
1105 q = quote_spaces('x')
1108 q = quote_spaces('x x')
1109 assert q == '"x x"', q
1111 q = quote_spaces('x\tx')
1112 assert q == '"x\tx"', q
1115 def __init__(self, name, children=[]):
1116 self.children = children
1124 def has_builder(self):
1126 def has_explicit_builder(self):
1128 def side_effect(self):
1132 def always_build(self):
1137 class LiteralTestCase(unittest.TestCase):
1138 def test_Literal(self):
1139 """Test the Literal() function."""
1140 input_list = [ '$FOO', Literal('$BAR') ]
1141 gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
1143 def escape_func(cmd):
1144 return '**' + cmd + '**'
1146 cmd_list = scons_subst_list(input_list, None, gvars=gvars)
1147 cmd_list = escape_list(cmd_list[0], escape_func)
1148 assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1150 class SpecialAttrWrapperTestCase(unittest.TestCase):
1151 def test_SpecialAttrWrapper(self):
1152 """Test the SpecialAttrWrapper() function."""
1153 input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
1154 gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
1156 def escape_func(cmd):
1157 return '**' + cmd + '**'
1159 cmd_list = scons_subst_list(input_list, None, gvars=gvars)
1160 cmd_list = escape_list(cmd_list[0], escape_func)
1161 assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1163 cmd_list = scons_subst_list(input_list, None, mode=SUBST_SIG, gvars=gvars)
1164 cmd_list = escape_list(cmd_list[0], escape_func)
1165 assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
1167 class subst_dict_TestCase(unittest.TestCase):
1168 def test_subst_dict(self):
1169 """Test substituting dictionary values in an Action
1173 d = subst_dict(target=t, source=s)
1174 assert str(d['TARGETS'][0]) == 't', d['TARGETS']
1175 assert str(d['TARGET']) == 't', d['TARGET']
1176 assert str(d['SOURCES'][0]) == 's', d['SOURCES']
1177 assert str(d['SOURCE']) == 's', d['SOURCE']
1179 t1 = DummyNode('t1')
1180 t2 = DummyNode('t2')
1181 s1 = DummyNode('s1')
1182 s2 = DummyNode('s2')
1183 d = subst_dict(target=[t1, t2], source=[s1, s2])
1184 TARGETS = sorted([str(x) for x in d['TARGETS']])
1185 assert TARGETS == ['t1', 't2'], d['TARGETS']
1186 assert str(d['TARGET']) == 't1', d['TARGET']
1187 SOURCES = sorted([str(x) for x in d['SOURCES']])
1188 assert SOURCES == ['s1', 's2'], d['SOURCES']
1189 assert str(d['SOURCE']) == 's1', d['SOURCE']
1192 # Fake Value node with no rfile() method.
1193 def __init__(self, name):
1196 return 'v-'+self.name
1197 def get_subst_proxy(self):
1202 return self.__class__('rstr-' + self.name)
1205 t4 = DummyNode('t4')
1207 s3 = DummyNode('s3')
1210 d = subst_dict(target=[t3, t4, t5], source=[s3, s4, s5])
1211 TARGETS = sorted([str(x) for x in d['TARGETS']])
1212 assert TARGETS == ['t4', 'v-t3', 'v-t5'], TARGETS
1213 SOURCES = sorted([str(x) for x in d['SOURCES']])
1214 assert SOURCES == ['s3', 'v-rstr-s4', 'v-s5'], SOURCES
1216 if __name__ == "__main__":
1217 suite = unittest.TestSuite()
1221 SpecialAttrWrapperTestCase,
1222 quote_spaces_TestCase,
1223 scons_subst_TestCase,
1224 scons_subst_list_TestCase,
1225 scons_subst_once_TestCase,
1226 subst_dict_TestCase,
1228 for tclass in tclasses:
1229 names = unittest.getTestCaseNames(tclass, 'test_')
1230 suite.addTests(list(map(tclass, names)))
1231 if not unittest.TextTestRunner().run(suite).wasSuccessful():
1236 # indent-tabs-mode:nil
1238 # vim: set expandtab tabstop=4 shiftwidth=4: