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