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