3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 from SCons
.Scanner
import ScannerBase
, Selector
, Classic
, ClassicCPP
, Current
, FindPathDirs
35 return DummyNode(name
)
37 class DummyEnvironment(collections
.UserDict
):
38 def __init__(self
, mapping
=None, **kwargs
) -> None:
39 super().__init
__(mapping
)
40 self
.data
.update(kwargs
)
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
=()) -> None:
60 self
.search_result
= tuple(search_result
)
62 def rexists(self
) -> bool:
65 def __str__(self
) -> str:
68 def Rfindalldirs(self
, pathlist
):
69 return self
.search_result
+ pathlist
70 def __repr__(self
) -> str:
72 def __eq__(self
, other
):
73 return self
.name
== other
.name
75 def __repr__(self
) -> str:
78 def __eq__(self
, other
):
79 return self
.name
== other
.name
81 class FindPathDirsTestCase(unittest
.TestCase
):
82 def test_FindPathDirs(self
) -> None:
83 """Test the FindPathDirs callable class"""
85 env
= DummyEnvironment(LIBPATH
= [ 'foo' ])
87 env
.fs
._cwd
= DummyNode('cwd')
89 dir = DummyNode('dir', ['xxx'])
90 fpd
= FindPathDirs('LIBPATH')
92 assert str(result
) == "('foo',)", result
93 result
= fpd(env
, dir)
94 assert str(result
) == "('xxx', 'foo')", result
96 class ScannerTestCase(unittest
.TestCase
):
98 def test_creation(self
) -> None:
99 """Test creation of Scanner objects"""
100 def func(self
) -> None:
102 s
= ScannerBase(func
)
103 assert isinstance(s
, ScannerBase
), s
105 assert isinstance(s
, ScannerBase
), s
107 s
= ScannerBase(func
, name
='fooscan')
108 assert str(s
) == 'fooscan', str(s
)
109 s
= ScannerBase({}, name
='barscan')
110 assert str(s
) == 'barscan', str(s
)
112 s
= ScannerBase(func
, name
='fooscan', argument
=9)
113 assert str(s
) == 'fooscan', str(s
)
114 assert s
.argument
== 9, s
.argument
115 s
= ScannerBase({}, name
='fooscan', argument
=888)
116 assert str(s
) == 'fooscan', str(s
)
117 assert s
.argument
== 888, s
.argument
120 class ScannerBaseTestCase(unittest
.TestCase
):
123 def __init__(self
, key
) -> None:
125 def scanner_key(self
):
127 def rexists(self
) -> bool:
130 def func(self
, filename
, env
, target
, *args
):
131 self
.filename
= filename
140 def test(self
, scanner
, env
, filename
, deps
, *args
) -> None:
142 path
= scanner
.path(env
)
143 scanned
= scanner(filename
, env
, path
)
144 scanned_strs
= [str(x
) for x
in scanned
]
146 self
.assertTrue(self
.filename
== filename
, "the filename was passed incorrectly")
147 self
.assertTrue(self
.env
== env
, "the environment was passed incorrectly")
148 self
.assertTrue(scanned_strs
== deps
, "the dependencies were returned incorrectly")
150 self
.assertTrue(not isinstance(d
, str), "got a string in the dependencies")
153 self
.assertTrue(self
.arg
== args
[0], "the argument was passed incorrectly")
155 self
.assertFalse(hasattr(self
, "arg"), "an argument was given when it shouldn't have been")
157 def test___call__dict(self
) -> None:
158 """Test calling ScannerBase objects with a dictionary"""
160 def s1func(node
, env
, path
, called
=called
):
161 called
.append('s1func')
164 def s2func(node
, env
, path
, called
=called
):
165 called
.append('s2func')
168 s1
= ScannerBase(s1func
)
169 s2
= ScannerBase(s2func
)
170 selector
= ScannerBase({'.x' : s1
, '.y' : s2
})
171 nx
= self
.skey_node('.x')
172 env
= DummyEnvironment()
173 selector(nx
, env
, [])
174 assert called
== ['s1func', nx
], called
176 ny
= self
.skey_node('.y')
177 selector(ny
, env
, [])
178 assert called
== ['s2func', ny
], called
180 def test_path(self
) -> None:
181 """Test the ScannerBase path() method"""
182 def pf(env
, cwd
, target
, source
, argument
=None) -> str:
183 return "pf: %s %s %s %s %s" % \
184 (env
.VARIABLE
, cwd
, target
[0], source
[0], argument
)
186 env
= DummyEnvironment()
188 target
= DummyNode('target')
189 source
= DummyNode('source')
191 s
= ScannerBase(self
.func
, path_function
=pf
)
192 p
= s
.path(env
, 'here', [target
], [source
])
193 assert p
== "pf: v1 here target source None", p
195 s
= ScannerBase(self
.func
, path_function
=pf
, argument
="xyz")
196 p
= s
.path(env
, 'here', [target
], [source
])
197 assert p
== "pf: v1 here target source xyz", p
199 def test_positional(self
) -> None:
200 """Test the ScannerBase class using positional arguments"""
201 s
= ScannerBase(self
.func
, "Pos")
202 env
= DummyEnvironment()
203 env
.VARIABLE
= "var1"
204 self
.test(s
, env
, DummyNode('f1.cpp'), ['f1.h', 'f1.hpp'])
206 env
= DummyEnvironment()
208 self
.test(s
, env
, DummyNode('i1.cpp'), ['i1.h', 'i1.hpp'])
210 def test_keywords(self
) -> None:
211 """Test the ScannerBase class using keyword arguments"""
212 s
= ScannerBase(function
= self
.func
, name
= "Key")
213 env
= DummyEnvironment()
214 env
.VARIABLE
= "var2"
215 self
.test(s
, env
, DummyNode('f2.cpp'), ['f2.h', 'f2.hpp'])
217 env
= DummyEnvironment()
220 self
.test(s
, env
, DummyNode('i2.cpp'), ['i2.h', 'i2.hpp'])
222 def test_pos_opt(self
) -> None:
223 """Test the ScannerBase class using both position and optional arguments"""
224 arg
= "this is the argument"
225 s
= ScannerBase(self
.func
, "PosArg", arg
)
226 env
= DummyEnvironment()
227 env
.VARIABLE
= "var3"
228 self
.test(s
, env
, DummyNode('f3.cpp'), ['f3.h', 'f3.hpp'], arg
)
230 env
= DummyEnvironment()
232 self
.test(s
, env
, DummyNode('i3.cpp'), ['i3.h', 'i3.hpp'], arg
)
234 def test_key_opt(self
) -> None:
235 """Test the ScannerBase class using both keyword and optional arguments"""
236 arg
= "this is another argument"
237 s
= ScannerBase(function
= self
.func
, name
= "KeyArg", argument
= arg
)
238 env
= DummyEnvironment()
239 env
.VARIABLE
= "var4"
240 self
.test(s
, env
, DummyNode('f4.cpp'), ['f4.h', 'f4.hpp'], arg
)
242 env
= DummyEnvironment()
244 self
.test(s
, env
, DummyNode('i4.cpp'), ['i4.h', 'i4.hpp'], arg
)
246 def test___cmp__(self
) -> None:
247 """Test the ScannerBase class __cmp__() method"""
248 s
= ScannerBase(self
.func
, "Cmp")
251 def test_hash(self
) -> None:
252 """Test the ScannerBase class __hash__() method"""
253 s
= ScannerBase(self
.func
, "Hash")
257 h
= hash(list(mapping
)[0])
258 self
.assertTrue(h
== i
,
259 "hash Scanner base class expected %s, got %s" % (i
, h
))
261 def test_scan_check(self
) -> None:
262 """Test the ScannerBase class scan_check() method"""
263 def my_scan(filename
, env
, target
, *args
):
265 def check(node
, env
, s
=self
) -> int:
266 s
.checked
[str(node
)] = 1
268 env
= DummyEnvironment()
269 s
= ScannerBase(my_scan
, "Check", scan_check
= check
)
272 scanned
= s(DummyNode('x'), env
, path
)
273 self
.assertTrue(self
.checked
['x'] == 1,
274 "did not call check function")
276 def test_recursive(self
) -> None:
277 """Test the ScannerBase class recursive flag"""
280 s
= ScannerBase(function
= self
.func
)
281 n
= s
.recurse_nodes(nodes
)
282 self
.assertTrue(n
== [], "default behavior returned nodes: %s" % n
)
284 s
= ScannerBase(function
= self
.func
, recursive
= None)
285 n
= s
.recurse_nodes(nodes
)
286 self
.assertTrue(n
== [], "recursive = None returned nodes: %s" % n
)
288 s
= ScannerBase(function
= self
.func
, recursive
= 1)
289 n
= s
.recurse_nodes(nodes
)
290 self
.assertTrue(n
== n
, "recursive = 1 didn't return all nodes: %s" % n
)
293 return [n
for n
in nodes
if n
% 2]
294 s
= ScannerBase(function
= self
.func
, recursive
= odd_only
)
295 n
= s
.recurse_nodes(nodes
)
296 self
.assertTrue(n
== [1, 3], "recursive = 1 didn't return all nodes: %s" % n
)
298 def test_get_skeys(self
) -> None:
299 """Test the ScannerBase get_skeys() method"""
300 s
= ScannerBase(function
= self
.func
)
302 self
.assertTrue(sk
== [], "did not initialize to expected []")
304 s
= ScannerBase(function
= self
.func
, skeys
= ['.1', '.2'])
306 self
.assertTrue(sk
== ['.1', '.2'], "sk was %s, not ['.1', '.2']")
308 s
= ScannerBase(function
= self
.func
, skeys
= '$LIST')
309 env
= DummyEnvironment(LIST
= ['.3', '.4'])
310 sk
= s
.get_skeys(env
)
311 self
.assertTrue(sk
== ['.3', '.4'], "sk was %s, not ['.3', '.4']")
313 def test_select(self
) -> None:
314 """Test the ScannerBase select() method"""
315 scanner
= ScannerBase(function
= self
.func
)
316 s
= scanner
.select('.x')
317 assert s
is scanner
, s
319 selector
= ScannerBase({'.x' : 1, '.y' : 2})
320 s
= selector
.select(self
.skey_node('.x'))
322 s
= selector
.select(self
.skey_node('.y'))
324 s
= selector
.select(self
.skey_node('.z'))
327 def test_add_scanner(self
) -> None:
328 """Test the ScannerBase add_scanner() method"""
329 selector
= ScannerBase({'.x' : 1, '.y' : 2})
330 s
= selector
.select(self
.skey_node('.z'))
332 selector
.add_scanner('.z', 3)
333 s
= selector
.select(self
.skey_node('.z'))
336 def test___str__(self
) -> None:
337 """Test the ScannerBase __str__() method"""
338 scanner
= ScannerBase(function
= self
.func
)
340 assert s
== 'NONE', s
341 scanner
= ScannerBase(function
= self
.func
, name
= 'xyzzy')
343 assert s
== 'xyzzy', s
345 class SelectorTestCase(unittest
.TestCase
):
348 def __init__(self
, key
) -> None:
350 def scanner_key(self
):
352 def rexists(self
) -> bool:
355 def test___init__(self
) -> None:
356 """Test creation of Scanner.Selector object"""
358 assert isinstance(s
, Selector
), s
359 assert s
.mapping
== {}, s
.mapping
361 def test___call__(self
) -> None:
362 """Test calling Scanner.Selector objects"""
364 def s1func(node
, env
, path
, called
=called
):
365 called
.append('s1func')
368 def s2func(node
, env
, path
, called
=called
):
369 called
.append('s2func')
372 s1
= ScannerBase(s1func
)
373 s2
= ScannerBase(s2func
)
374 selector
= Selector({'.x' : s1
, '.y' : s2
})
375 nx
= self
.skey_node('.x')
376 env
= DummyEnvironment()
377 selector(nx
, env
, [])
378 assert called
== ['s1func', nx
], called
380 ny
= self
.skey_node('.y')
381 selector(ny
, env
, [])
382 assert called
== ['s2func', ny
], called
384 def test_select(self
) -> None:
385 """Test the Scanner.Selector select() method"""
386 selector
= Selector({'.x' : 1, '.y' : 2})
387 s
= selector
.select(self
.skey_node('.x'))
389 s
= selector
.select(self
.skey_node('.y'))
391 s
= selector
.select(self
.skey_node('.z'))
394 def test_add_scanner(self
) -> None:
395 """Test the Scanner.Selector add_scanner() method"""
396 selector
= Selector({'.x' : 1, '.y' : 2})
397 s
= selector
.select(self
.skey_node('.z'))
399 selector
.add_scanner('.z', 3)
400 s
= selector
.select(self
.skey_node('.z'))
403 class CurrentTestCase(unittest
.TestCase
):
404 def test_class(self
) -> None:
405 """Test the Scanner.Current class"""
407 def __init__(self
) -> None:
408 self
.called_has_builder
= None
409 self
.called_is_up_to_date
= None
410 self
.func_called
= None
411 def rexists(self
) -> bool:
413 class HasNoBuilder(MyNode
):
414 def has_builder(self
) -> bool:
415 self
.called_has_builder
= True
417 class IsNotCurrent(MyNode
):
418 def has_builder(self
) -> bool:
419 self
.called_has_builder
= True
421 def is_up_to_date(self
) -> bool:
422 self
.called_is_up_to_date
= True
424 class IsCurrent(MyNode
):
425 def has_builder(self
) -> bool:
426 self
.called_has_builder
= True
428 def is_up_to_date(self
) -> bool:
429 self
.called_is_up_to_date
= True
431 def func(node
, env
, path
):
432 node
.func_called
= True
434 env
= DummyEnvironment()
439 self
.assertTrue(hnb
.called_has_builder
, "did not call has_builder()")
440 self
.assertTrue(not hnb
.called_is_up_to_date
, "did call is_up_to_date()")
441 self
.assertTrue(hnb
.func_called
, "did not call func()")
444 self
.assertTrue(inc
.called_has_builder
, "did not call has_builder()")
445 self
.assertTrue(inc
.called_is_up_to_date
, "did not call is_up_to_date()")
446 self
.assertTrue(not inc
.func_called
, "did call func()")
449 self
.assertTrue(ic
.called_has_builder
, "did not call has_builder()")
450 self
.assertTrue(ic
.called_is_up_to_date
, "did not call is_up_to_date()")
451 self
.assertTrue(ic
.func_called
, "did not call func()")
453 class ClassicTestCase(unittest
.TestCase
):
455 def func(self
, filename
, env
, target
, *args
):
456 self
.filename
= filename
465 def test_find_include(self
) -> None:
466 """Test the Scanner.Classic find_include() method"""
467 env
= DummyEnvironment()
468 s
= Classic("t", ['.suf'], 'MYPATH', r
'^my_inc (\S+)')
470 def _find_file(filename
, paths
):
471 return paths
[0]+'/'+filename
473 save
= SCons
.Node
.FS
.find_file
474 SCons
.Node
.FS
.find_file
= _find_file
477 n
, i
= s
.find_include('aaa', 'foo', ('path',))
478 assert n
== 'foo/aaa', n
482 SCons
.Node
.FS
.find_file
= save
484 def test_name(self
) -> None:
485 """Test setting the Scanner.Classic name"""
486 s
= Classic("my_name", ['.s'], 'MYPATH', r
'^my_inc (\S+)')
487 assert s
.name
== "my_name", s
.name
489 def test_scan(self
) -> None:
490 """Test the Scanner.Classic scan() method"""
492 def __init__(self
, name
) -> None:
500 def get_contents(self
):
501 return self
._contents
502 def get_text_contents(self
):
503 return self
._contents
507 class MyScanner(Classic
):
508 def find_include(self
, include
, source_dir
, path
):
509 return include
, include
511 env
= DummyEnvironment()
512 s
= MyScanner("t", ['.suf'], 'MYPATH', r
'^my_inc (\S+)')
514 # This set of tests is intended to test the scanning operation
515 # of the Classic scanner.
517 # Note that caching has been added for not just the includes
518 # but the entire scan call. The caching is based on the
519 # arguments, so we will fiddle with the path parameter to
520 # defeat this caching for the purposes of these tests.
522 # If the node doesn't exist, scanning turns up nothing.
525 ret
= s
.function(n1
, env
)
526 assert ret
== [], ret
528 # Verify that it finds includes from the contents.
531 n
._dir
= MyNode("n._dir")
532 n
._contents
= 'my_inc abc\n'
533 ret
= s
.function(n
, env
, ('foo',))
534 assert ret
== ['abc'], ret
536 # Verify that it uses the cached include info.
537 n
._contents
= 'my_inc def\n'
538 ret
= s
.function(n
, env
, ('foo2',))
539 assert ret
== ['abc'], ret
541 # Verify that if we wipe the cache, it uses the new contents.
543 ret
= s
.function(n
, env
, ('foo3',))
544 assert ret
== ['def'], ret
546 # We no longer cache overall scan results, which would be returned
547 # if individual results are de-cached. If we ever restore that
548 # functionality, this test goes back here.
549 #ret = s.function(n, env, ('foo2',))
550 #assert ret == ['abc'], 'caching inactive; got: %s'%ret
552 # Verify that it sorts what it finds.
553 n
.includes
= ['xyz', 'uvw']
554 ret
= s
.function(n
, env
, ('foo4',))
555 assert ret
== ['uvw', 'xyz'], ret
557 # Verify that we use the rfile() node.
560 nr
._dir
= MyNode("nr._dir")
561 nr
.includes
= ['jkl', 'mno']
563 ret
= s
.function(n
, env
, ('foo5',))
564 assert ret
== ['jkl', 'mno'], ret
566 def test_recursive(self
) -> None:
567 """Test the Scanner.Classic class recursive flag"""
571 s
= Classic("Test", [], None, "", function
=self
.func
, recursive
=1)
572 n
= s
.recurse_nodes(nodes
)
573 self
.assertTrue(n
== n
,
574 "recursive = 1 didn't return all nodes: %s" % n
)
577 return [n
for n
in nodes
if n
% 2]
579 s
= Classic("Test", [], None, "", function
=self
.func
, recursive
=odd_only
)
580 n
= s
.recurse_nodes(nodes
)
581 self
.assertTrue(n
== [1, 3],
582 "recursive = 1 didn't return all nodes: %s" % n
)
585 class ClassicCPPTestCase(unittest
.TestCase
):
586 def test_find_include(self
) -> None:
587 """Test the Scanner.ClassicCPP find_include() method"""
588 env
= DummyEnvironment()
589 s
= ClassicCPP("Test", [], None, "")
591 def _find_file(filename
, paths
):
592 return paths
[0]+'/'+filename
594 save
= SCons
.Node
.FS
.find_file
595 SCons
.Node
.FS
.find_file
= _find_file
598 n
, i
= s
.find_include(('"', 'aaa'), 'foo', ('path',))
599 assert n
== 'foo/aaa', n
602 n
, i
= s
.find_include(('<', 'bbb'), 'foo', ('path',))
603 assert n
== 'path/bbb', n
606 n
, i
= s
.find_include(('<', 'ccc'), 'foo', ('path',))
607 assert n
== 'path/ccc', n
611 SCons
.Node
.FS
.find_file
= save
614 suite
= unittest
.TestSuite()
616 FindPathDirsTestCase
,
624 for tclass
in tclasses
:
625 loader
= unittest
.TestLoader()
626 loader
.testMethodPrefix
= 'test_'
627 names
= loader
.getTestCaseNames(tclass
)
628 suite
.addTests(list(map(tclass
, names
)))
631 if __name__
== "__main__":
632 TestUnit
.run(suite())
636 # indent-tabs-mode:nil
638 # vim: set expandtab tabstop=4 shiftwidth=4: