Disable tool initialization in tests which don't need it
[scons.git] / SCons / ExecutorTests.py
blob7e5df1441f226d61e7cbfb8d672fd737d8317e23
1 # MIT License
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.
24 import unittest
26 import SCons.Executor
29 class MyEnvironment:
30 def __init__(self, **kw) -> None:
31 self._dict = {}
32 self._dict.update(kw)
33 def __getitem__(self, key):
34 return self._dict[key]
35 def Override(self, overrides):
36 d = self._dict.copy()
37 d.update(overrides)
38 return MyEnvironment(**d)
39 def _update(self, dict) -> None:
40 self._dict.update(dict)
42 class MyAction:
43 def __init__(self, actions=['action1', 'action2']) -> None:
44 self.actions = actions
45 def __call__(self, target, source, env, **kw) -> None:
46 for action in self.actions:
47 action(target, source, env, **kw)
48 def genstring(self, target, source, env):
49 return ' '.join(['GENSTRING'] + list(map(str, self.actions)) + target + source)
50 def get_contents(self, target, source, env):
51 return b' '.join(
52 [SCons.Util.to_bytes(aa) for aa in self.actions] +
53 [SCons.Util.to_bytes(tt) for tt in target] +
54 [SCons.Util.to_bytes(ss) for ss in source]
56 def get_implicit_deps(self, target, source, env):
57 return []
59 class MyBuilder:
60 def __init__(self, env, overrides) -> None:
61 self.env = env
62 self.overrides = overrides
63 self.action = MyAction()
65 class MyNode:
66 def __init__(self, name=None, pre=[], post=[]) -> None:
67 self.name = name
68 self.implicit = []
69 self.pre_actions = pre
70 self.post_actions = post
71 self.missing_val = None
72 self.always_build = False
73 self.up_to_date = False
75 def __str__(self) -> str:
76 return self.name
78 def build(self) -> None:
79 executor = SCons.Executor.Executor(MyAction(self.pre_actions +
80 [self.builder.action] +
81 self.post_actions),
82 self.builder.env,
83 [],
84 [self],
85 ['s1', 's2'])
86 executor(self)
87 def get_env_scanner(self, env, kw):
88 return MyScanner('dep-')
89 def get_implicit_deps(self, env, scanner, path, kw={}):
90 if not scanner:
91 scanner = self.get_env_scanner(env, kw)
92 return [scanner.prefix + str(self)]
93 def add_to_implicit(self, deps) -> None:
94 self.implicit.extend(deps)
95 def missing(self):
96 return self.missing_val
97 def calc_signature(self, calc):
98 return 'cs-'+calc+'-'+self.name
99 def disambiguate(self):
100 return self
102 def is_up_to_date(self) -> bool:
103 return self.up_to_date
105 class MyScanner:
106 def __init__(self, prefix) -> None:
107 self.prefix = prefix
108 def path(self, env, cwd, target, source):
109 return ()
110 def select(self, node):
111 return self
113 class ExecutorTestCase(unittest.TestCase):
115 def test__init__(self):
116 """Test creating an Executor"""
117 source_list = ['s1', 's2']
118 x = SCons.Executor.Executor('a', 'e', ['o'], 't', source_list)
119 assert x.action_list == ['a'], x.action_list
120 assert x.env == 'e', x.env
121 assert x.overridelist == ['o'], x.overridelist
122 targets = x.get_all_targets()
123 assert targets == ['t'], targets
124 source_list.append('s3')
125 sources = x.get_all_sources()
126 assert sources == ['s1', 's2'], sources
127 try:
128 x = SCons.Executor.Executor(None, 'e', ['o'], 't', source_list)
129 except SCons.Errors.UserError:
130 pass
131 else:
132 raise Exception("Did not catch expected UserError")
134 def test__action_list(self) -> None:
135 """Test the {get,set}_action_list() methods"""
136 x = SCons.Executor.Executor('a', 'e', 'o', 't', ['s1', 's2'])
138 l = x.get_action_list()
139 assert l == ['a'], l
141 x.add_pre_action('pre')
142 x.add_post_action('post')
143 l = x.get_action_list()
144 assert l == ['pre', 'a', 'post'], l
146 x.set_action_list('b')
147 l = x.get_action_list()
148 assert l == ['pre', 'b', 'post'], l
150 x.set_action_list(['c'])
151 l = x.get_action_list()
152 assert l == ['pre', 'c', 'post'], l
154 def test_get_build_env(self) -> None:
155 """Test fetching and generating a build environment"""
156 x = SCons.Executor.Executor(MyAction(), MyEnvironment(e=1), [],
157 't', ['s1', 's2'])
158 x.env = MyEnvironment(eee=1)
159 be = x.get_build_env()
160 assert be['eee'] == 1, be
162 env = MyEnvironment(X='xxx')
163 x = SCons.Executor.Executor(MyAction(),
164 env,
165 [{'O':'o2'}],
166 't',
167 ['s1', 's2'])
168 be = x.get_build_env()
169 assert be['O'] == 'o2', be['O']
170 assert be['X'] == 'xxx', be['X']
172 env = MyEnvironment(Y='yyy')
173 overrides = [{'O':'ob3'}, {'O':'oo3'}]
174 x = SCons.Executor.Executor(MyAction(), env, overrides, ['t'], ['s'])
175 be = x.get_build_env()
176 assert be['O'] == 'oo3', be['O']
177 assert be['Y'] == 'yyy', be['Y']
178 overrides = [{'O':'ob3'}]
179 x = SCons.Executor.Executor(MyAction(), env, overrides, ['t'], ['s'])
180 be = x.get_build_env()
181 assert be['O'] == 'ob3', be['O']
182 assert be['Y'] == 'yyy', be['Y']
184 def test_get_build_scanner_path(self) -> None:
185 """Test fetching the path for the specified scanner."""
186 t = MyNode('t')
187 t.cwd = 'here'
188 x = SCons.Executor.Executor(MyAction(),
189 MyEnvironment(SCANNERVAL='sss'),
191 [t],
192 ['s1', 's2'])
194 class LocalScanner:
195 def path(self, env, dir, target, source) -> str:
196 target = list(map(str, target))
197 source = list(map(str, source))
198 return "scanner: %s, %s, %s, %s" % (env['SCANNERVAL'], dir, target, source)
199 s = LocalScanner()
201 p = x.get_build_scanner_path(s)
202 assert p == "scanner: sss, here, ['t'], ['s1', 's2']", p
204 def test_get_kw(self) -> None:
205 """Test the get_kw() method"""
206 t = MyNode('t')
207 x = SCons.Executor.Executor(MyAction(),
208 MyEnvironment(),
210 [t],
211 ['s1', 's2'],
212 builder_kw={'X':1, 'Y':2})
213 kw = x.get_kw()
214 assert kw == {'X':1, 'Y':2, 'executor':x}, kw
215 kw = x.get_kw({'Z':3})
216 assert kw == {'X':1, 'Y':2, 'Z':3, 'executor':x}, kw
217 kw = x.get_kw({'X':4})
218 assert kw == {'X':4, 'Y':2, 'executor':x}, kw
220 def test__call__(self):
221 """Test calling an Executor"""
222 result = []
223 def pre(target, source, env, result=result, **kw) -> None:
224 result.append('pre')
225 def action1(target, source, env, result=result, **kw) -> None:
226 result.append('action1')
227 def action2(target, source, env, result=result, **kw) -> None:
228 result.append('action2')
229 def post(target, source, env, result=result, **kw) -> None:
230 result.append('post')
232 env = MyEnvironment()
233 a = MyAction([action1, action2])
234 t = MyNode('t')
236 x = SCons.Executor.Executor(a, env, [], [t], ['s1', 's2'])
237 x.add_pre_action(pre)
238 x.add_post_action(post)
239 x(t)
240 assert result == ['pre', 'action1', 'action2', 'post'], result
241 del result[:]
243 def pre_err(target, source, env, result=result, **kw) -> int:
244 result.append('pre_err')
245 return 1
247 x = SCons.Executor.Executor(a, env, [], [t], ['s1', 's2'])
248 x.add_pre_action(pre_err)
249 x.add_post_action(post)
250 try:
251 x(t)
252 except SCons.Errors.BuildError:
253 pass
254 else:
255 raise Exception("Did not catch expected BuildError")
256 assert result == ['pre_err'], result
257 del result[:]
259 def test_cleanup(self) -> None:
260 """Test cleaning up an Executor"""
261 orig_env = MyEnvironment(e=1)
262 x = SCons.Executor.Executor('b', orig_env, [{'o':1}],
263 't', ['s1', 's2'])
265 be = x.get_build_env()
266 assert be['e'] == 1, be['e']
268 x.cleanup()
270 x.env = MyEnvironment(eee=1)
271 be = x.get_build_env()
272 assert be['eee'] == 1, be['eee']
274 x.cleanup()
276 be = x.get_build_env()
277 assert be['eee'] == 1, be['eee']
279 def test_add_sources(self) -> None:
280 """Test adding sources to an Executor"""
281 x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
282 sources = x.get_all_sources()
283 assert sources == ['s1', 's2'], sources
285 x.add_sources(['s1', 's2'])
286 sources = x.get_all_sources()
287 assert sources == ['s1', 's2'], sources
289 x.add_sources(['s3', 's1', 's4'])
290 sources = x.get_all_sources()
291 assert sources == ['s1', 's2', 's3', 's4'], sources
293 def test_get_sources(self) -> None:
294 """Test getting sources from an Executor"""
295 x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
296 sources = x.get_sources()
297 assert sources == ['s1', 's2'], sources
299 x.add_sources(['s1', 's2'])
300 sources = x.get_sources()
301 assert sources == ['s1', 's2'], sources
303 x.add_sources(['s3', 's1', 's4'])
304 sources = x.get_sources()
305 assert sources == ['s1', 's2', 's3', 's4'], sources
307 def test_prepare(self):
308 """Test the Executor's prepare() method"""
309 env = MyEnvironment()
310 t1 = MyNode('t1')
311 s1 = MyNode('s1')
312 s2 = MyNode('s2')
313 s3 = MyNode('s3')
314 x = SCons.Executor.Executor('b', env, [{}], [t1], [s1, s2, s3])
316 s2.missing_val = True
318 try:
319 r = x.prepare()
320 except SCons.Errors.StopError as e:
321 assert str(e) == "Source `s2' not found, needed by target `t1'.", e
322 else:
323 raise AssertionError("did not catch expected StopError: %s" % r)
325 def test_add_pre_action(self) -> None:
326 """Test adding pre-actions to an Executor"""
327 x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
328 x.add_pre_action('a1')
329 assert x.pre_actions == ['a1']
330 x.add_pre_action('a2')
331 assert x.pre_actions == ['a1', 'a2']
333 def test_add_post_action(self) -> None:
334 """Test adding post-actions to an Executor"""
335 x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
336 x.add_post_action('a1')
337 assert x.post_actions == ['a1']
338 x.add_post_action('a2')
339 assert x.post_actions == ['a1', 'a2']
341 def test___str__(self) -> None:
342 """Test the __str__() method"""
343 env = MyEnvironment(S='string')
345 x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
346 c = str(x)
347 assert c == 'GENSTRING action1 action2 t s', c
349 x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
350 x.add_pre_action(MyAction(['pre']))
351 x.add_post_action(MyAction(['post']))
352 c = str(x)
353 expect = 'GENSTRING pre t s\n' + \
354 'GENSTRING action1 action2 t s\n' + \
355 'GENSTRING post t s'
356 assert c == expect, c
358 def test_nullify(self) -> None:
359 """Test the nullify() method"""
360 env = MyEnvironment(S='string')
362 result = []
363 def action1(target, source, env, result=result, **kw) -> None:
364 result.append('action1')
366 env = MyEnvironment()
367 a = MyAction([action1])
368 x = SCons.Executor.Executor(a, env, [], ['t1', 't2'], ['s1', 's2'])
370 x(MyNode('', [], []))
371 assert result == ['action1'], result
372 s = str(x)
373 assert s[:10] == 'GENSTRING ', s
375 del result[:]
376 x.nullify()
378 assert result == [], result
379 x(MyNode('', [], []))
380 assert result == [], result
381 s = str(x)
382 assert s == '', s
384 def test_get_contents(self) -> None:
385 """Test fetching the signatures contents"""
386 env = MyEnvironment(C='contents')
388 x = SCons.Executor.Executor(MyAction(), env, [], ['t'], ['s'])
389 c = x.get_contents()
390 assert c == b'action1 action2 t s', c
392 x = SCons.Executor.Executor(MyAction(actions=['grow']), env, [],
393 ['t'], ['s'])
394 x.add_pre_action(MyAction(['pre']))
395 x.add_post_action(MyAction(['post']))
396 c = x.get_contents()
397 assert c == b'pre t sgrow t spost t s', c
399 def test_get_timestamp(self) -> None:
400 """Test fetching the "timestamp" """
401 x = SCons.Executor.Executor('b', 'e', 'o', 't', ['s1', 's2'])
402 ts = x.get_timestamp()
403 assert ts == 0, ts
405 def test_scan_targets(self) -> None:
406 """Test scanning the targets for implicit dependencies"""
407 env = MyEnvironment(S='string')
408 t1 = MyNode('t1')
409 t2 = MyNode('t2')
410 sources = [MyNode('s1'), MyNode('s2')]
411 x = SCons.Executor.Executor(MyAction(), env, [{}], [t1, t2], sources)
413 deps = x.scan_targets(None)
414 assert t1.implicit == ['dep-t1', 'dep-t2'], t1.implicit
415 assert t2.implicit == ['dep-t1', 'dep-t2'], t2.implicit
417 t1.implicit = []
418 t2.implicit = []
420 deps = x.scan_targets(MyScanner('scanner-'))
421 assert t1.implicit == ['scanner-t1', 'scanner-t2'], t1.implicit
422 assert t2.implicit == ['scanner-t1', 'scanner-t2'], t2.implicit
424 def test_scan_sources(self) -> None:
425 """Test scanning the sources for implicit dependencies"""
426 env = MyEnvironment(S='string')
427 t1 = MyNode('t1')
428 t2 = MyNode('t2')
429 sources = [MyNode('s1'), MyNode('s2')]
430 x = SCons.Executor.Executor(MyAction(), env, [{}], [t1, t2], sources)
432 deps = x.scan_sources(None)
433 assert t1.implicit == ['dep-s1', 'dep-s2'], t1.implicit
434 assert t2.implicit == ['dep-s1', 'dep-s2'], t2.implicit
436 t1.implicit = []
437 t2.implicit = []
439 deps = x.scan_sources(MyScanner('scanner-'))
440 assert t1.implicit == ['scanner-s1', 'scanner-s2'], t1.implicit
441 assert t2.implicit == ['scanner-s1', 'scanner-s2'], t2.implicit
443 def test_get_unignored_sources(self) -> None:
444 """Test fetching the unignored source list"""
445 env = MyEnvironment()
446 s1 = MyNode('s1')
447 s2 = MyNode('s2')
448 s3 = MyNode('s3')
449 x = SCons.Executor.Executor('b', env, [{}], [], [s1, s2, s3])
451 r = x.get_unignored_sources(None, [])
452 assert r == [s1, s2, s3], list(map(str, r))
454 r = x.get_unignored_sources(None, [s2])
455 assert r == [s1, s3], list(map(str, r))
457 r = x.get_unignored_sources(None, [s1, s3])
458 assert r == [s2], list(map(str, r))
460 def test_changed_sources_for_alwaysBuild(self) -> None:
462 Ensure if a target is marked always build that the sources are always marked changed sources
463 :return:
465 env = MyEnvironment()
466 s1 = MyNode('s1')
467 s2 = MyNode('s2')
468 t1 = MyNode('t1')
469 t1.up_to_date = True
470 t1.always_build = True
472 x = SCons.Executor.Executor('b', env, [{}], [t1], [s1, s2])
474 changed_sources = x._get_changed_sources()
475 assert changed_sources == [s1, s2], "If target marked AlwaysBuild sources should always be marked changed"
480 if __name__ == "__main__":
481 unittest.main()
483 # Local Variables:
484 # tab-width:4
485 # indent-tabs-mode:nil
486 # End:
487 # vim: set expandtab tabstop=4 shiftwidth=4: