Refactor the scons_subst*() tests to make it easier to add tests and to more complete...
[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 class OutBuffer:
36     def __init__(self):
37         self.buffer = ""
38
39     def write(self, str):
40         self.buffer = self.buffer + str
41
42 class DummyNode:
43     """Simple node work-alike."""
44     def __init__(self, name):
45         self.name = os.path.normpath(name)
46     def __str__(self):
47         return self.name
48     def is_literal(self):
49         return 1
50     def rfile(self):
51         return self
52     def get_subst_proxy(self):
53         return self
54
55 class DummyEnv:
56     def __init__(self, dict={}):
57         self.dict = dict
58
59     def Dictionary(self, key = None):
60         if not key:
61             return self.dict
62         return self.dict[key]
63
64     def sig_dict(self):
65         dict = self.dict.copy()
66         dict["TARGETS"] = 'tsig'
67         dict["SOURCES"] = 'ssig'
68         return dict
69
70 def cs(target=None, source=None, env=None, for_signature=None):
71     return 'cs'
72
73 def cl(target=None, source=None, env=None, for_signature=None):
74     return ['cl']
75
76 def CmdGen1(target, source, env, for_signature):
77     # Nifty trick...since Environment references are interpolated,
78     # instantiate an instance of a callable class with this one,
79     # which will then get evaluated.
80     assert str(target) == 't', target
81     assert str(source) == 's', source
82     return "${CMDGEN2('foo', %d)}" % for_signature
83
84 class CmdGen2:
85     def __init__(self, mystr, forsig):
86         self.mystr = mystr
87         self.expect_for_signature = forsig
88
89     def __call__(self, target, source, env, for_signature):
90         assert str(target) == 't', target
91         assert str(source) == 's', source
92         assert for_signature == self.expect_for_signature, for_signature
93         return [ self.mystr, env.Dictionary('BAR') ]
94
95 if os.sep == '/':
96     def cvt(str):
97         return str
98 else:
99     def cvt(str):
100         return string.replace(str, '/', os.sep)
101
102 class UtilTestCase(unittest.TestCase):
103     def test_subst(self):
104         """Test the subst function"""
105         class MyNode(DummyNode):
106             """Simple node work-alike with some extra stuff for testing."""
107             def get_stuff(self, extra):
108                 return self.name + extra
109             foo = 1
110
111         class TestLiteral:
112             def __init__(self, literal):
113                 self.literal = literal
114             def __str__(self):
115                 return self.literal
116             def is_literal(self):
117                 return 1
118
119         def function_foo(arg):
120             pass
121
122         target = [ MyNode("./foo/bar.exe"),
123                    MyNode("/bar/baz.obj"),
124                    MyNode("../foo/baz.obj") ]
125         source = [ MyNode("./foo/blah.cpp"),
126                    MyNode("/bar/ack.cpp"),
127                    MyNode("../foo/ack.c") ]
128
129         loc = {
130             'xxx'       : None,
131             'null'      : '',
132             'zero'      : 0,
133             'one'       : 1,
134             'BAR'       : 'baz',
135             'ONE'       : '$TWO',
136             'TWO'       : '$THREE',
137             'THREE'     : 'four',
138
139             'AAA'       : 'a',
140             'BBB'       : 'b',
141
142             # $XXX$HHH should expand to GGGIII, not BADNEWS.
143             'XXX'       : '$FFF',
144             'FFF'       : 'GGG',
145             'HHH'       : 'III',
146             'FFFIII'    : 'BADNEWS',
147
148             'LITERAL'   : TestLiteral("$XXX"),
149
150             # Test that we can expand to and return a function.
151             #'FUNCTION'  : function_foo,
152
153             'CMDGEN1'   : CmdGen1,
154             'CMDGEN2'   : CmdGen2,
155
156             'NOTHING'   : "",
157             'NONE'      : None,
158
159             # Test various combinations of strings, lists and functions.
160             'N'         : None,
161             'X'         : 'x',
162             'Y'         : '$X',
163             'R'         : '$R',
164             'S'         : 'x y',
165             'LS'        : ['x y'],
166             'L'         : ['x', 'y'],
167             'CS'        : cs,
168             'CL'        : cl,
169
170             # Test recursion.
171             #'RECURSE'   : 'foo $RECURSE bar',
172             #'RRR'       : 'foo $SSS bar',
173             #'SSS'       : '$RRR',
174         }
175
176         env = DummyEnv(loc)
177
178         # Basic tests of substitution functionality.
179         cases = [
180             # Basics:  strings without expansions are left alone, and
181             # the simplest possible expansion to a null-string value.
182             "test",                 "test",
183             "$null",                "",
184
185             # Test expansion of integer values.
186             "test $zero",           "test 0",
187             "test $one",            "test 1",
188
189             # Test multiple re-expansion of values.
190             "test $ONE",            "test four",
191
192             # Test a whole bunch of $TARGET[S] and $SOURCE[S] expansions.
193             "test $TARGETS $SOURCES",
194             "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c",
195
196             "test ${TARGETS[:]} ${SOURCES[0]}",
197             "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp",
198
199             "test ${TARGETS[1:]}v",
200             "test /bar/baz.obj ../foo/baz.objv",
201
202             "test $TARGET",
203             "test foo/bar.exe",
204
205             "test $TARGET$FOO[0]",
206             "test foo/bar.exe[0]",
207
208             "test $TARGETS.foo",
209             "test 1 1 1",
210
211             "test ${SOURCES[0:2].foo}",
212             "test 1 1",
213
214             "test $SOURCE.foo",
215             "test 1",
216
217             "test ${TARGET.get_stuff('blah')}",
218             "test foo/bar.exeblah",
219
220             "test ${SOURCES.get_stuff('blah')}",
221             "test foo/blah.cppblah /bar/ack.cppblah ../foo/ack.cblah",
222
223             "test ${SOURCES[0:2].get_stuff('blah')}",
224             "test foo/blah.cppblah /bar/ack.cppblah",
225
226             "test ${SOURCES[0:2].get_stuff('blah')}",
227             "test foo/blah.cppblah /bar/ack.cppblah",
228
229             # Test that adjacent expansions don't get re-interpreted
230             # together.  The correct disambiguated expansion should be:
231             #   $XXX$HHH => ${FFF}III => GGGIII
232             # not:
233             #   $XXX$HHH => ${FFFIII} => BADNEWS
234             "$XXX$HHH",             "GGGIII",
235
236             # Test double-dollar-sign behavior.
237             "$$FFF$HHH",            "$FFFIII",
238
239             # Test that a Literal will stop dollar-sign substitution.
240             "$XXX $LITERAL $FFF",   "GGG $XXX GGG",
241
242             # Test that we don't blow up even if they subscript
243             # something in ways they "can't."
244             "${FFF[0]}",            "G",
245             "${FFF[7]}",            "",
246             "${NOTHING[1]}",        "",
247             "${NONE[2]}",           "",
248
249             # Test various combinations of strings and lists.
250             #None,                   '',
251             '',                     '',
252             'x',                    'x',
253             'x y',                  'x y',
254             '$N',                   '',
255             '$X',                   'x',
256             '$Y',                   'x',
257             #'$R',                   '',
258             '$S',                   'x y',
259             '$LS',                  'x y',
260             '$L',                   'x y',
261             #cs,                     'cs',
262             #cl,                     'cl',
263             '$CS',                  'cs',
264             '$CL',                  'cl',
265         ]
266
267         kwargs = {'target' : target, 'source' : source}
268
269         while cases:
270             input, expect = cases[:2]
271             expect = cvt(expect)
272             result = apply(scons_subst, (input, env), kwargs)
273             assert result == expect, \
274                    "input %s => %s did not match %s" % (repr(input), result, expect)
275             del cases[:2]
276
277         # Tests of the various SUBST_* modes of substitution.
278         subst_cases = [
279             "test $xxx",
280                 "test ",
281                 "test",
282                 "test",
283
284             "test $($xxx$)",
285                 "test $($)",
286                 "test",
287                 "test",
288
289             "test $( $xxx $)",
290                 "test $(  $)",
291                 "test",
292                 "test",
293
294             "$AAA ${AAA}A $BBBB $BBB",
295                 "a aA  b",
296                 "a aA b",
297                 "a aA b",
298
299             #"$RECURSE",
300             #   "foo  bar"
301             #   "foo bar"
302             #   "foo bar"
303
304             #"$RRR",
305             #   "foo  bar"
306             #   "foo bar"
307             #   "foo bar"
308         ]
309
310         while subst_cases:
311             input, eraw, ecmd, esig = subst_cases[:4]
312             result = scons_subst(input, env, mode=SUBST_RAW)
313             assert result == eraw, \
314                    "input %s => RAW %s did not match %s" % (repr(input), result, eraw)
315             result = scons_subst(input, env, mode=SUBST_CMD)
316             assert result == ecmd, \
317                    "input %s => CMD %s did not match %s" % (repr(input), result, ecmd)
318             result = scons_subst(input, env, mode=SUBST_SIG)
319             assert result == esig, \
320                    "input %s => SIG %s did not match %s" % (repr(input), result, esig)
321             del subst_cases[:4]
322
323         # Test interpolating a callable.
324         newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
325                              env, target=MyNode('t'), source=MyNode('s'))
326         assert newcom == "test foo baz s t", newcom
327
328         # Test returning a function.
329         #env = DummyEnv({'FUNCTION' : foo})
330         #func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None)
331         #assert func is function_foo, func
332         #func = scons_subst("$FUNCTION", env, mode=SUBST_CMD, call=None)
333         #assert func is function_foo, func
334         #func = scons_subst("$FUNCTION", env, mode=SUBST_SIG, call=None)
335         #assert func is function_foo, func
336
337     def test_subst_list(self):
338         """Testing the scons_subst_list() method..."""
339         target = [ DummyNode("./foo/bar.exe"),
340                    DummyNode("/bar/baz with spaces.obj"),
341                    DummyNode("../foo/baz.obj") ]
342         source = [ DummyNode("./foo/blah with spaces.cpp"),
343                    DummyNode("/bar/ack.cpp"),
344                    DummyNode("../foo/ack.c") ]
345
346         loc = {
347             'xxx'       : None,
348             'NEWLINE'   : 'before\nafter',
349
350             'DO'        : DummyNode('do something'),
351             'FOO'       : DummyNode('foo.in'),
352             'BAR'       : DummyNode('bar with spaces.out'),
353             'CRAZY'     : DummyNode('crazy\nfile.in'),
354
355             # $XXX$HHH should expand to GGGIII, not BADNEWS.
356             'XXX'       : '$FFF',
357             'FFF'       : 'GGG',
358             'HHH'       : 'III',
359             'FFFIII'    : 'BADNEWS',
360
361             'CMDGEN1'   : CmdGen1,
362             'CMDGEN2'   : CmdGen2,
363
364             'LITERALS'  : [ Literal('foo\nwith\nnewlines'),
365                             Literal('bar\nwith\nnewlines') ],
366
367             # Test various combinations of strings, lists and functions.
368             'N'         : None,
369             'X'         : 'x',
370             'Y'         : '$X',
371             'R'         : '$R',
372             'S'         : 'x y',
373             'LS'        : ['x y'],
374             'L'         : ['x', 'y'],
375             'CS'        : cs,
376             'CL'        : cl,
377         }
378
379         env = DummyEnv(loc)
380
381         cases = [
382             "$TARGETS",
383             [
384                 ["foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
385             ],
386
387             "$SOURCES $NEWLINE $TARGETS",
388             [
389                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.c", "before"],
390                 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj"],
391             ],
392
393             "$SOURCES$NEWLINE",
394             [
395                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
396                 ["after"],
397             ],
398
399             "$DO --in=$FOO --out=$BAR",
400             [
401                 ["do something", "--in=foo.in", "--out=bar with spaces.out"],
402             ],
403
404             # This test is now fixed, and works like it should.
405             "$DO --in=$CRAZY --out=$BAR",
406             [
407                 ["do something", "--in=crazy\nfile.in", "--out=bar with spaces.out"],
408             ],
409
410             # Try passing a list to scons_subst_list().
411             [ "$SOURCES$NEWLINE", "$TARGETS", "This is a test"],
412             [
413                 ["foo/blah with spaces.cpp", "/bar/ack.cpp", "../foo/ack.cbefore"],
414                 ["after", "foo/bar.exe", "/bar/baz with spaces.obj", "../foo/baz.obj", "This is a test"],
415             ],
416
417
418             # Test against a former bug in scons_subst_list().
419             "$XXX$HHH",
420             [
421                 ["GGGIII"],
422             ],
423
424             # Test double-dollar-sign behavior.
425             "$$FFF$HHH",
426             [
427                 ["$FFFIII"],
428             ],
429
430             # Test various combinations of strings, lists and functions.
431             None,                   [[]],
432             #[None],                 [[]],
433             '',                     [[]],
434             [''],                   [[]],
435             'x',                    [['x']],
436             ['x'],                  [['x']],
437             'x y',                  [['x', 'y']],
438             ['x y'],                [['x y']],
439             ['x', 'y'],             [['x', 'y']],
440             '$N',                   [[]],
441             ['$N'],                 [[]],
442             '$X',                   [['x']],
443             ['$X'],                 [['x']],
444             '$Y',                   [['x']],
445             ['$Y'],                 [['x']],
446             #'$R',                   [[]],
447             #['$R'],                 [[]],
448             '$S',                   [['x', 'y']],
449             ['$S'],                 [['x', 'y']],
450             '$LS',                  [['x y']],
451             ['$LS'],                [['x y']],
452             '$L',                   [['x', 'y']],
453             ['$L'],                 [['x', 'y']],
454             #cs,                     [['cs']],
455             #[cs],                   [['cs']],
456             #cl,                     [['cl']],
457             #[cl],                   [['cl']],
458             '$CS',                  [['cs']],
459             ['$CS'],                [['cs']],
460             '$CL',                  [['cl']],
461             ['$CL'],                [['cl']],
462         ]
463
464         kwargs = {'target' : target, 'source' : source}
465
466         while cases:
467             input, expect = cases[:2]
468             expect = map(lambda l: map(cvt, l), expect)
469             result = apply(scons_subst_list, (input, env), kwargs)
470             assert result == expect, \
471                    "input %s => %s did not match %s" % (repr(input), result, repr(expect))
472             del cases[:2]
473
474         # Test interpolating a callable.
475         cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES",
476                                     env,
477                                     target=DummyNode('t'),
478                                     source=DummyNode('s'))
479         assert cmd_list == [['testing', 'foo', 'bar with spaces.out', 't', 's']], cmd_list
480
481         # Test escape functionality.
482         def escape_func(foo):
483             return '**' + foo + '**'
484         cmd_list = scons_subst_list("$LITERALS", env)
485         assert cmd_list == [['foo\nwith\nnewlines',
486                             'bar\nwith\nnewlines']], cmd_list
487         cmd_list[0][0].escape(escape_func)
488         assert cmd_list[0][0] == '**foo\nwith\nnewlines**', cmd_list[0][0]
489         cmd_list[0][1].escape(escape_func)
490         assert cmd_list[0][1] == '**bar\nwith\nnewlines**', cmd_list[0][0]
491
492     def test_splitext(self):
493         assert splitext('foo') == ('foo','')
494         assert splitext('foo.bar') == ('foo','.bar')
495         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
496
497     def test_quote_spaces(self):
498         """Testing the quote_spaces() method..."""
499         q = quote_spaces('x')
500         assert q == 'x', q
501
502         q = quote_spaces('x x')
503         assert q == '"x x"', q
504
505         q = quote_spaces('x\tx')
506         assert q == '"x\tx"', q
507
508     def test_render_tree(self):
509         class Node:
510             def __init__(self, name, children=[]):
511                 self.children = children
512                 self.name = name
513             def __str__(self):
514                 return self.name
515
516         def get_children(node):
517             return node.children
518
519         windows_h = Node("windows.h")
520         stdlib_h = Node("stdlib.h")
521         stdio_h = Node("stdio.h")
522         bar_c = Node("bar.c", [stdlib_h, windows_h])
523         bar_o = Node("bar.o", [bar_c])
524         foo_c = Node("foo.c", [stdio_h])
525         foo_o = Node("foo.o", [foo_c])
526         foo = Node("foo", [foo_o, bar_o])
527
528         expect = """\
529 +-foo
530   +-foo.o
531   | +-foo.c
532   |   +-stdio.h
533   +-bar.o
534     +-bar.c
535       +-stdlib.h
536       +-windows.h
537 """
538
539         actual = render_tree(foo, get_children)
540         assert expect == actual, (expect, actual)
541
542         bar_h = Node('bar.h', [stdlib_h])
543         blat_h = Node('blat.h', [stdlib_h])
544         blat_c = Node('blat.c', [blat_h, bar_h])
545         blat_o = Node('blat.o', [blat_c])
546
547         expect = """\
548 +-blat.o
549   +-blat.c
550     +-blat.h
551     | +-stdlib.h
552     +-bar.h
553 """
554
555         actual = render_tree(blat_o, get_children, 1)
556         assert expect == actual, (expect, actual)
557
558     def test_is_Dict(self):
559         assert is_Dict({})
560         import UserDict
561         assert is_Dict(UserDict.UserDict())
562         assert not is_Dict([])
563         assert not is_Dict("")
564         if hasattr(types, 'UnicodeType'):
565             exec "assert not is_Dict(u'')"
566
567     def test_is_List(self):
568         assert is_List([])
569         import UserList
570         assert is_List(UserList.UserList())
571         assert not is_List({})
572         assert not is_List("")
573         if hasattr(types, 'UnicodeType'):
574             exec "assert not is_List(u'')"
575
576     def test_is_String(self):
577         assert is_String("")
578         if hasattr(types, 'UnicodeType'):
579             exec "assert is_String(u'')"
580         try:
581             import UserString
582         except:
583             pass
584         else:
585             assert is_String(UserString.UserString(''))
586         assert not is_String({})
587         assert not is_String([])
588
589     def test_to_String(self):
590         """Test the to_String() method."""
591         assert to_String(1) == "1", to_String(1)
592         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
593         assert to_String("foo") == "foo", to_String("foo")
594
595         try:
596             import UserString
597
598             s1=UserString.UserString('blah')
599             assert to_String(s1) == s1, s1
600             assert to_String(s1) == 'blah', s1
601
602             class Derived(UserString.UserString):
603                 pass
604             s2 = Derived('foo')
605             assert to_String(s2) == s2, s2
606             assert to_String(s2) == 'foo', s2
607
608             if hasattr(types, 'UnicodeType'):
609                 s3=UserString.UserString(unicode('bar'))
610                 assert to_String(s3) == s3, s3
611                 assert to_String(s3) == unicode('bar'), s3
612                 assert type(to_String(s3)) is types.UnicodeType, \
613                        type(to_String(s3))
614         except ImportError:
615             pass
616
617         if hasattr(types, 'UnicodeType'):
618             s4 = unicode('baz')
619             assert to_String(s4) == unicode('baz'), to_String(s4)
620             assert type(to_String(s4)) is types.UnicodeType, \
621                    type(to_String(s4))
622
623     def test_WhereIs(self):
624         test = TestCmd.TestCmd(workdir = '')
625
626         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
627         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
628         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
629         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
630
631         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
632
633         if sys.platform != 'win32':
634             test.write(sub1_xxx_exe, "\n")
635
636         os.mkdir(sub2_xxx_exe)
637
638         test.write(sub3_xxx_exe, "\n")
639         os.chmod(sub3_xxx_exe, 0777)
640
641         test.write(sub4_xxx_exe, "\n")
642         os.chmod(sub4_xxx_exe, 0777)
643
644         env_path = os.environ['PATH']
645
646         pathdirs_1234 = [ test.workpath('sub1'),
647                           test.workpath('sub2'),
648                           test.workpath('sub3'),
649                           test.workpath('sub4'),
650                         ] + string.split(env_path, os.pathsep)
651
652         pathdirs_1243 = [ test.workpath('sub1'),
653                           test.workpath('sub2'),
654                           test.workpath('sub4'),
655                           test.workpath('sub3'),
656                         ] + string.split(env_path, os.pathsep)
657
658         os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
659         wi = WhereIs('xxx.exe')
660         assert wi == test.workpath(sub3_xxx_exe), wi
661         wi = WhereIs('xxx.exe', pathdirs_1243)
662         assert wi == test.workpath(sub4_xxx_exe), wi
663         wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
664         assert wi == test.workpath(sub4_xxx_exe), wi
665
666         os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
667         wi = WhereIs('xxx.exe')
668         assert wi == test.workpath(sub4_xxx_exe), wi
669         wi = WhereIs('xxx.exe', pathdirs_1234)
670         assert wi == test.workpath(sub3_xxx_exe), wi
671         wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
672         assert wi == test.workpath(sub3_xxx_exe), wi
673
674         if sys.platform == 'win32':
675             wi = WhereIs('xxx', pathext = '')
676             assert wi is None, wi
677
678             wi = WhereIs('xxx', pathext = '.exe')
679             assert wi == test.workpath(sub4_xxx_exe), wi
680
681             wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
682             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
683
684             # Test that we return a normalized path even when
685             # the path contains forward slashes.
686             forward_slash = test.workpath('') + '/sub3'
687             wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
688             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
689
690     def test_is_valid_construction_var(self):
691         """Testing is_valid_construction_var()"""
692         r = is_valid_construction_var("_a")
693         assert not r is None, r
694         r = is_valid_construction_var("z_")
695         assert not r is None, r
696         r = is_valid_construction_var("X_")
697         assert not r is None, r
698         r = is_valid_construction_var("2a")
699         assert r is None, r
700         r = is_valid_construction_var("a2_")
701         assert not r is None, r
702         r = is_valid_construction_var("/")
703         assert r is None, r
704         r = is_valid_construction_var("_/")
705         assert r is None, r
706         r = is_valid_construction_var("a/")
707         assert r is None, r
708         r = is_valid_construction_var(".b")
709         assert r is None, r
710         r = is_valid_construction_var("_.b")
711         assert r is None, r
712         r = is_valid_construction_var("b1._")
713         assert r is None, r
714         r = is_valid_construction_var("-b")
715         assert r is None, r
716         r = is_valid_construction_var("_-b")
717         assert r is None, r
718         r = is_valid_construction_var("b1-_")
719         assert r is None, r
720
721     def test_get_env_var(self):
722         """Testing get_environment_var()."""
723         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
724         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
725         assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
726         assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
727         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
728         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
729         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
730         assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
731         assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
732
733     def test_Proxy(self):
734         """Test generic Proxy class."""
735         class Subject:
736             def foo(self):
737                 return 1
738             def bar(self):
739                 return 2
740
741         s=Subject()
742         s.baz = 3
743
744         class ProxyTest(Proxy):
745             def bar(self):
746                 return 4
747
748         p=ProxyTest(s)
749
750         assert p.foo() == 1, p.foo()
751         assert p.bar() == 4, p.bar()
752         assert p.baz == 3, p.baz
753
754         p.baz = 5
755         s.baz = 6
756
757         assert p.baz == 5, p.baz
758         assert p.get() == s, p.get()
759
760     def test_Literal(self):
761         """Test the Literal() function."""
762         cmd_list = [ '$FOO', Literal('$BAR') ]
763         cmd_list = scons_subst_list(cmd_list,
764                                     DummyEnv({ 'FOO' : 'BAZ',
765                                                'BAR' : 'BLAT' }))
766         def escape_func(cmd):
767             return '**' + cmd + '**'
768
769         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
770         cmd_list = map(str, cmd_list[0])
771         assert cmd_list[0] == 'BAZ', cmd_list[0]
772         assert cmd_list[1] == '**$BAR**', cmd_list[1]
773
774     def test_SpecialAttrWrapper(self):
775         """Test the SpecialAttrWrapper() function."""
776         input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
777
778         def escape_func(cmd):
779             return '**' + cmd + '**'
780
781         cmd_list = scons_subst_list(input_list,
782                                     DummyEnv({ 'FOO' : 'BAZ',
783                                                'BAR' : 'BLAT' }))
784         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
785         cmd_list = map(str, cmd_list[0])
786         assert cmd_list[0] == 'BAZ', cmd_list[0]
787         assert cmd_list[1] == '**$BAR**', cmd_list[1]
788
789         cmd_list = scons_subst_list(input_list,
790                                     DummyEnv({ 'FOO' : 'BAZ',
791                                                'BAR' : 'BLAT' }),
792                                     mode=SUBST_SIG)
793         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
794         cmd_list = map(str, cmd_list[0])
795         assert cmd_list[0] == 'BAZ', cmd_list[0]
796         assert cmd_list[1] == '**BLEH**', cmd_list[1]
797
798     def test_mapPaths(self):
799         """Test the mapPaths function"""
800         class MyFileNode:
801             def __init__(self, path):
802                 self.path = path
803             def __str__(self):
804                 return self.path
805
806         dir=MyFileNode('foo')
807         file=MyFileNode('bar/file')
808
809         class DummyEnv:
810             def subst(self, arg):
811                 return 'bar'
812
813         res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
814         assert res[0] == file, res[0]
815         assert res[1] == os.path.join('foo', 'baz'), res[1]
816         assert res[2] == os.path.join('foo', 'blat/boo'), res[2]
817         assert res[3] == '#test', res[3]
818
819         env=DummyEnv()
820         res=mapPaths('bleh', dir, env)
821         assert res[0] == os.path.normpath('foo/bar'), res[1]
822
823     def test_display(self):
824         old_stdout = sys.stdout
825         sys.stdout = OutBuffer()
826         display("line1")
827         display.set_mode(0)
828         display("line2")
829         display.set_mode(1)
830         display("line3")
831
832         assert sys.stdout.buffer == "line1\nline3\n"
833         sys.stdout = old_stdout
834
835     def test_fs_delete(self):
836         test = TestCmd.TestCmd(workdir = '')
837         base = test.workpath('')
838         xxx = test.workpath('xxx.xxx')
839         ZZZ = test.workpath('ZZZ.ZZZ')
840         sub1_yyy = test.workpath('sub1', 'yyy.yyy')
841
842         test.subdir('sub1')
843         test.write(xxx, "\n")
844         test.write(ZZZ, "\n")
845         test.write(sub1_yyy, "\n")
846
847         old_stdout = sys.stdout
848         sys.stdout = OutBuffer()
849
850         exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
851               "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
852               "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
853               "Removed " + os.path.join(base, xxx) + '\n' + \
854               "Removed directory " + base + '\n'
855
856         fs_delete(base, remove=0)
857         assert sys.stdout.buffer == exp, sys.stdout.buffer
858         assert os.path.exists(sub1_yyy)
859
860         sys.stdout.buffer = ""
861         fs_delete(base, remove=1)
862         assert sys.stdout.buffer == exp
863         assert not os.path.exists(base)
864
865         test._dirlist = None
866         sys.stdout = old_stdout
867
868     def test_get_native_path(self):
869         """Test the get_native_path() function."""
870         import tempfile
871         filename = tempfile.mktemp()
872         str = '1234567890 ' + filename
873         open(filename, 'w').write(str)
874         assert open(get_native_path(filename)).read() == str
875
876     def test_subst_dict(self):
877         """Test substituting dictionary values in an Action
878         """
879         env = DummyEnv({'a' : 'A', 'b' : 'B'})
880         d = subst_dict([], [], env)
881         assert d['__env__'] is env, d['__env__']
882
883         d = subst_dict(target = DummyNode('t'), source = DummyNode('s'), env=DummyEnv())
884         assert str(d['TARGETS'][0]) == 't', d['TARGETS']
885         assert str(d['TARGET']) == 't', d['TARGET']
886         assert str(d['SOURCES'][0]) == 's', d['SOURCES']
887         assert str(d['SOURCE']) == 's', d['SOURCE']
888
889         d = subst_dict(target = [DummyNode('t1'), DummyNode('t2')],
890                        source = [DummyNode('s1'), DummyNode('s2')],
891                        env = DummyEnv())
892         TARGETS = map(lambda x: str(x), d['TARGETS'])
893         TARGETS.sort()
894         assert TARGETS == ['t1', 't2'], d['TARGETS']
895         assert str(d['TARGET']) == 't1', d['TARGET']
896         SOURCES = map(lambda x: str(x), d['SOURCES'])
897         SOURCES.sort()
898         assert SOURCES == ['s1', 's2'], d['SOURCES']
899         assert str(d['SOURCE']) == 's1', d['SOURCE']
900
901         class N:
902             def __init__(self, name):
903                 self.name = name
904             def __str__(self):
905                 return self.name
906             def rfile(self):
907                 return self.__class__('rstr-' + self.name)
908             def get_subst_proxy(self):
909                 return self
910
911         d = subst_dict(target = [N('t3'), DummyNode('t4')],
912                        source = [DummyNode('s3'), N('s4')],
913                        env = DummyEnv())
914         TARGETS = map(lambda x: str(x), d['TARGETS'])
915         TARGETS.sort()
916         assert TARGETS == ['t3', 't4'], d['TARGETS']
917         SOURCES = map(lambda x: str(x), d['SOURCES'])
918         SOURCES.sort()
919         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
920
921     def test_PrependPath(self):
922         """Test prepending to a path"""
923         p1 = r'C:\dir\num\one;C:\dir\num\two'
924         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
925         # have to include the pathsep here so that the test will work on UNIX too.
926         p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';')
927         p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
928         p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
929         p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
930         assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
931         assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
932
933     def test_AppendPath(self):
934         """Test appending to a path."""
935         p1 = r'C:\dir\num\one;C:\dir\num\two'
936         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
937         # have to include the pathsep here so that the test will work on UNIX too.
938         p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';')
939         p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
940         p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
941         p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
942         assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
943         assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
944
945     def test_NodeList(self):
946         """Test NodeList class"""
947         class TestClass:
948             def __init__(self, name, child=None):
949                 self.child = child
950                 self.bar = name
951             def foo(self):
952                 return self.bar + "foo"
953             def getself(self):
954                 return self
955
956         t1 = TestClass('t1', TestClass('t1child'))
957         t2 = TestClass('t2', TestClass('t2child'))
958         t3 = TestClass('t3')
959
960         nl = NodeList([t1, t2, t3])
961         assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
962         assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
963         assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
964         assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
965                nl[0:2].child.foo()
966         assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
967                nl[0:2].child.bar
968
969     def test_Selector(self):
970         """Test the Selector class"""
971
972         s = Selector({'a' : 'AAA', 'b' : 'BBB'})
973         assert s['a'] == 'AAA', s['a']
974         assert s['b'] == 'BBB', s['b']
975         exc_caught = None
976         try:
977             x = s['c']
978         except KeyError:
979             exc_caught = 1
980         assert exc_caught, "should have caught a KeyError"
981         s['c'] = 'CCC'
982         assert s['c'] == 'CCC', s['c']
983
984         class DummyEnv(UserDict.UserDict):
985             def subst(self, key):
986                 if key[0] == '$':
987                     return self[key[1:]]
988                 return key
989
990         env = DummyEnv()
991
992         s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
993         ret = s(env, ['foo.d'])
994         assert ret == 'DDD', ret
995         ret = s(env, ['bar.e'])
996         assert ret == 'EEE', ret
997         ret = s(env, ['bar.x'])
998         assert ret == None, ret
999         s[None] = 'XXX'
1000         ret = s(env, ['bar.x'])
1001         assert ret == 'XXX', ret
1002
1003         env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'})
1004
1005         s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'})
1006         ret = s(env, ['foo.f'])
1007         assert ret == 'FFF', ret
1008         ret = s(env, ['bar.g'])
1009         assert ret == 'GGG', ret
1010
1011 if __name__ == "__main__":
1012     suite = unittest.makeSuite(UtilTestCase, 'test_')
1013     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1014         sys.exit(1)