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:
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
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.
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
34 return DummyNode(name)
36 class DummyEnvironment(UserDict.UserDict):
37 def __init__(self, dict=None, **kw):
38 UserDict.UserDict.__init__(self, dict)
41 def subst(self, strSubst, target=None, source=None, conv=None):
42 if strSubst[0] == '$':
43 return self.data[strSubst[1:]]
45 def subst_list(self, strSubst, target=None, source=None, conv=None):
46 if strSubst[0] == '$':
47 return [self.data[strSubst[1:]]]
49 def subst_path(self, path, target=None, source=None, conv=None):
50 if type(path) != type([]):
52 return map(self.subst, path)
53 def get_factory(self, factory):
54 return factory or self.fs.File
57 def __init__(self, name, search_result=()):
59 self.search_result = tuple(search_result)
64 def Rfindalldirs(self, pathlist):
65 return self.search_result + pathlist
67 class FindPathDirsTestCase(unittest.TestCase):
68 def test_FindPathDirs(self):
69 """Test the FindPathDirs callable class"""
71 env = DummyEnvironment(LIBPATH = [ 'foo' ])
73 env.fs._cwd = DummyNode('cwd')
75 dir = DummyNode('dir', ['xxx'])
76 fpd = SCons.Scanner.FindPathDirs('LIBPATH')
78 assert str(result) == "('foo',)", result
79 result = fpd(env, dir)
80 assert str(result) == "('xxx', 'foo')", result
82 class ScannerTestCase(unittest.TestCase):
84 def test_creation(self):
85 """Test creation of Scanner objects"""
88 s = SCons.Scanner.Base(func)
89 assert isinstance(s, SCons.Scanner.Base), s
90 s = SCons.Scanner.Base({})
91 assert isinstance(s, SCons.Scanner.Base), s
93 s = SCons.Scanner.Base(func, name='fooscan')
94 assert str(s) == 'fooscan', str(s)
95 s = SCons.Scanner.Base({}, name='barscan')
96 assert str(s) == 'barscan', str(s)
98 s = SCons.Scanner.Base(func, name='fooscan', argument=9)
99 assert str(s) == 'fooscan', str(s)
100 assert s.argument == 9, s.argument
101 s = SCons.Scanner.Base({}, name='fooscan', argument=888)
102 assert str(s) == 'fooscan', str(s)
103 assert s.argument == 888, s.argument
106 class BaseTestCase(unittest.TestCase):
109 def __init__(self, key):
111 def scanner_key(self):
116 def func(self, filename, env, target, *args):
117 self.filename = filename
126 def test(self, scanner, env, filename, deps, *args):
128 path = scanner.path(env)
129 scanned = scanner(filename, env, path)
130 scanned_strs = map(lambda x: str(x), scanned)
132 self.failUnless(self.filename == filename, "the filename was passed incorrectly")
133 self.failUnless(self.env == env, "the environment was passed incorrectly")
134 self.failUnless(scanned_strs == deps, "the dependencies were returned incorrectly")
136 self.failUnless(type(d) != type(""), "got a string in the dependencies")
139 self.failUnless(self.arg == args[0], "the argument was passed incorrectly")
141 self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been")
143 def test___call__dict(self):
144 """Test calling Scanner.Base objects with a dictionary"""
146 def s1func(node, env, path, called=called):
147 called.append('s1func')
150 def s2func(node, env, path, called=called):
151 called.append('s2func')
154 s1 = SCons.Scanner.Base(s1func)
155 s2 = SCons.Scanner.Base(s2func)
156 selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2})
157 nx = self.skey_node('.x')
158 env = DummyEnvironment()
159 selector(nx, env, [])
160 assert called == ['s1func', nx], called
162 ny = self.skey_node('.y')
163 selector(ny, env, [])
164 assert called == ['s2func', ny], called
167 """Test the Scanner.Base path() method"""
168 def pf(env, cwd, target, source, argument=None):
169 return "pf: %s %s %s %s %s" % \
170 (env.VARIABLE, cwd, target[0], source[0], argument)
172 env = DummyEnvironment()
174 target = DummyNode('target')
175 source = DummyNode('source')
177 s = SCons.Scanner.Base(self.func, path_function=pf)
178 p = s.path(env, 'here', [target], [source])
179 assert p == "pf: v1 here target source None", p
181 s = SCons.Scanner.Base(self.func, path_function=pf, argument="xyz")
182 p = s.path(env, 'here', [target], [source])
183 assert p == "pf: v1 here target source xyz", p
185 def test_positional(self):
186 """Test the Scanner.Base class using positional arguments"""
187 s = SCons.Scanner.Base(self.func, "Pos")
188 env = DummyEnvironment()
189 env.VARIABLE = "var1"
190 self.test(s, env, DummyNode('f1.cpp'), ['f1.h', 'f1.hpp'])
192 env = DummyEnvironment()
194 self.test(s, env, DummyNode('i1.cpp'), ['i1.h', 'i1.hpp'])
196 def test_keywords(self):
197 """Test the Scanner.Base class using keyword arguments"""
198 s = SCons.Scanner.Base(function = self.func, name = "Key")
199 env = DummyEnvironment()
200 env.VARIABLE = "var2"
201 self.test(s, env, DummyNode('f2.cpp'), ['f2.h', 'f2.hpp'])
203 env = DummyEnvironment()
206 self.test(s, env, DummyNode('i2.cpp'), ['i2.h', 'i2.hpp'])
208 def test_pos_opt(self):
209 """Test the Scanner.Base class using both position and optional arguments"""
210 arg = "this is the argument"
211 s = SCons.Scanner.Base(self.func, "PosArg", arg)
212 env = DummyEnvironment()
213 env.VARIABLE = "var3"
214 self.test(s, env, DummyNode('f3.cpp'), ['f3.h', 'f3.hpp'], arg)
216 env = DummyEnvironment()
218 self.test(s, env, DummyNode('i3.cpp'), ['i3.h', 'i3.hpp'], arg)
220 def test_key_opt(self):
221 """Test the Scanner.Base class using both keyword and optional arguments"""
222 arg = "this is another argument"
223 s = SCons.Scanner.Base(function = self.func, name = "KeyArg",
225 env = DummyEnvironment()
226 env.VARIABLE = "var4"
227 self.test(s, env, DummyNode('f4.cpp'), ['f4.h', 'f4.hpp'], arg)
229 env = DummyEnvironment()
231 self.test(s, env, DummyNode('i4.cpp'), ['i4.h', 'i4.hpp'], arg)
233 def test___cmp__(self):
234 """Test the Scanner.Base class __cmp__() method"""
235 s = SCons.Scanner.Base(self.func, "Cmp")
239 """Test the Scanner.Base class __hash__() method"""
240 s = SCons.Scanner.Base(self.func, "Hash")
244 h = hash(dict.keys()[0])
245 self.failUnless(h == i,
246 "hash Scanner base class expected %s, got %s" % (i, h))
248 def test_scan_check(self):
249 """Test the Scanner.Base class scan_check() method"""
250 def my_scan(filename, env, target, *args):
252 def check(node, env, s=self):
253 s.checked[str(node)] = 1
255 env = DummyEnvironment()
256 s = SCons.Scanner.Base(my_scan, "Check", scan_check = check)
259 scanned = s(DummyNode('x'), env, path)
260 self.failUnless(self.checked['x'] == 1,
261 "did not call check function")
263 def test_recursive(self):
264 """Test the Scanner.Base class recursive flag"""
267 s = SCons.Scanner.Base(function = self.func)
268 n = s.recurse_nodes(nodes)
269 self.failUnless(n == [],
270 "default behavior returned nodes: %s" % n)
272 s = SCons.Scanner.Base(function = self.func, recursive = None)
273 n = s.recurse_nodes(nodes)
274 self.failUnless(n == [],
275 "recursive = None returned nodes: %s" % n)
277 s = SCons.Scanner.Base(function = self.func, recursive = 1)
278 n = s.recurse_nodes(nodes)
279 self.failUnless(n == n,
280 "recursive = 1 didn't return all nodes: %s" % n)
283 return filter(lambda n: n % 2, nodes)
284 s = SCons.Scanner.Base(function = self.func, recursive = odd_only)
285 n = s.recurse_nodes(nodes)
286 self.failUnless(n == [1, 3],
287 "recursive = 1 didn't return all nodes: %s" % n)
289 def test_get_skeys(self):
290 """Test the Scanner.Base get_skeys() method"""
291 s = SCons.Scanner.Base(function = self.func)
293 self.failUnless(sk == [],
294 "did not initialize to expected []")
296 s = SCons.Scanner.Base(function = self.func, skeys = ['.1', '.2'])
298 self.failUnless(sk == ['.1', '.2'],
299 "sk was %s, not ['.1', '.2']")
301 s = SCons.Scanner.Base(function = self.func, skeys = '$LIST')
302 env = DummyEnvironment(LIST = ['.3', '.4'])
303 sk = s.get_skeys(env)
304 self.failUnless(sk == ['.3', '.4'],
305 "sk was %s, not ['.3', '.4']")
307 def test_select(self):
308 """Test the Scanner.Base select() method"""
309 scanner = SCons.Scanner.Base(function = self.func)
310 s = scanner.select('.x')
311 assert s is scanner, s
313 selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
314 s = selector.select(self.skey_node('.x'))
316 s = selector.select(self.skey_node('.y'))
318 s = selector.select(self.skey_node('.z'))
321 def test_add_scanner(self):
322 """Test the Scanner.Base add_scanner() method"""
323 selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
324 s = selector.select(self.skey_node('.z'))
326 selector.add_scanner('.z', 3)
327 s = selector.select(self.skey_node('.z'))
330 def test___str__(self):
331 """Test the Scanner.Base __str__() method"""
332 scanner = SCons.Scanner.Base(function = self.func)
334 assert s == 'NONE', s
335 scanner = SCons.Scanner.Base(function = self.func, name = 'xyzzy')
337 assert s == 'xyzzy', s
339 class SelectorTestCase(unittest.TestCase):
341 def __init__(self, key):
343 def scanner_key(self):
348 def test___init__(self):
349 """Test creation of Scanner.Selector object"""
350 s = SCons.Scanner.Selector({})
351 assert isinstance(s, SCons.Scanner.Selector), s
352 assert s.dict == {}, s.dict
354 def test___call__(self):
355 """Test calling Scanner.Selector objects"""
357 def s1func(node, env, path, called=called):
358 called.append('s1func')
361 def s2func(node, env, path, called=called):
362 called.append('s2func')
365 s1 = SCons.Scanner.Base(s1func)
366 s2 = SCons.Scanner.Base(s2func)
367 selector = SCons.Scanner.Selector({'.x' : s1, '.y' : s2})
368 nx = self.skey_node('.x')
369 env = DummyEnvironment()
370 selector(nx, env, [])
371 assert called == ['s1func', nx], called
373 ny = self.skey_node('.y')
374 selector(ny, env, [])
375 assert called == ['s2func', ny], called
377 def test_select(self):
378 """Test the Scanner.Selector select() method"""
379 selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
380 s = selector.select(self.skey_node('.x'))
382 s = selector.select(self.skey_node('.y'))
384 s = selector.select(self.skey_node('.z'))
387 def test_add_scanner(self):
388 """Test the Scanner.Selector add_scanner() method"""
389 selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
390 s = selector.select(self.skey_node('.z'))
392 selector.add_scanner('.z', 3)
393 s = selector.select(self.skey_node('.z'))
396 class CurrentTestCase(unittest.TestCase):
397 def test_class(self):
398 """Test the Scanner.Current class"""
401 self.called_has_builder = None
402 self.called_is_up_to_date = None
403 self.func_called = None
406 class HasNoBuilder(MyNode):
407 def has_builder(self):
408 self.called_has_builder = 1
410 class IsNotCurrent(MyNode):
411 def has_builder(self):
412 self.called_has_builder = 1
414 def is_up_to_date(self):
415 self.called_is_up_to_date = 1
417 class IsCurrent(MyNode):
418 def has_builder(self):
419 self.called_has_builder = 1
421 def is_up_to_date(self):
422 self.called_is_up_to_date = 1
424 def func(node, env, path):
427 env = DummyEnvironment()
428 s = SCons.Scanner.Current(func)
432 self.failUnless(hnb.called_has_builder, "did not call has_builder()")
433 self.failUnless(not hnb.called_is_up_to_date, "did call is_up_to_date()")
434 self.failUnless(hnb.func_called, "did not call func()")
437 self.failUnless(inc.called_has_builder, "did not call has_builder()")
438 self.failUnless(inc.called_is_up_to_date, "did not call is_up_to_date()")
439 self.failUnless(not inc.func_called, "did call func()")
442 self.failUnless(ic.called_has_builder, "did not call has_builder()")
443 self.failUnless(ic.called_is_up_to_date, "did not call is_up_to_date()")
444 self.failUnless(ic.func_called, "did not call func()")
446 class ClassicTestCase(unittest.TestCase):
447 def test_find_include(self):
448 """Test the Scanner.Classic find_include() method"""
449 env = DummyEnvironment()
450 s = SCons.Scanner.Classic("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
452 def _find_file(filename, paths):
453 return paths[0]+'/'+filename
455 save = SCons.Node.FS.find_file
456 SCons.Node.FS.find_file = _find_file
459 n, i = s.find_include('aaa', 'foo', ('path',))
460 assert n == 'foo/aaa', n
464 SCons.Node.FS.find_file = save
467 """Test setting the Scanner.Classic name"""
468 s = SCons.Scanner.Classic("my_name", ['.s'], 'MYPATH', '^my_inc (\S+)')
469 assert s.name == "my_name", s.name
472 """Test the Scanner.Classic scan() method"""
474 def __init__(self, name):
482 def get_contents(self):
483 return self._contents
484 def get_text_contents(self):
485 return self._contents
489 class MyScanner(SCons.Scanner.Classic):
490 def find_include(self, include, source_dir, path):
491 return include, include
493 env = DummyEnvironment()
494 s = MyScanner("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
496 # This set of tests is intended to test the scanning operation
497 # of the Classic scanner.
499 # Note that caching has been added for not just the includes
500 # but the entire scan call. The caching is based on the
501 # arguments, so we will fiddle with the path parameter to
502 # defeat this caching for the purposes of these tests.
504 # If the node doesn't exist, scanning turns up nothing.
507 ret = s.function(n1, env)
508 assert ret == [], ret
510 # Verify that it finds includes from the contents.
513 n._dir = MyNode("n._dir")
514 n._contents = 'my_inc abc\n'
515 ret = s.function(n, env, ('foo',))
516 assert ret == ['abc'], ret
518 # Verify that it uses the cached include info.
519 n._contents = 'my_inc def\n'
520 ret = s.function(n, env, ('foo2',))
521 assert ret == ['abc'], ret
523 # Verify that if we wipe the cache, it uses the new contents.
525 ret = s.function(n, env, ('foo3',))
526 assert ret == ['def'], ret
528 # We no longer cache overall scan results, which would be returned
529 # if individual results are de-cached. If we ever restore that
530 # functionality, this test goes back here.
531 #ret = s.function(n, env, ('foo2',))
532 #assert ret == ['abc'], 'caching inactive; got: %s'%ret
534 # Verify that it sorts what it finds.
535 n.includes = ['xyz', 'uvw']
536 ret = s.function(n, env, ('foo4',))
537 assert ret == ['uvw', 'xyz'], ret
539 # Verify that we use the rfile() node.
542 nr._dir = MyNode("nr._dir")
543 nr.includes = ['jkl', 'mno']
545 ret = s.function(n, env, ('foo5',))
546 assert ret == ['jkl', 'mno'], ret
550 class ClassicCPPTestCase(unittest.TestCase):
551 def test_find_include(self):
552 """Test the Scanner.ClassicCPP find_include() method"""
553 env = DummyEnvironment()
554 s = SCons.Scanner.ClassicCPP("Test", [], None, "")
556 def _find_file(filename, paths):
557 return paths[0]+'/'+filename
559 save = SCons.Node.FS.find_file
560 SCons.Node.FS.find_file = _find_file
563 n, i = s.find_include(('"', 'aaa'), 'foo', ('path',))
564 assert n == 'foo/aaa', n
567 n, i = s.find_include(('<', 'bbb'), 'foo', ('path',))
568 assert n == 'path/bbb', n
571 # TODO(1.5): remove when 2.2 is minimal; replace ccc
572 # variable in find_include() call below with in-line u'ccc'.
578 n, i = s.find_include(('<', ccc), 'foo', ('path',))
579 assert n == 'path/ccc', n
583 SCons.Node.FS.find_file = save
586 suite = unittest.TestSuite()
588 FindPathDirsTestCase,
596 for tclass in tclasses:
597 names = unittest.getTestCaseNames(tclass, 'test_')
598 suite.addTests(map(tclass, names))
601 if __name__ == "__main__":
602 runner = unittest.TextTestRunner()
603 result = runner.run(suite())
604 if not result.wasSuccessful():
609 # indent-tabs-mode:nil
611 # vim: set expandtab tabstop=4 shiftwidth=4: