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.
23 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
25 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
35 return DummyNode(name)
37 class DummyEnvironment(UserDict.UserDict):
38 def __init__(self, dict=None, **kw):
39 UserDict.UserDict.__init__(self, dict)
42 def subst(self, strSubst, target=None, source=None, conv=None):
43 if strSubst[0] == '$':
44 return self.data[strSubst[1:]]
46 def subst_list(self, strSubst, target=None, source=None, conv=None):
47 if strSubst[0] == '$':
48 return [self.data[strSubst[1:]]]
50 def subst_path(self, path, target=None, source=None, conv=None):
51 if not isinstance(path, list):
53 return list(map(self.subst, path))
54 def get_factory(self, factory):
55 return factory or self.fs.File
58 def __init__(self, name, search_result=()):
60 self.search_result = tuple(search_result)
65 def Rfindalldirs(self, pathlist):
66 return self.search_result + pathlist
68 class FindPathDirsTestCase(unittest.TestCase):
69 def test_FindPathDirs(self):
70 """Test the FindPathDirs callable class"""
72 env = DummyEnvironment(LIBPATH = [ 'foo' ])
74 env.fs._cwd = DummyNode('cwd')
76 dir = DummyNode('dir', ['xxx'])
77 fpd = SCons.Scanner.FindPathDirs('LIBPATH')
79 assert str(result) == "('foo',)", result
80 result = fpd(env, dir)
81 assert str(result) == "('xxx', 'foo')", result
83 class ScannerTestCase(unittest.TestCase):
85 def test_creation(self):
86 """Test creation of Scanner objects"""
89 s = SCons.Scanner.Base(func)
90 assert isinstance(s, SCons.Scanner.Base), s
91 s = SCons.Scanner.Base({})
92 assert isinstance(s, SCons.Scanner.Base), s
94 s = SCons.Scanner.Base(func, name='fooscan')
95 assert str(s) == 'fooscan', str(s)
96 s = SCons.Scanner.Base({}, name='barscan')
97 assert str(s) == 'barscan', str(s)
99 s = SCons.Scanner.Base(func, name='fooscan', argument=9)
100 assert str(s) == 'fooscan', str(s)
101 assert s.argument == 9, s.argument
102 s = SCons.Scanner.Base({}, name='fooscan', argument=888)
103 assert str(s) == 'fooscan', str(s)
104 assert s.argument == 888, s.argument
107 class BaseTestCase(unittest.TestCase):
110 def __init__(self, key):
112 def scanner_key(self):
117 def func(self, filename, env, target, *args):
118 self.filename = filename
127 def test(self, scanner, env, filename, deps, *args):
129 path = scanner.path(env)
130 scanned = scanner(filename, env, path)
131 scanned_strs = [str(x) for x in scanned]
133 self.failUnless(self.filename == filename, "the filename was passed incorrectly")
134 self.failUnless(self.env == env, "the environment was passed incorrectly")
135 self.failUnless(scanned_strs == deps, "the dependencies were returned incorrectly")
137 self.failUnless(not isinstance(d, str), "got a string in the dependencies")
140 self.failUnless(self.arg == args[0], "the argument was passed incorrectly")
142 self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been")
144 def test___call__dict(self):
145 """Test calling Scanner.Base objects with a dictionary"""
147 def s1func(node, env, path, called=called):
148 called.append('s1func')
151 def s2func(node, env, path, called=called):
152 called.append('s2func')
155 s1 = SCons.Scanner.Base(s1func)
156 s2 = SCons.Scanner.Base(s2func)
157 selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2})
158 nx = self.skey_node('.x')
159 env = DummyEnvironment()
160 selector(nx, env, [])
161 assert called == ['s1func', nx], called
163 ny = self.skey_node('.y')
164 selector(ny, env, [])
165 assert called == ['s2func', ny], called
168 """Test the Scanner.Base path() method"""
169 def pf(env, cwd, target, source, argument=None):
170 return "pf: %s %s %s %s %s" % \
171 (env.VARIABLE, cwd, target[0], source[0], argument)
173 env = DummyEnvironment()
175 target = DummyNode('target')
176 source = DummyNode('source')
178 s = SCons.Scanner.Base(self.func, path_function=pf)
179 p = s.path(env, 'here', [target], [source])
180 assert p == "pf: v1 here target source None", p
182 s = SCons.Scanner.Base(self.func, path_function=pf, argument="xyz")
183 p = s.path(env, 'here', [target], [source])
184 assert p == "pf: v1 here target source xyz", p
186 def test_positional(self):
187 """Test the Scanner.Base class using positional arguments"""
188 s = SCons.Scanner.Base(self.func, "Pos")
189 env = DummyEnvironment()
190 env.VARIABLE = "var1"
191 self.test(s, env, DummyNode('f1.cpp'), ['f1.h', 'f1.hpp'])
193 env = DummyEnvironment()
195 self.test(s, env, DummyNode('i1.cpp'), ['i1.h', 'i1.hpp'])
197 def test_keywords(self):
198 """Test the Scanner.Base class using keyword arguments"""
199 s = SCons.Scanner.Base(function = self.func, name = "Key")
200 env = DummyEnvironment()
201 env.VARIABLE = "var2"
202 self.test(s, env, DummyNode('f2.cpp'), ['f2.h', 'f2.hpp'])
204 env = DummyEnvironment()
207 self.test(s, env, DummyNode('i2.cpp'), ['i2.h', 'i2.hpp'])
209 def test_pos_opt(self):
210 """Test the Scanner.Base class using both position and optional arguments"""
211 arg = "this is the argument"
212 s = SCons.Scanner.Base(self.func, "PosArg", arg)
213 env = DummyEnvironment()
214 env.VARIABLE = "var3"
215 self.test(s, env, DummyNode('f3.cpp'), ['f3.h', 'f3.hpp'], arg)
217 env = DummyEnvironment()
219 self.test(s, env, DummyNode('i3.cpp'), ['i3.h', 'i3.hpp'], arg)
221 def test_key_opt(self):
222 """Test the Scanner.Base class using both keyword and optional arguments"""
223 arg = "this is another argument"
224 s = SCons.Scanner.Base(function = self.func, name = "KeyArg",
226 env = DummyEnvironment()
227 env.VARIABLE = "var4"
228 self.test(s, env, DummyNode('f4.cpp'), ['f4.h', 'f4.hpp'], arg)
230 env = DummyEnvironment()
232 self.test(s, env, DummyNode('i4.cpp'), ['i4.h', 'i4.hpp'], arg)
234 def test___cmp__(self):
235 """Test the Scanner.Base class __cmp__() method"""
236 s = SCons.Scanner.Base(self.func, "Cmp")
240 """Test the Scanner.Base class __hash__() method"""
241 s = SCons.Scanner.Base(self.func, "Hash")
245 h = hash(dict.keys()[0])
246 self.failUnless(h == i,
247 "hash Scanner base class expected %s, got %s" % (i, h))
249 def test_scan_check(self):
250 """Test the Scanner.Base class scan_check() method"""
251 def my_scan(filename, env, target, *args):
253 def check(node, env, s=self):
254 s.checked[str(node)] = 1
256 env = DummyEnvironment()
257 s = SCons.Scanner.Base(my_scan, "Check", scan_check = check)
260 scanned = s(DummyNode('x'), env, path)
261 self.failUnless(self.checked['x'] == 1,
262 "did not call check function")
264 def test_recursive(self):
265 """Test the Scanner.Base class recursive flag"""
268 s = SCons.Scanner.Base(function = self.func)
269 n = s.recurse_nodes(nodes)
270 self.failUnless(n == [],
271 "default behavior returned nodes: %s" % n)
273 s = SCons.Scanner.Base(function = self.func, recursive = None)
274 n = s.recurse_nodes(nodes)
275 self.failUnless(n == [],
276 "recursive = None returned nodes: %s" % n)
278 s = SCons.Scanner.Base(function = self.func, recursive = 1)
279 n = s.recurse_nodes(nodes)
280 self.failUnless(n == n,
281 "recursive = 1 didn't return all nodes: %s" % n)
284 return [n for n in nodes if n % 2]
285 s = SCons.Scanner.Base(function = self.func, recursive = odd_only)
286 n = s.recurse_nodes(nodes)
287 self.failUnless(n == [1, 3],
288 "recursive = 1 didn't return all nodes: %s" % n)
290 def test_get_skeys(self):
291 """Test the Scanner.Base get_skeys() method"""
292 s = SCons.Scanner.Base(function = self.func)
294 self.failUnless(sk == [],
295 "did not initialize to expected []")
297 s = SCons.Scanner.Base(function = self.func, skeys = ['.1', '.2'])
299 self.failUnless(sk == ['.1', '.2'],
300 "sk was %s, not ['.1', '.2']")
302 s = SCons.Scanner.Base(function = self.func, skeys = '$LIST')
303 env = DummyEnvironment(LIST = ['.3', '.4'])
304 sk = s.get_skeys(env)
305 self.failUnless(sk == ['.3', '.4'],
306 "sk was %s, not ['.3', '.4']")
308 def test_select(self):
309 """Test the Scanner.Base select() method"""
310 scanner = SCons.Scanner.Base(function = self.func)
311 s = scanner.select('.x')
312 assert s is scanner, s
314 selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
315 s = selector.select(self.skey_node('.x'))
317 s = selector.select(self.skey_node('.y'))
319 s = selector.select(self.skey_node('.z'))
322 def test_add_scanner(self):
323 """Test the Scanner.Base add_scanner() method"""
324 selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
325 s = selector.select(self.skey_node('.z'))
327 selector.add_scanner('.z', 3)
328 s = selector.select(self.skey_node('.z'))
331 def test___str__(self):
332 """Test the Scanner.Base __str__() method"""
333 scanner = SCons.Scanner.Base(function = self.func)
335 assert s == 'NONE', s
336 scanner = SCons.Scanner.Base(function = self.func, name = 'xyzzy')
338 assert s == 'xyzzy', s
340 class SelectorTestCase(unittest.TestCase):
342 def __init__(self, key):
344 def scanner_key(self):
349 def test___init__(self):
350 """Test creation of Scanner.Selector object"""
351 s = SCons.Scanner.Selector({})
352 assert isinstance(s, SCons.Scanner.Selector), s
353 assert s.dict == {}, s.dict
355 def test___call__(self):
356 """Test calling Scanner.Selector objects"""
358 def s1func(node, env, path, called=called):
359 called.append('s1func')
362 def s2func(node, env, path, called=called):
363 called.append('s2func')
366 s1 = SCons.Scanner.Base(s1func)
367 s2 = SCons.Scanner.Base(s2func)
368 selector = SCons.Scanner.Selector({'.x' : s1, '.y' : s2})
369 nx = self.skey_node('.x')
370 env = DummyEnvironment()
371 selector(nx, env, [])
372 assert called == ['s1func', nx], called
374 ny = self.skey_node('.y')
375 selector(ny, env, [])
376 assert called == ['s2func', ny], called
378 def test_select(self):
379 """Test the Scanner.Selector select() method"""
380 selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
381 s = selector.select(self.skey_node('.x'))
383 s = selector.select(self.skey_node('.y'))
385 s = selector.select(self.skey_node('.z'))
388 def test_add_scanner(self):
389 """Test the Scanner.Selector add_scanner() method"""
390 selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
391 s = selector.select(self.skey_node('.z'))
393 selector.add_scanner('.z', 3)
394 s = selector.select(self.skey_node('.z'))
397 class CurrentTestCase(unittest.TestCase):
398 def test_class(self):
399 """Test the Scanner.Current class"""
402 self.called_has_builder = None
403 self.called_is_up_to_date = None
404 self.func_called = None
407 class HasNoBuilder(MyNode):
408 def has_builder(self):
409 self.called_has_builder = 1
411 class IsNotCurrent(MyNode):
412 def has_builder(self):
413 self.called_has_builder = 1
415 def is_up_to_date(self):
416 self.called_is_up_to_date = 1
418 class IsCurrent(MyNode):
419 def has_builder(self):
420 self.called_has_builder = 1
422 def is_up_to_date(self):
423 self.called_is_up_to_date = 1
425 def func(node, env, path):
428 env = DummyEnvironment()
429 s = SCons.Scanner.Current(func)
433 self.failUnless(hnb.called_has_builder, "did not call has_builder()")
434 self.failUnless(not hnb.called_is_up_to_date, "did call is_up_to_date()")
435 self.failUnless(hnb.func_called, "did not call func()")
438 self.failUnless(inc.called_has_builder, "did not call has_builder()")
439 self.failUnless(inc.called_is_up_to_date, "did not call is_up_to_date()")
440 self.failUnless(not inc.func_called, "did call func()")
443 self.failUnless(ic.called_has_builder, "did not call has_builder()")
444 self.failUnless(ic.called_is_up_to_date, "did not call is_up_to_date()")
445 self.failUnless(ic.func_called, "did not call func()")
447 class ClassicTestCase(unittest.TestCase):
448 def test_find_include(self):
449 """Test the Scanner.Classic find_include() method"""
450 env = DummyEnvironment()
451 s = SCons.Scanner.Classic("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
453 def _find_file(filename, paths):
454 return paths[0]+'/'+filename
456 save = SCons.Node.FS.find_file
457 SCons.Node.FS.find_file = _find_file
460 n, i = s.find_include('aaa', 'foo', ('path',))
461 assert n == 'foo/aaa', n
465 SCons.Node.FS.find_file = save
468 """Test setting the Scanner.Classic name"""
469 s = SCons.Scanner.Classic("my_name", ['.s'], 'MYPATH', '^my_inc (\S+)')
470 assert s.name == "my_name", s.name
473 """Test the Scanner.Classic scan() method"""
475 def __init__(self, name):
483 def get_contents(self):
484 return self._contents
485 def get_text_contents(self):
486 return self._contents
490 class MyScanner(SCons.Scanner.Classic):
491 def find_include(self, include, source_dir, path):
492 return include, include
494 env = DummyEnvironment()
495 s = MyScanner("t", ['.suf'], 'MYPATH', '^my_inc (\S+)')
497 # This set of tests is intended to test the scanning operation
498 # of the Classic scanner.
500 # Note that caching has been added for not just the includes
501 # but the entire scan call. The caching is based on the
502 # arguments, so we will fiddle with the path parameter to
503 # defeat this caching for the purposes of these tests.
505 # If the node doesn't exist, scanning turns up nothing.
508 ret = s.function(n1, env)
509 assert ret == [], ret
511 # Verify that it finds includes from the contents.
514 n._dir = MyNode("n._dir")
515 n._contents = 'my_inc abc\n'
516 ret = s.function(n, env, ('foo',))
517 assert ret == ['abc'], ret
519 # Verify that it uses the cached include info.
520 n._contents = 'my_inc def\n'
521 ret = s.function(n, env, ('foo2',))
522 assert ret == ['abc'], ret
524 # Verify that if we wipe the cache, it uses the new contents.
526 ret = s.function(n, env, ('foo3',))
527 assert ret == ['def'], ret
529 # We no longer cache overall scan results, which would be returned
530 # if individual results are de-cached. If we ever restore that
531 # functionality, this test goes back here.
532 #ret = s.function(n, env, ('foo2',))
533 #assert ret == ['abc'], 'caching inactive; got: %s'%ret
535 # Verify that it sorts what it finds.
536 n.includes = ['xyz', 'uvw']
537 ret = s.function(n, env, ('foo4',))
538 assert ret == ['uvw', 'xyz'], ret
540 # Verify that we use the rfile() node.
543 nr._dir = MyNode("nr._dir")
544 nr.includes = ['jkl', 'mno']
546 ret = s.function(n, env, ('foo5',))
547 assert ret == ['jkl', 'mno'], ret
551 class ClassicCPPTestCase(unittest.TestCase):
552 def test_find_include(self):
553 """Test the Scanner.ClassicCPP find_include() method"""
554 env = DummyEnvironment()
555 s = SCons.Scanner.ClassicCPP("Test", [], None, "")
557 def _find_file(filename, paths):
558 return paths[0]+'/'+filename
560 save = SCons.Node.FS.find_file
561 SCons.Node.FS.find_file = _find_file
564 n, i = s.find_include(('"', 'aaa'), 'foo', ('path',))
565 assert n == 'foo/aaa', n
568 n, i = s.find_include(('<', 'bbb'), 'foo', ('path',))
569 assert n == 'path/bbb', n
572 # TODO(1.5): remove when 2.2 is minimal; replace ccc
573 # variable in find_include() call below with in-line u'ccc'.
579 n, i = s.find_include(('<', ccc), 'foo', ('path',))
580 assert n == 'path/ccc', n
584 SCons.Node.FS.find_file = save
587 suite = unittest.TestSuite()
589 FindPathDirsTestCase,
597 for tclass in tclasses:
598 names = unittest.getTestCaseNames(tclass, 'test_')
599 suite.addTests(list(map(tclass, names)))
602 if __name__ == "__main__":
603 runner = unittest.TextTestRunner()
604 result = runner.run(suite())
605 if not result.wasSuccessful():
610 # indent-tabs-mode:nil
612 # vim: set expandtab tabstop=4 shiftwidth=4: