[ci skip] add test framework reformat to .git-blame-ignore-revs
[scons.git] / test / Parallel / multiple-parents.py
blob3712638d283be129a82704832dfded7ca1f872b1
1 #!/usr/bin/env python
3 # MIT License
5 # Copyright The SCons Foundation
7 # Permission is hereby granted, free of charge, to any person obtaining
8 # a copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sublicense, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
15 # The above copyright notice and this permission notice shall be included
16 # in all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 """
27 Verify that a failed build action with -j works as expected.
28 """
30 import sys
32 import TestSCons
34 _python_ = TestSCons._python_
36 try:
37 import threading
38 except ImportError:
39 # if threads are not supported, then
40 # there is nothing to test
41 TestCmd.no_result()
42 sys.exit()
45 test = TestSCons.TestSCons()
47 # Test that we can handle parallel builds with a dependency graph
48 # where:
49 # a) Some nodes have multiple parents
50 # b) Some targets fail building
51 # c) Some targets succeed building
52 # d) Some children are ignored
53 # e) Some children are pre-requesites
54 # f) Some children have side-effects
55 # g) Some sources are missing
56 # h) Builds that are interrupted
58 test.write('SConstruct', """
59 vars = Variables()
60 vars.Add(BoolVariable('interrupt', 'Interrupt the build.', False))
61 DefaultEnvironment(tools=[]) # test speedup
62 varEnv = Environment(variables=vars)
64 def fail_action(target=None, source=None, env=None):
65 return 2
67 def simulate_keyboard_interrupt(target=None, source=None, env=None):
68 # Directly invoked the SIGINT handler to simulate a
69 # KeyboardInterrupt. This hack is necessary because there is no
70 # easy way to get access to the current Job/Taskmaster object.
71 import signal
73 handler = signal.getsignal(signal.SIGINT)
74 handler(signal.SIGINT, None)
75 return 0
77 interrupt = Command(target='interrupt', source='', action=simulate_keyboard_interrupt)
79 touch0 = Touch('${TARGETS[0]}')
80 touch1 = Touch('${TARGETS[1]}')
81 touch2 = Touch('${TARGETS[2]}')
83 failed0 = Command(target='failed00', source='', action=fail_action)
84 ok0 = Command(
85 target=['ok00a', 'ok00b', 'ok00c'],
86 source='',
87 action=[touch0, touch1, touch2],
89 prereq0 = Command(target='prereq00', source='', action=touch0)
90 ignore0 = Command(target='ignore00', source='', action=touch0)
91 igreq0 = Command(target='igreq00', source='', action=touch0)
92 missing0 = Command(target='missing00', source='MissingSrc', action=touch0)
93 withSE0 = Command(
94 target=['withSE00a', 'withSE00b', 'withSE00c'],
95 source='',
96 action=[touch0, touch1, touch2, Touch('side_effect')],
98 SideEffect('side_effect', withSE0)
100 prev_level = failed0 + ok0 + ignore0 + missing0 + withSE0
101 prev_prereq = prereq0
102 prev_ignore = ignore0
103 prev_igreq = igreq0
105 if varEnv['interrupt']:
106 prev_level = prev_level + interrupt
108 for i in range(1, 20):
110 failed = Command(target='failed%02d' % i, source='', action=fail_action)
111 ok = Command(
112 target=['ok%02da' % i, 'ok%02db' % i, 'ok%02dc' % i],
113 source='',
114 action=[touch0, touch1, touch2],
116 prereq = Command(target='prereq%02d' % i, source='', action=touch0)
117 ignore = Command(target='ignore%02d' % i, source='', action=touch0)
118 igreq = Command(target='igreq%02d' % i, source='', action=touch0)
119 missing = Command(target='missing%02d' % i, source='MissingSrc', action=touch0)
120 withSE = Command(
121 target=['withSE%02da' % i, 'withSE%02db' % i, 'withSE%02dc' % i],
122 source='',
123 action=[touch0, touch1, touch2, Touch('side_effect')],
125 SideEffect('side_effect', withSE)
127 next_level = failed + ok + ignore + igreq + missing + withSE
129 for j in range(1, 10):
130 a = Alias('a%02d%02d' % (i, j), prev_level)
132 Requires(a, prev_prereq)
133 Ignore(a, prev_ignore)
135 Requires(a, prev_igreq)
136 Ignore(a, prev_igreq)
138 next_level = next_level + a
140 prev_level = next_level
141 prev_prereq = prereq
142 prev_ignore = ignore
143 prev_igreq = igreq
145 all = Alias('all', prev_level)
147 Requires(all, prev_prereq)
148 Ignore(all, prev_ignore)
150 Requires(all, prev_igreq)
151 Ignore(all, prev_igreq)
153 Default(all)
154 """)
156 re_error = """\
157 (scons: \\*\\*\\* \\[failed\\d+] Error 2\\n)|\
158 (scons: \\*\\*\\* \\[missing\\d+] Source `MissingSrc' not found, needed by target `missing\\d+'\\.( Stop\\.)?\\n)|\
159 (scons: \\*\\*\\* \\[\\w+] Build interrupted\\.\\n)|\
160 (scons: Build interrupted\\.\\n)\
163 re_errors = "(" + re_error + ")+"
165 # Make the script chatty so lack of output doesn't fool buildbot into
166 # thinking it's hung.
168 sys.stdout.write('Initial build.\n')
169 test.run(arguments = 'all',
170 status = 2,
171 stderr = "scons: *** [failed19] Error 2\n")
172 test.must_not_exist(test.workpath('side_effect'))
173 for i in range(20):
174 test.must_not_exist(test.workpath('ok%02da' % i))
175 test.must_not_exist(test.workpath('ok%02db' % i))
176 test.must_not_exist(test.workpath('ok%02dc' % i))
177 test.must_not_exist(test.workpath('ignore%02d' % i))
178 test.must_not_exist(test.workpath('withSE%02da' % i))
179 test.must_not_exist(test.workpath('withSE%02db' % i))
180 test.must_not_exist(test.workpath('withSE%02dc' % i))
182 # prereq19 and igreq19 will exist because, as prerequisites,
183 # they are now evaluated *before* the direct children of the Node.
184 for i in range(19):
185 test.must_not_exist(test.workpath('prereq%02d' % i))
186 test.must_not_exist(test.workpath('igreq%02d' % i))
187 test.must_exist(test.workpath('prereq19'))
188 test.must_exist(test.workpath('igreq19'))
191 sys.stdout.write('-j8 all\n')
192 for i in range(3):
193 test.run(arguments = '-c all')
195 test.run(arguments = '-j8 all',
196 status = 2,
197 stderr = re_errors,
198 match=TestSCons.match_re_dotall)
201 sys.stdout.write('-j 8 -k all\n')
202 for i in range(3):
203 test.run(arguments = '-c all')
205 test.run(arguments = '-j 8 -k all',
206 status = 2,
207 stderr = re_errors,
208 match=TestSCons.match_re_dotall)
209 test.must_exist(test.workpath('side_effect'))
210 for i in range(20):
211 test.must_exist(test.workpath('ok%02da' % i))
212 test.must_exist(test.workpath('ok%02db' % i))
213 test.must_exist(test.workpath('ok%02dc' % i))
214 test.must_exist(test.workpath('prereq%02d' % i))
215 test.must_not_exist(test.workpath('ignore%02d' % i))
216 test.must_exist(test.workpath('igreq%02d' % i))
217 test.must_exist(test.workpath('withSE%02da' % i))
218 test.must_exist(test.workpath('withSE%02db' % i))
219 test.must_exist(test.workpath('withSE%02dc' % i))
222 sys.stdout.write('all --random\n')
223 for i in range(3):
224 test.run(arguments = 'all --random',
225 status = 2,
226 stderr = re_errors,
227 match=TestSCons.match_re_dotall)
230 sys.stdout.write('-j8 --random all\n')
231 for i in range(3):
232 test.run(arguments = '-c all')
234 test.run(arguments = '-j8 --random all',
235 status = 2,
236 stderr = re_errors,
237 match=TestSCons.match_re_dotall)
240 sys.stdout.write('-j8 -k --random all\n')
241 for i in range(3):
242 test.run(arguments = '-c all')
244 test.run(arguments = '-j 8 -k --random all',
245 status = 2,
246 stderr = re_errors,
247 match=TestSCons.match_re_dotall)
248 test.must_exist(test.workpath('side_effect'))
249 for i in range(20):
250 test.must_exist(test.workpath('ok%02da' % i))
251 test.must_exist(test.workpath('ok%02db' % i))
252 test.must_exist(test.workpath('ok%02dc' % i))
253 test.must_exist(test.workpath('prereq%02d' % i))
254 test.must_not_exist(test.workpath('ignore%02d' % i))
255 test.must_exist(test.workpath('igreq%02d' % i))
256 test.must_exist(test.workpath('withSE%02da' % i))
257 test.must_exist(test.workpath('withSE%02db' % i))
258 test.must_exist(test.workpath('withSE%02dc' % i))
261 sys.stdout.write('-j8 -k --random all interupt=yes\n')
262 for i in range(3):
263 test.run(arguments = '-c all')
265 test.run(arguments = '-j 8 -k --random interrupt=yes all',
266 status = 2,
267 stderr = re_errors,
268 match=TestSCons.match_re_dotall)
271 test.pass_test()
273 # Local Variables:
274 # tab-width:4
275 # indent-tabs-mode:nil
276 # End:
277 # vim: set expandtab tabstop=4 shiftwidth=4: