http://scons.tigris.org/issues/show_bug.cgi?id=2345
[scons.git] / src / engine / SCons / Scanner / IDLTests.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 TestCmd
27 import SCons.Scanner.IDL
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('t1.idl','''
42 #include "f1.idl"
43 #include <f2.idl>
44 import "f3.idl";
45
46 [
47         object,
48         uuid(22995106-CE26-4561-AF1B-C71C6934B840),
49         dual,
50         helpstring("IBarObject Interface"),
51         pointer_default(unique)
52 ]
53 interface IBarObject : IDispatch
54 {
55 };
56 ''')
57
58 test.write('t2.idl',"""
59 #include \"d1/f1.idl\"
60 #include <d2/f1.idl>
61 #include \"f1.idl\"
62 import <f3.idl>;
63
64 [
65         object,
66         uuid(22995106-CE26-4561-AF1B-C71C6934B840),
67         dual,
68         helpstring(\"IBarObject Interface\"),
69         pointer_default(unique)
70 ]
71 interface IBarObject : IDispatch
72 {
73 };
74 """)
75
76 test.write('t3.idl',"""
77 #include \t \"f1.idl\"
78    \t #include \"f2.idl\"
79 #   \t include \"f3-test.idl\"
80
81 #include \t <d1/f1.idl>
82    \t #include <d1/f2.idl>
83 #   \t include <d1/f3-test.idl>
84
85 import \t \"d1/f1.idl\"
86    \t import \"d1/f2.idl\"
87
88 include \t \"never.idl\"
89    \t include \"never.idl\"
90
91 // #include \"never.idl\"
92
93 const char* x = \"#include <never.idl>\"
94
95 [
96         object,
97         uuid(22995106-CE26-4561-AF1B-C71C6934B840),
98         dual,
99         helpstring(\"IBarObject Interface\"),
100         pointer_default(unique)
101 ]
102 interface IBarObject : IDispatch
103 {
104 };
105 """)
106
107 test.subdir('d1', ['d1', 'd2'])
108
109 headers = ['f1.idl','f2.idl', 'f3.idl', 'f3-test.idl', 'fi.idl', 'fj.idl', 'never.idl',
110            'd1/f1.idl', 'd1/f2.idl', 'd1/f3-test.idl', 'd1/fi.idl', 'd1/fj.idl',
111            'd1/d2/f1.idl', 'd1/d2/f2.idl', 'd1/d2/f3-test.idl',
112            'd1/d2/f4.idl', 'd1/d2/fi.idl', 'd1/d2/fj.idl']
113
114 for h in headers:
115     test.write(h, " ")
116
117 test.write('f2.idl',"""
118 #include "fi.idl"
119 """)
120
121 test.write('f3-test.idl',"""
122 #include <fj.idl>
123 """)
124
125
126 test.subdir('include', 'subdir', ['subdir', 'include'])
127
128 test.write('t4.idl',"""
129 #include \"fa.idl\"
130 #include <fb.idl>
131
132 [
133         object,
134         uuid(22995106-CE26-4561-AF1B-C71C6934B840),
135         dual,
136         helpstring(\"IBarObject Interface\"),
137         pointer_default(unique)
138 ]
139 interface IBarObject : IDispatch
140 {
141 };
142 """)
143
144 test.write(['include', 'fa.idl'], "\n")
145 test.write(['include', 'fb.idl'], "\n")
146 test.write(['subdir', 'include', 'fa.idl'], "\n")
147 test.write(['subdir', 'include', 'fb.idl'], "\n")
148
149 test.subdir('repository', ['repository', 'include'],
150             ['repository', 'src' ])
151 test.subdir('work', ['work', 'src'])
152
153 test.write(['repository', 'include', 'iii.idl'], "\n")
154
155 test.write(['work', 'src', 'fff.c'], """
156 #include <iii.idl>
157 #include <jjj.idl>
158
159 int main()
160 {
161     return 0;
162 }
163 """)
164
165 test.write([ 'work', 'src', 'aaa.c'], """
166 #include "bbb.idl"
167
168 int main()
169 {
170    return 0;
171 }
172 """)
173
174 test.write([ 'work', 'src', 'bbb.idl'], "\n")
175
176 test.write([ 'repository', 'src', 'ccc.c'], """
177 #include "ddd.idl"
178
179 int main()
180 {
181    return 0;
182 }
183 """)
184
185 test.write([ 'repository', 'src', 'ddd.idl'], "\n")
186
187 # define some helpers:
188
189 class DummyEnvironment:
190     def __init__(self, listCppPath):
191         self.path = listCppPath
192         self.fs = SCons.Node.FS.FS(test.workpath(''))
193         
194     def Dictionary(self, *args):
195         if not args:
196             return { 'CPPPATH': self.path }
197         elif len(args) == 1 and args[0] == 'CPPPATH':
198             return self.path
199         else:
200             raise KeyError("Dummy environment only has CPPPATH attribute.")
201
202     def subst(self, arg, target=None, source=None, conv=None):
203         return arg
204
205     def subst_path(self, path, target=None, source=None, conv=None):
206         if not isinstance(path, list):
207             path = [path]
208         return list(map(self.subst, path))
209
210     def has_key(self, key):
211         return key in self.Dictionary()
212
213     def __getitem__(self,key):
214         return self.Dictionary()[key]
215
216     def __setitem__(self,key,value):
217         self.Dictionary()[key] = value
218
219     def __delitem__(self,key):
220         del self.Dictionary()[key]
221
222     def get_calculator(self):
223         return None
224
225     def get_factory(self, factory):
226         return factory or self.fs.File
227
228     def Dir(self, filename):
229         return self.fs.Dir(filename)
230
231     def File(self, filename):
232         return self.fs.File(filename)
233
234 global my_normpath
235 my_normpath = os.path.normpath
236
237 if os.path.normcase('foo') == os.path.normcase('FOO'):
238     my_normpath = os.path.normcase
239
240 def deps_match(self, deps, headers):
241     scanned = list(map(my_normpath, list(map(str, deps))))
242     expect = list(map(my_normpath, headers))
243     self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
244
245 # define some tests:
246
247 class IDLScannerTestCase1(unittest.TestCase):
248     def runTest(self):
249         env = DummyEnvironment([])
250         s = SCons.Scanner.IDL.IDLScan()
251         path = s.path(env)
252         deps = s(env.File('t1.idl'), env, path)
253         headers = ['f1.idl', 'f3.idl', 'f2.idl']
254         deps_match(self, deps, headers)
255
256 class IDLScannerTestCase2(unittest.TestCase):
257     def runTest(self):
258         env = DummyEnvironment([test.workpath("d1")])
259         s = SCons.Scanner.IDL.IDLScan()
260         path = s.path(env)
261         deps = s(env.File('t1.idl'), env, path)
262         headers = ['f1.idl', 'f3.idl', 'd1/f2.idl']
263         deps_match(self, deps, headers)
264
265 class IDLScannerTestCase3(unittest.TestCase):
266     def runTest(self):
267         env = DummyEnvironment([test.workpath("d1")])
268         s = SCons.Scanner.IDL.IDLScan()
269         path = s.path(env)
270         deps = s(env.File('t2.idl'), env, path)
271         headers = ['d1/f1.idl', 'f1.idl', 'd1/d2/f1.idl', 'f3.idl']
272         deps_match(self, deps, headers)
273
274 class IDLScannerTestCase4(unittest.TestCase):
275     def runTest(self):
276         env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")])
277         s = SCons.Scanner.IDL.IDLScan()
278         path = s.path(env)
279         deps = s(env.File('t2.idl'), env, path)
280         headers =  ['d1/f1.idl', 'f1.idl', 'd1/d2/f1.idl', 'f3.idl']
281         deps_match(self, deps, headers)
282         
283 class IDLScannerTestCase5(unittest.TestCase):
284     def runTest(self):
285         env = DummyEnvironment([])
286         s = SCons.Scanner.IDL.IDLScan()
287         path = s.path(env)
288
289         n = env.File('t3.idl')
290         def my_rexists(s=n):
291             s.rexists_called = 1
292             return s.old_rexists()
293         setattr(n, 'old_rexists', n.rexists)
294         setattr(n, 'rexists', my_rexists)
295
296         deps = s(n, env, path)
297
298         # Make sure rexists() got called on the file node being
299         # scanned, essential for cooperation with VariantDir functionality.
300         assert n.rexists_called
301         
302         headers =  ['d1/f1.idl', 'd1/f2.idl',
303                     'f1.idl', 'f2.idl', 'f3-test.idl',
304                     'd1/f1.idl', 'd1/f2.idl', 'd1/f3-test.idl']
305         deps_match(self, deps, headers)
306
307 class IDLScannerTestCase6(unittest.TestCase):
308     def runTest(self):
309         env1 = DummyEnvironment([test.workpath("d1")])
310         env2 = DummyEnvironment([test.workpath("d1/d2")])
311         s = SCons.Scanner.IDL.IDLScan()
312         path1 = s.path(env1)
313         path2 = s.path(env2)
314         deps1 = s(env1.File('t1.idl'), env1, path1)
315         deps2 = s(env2.File('t1.idl'), env2, path2)
316         headers1 = ['f1.idl', 'f3.idl', 'd1/f2.idl']
317         headers2 = ['f1.idl', 'f3.idl', 'd1/d2/f2.idl']
318         deps_match(self, deps1, headers1)
319         deps_match(self, deps2, headers2)
320
321 class IDLScannerTestCase7(unittest.TestCase):
322     def runTest(self):
323         env = DummyEnvironment(["include"])
324         s = SCons.Scanner.IDL.IDLScan()
325         path = s.path(env)
326         deps1 = s(env.File('t4.idl'), env, path)
327         env.fs.chdir(env.Dir('subdir'))
328         dir = env.fs.getcwd()
329         env.fs.chdir(env.Dir(''))
330         path = s.path(env, dir)
331         deps2 = s(env.File('#t4.idl'), env, path)
332         headers1 =  list(map(test.workpath, ['include/fa.idl', 'include/fb.idl']))
333         headers2 =  ['include/fa.idl', 'include/fb.idl']
334         deps_match(self, deps1, headers1)
335         deps_match(self, deps2, headers2)
336
337 class IDLScannerTestCase8(unittest.TestCase):
338     def runTest(self):
339         SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
340         class TestOut:
341             def __call__(self, x):
342                 self.out = x
343
344         to = TestOut()
345         to.out = None
346         SCons.Warnings._warningOut = to
347         test.write('fa.idl','\n')
348         env = DummyEnvironment([])
349         s = SCons.Scanner.IDL.IDLScan()
350         path = s.path(env)
351         deps = s(env.File('t4.idl'), env, path)
352
353         # Did we catch the warning associated with not finding fb.idl?
354         assert to.out
355         
356         deps_match(self, deps, [ 'fa.idl' ])
357         test.unlink('fa.idl')
358
359 class IDLScannerTestCase9(unittest.TestCase):
360     def runTest(self):
361         env = DummyEnvironment([])
362         env.fs.chdir(env.Dir('include'))
363         s = SCons.Scanner.IDL.IDLScan()
364         path = s.path(env)
365         test.write('include/t4.idl', test.read('t4.idl'))
366         deps = s(env.File('#include/t4.idl'), env, path)
367         env.fs.chdir(env.Dir(''))
368         deps_match(self, deps, [ 'fa.idl', 'fb.idl' ])
369         test.unlink('include/t4.idl')
370
371 class IDLScannerTestCase10(unittest.TestCase):
372     def runTest(self):
373         os.chdir(test.workpath('work'))
374         fs = SCons.Node.FS.FS(test.workpath('work'))
375         fs.Repository(test.workpath('repository'))
376
377         # Create a derived file in a directory that does not exist yet.
378         # This was a bug at one time.
379         env = DummyEnvironment(['include', 'include2'])
380         env.fs = fs
381         f1 = fs.File('include2/jjj.idl')
382         f1.builder = 1
383         s = SCons.Scanner.IDL.IDLScan()
384         path = s.path(env)
385         deps = s(fs.File('src/fff.c'), env, path)
386         deps_match(self, deps, [ test.workpath('repository/include/iii.idl'),
387                                  'include2/jjj.idl' ])
388         os.chdir(test.workpath(''))
389
390 class IDLScannerTestCase11(unittest.TestCase):
391     def runTest(self):
392         os.chdir(test.workpath('work'))
393         fs = SCons.Node.FS.FS(test.workpath('work'))
394         fs.VariantDir('build1', 'src', 1)
395         fs.VariantDir('build2', 'src', 0)
396         fs.Repository(test.workpath('repository'))
397         env = DummyEnvironment([])
398         env.fs = fs
399         s = SCons.Scanner.IDL.IDLScan()
400         path = s.path(env)
401         deps1 = s(fs.File('build1/aaa.c'), env, path)
402         deps_match(self, deps1, [ 'build1/bbb.idl' ])
403         deps2 = s(fs.File('build2/aaa.c'), env, path)
404         deps_match(self, deps2, [ 'src/bbb.idl' ])
405         deps3 = s(fs.File('build1/ccc.c'), env, path)
406         deps_match(self, deps3, [ 'build1/ddd.idl' ])
407         deps4 = s(fs.File('build2/ccc.c'), env, path)
408         deps_match(self, deps4, [ test.workpath('repository/src/ddd.idl') ])
409         os.chdir(test.workpath(''))
410
411 class IDLScannerTestCase12(unittest.TestCase):
412     def runTest(self):
413         class SubstEnvironment(DummyEnvironment):
414             def subst(self, arg, target=None, source=None, conv=None, test=test):
415                 if arg == "$blah":
416                     return test.workpath("d1")
417                 else:
418                     return arg
419         env = SubstEnvironment(["$blah"])
420         s = SCons.Scanner.IDL.IDLScan()
421         path = s.path(env)
422         deps = s(env.File('t1.idl'), env, path)
423         headers = ['f1.idl', 'f3.idl', 'd1/f2.idl']
424         deps_match(self, deps, headers)
425         
426
427 def suite():
428     suite = unittest.TestSuite()
429     suite.addTest(IDLScannerTestCase1())
430     suite.addTest(IDLScannerTestCase2())
431     suite.addTest(IDLScannerTestCase3())
432     suite.addTest(IDLScannerTestCase4())
433     suite.addTest(IDLScannerTestCase5())
434     suite.addTest(IDLScannerTestCase6())
435     suite.addTest(IDLScannerTestCase7())
436     suite.addTest(IDLScannerTestCase8())
437     suite.addTest(IDLScannerTestCase9())
438     suite.addTest(IDLScannerTestCase10())
439     suite.addTest(IDLScannerTestCase11())
440     suite.addTest(IDLScannerTestCase12())
441     return suite
442
443 if __name__ == "__main__":
444     runner = unittest.TextTestRunner()
445     result = runner.run(suite())
446     if not result.wasSuccessful():
447         sys.exit(1)
448
449 # Local Variables:
450 # tab-width:4
451 # indent-tabs-mode:nil
452 # End:
453 # vim: set expandtab tabstop=4 shiftwidth=4: