Handle recursive substitution in overrides.
[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             assert str(e) == "Syntax error trying to evaluate `$foo.bar.3.0'", e
429         else:
430             raise AssertionError, "did not catch expected UserError"
431
432         # Test returning a function.
433         #env = DummyEnv({'FUNCTION' : foo})
434         #func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None)
435         #assert func is function_foo, func
436         #func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None)
437         #assert func is function_foo, func
438         #func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None)
439         #assert func is function_foo, func
440
441     def test_subst_list(self):
442         """Testing the scons_subst_list() method..."""
443         class MyNode(DummyNode):
444             """Simple node work-alike with some extra stuff for testing."""
445             def __init__(self, name):
446                 DummyNode.__init__(self, name)
447                 class Attribute:
448                     pass
449                 self.attribute = Attribute()
450                 self.attribute.attr1 = 'attr$1-' + os.path.basename(name)
451                 self.attribute.attr2 = 'attr$2-' + os.path.basename(name)
452
453         target = [ MyNode("./foo/bar.exe"),
454                    MyNode("/bar/baz with spaces.obj"),
455                    MyNode("../foo/baz.obj") ]
456         source = [ MyNode("./foo/blah with spaces.cpp"),
457                    MyNode("/bar/ack.cpp"),
458                    MyNode("../foo/ack.c") ]
459
460         loc = {
461             'xxx'       : None,
462             'NEWLINE'   : 'before\nafter',
463
464             'AAA'       : 'a',
465             'BBB'       : 'b',
466             'CCC'       : 'c',
467
468             'DO'        : DummyNode('do something'),
469             'FOO'       : DummyNode('foo.in'),
470             'BAR'       : DummyNode('bar with spaces.out'),
471             'CRAZY'     : DummyNode('crazy\nfile.in'),
472
473             # $XXX$HHH should expand to GGGIII, not BADNEWS.
474             'XXX'       : '$FFF',
475             'FFF'       : 'GGG',
476             'HHH'       : 'III',
477             'FFFIII'    : 'BADNEWS',
478
479             'CMDGEN1'   : CmdGen1,
480             'CMDGEN2'   : CmdGen2,
481
482             'LITERALS'  : [ Literal('foo\nwith\nnewlines'),
483                             Literal('bar\nwith\nnewlines') ],
484
485             # Test various combinations of strings, lists and functions.
486             'N'         : None,
487             'X'         : 'x',
488             'Y'         : '$X',
489             'R'         : '$R',
490             'S'         : 'x y',
491             'LS'        : ['x y'],
492             'L'         : ['x', 'y'],
493             'CS'        : cs,
494             'CL'        : cl,
495
496             # Test function calls within ${}.
497             'FUNCCALL'  : '${FUNC1("$AAA $FUNC2 $BBB")}',
498             'FUNC1'     : lambda x: x,
499             'FUNC2'     : lambda target, source, env, for_signature: ['x$CCC'],
500
501             # Various tests refactored from ActionTests.py.
502             'LIST'      : [["This", "is", "$(", "$a", "$)", "test"]],
503
504             # Test recursion.
505             'RECURSE'   : 'foo $RECURSE bar',
506             'RRR'       : 'foo $SSS bar',
507             'SSS'       : '$RRR',
508         }
509
510         env = DummyEnv(loc)
511
512         cases = [
513             "$TARGETS",
514             [
515                 ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
516             ],
517
518             "$SOURCES $NEWLINE $TARGETS",
519             [
520                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"],
521                 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
522             ],
523
524             "$SOURCES$NEWLINE",
525             [
526                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
527                 ["after"],
528             ],
529
530             "foo$FFF",
531             [
532                 ["fooGGG"],
533             ],
534
535             "foo${FFF}",
536             [
537                 ["fooGGG"],
538             ],
539
540             "test ${SOURCES.attribute.attr1}",
541             [
542                 ["test", "attr$1-blah with spaces.cpp", "attr$1-ack.cpp", "attr$1-ack.c"],
543             ],
544
545             "test ${SOURCES.attribute.attr2}",
546             [
547                 ["test", "attr$2-blah with spaces.cpp", "attr$2-ack.cpp", "attr$2-ack.c"],
548             ],
549
550             "$DO --in=$FOO --out=$BAR",
551             [
552                 ["do something", "--in=foo.in", "--out=bar with spaces.out"],
553             ],
554
555             # This test is now fixed, and works like it should.
556             "$DO --in=$CRAZY --out=$BAR",
557             [
558                 ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"],
559             ],
560
561             # Try passing a list to scons_subst_list().
562             [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"],
563             [
564                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
565                 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"],
566             ],
567
568             # Test against a former bug in scons_subst_list().
569             "$XXX$HHH",
570             [
571                 ["GGGIII"],
572             ],
573
574             # Test double-dollar-sign behavior.
575             "$$FFF$HHH",
576             [
577                 ["$FFFIII"],
578             ],
579
580             # Test various combinations of strings, lists and functions.
581             None,                   [[]],
582             [None],                 [[]],
583             '',                     [[]],
584             [''],                   [[]],
585             'x',                    [['x']],
586             ['x'],                  [['x']],
587             'x y',                  [['x', 'y']],
588             ['x y'],                [['x y']],
589             ['x', 'y'],             [['x', 'y']],
590             '$N',                   [[]],
591             ['$N'],                 [[]],
592             '$X',                   [['x']],
593             ['$X'],                 [['x']],
594             '$Y',                   [['x']],
595             ['$Y'],                 [['x']],
596             #'$R',                   [[]],
597             #['$R'],                 [[]],
598             '$S',                   [['x', 'y']],
599             '$S z',                 [['x', 'y', 'z']],
600             ['$S'],                 [['x', 'y']],
601             ['$S z'],               [['x', 'y z']],     # XXX - IS THIS BEST?
602             ['$S', 'z'],            [['x', 'y', 'z']],
603             '$LS',                  [['x y']],
604             '$LS z',                [['x y', 'z']],
605             ['$LS'],                [['x y']],
606             ['$LS z'],              [['x y z']],
607             ['$LS', 'z'],           [['x y', 'z']],
608             '$L',                   [['x', 'y']],
609             '$L z',                 [['x', 'y', 'z']],
610             ['$L'],                 [['x', 'y']],
611             ['$L z'],               [['x', 'y z']],     # XXX - IS THIS BEST?
612             ['$L', 'z'],            [['x', 'y', 'z']],
613             cs,                     [['cs']],
614             [cs],                   [['cs']],
615             cl,                     [['cl']],
616             [cl],                   [['cl']],
617             '$CS',                  [['cs']],
618             ['$CS'],                [['cs']],
619             '$CL',                  [['cl']],
620             ['$CL'],                [['cl']],
621
622             # Test function calls within ${}.
623             '$FUNCCALL',            [['a', 'xc', 'b']],
624
625             # Test handling of newlines in white space.
626             'foo\nbar',             [['foo'], ['bar']],
627             'foo\n\nbar',           [['foo'], ['bar']],
628             'foo \n \n bar',        [['foo'], ['bar']],
629             'foo \nmiddle\n bar',   [['foo'], ['middle'], ['bar']],
630
631             # Bug reported by Christoph Wiedemann.
632             cvt('$xxx/bin'),        [['/bin']],
633         ]
634
635         kwargs = {'target' : target, 'source' : source}
636
637         failed = 0
638         while cases:
639             input, expect = cases[:2]
640             expect = map(lambda l: map(cvt, l), expect)
641             result = apply(scons_subst_list, (input, env), kwargs)
642             if result != expect:
643                 if failed == 0: print
644                 print "    input %s => %s did not match %s" % (repr(input), result, repr(expect))
645                 failed = failed + 1
646             del cases[:2]
647         assert failed == 0, "%d subst_list() cases failed" % failed
648
649         t1 = MyNode('t1')
650         t2 = MyNode('t2')
651         s1 = MyNode('s1')
652         s2 = MyNode('s2')
653         result = scons_subst_list("$TARGET $SOURCES", env,
654                                   target=[t1, t2],
655                                   source=[s1, s2])
656         assert result == [['t1', 's1', 's2']], result
657         result = scons_subst_list("$TARGET $SOURCES", env,
658                                   target=[t1, t2],
659                                   source=[s1, s2],
660                                   dict={})
661         assert result == [[]], result
662
663         # Test interpolating a callable.
664         _t = DummyNode('t')
665         _s = DummyNode('s')
666         cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
667                                     env, target=_t, source=_s)
668         assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
669
670         # Test escape functionality.
671         def escape_func(foo):
672             return '**' + foo + '**'
673         cmd_list = scons_subst_list("abc $LITERALS xyz", env)
674         assert cmd_list == [['abc',
675                              'foo\nwith\nnewlines',
676                              'bar\nwith\nnewlines',
677                              'xyz']], cmd_list
678         c = cmd_list[0][0].escape(escape_func)
679         assert c == 'abc', c
680         c = cmd_list[0][1].escape(escape_func)
681         assert c == '**foo\nwith\nnewlines**', c
682         c = cmd_list[0][2].escape(escape_func)
683         assert c == '**bar\nwith\nnewlines**', c
684         c = cmd_list[0][3].escape(escape_func)
685         assert c == 'xyz', c
686
687         # Tests of the various SUBST_* modes of substitution.
688         subst_list_cases = [
689             "test $xxx",
690                 [["test"]],
691                 [["test"]],
692                 [["test"]],
693
694             "test $($xxx$)",
695                 [["test", "$($)"]],
696                 [["test"]],
697                 [["test"]],
698
699             "test $( $xxx $)",
700                 [["test", "$(", "$)"]],
701                 [["test"]],
702                 [["test"]],
703
704             "$AAA ${AAA}A $BBBB $BBB",
705                 [["a", "aA", "b"]],
706                 [["a", "aA", "b"]],
707                 [["a", "aA", "b"]],
708
709             "$RECURSE",
710                 [["foo", "bar"]],
711                 [["foo", "bar"]],
712                 [["foo", "bar"]],
713
714             "$RRR",
715                 [["foo", "bar"]],
716                 [["foo", "bar"]],
717                 [["foo", "bar"]],
718
719             # Verify what happens with no target or source nodes.
720             "$TARGET $SOURCES",
721                 [[]],
722                 [[]],
723                 [[]],
724
725             "$TARGETS $SOURCE",
726                 [[]],
727                 [[]],
728                 [[]],
729
730             # Various test refactored from ActionTests.py
731             "${LIST}",
732                 [['This', 'is', '$(', '$)', 'test']],
733                 [['This', 'is', 'test']],
734                 [['This', 'is', 'test']],
735
736             ["|", "$(", "$AAA", "|", "$BBB", "$)", "|", "$CCC", 1],
737                 [["|", "$(", "a", "|", "b", "$)", "|", "c", "1"]],
738                 [["|", "a", "|", "b", "|", "c", "1"]],
739                 [["|", "|", "c", "1"]],
740         ]
741
742         r = scons_subst_list("$TARGET $SOURCES", env, mode=SUBST_RAW)
743         assert r == [[]], r
744
745         failed = 0
746         while subst_list_cases:
747             input, eraw, ecmd, esig = subst_list_cases[:4]
748             result = scons_subst_list(input, env, mode=SUBST_RAW)
749             if result != eraw:
750                 if failed == 0: print
751                 print "    input %s => RAW %s did not match %s" % (repr(input), repr(result), repr(eraw))
752                 failed = failed + 1
753             result = scons_subst_list(input, env, mode=SUBST_CMD)
754             if result != ecmd:
755                 if failed == 0: print
756                 print "    input %s => CMD %s did not match %s" % (repr(input), repr(result), repr(ecmd))
757                 failed = failed + 1
758             result = scons_subst_list(input, env, mode=SUBST_SIG)
759             if result != esig:
760                 if failed == 0: print
761                 print "    input %s => SIG %s did not match %s" % (repr(input), repr(result), repr(esig))
762                 failed = failed + 1
763             del subst_list_cases[:4]
764         assert failed == 0, "%d subst() mode cases failed" % failed
765
766         # Test that we handle syntax errors during expansion as expected.
767         try:
768             scons_subst_list('$foo.bar.3.0', env)
769         except SCons.Errors.UserError, e:
770             assert str(e) == "Syntax error trying to evaluate `$foo.bar.3.0'", e
771         else:
772             raise AssertionError, "did not catch expected SyntaxError"
773
774     def test_subst_once(self):
775         """Testing the scons_subst_once() method"""
776
777         loc = {
778             'CCFLAGS'           : '-DFOO',
779             'ONE'               : 1,
780             'RECURSE'           : 'r $RECURSE r',
781             'LIST'              : ['a', 'b', 'c'],
782         }
783
784         env = DummyEnv(loc)
785
786         cases = [
787             '$CCFLAGS -DBAR',
788             'OTHER_KEY',
789             '$CCFLAGS -DBAR',
790
791             '$CCFLAGS -DBAR',
792             'CCFLAGS',
793             '-DFOO -DBAR',
794
795             'x $ONE y',
796             'ONE',
797             'x 1 y',
798
799             'x $RECURSE y',
800             'RECURSE',
801             'x r $RECURSE r y',
802
803             '$LIST',
804             'LIST',
805             'a b c',
806
807             ['$LIST'],
808             'LIST',
809             ['a', 'b', 'c'],
810
811             ['x', '$LIST', 'y'],
812             'LIST',
813             ['x', 'a', 'b', 'c', 'y'],
814
815             ['x', 'x $LIST y', 'y'],
816             'LIST',
817             ['x', 'x a b c y', 'y'],
818
819             ['x', 'x $CCFLAGS y', 'y'],
820             'LIST',
821             ['x', 'x $CCFLAGS y', 'y'],
822
823             ['x', 'x $RECURSE y', 'y'],
824             'LIST',
825             ['x', 'x $RECURSE y', 'y'],
826         ]
827
828         failed = 0
829         while cases:
830             input, key, expect = cases[:3]
831             result = scons_subst_once(input, env, key)
832             if result != expect:
833                 if failed == 0: print
834                 print "    input %s (%s) => %s did not match %s" % (repr(input), repr(key), repr(result), repr(expect))
835                 failed = failed + 1
836             del cases[:3]
837         assert failed == 0, "%d subst() cases failed" % failed
838
839     def test_splitext(self):
840         assert splitext('foo') == ('foo','')
841         assert splitext('foo.bar') == ('foo','.bar')
842         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
843
844     def test_quote_spaces(self):
845         """Testing the quote_spaces() method..."""
846         q = quote_spaces('x')
847         assert q == 'x', q
848
849         q = quote_spaces('x x')
850         assert q == '"x x"', q
851
852         q = quote_spaces('x\tx')
853         assert q == '"x\tx"', q
854
855     def test_render_tree(self):
856         class Node:
857             def __init__(self, name, children=[]):
858                 self.children = children
859                 self.name = name
860             def __str__(self):
861                 return self.name
862
863         def get_children(node):
864             return node.children
865
866         windows_h = Node("windows.h")
867         stdlib_h = Node("stdlib.h")
868         stdio_h = Node("stdio.h")
869         bar_c = Node("bar.c", [stdlib_h, windows_h])
870         bar_o = Node("bar.o", [bar_c])
871         foo_c = Node("foo.c", [stdio_h])
872         foo_o = Node("foo.o", [foo_c])
873         foo = Node("foo", [foo_o, bar_o])
874
875         expect = """\
876 +-foo
877   +-foo.o
878   | +-foo.c
879   |   +-stdio.h
880   +-bar.o
881     +-bar.c
882       +-stdlib.h
883       +-windows.h
884 """
885
886         actual = render_tree(foo, get_children)
887         assert expect == actual, (expect, actual)
888
889         bar_h = Node('bar.h', [stdlib_h])
890         blat_h = Node('blat.h', [stdlib_h])
891         blat_c = Node('blat.c', [blat_h, bar_h])
892         blat_o = Node('blat.o', [blat_c])
893
894         expect = """\
895 +-blat.o
896   +-blat.c
897     +-blat.h
898     | +-stdlib.h
899     +-bar.h
900 """
901
902         actual = render_tree(blat_o, get_children, 1)
903         assert expect == actual, (expect, actual)
904
905     def test_is_Dict(self):
906         assert is_Dict({})
907         import UserDict
908         assert is_Dict(UserDict.UserDict())
909         assert not is_Dict([])
910         assert not is_Dict("")
911         if hasattr(types, 'UnicodeType'):
912             exec "assert not is_Dict(u'')"
913
914     def test_is_List(self):
915         assert is_List([])
916         import UserList
917         assert is_List(UserList.UserList())
918         assert not is_List({})
919         assert not is_List("")
920         if hasattr(types, 'UnicodeType'):
921             exec "assert not is_List(u'')"
922
923     def test_is_String(self):
924         assert is_String("")
925         if hasattr(types, 'UnicodeType'):
926             exec "assert is_String(u'')"
927         try:
928             import UserString
929         except:
930             pass
931         else:
932             assert is_String(UserString.UserString(''))
933         assert not is_String({})
934         assert not is_String([])
935
936     def test_to_String(self):
937         """Test the to_String() method."""
938         assert to_String(1) == "1", to_String(1)
939         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
940         assert to_String("foo") == "foo", to_String("foo")
941
942         try:
943             import UserString
944
945             s1=UserString.UserString('blah')
946             assert to_String(s1) == s1, s1
947             assert to_String(s1) == 'blah', s1
948
949             class Derived(UserString.UserString):
950                 pass
951             s2 = Derived('foo')
952             assert to_String(s2) == s2, s2
953             assert to_String(s2) == 'foo', s2
954
955             if hasattr(types, 'UnicodeType'):
956                 s3=UserString.UserString(unicode('bar'))
957                 assert to_String(s3) == s3, s3
958                 assert to_String(s3) == unicode('bar'), s3
959                 assert type(to_String(s3)) is types.UnicodeType, \
960                        type(to_String(s3))
961         except ImportError:
962             pass
963
964         if hasattr(types, 'UnicodeType'):
965             s4 = unicode('baz')
966             assert to_String(s4) == unicode('baz'), to_String(s4)
967             assert type(to_String(s4)) is types.UnicodeType, \
968                    type(to_String(s4))
969
970     def test_WhereIs(self):
971         test = TestCmd.TestCmd(workdir = '')
972
973         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
974         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
975         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
976         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
977
978         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
979
980         if sys.platform != 'win32':
981             test.write(sub1_xxx_exe, "\n")
982
983         os.mkdir(sub2_xxx_exe)
984
985         test.write(sub3_xxx_exe, "\n")
986         os.chmod(sub3_xxx_exe, 0777)
987
988         test.write(sub4_xxx_exe, "\n")
989         os.chmod(sub4_xxx_exe, 0777)
990
991         env_path = os.environ['PATH']
992
993         pathdirs_1234 = [ test.workpath('sub1'),
994                           test.workpath('sub2'),
995                           test.workpath('sub3'),
996                           test.workpath('sub4'),
997                         ] + string.split(env_path, os.pathsep)
998
999         pathdirs_1243 = [ test.workpath('sub1'),
1000                           test.workpath('sub2'),
1001                           test.workpath('sub4'),
1002                           test.workpath('sub3'),
1003                         ] + string.split(env_path, os.pathsep)
1004
1005         os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
1006         wi = WhereIs('xxx.exe')
1007         assert wi == test.workpath(sub3_xxx_exe), wi
1008         wi = WhereIs('xxx.exe', pathdirs_1243)
1009         assert wi == test.workpath(sub4_xxx_exe), wi
1010         wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
1011         assert wi == test.workpath(sub4_xxx_exe), wi
1012
1013         os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
1014         wi = WhereIs('xxx.exe')
1015         assert wi == test.workpath(sub4_xxx_exe), wi
1016         wi = WhereIs('xxx.exe', pathdirs_1234)
1017         assert wi == test.workpath(sub3_xxx_exe), wi
1018         wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
1019         assert wi == test.workpath(sub3_xxx_exe), wi
1020
1021         if sys.platform == 'win32':
1022             wi = WhereIs('xxx', pathext = '')
1023             assert wi is None, wi
1024
1025             wi = WhereIs('xxx', pathext = '.exe')
1026             assert wi == test.workpath(sub4_xxx_exe), wi
1027
1028             wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
1029             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
1030
1031             # Test that we return a normalized path even when
1032             # the path contains forward slashes.
1033             forward_slash = test.workpath('') + '/sub3'
1034             wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
1035             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
1036
1037     def test_is_valid_construction_var(self):
1038         """Testing is_valid_construction_var()"""
1039         r = is_valid_construction_var("_a")
1040         assert not r is None, r
1041         r = is_valid_construction_var("z_")
1042         assert not r is None, r
1043         r = is_valid_construction_var("X_")
1044         assert not r is None, r
1045         r = is_valid_construction_var("2a")
1046         assert r is None, r
1047         r = is_valid_construction_var("a2_")
1048         assert not r is None, r
1049         r = is_valid_construction_var("/")
1050         assert r is None, r
1051         r = is_valid_construction_var("_/")
1052         assert r is None, r
1053         r = is_valid_construction_var("a/")
1054         assert r is None, r
1055         r = is_valid_construction_var(".b")
1056         assert r is None, r
1057         r = is_valid_construction_var("_.b")
1058         assert r is None, r
1059         r = is_valid_construction_var("b1._")
1060         assert r is None, r
1061         r = is_valid_construction_var("-b")
1062         assert r is None, r
1063         r = is_valid_construction_var("_-b")
1064         assert r is None, r
1065         r = is_valid_construction_var("b1-_")
1066         assert r is None, r
1067
1068     def test_get_env_var(self):
1069         """Testing get_environment_var()."""
1070         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
1071         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
1072         assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
1073         assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
1074         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
1075         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
1076         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
1077         assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
1078         assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
1079
1080     def test_Proxy(self):
1081         """Test generic Proxy class."""
1082         class Subject:
1083             def foo(self):
1084                 return 1
1085             def bar(self):
1086                 return 2
1087
1088         s=Subject()
1089         s.baz = 3
1090
1091         class ProxyTest(Proxy):
1092             def bar(self):
1093                 return 4
1094
1095         p=ProxyTest(s)
1096
1097         assert p.foo() == 1, p.foo()
1098         assert p.bar() == 4, p.bar()
1099         assert p.baz == 3, p.baz
1100
1101         p.baz = 5
1102         s.baz = 6
1103
1104         assert p.baz == 5, p.baz
1105         assert p.get() == s, p.get()
1106
1107     def test_Literal(self):
1108         """Test the Literal() function."""
1109         input_list = [ '$FOO', Literal('$BAR') ]
1110         dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
1111
1112         def escape_func(cmd):
1113             return '**' + cmd + '**'
1114
1115         cmd_list = scons_subst_list(input_list, dummy_env)
1116         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1117         assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1118
1119     def test_SpecialAttrWrapper(self):
1120         """Test the SpecialAttrWrapper() function."""
1121         input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
1122         dummy_env = DummyEnv({ 'FOO' : 'BAZ', 'BAR' : 'BLAT' })
1123
1124         def escape_func(cmd):
1125             return '**' + cmd + '**'
1126
1127         cmd_list = scons_subst_list(input_list, dummy_env)
1128         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1129         assert cmd_list == ['BAZ', '**$BAR**'], cmd_list
1130
1131         cmd_list = scons_subst_list(input_list, dummy_env, mode=SUBST_SIG)
1132         cmd_list = SCons.Util.escape_list(cmd_list[0], escape_func)
1133         assert cmd_list == ['BAZ', '**BLEH**'], cmd_list
1134
1135     def test_mapPaths(self):
1136         """Test the mapPaths function"""
1137         class MyFileNode:
1138             def __init__(self, path):
1139                 self.path = path
1140             def __str__(self):
1141                 return self.path
1142
1143         dir=MyFileNode('foo')
1144         file=MyFileNode('bar/file')
1145
1146         class DummyEnv:
1147             def subst(self, arg):
1148                 return 'bar'
1149
1150         res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
1151         assert res[0] == file, res[0]
1152         assert res[1] == os.path.join('foo', 'baz'), res[1]
1153         assert res[2] == os.path.join('foo', 'blat/boo'), res[2]
1154         assert res[3] == '#test', res[3]
1155
1156         env=DummyEnv()
1157         res=mapPaths('bleh', dir, env)
1158         assert res[0] == os.path.normpath('foo/bar'), res[1]
1159
1160     def test_display(self):
1161         old_stdout = sys.stdout
1162         sys.stdout = OutBuffer()
1163         display("line1")
1164         display.set_mode(0)
1165         display("line2")
1166         display.set_mode(1)
1167         display("line3")
1168
1169         assert sys.stdout.buffer == "line1\nline3\n"
1170         sys.stdout = old_stdout
1171
1172     def test_fs_delete(self):
1173         test = TestCmd.TestCmd(workdir = '')
1174         base = test.workpath('')
1175         xxx = test.workpath('xxx.xxx')
1176         ZZZ = test.workpath('ZZZ.ZZZ')
1177         sub1_yyy = test.workpath('sub1', 'yyy.yyy')
1178
1179         test.subdir('sub1')
1180         test.write(xxx, "\n")
1181         test.write(ZZZ, "\n")
1182         test.write(sub1_yyy, "\n")
1183
1184         old_stdout = sys.stdout
1185         sys.stdout = OutBuffer()
1186
1187         exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
1188               "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
1189               "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
1190               "Removed " + os.path.join(base, xxx) + '\n' + \
1191               "Removed directory " + base + '\n'
1192
1193         fs_delete(base, remove=0)
1194         assert sys.stdout.buffer == exp, sys.stdout.buffer
1195         assert os.path.exists(sub1_yyy)
1196
1197         sys.stdout.buffer = ""
1198         fs_delete(base, remove=1)
1199         assert sys.stdout.buffer == exp
1200         assert not os.path.exists(base)
1201
1202         test._dirlist = None
1203         sys.stdout = old_stdout
1204
1205     def test_get_native_path(self):
1206         """Test the get_native_path() function."""
1207         import tempfile
1208         filename = tempfile.mktemp()
1209         str = '1234567890 ' + filename
1210         open(filename, 'w').write(str)
1211         assert open(get_native_path(filename)).read() == str
1212
1213     def test_subst_dict(self):
1214         """Test substituting dictionary values in an Action
1215         """
1216         t = DummyNode('t')
1217         s = DummyNode('s')
1218         d = subst_dict(target=t, source=s)
1219         assert str(d['TARGETS'][0]) == 't', d['TARGETS']
1220         assert str(d['TARGET']) == 't', d['TARGET']
1221         assert str(d['SOURCES'][0]) == 's', d['SOURCES']
1222         assert str(d['SOURCE']) == 's', d['SOURCE']
1223
1224         t1 = DummyNode('t1')
1225         t2 = DummyNode('t2')
1226         s1 = DummyNode('s1')
1227         s2 = DummyNode('s2')
1228         d = subst_dict(target=[t1, t2], source=[s1, s2])
1229         TARGETS = map(lambda x: str(x), d['TARGETS'])
1230         TARGETS.sort()
1231         assert TARGETS == ['t1', 't2'], d['TARGETS']
1232         assert str(d['TARGET']) == 't1', d['TARGET']
1233         SOURCES = map(lambda x: str(x), d['SOURCES'])
1234         SOURCES.sort()
1235         assert SOURCES == ['s1', 's2'], d['SOURCES']
1236         assert str(d['SOURCE']) == 's1', d['SOURCE']
1237
1238         class N:
1239             def __init__(self, name):
1240                 self.name = name
1241             def __str__(self):
1242                 return self.name
1243             def rfile(self):
1244                 return self.__class__('rstr-' + self.name)
1245             def get_subst_proxy(self):
1246                 return self
1247
1248         t3 = N('t3')
1249         t4 = DummyNode('t4')
1250         s3 = DummyNode('s3')
1251         s4 = N('s4')
1252         d = subst_dict(target=[t3, t4], source=[s3, s4])
1253         TARGETS = map(lambda x: str(x), d['TARGETS'])
1254         TARGETS.sort()
1255         assert TARGETS == ['t3', 't4'], d['TARGETS']
1256         SOURCES = map(lambda x: str(x), d['SOURCES'])
1257         SOURCES.sort()
1258         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
1259
1260     def test_PrependPath(self):
1261         """Test prepending to a path"""
1262         p1 = r'C:\dir\num\one;C:\dir\num\two'
1263         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
1264         # have to include the pathsep here so that the test will work on UNIX too.
1265         p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';')
1266         p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
1267         p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
1268         p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
1269         assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
1270         assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
1271
1272     def test_AppendPath(self):
1273         """Test appending to a path."""
1274         p1 = r'C:\dir\num\one;C:\dir\num\two'
1275         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
1276         # have to include the pathsep here so that the test will work on UNIX too.
1277         p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';')
1278         p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
1279         p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
1280         p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
1281         assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
1282         assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
1283
1284     def test_NodeList(self):
1285         """Test NodeList class"""
1286         class TestClass:
1287             def __init__(self, name, child=None):
1288                 self.child = child
1289                 self.bar = name
1290             def foo(self):
1291                 return self.bar + "foo"
1292             def getself(self):
1293                 return self
1294
1295         t1 = TestClass('t1', TestClass('t1child'))
1296         t2 = TestClass('t2', TestClass('t2child'))
1297         t3 = TestClass('t3')
1298
1299         nl = NodeList([t1, t2, t3])
1300         assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
1301         assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
1302         assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
1303         assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
1304                nl[0:2].child.foo()
1305         assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
1306                nl[0:2].child.bar
1307
1308     def test_Selector(self):
1309         """Test the Selector class"""
1310
1311         s = Selector({'a' : 'AAA', 'b' : 'BBB'})
1312         assert s['a'] == 'AAA', s['a']
1313         assert s['b'] == 'BBB', s['b']
1314         exc_caught = None
1315         try:
1316             x = s['c']
1317         except KeyError:
1318             exc_caught = 1
1319         assert exc_caught, "should have caught a KeyError"
1320         s['c'] = 'CCC'
1321         assert s['c'] == 'CCC', s['c']
1322
1323         class DummyEnv(UserDict.UserDict):
1324             def subst(self, key):
1325                 if key[0] == '$':
1326                     return self[key[1:]]
1327                 return key
1328
1329         env = DummyEnv()
1330
1331         s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
1332         ret = s(env, ['foo.d'])
1333         assert ret == 'DDD', ret
1334         ret = s(env, ['bar.e'])
1335         assert ret == 'EEE', ret
1336         ret = s(env, ['bar.x'])
1337         assert ret == None, ret
1338         s[None] = 'XXX'
1339         ret = s(env, ['bar.x'])
1340         assert ret == 'XXX', ret
1341
1342         env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'})
1343
1344         s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'})
1345         ret = s(env, ['foo.f'])
1346         assert ret == 'FFF', ret
1347         ret = s(env, ['bar.g'])
1348         assert ret == 'GGG', ret
1349
1350     def test_adjustixes(self):
1351         """Test the adjustixes() function"""
1352         r = adjustixes('file', 'pre-', '-suf')
1353         assert r == 'pre-file-suf', r
1354         r = adjustixes('pre-file', 'pre-', '-suf')
1355         assert r == 'pre-file-suf', r
1356         r = adjustixes('file-suf', 'pre-', '-suf')
1357         assert r == 'pre-file-suf', r
1358         r = adjustixes('pre-file-suf', 'pre-', '-suf')
1359         assert r == 'pre-file-suf', r
1360         r = adjustixes('pre-file.xxx', 'pre-', '-suf')
1361         assert r == 'pre-file.xxx', r
1362         r = adjustixes('dir/file', 'pre-', '-suf')
1363         assert r == os.path.join('dir', 'pre-file-suf'), r
1364
1365 if __name__ == "__main__":
1366     suite = unittest.makeSuite(UtilTestCase, 'test_')
1367     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1368         sys.exit(1)