Support variable substitution on scanner directories. (Charles Crain)
[scons.git] / src / engine / SCons / UtilTests.py
1 #
2 # Copyright (c) 2001, 2002 Steven Knight
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 re
29 import string
30 import sys
31 import types
32 import unittest
33 import SCons.Node
34 import SCons.Node.FS
35 from SCons.Util import *
36 import TestCmd
37
38 class UtilTestCase(unittest.TestCase):
39     def test_subst(self):
40         """Test the subst function."""
41         loc = {}
42         loc['TARGETS'] = PathList(map(os.path.normpath, [ "./foo/bar.exe",
43                                                           "/bar/baz.obj",
44                                                           "../foo/baz.obj" ]))
45         loc['TARGET'] = loc['TARGETS'][0]
46         loc['SOURCES'] = PathList(map(os.path.normpath, [ "./foo/blah.cpp",
47                                                           "/bar/ack.cpp",
48                                                           "../foo/ack.c" ]))
49         loc['SOURCE'] = loc['SOURCES'][0]
50         loc['xxx'] = None
51         loc['zero'] = 0
52         loc['one'] = 1
53
54         if os.sep == '/':
55             def cvt(str):
56                 return str
57         else:
58             def cvt(str):
59                 return string.replace(str, '/', os.sep)
60
61         newcom = scons_subst("test $TARGETS $SOURCES", loc, {})
62         assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c")
63
64         newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", loc, {})
65         assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp")
66
67         newcom = scons_subst("test ${TARGETS[1:]}v", loc, {})
68         assert newcom == cvt("test /bar/baz.obj ../foo/baz.objv")
69
70         newcom = scons_subst("test $TARGET", loc, {})
71         assert newcom == cvt("test foo/bar.exe")
72
73         newcom = scons_subst("test $TARGET$FOO[0]", loc, {})
74         assert newcom == cvt("test foo/bar.exe[0]")
75
76         newcom = scons_subst("test ${TARGET.file}", loc, {})
77         assert newcom == cvt("test bar.exe")
78
79         newcom = scons_subst("test ${TARGET.filebase}", loc, {})
80         assert newcom == cvt("test bar")
81
82         newcom = scons_subst("test ${TARGET.suffix}", loc, {})
83         assert newcom == cvt("test .exe")
84
85         newcom = scons_subst("test ${TARGET.base}", loc, {})
86         assert newcom == cvt("test foo/bar")
87
88         newcom = scons_subst("test ${TARGET.dir}", loc, {})
89         assert newcom == cvt("test foo")
90
91         newcom = scons_subst("test ${TARGET.abspath}", loc, {})
92         assert newcom == cvt("test %s/foo/bar.exe"%SCons.Util.updrive(os.getcwd())), newcom
93
94         newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
95         assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(SCons.Util.updrive(os.getcwd()),
96                                                                      SCons.Util.updrive(os.path.abspath(os.path.normpath("/bar/ack.cpp"))),
97                                                                      SCons.Util.updrive(os.path.normpath(os.getcwd()+"/..")))), newcom
98
99         newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
100         assert newcom == cvt("test %s/foo/blah.cpp"%SCons.Util.updrive(os.getcwd())), newcom
101
102         newcom = scons_subst("test $xxx", loc, {})
103         assert newcom == cvt("test"), newcom
104
105         newcom = scons_subst("test $($xxx$)", loc, {})
106         assert newcom == cvt("test $($)"), newcom
107
108         newcom = scons_subst("test $( $xxx $)", loc, {})
109         assert newcom == cvt("test $( $)"), newcom
110
111         newcom = scons_subst("test $($xxx$)", loc, {}, re.compile('\$[()]'))
112         assert newcom == cvt("test"), newcom
113
114         newcom = scons_subst("test $( $xxx $)", loc, {}, re.compile('\$[()]'))
115         assert newcom == cvt("test"), newcom
116
117         newcom = scons_subst("test $zero", loc, {})
118         assert newcom == cvt("test 0"), newcom
119
120         newcom = scons_subst("test $one", loc, {})
121         assert newcom == cvt("test 1"), newcom
122
123         newcom = scons_subst("test aXbXcXd", loc, {}, re.compile('X'))
124         assert newcom == cvt("test abcd"), newcom
125
126         glob = { 'a' : 1, 'b' : 2 }
127         loc = {'a' : 3, 'c' : 4 }
128         newcom = scons_subst("test $a $b $c $d test", glob, loc)
129         assert newcom == "test 3 2 4 test", newcom
130
131         # Test against a former bug in scons_subst_list()
132         glob = { "FOO" : "$BAR",
133                  "BAR" : "BAZ",
134                  "BLAT" : "XYX",
135                  "BARXYX" : "BADNEWS" }
136         newcom = scons_subst("$FOO$BLAT", glob, {})
137         assert newcom == "BAZXYX", newcom
138
139         # Test for double-dollar-sign behavior
140         glob = { "FOO" : "BAR",
141                  "BAZ" : "BLAT" }
142         newcom = scons_subst("$$FOO$BAZ", glob, {})
143         assert newcom == "$FOOBLAT", newcom
144
145     def test_splitext(self):
146         assert splitext('foo') == ('foo','')
147         assert splitext('foo.bar') == ('foo','.bar')
148         assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
149
150     def test_subst_list(self):
151         """Testing the scons_subst_list() method..."""
152
153         class Node:
154             def __init__(self, name):
155                 self.name = name
156             def __str__(self):
157                 return self.name
158             def is_literal(self):
159                 return 1
160         
161         loc = {}
162         loc['TARGETS'] = PathList(map(os.path.normpath, [ "./foo/bar.exe",
163                                                           "/bar/baz with spaces.obj",
164                                                           "../foo/baz.obj" ]))
165         loc['TARGET'] = loc['TARGETS'][0]
166         loc['SOURCES'] = PathList(map(os.path.normpath, [ "./foo/blah with spaces.cpp",
167                                                           "/bar/ack.cpp",
168                                                           "../foo/ack.c" ]))
169         loc['xxx'] = None
170         loc['NEWLINE'] = 'before\nafter'
171
172         loc['DO'] = Node('do something')
173         loc['FOO'] = Node('foo.in')
174         loc['BAR'] = Node('bar with spaces.out')
175         loc['CRAZY'] = Node('crazy\nfile.in')
176
177         if os.sep == '/':
178             def cvt(str):
179                 return str
180         else:
181             def cvt(str):
182                 return string.replace(str, '/', os.sep)
183
184         cmd_list = scons_subst_list("$TARGETS", loc, {})
185         assert cmd_list[0][1] == cvt("/bar/baz with spaces.obj"), cmd_list[0][1]
186
187         cmd_list = scons_subst_list("$SOURCES $NEWLINE $TARGETS", loc, {})
188         assert len(cmd_list) == 2, cmd_list
189         assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
190         assert cmd_list[1][2] == cvt("/bar/baz with spaces.obj"), cmd_list[1]
191
192         cmd_list = scons_subst_list("$SOURCES$NEWLINE", loc, {})
193         assert len(cmd_list) == 2, cmd_list
194         assert cmd_list[1][0] == 'after', cmd_list[1][0]
195         assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
196
197         cmd_list = scons_subst_list("$DO --in=$FOO --out=$BAR", loc, {})
198         assert len(cmd_list) == 1, cmd_list
199         assert len(cmd_list[0]) == 3, cmd_list
200         assert cmd_list[0][0] == 'do something', cmd_list[0][0]
201         assert cmd_list[0][1] == '--in=foo.in', cmd_list[0][1]
202         assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
203
204         # This test is now fixed, and works like it should.
205         cmd_list = scons_subst_list("$DO --in=$CRAZY --out=$BAR", loc, {})
206         assert len(cmd_list) == 1, map(str, cmd_list[0])
207         assert len(cmd_list[0]) == 3, cmd_list
208         assert cmd_list[0][0] == 'do something', cmd_list[0][0]
209         assert cmd_list[0][1] == '--in=crazy\nfile.in', cmd_list[0][1]
210         assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
211         
212         # Test inputting a list to scons_subst_list()
213         cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
214                                         "This is a test" ],
215                                     loc, {})
216         assert len(cmd_list) == 2, len(cmd_list)
217         assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
218         assert cmd_list[1][0] == cvt("after"), cmd_list[1]
219         assert cmd_list[1][4] == "This is a test", cmd_list[1]
220
221         glob = { 'a' : 1, 'b' : 2 }
222         loc = {'a' : 3, 'c' : 4 }
223         cmd_list = scons_subst_list("test $a $b $c $d test", glob, loc)
224         assert len(cmd_list) == 1, cmd_list
225         assert map(str, cmd_list[0]) == ['test', '3', '2', '4', 'test'], map(str, cmd_list[0])
226
227         # Test against a former bug in scons_subst_list()
228         glob = { "FOO" : "$BAR",
229                  "BAR" : "BAZ",
230                  "BLAT" : "XYX",
231                  "BARXYX" : "BADNEWS" }
232         cmd_list = scons_subst_list("$FOO$BLAT", glob, {})
233         assert cmd_list[0][0] == "BAZXYX", cmd_list[0][0]
234
235         # Test for double-dollar-sign behavior
236         glob = { "FOO" : "BAR",
237                  "BAZ" : "BLAT" }
238         cmd_list = scons_subst_list("$$FOO$BAZ", glob, {})
239         assert cmd_list[0][0] == "$FOOBLAT", cmd_list[0][0]
240
241         # Now test escape functionality
242         def escape_func(foo):
243             return '**' + foo + '**'
244         def quote_func(foo):
245             return foo
246         glob = { "FOO" : PathList([ 'foo\nwith\nnewlines',
247                                     'bar\nwith\nnewlines' ]) }
248         cmd_list = scons_subst_list("$FOO", glob, {})
249         assert cmd_list[0][0] == 'foo\nwith\nnewlines', cmd_list[0][0]
250         cmd_list[0][0].escape(escape_func)
251         assert cmd_list[0][0] == '**foo\nwith\nnewlines**', cmd_list[0][0]
252         assert cmd_list[0][1] == 'bar\nwith\nnewlines', cmd_list[0][0]
253         cmd_list[0][1].escape(escape_func)
254         assert cmd_list[0][1] == '**bar\nwith\nnewlines**', cmd_list[0][0]
255
256     def test_quote_spaces(self):
257         """Testing the quote_spaces() method..."""
258         q = quote_spaces('x')
259         assert q == 'x', q
260
261         q = quote_spaces('x x')
262         assert q == '"x x"', q
263
264         q = quote_spaces('x\tx')
265         assert q == '"x\tx"', q
266
267     def test_render_tree(self):
268         class Node:
269             def __init__(self, name, children=[]):
270                 self.children = children
271                 self.name = name
272             def __str__(self):
273                 return self.name
274
275         def get_children(node):
276             return node.children
277
278         windows_h = Node("windows.h")
279         stdlib_h = Node("stdlib.h")
280         stdio_h = Node("stdio.h")
281         bar_c = Node("bar.c", [stdlib_h, windows_h])
282         bar_o = Node("bar.o", [bar_c])
283         foo_c = Node("foo.c", [stdio_h])
284         foo_o = Node("foo.o", [foo_c])
285         foo = Node("foo", [foo_o, bar_o])
286
287         expect = """\
288 +-foo
289   +-foo.o
290   | +-foo.c
291   |   +-stdio.h
292   +-bar.o
293     +-bar.c
294       +-stdlib.h
295       +-windows.h
296 """
297
298         actual = render_tree(foo, get_children)
299         assert expect == actual, (expect, actual)
300
301     def test_is_Dict(self):
302         assert is_Dict({})
303         import UserDict
304         assert is_Dict(UserDict.UserDict())
305         assert not is_Dict([])
306         assert not is_Dict("")
307         if hasattr(types, 'UnicodeType'):
308             exec "assert not is_Dict(u'')"
309
310     def test_is_List(self):
311         assert is_List([])
312         import UserList
313         assert is_List(UserList.UserList())
314         assert not is_List({})
315         assert not is_List("")
316         if hasattr(types, 'UnicodeType'):
317             exec "assert not is_List(u'')"
318
319     def test_Split(self):
320         assert Split("foo bar") == ["foo", "bar"]
321         assert Split(["foo", "bar"]) == ["foo", "bar"]
322         assert Split("foo") == ["foo"]
323
324     def test_is_String(self):
325         assert is_String("")
326         if hasattr(types, 'UnicodeType'):
327             exec "assert is_String(u'')"
328         try:
329             import UserString
330         except:
331             pass
332         else:
333             assert is_String(UserString.UserString(''))
334         assert not is_String({})
335         assert not is_String([])
336
337     def test_to_String(self):
338         """Test the to_String() method."""
339         assert to_String(1) == "1", to_String(1)
340         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
341         assert to_String("foo") == "foo", to_String("foo")
342
343         try:
344             import UserString
345
346             s1=UserString.UserString('blah')
347             assert to_String(s1) == s1, s1
348             assert to_String(s1) == 'blah', s1
349
350             class Derived(UserString.UserString):
351                 pass
352             s2 = Derived('foo')
353             assert to_String(s2) == s2, s2
354             assert to_String(s2) == 'foo', s2
355
356             if hasattr(types, 'UnicodeType'):
357                 s3=UserString.UserString(unicode('bar'))
358                 assert to_String(s3) == s3, s3
359                 assert to_String(s3) == unicode('bar'), s3
360                 assert type(to_String(s3)) is types.UnicodeType, \
361                        type(to_String(s3))
362         except ImportError:
363             pass
364
365         if hasattr(types, 'UnicodeType'):
366             s4 = unicode('baz')
367             assert to_String(s4) == unicode('baz'), to_String(s4)
368             assert type(to_String(s4)) is types.UnicodeType, \
369                    type(to_String(s4))
370
371     def test_WhereIs(self):
372         test = TestCmd.TestCmd(workdir = '')
373
374         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
375         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
376         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
377         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
378
379         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
380
381         if sys.platform != 'win32':
382             test.write(sub1_xxx_exe, "\n")
383
384         os.mkdir(sub2_xxx_exe)
385
386         test.write(sub3_xxx_exe, "\n")
387         os.chmod(sub3_xxx_exe, 0777)
388
389         test.write(sub4_xxx_exe, "\n")
390         os.chmod(sub4_xxx_exe, 0777)
391
392         env_path = os.environ['PATH']
393
394         pathdirs_1234 = [ test.workpath('sub1'),
395                           test.workpath('sub2'),
396                           test.workpath('sub3'),
397                           test.workpath('sub4'),
398                         ] + string.split(env_path, os.pathsep)
399
400         pathdirs_1243 = [ test.workpath('sub1'),
401                           test.workpath('sub2'),
402                           test.workpath('sub4'),
403                           test.workpath('sub3'),
404                         ] + string.split(env_path, os.pathsep)
405
406         os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
407         wi = WhereIs('xxx.exe')
408         assert wi == test.workpath(sub3_xxx_exe), wi
409         wi = WhereIs('xxx.exe', pathdirs_1243)
410         assert wi == test.workpath(sub4_xxx_exe), wi
411         wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
412         assert wi == test.workpath(sub4_xxx_exe), wi
413
414         os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
415         wi = WhereIs('xxx.exe')
416         assert wi == test.workpath(sub4_xxx_exe), wi
417         wi = WhereIs('xxx.exe', pathdirs_1234)
418         assert wi == test.workpath(sub3_xxx_exe), wi
419         wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
420         assert wi == test.workpath(sub3_xxx_exe), wi
421
422         if sys.platform == 'win32':
423             wi = WhereIs('xxx', pathext = '')
424             assert wi is None, wi
425
426             wi = WhereIs('xxx', pathext = '.exe')
427             assert wi == test.workpath(sub4_xxx_exe), wi
428
429             wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
430             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
431
432     def test_get_env_var(self):
433         """Testing get_environment_var()."""
434         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
435         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
436         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
437         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
438         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
439
440     def test_Proxy(self):
441         """Test generic Proxy class."""
442         class Subject:
443             def foo(self):
444                 return 1
445             def bar(self):
446                 return 2
447
448         s=Subject()
449         s.baz = 3
450
451         class ProxyTest(Proxy):
452             def bar(self):
453                 return 4
454
455         p=ProxyTest(s)
456
457         assert p.foo() == 1, p.foo()
458         assert p.bar() == 4, p.bar()
459         assert p.baz == 3, p.baz
460
461         p.baz = 5
462         s.baz = 6
463
464         assert p.baz == 5, p.baz
465
466     def test_Literal(self):
467         """Test the Literal() function."""
468         cmd_list = [ '$FOO', Literal('$BAR') ]
469         cmd_list = scons_subst_list(cmd_list,
470                                     { 'FOO' : 'BAZ',
471                                       'BAR' : 'BLAT' }, {})
472         def escape_func(cmd):
473             return '**' + cmd + '**'
474
475         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
476         cmd_list = map(str, cmd_list[0])
477         assert cmd_list[0] == 'BAZ', cmd_list[0]
478         assert cmd_list[1] == '**$BAR**', cmd_list[1]
479
480     def test_mapPaths(self):
481         """Test the mapPaths function"""
482         fs = SCons.Node.FS.FS()
483         dir=fs.Dir('foo')
484         file=fs.File('bar/file')
485         
486         class DummyEnv:
487             def subst(self, arg):
488                 return 'bar'
489
490         res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
491         assert res[0] == file, res[0]
492         assert res[1] == os.path.normpath('foo/baz'), res[1]
493         assert res[2] == os.path.normpath('foo/blat/boo'), res[2]
494         assert res[3] == '#test', res[3]
495
496         env=DummyEnv()
497         res=mapPaths('bleh', dir, env)
498         assert res[0] == os.path.normpath('foo/bar'), res[1]
499         
500
501 if __name__ == "__main__":
502     suite = unittest.makeSuite(UtilTestCase, 'test_')
503     if not unittest.TextTestRunner().run(suite).wasSuccessful():
504         sys.exit(1)