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