Refactor path-substitution logic into an Environment method.
[scons.git] / src / engine / SCons / Scanner / FortranTests.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 os
27 import os.path
28 import sys
29 import unittest
30
31 import SCons.Scanner.Fortran
32 import SCons.Node.FS
33 import SCons.Warnings
34
35 import TestCmd
36
37 original = os.getcwd()
38
39 test = TestCmd.TestCmd(workdir = '')
40
41 os.chdir(test.workpath(''))
42
43 # create some source files and headers:
44
45 test.write('fff1.f',"""
46       PROGRAM FOO
47       INCLUDE 'f1.f'
48       include 'f2.f'
49       STOP
50       END
51 """)
52
53 test.write('fff2.f',"""
54       PROGRAM FOO
55       INCLUDE 'f2.f'
56       include 'd1/f2.f'
57       INCLUDE 'd2/f2.f'
58       STOP
59       END
60 """)
61
62 test.write('fff3.f',"""
63       PROGRAM FOO
64       INCLUDE 'f3.f' ; INCLUDE\t'd1/f3.f'
65       STOP
66       END
67 """)
68
69
70 # for Emacs -> "
71
72 test.subdir('d1', ['d1', 'd2'])
73
74 headers = ['fi.f', 'never.f',
75            'd1/f1.f', 'd1/f2.f', 'd1/f3.f', 'd1/fi.f',
76            'd1/d2/f1.f', 'd1/d2/f2.f', 'd1/d2/f3.f',
77            'd1/d2/f4.f', 'd1/d2/fi.f']
78
79 for h in headers:
80     test.write(h, "\n")
81
82
83 test.subdir('include', 'subdir', ['subdir', 'include'])
84
85 test.write('fff4.f',"""
86       PROGRAM FOO
87       INCLUDE 'f4.f'
88       STOP
89       END
90 """)
91
92 test.write('include/f4.f', "\n")
93 test.write('subdir/include/f4.f', "\n")
94
95 test.write('fff5.f',"""
96       PROGRAM FOO
97       INCLUDE 'f5.f'
98       INCLUDE 'not_there.f'
99       STOP
100       END
101 """)
102
103 test.write('f5.f', "\n")
104
105 test.subdir('repository', ['repository', 'include'],
106             [ 'repository', 'src' ])
107 test.subdir('work', ['work', 'src'])
108
109 test.write(['repository', 'include', 'iii.f'], "\n")
110
111 test.write(['work', 'src', 'fff.f'], """
112       PROGRAM FOO
113       INCLUDE 'iii.f'
114       INCLUDE 'jjj.f'
115       STOP
116       END
117 """)
118
119 test.write([ 'work', 'src', 'aaa.f'], """
120       PROGRAM FOO
121       INCLUDE 'bbb.f'
122       STOP
123       END
124 """)
125
126 test.write([ 'work', 'src', 'bbb.f'], "\n")
127
128 test.write([ 'repository', 'src', 'ccc.f'], """
129       PROGRAM FOO
130       INCLUDE 'ddd.f'
131       STOP
132       END
133 """)
134
135 test.write([ 'repository', 'src', 'ddd.f'], "\n")
136
137 # define some helpers:
138
139 class DummyEnvironment:
140     def __init__(self, listCppPath):
141         self.path = listCppPath
142         
143     def Dictionary(self, *args):
144         if not args:
145             return { 'F77PATH': self.path }
146         elif len(args) == 1 and args[0] == 'F77PATH':
147             return self.path
148         else:
149             raise KeyError, "Dummy environment only has F77PATH attribute."
150
151     def has_key(self, key):
152         return self.Dictionary().has_key(key)
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     def subst(self, arg):
164         return arg
165
166     def subst_path(self, path):
167         if type(path) != type([]):
168             path = [path]
169         return map(self.subst, path)
170
171 def deps_match(self, deps, headers):
172     scanned = map(os.path.normpath, map(str, deps))
173     expect = map(os.path.normpath, headers)
174     self.failUnless(scanned == expect, "expect %s != scanned %s" % (expect, scanned))
175
176 def make_node(filename, fs=SCons.Node.FS.default_fs):
177     return fs.File(test.workpath(filename))
178
179 # define some tests:
180
181 class FortranScannerTestCase1(unittest.TestCase):
182     def runTest(self):
183         test.write('f1.f', "\n")
184         test.write('f2.f', "      INCLUDE 'fi.f'\n")
185         env = DummyEnvironment([])
186         s = SCons.Scanner.Fortran.FortranScan()
187         path = s.path(env)
188         fs = SCons.Node.FS.FS(original)
189         deps = s(make_node('fff1.f', fs), env, path)
190         headers = ['f1.f', 'f2.f']
191         deps_match(self, deps, map(test.workpath, headers))
192         test.unlink('f1.f')
193         test.unlink('f2.f')
194
195 class FortranScannerTestCase2(unittest.TestCase):
196     def runTest(self):
197         test.write('f1.f', "\n")
198         test.write('f2.f', "      INCLUDE 'fi.f'\n")
199         env = DummyEnvironment([test.workpath("d1")])
200         s = SCons.Scanner.Fortran.FortranScan()
201         path = s.path(env)
202         fs = SCons.Node.FS.FS(original)
203         deps = s(make_node('fff1.f', fs), env, path)
204         headers = ['f1.f', 'f2.f']
205         deps_match(self, deps, map(test.workpath, headers))
206         test.unlink('f1.f')
207         test.unlink('f2.f')
208
209 class FortranScannerTestCase3(unittest.TestCase):
210     def runTest(self):
211         env = DummyEnvironment([test.workpath("d1")])
212         s = SCons.Scanner.Fortran.FortranScan()
213         path = s.path(env)
214         fs = SCons.Node.FS.FS(original)
215         deps = s(make_node('fff1.f', fs), env, path)
216         headers = ['d1/f1.f', 'd1/f2.f']
217         deps_match(self, deps, map(test.workpath, headers))
218
219 class FortranScannerTestCase4(unittest.TestCase):
220     def runTest(self):
221         test.write(['d1', 'f2.f'], "      INCLUDE 'fi.f'\n")
222         env = DummyEnvironment([test.workpath("d1")])
223         s = SCons.Scanner.Fortran.FortranScan()
224         path = s.path(env)
225         fs = SCons.Node.FS.FS(original)
226         deps = s(make_node('fff1.f', fs), env, path)
227         headers = ['d1/f1.f', 'd1/f2.f']
228         deps_match(self, deps, map(test.workpath, headers))
229         test.write(['d1', 'f2.f'], "\n")
230
231 class FortranScannerTestCase5(unittest.TestCase):
232     def runTest(self):
233         env = DummyEnvironment([test.workpath("d1")])
234         s = SCons.Scanner.Fortran.FortranScan()
235         path = s.path(env)
236         fs = SCons.Node.FS.FS(original)
237         deps = s(make_node('fff2.f', fs), env, path)
238         headers = ['d1/d2/f2.f', 'd1/f2.f', 'd1/f2.f']
239         deps_match(self, deps, map(test.workpath, headers))
240
241 class FortranScannerTestCase6(unittest.TestCase):
242     def runTest(self):
243         test.write('f2.f', "\n")
244         env = DummyEnvironment([test.workpath("d1")])
245         s = SCons.Scanner.Fortran.FortranScan()
246         path = s.path(env)
247         fs = SCons.Node.FS.FS(original)
248         deps = s(make_node('fff2.f', fs), env, path)
249         headers =  ['d1/d2/f2.f', 'd1/f2.f', 'f2.f']
250         deps_match(self, deps, map(test.workpath, headers))
251         test.unlink('f2.f')
252
253 class FortranScannerTestCase7(unittest.TestCase):
254     def runTest(self):
255         env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
256         s = SCons.Scanner.Fortran.FortranScan()
257         path = s.path(env)
258         fs = SCons.Node.FS.FS(original)
259         deps = s(make_node('fff2.f', fs), env, path)
260         headers =  ['d1/d2/f2.f', 'd1/d2/f2.f', 'd1/f2.f']
261         deps_match(self, deps, map(test.workpath, headers))
262
263 class FortranScannerTestCase8(unittest.TestCase):
264     def runTest(self):
265         test.write('f2.f', "\n")
266         env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
267         s = SCons.Scanner.Fortran.FortranScan()
268         path = s.path(env)
269         fs = SCons.Node.FS.FS(original)
270         deps = s(make_node('fff2.f', fs), env, path)
271         headers =  ['d1/d2/f2.f', 'd1/f2.f', 'f2.f']
272         deps_match(self, deps, map(test.workpath, headers))
273         test.unlink('f2.f')
274         
275 class FortranScannerTestCase9(unittest.TestCase):
276     def runTest(self):
277         test.write('f3.f', "\n")
278         env = DummyEnvironment([])
279         s = SCons.Scanner.Fortran.FortranScan()
280         path = s.path(env)
281
282         n = make_node('fff3.f')
283         def my_rexists(s=n):
284             s.rexists_called = 1
285             return s.old_rexists()
286         setattr(n, 'old_rexists', n.rexists)
287         setattr(n, 'rexists', my_rexists)
288
289         deps = s(n, env, path)
290         
291         # Make sure rexists() got called on the file node being
292         # scanned, essential for cooperation with BuildDir functionality.
293         assert n.rexists_called
294         
295         headers =  ['d1/f3.f', 'f3.f']
296         deps_match(self, deps, map(test.workpath, headers))
297         test.unlink('f3.f')
298
299 class FortranScannerTestCase10(unittest.TestCase):
300     def runTest(self):
301         fs = SCons.Node.FS.FS(test.workpath(''))
302         env = DummyEnvironment(["include"])
303         s = SCons.Scanner.Fortran.FortranScan(fs = fs)
304         path = s.path(env)
305         deps1 = s(fs.File('fff4.f'), env, path)
306         fs.chdir(fs.Dir('subdir'))
307         dir = fs.getcwd()
308         fs.chdir(fs.Dir('..'))
309         path = s.path(env, dir)
310         deps2 = s(fs.File('#fff4.f'), env, path)
311         headers1 =  ['include/f4.f']
312         headers2 =  ['subdir/include/f4.f']
313         deps_match(self, deps1, headers1)
314         deps_match(self, deps2, headers2)
315
316 class FortranScannerTestCase11(unittest.TestCase):
317     def runTest(self):
318         SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning)
319         class TestOut:
320             def __call__(self, x):
321                 self.out = x
322
323         to = TestOut()
324         to.out = None
325         SCons.Warnings._warningOut = to
326         fs = SCons.Node.FS.FS(test.workpath(''))
327         env = DummyEnvironment([])
328         s = SCons.Scanner.Fortran.FortranScan(fs=fs)
329         path = s.path(env)
330         deps = s(fs.File('fff5.f'), env, path)
331
332         # Did we catch the warning from not finding not_there.f?
333         assert to.out
334         
335         deps_match(self, deps, [ 'f5.f' ])
336
337 class FortranScannerTestCase12(unittest.TestCase):
338     def runTest(self):
339         fs = SCons.Node.FS.FS(test.workpath(''))
340         fs.chdir(fs.Dir('include'))
341         env = DummyEnvironment([])
342         s = SCons.Scanner.Fortran.FortranScan(fs=fs)
343         path = s.path(env)
344         test.write('include/fff4.f', test.read('fff4.f'))
345         deps = s(fs.File('#include/fff4.f'), env, path)
346         fs.chdir(fs.Dir('..'))
347         deps_match(self, deps, ['include/f4.f'])
348         test.unlink('include/fff4.f')
349
350 class FortranScannerTestCase13(unittest.TestCase):
351     def runTest(self):
352         os.chdir(test.workpath('work'))
353         fs = SCons.Node.FS.FS(test.workpath('work'))
354         fs.Repository(test.workpath('repository'))
355
356         # Create a derived file in a directory that does not exist yet.
357         # This was a bug at one time.
358         f1=fs.File('include2/jjj.f')
359         f1.builder=1
360         env = DummyEnvironment(['include','include2'])
361         s = SCons.Scanner.Fortran.FortranScan(fs=fs)
362         path = s.path(env)
363         deps = s(fs.File('src/fff.f'), env, path)
364         deps_match(self, deps, [test.workpath('repository/include/iii.f'), 'include2/jjj.f'])
365         os.chdir(test.workpath(''))
366
367 class FortranScannerTestCase14(unittest.TestCase):
368     def runTest(self):
369         os.chdir(test.workpath('work'))
370         fs = SCons.Node.FS.FS(test.workpath('work'))
371         fs.BuildDir('build1', 'src', 1)
372         fs.BuildDir('build2', 'src', 0)
373         fs.Repository(test.workpath('repository'))
374         env = DummyEnvironment([])
375         s = SCons.Scanner.Fortran.FortranScan(fs = fs)
376         path = s.path(env)
377         deps1 = s(fs.File('build1/aaa.f'), env, path)
378         deps_match(self, deps1, [ 'build1/bbb.f' ])
379         deps2 = s(fs.File('build2/aaa.f'), env, path)
380         deps_match(self, deps2, [ 'src/bbb.f' ])
381         deps3 = s(fs.File('build1/ccc.f'), env, path)
382         deps_match(self, deps3, [ 'build1/ddd.f' ])
383         deps4 = s(fs.File('build2/ccc.f'), env, path)
384         deps_match(self, deps4, [ test.workpath('repository/src/ddd.f') ])
385         os.chdir(test.workpath(''))
386
387 class FortranScannerTestCase15(unittest.TestCase):
388     def runTest(self):
389         class SubstEnvironment(DummyEnvironment):
390             def subst(self, arg, test=test):
391                 return test.workpath("d1")
392         test.write(['d1', 'f2.f'], "      INCLUDE 'fi.f'\n")
393         env = SubstEnvironment(["junk"])
394         s = SCons.Scanner.Fortran.FortranScan()
395         path = s.path(env)
396         fs = SCons.Node.FS.FS(original)
397         deps = s(make_node('fff1.f', fs), env, path)
398         headers = ['d1/f1.f', 'd1/f2.f']
399         deps_match(self, deps, map(test.workpath, headers))
400         test.write(['d1', 'f2.f'], "\n")
401
402 def suite():
403     suite = unittest.TestSuite()
404     suite.addTest(FortranScannerTestCase1())
405     suite.addTest(FortranScannerTestCase2())
406     suite.addTest(FortranScannerTestCase3())
407     suite.addTest(FortranScannerTestCase4())
408     suite.addTest(FortranScannerTestCase5())
409     suite.addTest(FortranScannerTestCase6())
410     suite.addTest(FortranScannerTestCase7())
411     suite.addTest(FortranScannerTestCase8())
412     suite.addTest(FortranScannerTestCase9())
413     suite.addTest(FortranScannerTestCase10())
414     suite.addTest(FortranScannerTestCase11())
415     suite.addTest(FortranScannerTestCase12())
416     suite.addTest(FortranScannerTestCase13())
417     suite.addTest(FortranScannerTestCase14())
418     suite.addTest(FortranScannerTestCase15())
419     return suite
420
421 if __name__ == "__main__":
422     runner = unittest.TextTestRunner()
423     result = runner.run(suite())
424     if not result.wasSuccessful():
425         sys.exit(1)