Don't blow up if the external PATH variable is not set.
[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
657         kwargs = {'target' : target, 'source' : source}
658
659         failed = 0
660         while cases:
661             input, expect = cases[:2]
662             expect = map(lambda l: map(cvt, l), expect)
663             result = apply(scons_subst_list, (input, env), kwargs)
664             if result != expect:
665                 if failed == 0: print
666                 print "    input %s => %s did not match %s" % (repr(input), result, repr(expect))
667                 failed = failed + 1
668             del cases[:2]
669         assert failed == 0, "%d subst_list() cases failed" % failed
670
671         t1 = MyNode('t1')
672         t2 = MyNode('t2')
673         s1 = MyNode('s1')
674         s2 = MyNode('s2')
675         result = scons_subst_list("$TARGET $SOURCES", env,
676                                   target=[t1, t2],
677                                   source=[s1, s2])
678         assert result == [['t1', 's1', 's2']], result
679         result = scons_subst_list("$TARGET $SOURCES", env,
680                                   target=[t1, t2],
681                                   source=[s1, s2],
682                                   dict={})
683         assert result == [[]], result
684
685         # Test interpolating a callable.
686         _t = DummyNode('t')
687         _s = DummyNode('s')
688         cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
689                                     env, target=_t, source=_s)
690         assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
691
692         # Test escape functionality.
693         def escape_func(foo):
694             return '**' + foo + '**'
695         cmd_list = scons_subst_list("abc $LITERALS xyz", env)
696         assert cmd_list == [['abc',
697                              'foo\nwith\nnewlines',
698                              'bar\nwith\nnewlines',
699                              'xyz']], cmd_list
700         c = cmd_list[0][0].escape(escape_func)
701         assert c == 'abc', c
702         c = cmd_list[0][1].escape(escape_func)
703         assert c == '**foo\nwith\nnewlines**', c
704         c = cmd_list[0][2].escape(escape_func)
705         assert c == '**bar\nwith\nnewlines**', c
706         c = cmd_list[0][3].escape(escape_func)
707         assert c == 'xyz', c
708
709         cmd_list = scons_subst_list("abc${LITERALS}xyz", env)
710         c = cmd_list[0][0].escape(escape_func)
711         assert c == '**abcfoo\nwith\nnewlines**', c
712         c = cmd_list[0][1].escape(escape_func)
713         assert c == '**bar\nwith\nnewlinesxyz**', c
714
715         # Tests of the various SUBST_* modes of substitution.
716         subst_list_cases = [
717             "test $xxx",
718                 [["test"]],
719                 [["test"]],
720                 [["test"]],
721
722             "test $($xxx$)",
723                 [["test", "$($)"]],
724                 [["test"]],
725                 [["test"]],
726
727             "test $( $xxx $)",
728                 [["test", "$(", "$)"]],
729                 [["test"]],
730                 [["test"]],
731
732             "$AAA ${AAA}A $BBBB $BBB",
733                 [["a", "aA", "b"]],
734                 [["a", "aA", "b"]],
735                 [["a", "aA", "b"]],
736
737             "$RECURSE",
738                 [["foo", "bar"]],
739                 [["foo", "bar"]],
740                 [["foo", "bar"]],
741
742             "$RRR",
743                 [["foo", "bar"]],
744                 [["foo", "bar"]],
745                 [["foo", "bar"]],
746
747             # Verify what happens with no target or source nodes.
748             "$TARGET $SOURCES",
749                 [[]],
750                 [[]],
751                 [[]],
752
753             "$TARGETS $SOURCE",
754                 [[]],
755                 [[]],
756                 [[]],
757
758             # Various test refactored from ActionTests.py
759             "${LIST}",
760                 [['This', 'is', '$(', '$)', 'test']],
761                 [['This', 'is', 'test']],
762                 [['This', 'is', 'test']],
763
764             ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
765                 [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]],
766                 [["|", "a", "|", "b", "|", "c", "1"]],
767                 [["|", "|", "c", "1"]],
768         ]
769
770         r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW)
771         assert r == [[]], r
772
773         failed = 0
774         while subst_list_cases:
775             input, eraw, ecmd, esig = subst_list_cases[:4]
776             result = scons_subst_list(input, env, mode=SUBST_RAW)
777             if result != eraw:
778                 if failed == 0: print
779                 print "    input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
780                 failed = failed + 1
781             result = scons_subst_list(input, env, mode=SUBST_CMD)
782             if result != ecmd:
783                 if failed == 0: print
784                 print "    input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
785                 failed = failed + 1
786             result = scons_subst_list(input, env, mode=SUBST_SIG)
787             if result != esig:
788                 if failed == 0: print
789                 print "    input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
790                 failed = failed + 1
791             del subst_list_cases[:4]
792         assert failed == 0, "%d subst() mode cases failed" % failed
793
794         # Test that we handle syntax errors during expansion as expected.
795         try:
796             scons_subst_list('$foo.bar.3.0', env)
797         except SCons.Errors.UserError, e:
798             expect1 = "Syntax error `invalid syntax' trying to evaluate `$foo.bar.3.0'"
799             expect2 = "Syntax error `invalid syntax (line 1)' trying to evaluate `$foo.bar.3.0'"
800             assert str(e) in [expect1, expect2], e
801         else:
802             raise AssertionError, "did not catch expected SyntaxError"
803
804         # Test we handle overriding the internal conversion routines.
805         def s(obj):
806             return obj
807
808         n1 = MyNode('n1')
809         env = DummyEnv({'NODE' : n1})
810         node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s)
811         assert node == [[n1]], node
812         node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s)
813         assert node == [[n1]], node
814         node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s)
815         assert node == [[n1]], node
816
817         # Test supplying an overriding gvars dictionary.
818         env = DummyEnv({'XXX' : 'xxx'})
819         result = scons_subst_list('$XXX', env)
820         assert result == [['xxx']], result
821         result = scons_subst_list('$XXX', env, gvars={'XXX' : 'yyy'})
822         assert result == [['yyy']], result
823
824     def test_subst_once(self):
825         """Testing the scons_subst_once() method"""
826
827         loc = {
828             'CCFLAGS'           : '-DFOO',
829             'ONE'               : 1,
830             'RECURSE'           : 'r $RECURSE r',
831             'LIST'              : ['a', 'b', 'c'],
832         }
833
834         env = DummyEnv(loc)
835
836         cases = [
837             '$CCFLAGS -DBAR',
838             'OTHER_KEY',
839             '$CCFLAGS -DBAR',
840
841             '$CCFLAGS -DBAR',
842             'CCFLAGS',
843             '-DFOO -DBAR',
844
845             'x $ONE y',
846             'ONE',
847             'x 1 y',
848
849             'x $RECURSE y',
850             'RECURSE',
851             'x r $RECURSE r y',
852
853             '$LIST',
854             'LIST',
855             'a b c',
856
857             ['$LIST'],
858             'LIST',
859             ['a', 'b', 'c'],
860
861             ['x', '$LIST', 'y'],
862             'LIST',
863             ['x', 'a', 'b', 'c', 'y'],
864
865             ['x', 'x $LIST y', 'y'],
866             'LIST',
867             ['x', 'x a b c y', 'y'],
868
869             ['x', 'x $CCFLAGS y', 'y'],
870             'LIST',
871             ['x', 'x $CCFLAGS y', 'y'],
872
873             ['x', 'x $RECURSE y', 'y'],
874             'LIST',
875             ['x', 'x $RECURSE y', 'y'],
876         ]
877
878         failed = 0
879         while cases:
880             input, key, expect = cases[:3]
881             result = scons_subst_once(input, env, key)
882             if result != expect:
883                 if failed == 0: print
884                 print "    input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect))
885                 failed = failed + 1
886             del cases[:3]
887         assert failed == 0, "%d subst() cases failed" % failed
888
889     def test_splitext(self):
890         assert splitext('foo') == ('foo','')
891         assert splitext('foo.bar') == ('foo','.bar')
892         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
893
894     def test_quote_spaces(self):
895         """Testing the quote_spaces() method..."""
896         q = quote_spaces('x')
897         assert q == 'x', q
898
899         q = quote_spaces('x x')
900         assert q == '"x x"', q
901
902         q = quote_spaces('x\tx')
903         assert q == '"x\tx"', q
904
905     def test_render_tree(self):
906         class Node:
907             def __init__(self, name, children=[]):
908                 self.children = children
909                 self.name = name
910             def __str__(self):
911                 return self.name
912
913         def get_children(node):
914             return node.children
915
916         windows_h = Node("windows.h")
917         stdlib_h = Node("stdlib.h")
918         stdio_h = Node("stdio.h")
919         bar_c = Node("bar.c", [stdlib_h, windows_h])
920         bar_o = Node("bar.o", [bar_c])
921         foo_c = Node("foo.c", [stdio_h])
922         foo_o = Node("foo.o", [foo_c])
923         foo = Node("foo", [foo_o, bar_o])
924
925         expect = """\
926 +-foo
927   +-foo.o
928   | +-foo.c
929   |   +-stdio.h
930   +-bar.o
931     +-bar.c
932       +-stdlib.h
933       +-windows.h
934 """
935
936         actual = render_tree(foo, get_children)
937         assert expect == actual, (expect, actual)
938
939         bar_h = Node('bar.h', [stdlib_h])
940         blat_h = Node('blat.h', [stdlib_h])
941         blat_c = Node('blat.c', [blat_h, bar_h])
942         blat_o = Node('blat.o', [blat_c])
943
944         expect = """\
945 +-blat.o
946   +-blat.c
947     +-blat.h
948     | +-stdlib.h
949     +-bar.h
950 """
951
952         actual = render_tree(blat_o, get_children, 1)
953         assert expect == actual, (expect, actual)
954
955     def test_is_Dict(self):
956         assert is_Dict({})
957         import UserDict
958         assert is_Dict(UserDict.UserDict())
959         assert not is_Dict([])
960         assert not is_Dict("")
961         if hasattr(types, 'UnicodeType'):
962             exec "assert not is_Dict(u'')"
963
964     def test_is_List(self):
965         assert is_List([])
966         import UserList
967         assert is_List(UserList.UserList())
968         assert not is_List({})
969         assert not is_List("")
970         if hasattr(types, 'UnicodeType'):
971             exec "assert not is_List(u'')"
972
973     def test_is_String(self):
974         assert is_String("")
975         if hasattr(types, 'UnicodeType'):
976             exec "assert is_String(u'')"
977         try:
978             import UserString
979         except:
980             pass
981         else:
982             assert is_String(UserString.UserString(''))
983         assert not is_String({})
984         assert not is_String([])
985
986     def test_to_String(self):
987         """Test the to_String() method."""
988         assert to_String(1) == "1", to_String(1)
989         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
990         assert to_String("foo") == "foo", to_String("foo")
991
992         try:
993             import UserString
994
995             s1=UserString.UserString('blah')
996             assert to_String(s1) == s1, s1
997             assert to_String(s1) == 'blah', s1
998
999             class Derived(UserString.UserString):
1000                 pass
1001             s2 = Derived('foo')
1002             assert to_String(s2) == s2, s2
1003             assert to_String(s2) == 'foo', s2
1004
1005             if hasattr(types, 'UnicodeType'):
1006                 s3=UserString.UserString(unicode('bar'))
1007                 assert to_String(s3) == s3, s3
1008                 assert to_String(s3) == unicode('bar'), s3
1009                 assert type(to_String(s3)) is types.UnicodeType, \
1010                        type(to_String(s3))
1011         except ImportError:
1012             pass
1013
1014         if hasattr(types, 'UnicodeType'):
1015             s4 = unicode('baz')
1016             assert to_String(s4) == unicode('baz'), to_String(s4)
1017             assert type(to_String(s4)) is types.UnicodeType, \
1018                    type(to_String(s4))
1019
1020     def test_WhereIs(self):
1021         test = TestCmd.TestCmd(workdir = '')
1022
1023         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
1024         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
1025         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
1026         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
1027
1028         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
1029
1030         if sys.platform != 'win32':
1031             test.write(sub1_xxx_exe, "\n")
1032
1033         os.mkdir(sub2_xxx_exe)
1034
1035         test.write(sub3_xxx_exe, "\n")
1036         os.chmod(sub3_xxx_exe, 0777)
1037
1038         test.write(sub4_xxx_exe, "\n")
1039         os.chmod(sub4_xxx_exe, 0777)
1040
1041         env_path = os.environ['PATH']
1042
1043         try:
1044             pathdirs_1234 = [ test.workpath('sub1'),
1045                               test.workpath('sub2'),
1046                               test.workpath('sub3'),
1047                               test.workpath('sub4'),
1048                             ] + string.split(env_path, os.pathsep)
1049
1050             pathdirs_1243 = [ test.workpath('sub1'),
1051                               test.workpath('sub2'),
1052                               test.workpath('sub4'),
1053                               test.workpath('sub3'),
1054                             ] + string.split(env_path, os.pathsep)
1055
1056             os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
1057             wi = WhereIs('xxx.exe')
1058             assert wi == test.workpath(sub3_xxx_exe), wi
1059             wi = WhereIs('xxx.exe', pathdirs_1243)
1060             assert wi == test.workpath(sub4_xxx_exe), wi
1061             wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
1062             assert wi == test.workpath(sub4_xxx_exe), wi
1063
1064             wi = WhereIs('xxx.exe',reject = sub3_xxx_exe)
1065             assert wi == test.workpath(sub4_xxx_exe), wi
1066             wi = WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
1067             assert wi == test.workpath(sub4_xxx_exe), wi
1068
1069             os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
1070             wi = WhereIs('xxx.exe')
1071             assert wi == test.workpath(sub4_xxx_exe), wi
1072             wi = WhereIs('xxx.exe', pathdirs_1234)
1073             assert wi == test.workpath(sub3_xxx_exe), wi
1074             wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
1075             assert wi == test.workpath(sub3_xxx_exe), wi
1076
1077             if sys.platform == 'win32':
1078                 wi = WhereIs('xxx', pathext = '')
1079                 assert wi is None, wi
1080
1081                 wi = WhereIs('xxx', pathext = '.exe')
1082                 assert wi == test.workpath(sub4_xxx_exe), wi
1083
1084                 wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
1085                 assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
1086
1087                 # Test that we return a normalized path even when
1088                 # the path contains forward slashes.
1089                 forward_slash = test.workpath('') + '/sub3'
1090                 wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
1091                 assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
1092
1093             del os.environ['PATH']
1094             wi = WhereIs('xxx.exe')
1095             assert wi is None, wi
1096
1097         finally:
1098             os.environ['PATH'] = env_path
1099             
1100
1101     def test_is_valid_construction_var(self):
1102         """Testing is_valid_construction_var()"""
1103         r = is_valid_construction_var("_a")
1104         assert not r is None, r
1105         r = is_valid_construction_var("z_")
1106         assert not r is None, r
1107         r = is_valid_construction_var("X_")
1108         assert not r is None, r
1109         r = is_valid_construction_var("2a")
1110         assert r is None, r
1111         r = is_valid_construction_var("a2_")
1112         assert not r is None, r
1113         r = is_valid_construction_var("/")
1114         assert r is None, r
1115         r = is_valid_construction_var("_/")
1116         assert r is None, r
1117         r = is_valid_construction_var("a/")
1118         assert r is None, r
1119         r = is_valid_construction_var(".b")
1120         assert r is None, r
1121         r = is_valid_construction_var("_.b")
1122         assert r is None, r
1123         r = is_valid_construction_var("b1._")
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
1132     def test_get_env_var(self):
1133         """Testing get_environment_var()."""
1134         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
1135         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
1136         assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
1137         assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
1138         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
1139         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
1140         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
1141         assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
1142         assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
1143
1144     def test_Proxy(self):
1145         """Test generic Proxy class."""
1146         class Subject:
1147             def foo(self):
1148                 return 1
1149             def bar(self):
1150                 return 2
1151
1152         s=Subject()
1153         s.baz = 3
1154
1155         class ProxyTest(Proxy):
1156             def bar(self):
1157                 return 4
1158
1159         p=ProxyTest(s)
1160
1161         assert p.foo() == 1, p.foo()
1162         assert p.bar() == 4, p.bar()
1163         assert p.baz == 3, p.baz
1164
1165         p.baz = 5
1166         s.baz = 6
1167
1168         assert p.baz == 5, p.baz
1169         assert p.get() == s, p.get()
1170
1171     def test_Literal(self):
1172         """Test the Literal() function."""
1173         input_list = [ '$FOO', Literal('$BAR') ]
1174         dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
1175
1176         def escape_func(cmd):
1177             return '**' + cmd + '**'
1178
1179         cmd_list = scons_subst_list(input_list, dummy_env)
1180         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1181         assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1182
1183     def test_SpecialAttrWrapper(self):
1184         """Test the SpecialAttrWrapper() function."""
1185         input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
1186         dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
1187
1188         def escape_func(cmd):
1189             return '**' + cmd + '**'
1190
1191         cmd_list = scons_subst_list(input_list, dummy_env)
1192         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1193         assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1194
1195         cmd_list = scons_subst_list(input_list, dummy_env, mode=SUBST_SIG)
1196         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1197         assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
1198
1199     def test_display(self):
1200         old_stdout = sys.stdout
1201         sys.stdout = OutBuffer()
1202         display("line1")
1203         display.set_mode(0)
1204         display("line2")
1205         display.set_mode(1)
1206         display("line3")
1207
1208         assert sys.stdout.buffer == "line1\nline3\n"
1209         sys.stdout = old_stdout
1210
1211     def test_fs_delete(self):
1212         test = TestCmd.TestCmd(workdir = '')
1213         base = test.workpath('')
1214         xxx = test.workpath('xxx.xxx')
1215         ZZZ = test.workpath('ZZZ.ZZZ')
1216         sub1_yyy = test.workpath('sub1', 'yyy.yyy')
1217
1218         test.subdir('sub1')
1219         test.write(xxx, "\n")
1220         test.write(ZZZ, "\n")
1221         test.write(sub1_yyy, "\n")
1222
1223         old_stdout = sys.stdout
1224         sys.stdout = OutBuffer()
1225
1226         exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
1227               "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
1228               "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
1229               "Removed " + os.path.join(base, xxx) + '\n' + \
1230               "Removed directory " + base + '\n'
1231
1232         fs_delete(base, remove=0)
1233         assert sys.stdout.buffer == exp, sys.stdout.buffer
1234         assert os.path.exists(sub1_yyy)
1235
1236         sys.stdout.buffer = ""
1237         fs_delete(base, remove=1)
1238         assert sys.stdout.buffer == exp
1239         assert not os.path.exists(base)
1240
1241         test._dirlist = None
1242         sys.stdout = old_stdout
1243
1244     def test_get_native_path(self):
1245         """Test the get_native_path() function."""
1246         import tempfile
1247         filename = tempfile.mktemp()
1248         str = '1234567890 ' + filename
1249         open(filename, 'w').write(str)
1250         assert open(get_native_path(filename)).read() == str
1251
1252     def test_subst_dict(self):
1253         """Test substituting dictionary values in an Action
1254         """
1255         t = DummyNode('t')
1256         s = DummyNode('s')
1257         d = subst_dict(target=t, source=s)
1258         assert str(d['TARGETS'][0]) == 't', d['TARGETS']
1259         assert str(d['TARGET']) == 't', d['TARGET']
1260         assert str(d['SOURCES'][0]) == 's', d['SOURCES']
1261         assert str(d['SOURCE']) == 's', d['SOURCE']
1262
1263         t1 = DummyNode('t1')
1264         t2 = DummyNode('t2')
1265         s1 = DummyNode('s1')
1266         s2 = DummyNode('s2')
1267         d = subst_dict(target=[t1, t2], source=[s1, s2])
1268         TARGETS = map(lambda x: str(x), d['TARGETS'])
1269         TARGETS.sort()
1270         assert TARGETS == ['t1', 't2'], d['TARGETS']
1271         assert str(d['TARGET']) == 't1', d['TARGET']
1272         SOURCES = map(lambda x: str(x), d['SOURCES'])
1273         SOURCES.sort()
1274         assert SOURCES == ['s1', 's2'], d['SOURCES']
1275         assert str(d['SOURCE']) == 's1', d['SOURCE']
1276
1277         class N:
1278             def __init__(self, name):
1279                 self.name = name
1280             def __str__(self):
1281                 return self.name
1282             def rfile(self):
1283                 return self.__class__('rstr-' + self.name)
1284             def get_subst_proxy(self):
1285                 return self
1286
1287         t3 = N('t3')
1288         t4 = DummyNode('t4')
1289         s3 = DummyNode('s3')
1290         s4 = N('s4')
1291         d = subst_dict(target=[t3, t4], source=[s3, s4])
1292         TARGETS = map(lambda x: str(x), d['TARGETS'])
1293         TARGETS.sort()
1294         assert TARGETS == ['t3', 't4'], d['TARGETS']
1295         SOURCES = map(lambda x: str(x), d['SOURCES'])
1296         SOURCES.sort()
1297         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
1298
1299     def test_PrependPath(self):
1300         """Test prepending to a path"""
1301         p1 = r'C:\dir\num\one;C:\dir\num\two'
1302         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
1303         # have to include the pathsep here so that the test will work on UNIX too.
1304         p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';')
1305         p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
1306         p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
1307         p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
1308         assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
1309         assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
1310
1311     def test_AppendPath(self):
1312         """Test appending 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 = AppendPath(p1,r'C:\dir\num\two',sep = ';')
1317         p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
1318         p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
1319         p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
1320         assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
1321         assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
1322
1323     def test_NodeList(self):
1324         """Test NodeList class"""
1325         class TestClass:
1326             def __init__(self, name, child=None):
1327                 self.child = child
1328                 self.bar = name
1329             def foo(self):
1330                 return self.bar + "foo"
1331             def getself(self):
1332                 return self
1333
1334         t1 = TestClass('t1', TestClass('t1child'))
1335         t2 = TestClass('t2', TestClass('t2child'))
1336         t3 = TestClass('t3')
1337
1338         nl = NodeList([t1, t2, t3])
1339         assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
1340         assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
1341         assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
1342         assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
1343                nl[0:2].child.foo()
1344         assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
1345                nl[0:2].child.bar
1346
1347     def test_CLVar(self):
1348         """Test the command-line construction variable class"""
1349         f = SCons.Util.CLVar('a b')
1350
1351         r = f + 'c d'
1352         assert isinstance(r, SCons.Util.CLVar), type(r)
1353         assert r.data == ['a', 'b', 'c', 'd'], r.data
1354         assert str(r) == 'a b c d', str(r)
1355
1356         r = f + ' c d'
1357         assert isinstance(r, SCons.Util.CLVar), type(r)
1358         assert r.data == ['a', 'b', 'c', 'd'], r.data
1359         assert str(r) == 'a b c d', str(r)
1360
1361         r = f + ['c d']
1362         assert isinstance(r, SCons.Util.CLVar), type(r)
1363         assert r.data == ['a', 'b', 'c d'], r.data
1364         assert str(r) == 'a b c d', str(r)
1365
1366         r = f + [' c d']
1367         assert isinstance(r, SCons.Util.CLVar), type(r)
1368         assert r.data == ['a', 'b', ' c d'], r.data
1369         assert str(r) == 'a b  c d', str(r)
1370
1371         r = f + ['c', 'd']
1372         assert isinstance(r, SCons.Util.CLVar), type(r)
1373         assert r.data == ['a', 'b', 'c', 'd'], r.data
1374         assert str(r) == 'a b c d', str(r)
1375
1376         r = f + [' c', 'd']
1377         assert isinstance(r, SCons.Util.CLVar), type(r)
1378         assert r.data == ['a', 'b', ' c', 'd'], r.data
1379         assert str(r) == 'a b  c d', str(r)
1380
1381         f = SCons.Util.CLVar(['a b'])
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         r = f + ['c d']
1394         assert isinstance(r, SCons.Util.CLVar), type(r)
1395         assert r.data == ['a b', 'c d'], r.data
1396         assert str(r) == 'a b c d', str(r)
1397
1398         r = f + [' c d']
1399         assert isinstance(r, SCons.Util.CLVar), type(r)
1400         assert r.data == ['a b', ' c d'], r.data
1401         assert str(r) == 'a b  c d', str(r)
1402
1403         r = f + ['c', 'd']
1404         assert isinstance(r, SCons.Util.CLVar), type(r)
1405         assert r.data == ['a b', 'c', 'd'], r.data
1406         assert str(r) == 'a b c d', str(r)
1407
1408         r = f + [' c', 'd']
1409         assert isinstance(r, SCons.Util.CLVar), type(r)
1410         assert r.data == ['a b', ' c', 'd'], r.data
1411         assert str(r) == 'a b  c d', str(r)
1412
1413         f = SCons.Util.CLVar(['a', 'b'])
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         r = f + ['c d']
1426         assert isinstance(r, SCons.Util.CLVar), type(r)
1427         assert r.data == ['a', 'b', 'c d'], r.data
1428         assert str(r) == 'a b c d', str(r)
1429
1430         r = f + [' c d']
1431         assert isinstance(r, SCons.Util.CLVar), type(r)
1432         assert r.data == ['a', 'b', ' c d'], r.data
1433         assert str(r) == 'a b  c d', str(r)
1434
1435         r = f + ['c', 'd']
1436         assert isinstance(r, SCons.Util.CLVar), type(r)
1437         assert r.data == ['a', 'b', 'c', 'd'], r.data
1438         assert str(r) == 'a b c d', str(r)
1439
1440         r = f + [' c', 'd']
1441         assert isinstance(r, SCons.Util.CLVar), type(r)
1442         assert r.data == ['a', 'b', ' c', 'd'], r.data
1443         assert str(r) == 'a b  c d', str(r)
1444
1445         loc = {}
1446         loc['FOO'] = 'foo'
1447         loc['BAR'] = SCons.Util.CLVar('bar')
1448         loc['CALL'] = lambda target, source, env, for_signature: 'call'
1449         env = DummyEnv(loc)
1450
1451         cmd = SCons.Util.CLVar("test $FOO $BAR $CALL test")
1452
1453         newcmd = scons_subst(cmd, env)
1454         assert newcmd == 'test foo bar call test', newcmd
1455
1456         cmd_list = scons_subst_list(cmd, env)
1457         assert len(cmd_list) == 1, cmd_list
1458         assert cmd_list[0][0] == "test", cmd_list[0][0]
1459         assert cmd_list[0][1] == "foo", cmd_list[0][1]
1460         assert cmd_list[0][2] == "bar", cmd_list[0][2]
1461         assert cmd_list[0][3] == "call", cmd_list[0][3]
1462         assert cmd_list[0][4] == "test", cmd_list[0][4]
1463
1464     def test_Selector(self):
1465         """Test the Selector class"""
1466
1467         s = Selector({'a' : 'AAA', 'b' : 'BBB'})
1468         assert s['a'] == 'AAA', s['a']
1469         assert s['b'] == 'BBB', s['b']
1470         exc_caught = None
1471         try:
1472             x = s['c']
1473         except KeyError:
1474             exc_caught = 1
1475         assert exc_caught, "should have caught a KeyError"
1476         s['c'] = 'CCC'
1477         assert s['c'] == 'CCC', s['c']
1478
1479         class DummyEnv(UserDict.UserDict):
1480             def subst(self, key):
1481                 if key[0] == '$':
1482                     return self[key[1:]]
1483                 return key
1484
1485         env = DummyEnv()
1486
1487         s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
1488         ret = s(env, ['foo.d'])
1489         assert ret == 'DDD', ret
1490         ret = s(env, ['bar.e'])
1491         assert ret == 'EEE', ret
1492         ret = s(env, ['bar.x'])
1493         assert ret == None, ret
1494         s[None] = 'XXX'
1495         ret = s(env, ['bar.x'])
1496         assert ret == 'XXX', ret
1497
1498         env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'})
1499
1500         s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'})
1501         ret = s(env, ['foo.f'])
1502         assert ret == 'FFF', ret
1503         ret = s(env, ['bar.g'])
1504         assert ret == 'GGG', ret
1505
1506     def test_adjustixes(self):
1507         """Test the adjustixes() function"""
1508         r = adjustixes('file', 'pre-', '-suf')
1509         assert r == 'pre-file-suf', r
1510         r = adjustixes('pre-file', 'pre-', '-suf')
1511         assert r == 'pre-file-suf', r
1512         r = adjustixes('file-suf', 'pre-', '-suf')
1513         assert r == 'pre-file-suf', r
1514         r = adjustixes('pre-file-suf', 'pre-', '-suf')
1515         assert r == 'pre-file-suf', r
1516         r = adjustixes('pre-file.xxx', 'pre-', '-suf')
1517         assert r == 'pre-file.xxx', r
1518         r = adjustixes('dir/file', 'pre-', '-suf')
1519         assert r == os.path.join('dir', 'pre-file-suf'), r
1520
1521     def test_containsAny(self):
1522         """Test the containsAny() function"""
1523         assert containsAny('*.py', '*?[]')
1524         assert not containsAny('file.txt', '*?[]')
1525
1526     def test_containsAll(self):
1527         """Test the containsAll() function"""
1528         assert containsAll('43221', '123')
1529         assert not containsAll('134', '123')
1530
1531     def test_containsOnly(self):
1532         """Test the containsOnly() function"""
1533         assert containsOnly('.83', '0123456789.')
1534         assert not containsOnly('43221', '123')
1535
1536 if __name__ == "__main__":
1537     suite = unittest.makeSuite(UtilTestCase, 'test_')
1538     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1539         sys.exit(1)