Suppress illegal construction variables.
[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 DummyEnv:
43     def __init__(self, dict={}):
44         self.dict = dict
45
46     def Dictionary(self, key = None):
47         if not key:
48             return self.dict
49         return self.dict[key]
50
51     def sig_dict(self):
52         dict = self.dict.copy()
53         dict["TARGETS"] = 'tsig'
54         dict["SOURCES"] = 'ssig'
55         return dict
56
57 def CmdGen1(target, source, env, for_signature):
58     # Nifty trick...since Environment references are interpolated,
59     # instantiate an instance of a callable class with this one,
60     # which will then get evaluated.
61     assert str(target) == 't', target
62     assert str(source) == 's', source
63     return "${CMDGEN2('foo', %d)}" % for_signature
64
65 class CmdGen2:
66     def __init__(self, mystr, forsig):
67         self.mystr = mystr
68         self.expect_for_signature = forsig
69
70     def __call__(self, target, source, env, for_signature):
71         assert str(target) == 't', target
72         assert str(source) == 's', source
73         assert for_signature == self.expect_for_signature, for_signature
74         return [ self.mystr, env.Dictionary('BAR') ]
75
76 class UtilTestCase(unittest.TestCase):
77     def test_subst(self):
78         """Test the subst function"""
79         loc = {}
80
81         class N:
82             """Simple node work-alike with some extra stuff for testing."""
83             def __init__(self, data):
84                 self.data = os.path.normpath(data)
85
86             def __str__(self):
87                 return self.data
88
89             def is_literal(self):
90                 return 1
91
92             def get_stuff(self, extra):
93                 return self.data + extra
94
95             def rfile(self):
96                 return self
97
98             def get_subst_proxy(self):
99                 return self
100
101             foo = 1
102         
103         target = [ N("./foo/bar.exe"),
104                    N("/bar/baz.obj"),
105                    N("../foo/baz.obj") ]
106         source = [ N("./foo/blah.cpp"),
107                    N("/bar/ack.cpp"),
108                    N("../foo/ack.c") ]
109         loc['xxx'] = None
110         loc['zero'] = 0
111         loc['one'] = 1
112         loc['BAR'] = 'baz'
113
114         loc['CMDGEN1'] = CmdGen1
115         loc['CMDGEN2'] = CmdGen2
116
117         env = DummyEnv(loc)
118
119         if os.sep == '/':
120             def cvt(str):
121                 return str
122         else:
123             def cvt(str):
124                 return string.replace(str, '/', os.sep)
125
126         newcom = scons_subst("test $TARGETS $SOURCES", env,
127                              target=target, source=source)
128         assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c")
129
130         newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", env,
131                              target=target, source=source)
132         assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp")
133
134         newcom = scons_subst("test ${TARGETS[1:]}v", env,
135                              target=target, source=source)
136         assert newcom == cvt("test /bar/baz.obj ../foo/baz.objv")
137
138         newcom = scons_subst("test $TARGET", env,
139                              target=target, source=source)
140         assert newcom == cvt("test foo/bar.exe")
141
142         newcom = scons_subst("test $TARGET$FOO[0]", env,
143                              target=target, source=source)
144         assert newcom == cvt("test foo/bar.exe[0]")
145
146         newcom = scons_subst("test $TARGETS.foo", env,
147                              target=target, source=source)
148         assert newcom == "test 1 1 1", newcom
149
150         newcom = scons_subst("test ${SOURCES[0:2].foo}", env,
151                              target=target, source=source)
152         assert newcom == "test 1 1", newcom
153
154         newcom = scons_subst("test $SOURCE.foo", env,
155                              target=target, source=source)
156         assert newcom == "test 1", newcom
157
158         newcom = scons_subst("test ${TARGET.get_stuff('blah')}", env,
159                              target=target, source=source)
160         assert newcom == cvt("test foo/bar.exeblah"), newcom
161
162         newcom = scons_subst("test ${SOURCES.get_stuff('blah')}", env,
163                              target=target, source=source)
164         assert newcom == cvt("test foo/blah.cppblah /bar/ack.cppblah ../foo/ack.cblah"), newcom
165
166         newcom = scons_subst("test ${SOURCES[0:2].get_stuff('blah')}", env,
167                              target=target, source=source)
168         assert newcom == cvt("test foo/blah.cppblah /bar/ack.cppblah"), newcom
169
170         newcom = scons_subst("test ${SOURCES[0:2].get_stuff('blah')}", env,
171                              target=target, source=source)
172         assert newcom == cvt("test foo/blah.cppblah /bar/ack.cppblah"), newcom
173
174         newcom = scons_subst("test $xxx", env)
175         assert newcom == cvt("test"), newcom
176
177         newcom = scons_subst("test $($xxx$)", env)
178         assert newcom == cvt("test $($)"), newcom
179
180         newcom = scons_subst("test $( $xxx $)", env)
181         assert newcom == cvt("test $( $)"), newcom
182
183         newcom = scons_subst("test $($xxx$)", env, mode=SUBST_SIG)
184         assert newcom == cvt("test"), newcom
185
186         newcom = scons_subst("test $( $xxx $)", env, mode=SUBST_SIG)
187         assert newcom == cvt("test"), newcom
188
189         newcom = scons_subst("test $zero", env)
190         assert newcom == cvt("test 0"), newcom
191
192         newcom = scons_subst("test $one", env)
193         assert newcom == cvt("test 1"), newcom
194
195         newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
196                              env, target=N('t'), source=N('s'))
197         assert newcom == cvt("test foo baz s t"), newcom
198
199         # Test against a former bug in scons_subst_list()
200         glob = { "FOO" : "$BAR",
201                  "BAR" : "BAZ",
202                  "BLAT" : "XYX",
203                  "BARXYX" : "BADNEWS" }
204         newcom = scons_subst("$FOO$BLAT", DummyEnv(glob))
205         assert newcom == "BAZXYX", newcom
206
207         # Test for double-dollar-sign behavior
208         glob = { "FOO" : "BAR",
209                  "BAZ" : "BLAT" }
210         newcom = scons_subst("$$FOO$BAZ", DummyEnv(glob))
211         assert newcom == "$FOOBLAT", newcom
212
213         class TestLiteral:
214             def __init__(self, literal):
215                 self.literal = literal
216
217             def __str__(self):
218                 return self.literal
219
220             def is_literal(self):
221                 return 1
222
223         # Test that a literal will stop dollar-sign substitution
224         glob = { "FOO" : "BAR",
225                  "BAZ" : TestLiteral("$FOO"),
226                  "BAR" : "$FOO" }
227         newcom = scons_subst("$FOO $BAZ $BAR", DummyEnv(glob))
228         assert newcom == "BAR $FOO BAR", newcom
229
230         # Test that we don't blow up even if they subscript something
231         # in ways they "can't."
232         glob = { "FOO" : "BAR",
233                  "NOTHING" : "" ,
234                  "NONE" : None }
235         newcom = scons_subst("${FOO[0]}", DummyEnv(glob))
236         assert newcom == "B", newcom
237         newcom = scons_subst("${FOO[7]}", DummyEnv(glob))
238         assert newcom == "", newcom
239         newcom = scons_subst("${NOTHING[1]}", DummyEnv(glob))
240         assert newcom == "", newcom
241         newcom = scons_subst("${NONE[2]}", DummyEnv(glob))
242         assert newcom == "", newcom
243
244     def test_splitext(self):
245         assert splitext('foo') == ('foo','')
246         assert splitext('foo.bar') == ('foo','.bar')
247         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
248
249     def test_subst_list(self):
250         """Testing the scons_subst_list() method..."""
251
252         class Node:
253             def __init__(self, name):
254                 self.name = os.path.normpath(name)
255             def __str__(self):
256                 return self.name
257             def is_literal(self):
258                 return 1
259             def rfile(self):
260                 return self
261             def get_subst_proxy(self):
262                 return self
263         
264         loc = {}
265         target = [ Node("./foo/bar.exe"),
266                    Node("/bar/baz with spaces.obj"),
267                    Node("../foo/baz.obj") ]
268         source = [ Node("./foo/blah with spaces.cpp"),
269                    Node("/bar/ack.cpp"),
270                    Node("../foo/ack.c") ]
271         loc['xxx'] = None
272         loc['NEWLINE'] = 'before\nafter'
273
274         loc['DO'] = Node('do something')
275         loc['FOO'] = Node('foo.in')
276         loc['BAR'] = Node('bar with spaces.out')
277         loc['CRAZY'] = Node('crazy\nfile.in')
278
279         loc['CMDGEN1'] = CmdGen1
280         loc['CMDGEN2'] = CmdGen2
281
282         env = DummyEnv(loc)
283
284         if os.sep == '/':
285             def cvt(str):
286                 return str
287         else:
288             def cvt(str):
289                 return string.replace(str, '/', os.sep)
290
291         cmd_list = scons_subst_list("$TARGETS", env,
292                                     target=target,
293                                     source=source)
294         assert cmd_list[0][1] == cvt("/bar/baz with spaces.obj"), cmd_list[0][1]
295
296         cmd_list = scons_subst_list("$SOURCES $NEWLINE $TARGETS", env,
297                                     target=target,
298                                     source=source)
299         assert len(cmd_list) == 2, cmd_list
300         assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
301         assert cmd_list[1][2] == cvt("/bar/baz with spaces.obj"), cmd_list[1]
302
303         cmd_list = scons_subst_list("$SOURCES$NEWLINE", env,
304                                     target=target,
305                                     source=source)
306         assert len(cmd_list) == 2, cmd_list
307         assert cmd_list[1][0] == 'after', cmd_list[1][0]
308         assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
309
310         cmd_list = scons_subst_list("$DO --in=$FOO --out=$BAR", env)
311         assert len(cmd_list) == 1, cmd_list
312         assert len(cmd_list[0]) == 3, cmd_list
313         assert cmd_list[0][0] == 'do something', cmd_list[0][0]
314         assert cmd_list[0][1] == '--in=foo.in', cmd_list[0][1]
315         assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
316
317         # This test is now fixed, and works like it should.
318         cmd_list = scons_subst_list("$DO --in=$CRAZY --out=$BAR", env)
319         assert len(cmd_list) == 1, map(str, cmd_list[0])
320         assert len(cmd_list[0]) == 3, cmd_list
321         assert cmd_list[0][0] == 'do something', cmd_list[0][0]
322         assert cmd_list[0][1] == '--in=crazy\nfile.in', cmd_list[0][1]
323         assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
324         
325         # Test inputting a list to scons_subst_list()
326         cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
327                                         "This is a test" ],
328                                     env,
329                                     target=target,
330                                     source=source)
331         assert len(cmd_list) == 2, len(cmd_list)
332         assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
333         assert cmd_list[1][0] == cvt("after"), cmd_list[1]
334         assert cmd_list[1][4] == "This is a test", cmd_list[1]
335
336         # Test interpolating a callable.
337         cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES", env,
338                                     target=Node('t'), source=Node('s'))
339         assert len(cmd_list) == 1, len(cmd_list)
340         assert cmd_list[0][0] == 'testing', cmd_list[0][0]
341         assert cmd_list[0][1] == 'foo', cmd_list[0][1]
342         assert cmd_list[0][2] == 'bar with spaces.out', cmd_list[0][2]
343         assert cmd_list[0][3] == 't', cmd_list[0][3]
344         assert cmd_list[0][4] == 's', cmd_list[0][4]
345
346
347         # Test against a former bug in scons_subst_list()
348         glob = { "FOO" : "$BAR",
349                  "BAR" : "BAZ",
350                  "BLAT" : "XYX",
351                  "BARXYX" : "BADNEWS" }
352         cmd_list = scons_subst_list("$FOO$BLAT", DummyEnv(glob))
353         assert cmd_list[0][0] == "BAZXYX", cmd_list[0][0]
354
355         # Test for double-dollar-sign behavior
356         glob = { "FOO" : "BAR",
357                  "BAZ" : "BLAT" }
358         cmd_list = scons_subst_list("$$FOO$BAZ", DummyEnv(glob))
359         assert cmd_list[0][0] == "$FOOBLAT", cmd_list[0][0]
360
361         # Now test escape functionality
362         def escape_func(foo):
363             return '**' + foo + '**'
364         def quote_func(foo):
365             return foo
366         glob = { "FOO" : [ Literal('foo\nwith\nnewlines'),
367                            Literal('bar\nwith\nnewlines') ] }
368         cmd_list = scons_subst_list("$FOO", DummyEnv(glob))
369         assert cmd_list[0][0] == 'foo\nwith\nnewlines', cmd_list[0][0]
370         cmd_list[0][0].escape(escape_func)
371         assert cmd_list[0][0] == '**foo\nwith\nnewlines**', cmd_list[0][0]
372         assert cmd_list[0][1] == 'bar\nwith\nnewlines', cmd_list[0][0]
373         cmd_list[0][1].escape(escape_func)
374         assert cmd_list[0][1] == '**bar\nwith\nnewlines**', cmd_list[0][0]
375
376     def test_quote_spaces(self):
377         """Testing the quote_spaces() method..."""
378         q = quote_spaces('x')
379         assert q == 'x', q
380
381         q = quote_spaces('x x')
382         assert q == '"x x"', q
383
384         q = quote_spaces('x\tx')
385         assert q == '"x\tx"', q
386
387     def test_render_tree(self):
388         class Node:
389             def __init__(self, name, children=[]):
390                 self.children = children
391                 self.name = name
392             def __str__(self):
393                 return self.name
394
395         def get_children(node):
396             return node.children
397
398         windows_h = Node("windows.h")
399         stdlib_h = Node("stdlib.h")
400         stdio_h = Node("stdio.h")
401         bar_c = Node("bar.c", [stdlib_h, windows_h])
402         bar_o = Node("bar.o", [bar_c])
403         foo_c = Node("foo.c", [stdio_h])
404         foo_o = Node("foo.o", [foo_c])
405         foo = Node("foo", [foo_o, bar_o])
406
407         expect = """\
408 +-foo
409   +-foo.o
410   | +-foo.c
411   |   +-stdio.h
412   +-bar.o
413     +-bar.c
414       +-stdlib.h
415       +-windows.h
416 """
417
418         actual = render_tree(foo, get_children)
419         assert expect == actual, (expect, actual)
420         
421         bar_h = Node('bar.h', [stdlib_h])
422         blat_h = Node('blat.h', [stdlib_h])
423         blat_c = Node('blat.c', [blat_h, bar_h])
424         blat_o = Node('blat.o', [blat_c])
425
426         expect = """\
427 +-blat.o
428   +-blat.c
429     +-blat.h
430     | +-stdlib.h
431     +-bar.h
432 """
433
434         actual = render_tree(blat_o, get_children, 1)
435         assert expect == actual, (expect, actual)        
436
437     def test_is_Dict(self):
438         assert is_Dict({})
439         import UserDict
440         assert is_Dict(UserDict.UserDict())
441         assert not is_Dict([])
442         assert not is_Dict("")
443         if hasattr(types, 'UnicodeType'):
444             exec "assert not is_Dict(u'')"
445
446     def test_is_List(self):
447         assert is_List([])
448         import UserList
449         assert is_List(UserList.UserList())
450         assert not is_List({})
451         assert not is_List("")
452         if hasattr(types, 'UnicodeType'):
453             exec "assert not is_List(u'')"
454
455     def test_Split(self):
456         assert Split("foo bar") == ["foo", "bar"]
457         assert Split(["foo", "bar"]) == ["foo", "bar"]
458         assert Split("foo") == ["foo"]
459
460     def test_is_String(self):
461         assert is_String("")
462         if hasattr(types, 'UnicodeType'):
463             exec "assert is_String(u'')"
464         try:
465             import UserString
466         except:
467             pass
468         else:
469             assert is_String(UserString.UserString(''))
470         assert not is_String({})
471         assert not is_String([])
472
473     def test_to_String(self):
474         """Test the to_String() method."""
475         assert to_String(1) == "1", to_String(1)
476         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
477         assert to_String("foo") == "foo", to_String("foo")
478
479         try:
480             import UserString
481
482             s1=UserString.UserString('blah')
483             assert to_String(s1) == s1, s1
484             assert to_String(s1) == 'blah', s1
485
486             class Derived(UserString.UserString):
487                 pass
488             s2 = Derived('foo')
489             assert to_String(s2) == s2, s2
490             assert to_String(s2) == 'foo', s2
491
492             if hasattr(types, 'UnicodeType'):
493                 s3=UserString.UserString(unicode('bar'))
494                 assert to_String(s3) == s3, s3
495                 assert to_String(s3) == unicode('bar'), s3
496                 assert type(to_String(s3)) is types.UnicodeType, \
497                        type(to_String(s3))
498         except ImportError:
499             pass
500
501         if hasattr(types, 'UnicodeType'):
502             s4 = unicode('baz')
503             assert to_String(s4) == unicode('baz'), to_String(s4)
504             assert type(to_String(s4)) is types.UnicodeType, \
505                    type(to_String(s4))
506
507     def test_WhereIs(self):
508         test = TestCmd.TestCmd(workdir = '')
509
510         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
511         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
512         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
513         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
514
515         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
516
517         if sys.platform != 'win32':
518             test.write(sub1_xxx_exe, "\n")
519
520         os.mkdir(sub2_xxx_exe)
521
522         test.write(sub3_xxx_exe, "\n")
523         os.chmod(sub3_xxx_exe, 0777)
524
525         test.write(sub4_xxx_exe, "\n")
526         os.chmod(sub4_xxx_exe, 0777)
527
528         env_path = os.environ['PATH']
529
530         pathdirs_1234 = [ test.workpath('sub1'),
531                           test.workpath('sub2'),
532                           test.workpath('sub3'),
533                           test.workpath('sub4'),
534                         ] + string.split(env_path, os.pathsep)
535
536         pathdirs_1243 = [ test.workpath('sub1'),
537                           test.workpath('sub2'),
538                           test.workpath('sub4'),
539                           test.workpath('sub3'),
540                         ] + string.split(env_path, os.pathsep)
541
542         os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
543         wi = WhereIs('xxx.exe')
544         assert wi == test.workpath(sub3_xxx_exe), wi
545         wi = WhereIs('xxx.exe', pathdirs_1243)
546         assert wi == test.workpath(sub4_xxx_exe), wi
547         wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
548         assert wi == test.workpath(sub4_xxx_exe), wi
549
550         os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
551         wi = WhereIs('xxx.exe')
552         assert wi == test.workpath(sub4_xxx_exe), wi
553         wi = WhereIs('xxx.exe', pathdirs_1234)
554         assert wi == test.workpath(sub3_xxx_exe), wi
555         wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
556         assert wi == test.workpath(sub3_xxx_exe), wi
557
558         if sys.platform == 'win32':
559             wi = WhereIs('xxx', pathext = '')
560             assert wi is None, wi
561
562             wi = WhereIs('xxx', pathext = '.exe')
563             assert wi == test.workpath(sub4_xxx_exe), wi
564
565             wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
566             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
567
568             # Test that we return a normalized path even when
569             # the path contains forward slashes.
570             forward_slash = test.workpath('') + '/sub3'
571             wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
572             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
573
574     def test_is_valid_construction_var(self):
575         """Testing is_valid_construction_var()"""
576         r = is_valid_construction_var("_a")
577         assert not r is None, r
578         r = is_valid_construction_var("z_")
579         assert not r is None, r
580         r = is_valid_construction_var("X_")
581         assert not r is None, r
582         r = is_valid_construction_var("2a")
583         assert r is None, r
584         r = is_valid_construction_var("a2_")
585         assert not r is None, r
586         r = is_valid_construction_var("/")
587         assert r is None, r
588         r = is_valid_construction_var("_/")
589         assert r is None, r
590         r = is_valid_construction_var("a/")
591         assert r is None, r
592         r = is_valid_construction_var(".b")
593         assert r is None, r
594         r = is_valid_construction_var("_.b")
595         assert r is None, r
596         r = is_valid_construction_var("b1._")
597         assert r is None, r
598         r = is_valid_construction_var("-b")
599         assert r is None, r
600         r = is_valid_construction_var("_-b")
601         assert r is None, r
602         r = is_valid_construction_var("b1-_")
603         assert r is None, r
604
605     def test_get_env_var(self):
606         """Testing get_environment_var()."""
607         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
608         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
609         assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
610         assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
611         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
612         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
613         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
614         assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
615         assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
616
617     def test_Proxy(self):
618         """Test generic Proxy class."""
619         class Subject:
620             def foo(self):
621                 return 1
622             def bar(self):
623                 return 2
624
625         s=Subject()
626         s.baz = 3
627
628         class ProxyTest(Proxy):
629             def bar(self):
630                 return 4
631
632         p=ProxyTest(s)
633
634         assert p.foo() == 1, p.foo()
635         assert p.bar() == 4, p.bar()
636         assert p.baz == 3, p.baz
637
638         p.baz = 5
639         s.baz = 6
640
641         assert p.baz == 5, p.baz
642         assert p.get() == s, p.get()
643
644     def test_Literal(self):
645         """Test the Literal() function."""
646         cmd_list = [ '$FOO', Literal('$BAR') ]
647         cmd_list = scons_subst_list(cmd_list,
648                                     DummyEnv({ 'FOO' : 'BAZ',
649                                                'BAR' : 'BLAT' }))
650         def escape_func(cmd):
651             return '**' + cmd + '**'
652
653         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
654         cmd_list = map(str, cmd_list[0])
655         assert cmd_list[0] == 'BAZ', cmd_list[0]
656         assert cmd_list[1] == '**$BAR**', cmd_list[1]
657
658     def test_SpecialAttrWrapper(self):
659         """Test the SpecialAttrWrapper() function."""
660         input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
661         
662         def escape_func(cmd):
663             return '**' + cmd + '**'
664
665         
666         cmd_list = scons_subst_list(input_list,
667                                     DummyEnv({ 'FOO' : 'BAZ',
668                                                'BAR' : 'BLAT' }))
669         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
670         cmd_list = map(str, cmd_list[0])
671         assert cmd_list[0] == 'BAZ', cmd_list[0]
672         assert cmd_list[1] == '**$BAR**', cmd_list[1]
673
674         cmd_list = scons_subst_list(input_list,
675                                     DummyEnv({ 'FOO' : 'BAZ',
676                                                'BAR' : 'BLAT' }),
677                                     mode=SUBST_SIG)
678         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
679         cmd_list = map(str, cmd_list[0])
680         assert cmd_list[0] == 'BAZ', cmd_list[0]
681         assert cmd_list[1] == '**BLEH**', cmd_list[1]
682
683     def test_mapPaths(self):
684         """Test the mapPaths function"""
685         class MyFileNode:
686             def __init__(self, path):
687                 self.path = path
688             def __str__(self):
689                 return self.path
690             
691         dir=MyFileNode('foo')
692         file=MyFileNode('bar/file')
693         
694         class DummyEnv:
695             def subst(self, arg):
696                 return 'bar'
697
698         res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
699         assert res[0] == file, res[0]
700         assert res[1] == os.path.join('foo', 'baz'), res[1]
701         assert res[2] == os.path.join('foo', 'blat/boo'), res[2]
702         assert res[3] == '#test', res[3]
703
704         env=DummyEnv()
705         res=mapPaths('bleh', dir, env)
706         assert res[0] == os.path.normpath('foo/bar'), res[1]
707
708     def test_display(self):
709         old_stdout = sys.stdout
710         sys.stdout = OutBuffer()
711         display("line1")
712         display.set_mode(0)
713         display("line2")
714         display.set_mode(1)
715         display("line3")
716
717         assert sys.stdout.buffer == "line1\nline3\n"
718         sys.stdout = old_stdout
719
720     def test_fs_delete(self):
721         test = TestCmd.TestCmd(workdir = '')
722         base = test.workpath('')
723         xxx = test.workpath('xxx.xxx')
724         ZZZ = test.workpath('ZZZ.ZZZ')
725         sub1_yyy = test.workpath('sub1', 'yyy.yyy')
726         
727         test.subdir('sub1')
728         test.write(xxx, "\n")
729         test.write(ZZZ, "\n")
730         test.write(sub1_yyy, "\n")
731
732         old_stdout = sys.stdout
733         sys.stdout = OutBuffer()
734
735         exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
736               "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
737               "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
738               "Removed " + os.path.join(base, xxx) + '\n' + \
739               "Removed directory " + base + '\n'
740
741         fs_delete(base, remove=0)
742         assert sys.stdout.buffer == exp, sys.stdout.buffer
743         assert os.path.exists(sub1_yyy)
744
745         sys.stdout.buffer = ""
746         fs_delete(base, remove=1)
747         assert sys.stdout.buffer == exp
748         assert not os.path.exists(base)
749
750         test._dirlist = None
751         sys.stdout = old_stdout
752
753     def test_get_native_path(self):
754         """Test the get_native_path() function."""
755         import tempfile
756         filename = tempfile.mktemp()
757         str = '1234567890 ' + filename
758         open(filename, 'w').write(str)
759         assert open(get_native_path(filename)).read() == str
760
761     def test_subst_dict(self):
762         """Test substituting dictionary values in an Action
763         """
764         env = DummyEnv({'a' : 'A', 'b' : 'B'})
765         d = subst_dict([], [], env)
766         assert d['__env__'] is env, d['__env__']
767
768         class SimpleNode:
769             def __init__(self, data):
770                 self.data = data
771             def __str__(self):
772                 return self.data
773             def rfile(self):
774                 return self
775             def is_literal(self):
776                 return 1
777             def get_subst_proxy(self):
778                 return self
779             
780         d = subst_dict(target = SimpleNode('t'), source = SimpleNode('s'), env=DummyEnv())
781         assert str(d['TARGETS'][0]) == 't', d['TARGETS']
782         assert str(d['TARGET']) == 't', d['TARGET']
783         assert str(d['SOURCES'][0]) == 's', d['SOURCES']
784         assert str(d['SOURCE']) == 's', d['SOURCE']
785
786         d = subst_dict(target = [SimpleNode('t1'), SimpleNode('t2')],
787                        source = [SimpleNode('s1'), SimpleNode('s2')],
788                        env = DummyEnv())
789         TARGETS = map(lambda x: str(x), d['TARGETS'])
790         TARGETS.sort()
791         assert TARGETS == ['t1', 't2'], d['TARGETS']
792         assert str(d['TARGET']) == 't1', d['TARGET']
793         SOURCES = map(lambda x: str(x), d['SOURCES'])
794         SOURCES.sort()
795         assert SOURCES == ['s1', 's2'], d['SOURCES']
796         assert str(d['SOURCE']) == 's1', d['SOURCE']
797
798         class N:
799             def __init__(self, name):
800                 self.name = name
801             def __str__(self):
802                 return self.name
803             def rfile(self):
804                 return self.__class__('rstr-' + self.name)
805             def get_subst_proxy(self):
806                 return self
807
808         d = subst_dict(target = [N('t3'), SimpleNode('t4')],
809                        source = [SimpleNode('s3'), N('s4')],
810                        env = DummyEnv())
811         TARGETS = map(lambda x: str(x), d['TARGETS'])
812         TARGETS.sort()
813         assert TARGETS == ['t3', 't4'], d['TARGETS']
814         SOURCES = map(lambda x: str(x), d['SOURCES'])
815         SOURCES.sort()
816         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
817
818     def test_NodeList(self):
819         """Test NodeList class"""
820         class TestClass:
821             def __init__(self, name, child=None):
822                 self.child = child
823                 self.bar = name
824             def foo(self):
825                 return self.bar + "foo"
826             def getself(self):
827                 return self
828
829         t1 = TestClass('t1', TestClass('t1child'))
830         t2 = TestClass('t2', TestClass('t2child'))
831         t3 = TestClass('t3')
832
833         nl = NodeList([t1, t2, t3])
834         assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
835         assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
836         assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
837         assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
838                nl[0:2].child.foo()
839         assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
840                nl[0:2].child.bar
841
842 if __name__ == "__main__":
843     suite = unittest.makeSuite(UtilTestCase, 'test_')
844     if not unittest.TextTestRunner().run(suite).wasSuccessful():
845         sys.exit(1)