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 UserDict 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 string.replace(str, '/', os.sep)
106 class SubstTestCase(unittest.TestCase):
107 def test_subst(self):
108 """Test the subst() function"""
109 class MyNode(DummyNode):
110 """Simple node work-alike with some extra stuff for testing."""
111 def __init__(self, name):
112 DummyNode.__init__(self, name)
115 self.attribute = Attribute()
116 self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
117 self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
118 def get_stuff(self, extra):
119 return self.name + extra
123 def __init__(self, literal):
124 self.literal = literal
127 def is_literal(self):
131 def __init__(self, value):
138 def function_foo(arg):
141 target = [ MyNode("./foo/bar.exe"),
142 MyNode("/bar/baz.obj"),
143 MyNode("../foo/baz.obj") ]
144 source = [ MyNode("./foo/blah.cpp"),
145 MyNode("/bar/ack.cpp"),
146 MyNode("../foo/ack.c") ]
148 callable_object = TestCallable('callable-1')
164 # $XXX$HHH should expand to GGGIII, not BADNEWS.
168 'FFFIII' : 'BADNEWS',
170 'LITERAL' : TestLiteral("$XXX"),
172 # Test that we can expand to and return a function.
173 #'FUNCTION' : function_foo,
181 # Test various combinations of strings, lists and functions.
194 # Test function calls within ${}.
195 'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}',
196 'FUNC1' : lambda x: x,
197 'FUNC2' : lambda target, source, env, for_signature: ['x$CCC'],
199 # Various tests refactored from ActionTests.py.
200 'LIST' : [["This", "is", "$(", "$a", "$)", "test"]],
203 'RECURSE' : 'foo $RECURSE bar',
204 'RRR' : 'foo $SSS bar',
207 # Test callables that don't match the calling arguments.
208 'CALLABLE' : callable_object,
213 # Basic tests of substitution functionality.
215 # Basics: strings without expansions are left alone, and
216 # the simplest possible expansion to a null-string value.
220 # Test expansion of integer values.
221 "test $zero", "test 0",
222 "test $one", "test 1",
224 # Test multiple re-expansion of values.
225 "test $ONE", "test four",
227 # Test a whole bunch of $TARGET[S] and $SOURCE[S] expansions.
228 "test $TARGETS $SOURCES",
229 "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c",
231 "test ${TARGETS[:]} ${SOURCES[0]}",
232 "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp",
234 "test ${TARGETS[1:]}v",
235 "test /bar/baz.obj ../foo/baz.objv",
240 "test $TARGET$FOO[0]",
241 "test foo/bar.exe[0]",
246 "test ${SOURCES[0:2].foo}",
252 "test ${TARGET.get_stuff('blah')}",
253 "test foo/bar.exeblah",
255 "test ${SOURCES.get_stuff('blah')}",
256 "test foo/blah.cppblah /bar/ack.cppblah ../foo/ack.cblah",
258 "test ${SOURCES[0:2].get_stuff('blah')}",
259 "test foo/blah.cppblah /bar/ack.cppblah",
261 "test ${SOURCES[0:2].get_stuff('blah')}",
262 "test foo/blah.cppblah /bar/ack.cppblah",
264 "test ${SOURCES.attribute.attr1}",
265 "test attr$1-blah.cpp attr$1-ack.cpp attr$1-ack.c",
267 "test ${SOURCES.attribute.attr2}",
268 "test attr$2-blah.cpp attr$2-ack.cpp attr$2-ack.c",
270 # Test adjacent expansions.
277 # Test that adjacent expansions don't get re-interpreted
278 # together. The correct disambiguated expansion should be:
279 # $XXX$HHH => ${FFF}III => GGGIII
281 # $XXX$HHH => ${FFFIII} => BADNEWS
282 "$XXX$HHH", "GGGIII",
284 # Test double-dollar-sign behavior.
285 "$$FFF$HHH", "$FFFIII",
287 # Test that a Literal will stop dollar-sign substitution.
288 "$XXX $LITERAL $FFF", "GGG $XXX GGG",
290 # Test that we don't blow up even if they subscript
291 # something in ways they "can't."
296 # Test various combinations of strings and lists.
320 # Test function calls within ${}.
321 '$FUNCCALL', 'a xc b',
323 # Bug reported by Christoph Wiedemann.
324 cvt('$xxx/bin'), '/bin',
326 # Tests callables that don't match our calling arguments.
327 '$CALLABLE', 'callable-1',
329 # Test handling of quotes.
330 'aaa "bbb ccc" ddd', 'aaa "bbb ccc" ddd',
333 kwargs = {'target' : target, 'source' : source,
334 'gvars' : env.Dictionary()}
338 input, expect = cases[:2]
341 result = apply(scons_subst, (input, env), kwargs)
343 print " input %s generated %s %s" % (repr(input), e.__class__.__name__, str(e))
346 if failed == 0: print
347 print " input %s => %s did not match %s" % (repr(input), repr(result), repr(expect))
350 assert failed == 0, "%d subst() cases failed" % failed
352 # The expansion dictionary no longer comes from the construction
353 # environment automatically.
354 s = scons_subst('$AAA', env)
357 # Tests of the various SUBST_* modes of substitution.
374 "$AAA ${AAA}A $BBBB $BBB",
389 # Verify what happens with no target or source nodes.
400 # Various tests refactored from ActionTests.py.
402 "This is $( $) test",
406 ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
407 "| $( a | b $) | c 1",
412 gvars = env.Dictionary()
416 input, eraw, ecmd, esig = subst_cases[:4]
417 result = scons_subst(input, env, mode=SUBST_RAW, gvars=gvars)
419 if failed == 0: print
420 print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
422 result = scons_subst(input, env, mode=SUBST_CMD, gvars=gvars)
424 if failed == 0: print
425 print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
427 result = scons_subst(input, env, mode=SUBST_SIG, gvars=gvars)
429 if failed == 0: print
430 print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
433 assert failed == 0, "%d subst() mode cases failed" % failed
439 result = scons_subst("$TARGET $SOURCES", env,
442 assert result == "t1 s1 s2", result
443 result = scons_subst("$TARGET $SOURCES", env,
447 assert result == "t1 s1 s2", result
449 result = scons_subst("$TARGET $SOURCES", env, target=[], source=[])
450 assert result == " ", result
451 result = scons_subst("$TARGETS $SOURCE", env, target=[], source=[])
452 assert result == " ", result
454 # Test interpolating a callable.
455 newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
456 env, target=MyNode('t'), source=MyNode('s'),
458 assert newcom == "test foo baz s t", newcom
460 # Test that we handle attribute errors during expansion as expected.
464 scons_subst('${foo.bar}', env, gvars={'foo':Foo()})
465 except SCons.Errors.UserError, e:
467 "AttributeError `bar' trying to evaluate `${foo.bar}'",
468 "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
469 "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
471 assert str(e) in expect, e
473 raise AssertionError, "did not catch expected UserError"
475 # Test that we handle syntax errors during expansion as expected.
477 scons_subst('$foo.bar.3.0', env)
478 except SCons.Errors.UserError, e:
481 "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
482 # Python 2.2, 2.3, 2.4
483 "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
485 "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
487 assert str(e) in expect, e
489 raise AssertionError, "did not catch expected UserError"
491 # Test that we handle type errors
493 scons_subst("${NONE[2]}", env, gvars={'NONE':None})
494 except SCons.Errors.UserError, e:
496 # Python 1.5, 2.2, 2.3, 2.4
497 "TypeError `unsubscriptable object' trying to evaluate `${NONE[2]}'",
498 # Python 2.5 and later
499 "TypeError `'NoneType' object is unsubscriptable' trying to evaluate `${NONE[2]}'",
501 assert str(e) in expect, e
503 raise AssertionError, "did not catch expected UserError"
508 scons_subst("${func(1)}", env, gvars={'func':func})
509 except SCons.Errors.UserError, e:
512 "TypeError `not enough arguments; expected 3, got 1' trying to evaluate `${func(1)}'",
513 # Python 2.2, 2.3, 2.4, 2.5
514 "TypeError `func() takes exactly 3 arguments (1 given)' trying to evaluate `${func(1)}'"
516 assert str(e) in expect, repr(str(e))
518 raise AssertionError, "did not catch expected UserError"
520 # Test that the combination of SUBST_RAW plus a pass-through
521 # conversion routine allows us to fetch a function through the
522 # dictionary. CommandAction uses this to allow delayed evaluation
523 # of $SPAWN variables.
525 r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars)
526 assert r is callable_object, repr(r)
527 r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars)
528 assert r == 'callable-1', repr(r)
530 # Test how we handle overriding the internal conversion routines.
535 env = DummyEnv({'NODE' : n1})
536 gvars = env.Dictionary()
537 node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
538 assert node is n1, node
539 node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
540 assert node is n1, node
541 node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
542 assert node is n1, node
544 # Test returning a function.
545 #env = DummyEnv({'FUNCTION' : foo})
546 #gvars = env.Dictionary()
547 #func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None, gvars=gvars)
548 #assert func is function_foo, func
549 #func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None, gvars=gvars)
550 #assert func is function_foo, func
551 #func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None, gvars=gvars)
552 #assert func is function_foo, func
554 # Test supplying an overriding gvars dictionary.
555 env = DummyEnv({'XXX' : 'xxx'})
556 result = scons_subst('$XXX', env, gvars=env.Dictionary())
557 assert result == 'xxx', result
558 result = scons_subst('$XXX', env, gvars={'XXX' : 'yyy'})
559 assert result == 'yyy', result
561 def test_CLVar(self):
562 """Test scons_subst() and scons_subst_list() with CLVar objects"""
566 loc['BAR'] = SCons.Util.CLVar('bar')
567 loc['CALL'] = lambda target, source, env, for_signature: 'call'
570 cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
572 newcmd = scons_subst(cmd, env, gvars=env.Dictionary())
573 assert newcmd == 'test foo bar call test', newcmd
575 cmd_list = scons_subst_list(cmd, env, gvars=env.Dictionary())
576 assert len(cmd_list) == 1, cmd_list
577 assert cmd_list[0][0] == "test", cmd_list[0][0]
578 assert cmd_list[0][1] == "foo", cmd_list[0][1]
579 assert cmd_list[0][2] == "bar", cmd_list[0][2]
580 assert cmd_list[0][3] == "call", cmd_list[0][3]
581 assert cmd_list[0][4] == "test", cmd_list[0][4]
583 def test_subst_list(self):
584 """Testing the scons_subst_list() method..."""
585 class MyNode(DummyNode):
586 """Simple node work-alike with some extra stuff for testing."""
587 def __init__(self, name):
588 DummyNode.__init__(self, name)
591 self.attribute = Attribute()
592 self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
593 self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
596 def __init__(self, value):
603 target = [ MyNode("./foo/bar.exe"),
604 MyNode("/bar/baz with spaces.obj"),
605 MyNode("../foo/baz.obj") ]
606 source = [ MyNode("./foo/blah with spaces.cpp"),
607 MyNode("/bar/ack.cpp"),
608 MyNode("../foo/ack.c") ]
610 callable_object = TestCallable('callable-2')
615 if SCons.Util.is_List(d) or type(d) is types.TupleType:
616 l.append(str(d[0]) + '=' + str(d[1]))
623 'NEWLINE' : 'before\nafter',
629 'DO' : DummyNode('do something'),
630 'FOO' : DummyNode('foo.in'),
631 'BAR' : DummyNode('bar with spaces.out'),
632 'CRAZY' : DummyNode('crazy\nfile.in'),
634 # $XXX$HHH should expand to GGGIII, not BADNEWS.
638 'FFFIII' : 'BADNEWS',
643 'LITERALS' : [ Literal('foo\nwith\nnewlines'),
644 Literal('bar\nwith\nnewlines') ],
646 # Test various combinations of strings, lists and functions.
657 # Test function calls within ${}.
658 'FUNCCALL' : '${FUNC1("$AAA $FUNC2 $BBB")}',
659 'FUNC1' : lambda x: x,
660 'FUNC2' : lambda target, source, env, for_signature: ['x$CCC'],
662 # Various tests refactored from ActionTests.py.
663 'LIST' : [["This", "is", "$(", "$a", "$)", "test"]],
666 'RECURSE' : 'foo $RECURSE bar',
667 'RRR' : 'foo $SSS bar',
670 # Test callable objects that don't match our calling arguments.
671 'CALLABLE' : callable_object,
673 '_defines' : _defines,
674 'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ],
682 ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
685 "$SOURCES $NEWLINE $TARGETS",
687 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"],
688 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
693 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
707 "test ${SOURCES.attribute.attr1}",
709 ["test", "attr$1-blah with spaces.cpp", "attr$1-ack.cpp", "attr$1-ack.c"],
712 "test ${SOURCES.attribute.attr2}",
714 ["test", "attr$2-blah with spaces.cpp", "attr$2-ack.cpp", "attr$2-ack.c"],
717 "$DO --in=$FOO --out=$BAR",
719 ["do something", "--in=foo.in", "--out=bar with spaces.out"],
722 # This test is now fixed, and works like it should.
723 "$DO --in=$CRAZY --out=$BAR",
725 ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"],
728 # Try passing a list to scons_subst_list().
729 [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"],
731 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
732 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"],
735 # Test against a former bug in scons_subst_list().
741 # Test double-dollar-sign behavior.
747 # Test various combinations of strings, lists and functions.
756 ['x', 'y'], [['x', 'y']],
766 '$S z', [['x', 'y', 'z']],
767 ['$S'], [['x', 'y']],
768 ['$S z'], [['x', 'y z']], # XXX - IS THIS BEST?
769 ['$S', 'z'], [['x', 'y', 'z']],
771 '$LS z', [['x y', 'z']],
773 ['$LS z'], [['x y z']],
774 ['$LS', 'z'], [['x y', 'z']],
776 '$L z', [['x', 'y', 'z']],
777 ['$L'], [['x', 'y']],
778 ['$L z'], [['x', 'y z']], # XXX - IS THIS BEST?
779 ['$L', 'z'], [['x', 'y', 'z']],
789 # Test function calls within ${}.
790 '$FUNCCALL', [['a', 'xc', 'b']],
792 # Test handling of newlines in white space.
793 'foo\nbar', [['foo'], ['bar']],
794 'foo\n\nbar', [['foo'], ['bar']],
795 'foo \n \n bar', [['foo'], ['bar']],
796 'foo \nmiddle\n bar', [['foo'], ['middle'], ['bar']],
798 # Bug reported by Christoph Wiedemann.
799 cvt('$xxx/bin'), [['/bin']],
801 # Test variables smooshed together with different prefixes.
802 'foo$AAA', [['fooa']],
803 '<$AAA', [['<', 'a']],
804 '>$AAA', [['>', 'a']],
805 '|$AAA', [['|', 'a']],
807 # Test callables that don't match our calling arguments.
808 '$CALLABLE', [['callable-2']],
810 # Test handling of quotes.
811 # XXX Find a way to handle this in the future.
812 #'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']],
814 '${_defines(DEFS)}', [['Q1="q1"', 'Q2="a"']],
817 gvars = env.Dictionary()
819 kwargs = {'target' : target, 'source' : source, 'gvars' : gvars}
823 input, expect = cases[:2]
824 expect = map(lambda l: map(cvt, l), expect)
825 result = apply(scons_subst_list, (input, env), kwargs)
827 if failed == 0: print
828 print " input %s => %s did not match %s" % (repr(input), result, repr(expect))
831 assert failed == 0, "%d subst_list() cases failed" % failed
833 # The expansion dictionary no longer comes from the construction
834 # environment automatically.
835 s = scons_subst_list('$AAA', env)
842 result = scons_subst_list("$TARGET $SOURCES", env,
846 assert result == [['t1', 's1', 's2']], result
847 result = scons_subst_list("$TARGET $SOURCES", env,
851 assert result == [['t1', 's1', 's2']], result
853 # Test interpolating a callable.
856 cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
857 env, target=_t, source=_s,
859 assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
861 # Test escape functionality.
862 def escape_func(foo):
863 return '**' + foo + '**'
864 cmd_list = scons_subst_list("abc $LITERALS xyz", env, gvars=gvars)
865 assert cmd_list == [['abc',
866 'foo\nwith\nnewlines',
867 'bar\nwith\nnewlines',
869 c = cmd_list[0][0].escape(escape_func)
871 c = cmd_list[0][1].escape(escape_func)
872 assert c == '**foo\nwith\nnewlines**', c
873 c = cmd_list[0][2].escape(escape_func)
874 assert c == '**bar\nwith\nnewlines**', c
875 c = cmd_list[0][3].escape(escape_func)
878 # We used to treat literals smooshed together like the whole
879 # thing was literal and escape it as a unit. The commented-out
880 # asserts below are in case we ever have to find a way to
881 # resurrect that functionality in some way.
882 cmd_list = scons_subst_list("abc${LITERALS}xyz", env, gvars=gvars)
883 c = cmd_list[0][0].escape(escape_func)
884 #assert c == '**abcfoo\nwith\nnewlines**', c
885 assert c == 'abcfoo\nwith\nnewlines', c
886 c = cmd_list[0][1].escape(escape_func)
887 #assert c == '**bar\nwith\nnewlinesxyz**', c
888 assert c == 'bar\nwith\nnewlinesxyz', c
890 cmd_list = scons_subst_list('echo "target: $TARGET"', env,
891 target=_t, gvars=gvars)
892 c = cmd_list[0][0].escape(escape_func)
893 assert c == 'echo', c
894 c = cmd_list[0][1].escape(escape_func)
895 assert c == '"target:', c
896 c = cmd_list[0][2].escape(escape_func)
899 # Tests of the various SUBST_* modes of substitution.
912 [["test", "$(", "$)"]],
916 "$AAA ${AAA}A $BBBB $BBB",
931 # Verify what happens with no target or source nodes.
942 # Various test refactored from ActionTests.py
944 [['This', 'is', '$(', '$)', 'test']],
945 [['This', 'is', 'test']],
946 [['This', 'is', 'test']],
948 ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
949 [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]],
950 [["|", "a", "|", "b", "|", "c", "1"]],
951 [["|", "|", "c", "1"]],
954 gvars = env.Dictionary()
956 r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW, gvars=gvars)
960 while subst_list_cases:
961 input, eraw, ecmd, esig = subst_list_cases[:4]
962 result = scons_subst_list(input, env, mode=SUBST_RAW, gvars=gvars)
964 if failed == 0: print
965 print " input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
967 result = scons_subst_list(input, env, mode=SUBST_CMD, gvars=gvars)
969 if failed == 0: print
970 print " input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
972 result = scons_subst_list(input, env, mode=SUBST_SIG, gvars=gvars)
974 if failed == 0: print
975 print " input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
977 del subst_list_cases[:4]
978 assert failed == 0, "%d subst() mode cases failed" % failed
980 # Test that we handle attribute errors during expansion as expected.
984 scons_subst_list('${foo.bar}', env, gvars={'foo':Foo()})
985 except SCons.Errors.UserError, e:
987 "AttributeError `bar' trying to evaluate `${foo.bar}'",
988 "AttributeError `Foo instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
989 "AttributeError `'Foo' instance has no attribute 'bar'' trying to evaluate `${foo.bar}'",
991 assert str(e) in expect, e
993 raise AssertionError, "did not catch expected UserError"
995 # Test that we handle syntax errors during expansion as expected.
997 scons_subst_list('$foo.bar.3.0', env)
998 except SCons.Errors.UserError, e:
1000 "SyntaxError `invalid syntax' trying to evaluate `$foo.bar.3.0'",
1001 "SyntaxError `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'",
1002 "SyntaxError `invalid syntax (<string>, line 1)' trying to evaluate `$foo.bar.3.0'",
1004 assert str(e) in expect, e
1006 raise AssertionError, "did not catch expected SyntaxError"
1008 # Test that the combination of SUBST_RAW plus a pass-through
1009 # conversion routine allows us to fetch a function through the
1012 r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars)
1013 assert r == [[callable_object]], repr(r)
1014 r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars)
1015 assert r == [['callable-2']], repr(r)
1017 # Test we handle overriding the internal conversion routines.
1022 env = DummyEnv({'NODE' : n1})
1023 gvars=env.Dictionary()
1024 node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s, gvars=gvars)
1025 assert node == [[n1]], node
1026 node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s, gvars=gvars)
1027 assert node == [[n1]], node
1028 node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s, gvars=gvars)
1029 assert node == [[n1]], node
1031 # Test supplying an overriding gvars dictionary.
1032 env = DummyEnv({'XXX' : 'xxx'})
1033 result = scons_subst_list('$XXX', env, gvars=env.Dictionary())
1034 assert result == [['xxx']], result
1035 result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
1036 assert result == [['yyy']], result
1038 def test_subst_once(self):
1039 """Testing the scons_subst_once() method"""
1042 'CCFLAGS' : '-DFOO',
1044 'RECURSE' : 'r $RECURSE r',
1045 'LIST' : ['a', 'b', 'c'],
1075 ['x', '$LIST', 'y'],
1077 ['x', 'a', 'b', 'c', 'y'],
1079 ['x', 'x $LIST y', 'y'],
1081 ['x', 'x a b c y', 'y'],
1083 ['x', 'x $CCFLAGS y', 'y'],
1085 ['x', 'x $CCFLAGS y', 'y'],
1087 ['x', 'x $RECURSE y', 'y'],
1089 ['x', 'x $RECURSE y', 'y'],
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 def test_quote_spaces(self):
1104 """Testing 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 def test_Literal(self):
1138 """Test the Literal() function."""
1139 input_list = [ '$FOO', Literal('$BAR') ]
1140 gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
1142 def escape_func(cmd):
1143 return '**' + cmd + '**'
1145 cmd_list = scons_subst_list(input_list, None, gvars=gvars)
1146 cmd_list = escape_list(cmd_list[0], escape_func)
1147 assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1149 def test_SpecialAttrWrapper(self):
1150 """Test the SpecialAttrWrapper() function."""
1151 input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
1152 gvars = { 'FOO' : 'BAZ', 'BAR' : 'BLAT' }
1154 def escape_func(cmd):
1155 return '**' + cmd + '**'
1157 cmd_list = scons_subst_list(input_list, None, gvars=gvars)
1158 cmd_list = escape_list(cmd_list[0], escape_func)
1159 assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1161 cmd_list = scons_subst_list(input_list, None, mode=SUBST_SIG, gvars=gvars)
1162 cmd_list = escape_list(cmd_list[0], escape_func)
1163 assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
1165 def test_subst_dict(self):
1166 """Test substituting dictionary values in an Action
1170 d = subst_dict(target=t, source=s)
1171 assert str(d['TARGETS'][0]) == 't', d['TARGETS']
1172 assert str(d['TARGET']) == 't', d['TARGET']
1173 assert str(d['SOURCES'][0]) == 's', d['SOURCES']
1174 assert str(d['SOURCE']) == 's', d['SOURCE']
1176 t1 = DummyNode('t1')
1177 t2 = DummyNode('t2')
1178 s1 = DummyNode('s1')
1179 s2 = DummyNode('s2')
1180 d = subst_dict(target=[t1, t2], source=[s1, s2])
1181 TARGETS = map(lambda x: str(x), d['TARGETS'])
1183 assert TARGETS == ['t1', 't2'], d['TARGETS']
1184 assert str(d['TARGET']) == 't1', d['TARGET']
1185 SOURCES = map(lambda x: str(x), d['SOURCES'])
1187 assert SOURCES == ['s1', 's2'], d['SOURCES']
1188 assert str(d['SOURCE']) == 's1', d['SOURCE']
1191 # Fake Value node with no rfile() method.
1192 def __init__(self, name):
1195 return 'v-'+self.name
1196 def get_subst_proxy(self):
1201 return self.__class__('rstr-' + self.name)
1204 t4 = DummyNode('t4')
1206 s3 = DummyNode('s3')
1209 d = subst_dict(target=[t3, t4, t5], source=[s3, s4, s5])
1210 TARGETS = map(lambda x: str(x), d['TARGETS'])
1212 assert TARGETS == ['t4', 'v-t3', 'v-t5'], TARGETS
1213 SOURCES = map(lambda x: str(x), d['SOURCES'])
1215 assert SOURCES == ['s3', 'v-rstr-s4', 'v-s5'], SOURCES
1217 if __name__ == "__main__":
1218 suite = unittest.makeSuite(SubstTestCase, 'test_')
1219 if not unittest.TextTestRunner().run(suite).wasSuccessful():