When subst() returns a non-string object, return the object and not a list on 1.5.2.
[scons.git] / src / engine / SCons / UtilTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import os
27 import os.path
28 import string
29 import sys
30 import types
31 import unittest
32 from UserDict import UserDict
33
34 from SCons.Util import *
35 import TestCmd
36
37 import SCons.Errors
38
39 class OutBuffer:
40     def __init__(self):
41         self.buffer = ""
42
43     def write(self, str):
44         self.buffer = self.buffer + str
45
46 class DummyNode:
47     """Simple node work-alike."""
48     def __init__(self, name):
49         self.name = os.path.normpath(name)
50     def __str__(self):
51         return self.name
52     def is_literal(self):
53         return 1
54     def rfile(self):
55         return self
56     def get_subst_proxy(self):
57         return self
58
59 class DummyEnv:
60     def __init__(self, dict={}):
61         self.dict = dict
62
63     def Dictionary(self, key = None):
64         if not key:
65             return self.dict
66         return self.dict[key]
67
68     def __getitem__(self, key):
69         return self.dict[key]
70
71     def get(self, key, default):
72         return self.dict.get(key, default)
73
74     def sig_dict(self):
75         dict = self.dict.copy()
76         dict["TARGETS"] = 'tsig'
77         dict["SOURCES"] = 'ssig'
78         return dict
79
80 def cs(target=None, source=None, env=None, for_signature=None):
81     return 'cs'
82
83 def cl(target=None, source=None, env=None, for_signature=None):
84     return ['cl']
85
86 def CmdGen1(target, source, env, for_signature):
87     # Nifty trick...since Environment references are interpolated,
88     # instantiate an instance of a callable class with this one,
89     # which will then get evaluated.
90     assert str(target) == 't', target
91     assert str(source) == 's', source
92     return "${CMDGEN2('foo', %d)}" % for_signature
93
94 class CmdGen2:
95     def __init__(self, mystr, forsig):
96         self.mystr = mystr
97         self.expect_for_signature = forsig
98
99     def __call__(self, target, source, env, for_signature):
100         assert str(target) == 't', target
101         assert str(source) == 's', source
102         assert for_signature == self.expect_for_signature, for_signature
103         return [ self.mystr, env.Dictionary('BAR') ]
104
105 if os.sep == '/':
106     def cvt(str):
107         return str
108 else:
109     def cvt(str):
110         return string.replace(str, '/', os.sep)
111
112 class UtilTestCase(unittest.TestCase):
113     def test_subst(self):
114         """Test the subst() function"""
115         class MyNode(DummyNode):
116             """Simple node work-alike with some extra stuff for testing."""
117             def __init__(self, name):
118                 DummyNode.__init__(self, name)
119                 class Attribute:
120                     pass
121                 self.attribute = Attribute()
122                 self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
123                 self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
124             def get_stuff(self, extra):
125                 return self.name + extra
126             foo = 1
127
128         class TestLiteral:
129             def __init__(self, literal):
130                 self.literal = literal
131             def __str__(self):
132                 return self.literal
133             def is_literal(self):
134                 return 1
135
136         class TestCallable:
137             def __init__(self, value):
138                 self.value = value
139             def __call__(self):
140                 pass
141             def __str__(self):
142                 return self.value
143
144         def function_foo(arg):
145             pass
146
147         target = [ MyNode("./foo/bar.exe"),
148                    MyNode("/bar/baz.obj"),
149                    MyNode("../foo/baz.obj") ]
150         source = [ MyNode("./foo/blah.cpp"),
151                    MyNode("/bar/ack.cpp"),
152                    MyNode("../foo/ack.c") ]
153
154         loc = {
155             'xxx'       : None,
156             'null'      : '',
157             'zero'      : 0,
158             'one'       : 1,
159             'BAR'       : 'baz',
160             'ONE'       : '$TWO',
161             'TWO'       : '$THREE',
162             'THREE'     : 'four',
163
164             'AAA'       : 'a',
165             'BBB'       : 'b',
166             'CCC'       : 'c',
167
168             # $XXX$HHH should expand to GGGIII, not BADNEWS.
169             'XXX'       : '$FFF',
170             'FFF'       : 'GGG',
171             'HHH'       : 'III',
172             'FFFIII'    : 'BADNEWS',
173
174             'LITERAL'   : TestLiteral("$XXX"),
175
176             # Test that we can expand to and return a function.
177             #'FUNCTION'  : function_foo,
178
179             'CMDGEN1'   : CmdGen1,
180             'CMDGEN2'   : CmdGen2,
181
182             'NOTHING'   : "",
183             'NONE'      : None,
184
185             # Test various combinations of strings, lists and functions.
186             'N'         : None,
187             'X'         : 'x',
188             'Y'         : '$X',
189             'R'         : '$R',
190             'S'         : 'x y',
191             'LS'        : ['x y'],
192             'L'         : ['x', 'y'],
193             'CS'        : cs,
194             'CL'        : cl,
195
196             # Test function calls within ${}.
197             'FUNCCALL'  : '${FUNC1("$AAA $FUNC2 $BBB")}',
198             'FUNC1'     : lambda x: x,
199             'FUNC2'     : lambda target, source, env, for_signature: ['x$CCC'],
200
201             # Various tests refactored from ActionTests.py.
202             'LIST'      : [["This", "is", "$(", "$a", "$)", "test"]],
203
204             # Test recursion.
205             'RECURSE'   : 'foo $RECURSE bar',
206             'RRR'       : 'foo $SSS bar',
207             'SSS'       : '$RRR',
208
209             # Test callables that don't match the calling arguments.
210             'CALLABLE'  : TestCallable('callable-1'),
211         }
212
213         env = DummyEnv(loc)
214
215         # Basic tests of substitution functionality.
216         cases = [
217             # Basics:  strings without expansions are left alone, and
218             # the simplest possible expansion to a null-string value.
219             "test",                 "test",
220             "$null",                "",
221
222             # Test expansion of integer values.
223             "test $zero",           "test 0",
224             "test $one",            "test 1",
225
226             # Test multiple re-expansion of values.
227             "test $ONE",            "test four",
228
229             # Test a whole bunch of $TARGET[S] and $SOURCE[S] expansions.
230             "test $TARGETS $SOURCES",
231             "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c",
232
233             "test ${TARGETS[:]} ${SOURCES[0]}",
234             "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp",
235
236             "test ${TARGETS[1:]}v",
237             "test /bar/baz.obj ../foo/baz.objv",
238
239             "test $TARGET",
240             "test foo/bar.exe",
241
242             "test $TARGET$FOO[0]",
243             "test foo/bar.exe[0]",
244
245             "test $TARGETS.foo",
246             "test 1 1 1",
247
248             "test ${SOURCES[0:2].foo}",
249             "test 1 1",
250
251             "test $SOURCE.foo",
252             "test 1",
253
254             "test ${TARGET.get_stuff('blah')}",
255             "test foo/bar.exeblah",
256
257             "test ${SOURCES.get_stuff('blah')}",
258             "test foo/blah.cppblah /bar/ack.cppblah ../foo/ack.cblah",
259
260             "test ${SOURCES[0:2].get_stuff('blah')}",
261             "test foo/blah.cppblah /bar/ack.cppblah",
262
263             "test ${SOURCES[0:2].get_stuff('blah')}",
264             "test foo/blah.cppblah /bar/ack.cppblah",
265
266             "test ${SOURCES.attribute.attr1}",
267             "test attr$1-blah.cpp attr$1-ack.cpp attr$1-ack.c",
268
269             "test ${SOURCES.attribute.attr2}",
270             "test attr$2-blah.cpp attr$2-ack.cpp attr$2-ack.c",
271
272             # Test adjacent expansions.
273             "foo$BAR",
274             "foobaz",
275
276             "foo${BAR}",
277             "foobaz",
278
279             # Test that adjacent expansions don't get re-interpreted
280             # together.  The correct disambiguated expansion should be:
281             #   $XXX$HHH => ${FFF}III => GGGIII
282             # not:
283             #   $XXX$HHH => ${FFFIII} => BADNEWS
284             "$XXX$HHH",             "GGGIII",
285
286             # Test double-dollar-sign behavior.
287             "$$FFF$HHH",            "$FFFIII",
288
289             # Test that a Literal will stop dollar-sign substitution.
290             "$XXX $LITERAL $FFF",   "GGG $XXX GGG",
291
292             # Test that we don't blow up even if they subscript
293             # something in ways they "can't."
294             "${FFF[0]}",            "G",
295             "${FFF[7]}",            "",
296             "${NOTHING[1]}",        "",
297             "${NONE[2]}",           "",
298
299             # Test various combinations of strings and lists.
300             #None,                   '',
301             '',                     '',
302             'x',                    'x',
303             'x y',                  'x y',
304             '$N',                   '',
305             '$X',                   'x',
306             '$Y',                   'x',
307             '$R',                   '',
308             '$S',                   'x y',
309             '$LS',                  'x y',
310             '$L',                   'x y',
311             '$S z',                 'x y z',
312             '$LS z',                'x y z',
313             '$L z',                 'x y z',
314             #cs,                     'cs',
315             #cl,                     'cl',
316             '$CS',                  'cs',
317             '$CL',                  'cl',
318
319             # Test function calls within ${}.
320             '$FUNCCALL',            'a xc b',
321
322             # Bug reported by Christoph Wiedemann.
323             cvt('$xxx/bin'),        '/bin',
324
325             # Tests callables that don't match our calling arguments.
326             '$CALLABLE',            'callable-1',
327         ]
328
329         kwargs = {'target' : target, 'source' : source}
330
331         failed = 0
332         while cases:
333             input, expect = cases[:2]
334             expect = cvt(expect)
335             result = apply(scons_subst, (input, env), kwargs)
336             if result != expect:
337                 if failed == 0: print
338                 print "    input %s => %s did not match %s" % (repr(input), repr(result), repr(expect))
339                 failed = failed + 1
340             del cases[:2]
341         assert failed == 0, "%d subst() cases failed" % failed
342
343         # Tests of the various SUBST_* modes of substitution.
344         subst_cases = [
345             "test $xxx",
346                 "test ",
347                 "test",
348                 "test",
349
350             "test $($xxx$)",
351                 "test $($)",
352                 "test",
353                 "test",
354
355             "test $( $xxx $)",
356                 "test $(  $)",
357                 "test",
358                 "test",
359
360             "$AAA ${AAA}A $BBBB $BBB",
361                 "a aA  b",
362                 "a aA b",
363                 "a aA b",
364
365             "$RECURSE",
366                "foo  bar",
367                "foo bar",
368                "foo bar",
369
370             "$RRR",
371                "foo  bar",
372                "foo bar",
373                "foo bar",
374
375             # Verify what happens with no target or source nodes.
376             "$TARGET $SOURCES",
377                 " ",
378                 "",
379                 "",
380
381             "$TARGETS $SOURCE",
382                 " ",
383                 "",
384                 "",
385
386             # Various tests refactored from ActionTests.py.
387             "${LIST}",
388                "This is $(  $) test",
389                "This is test",
390                "This is test",
391
392             ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
393                 "| $( a | b $) | c 1",
394                 "| a | b | c 1",
395                 "| | c 1",
396         ]
397
398         failed = 0
399         while subst_cases:
400             input, eraw, ecmd, esig = subst_cases[:4]
401             result = scons_subst(input, env, mode=SUBST_RAW)
402             if result != eraw:
403                 if failed == 0: print
404                 print "    input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
405                 failed = failed + 1
406             result = scons_subst(input, env, mode=SUBST_CMD)
407             if result != ecmd:
408                 if failed == 0: print
409                 print "    input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
410                 failed = failed + 1
411             result = scons_subst(input, env, mode=SUBST_SIG)
412             if result != esig:
413                 if failed == 0: print
414                 print "    input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
415                 failed = failed + 1
416             del subst_cases[:4]
417         assert failed == 0, "%d subst() mode cases failed" % failed
418
419         t1 = MyNode('t1')
420         t2 = MyNode('t2')
421         s1 = MyNode('s1')
422         s2 = MyNode('s2')
423         result = scons_subst("$TARGET $SOURCES", env,
424                                   target=[t1, t2],
425                                   source=[s1, s2])
426         assert result == "t1 s1 s2", result
427         result = scons_subst("$TARGET $SOURCES", env,
428                                   target=[t1, t2],
429                                   source=[s1, s2],
430                                   dict={})
431         assert result == " ", result
432
433         result = scons_subst("$TARGET $SOURCES", env, target=[], source=[])
434         assert result == " ", result
435         result = scons_subst("$TARGETS $SOURCE", env, target=[], source=[])
436         assert result == " ", result
437
438         # Test interpolating a callable.
439         newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
440                              env, target=MyNode('t'), source=MyNode('s'))
441         assert newcom == "test foo baz s t", newcom
442
443         # Test that we handle syntax errors during expansion as expected.
444         try:
445             scons_subst('$foo.bar.3.0', env)
446         except SCons.Errors.UserError, e:
447             expect1 = "Syntax error `invalid syntax' trying to evaluate `$foo.bar.3.0'"
448             expect2 = "Syntax error `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'"
449             assert str(e) in [expect1, expect2], e
450         else:
451             raise AssertionError, "did not catch expected UserError"
452
453         # Test how we handle overriding the internal conversion routines.
454         def s(obj):
455             return obj
456
457         n1 = MyNode('n1')
458         env = DummyEnv({'NODE' : n1})
459         node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s)
460         assert node is n1, node
461         node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s)
462         assert node is n1, node
463         node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s)
464         assert node is n1, node
465
466         # Test returning a function.
467         #env = DummyEnv({'FUNCTION' : foo})
468         #func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None)
469         #assert func is function_foo, func
470         #func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None)
471         #assert func is function_foo, func
472         #func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None)
473         #assert func is function_foo, func
474
475         # Test supplying an overriding gvars dictionary.
476         env = DummyEnv({'XXX' : 'xxx'})
477         result = scons_subst('$XXX', env)
478         assert result == 'xxx', result
479         result = scons_subst('$XXX', env, gvars={'XXX' : 'yyy'})
480         assert result == 'yyy', result
481
482     def test_subst_list(self):
483         """Testing the scons_subst_list() method..."""
484         class MyNode(DummyNode):
485             """Simple node work-alike with some extra stuff for testing."""
486             def __init__(self, name):
487                 DummyNode.__init__(self, name)
488                 class Attribute:
489                     pass
490                 self.attribute = Attribute()
491                 self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
492                 self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
493
494         class TestCallable:
495             def __init__(self, value):
496                 self.value = value
497             def __call__(self):
498                 pass
499             def __str__(self):
500                 return self.value
501
502         target = [ MyNode("./foo/bar.exe"),
503                    MyNode("/bar/baz with spaces.obj"),
504                    MyNode("../foo/baz.obj") ]
505         source = [ MyNode("./foo/blah with spaces.cpp"),
506                    MyNode("/bar/ack.cpp"),
507                    MyNode("../foo/ack.c") ]
508
509         loc = {
510             'xxx'       : None,
511             'NEWLINE'   : 'before\nafter',
512
513             'AAA'       : 'a',
514             'BBB'       : 'b',
515             'CCC'       : 'c',
516
517             'DO'        : DummyNode('do something'),
518             'FOO'       : DummyNode('foo.in'),
519             'BAR'       : DummyNode('bar with spaces.out'),
520             'CRAZY'     : DummyNode('crazy\nfile.in'),
521
522             # $XXX$HHH should expand to GGGIII, not BADNEWS.
523             'XXX'       : '$FFF',
524             'FFF'       : 'GGG',
525             'HHH'       : 'III',
526             'FFFIII'    : 'BADNEWS',
527
528             'CMDGEN1'   : CmdGen1,
529             'CMDGEN2'   : CmdGen2,
530
531             'LITERALS'  : [ Literal('foo\nwith\nnewlines'),
532                             Literal('bar\nwith\nnewlines') ],
533
534             # Test various combinations of strings, lists and functions.
535             'N'         : None,
536             'X'         : 'x',
537             'Y'         : '$X',
538             'R'         : '$R',
539             'S'         : 'x y',
540             'LS'        : ['x y'],
541             'L'         : ['x', 'y'],
542             'CS'        : cs,
543             'CL'        : cl,
544
545             # Test function calls within ${}.
546             'FUNCCALL'  : '${FUNC1("$AAA $FUNC2 $BBB")}',
547             'FUNC1'     : lambda x: x,
548             'FUNC2'     : lambda target, source, env, for_signature: ['x$CCC'],
549
550             # Various tests refactored from ActionTests.py.
551             'LIST'      : [["This", "is", "$(", "$a", "$)", "test"]],
552
553             # Test recursion.
554             'RECURSE'   : 'foo $RECURSE bar',
555             'RRR'       : 'foo $SSS bar',
556             'SSS'       : '$RRR',
557
558             # Test callable objects that don't match our calling arguments.
559             'CALLABLE'  : TestCallable('callable-2'),
560         }
561
562         env = DummyEnv(loc)
563
564         cases = [
565             "$TARGETS",
566             [
567                 ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
568             ],
569
570             "$SOURCES $NEWLINE $TARGETS",
571             [
572                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"],
573                 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
574             ],
575
576             "$SOURCES$NEWLINE",
577             [
578                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
579                 ["after"],
580             ],
581
582             "foo$FFF",
583             [
584                 ["fooGGG"],
585             ],
586
587             "foo${FFF}",
588             [
589                 ["fooGGG"],
590             ],
591
592             "test ${SOURCES.attribute.attr1}",
593             [
594                 ["test", "attr$1-blah with spaces.cpp", "attr$1-ack.cpp", "attr$1-ack.c"],
595             ],
596
597             "test ${SOURCES.attribute.attr2}",
598             [
599                 ["test", "attr$2-blah with spaces.cpp", "attr$2-ack.cpp", "attr$2-ack.c"],
600             ],
601
602             "$DO --in=$FOO --out=$BAR",
603             [
604                 ["do something", "--in=foo.in", "--out=bar with spaces.out"],
605             ],
606
607             # This test is now fixed, and works like it should.
608             "$DO --in=$CRAZY --out=$BAR",
609             [
610                 ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"],
611             ],
612
613             # Try passing a list to scons_subst_list().
614             [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"],
615             [
616                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
617                 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"],
618             ],
619
620             # Test against a former bug in scons_subst_list().
621             "$XXX$HHH",
622             [
623                 ["GGGIII"],
624             ],
625
626             # Test double-dollar-sign behavior.
627             "$$FFF$HHH",
628             [
629                 ["$FFFIII"],
630             ],
631
632             # Test various combinations of strings, lists and functions.
633             None,                   [[]],
634             [None],                 [[]],
635             '',                     [[]],
636             [''],                   [[]],
637             'x',                    [['x']],
638             ['x'],                  [['x']],
639             'x y',                  [['x', 'y']],
640             ['x y'],                [['x y']],
641             ['x', 'y'],             [['x', 'y']],
642             '$N',                   [[]],
643             ['$N'],                 [[]],
644             '$X',                   [['x']],
645             ['$X'],                 [['x']],
646             '$Y',                   [['x']],
647             ['$Y'],                 [['x']],
648             #'$R',                   [[]],
649             #['$R'],                 [[]],
650             '$S',                   [['x', 'y']],
651             '$S z',                 [['x', 'y', 'z']],
652             ['$S'],                 [['x', 'y']],
653             ['$S z'],               [['x', 'y z']],     # XXX - IS THIS BEST?
654             ['$S', 'z'],            [['x', 'y', 'z']],
655             '$LS',                  [['x y']],
656             '$LS z',                [['x y', 'z']],
657             ['$LS'],                [['x y']],
658             ['$LS z'],              [['x y z']],
659             ['$LS', 'z'],           [['x y', 'z']],
660             '$L',                   [['x', 'y']],
661             '$L z',                 [['x', 'y', 'z']],
662             ['$L'],                 [['x', 'y']],
663             ['$L z'],               [['x', 'y z']],     # XXX - IS THIS BEST?
664             ['$L', 'z'],            [['x', 'y', 'z']],
665             cs,                     [['cs']],
666             [cs],                   [['cs']],
667             cl,                     [['cl']],
668             [cl],                   [['cl']],
669             '$CS',                  [['cs']],
670             ['$CS'],                [['cs']],
671             '$CL',                  [['cl']],
672             ['$CL'],                [['cl']],
673
674             # Test function calls within ${}.
675             '$FUNCCALL',            [['a', 'xc', 'b']],
676
677             # Test handling of newlines in white space.
678             'foo\nbar',             [['foo'], ['bar']],
679             'foo\n\nbar',           [['foo'], ['bar']],
680             'foo \n \n bar',        [['foo'], ['bar']],
681             'foo \nmiddle\n bar',   [['foo'], ['middle'], ['bar']],
682
683             # Bug reported by Christoph Wiedemann.
684             cvt('$xxx/bin'),        [['/bin']],
685
686             # Test variables smooshed together with different prefixes.
687             'foo$AAA',              [['fooa']],
688             '<$AAA',                [['<', 'a']],
689             '>$AAA',                [['>', 'a']],
690             '|$AAA',                [['|', 'a']],
691
692             # Test callables that don't match our calling arguments.
693             '$CALLABLE',            [['callable-2']],
694         ]
695
696         kwargs = {'target' : target, 'source' : source}
697
698         failed = 0
699         while cases:
700             input, expect = cases[:2]
701             expect = map(lambda l: map(cvt, l), expect)
702             result = apply(scons_subst_list, (input, env), kwargs)
703             if result != expect:
704                 if failed == 0: print
705                 print "    input %s => %s did not match %s" % (repr(input), result, repr(expect))
706                 failed = failed + 1
707             del cases[:2]
708         assert failed == 0, "%d subst_list() cases failed" % failed
709
710         t1 = MyNode('t1')
711         t2 = MyNode('t2')
712         s1 = MyNode('s1')
713         s2 = MyNode('s2')
714         result = scons_subst_list("$TARGET $SOURCES", env,
715                                   target=[t1, t2],
716                                   source=[s1, s2])
717         assert result == [['t1', 's1', 's2']], result
718         result = scons_subst_list("$TARGET $SOURCES", env,
719                                   target=[t1, t2],
720                                   source=[s1, s2],
721                                   dict={})
722         assert result == [[]], result
723
724         # Test interpolating a callable.
725         _t = DummyNode('t')
726         _s = DummyNode('s')
727         cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
728                                     env, target=_t, source=_s)
729         assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
730
731         # Test escape functionality.
732         def escape_func(foo):
733             return '**' + foo + '**'
734         cmd_list = scons_subst_list("abc $LITERALS xyz", env)
735         assert cmd_list == [['abc',
736                              'foo\nwith\nnewlines',
737                              'bar\nwith\nnewlines',
738                              'xyz']], cmd_list
739         c = cmd_list[0][0].escape(escape_func)
740         assert c == 'abc', c
741         c = cmd_list[0][1].escape(escape_func)
742         assert c == '**foo\nwith\nnewlines**', c
743         c = cmd_list[0][2].escape(escape_func)
744         assert c == '**bar\nwith\nnewlines**', c
745         c = cmd_list[0][3].escape(escape_func)
746         assert c == 'xyz', c
747
748         cmd_list = scons_subst_list("abc${LITERALS}xyz", env)
749         c = cmd_list[0][0].escape(escape_func)
750         assert c == '**abcfoo\nwith\nnewlines**', c
751         c = cmd_list[0][1].escape(escape_func)
752         assert c == '**bar\nwith\nnewlinesxyz**', c
753
754         # Tests of the various SUBST_* modes of substitution.
755         subst_list_cases = [
756             "test $xxx",
757                 [["test"]],
758                 [["test"]],
759                 [["test"]],
760
761             "test $($xxx$)",
762                 [["test", "$($)"]],
763                 [["test"]],
764                 [["test"]],
765
766             "test $( $xxx $)",
767                 [["test", "$(", "$)"]],
768                 [["test"]],
769                 [["test"]],
770
771             "$AAA ${AAA}A $BBBB $BBB",
772                 [["a", "aA", "b"]],
773                 [["a", "aA", "b"]],
774                 [["a", "aA", "b"]],
775
776             "$RECURSE",
777                 [["foo", "bar"]],
778                 [["foo", "bar"]],
779                 [["foo", "bar"]],
780
781             "$RRR",
782                 [["foo", "bar"]],
783                 [["foo", "bar"]],
784                 [["foo", "bar"]],
785
786             # Verify what happens with no target or source nodes.
787             "$TARGET $SOURCES",
788                 [[]],
789                 [[]],
790                 [[]],
791
792             "$TARGETS $SOURCE",
793                 [[]],
794                 [[]],
795                 [[]],
796
797             # Various test refactored from ActionTests.py
798             "${LIST}",
799                 [['This', 'is', '$(', '$)', 'test']],
800                 [['This', 'is', 'test']],
801                 [['This', 'is', 'test']],
802
803             ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
804                 [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]],
805                 [["|", "a", "|", "b", "|", "c", "1"]],
806                 [["|", "|", "c", "1"]],
807         ]
808
809         r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW)
810         assert r == [[]], r
811
812         failed = 0
813         while subst_list_cases:
814             input, eraw, ecmd, esig = subst_list_cases[:4]
815             result = scons_subst_list(input, env, mode=SUBST_RAW)
816             if result != eraw:
817                 if failed == 0: print
818                 print "    input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
819                 failed = failed + 1
820             result = scons_subst_list(input, env, mode=SUBST_CMD)
821             if result != ecmd:
822                 if failed == 0: print
823                 print "    input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
824                 failed = failed + 1
825             result = scons_subst_list(input, env, mode=SUBST_SIG)
826             if result != esig:
827                 if failed == 0: print
828                 print "    input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
829                 failed = failed + 1
830             del subst_list_cases[:4]
831         assert failed == 0, "%d subst() mode cases failed" % failed
832
833         # Test that we handle syntax errors during expansion as expected.
834         try:
835             scons_subst_list('$foo.bar.3.0', env)
836         except SCons.Errors.UserError, e:
837             expect1 = "Syntax error `invalid syntax' trying to evaluate `$foo.bar.3.0'"
838             expect2 = "Syntax error `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'"
839             assert str(e) in [expect1, expect2], e
840         else:
841             raise AssertionError, "did not catch expected SyntaxError"
842
843         # Test we handle overriding the internal conversion routines.
844         def s(obj):
845             return obj
846
847         n1 = MyNode('n1')
848         env = DummyEnv({'NODE' : n1})
849         node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s)
850         assert node == [[n1]], node
851         node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s)
852         assert node == [[n1]], node
853         node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s)
854         assert node == [[n1]], node
855
856         # Test supplying an overriding gvars dictionary.
857         env = DummyEnv({'XXX' : 'xxx'})
858         result = scons_subst_list('$XXX', env)
859         assert result == [['xxx']], result
860         result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
861         assert result == [['yyy']], result
862
863     def test_subst_once(self):
864         """Testing the scons_subst_once() method"""
865
866         loc = {
867             'CCFLAGS'           : '-DFOO',
868             'ONE'               : 1,
869             'RECURSE'           : 'r $RECURSE r',
870             'LIST'              : ['a', 'b', 'c'],
871         }
872
873         env = DummyEnv(loc)
874
875         cases = [
876             '$CCFLAGS -DBAR',
877             'OTHER_KEY',
878             '$CCFLAGS -DBAR',
879
880             '$CCFLAGS -DBAR',
881             'CCFLAGS',
882             '-DFOO -DBAR',
883
884             'x $ONE y',
885             'ONE',
886             'x 1 y',
887
888             'x $RECURSE y',
889             'RECURSE',
890             'x r $RECURSE r y',
891
892             '$LIST',
893             'LIST',
894             'a b c',
895
896             ['$LIST'],
897             'LIST',
898             ['a', 'b', 'c'],
899
900             ['x', '$LIST', 'y'],
901             'LIST',
902             ['x', 'a', 'b', 'c', 'y'],
903
904             ['x', 'x $LIST y', 'y'],
905             'LIST',
906             ['x', 'x a b c y', 'y'],
907
908             ['x', 'x $CCFLAGS y', 'y'],
909             'LIST',
910             ['x', 'x $CCFLAGS y', 'y'],
911
912             ['x', 'x $RECURSE y', 'y'],
913             'LIST',
914             ['x', 'x $RECURSE y', 'y'],
915         ]
916
917         failed = 0
918         while cases:
919             input, key, expect = cases[:3]
920             result = scons_subst_once(input, env, key)
921             if result != expect:
922                 if failed == 0: print
923                 print "    input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect))
924                 failed = failed + 1
925             del cases[:3]
926         assert failed == 0, "%d subst() cases failed" % failed
927
928     def test_splitext(self):
929         assert splitext('foo') == ('foo','')
930         assert splitext('foo.bar') == ('foo','.bar')
931         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
932
933     def test_quote_spaces(self):
934         """Testing the quote_spaces() method..."""
935         q = quote_spaces('x')
936         assert q == 'x', q
937
938         q = quote_spaces('x x')
939         assert q == '"x x"', q
940
941         q = quote_spaces('x\tx')
942         assert q == '"x\tx"', q
943
944     def test_render_tree(self):
945         class Node:
946             def __init__(self, name, children=[]):
947                 self.children = children
948                 self.name = name
949             def __str__(self):
950                 return self.name
951
952         def get_children(node):
953             return node.children
954
955         windows_h = Node("windows.h")
956         stdlib_h = Node("stdlib.h")
957         stdio_h = Node("stdio.h")
958         bar_c = Node("bar.c", [stdlib_h, windows_h])
959         bar_o = Node("bar.o", [bar_c])
960         foo_c = Node("foo.c", [stdio_h])
961         foo_o = Node("foo.o", [foo_c])
962         foo = Node("foo", [foo_o, bar_o])
963
964         expect = """\
965 +-foo
966   +-foo.o
967   | +-foo.c
968   |   +-stdio.h
969   +-bar.o
970     +-bar.c
971       +-stdlib.h
972       +-windows.h
973 """
974
975         actual = render_tree(foo, get_children)
976         assert expect == actual, (expect, actual)
977
978         bar_h = Node('bar.h', [stdlib_h])
979         blat_h = Node('blat.h', [stdlib_h])
980         blat_c = Node('blat.c', [blat_h, bar_h])
981         blat_o = Node('blat.o', [blat_c])
982
983         expect = """\
984 +-blat.o
985   +-blat.c
986     +-blat.h
987     | +-stdlib.h
988     +-bar.h
989 """
990
991         actual = render_tree(blat_o, get_children, 1)
992         assert expect == actual, (expect, actual)
993
994     def test_is_Dict(self):
995         assert is_Dict({})
996         assert is_Dict(UserDict())
997         assert not is_Dict([])
998         assert not is_Dict("")
999         if hasattr(types, 'UnicodeType'):
1000             exec "assert not is_Dict(u'')"
1001
1002     def test_is_List(self):
1003         assert is_List([])
1004         import UserList
1005         assert is_List(UserList.UserList())
1006         assert not is_List({})
1007         assert not is_List("")
1008         if hasattr(types, 'UnicodeType'):
1009             exec "assert not is_List(u'')"
1010
1011     def test_is_String(self):
1012         assert is_String("")
1013         if hasattr(types, 'UnicodeType'):
1014             exec "assert is_String(u'')"
1015         try:
1016             import UserString
1017         except:
1018             pass
1019         else:
1020             assert is_String(UserString.UserString(''))
1021         assert not is_String({})
1022         assert not is_String([])
1023
1024     def test_to_String(self):
1025         """Test the to_String() method."""
1026         assert to_String(1) == "1", to_String(1)
1027         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
1028         assert to_String("foo") == "foo", to_String("foo")
1029
1030         try:
1031             import UserString
1032
1033             s1=UserString.UserString('blah')
1034             assert to_String(s1) == s1, s1
1035             assert to_String(s1) == 'blah', s1
1036
1037             class Derived(UserString.UserString):
1038                 pass
1039             s2 = Derived('foo')
1040             assert to_String(s2) == s2, s2
1041             assert to_String(s2) == 'foo', s2
1042
1043             if hasattr(types, 'UnicodeType'):
1044                 s3=UserString.UserString(unicode('bar'))
1045                 assert to_String(s3) == s3, s3
1046                 assert to_String(s3) == unicode('bar'), s3
1047                 assert type(to_String(s3)) is types.UnicodeType, \
1048                        type(to_String(s3))
1049         except ImportError:
1050             pass
1051
1052         if hasattr(types, 'UnicodeType'):
1053             s4 = unicode('baz')
1054             assert to_String(s4) == unicode('baz'), to_String(s4)
1055             assert type(to_String(s4)) is types.UnicodeType, \
1056                    type(to_String(s4))
1057
1058     def test_WhereIs(self):
1059         test = TestCmd.TestCmd(workdir = '')
1060
1061         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
1062         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
1063         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
1064         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
1065
1066         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
1067
1068         if sys.platform != 'win32':
1069             test.write(sub1_xxx_exe, "\n")
1070
1071         os.mkdir(sub2_xxx_exe)
1072
1073         test.write(sub3_xxx_exe, "\n")
1074         os.chmod(sub3_xxx_exe, 0777)
1075
1076         test.write(sub4_xxx_exe, "\n")
1077         os.chmod(sub4_xxx_exe, 0777)
1078
1079         env_path = os.environ['PATH']
1080
1081         try:
1082             pathdirs_1234 = [ test.workpath('sub1'),
1083                               test.workpath('sub2'),
1084                               test.workpath('sub3'),
1085                               test.workpath('sub4'),
1086                             ] + string.split(env_path, os.pathsep)
1087
1088             pathdirs_1243 = [ test.workpath('sub1'),
1089                               test.workpath('sub2'),
1090                               test.workpath('sub4'),
1091                               test.workpath('sub3'),
1092                             ] + string.split(env_path, os.pathsep)
1093
1094             os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
1095             wi = WhereIs('xxx.exe')
1096             assert wi == test.workpath(sub3_xxx_exe), wi
1097             wi = WhereIs('xxx.exe', pathdirs_1243)
1098             assert wi == test.workpath(sub4_xxx_exe), wi
1099             wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
1100             assert wi == test.workpath(sub4_xxx_exe), wi
1101
1102             wi = WhereIs('xxx.exe',reject = sub3_xxx_exe)
1103             assert wi == test.workpath(sub4_xxx_exe), wi
1104             wi = WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
1105             assert wi == test.workpath(sub4_xxx_exe), wi
1106
1107             os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
1108             wi = WhereIs('xxx.exe')
1109             assert wi == test.workpath(sub4_xxx_exe), wi
1110             wi = WhereIs('xxx.exe', pathdirs_1234)
1111             assert wi == test.workpath(sub3_xxx_exe), wi
1112             wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
1113             assert wi == test.workpath(sub3_xxx_exe), wi
1114
1115             if sys.platform == 'win32':
1116                 wi = WhereIs('xxx', pathext = '')
1117                 assert wi is None, wi
1118
1119                 wi = WhereIs('xxx', pathext = '.exe')
1120                 assert wi == test.workpath(sub4_xxx_exe), wi
1121
1122                 wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
1123                 assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
1124
1125                 # Test that we return a normalized path even when
1126                 # the path contains forward slashes.
1127                 forward_slash = test.workpath('') + '/sub3'
1128                 wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
1129                 assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
1130
1131             del os.environ['PATH']
1132             wi = WhereIs('xxx.exe')
1133             assert wi is None, wi
1134
1135         finally:
1136             os.environ['PATH'] = env_path
1137             
1138
1139     def test_is_valid_construction_var(self):
1140         """Testing is_valid_construction_var()"""
1141         r = is_valid_construction_var("_a")
1142         assert not r is None, r
1143         r = is_valid_construction_var("z_")
1144         assert not r is None, r
1145         r = is_valid_construction_var("X_")
1146         assert not r is None, r
1147         r = is_valid_construction_var("2a")
1148         assert r is None, r
1149         r = is_valid_construction_var("a2_")
1150         assert not r is None, r
1151         r = is_valid_construction_var("/")
1152         assert r is None, r
1153         r = is_valid_construction_var("_/")
1154         assert r is None, r
1155         r = is_valid_construction_var("a/")
1156         assert r is None, r
1157         r = is_valid_construction_var(".b")
1158         assert r is None, r
1159         r = is_valid_construction_var("_.b")
1160         assert r is None, r
1161         r = is_valid_construction_var("b1._")
1162         assert r is None, r
1163         r = is_valid_construction_var("-b")
1164         assert r is None, r
1165         r = is_valid_construction_var("_-b")
1166         assert r is None, r
1167         r = is_valid_construction_var("b1-_")
1168         assert r is None, r
1169
1170     def test_get_env_var(self):
1171         """Testing get_environment_var()."""
1172         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
1173         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
1174         assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
1175         assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
1176         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
1177         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
1178         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
1179         assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
1180         assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
1181
1182     def test_Proxy(self):
1183         """Test generic Proxy class."""
1184         class Subject:
1185             def foo(self):
1186                 return 1
1187             def bar(self):
1188                 return 2
1189
1190         s=Subject()
1191         s.baz = 3
1192
1193         class ProxyTest(Proxy):
1194             def bar(self):
1195                 return 4
1196
1197         p=ProxyTest(s)
1198
1199         assert p.foo() == 1, p.foo()
1200         assert p.bar() == 4, p.bar()
1201         assert p.baz == 3, p.baz
1202
1203         p.baz = 5
1204         s.baz = 6
1205
1206         assert p.baz == 5, p.baz
1207         assert p.get() == s, p.get()
1208
1209     def test_Literal(self):
1210         """Test the Literal() function."""
1211         input_list = [ '$FOO', Literal('$BAR') ]
1212         dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
1213
1214         def escape_func(cmd):
1215             return '**' + cmd + '**'
1216
1217         cmd_list = scons_subst_list(input_list, dummy_env)
1218         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1219         assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1220
1221     def test_SpecialAttrWrapper(self):
1222         """Test the SpecialAttrWrapper() function."""
1223         input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
1224         dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
1225
1226         def escape_func(cmd):
1227             return '**' + cmd + '**'
1228
1229         cmd_list = scons_subst_list(input_list, dummy_env)
1230         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1231         assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1232
1233         cmd_list = scons_subst_list(input_list, dummy_env, mode=SUBST_SIG)
1234         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1235         assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
1236
1237     def test_display(self):
1238         old_stdout = sys.stdout
1239         sys.stdout = OutBuffer()
1240         display("line1")
1241         display.set_mode(0)
1242         display("line2")
1243         display.set_mode(1)
1244         display("line3")
1245         display("line4\n", append_newline=0)
1246         display.set_mode(0)
1247         display("dont print1")
1248         display("dont print2\n", append_newline=0)
1249         display.set_mode(1)
1250         assert sys.stdout.buffer == "line1\nline3\nline4\n"
1251         sys.stdout = old_stdout
1252
1253     def test_fs_delete(self):
1254         test = TestCmd.TestCmd(workdir = '')
1255         base = test.workpath('')
1256         xxx = test.workpath('xxx.xxx')
1257         ZZZ = test.workpath('ZZZ.ZZZ')
1258         sub1_yyy = test.workpath('sub1', 'yyy.yyy')
1259
1260         test.subdir('sub1')
1261         test.write(xxx, "\n")
1262         test.write(ZZZ, "\n")
1263         test.write(sub1_yyy, "\n")
1264
1265         old_stdout = sys.stdout
1266         sys.stdout = OutBuffer()
1267
1268         exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
1269               "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
1270               "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
1271               "Removed " + os.path.join(base, xxx) + '\n' + \
1272               "Removed directory " + base + '\n'
1273
1274         fs_delete(base, remove=0)
1275         assert sys.stdout.buffer == exp, sys.stdout.buffer
1276         assert os.path.exists(sub1_yyy)
1277
1278         sys.stdout.buffer = ""
1279         fs_delete(base, remove=1)
1280         assert sys.stdout.buffer == exp
1281         assert not os.path.exists(base)
1282
1283         test._dirlist = None
1284         sys.stdout = old_stdout
1285
1286     def test_get_native_path(self):
1287         """Test the get_native_path() function."""
1288         import tempfile
1289         filename = tempfile.mktemp()
1290         str = '1234567890 ' + filename
1291         try:
1292             open(filename, 'w').write(str)
1293             assert open(get_native_path(filename)).read() == str
1294         finally:
1295             try:
1296                 os.unlink(filename)
1297             except OSError:
1298                 pass
1299
1300     def test_subst_dict(self):
1301         """Test substituting dictionary values in an Action
1302         """
1303         t = DummyNode('t')
1304         s = DummyNode('s')
1305         d = subst_dict(target=t, source=s)
1306         assert str(d['TARGETS'][0]) == 't', d['TARGETS']
1307         assert str(d['TARGET']) == 't', d['TARGET']
1308         assert str(d['SOURCES'][0]) == 's', d['SOURCES']
1309         assert str(d['SOURCE']) == 's', d['SOURCE']
1310
1311         t1 = DummyNode('t1')
1312         t2 = DummyNode('t2')
1313         s1 = DummyNode('s1')
1314         s2 = DummyNode('s2')
1315         d = subst_dict(target=[t1, t2], source=[s1, s2])
1316         TARGETS = map(lambda x: str(x), d['TARGETS'])
1317         TARGETS.sort()
1318         assert TARGETS == ['t1', 't2'], d['TARGETS']
1319         assert str(d['TARGET']) == 't1', d['TARGET']
1320         SOURCES = map(lambda x: str(x), d['SOURCES'])
1321         SOURCES.sort()
1322         assert SOURCES == ['s1', 's2'], d['SOURCES']
1323         assert str(d['SOURCE']) == 's1', d['SOURCE']
1324
1325         class N:
1326             def __init__(self, name):
1327                 self.name = name
1328             def __str__(self):
1329                 return self.name
1330             def rfile(self):
1331                 return self.__class__('rstr-' + self.name)
1332             def get_subst_proxy(self):
1333                 return self
1334
1335         t3 = N('t3')
1336         t4 = DummyNode('t4')
1337         s3 = DummyNode('s3')
1338         s4 = N('s4')
1339         d = subst_dict(target=[t3, t4], source=[s3, s4])
1340         TARGETS = map(lambda x: str(x), d['TARGETS'])
1341         TARGETS.sort()
1342         assert TARGETS == ['t3', 't4'], d['TARGETS']
1343         SOURCES = map(lambda x: str(x), d['SOURCES'])
1344         SOURCES.sort()
1345         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
1346
1347     def test_PrependPath(self):
1348         """Test prepending to a path"""
1349         p1 = r'C:\dir\num\one;C:\dir\num\two'
1350         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
1351         # have to include the pathsep here so that the test will work on UNIX too.
1352         p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';')
1353         p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
1354         p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
1355         p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
1356         assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
1357         assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
1358
1359     def test_AppendPath(self):
1360         """Test appending to a path."""
1361         p1 = r'C:\dir\num\one;C:\dir\num\two'
1362         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
1363         # have to include the pathsep here so that the test will work on UNIX too.
1364         p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';')
1365         p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
1366         p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
1367         p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
1368         assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
1369         assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
1370
1371     def test_NodeList(self):
1372         """Test NodeList class"""
1373         class TestClass:
1374             def __init__(self, name, child=None):
1375                 self.child = child
1376                 self.bar = name
1377             def foo(self):
1378                 return self.bar + "foo"
1379             def getself(self):
1380                 return self
1381
1382         t1 = TestClass('t1', TestClass('t1child'))
1383         t2 = TestClass('t2', TestClass('t2child'))
1384         t3 = TestClass('t3')
1385
1386         nl = NodeList([t1, t2, t3])
1387         assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
1388         assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
1389         assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
1390         assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
1391                nl[0:2].child.foo()
1392         assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
1393                nl[0:2].child.bar
1394
1395     def test_CLVar(self):
1396         """Test the command-line construction variable class"""
1397         f = SCons.Util.CLVar('a b')
1398
1399         r = f + 'c d'
1400         assert isinstance(r, SCons.Util.CLVar), type(r)
1401         assert r.data == ['a', 'b', 'c', 'd'], r.data
1402         assert str(r) == 'a b c d', str(r)
1403
1404         r = f + ' c d'
1405         assert isinstance(r, SCons.Util.CLVar), type(r)
1406         assert r.data == ['a', 'b', 'c', 'd'], r.data
1407         assert str(r) == 'a b c d', str(r)
1408
1409         r = f + ['c d']
1410         assert isinstance(r, SCons.Util.CLVar), type(r)
1411         assert r.data == ['a', 'b', 'c d'], r.data
1412         assert str(r) == 'a b c d', str(r)
1413
1414         r = f + [' c d']
1415         assert isinstance(r, SCons.Util.CLVar), type(r)
1416         assert r.data == ['a', 'b', ' c d'], r.data
1417         assert str(r) == 'a b  c d', str(r)
1418
1419         r = f + ['c', 'd']
1420         assert isinstance(r, SCons.Util.CLVar), type(r)
1421         assert r.data == ['a', 'b', 'c', 'd'], r.data
1422         assert str(r) == 'a b c d', str(r)
1423
1424         r = f + [' c', 'd']
1425         assert isinstance(r, SCons.Util.CLVar), type(r)
1426         assert r.data == ['a', 'b', ' c', 'd'], r.data
1427         assert str(r) == 'a b  c d', str(r)
1428
1429         f = SCons.Util.CLVar(['a b'])
1430
1431         r = f + 'c d'
1432         assert isinstance(r, SCons.Util.CLVar), type(r)
1433         assert r.data == ['a b', 'c', 'd'], r.data
1434         assert str(r) == 'a b c d', str(r)
1435
1436         r = f + ' c d'
1437         assert isinstance(r, SCons.Util.CLVar), type(r)
1438         assert r.data == ['a b', 'c', 'd'], r.data
1439         assert str(r) == 'a b c d', str(r)
1440
1441         r = f + ['c d']
1442         assert isinstance(r, SCons.Util.CLVar), type(r)
1443         assert r.data == ['a b', 'c d'], r.data
1444         assert str(r) == 'a b c d', str(r)
1445
1446         r = f + [' c d']
1447         assert isinstance(r, SCons.Util.CLVar), type(r)
1448         assert r.data == ['a b', ' c d'], r.data
1449         assert str(r) == 'a b  c d', str(r)
1450
1451         r = f + ['c', 'd']
1452         assert isinstance(r, SCons.Util.CLVar), type(r)
1453         assert r.data == ['a b', 'c', 'd'], r.data
1454         assert str(r) == 'a b c d', str(r)
1455
1456         r = f + [' c', 'd']
1457         assert isinstance(r, SCons.Util.CLVar), type(r)
1458         assert r.data == ['a b', ' c', 'd'], r.data
1459         assert str(r) == 'a b  c d', str(r)
1460
1461         f = SCons.Util.CLVar(['a', 'b'])
1462
1463         r = f + 'c d'
1464         assert isinstance(r, SCons.Util.CLVar), type(r)
1465         assert r.data == ['a', 'b', 'c', 'd'], r.data
1466         assert str(r) == 'a b c d', str(r)
1467
1468         r = f + ' c d'
1469         assert isinstance(r, SCons.Util.CLVar), type(r)
1470         assert r.data == ['a', 'b', 'c', 'd'], r.data
1471         assert str(r) == 'a b c d', str(r)
1472
1473         r = f + ['c d']
1474         assert isinstance(r, SCons.Util.CLVar), type(r)
1475         assert r.data == ['a', 'b', 'c d'], r.data
1476         assert str(r) == 'a b c d', str(r)
1477
1478         r = f + [' c d']
1479         assert isinstance(r, SCons.Util.CLVar), type(r)
1480         assert r.data == ['a', 'b', ' c d'], r.data
1481         assert str(r) == 'a b  c d', str(r)
1482
1483         r = f + ['c', 'd']
1484         assert isinstance(r, SCons.Util.CLVar), type(r)
1485         assert r.data == ['a', 'b', 'c', 'd'], r.data
1486         assert str(r) == 'a b c d', str(r)
1487
1488         r = f + [' c', 'd']
1489         assert isinstance(r, SCons.Util.CLVar), type(r)
1490         assert r.data == ['a', 'b', ' c', 'd'], r.data
1491         assert str(r) == 'a b  c d', str(r)
1492
1493         loc = {}
1494         loc['FOO'] = 'foo'
1495         loc['BAR'] = SCons.Util.CLVar('bar')
1496         loc['CALL'] = lambda target, source, env, for_signature: 'call'
1497         env = DummyEnv(loc)
1498
1499         cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
1500
1501         newcmd = scons_subst(cmd, env)
1502         assert newcmd == 'test foo bar call test', newcmd
1503
1504         cmd_list = scons_subst_list(cmd, env)
1505         assert len(cmd_list) == 1, cmd_list
1506         assert cmd_list[0][0] == "test", cmd_list[0][0]
1507         assert cmd_list[0][1] == "foo", cmd_list[0][1]
1508         assert cmd_list[0][2] == "bar", cmd_list[0][2]
1509         assert cmd_list[0][3] == "call", cmd_list[0][3]
1510         assert cmd_list[0][4] == "test", cmd_list[0][4]
1511
1512     def test_Selector(self):
1513         """Test the Selector class"""
1514
1515         s = Selector({'a' : 'AAA', 'b' : 'BBB'})
1516         assert s['a'] == 'AAA', s['a']
1517         assert s['b'] == 'BBB', s['b']
1518         exc_caught = None
1519         try:
1520             x = s['c']
1521         except KeyError:
1522             exc_caught = 1
1523         assert exc_caught, "should have caught a KeyError"
1524         s['c'] = 'CCC'
1525         assert s['c'] == 'CCC', s['c']
1526
1527         class DummyEnv(UserDict):
1528             def subst(self, key):
1529                 if key[0] == '$':
1530                     return self[key[1:]]
1531                 return key
1532
1533         env = DummyEnv()
1534
1535         s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
1536         ret = s(env, [])
1537         assert ret == None, ret
1538         ret = s(env, ['foo.d'])
1539         assert ret == 'DDD', ret
1540         ret = s(env, ['bar.e'])
1541         assert ret == 'EEE', ret
1542         ret = s(env, ['bar.x'])
1543         assert ret == None, ret
1544         s[None] = 'XXX'
1545         ret = s(env, ['bar.x'])
1546         assert ret == 'XXX', ret
1547
1548         env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'})
1549
1550         s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'})
1551         ret = s(env, ['foo.f'])
1552         assert ret == 'FFF', ret
1553         ret = s(env, ['bar.g'])
1554         assert ret == 'GGG', ret
1555
1556     def test_adjustixes(self):
1557         """Test the adjustixes() function"""
1558         r = adjustixes('file', 'pre-', '-suf')
1559         assert r == 'pre-file-suf', r
1560         r = adjustixes('pre-file', 'pre-', '-suf')
1561         assert r == 'pre-file-suf', r
1562         r = adjustixes('file-suf', 'pre-', '-suf')
1563         assert r == 'pre-file-suf', r
1564         r = adjustixes('pre-file-suf', 'pre-', '-suf')
1565         assert r == 'pre-file-suf', r
1566         r = adjustixes('pre-file.xxx', 'pre-', '-suf')
1567         assert r == 'pre-file.xxx', r
1568         r = adjustixes('dir/file', 'pre-', '-suf')
1569         assert r == os.path.join('dir', 'pre-file-suf'), r
1570
1571     def test_containsAny(self):
1572         """Test the containsAny() function"""
1573         assert containsAny('*.py', '*?[]')
1574         assert not containsAny('file.txt', '*?[]')
1575
1576     def test_containsAll(self):
1577         """Test the containsAll() function"""
1578         assert containsAll('43221', '123')
1579         assert not containsAll('134', '123')
1580
1581     def test_containsOnly(self):
1582         """Test the containsOnly() function"""
1583         assert containsOnly('.83', '0123456789.')
1584         assert not containsOnly('43221', '123')
1585
1586     def test_LogicalLines(self):
1587         """Test the LogicalLines class"""
1588         import StringIO
1589
1590         fobj = StringIO.StringIO(r"""
1591 foo \
1592 bar \
1593 baz
1594 foo
1595 bling \
1596 bling \ bling
1597 bling
1598 """)
1599
1600         lines = LogicalLines(fobj).readlines()
1601         assert lines == [
1602             '\n',
1603             'foo bar baz\n',
1604             'foo\n',
1605             'bling bling \\ bling\n',
1606             'bling\n',
1607         ], lines
1608
1609 if __name__ == "__main__":
1610     suite = unittest.makeSuite(UtilTestCase, 'test_')
1611     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1612         sys.exit(1)