Add more environment methods for global functions: Action(), Builder(), Environment...
[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         newcom = scons_subst("test $xxx", env, mode=SUBST_CMD)
177         assert newcom == cvt("test"), newcom
178         newcom = scons_subst("test $xxx", env, mode=SUBST_SIG)
179         assert newcom == cvt("test"), newcom
180
181         newcom = scons_subst("test $($xxx$)", env)
182         assert newcom == cvt("test $($)"), newcom
183         newcom = scons_subst("test $($xxx$)", env, mode=SUBST_CMD)
184         assert newcom == cvt("test"), newcom
185         newcom = scons_subst("test $($xxx$)", env, mode=SUBST_SIG)
186         assert newcom == cvt("test"), newcom
187
188         newcom = scons_subst("test $( $xxx $)", env)
189         assert newcom == cvt("test $(  $)"), newcom
190         newcom = scons_subst("test $( $xxx $)", env, mode=SUBST_CMD)
191         assert newcom == cvt("test"), newcom
192         newcom = scons_subst("test $( $xxx $)", env, mode=SUBST_SIG)
193         assert newcom == cvt("test"), newcom
194
195         newcom = scons_subst("test $zero", env)
196         assert newcom == cvt("test 0"), newcom
197
198         newcom = scons_subst("test $one", env)
199         assert newcom == cvt("test 1"), newcom
200
201         newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
202                              env, target=N('t'), source=N('s'))
203         assert newcom == cvt("test foo baz s t"), newcom
204
205         # Test against a former bug in scons_subst_list()
206         glob = { "FOO" : "$BAR",
207                  "BAR" : "BAZ",
208                  "BLAT" : "XYX",
209                  "BARXYX" : "BADNEWS" }
210         newcom = scons_subst("$FOO$BLAT", DummyEnv(glob))
211         assert newcom == "BAZXYX", newcom
212
213         # Test for double-dollar-sign behavior
214         glob = { "FOO" : "BAR",
215                  "BAZ" : "BLAT" }
216         newcom = scons_subst("$$FOO$BAZ", DummyEnv(glob))
217         assert newcom == "$FOOBLAT", newcom
218
219         class TestLiteral:
220             def __init__(self, literal):
221                 self.literal = literal
222
223             def __str__(self):
224                 return self.literal
225
226             def is_literal(self):
227                 return 1
228
229         # Test that a literal will stop dollar-sign substitution
230         glob = { "FOO" : "BAR",
231                  "BAZ" : TestLiteral("$FOO"),
232                  "BAR" : "$FOO" }
233         newcom = scons_subst("$FOO $BAZ $BAR", DummyEnv(glob))
234         assert newcom == "BAR $FOO BAR", newcom
235
236         # Test that we don't blow up even if they subscript something
237         # in ways they "can't."
238         glob = { "FOO" : "BAR",
239                  "NOTHING" : "" ,
240                  "NONE" : None }
241         newcom = scons_subst("${FOO[0]}", DummyEnv(glob))
242         assert newcom == "B", newcom
243         newcom = scons_subst("${FOO[7]}", DummyEnv(glob))
244         assert newcom == "", newcom
245         newcom = scons_subst("${NOTHING[1]}", DummyEnv(glob))
246         assert newcom == "", newcom
247         newcom = scons_subst("${NONE[2]}", DummyEnv(glob))
248         assert newcom == "", newcom
249
250     def test_splitext(self):
251         assert splitext('foo') == ('foo','')
252         assert splitext('foo.bar') == ('foo','.bar')
253         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
254
255     def test_subst_list(self):
256         """Testing the scons_subst_list() method..."""
257
258         class Node:
259             def __init__(self, name):
260                 self.name = os.path.normpath(name)
261             def __str__(self):
262                 return self.name
263             def is_literal(self):
264                 return 1
265             def rfile(self):
266                 return self
267             def get_subst_proxy(self):
268                 return self
269         
270         loc = {}
271         target = [ Node("./foo/bar.exe"),
272                    Node("/bar/baz with spaces.obj"),
273                    Node("../foo/baz.obj") ]
274         source = [ Node("./foo/blah with spaces.cpp"),
275                    Node("/bar/ack.cpp"),
276                    Node("../foo/ack.c") ]
277         loc['xxx'] = None
278         loc['NEWLINE'] = 'before\nafter'
279
280         loc['DO'] = Node('do something')
281         loc['FOO'] = Node('foo.in')
282         loc['BAR'] = Node('bar with spaces.out')
283         loc['CRAZY'] = Node('crazy\nfile.in')
284
285         loc['CMDGEN1'] = CmdGen1
286         loc['CMDGEN2'] = CmdGen2
287
288         env = DummyEnv(loc)
289
290         if os.sep == '/':
291             def cvt(str):
292                 return str
293         else:
294             def cvt(str):
295                 return string.replace(str, '/', os.sep)
296
297         cmd_list = scons_subst_list("$TARGETS", env,
298                                     target=target,
299                                     source=source)
300         assert cmd_list[0][1] == cvt("/bar/baz with spaces.obj"), cmd_list[0][1]
301
302         cmd_list = scons_subst_list("$SOURCES $NEWLINE $TARGETS", env,
303                                     target=target,
304                                     source=source)
305         assert len(cmd_list) == 2, cmd_list
306         assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
307         assert cmd_list[1][2] == cvt("/bar/baz with spaces.obj"), cmd_list[1]
308
309         cmd_list = scons_subst_list("$SOURCES$NEWLINE", env,
310                                     target=target,
311                                     source=source)
312         assert len(cmd_list) == 2, cmd_list
313         assert cmd_list[1][0] == 'after', cmd_list[1][0]
314         assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
315
316         cmd_list = scons_subst_list("$DO --in=$FOO --out=$BAR", env)
317         assert len(cmd_list) == 1, cmd_list
318         assert len(cmd_list[0]) == 3, cmd_list
319         assert cmd_list[0][0] == 'do something', cmd_list[0][0]
320         assert cmd_list[0][1] == '--in=foo.in', cmd_list[0][1]
321         assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
322
323         # This test is now fixed, and works like it should.
324         cmd_list = scons_subst_list("$DO --in=$CRAZY --out=$BAR", env)
325         assert len(cmd_list) == 1, map(str, cmd_list[0])
326         assert len(cmd_list[0]) == 3, cmd_list
327         assert cmd_list[0][0] == 'do something', cmd_list[0][0]
328         assert cmd_list[0][1] == '--in=crazy\nfile.in', cmd_list[0][1]
329         assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
330         
331         # Test inputting a list to scons_subst_list()
332         cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
333                                         "This is a test" ],
334                                     env,
335                                     target=target,
336                                     source=source)
337         assert len(cmd_list) == 2, len(cmd_list)
338         assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
339         assert cmd_list[1][0] == cvt("after"), cmd_list[1]
340         assert cmd_list[1][4] == "This is a test", cmd_list[1]
341
342         # Test interpolating a callable.
343         cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES", env,
344                                     target=Node('t'), source=Node('s'))
345         assert len(cmd_list) == 1, len(cmd_list)
346         assert cmd_list[0][0] == 'testing', cmd_list[0][0]
347         assert cmd_list[0][1] == 'foo', cmd_list[0][1]
348         assert cmd_list[0][2] == 'bar with spaces.out', cmd_list[0][2]
349         assert cmd_list[0][3] == 't', cmd_list[0][3]
350         assert cmd_list[0][4] == 's', cmd_list[0][4]
351
352
353         # Test against a former bug in scons_subst_list()
354         glob = { "FOO" : "$BAR",
355                  "BAR" : "BAZ",
356                  "BLAT" : "XYX",
357                  "BARXYX" : "BADNEWS" }
358         cmd_list = scons_subst_list("$FOO$BLAT", DummyEnv(glob))
359         assert cmd_list[0][0] == "BAZXYX", cmd_list[0][0]
360
361         # Test for double-dollar-sign behavior
362         glob = { "FOO" : "BAR",
363                  "BAZ" : "BLAT" }
364         cmd_list = scons_subst_list("$$FOO$BAZ", DummyEnv(glob))
365         assert cmd_list[0][0] == "$FOOBLAT", cmd_list[0][0]
366
367         # Now test escape functionality
368         def escape_func(foo):
369             return '**' + foo + '**'
370         def quote_func(foo):
371             return foo
372         glob = { "FOO" : [ Literal('foo\nwith\nnewlines'),
373                            Literal('bar\nwith\nnewlines') ] }
374         cmd_list = scons_subst_list("$FOO", DummyEnv(glob))
375         assert cmd_list[0][0] == 'foo\nwith\nnewlines', cmd_list[0][0]
376         cmd_list[0][0].escape(escape_func)
377         assert cmd_list[0][0] == '**foo\nwith\nnewlines**', cmd_list[0][0]
378         assert cmd_list[0][1] == 'bar\nwith\nnewlines', cmd_list[0][0]
379         cmd_list[0][1].escape(escape_func)
380         assert cmd_list[0][1] == '**bar\nwith\nnewlines**', cmd_list[0][0]
381
382     def test_quote_spaces(self):
383         """Testing the quote_spaces() method..."""
384         q = quote_spaces('x')
385         assert q == 'x', q
386
387         q = quote_spaces('x x')
388         assert q == '"x x"', q
389
390         q = quote_spaces('x\tx')
391         assert q == '"x\tx"', q
392
393     def test_render_tree(self):
394         class Node:
395             def __init__(self, name, children=[]):
396                 self.children = children
397                 self.name = name
398             def __str__(self):
399                 return self.name
400
401         def get_children(node):
402             return node.children
403
404         windows_h = Node("windows.h")
405         stdlib_h = Node("stdlib.h")
406         stdio_h = Node("stdio.h")
407         bar_c = Node("bar.c", [stdlib_h, windows_h])
408         bar_o = Node("bar.o", [bar_c])
409         foo_c = Node("foo.c", [stdio_h])
410         foo_o = Node("foo.o", [foo_c])
411         foo = Node("foo", [foo_o, bar_o])
412
413         expect = """\
414 +-foo
415   +-foo.o
416   | +-foo.c
417   |   +-stdio.h
418   +-bar.o
419     +-bar.c
420       +-stdlib.h
421       +-windows.h
422 """
423
424         actual = render_tree(foo, get_children)
425         assert expect == actual, (expect, actual)
426         
427         bar_h = Node('bar.h', [stdlib_h])
428         blat_h = Node('blat.h', [stdlib_h])
429         blat_c = Node('blat.c', [blat_h, bar_h])
430         blat_o = Node('blat.o', [blat_c])
431
432         expect = """\
433 +-blat.o
434   +-blat.c
435     +-blat.h
436     | +-stdlib.h
437     +-bar.h
438 """
439
440         actual = render_tree(blat_o, get_children, 1)
441         assert expect == actual, (expect, actual)        
442
443     def test_is_Dict(self):
444         assert is_Dict({})
445         import UserDict
446         assert is_Dict(UserDict.UserDict())
447         assert not is_Dict([])
448         assert not is_Dict("")
449         if hasattr(types, 'UnicodeType'):
450             exec "assert not is_Dict(u'')"
451
452     def test_is_List(self):
453         assert is_List([])
454         import UserList
455         assert is_List(UserList.UserList())
456         assert not is_List({})
457         assert not is_List("")
458         if hasattr(types, 'UnicodeType'):
459             exec "assert not is_List(u'')"
460
461     def test_is_String(self):
462         assert is_String("")
463         if hasattr(types, 'UnicodeType'):
464             exec "assert is_String(u'')"
465         try:
466             import UserString
467         except:
468             pass
469         else:
470             assert is_String(UserString.UserString(''))
471         assert not is_String({})
472         assert not is_String([])
473
474     def test_to_String(self):
475         """Test the to_String() method."""
476         assert to_String(1) == "1", to_String(1)
477         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
478         assert to_String("foo") == "foo", to_String("foo")
479
480         try:
481             import UserString
482
483             s1=UserString.UserString('blah')
484             assert to_String(s1) == s1, s1
485             assert to_String(s1) == 'blah', s1
486
487             class Derived(UserString.UserString):
488                 pass
489             s2 = Derived('foo')
490             assert to_String(s2) == s2, s2
491             assert to_String(s2) == 'foo', s2
492
493             if hasattr(types, 'UnicodeType'):
494                 s3=UserString.UserString(unicode('bar'))
495                 assert to_String(s3) == s3, s3
496                 assert to_String(s3) == unicode('bar'), s3
497                 assert type(to_String(s3)) is types.UnicodeType, \
498                        type(to_String(s3))
499         except ImportError:
500             pass
501
502         if hasattr(types, 'UnicodeType'):
503             s4 = unicode('baz')
504             assert to_String(s4) == unicode('baz'), to_String(s4)
505             assert type(to_String(s4)) is types.UnicodeType, \
506                    type(to_String(s4))
507
508     def test_WhereIs(self):
509         test = TestCmd.TestCmd(workdir = '')
510
511         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
512         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
513         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
514         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
515
516         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
517
518         if sys.platform != 'win32':
519             test.write(sub1_xxx_exe, "\n")
520
521         os.mkdir(sub2_xxx_exe)
522
523         test.write(sub3_xxx_exe, "\n")
524         os.chmod(sub3_xxx_exe, 0777)
525
526         test.write(sub4_xxx_exe, "\n")
527         os.chmod(sub4_xxx_exe, 0777)
528
529         env_path = os.environ['PATH']
530
531         pathdirs_1234 = [ test.workpath('sub1'),
532                           test.workpath('sub2'),
533                           test.workpath('sub3'),
534                           test.workpath('sub4'),
535                         ] + string.split(env_path, os.pathsep)
536
537         pathdirs_1243 = [ test.workpath('sub1'),
538                           test.workpath('sub2'),
539                           test.workpath('sub4'),
540                           test.workpath('sub3'),
541                         ] + string.split(env_path, os.pathsep)
542
543         os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
544         wi = WhereIs('xxx.exe')
545         assert wi == test.workpath(sub3_xxx_exe), wi
546         wi = WhereIs('xxx.exe', pathdirs_1243)
547         assert wi == test.workpath(sub4_xxx_exe), wi
548         wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
549         assert wi == test.workpath(sub4_xxx_exe), wi
550
551         os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
552         wi = WhereIs('xxx.exe')
553         assert wi == test.workpath(sub4_xxx_exe), wi
554         wi = WhereIs('xxx.exe', pathdirs_1234)
555         assert wi == test.workpath(sub3_xxx_exe), wi
556         wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
557         assert wi == test.workpath(sub3_xxx_exe), wi
558
559         if sys.platform == 'win32':
560             wi = WhereIs('xxx', pathext = '')
561             assert wi is None, wi
562
563             wi = WhereIs('xxx', pathext = '.exe')
564             assert wi == test.workpath(sub4_xxx_exe), wi
565
566             wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
567             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
568
569             # Test that we return a normalized path even when
570             # the path contains forward slashes.
571             forward_slash = test.workpath('') + '/sub3'
572             wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
573             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
574
575     def test_is_valid_construction_var(self):
576         """Testing is_valid_construction_var()"""
577         r = is_valid_construction_var("_a")
578         assert not r is None, r
579         r = is_valid_construction_var("z_")
580         assert not r is None, r
581         r = is_valid_construction_var("X_")
582         assert not r is None, r
583         r = is_valid_construction_var("2a")
584         assert r is None, r
585         r = is_valid_construction_var("a2_")
586         assert not r is None, r
587         r = is_valid_construction_var("/")
588         assert r is None, r
589         r = is_valid_construction_var("_/")
590         assert r is None, r
591         r = is_valid_construction_var("a/")
592         assert r is None, r
593         r = is_valid_construction_var(".b")
594         assert r is None, r
595         r = is_valid_construction_var("_.b")
596         assert r is None, r
597         r = is_valid_construction_var("b1._")
598         assert r is None, r
599         r = is_valid_construction_var("-b")
600         assert r is None, r
601         r = is_valid_construction_var("_-b")
602         assert r is None, r
603         r = is_valid_construction_var("b1-_")
604         assert r is None, r
605
606     def test_get_env_var(self):
607         """Testing get_environment_var()."""
608         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
609         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
610         assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
611         assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
612         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
613         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
614         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
615         assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
616         assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
617
618     def test_Proxy(self):
619         """Test generic Proxy class."""
620         class Subject:
621             def foo(self):
622                 return 1
623             def bar(self):
624                 return 2
625
626         s=Subject()
627         s.baz = 3
628
629         class ProxyTest(Proxy):
630             def bar(self):
631                 return 4
632
633         p=ProxyTest(s)
634
635         assert p.foo() == 1, p.foo()
636         assert p.bar() == 4, p.bar()
637         assert p.baz == 3, p.baz
638
639         p.baz = 5
640         s.baz = 6
641
642         assert p.baz == 5, p.baz
643         assert p.get() == s, p.get()
644
645     def test_Literal(self):
646         """Test the Literal() function."""
647         cmd_list = [ '$FOO', Literal('$BAR') ]
648         cmd_list = scons_subst_list(cmd_list,
649                                     DummyEnv({ 'FOO' : 'BAZ',
650                                                'BAR' : 'BLAT' }))
651         def escape_func(cmd):
652             return '**' + cmd + '**'
653
654         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
655         cmd_list = map(str, cmd_list[0])
656         assert cmd_list[0] == 'BAZ', cmd_list[0]
657         assert cmd_list[1] == '**$BAR**', cmd_list[1]
658
659     def test_SpecialAttrWrapper(self):
660         """Test the SpecialAttrWrapper() function."""
661         input_list = [ '$FOO', SpecialAttrWrapper('$BAR', 'BLEH') ]
662         
663         def escape_func(cmd):
664             return '**' + cmd + '**'
665
666         
667         cmd_list = scons_subst_list(input_list,
668                                     DummyEnv({ 'FOO' : 'BAZ',
669                                                'BAR' : 'BLAT' }))
670         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
671         cmd_list = map(str, cmd_list[0])
672         assert cmd_list[0] == 'BAZ', cmd_list[0]
673         assert cmd_list[1] == '**$BAR**', cmd_list[1]
674
675         cmd_list = scons_subst_list(input_list,
676                                     DummyEnv({ 'FOO' : 'BAZ',
677                                                'BAR' : 'BLAT' }),
678                                     mode=SUBST_SIG)
679         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
680         cmd_list = map(str, cmd_list[0])
681         assert cmd_list[0] == 'BAZ', cmd_list[0]
682         assert cmd_list[1] == '**BLEH**', cmd_list[1]
683
684     def test_mapPaths(self):
685         """Test the mapPaths function"""
686         class MyFileNode:
687             def __init__(self, path):
688                 self.path = path
689             def __str__(self):
690                 return self.path
691             
692         dir=MyFileNode('foo')
693         file=MyFileNode('bar/file')
694         
695         class DummyEnv:
696             def subst(self, arg):
697                 return 'bar'
698
699         res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
700         assert res[0] == file, res[0]
701         assert res[1] == os.path.join('foo', 'baz'), res[1]
702         assert res[2] == os.path.join('foo', 'blat/boo'), res[2]
703         assert res[3] == '#test', res[3]
704
705         env=DummyEnv()
706         res=mapPaths('bleh', dir, env)
707         assert res[0] == os.path.normpath('foo/bar'), res[1]
708
709     def test_display(self):
710         old_stdout = sys.stdout
711         sys.stdout = OutBuffer()
712         display("line1")
713         display.set_mode(0)
714         display("line2")
715         display.set_mode(1)
716         display("line3")
717
718         assert sys.stdout.buffer == "line1\nline3\n"
719         sys.stdout = old_stdout
720
721     def test_fs_delete(self):
722         test = TestCmd.TestCmd(workdir = '')
723         base = test.workpath('')
724         xxx = test.workpath('xxx.xxx')
725         ZZZ = test.workpath('ZZZ.ZZZ')
726         sub1_yyy = test.workpath('sub1', 'yyy.yyy')
727         
728         test.subdir('sub1')
729         test.write(xxx, "\n")
730         test.write(ZZZ, "\n")
731         test.write(sub1_yyy, "\n")
732
733         old_stdout = sys.stdout
734         sys.stdout = OutBuffer()
735
736         exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
737               "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
738               "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
739               "Removed " + os.path.join(base, xxx) + '\n' + \
740               "Removed directory " + base + '\n'
741
742         fs_delete(base, remove=0)
743         assert sys.stdout.buffer == exp, sys.stdout.buffer
744         assert os.path.exists(sub1_yyy)
745
746         sys.stdout.buffer = ""
747         fs_delete(base, remove=1)
748         assert sys.stdout.buffer == exp
749         assert not os.path.exists(base)
750
751         test._dirlist = None
752         sys.stdout = old_stdout
753
754     def test_get_native_path(self):
755         """Test the get_native_path() function."""
756         import tempfile
757         filename = tempfile.mktemp()
758         str = '1234567890 ' + filename
759         open(filename, 'w').write(str)
760         assert open(get_native_path(filename)).read() == str
761
762     def test_subst_dict(self):
763         """Test substituting dictionary values in an Action
764         """
765         env = DummyEnv({'a' : 'A', 'b' : 'B'})
766         d = subst_dict([], [], env)
767         assert d['__env__'] is env, d['__env__']
768
769         class SimpleNode:
770             def __init__(self, data):
771                 self.data = data
772             def __str__(self):
773                 return self.data
774             def rfile(self):
775                 return self
776             def is_literal(self):
777                 return 1
778             def get_subst_proxy(self):
779                 return self
780             
781         d = subst_dict(target = SimpleNode('t'), source = SimpleNode('s'), env=DummyEnv())
782         assert str(d['TARGETS'][0]) == 't', d['TARGETS']
783         assert str(d['TARGET']) == 't', d['TARGET']
784         assert str(d['SOURCES'][0]) == 's', d['SOURCES']
785         assert str(d['SOURCE']) == 's', d['SOURCE']
786
787         d = subst_dict(target = [SimpleNode('t1'), SimpleNode('t2')],
788                        source = [SimpleNode('s1'), SimpleNode('s2')],
789                        env = DummyEnv())
790         TARGETS = map(lambda x: str(x), d['TARGETS'])
791         TARGETS.sort()
792         assert TARGETS == ['t1', 't2'], d['TARGETS']
793         assert str(d['TARGET']) == 't1', d['TARGET']
794         SOURCES = map(lambda x: str(x), d['SOURCES'])
795         SOURCES.sort()
796         assert SOURCES == ['s1', 's2'], d['SOURCES']
797         assert str(d['SOURCE']) == 's1', d['SOURCE']
798
799         class N:
800             def __init__(self, name):
801                 self.name = name
802             def __str__(self):
803                 return self.name
804             def rfile(self):
805                 return self.__class__('rstr-' + self.name)
806             def get_subst_proxy(self):
807                 return self
808
809         d = subst_dict(target = [N('t3'), SimpleNode('t4')],
810                        source = [SimpleNode('s3'), N('s4')],
811                        env = DummyEnv())
812         TARGETS = map(lambda x: str(x), d['TARGETS'])
813         TARGETS.sort()
814         assert TARGETS == ['t3', 't4'], d['TARGETS']
815         SOURCES = map(lambda x: str(x), d['SOURCES'])
816         SOURCES.sort()
817         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
818
819     def test_PrependPath(self):
820         """Test prepending to a path"""
821         p1 = r'C:\dir\num\one;C:\dir\num\two'
822         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
823         # have to include the pathsep here so that the test will work on UNIX too.
824         p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';') 
825         p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
826         p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
827         p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
828         assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
829         assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
830
831     def test_AppendPath(self):
832         """Test appending to a path."""
833         p1 = r'C:\dir\num\one;C:\dir\num\two'
834         p2 = r'C:\mydir\num\one;C:\mydir\num\two'
835         # have to include the pathsep here so that the test will work on UNIX too.
836         p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';') 
837         p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
838         p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
839         p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
840         assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
841         assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
842
843     def test_NodeList(self):
844         """Test NodeList class"""
845         class TestClass:
846             def __init__(self, name, child=None):
847                 self.child = child
848                 self.bar = name
849             def foo(self):
850                 return self.bar + "foo"
851             def getself(self):
852                 return self
853
854         t1 = TestClass('t1', TestClass('t1child'))
855         t2 = TestClass('t2', TestClass('t2child'))
856         t3 = TestClass('t3')
857
858         nl = NodeList([t1, t2, t3])
859         assert nl.foo() == [ 't1foo', 't2foo', 't3foo' ], nl.foo()
860         assert nl.bar == [ 't1', 't2', 't3' ], nl.bar
861         assert nl.getself().bar == [ 't1', 't2', 't3' ], nl.getself().bar
862         assert nl[0:2].child.foo() == [ 't1childfoo', 't2childfoo' ], \
863                nl[0:2].child.foo()
864         assert nl[0:2].child.bar == [ 't1child', 't2child' ], \
865                nl[0:2].child.bar
866
867     def test_Selector(self):
868         """Test the Selector class"""
869
870         s = Selector({'a' : 'AAA', 'b' : 'BBB'})
871         assert s['a'] == 'AAA', s['a']
872         assert s['b'] == 'BBB', s['b']
873         exc_caught = None
874         try:
875             x = s['c']
876         except KeyError:
877             exc_caught = 1
878         assert exc_caught, "should have caught a KeyError"
879         s['c'] = 'CCC'
880         assert s['c'] == 'CCC', s['c']
881
882         class DummyEnv(UserDict.UserDict):
883             def subst(self, key):
884                 if key[0] == '$':
885                     return self[key[1:]]
886                 return key
887
888         env = DummyEnv()
889
890         s = Selector({'.d' : 'DDD', '.e' : 'EEE'})
891         ret = s(env, ['foo.d'])
892         assert ret == 'DDD', ret
893         ret = s(env, ['bar.e'])
894         assert ret == 'EEE', ret
895         ret = s(env, ['bar.x'])
896         assert ret == None, ret
897         s[None] = 'XXX'
898         ret = s(env, ['bar.x'])
899         assert ret == 'XXX', ret
900
901         env = DummyEnv({'FSUFF' : '.f', 'GSUFF' : '.g'})
902
903         s = Selector({'$FSUFF' : 'FFF', '$GSUFF' : 'GGG'})
904         ret = s(env, ['foo.f'])
905         assert ret == 'FFF', ret
906         ret = s(env, ['bar.g'])
907         assert ret == 'GGG', ret
908
909 if __name__ == "__main__":
910     suite = unittest.makeSuite(UtilTestCase, 'test_')
911     if not unittest.TextTestRunner().run(suite).wasSuccessful():
912         sys.exit(1)