Add BuildDir() support to the Repository functionality.
[scons.git] / src / engine / SCons / Scanner / CTests.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 TestCmd
27 import SCons.Scanner.C
28 import unittest
29 import sys
30 import os
31 import os.path
32 import SCons.Node.FS
33 import SCons.Warnings
34
35 test = TestCmd.TestCmd(workdir = '')
36
37 os.chdir(test.workpath(''))
38
39 # create some source files and headers:
40
41 test.write('f1.cpp',"""
42 #include \"f1.h\"
43 #include <f2.h>
44
45 int main()
46 {
47    return 0;
48 }
49 """)
50
51 test.write('f2.cpp',"""
52 #include \"d1/f1.h\"
53 #include <d2/f1.h>
54 #include \"f1.h\"
55 #include <f4.h>
56
57 int main()
58 {
59    return 0;
60 }
61 """)
62
63 test.write('f3.cpp',"""
64 #include \t "f1.h"
65    \t #include "f2.h"
66 #   \t include "f3-test.h"
67
68 #include \t <d1/f1.h>
69    \t #include <d1/f2.h>
70 #   \t include <d1/f3-test.h>
71
72 // #include "never.h"
73
74 const char* x = "#include <never.h>"
75
76 int main()
77 {
78    return 0;
79 }
80 """)
81
82
83 # for Emacs -> "
84
85 test.subdir('d1', ['d1', 'd2'])
86
87 headers = ['f1.h','f2.h', 'f3-test.h', 'fi.h', 'fj.h', 'never.h',
88            'd1/f1.h', 'd1/f2.h', 'd1/f3-test.h', 'd1/fi.h', 'd1/fj.h',
89            'd1/d2/f1.h', 'd1/d2/f2.h', 'd1/d2/f3-test.h',
90            'd1/d2/f4.h', 'd1/d2/fi.h', 'd1/d2/fj.h']
91
92 for h in headers:
93     test.write(h, " ")
94
95 test.write('f2.h',"""
96 #include "fi.h"
97 """)
98
99 test.write('f3-test.h',"""
100 #include <fj.h>
101 """)
102
103
104 test.subdir('include', 'subdir', ['subdir', 'include'])
105
106 test.write('fa.cpp',"""
107 #include \"fa.h\"
108 #include <fb.h>
109
110 int main()
111 {
112    return 0;
113 }
114 """)
115
116 test.write(['include', 'fa.h'], "\n")
117 test.write(['include', 'fb.h'], "\n")
118 test.write(['subdir', 'include', 'fa.h'], "\n")
119 test.write(['subdir', 'include', 'fb.h'], "\n")
120
121
122 test.subdir('repository', ['repository', 'include'])
123 test.subdir('work', ['work', 'src'])
124
125 test.write(['repository', 'include', 'iii.h'], "\n")
126
127 test.write(['work', 'src', 'fff.c'], """
128 #include <iii.h>
129
130 int main()
131 {
132     return 0;
133 }
134 """)
135
136 # define some helpers:
137
138 class DummyTarget:
139     def __init__(self, cwd=None):
140         self.cwd = cwd
141
142 class DummyEnvironment:
143     def __init__(self, listCppPath):
144         self.path = listCppPath
145         
146     def Dictionary(self, *args):
147         if not args:
148             return { 'CPPPATH': self.path }
149         elif len(args) == 1 and args[0] == 'CPPPATH':
150             return self.path
151         else:
152             raise KeyError, "Dummy environment only has CPPPATH attribute."
153
154     def __getitem__(self,key):
155         return self.Dictionary()[key]
156
157     def __setitem__(self,key,value):
158         self.Dictionary()[key] = value
159
160     def __delitem__(self,key):
161         del self.Dictionary()[key]
162
163 my_normpath = os.path.normpath
164 if os.path.normcase('foo') == os.path.normcase('FOO'):
165     global my_normpath
166     my_normpath = os.path.normcase
167
168 def deps_match(self, deps, headers):
169     scanned = map(my_normpath, map(str, deps))
170     expect = map(my_normpath, headers)
171     self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
172
173 def make_node(filename, fs=SCons.Node.FS.default_fs):
174     return fs.File(test.workpath(filename))
175
176 # define some tests:
177
178 class CScannerTestCase1(unittest.TestCase):
179     def runTest(self):
180         env = DummyEnvironment([])
181         s = SCons.Scanner.C.CScan()
182         deps = s.scan(make_node('f1.cpp'), env, DummyTarget())
183         headers = ['f1.h', 'f2.h', 'fi.h']
184         deps_match(self, deps, map(test.workpath, headers))
185
186 class CScannerTestCase2(unittest.TestCase):
187     def runTest(self):
188         env = DummyEnvironment([test.workpath("d1")])
189         s = SCons.Scanner.C.CScan()
190         deps = s.scan(make_node('f1.cpp'), env, DummyTarget())
191         headers = ['d1/f2.h', 'f1.h']
192         deps_match(self, deps, map(test.workpath, headers))
193
194 class CScannerTestCase3(unittest.TestCase):
195     def runTest(self):
196         env = DummyEnvironment([test.workpath("d1")])
197         s = SCons.Scanner.C.CScan()
198         deps = s.scan(make_node('f2.cpp'), env, DummyTarget())
199         headers = ['d1/d2/f1.h', 'd1/f1.h', 'f1.h']
200         deps_match(self, deps, map(test.workpath, headers))
201
202 class CScannerTestCase4(unittest.TestCase):
203     def runTest(self):
204         env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")])
205         s = SCons.Scanner.C.CScan()
206         deps = s.scan(make_node('f2.cpp'), env, DummyTarget())
207         headers =  ['d1/d2/f1.h', 'd1/d2/f4.h', 'd1/f1.h', 'f1.h']
208         deps_match(self, deps, map(test.workpath, headers))
209         
210 class CScannerTestCase5(unittest.TestCase):
211     def runTest(self):
212         env = DummyEnvironment([])
213         s = SCons.Scanner.C.CScan()
214
215         n = make_node('f3.cpp')
216         def my_rexists(s=n):
217             s.rexists_called = 1
218             return s.old_rexists()
219         setattr(n, 'old_rexists', n.rexists)
220         setattr(n, 'rexists', my_rexists)
221
222         deps = s.scan(n, env, DummyTarget())
223
224         # Make sure rexists() got called on the file node being
225         # scanned, essential for cooperation with BuildDir functionality.
226         assert n.rexists_called
227         
228         headers =  ['d1/f1.h', 'd1/f2.h', 'd1/f3-test.h',
229                     'f1.h', 'f2.h', 'f3-test.h', 'fi.h', 'fj.h']
230         deps_match(self, deps, map(test.workpath, headers))
231
232 class CScannerTestCase6(unittest.TestCase):
233     def runTest(self):
234         env1 = DummyEnvironment([test.workpath("d1")])
235         env2 = DummyEnvironment([test.workpath("d1/d2")])
236         env3 = DummyEnvironment([test.workpath("d1/../d1")])
237         s = SCons.Scanner.C.CScan()
238         deps1 = s.scan(make_node('f1.cpp'), env1, DummyTarget())
239         deps2 = s.scan(make_node('f1.cpp'), env2, DummyTarget())
240         headers1 =  ['d1/f2.h', 'f1.h']
241         headers2 =  ['d1/d2/f2.h', 'f1.h']
242         deps_match(self, deps1, map(test.workpath, headers1))
243         deps_match(self, deps2, map(test.workpath, headers2))
244
245 class CScannerTestCase8(unittest.TestCase):
246     def runTest(self):
247         fs = SCons.Node.FS.FS(test.workpath(''))
248         env = DummyEnvironment(["include"])
249         s = SCons.Scanner.C.CScan(fs = fs)
250         deps1 = s.scan(fs.File('fa.cpp'), env, DummyTarget())
251         fs.chdir(fs.Dir('subdir'))
252         target = DummyTarget(fs.getcwd())
253         fs.chdir(fs.Dir('..'))
254         deps2 = s.scan(fs.File('#fa.cpp'), env, target)
255         headers1 =  ['include/fa.h', 'include/fb.h']
256         headers2 =  ['subdir/include/fa.h', 'subdir/include/fb.h']
257         deps_match(self, deps1, headers1)
258         deps_match(self, deps2, headers2)
259
260 class CScannerTestCase9(unittest.TestCase):
261     def runTest(self):
262         SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
263         class TestOut:
264             def __call__(self, x):
265                 self.out = x
266
267         to = TestOut()
268         to.out = None
269         SCons.Warnings._warningOut = to
270         test.write('fa.h','\n')
271         fs = SCons.Node.FS.FS(test.workpath(''))
272         s = SCons.Scanner.C.CScan(fs=fs)
273         env = DummyEnvironment([])
274         deps = s.scan(fs.File('fa.cpp'), env, DummyTarget())
275
276         # Did we catch the warning associated with not finding fb.h?
277         assert to.out
278         
279         deps_match(self, deps, [ 'fa.h' ])
280         test.unlink('fa.h')
281
282 class CScannerTestCase10(unittest.TestCase):
283     def runTest(self):
284         fs = SCons.Node.FS.FS(test.workpath(''))
285         fs.chdir(fs.Dir('include'))
286         s = SCons.Scanner.C.CScan(fs=fs)
287         env = DummyEnvironment([])
288         test.write('include/fa.cpp', test.read('fa.cpp'))
289         deps = s.scan(fs.File('#include/fa.cpp'), env, DummyTarget())
290         deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ])
291         test.unlink('include/fa.cpp')
292
293 class CScannerTestCase11(unittest.TestCase):
294     def runTest(self):
295         os.chdir(test.workpath('work'))
296         fs = SCons.Node.FS.FS(test.workpath('work'))
297         fs.Repository(test.workpath('repository'))
298         s = SCons.Scanner.C.CScan(fs=fs)
299         env = DummyEnvironment(['include'])
300         deps = s.scan(fs.File('src/fff.c'), env, DummyTarget())
301         deps_match(self, deps, [test.workpath('repository/include/iii.h')])
302         os.chdir(test.workpath(''))
303
304 def suite():
305     suite = unittest.TestSuite()
306     suite.addTest(CScannerTestCase1())
307     suite.addTest(CScannerTestCase2())
308     suite.addTest(CScannerTestCase3())
309     suite.addTest(CScannerTestCase4())
310     suite.addTest(CScannerTestCase5())
311     suite.addTest(CScannerTestCase6())
312     suite.addTest(CScannerTestCase8())
313     suite.addTest(CScannerTestCase9())
314     suite.addTest(CScannerTestCase10())
315     suite.addTest(CScannerTestCase11())
316     return suite
317
318 if __name__ == "__main__":
319     runner = unittest.TextTestRunner()
320     result = runner.run(suite())
321     if not result.wasSuccessful():
322         sys.exit(1)