91a0f8c8a50bd3f10ddc3df81a8936c1ec89a4f6
[scons.git] / src / engine / SCons / Scanner / ScannerTests.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 sys
27 import unittest
28 import UserDict
29
30 import SCons.Scanner
31
32 class DummyEnvironment(UserDict.UserDict):
33     def __init__(self, dict=None, **kw):
34         UserDict.UserDict.__init__(self, dict)
35         self.data.update(kw)
36     def subst(self, strSubst):
37         if strSubst[0] == '$':
38             return self.data[strSubst[1:]]
39         return strSubst
40     def subst_list(self, strSubst):
41         if strSubst[0] == '$':
42             return [self.data[strSubst[1:]]]
43         return [[strSubst]]
44     def subst_path(self, path):
45         if type(path) != type([]):
46             path = [path]
47         return map(self.subst, path)
48
49 class FindPathDirsTestCase(unittest.TestCase):
50     def test_FindPathDirs(self):
51         """Test the FindPathDirs callable class"""
52
53         class FS:
54             def Rsearchall(self, nodes, must_exist=0, clazz=None, cwd=dir):
55                 return ['xxx'] + nodes
56
57         env = DummyEnvironment(LIBPATH = [ 'foo' ])
58
59         fpd = SCons.Scanner.FindPathDirs('LIBPATH', FS())
60         result = fpd(env, dir)
61         assert result == ('xxx', 'foo'), result
62
63 class ScannerTestCase(unittest.TestCase):
64     
65     def func(self, filename, env, target, *args):
66         self.filename = filename
67         self.env = env
68         self.target = target
69
70         if len(args) > 0:
71             self.arg = args[0]
72         
73         return self.deps
74
75     def test(self, scanner, env, filename, deps, *args):
76         self.deps = deps
77         path = scanner.path(env)
78         scanned = scanner(filename, env, path)
79         scanned_strs = map(lambda x: str(x), scanned)
80
81         self.failUnless(self.filename == filename, "the filename was passed incorrectly")
82         self.failUnless(self.env == env, "the environment was passed incorrectly")
83         self.failUnless(scanned_strs == deps, "the dependencies were returned incorrectly")
84         for d in scanned:
85             self.failUnless(type(d) != type(""), "got a string in the dependencies")
86
87         if len(args) > 0:
88             self.failUnless(self.arg == args[0], "the argument was passed incorrectly")
89         else:
90             self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been")
91
92     def test_positional(self):
93         """Test the Scanner.Base class using positional arguments"""
94         s = SCons.Scanner.Base(self.func, "Pos")
95         env = DummyEnvironment()
96         env.VARIABLE = "var1"
97         self.test(s, env, 'f1.cpp', ['f1.h', 'f1.hpp'])
98
99         env = DummyEnvironment()
100         env.VARIABLE = "i1"
101         self.test(s, env, 'i1.cpp', ['i1.h', 'i1.hpp'])
102
103     def test_keywords(self):
104         """Test the Scanner.Base class using keyword arguments"""
105         s = SCons.Scanner.Base(function = self.func, name = "Key")
106         env = DummyEnvironment()
107         env.VARIABLE = "var2"
108         self.test(s, env, 'f2.cpp', ['f2.h', 'f2.hpp'])
109
110         env = DummyEnvironment()
111         env.VARIABLE = "i2"
112         self.test(s, env, 'i2.cpp', ['i2.h', 'i2.hpp'])
113
114     def test_pos_opt(self):
115         """Test the Scanner.Base class using both position and optional arguments"""
116         arg = "this is the argument"
117         s = SCons.Scanner.Base(self.func, "PosArg", arg)
118         env = DummyEnvironment()
119         env.VARIABLE = "var3"
120         self.test(s, env, 'f3.cpp', ['f3.h', 'f3.hpp'], arg)
121
122         env = DummyEnvironment()
123         env.VARIABLE = "i3"
124         self.test(s, env, 'i3.cpp', ['i3.h', 'i3.hpp'], arg)
125
126     def test_key_opt(self):
127         """Test the Scanner.Base class using both keyword and optional arguments"""
128         arg = "this is another argument"
129         s = SCons.Scanner.Base(function = self.func, name = "KeyArg",
130                                argument = arg)
131         env = DummyEnvironment()
132         env.VARIABLE = "var4"
133         self.test(s, env, 'f4.cpp', ['f4.h', 'f4.hpp'], arg)
134
135         env = DummyEnvironment()
136         env.VARIABLE = "i4"
137         self.test(s, env, 'i4.cpp', ['i4.h', 'i4.hpp'], arg)
138
139     def test_hash(self):
140         """Test the Scanner.Base class __hash__() method"""
141         s = SCons.Scanner.Base(self.func, "Hash")
142         dict = {}
143         dict[s] = 777
144         self.failUnless(hash(dict.keys()[0]) == hash(repr(s)),
145                         "did not hash Scanner base class as expected")
146
147     def test_scan_check(self):
148         """Test the Scanner.Base class scan_check() method"""
149         def my_scan(filename, env, target, *args):
150             return []
151         def check(node, s=self):
152             s.checked[node] = 1
153             return 1
154         env = DummyEnvironment()
155         s = SCons.Scanner.Base(my_scan, "Check", scan_check = check)
156         self.checked = {}
157         path = s.path(env)
158         scanned = s('x', env, path)
159         self.failUnless(self.checked['x'] == 1,
160                         "did not call check function")
161
162     def test_recursive(self):
163         """Test the Scanner.Base class recursive flag"""
164         s = SCons.Scanner.Base(function = self.func)
165         self.failUnless(s.recursive == None,
166                         "incorrect default recursive value")
167         s = SCons.Scanner.Base(function = self.func, recursive = None)
168         self.failUnless(s.recursive == None,
169                         "did not set recursive flag to None")
170         s = SCons.Scanner.Base(function = self.func, recursive = 1)
171         self.failUnless(s.recursive == 1,
172                         "did not set recursive flag to 1")
173
174     def test_get_skeys(self):
175         """Test the Scanner.Base get_skeys() method"""
176         s = SCons.Scanner.Base(function = self.func)
177         sk = s.get_skeys()
178         self.failUnless(sk == [],
179                         "did not initialize to expected []")
180
181         s = SCons.Scanner.Base(function = self.func, skeys = ['.1', '.2'])
182         sk = s.get_skeys()
183         self.failUnless(sk == ['.1', '.2'],
184                         "sk was %s, not ['.1', '.2']")
185
186         s = SCons.Scanner.Base(function = self.func, skeys = '$LIST')
187         env = DummyEnvironment(LIST = ['.3', '.4'])
188         sk = s.get_skeys(env)
189         self.failUnless(sk == ['.3', '.4'],
190                         "sk was %s, not ['.3', '.4']")
191
192 class CurrentTestCase(unittest.TestCase):
193     def test_class(self):
194         """Test the Scanner.Current class"""
195         class MyNode:
196             def __init__(self):
197                 self.called_has_builder = None
198                 self.called_current = None
199                 self.func_called = None
200         class HasNoBuilder(MyNode):
201             def has_builder(self):
202                 self.called_has_builder = 1
203                 return None
204         class IsNotCurrent(MyNode):
205             def has_builder(self):
206                 self.called_has_builder = 1
207                 return 1
208             def current(self, sig):
209                 self.called_current = 1
210                 return None
211         class IsCurrent(MyNode):
212             def has_builder(self):
213                 self.called_has_builder = 1
214                 return 1
215             def current(self, sig):
216                 self.called_current = 1
217                 return 1
218         def func(node, env, path):
219             node.func_called = 1
220             return []
221         env = DummyEnvironment()
222         s = SCons.Scanner.Current(func)
223         path = s.path(env)
224         hnb = HasNoBuilder()
225         s(hnb, env, path)
226         self.failUnless(hnb.called_has_builder, "did not call has_builder()")
227         self.failUnless(not hnb.called_current, "did call current()")
228         self.failUnless(hnb.func_called, "did not call func()")
229         inc = IsNotCurrent()
230         s(inc, env, path)
231         self.failUnless(inc.called_has_builder, "did not call has_builder()")
232         self.failUnless(inc.called_current, "did not call current()")
233         self.failUnless(not inc.func_called, "did call func()")
234         ic = IsCurrent()
235         s(ic, env, path)
236         self.failUnless(ic.called_has_builder, "did not call has_builder()")
237         self.failUnless(ic.called_current, "did not call current()")
238         self.failUnless(ic.func_called, "did not call func()")
239
240 class ClassicTestCase(unittest.TestCase):
241     def test_find_include(self):
242         """Test the Scanner.Classic find_include() method"""
243         env = DummyEnvironment()
244         s = SCons.Scanner.Classic("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
245
246         def _find_file(filename, paths, factory):
247             return paths[0]+'/'+filename
248
249         save = SCons.Node.FS.find_file
250         SCons.Node.FS.find_file = _find_file
251
252         try:
253             n, i = s.find_include('aaa', 'foo', ('path',))
254             assert n == 'foo/aaa', n
255             assert i == 'aaa', i
256
257         finally:
258             SCons.Node.FS.find_file = save
259
260     def test_name(self):
261         """Test setting the Scanner.Classic name"""
262         s = SCons.Scanner.Classic("my_name", ['.s'], 'MYPATH', '^my_inc (\S+)')
263         assert s.name == "my_name", s.name
264
265     def test_scan(self):
266         """Test the Scanner.Classic scan() method"""
267         class MyNode:
268             def __init__(self, name):
269                 self.name = name
270                 self._rfile = self
271                 self.includes = None
272             def rfile(self):
273                 return self._rfile
274             def exists(self):
275                 return self._exists
276             def get_contents(self):
277                 return self._contents
278             def get_dir(self):
279                 return self._dir
280
281         class MyScanner(SCons.Scanner.Classic):
282             def find_include(self, include, source_dir, path):
283                 return include, include
284
285         env = DummyEnvironment()
286         s = MyScanner("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
287
288         # If the node doesn't exist, scanning turns up nothing.
289         n1 = MyNode("n1")
290         n1._exists = None
291         ret = s.scan(n1, env)
292         assert ret == [], ret
293
294         # Verify that it finds includes from the contents.
295         n = MyNode("n")
296         n._exists = 1
297         n._dir = MyNode("n._dir")
298         n._contents = 'my_inc abc\n'
299         ret = s.scan(n, env)
300         assert ret == ['abc'], ret
301
302         # Verify that it uses the cached include info.
303         n._contents = 'my_inc def\n'
304         ret = s.scan(n, env)
305         assert ret == ['abc'], ret
306
307         # Verify that if we wipe the cache, it uses the new contents.
308         n.includes = None
309         ret = s.scan(n, env)
310         assert ret == ['def'], ret
311
312         # Verify that it sorts what it finds.
313         n.includes = ['xyz', 'uvw']
314         ret = s.scan(n, env)
315         assert ret == ['uvw', 'xyz'], ret
316
317         # Verify that we use the rfile() node.
318         nr = MyNode("nr")
319         nr._exists = 1
320         nr._dir = MyNode("nr._dir")
321         nr.includes = ['jkl', 'mno']
322         n._rfile = nr
323         ret = s.scan(n, env)
324         assert ret == ['jkl', 'mno'], ret
325
326 class ClassicCPPTestCase(unittest.TestCase):
327     def test_find_include(self):
328         """Test the Scanner.ClassicCPP find_include() method"""
329         env = DummyEnvironment()
330         s = SCons.Scanner.ClassicCPP("Test", [], None, "")
331
332         def _find_file(filename, paths, factory):
333             return paths[0]+'/'+filename
334
335         save = SCons.Node.FS.find_file
336         SCons.Node.FS.find_file = _find_file
337
338         try:
339             n, i = s.find_include(('"', 'aaa'), 'foo', ('path',))
340             assert n == 'foo/aaa', n
341             assert i == 'aaa', i
342
343             n, i = s.find_include(('<', 'bbb'), 'foo', ('path',))
344             assert n == 'path/bbb', n
345             assert i == 'bbb', i
346
347         finally:
348             SCons.Node.FS.find_file = _find_file
349
350 def suite():
351     suite = unittest.TestSuite()
352     tclasses = [
353                  FindPathDirsTestCase,
354                  ScannerTestCase,
355                  CurrentTestCase,
356                  ClassicTestCase,
357                  ClassicCPPTestCase,
358                ]
359     for tclass in tclasses:
360         names = unittest.getTestCaseNames(tclass, 'test_')
361         suite.addTests(map(tclass, names))
362     return suite
363
364 if __name__ == "__main__":
365     runner = unittest.TextTestRunner()
366     result = runner.run(suite())
367     if not result.wasSuccessful():
368         sys.exit(1)