Add --debug-includes. (Anthony Roach)
[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         bar_h = Node('bar.h', [stdlib_h])
311         blat_h = Node('blat.h', [stdlib_h])
312         blat_c = Node('blat.c', [blat_h, bar_h])
313         blat_o = Node('blat.o', [blat_c])
314
315         expect = """\
316 +-blat.o
317   +-blat.c
318     +-blat.h
319     | +-stdlib.h
320     +-bar.h
321 """
322
323         actual = render_tree(blat_o, get_children, 1)
324         assert expect == actual, (expect, actual)        
325
326     def test_is_Dict(self):
327         assert is_Dict({})
328         import UserDict
329         assert is_Dict(UserDict.UserDict())
330         assert not is_Dict([])
331         assert not is_Dict("")
332         if hasattr(types, 'UnicodeType'):
333             exec "assert not is_Dict(u'')"
334
335     def test_is_List(self):
336         assert is_List([])
337         import UserList
338         assert is_List(UserList.UserList())
339         assert not is_List({})
340         assert not is_List("")
341         if hasattr(types, 'UnicodeType'):
342             exec "assert not is_List(u'')"
343
344     def test_Split(self):
345         assert Split("foo bar") == ["foo", "bar"]
346         assert Split(["foo", "bar"]) == ["foo", "bar"]
347         assert Split("foo") == ["foo"]
348
349     def test_is_String(self):
350         assert is_String("")
351         if hasattr(types, 'UnicodeType'):
352             exec "assert is_String(u'')"
353         try:
354             import UserString
355         except:
356             pass
357         else:
358             assert is_String(UserString.UserString(''))
359         assert not is_String({})
360         assert not is_String([])
361
362     def test_to_String(self):
363         """Test the to_String() method."""
364         assert to_String(1) == "1", to_String(1)
365         assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
366         assert to_String("foo") == "foo", to_String("foo")
367
368         try:
369             import UserString
370
371             s1=UserString.UserString('blah')
372             assert to_String(s1) == s1, s1
373             assert to_String(s1) == 'blah', s1
374
375             class Derived(UserString.UserString):
376                 pass
377             s2 = Derived('foo')
378             assert to_String(s2) == s2, s2
379             assert to_String(s2) == 'foo', s2
380
381             if hasattr(types, 'UnicodeType'):
382                 s3=UserString.UserString(unicode('bar'))
383                 assert to_String(s3) == s3, s3
384                 assert to_String(s3) == unicode('bar'), s3
385                 assert type(to_String(s3)) is types.UnicodeType, \
386                        type(to_String(s3))
387         except ImportError:
388             pass
389
390         if hasattr(types, 'UnicodeType'):
391             s4 = unicode('baz')
392             assert to_String(s4) == unicode('baz'), to_String(s4)
393             assert type(to_String(s4)) is types.UnicodeType, \
394                    type(to_String(s4))
395
396     def test_WhereIs(self):
397         test = TestCmd.TestCmd(workdir = '')
398
399         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
400         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
401         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
402         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
403
404         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
405
406         if sys.platform != 'win32':
407             test.write(sub1_xxx_exe, "\n")
408
409         os.mkdir(sub2_xxx_exe)
410
411         test.write(sub3_xxx_exe, "\n")
412         os.chmod(sub3_xxx_exe, 0777)
413
414         test.write(sub4_xxx_exe, "\n")
415         os.chmod(sub4_xxx_exe, 0777)
416
417         env_path = os.environ['PATH']
418
419         pathdirs_1234 = [ test.workpath('sub1'),
420                           test.workpath('sub2'),
421                           test.workpath('sub3'),
422                           test.workpath('sub4'),
423                         ] + string.split(env_path, os.pathsep)
424
425         pathdirs_1243 = [ test.workpath('sub1'),
426                           test.workpath('sub2'),
427                           test.workpath('sub4'),
428                           test.workpath('sub3'),
429                         ] + string.split(env_path, os.pathsep)
430
431         os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
432         wi = WhereIs('xxx.exe')
433         assert wi == test.workpath(sub3_xxx_exe), wi
434         wi = WhereIs('xxx.exe', pathdirs_1243)
435         assert wi == test.workpath(sub4_xxx_exe), wi
436         wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
437         assert wi == test.workpath(sub4_xxx_exe), wi
438
439         os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
440         wi = WhereIs('xxx.exe')
441         assert wi == test.workpath(sub4_xxx_exe), wi
442         wi = WhereIs('xxx.exe', pathdirs_1234)
443         assert wi == test.workpath(sub3_xxx_exe), wi
444         wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
445         assert wi == test.workpath(sub3_xxx_exe), wi
446
447         if sys.platform == 'win32':
448             wi = WhereIs('xxx', pathext = '')
449             assert wi is None, wi
450
451             wi = WhereIs('xxx', pathext = '.exe')
452             assert wi == test.workpath(sub4_xxx_exe), wi
453
454             wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
455             assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
456
457     def test_get_env_var(self):
458         """Testing get_environment_var()."""
459         assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
460         assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
461         assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
462         assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
463         assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
464
465     def test_Proxy(self):
466         """Test generic Proxy class."""
467         class Subject:
468             def foo(self):
469                 return 1
470             def bar(self):
471                 return 2
472
473         s=Subject()
474         s.baz = 3
475
476         class ProxyTest(Proxy):
477             def bar(self):
478                 return 4
479
480         p=ProxyTest(s)
481
482         assert p.foo() == 1, p.foo()
483         assert p.bar() == 4, p.bar()
484         assert p.baz == 3, p.baz
485
486         p.baz = 5
487         s.baz = 6
488
489         assert p.baz == 5, p.baz
490
491     def test_Literal(self):
492         """Test the Literal() function."""
493         cmd_list = [ '$FOO', Literal('$BAR') ]
494         cmd_list = scons_subst_list(cmd_list,
495                                     { 'FOO' : 'BAZ',
496                                       'BAR' : 'BLAT' }, {})
497         def escape_func(cmd):
498             return '**' + cmd + '**'
499
500         map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
501         cmd_list = map(str, cmd_list[0])
502         assert cmd_list[0] == 'BAZ', cmd_list[0]
503         assert cmd_list[1] == '**$BAR**', cmd_list[1]
504
505     def test_mapPaths(self):
506         """Test the mapPaths function"""
507         fs = SCons.Node.FS.FS()
508         dir=fs.Dir('foo')
509         file=fs.File('bar/file')
510         
511         class DummyEnv:
512             def subst(self, arg):
513                 return 'bar'
514
515         res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
516         assert res[0] == file, res[0]
517         assert res[1] == os.path.join('foo', 'baz'), res[1]
518         assert res[2] == os.path.join('foo', 'blat/boo'), res[2]
519         assert res[3] == '#test', res[3]
520
521         env=DummyEnv()
522         res=mapPaths('bleh', dir, env)
523         assert res[0] == os.path.normpath('foo/bar'), res[1]
524
525     def test_display(self):
526         old_stdout = sys.stdout
527         sys.stdout = OutBuffer()
528         SCons.Util.display("line1")
529         display.set_mode(0)
530         SCons.Util.display("line2")
531         display.set_mode(1)
532         SCons.Util.display("line3")
533
534         assert sys.stdout.buffer == "line1\nline3\n"
535         sys.stdout = old_stdout
536
537     def test_fs_delete(self):
538         test = TestCmd.TestCmd(workdir = '')
539         base = test.workpath('')
540         xxx = test.workpath('xxx.xxx')
541         sub1_yyy = test.workpath('sub1', 'yyy.yyy')
542         test.subdir('sub1')
543         test.write(xxx, "\n")
544         test.write(sub1_yyy, "\n")
545
546         old_stdout = sys.stdout
547         sys.stdout = OutBuffer()
548
549         exp = "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
550               "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
551               "Removed " + os.path.join(base, xxx) + '\n' + \
552               "Removed directory " + base + '\n'
553
554         SCons.Util.fs_delete(base, remove=0)
555         assert sys.stdout.buffer == exp
556         assert os.path.exists(sub1_yyy)
557
558         sys.stdout.buffer = ""
559         SCons.Util.fs_delete(base, remove=1)
560         assert sys.stdout.buffer == exp
561         assert not os.path.exists(base)
562
563         test._dirlist = None
564         sys.stdout = old_stdout
565
566
567 if __name__ == "__main__":
568     suite = unittest.makeSuite(UtilTestCase, 'test_')
569     if not unittest.TextTestRunner().run(suite).wasSuccessful():
570         sys.exit(1)