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