blob: 6806ea7fbe6c7ef5d7526268b407374c354f0cc6 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassed098bb2014-09-05 19:00:13 -06002# Copyright (c) 2014 Google, Inc
3#
Simon Glassed098bb2014-09-05 19:00:13 -06004
5import os
6import shutil
7import sys
8import tempfile
9import unittest
10
Simon Glassf0d9c102020-04-17 18:09:02 -060011from buildman import board
12from buildman import bsettings
13from buildman import cmdline
14from buildman import control
15from buildman import toolchain
Simon Glassa997ea52020-04-17 18:09:04 -060016from patman import command
17from patman import gitutil
18from patman import terminal
Simon Glass9bf9a722021-04-11 16:27:27 +120019from patman import test_util
Simon Glassa997ea52020-04-17 18:09:04 -060020from patman import tools
Simon Glassed098bb2014-09-05 19:00:13 -060021
Simon Glass5e0441d2014-09-05 19:00:15 -060022settings_data = '''
23# Buildman settings file
24
25[toolchain]
26
27[toolchain-alias]
28
29[make-flags]
30src=/home/sjg/c/src
31chroot=/home/sjg/c/chroot
Masahiro Yamada72e545a2018-08-06 20:47:38 +090032vboot=VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
Simon Glass5e0441d2014-09-05 19:00:15 -060033chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
34chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
35chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
36'''
37
Simon Glassd4c6c8a2022-07-11 19:03:58 -060038BOARDS = [
Simon Glasscbd36582014-09-05 19:00:16 -060039 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''],
40 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
41 ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
Simon Glasscbd36582014-09-05 19:00:16 -060042 ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
43]
44
Simon Glass8e959562014-09-05 19:00:20 -060045commit_shortlog = """4aca821 patman: Avoid changing the order of tags
4639403bb patman: Use --no-pager' to stop git from forking a pager
47db6e6f2 patman: Remove the -a option
48f2ccf03 patman: Correct unit tests to run correctly
491d097f9 patman: Fix indentation in terminal.py
50d073747 patman: Support the 'reverse' option for 'git log
51"""
52
53commit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd
54Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
55Date: Fri Aug 22 19:12:41 2014 +0900
56
57 buildman: refactor help message
58
59 "buildman [options]" is displayed by default.
60
61 Append the rest of help messages to parser.usage
62 instead of replacing it.
63
64 Besides, "-b <branch>" is not mandatory since commit fea5858e.
65 Drop it from the usage.
66
67 Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
68""",
69"""commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8
70Author: Simon Glass <sjg@chromium.org>
71Date: Thu Aug 14 16:48:25 2014 -0600
72
73 patman: Support the 'reverse' option for 'git log'
74
75 This option is currently not supported, but needs to be, for buildman to
76 operate as expected.
77
78 Series-changes: 7
79 - Add new patch to fix the 'reverse' bug
80
Simon Glass359b55a62014-09-05 19:00:23 -060081 Series-version: 8
Simon Glass8e959562014-09-05 19:00:20 -060082
83 Change-Id: I79078f792e8b390b8a1272a8023537821d45feda
84 Reported-by: York Sun <yorksun@freescale.com>
85 Signed-off-by: Simon Glass <sjg@chromium.org>
86
87""",
88"""commit 1d097f9ab487c5019152fd47bda126839f3bf9fc
89Author: Simon Glass <sjg@chromium.org>
90Date: Sat Aug 9 11:44:32 2014 -0600
91
92 patman: Fix indentation in terminal.py
93
94 This code came from a different project with 2-character indentation. Fix
95 it for U-Boot.
96
97 Series-changes: 6
98 - Add new patch to fix indentation in teminal.py
99
100 Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34
101 Signed-off-by: Simon Glass <sjg@chromium.org>
102
103""",
104"""commit f2ccf03869d1e152c836515a3ceb83cdfe04a105
105Author: Simon Glass <sjg@chromium.org>
106Date: Sat Aug 9 11:08:24 2014 -0600
107
108 patman: Correct unit tests to run correctly
109
110 It seems that doctest behaves differently now, and some of the unit tests
111 do not run. Adjust the tests to work correctly.
112
113 ./tools/patman/patman --test
114 <unittest.result.TestResult run=10 errors=0 failures=0>
115
116 Series-changes: 6
117 - Add new patch to fix patman unit tests
118
119 Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b
120
121""",
122"""commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c
123Author: Simon Glass <sjg@chromium.org>
124Date: Sat Aug 9 12:06:02 2014 -0600
125
126 patman: Remove the -a option
127
128 It seems that this is no longer needed, since checkpatch.pl will catch
129 whitespace problems in patches. Also the option is not widely used, so
130 it seems safe to just remove it.
131
132 Series-changes: 6
133 - Add new patch to remove patman's -a option
134
135 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
136 Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc
137
138""",
139"""commit 39403bb4f838153028a6f21ca30bf100f3791133
140Author: Simon Glass <sjg@chromium.org>
141Date: Thu Aug 14 21:50:52 2014 -0600
142
143 patman: Use --no-pager' to stop git from forking a pager
144
145""",
146"""commit 4aca821e27e97925c039e69fd37375b09c6f129c
147Author: Simon Glass <sjg@chromium.org>
148Date: Fri Aug 22 15:57:39 2014 -0600
149
150 patman: Avoid changing the order of tags
151
152 patman collects tags that it sees in the commit and places them nicely
153 sorted at the end of the patch. However, this is not really necessary and
154 in fact is apparently not desirable.
155
156 Series-changes: 9
157 - Add new patch to avoid changing the order of tags
158
Simon Glass359b55a62014-09-05 19:00:23 -0600159 Series-version: 9
160
Simon Glass8e959562014-09-05 19:00:20 -0600161 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
162 Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db
163"""]
164
165TEST_BRANCH = '__testbranch'
166
Simon Glassed098bb2014-09-05 19:00:13 -0600167class TestFunctional(unittest.TestCase):
168 """Functional test for buildman.
169
170 This aims to test from just below the invocation of buildman (parsing
171 of arguments) to 'make' and 'git' invocation. It is not a true
172 emd-to-end test, as it mocks git, make and the tool chain. But this
173 makes it easier to detect when the builder is doing the wrong thing,
174 since in many cases this test code will fail. For example, only a
175 very limited subset of 'git' arguments is supported - anything
176 unexpected will fail.
177 """
178 def setUp(self):
179 self._base_dir = tempfile.mkdtemp()
Tom Rinie95eddc2019-10-07 17:17:36 -0400180 self._output_dir = tempfile.mkdtemp()
Simon Glassed098bb2014-09-05 19:00:13 -0600181 self._git_dir = os.path.join(self._base_dir, 'src')
182 self._buildman_pathname = sys.argv[0]
Simon Glass5d4a7872016-07-27 20:33:00 -0600183 self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
Simon Glassed098bb2014-09-05 19:00:13 -0600184 command.test_result = self._HandleCommand
Simon Glass22901f92022-01-22 05:07:31 -0700185 bsettings.Setup(None)
186 bsettings.AddFile(settings_data)
Simon Glass8e959562014-09-05 19:00:20 -0600187 self.setupToolchains()
188 self._toolchains.Add('arm-gcc', test=False)
189 self._toolchains.Add('powerpc-gcc', test=False)
Simon Glasscbd36582014-09-05 19:00:16 -0600190 self._boards = board.Boards()
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600191 for brd in BOARDS:
Simon Glass127a2392022-07-11 19:04:02 -0600192 self._boards.add_board(board.Board(*brd))
Simon Glassed098bb2014-09-05 19:00:13 -0600193
Simon Glass8e959562014-09-05 19:00:20 -0600194 # Directories where the source been cloned
195 self._clone_dirs = []
196 self._commits = len(commit_shortlog.splitlines()) + 1
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600197 self._total_builds = self._commits * len(BOARDS)
Simon Glass8e959562014-09-05 19:00:20 -0600198
199 # Number of calls to make
200 self._make_calls = 0
201
202 # Map of [board, commit] to error messages
203 self._error = {}
204
Simon Glass4aeceb92014-09-05 19:00:22 -0600205 self._test_branch = TEST_BRANCH
206
Simon Glass8e959562014-09-05 19:00:20 -0600207 # Avoid sending any output and clear all terminal output
Simon Glass02811582022-01-29 14:14:18 -0700208 terminal.set_print_test_mode()
209 terminal.get_print_test_lines()
Simon Glass8e959562014-09-05 19:00:20 -0600210
Simon Glassed098bb2014-09-05 19:00:13 -0600211 def tearDown(self):
212 shutil.rmtree(self._base_dir)
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600213 #shutil.rmtree(self._output_dir)
Simon Glassed098bb2014-09-05 19:00:13 -0600214
Simon Glass8e959562014-09-05 19:00:20 -0600215 def setupToolchains(self):
216 self._toolchains = toolchain.Toolchains()
217 self._toolchains.Add('gcc', test=False)
218
Simon Glassed098bb2014-09-05 19:00:13 -0600219 def _RunBuildman(self, *args):
Simon Glass840be732022-01-29 14:14:05 -0700220 return command.run_pipe([[self._buildman_pathname] + list(args)],
Simon Glassed098bb2014-09-05 19:00:13 -0600221 capture=True, capture_stderr=True)
222
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600223 def _RunControl(self, *args, brds=None, clean_dir=False,
Simon Glass9bf9a722021-04-11 16:27:27 +1200224 test_thread_exceptions=False):
Simon Glassa29b3ea2021-04-11 16:27:25 +1200225 """Run buildman
226
227 Args:
228 args: List of arguments to pass
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600229 brds: Boards object
Simon Glassa29b3ea2021-04-11 16:27:25 +1200230 clean_dir: Used for tests only, indicates that the existing output_dir
231 should be removed before starting the build
Simon Glass9bf9a722021-04-11 16:27:27 +1200232 test_thread_exceptions: Uses for tests only, True to make the threads
233 raise an exception instead of reporting their result. This simulates
234 a failure in the code somewhere
Simon Glassa29b3ea2021-04-11 16:27:25 +1200235
236 Returns:
237 result code from buildman
238 """
Simon Glassed098bb2014-09-05 19:00:13 -0600239 sys.argv = [sys.argv[0]] + list(args)
240 options, args = cmdline.ParseArgs()
Simon Glass8e959562014-09-05 19:00:20 -0600241 result = control.DoBuildman(options, args, toolchains=self._toolchains,
Simon Glass5df45222022-07-11 19:04:00 -0600242 make_func=self._HandleMake, brds=brds or self._boards,
Simon Glass9bf9a722021-04-11 16:27:27 +1200243 clean_dir=clean_dir,
244 test_thread_exceptions=test_thread_exceptions)
Simon Glass8e959562014-09-05 19:00:20 -0600245 self._builder = control.builder
246 return result
Simon Glassed098bb2014-09-05 19:00:13 -0600247
248 def testFullHelp(self):
249 command.test_result = None
250 result = self._RunBuildman('-H')
251 help_file = os.path.join(self._buildman_dir, 'README')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500252 # Remove possible extraneous strings
253 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
254 gothelp = result.stdout.replace(extra, '')
255 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glassed098bb2014-09-05 19:00:13 -0600256 self.assertEqual(0, len(result.stderr))
257 self.assertEqual(0, result.return_code)
258
259 def testHelp(self):
260 command.test_result = None
261 result = self._RunBuildman('-h')
262 help_file = os.path.join(self._buildman_dir, 'README')
263 self.assertTrue(len(result.stdout) > 1000)
264 self.assertEqual(0, len(result.stderr))
265 self.assertEqual(0, result.return_code)
266
267 def testGitSetup(self):
268 """Test gitutils.Setup(), from outside the module itself"""
269 command.test_result = command.CommandResult(return_code=1)
Simon Glass761648b2022-01-29 14:14:11 -0700270 gitutil.setup()
Simon Glassed098bb2014-09-05 19:00:13 -0600271 self.assertEqual(gitutil.use_no_decorate, False)
272
273 command.test_result = command.CommandResult(return_code=0)
Simon Glass761648b2022-01-29 14:14:11 -0700274 gitutil.setup()
Simon Glassed098bb2014-09-05 19:00:13 -0600275 self.assertEqual(gitutil.use_no_decorate, True)
276
277 def _HandleCommandGitLog(self, args):
Simon Glass642e9a62016-03-12 18:50:31 -0700278 if args[-1] == '--':
279 args = args[:-1]
Simon Glassed098bb2014-09-05 19:00:13 -0600280 if '-n0' in args:
281 return command.CommandResult(return_code=0)
Simon Glass4aeceb92014-09-05 19:00:22 -0600282 elif args[-1] == 'upstream/master..%s' % self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600283 return command.CommandResult(return_code=0, stdout=commit_shortlog)
284 elif args[:3] == ['--no-color', '--no-decorate', '--reverse']:
Simon Glass4aeceb92014-09-05 19:00:22 -0600285 if args[-1] == self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600286 count = int(args[3][2:])
287 return command.CommandResult(return_code=0,
288 stdout=''.join(commit_log[:count]))
Simon Glassed098bb2014-09-05 19:00:13 -0600289
290 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600291 print('git log', args)
Simon Glassed098bb2014-09-05 19:00:13 -0600292 sys.exit(1)
293
Simon Glass8e959562014-09-05 19:00:20 -0600294 def _HandleCommandGitConfig(self, args):
295 config = args[0]
296 if config == 'sendemail.aliasesfile':
297 return command.CommandResult(return_code=0)
298 elif config.startswith('branch.badbranch'):
299 return command.CommandResult(return_code=1)
Simon Glass4aeceb92014-09-05 19:00:22 -0600300 elif config == 'branch.%s.remote' % self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600301 return command.CommandResult(return_code=0, stdout='upstream\n')
Simon Glass4aeceb92014-09-05 19:00:22 -0600302 elif config == 'branch.%s.merge' % self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600303 return command.CommandResult(return_code=0,
304 stdout='refs/heads/master\n')
305
306 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600307 print('git config', args)
Simon Glass8e959562014-09-05 19:00:20 -0600308 sys.exit(1)
309
Simon Glassed098bb2014-09-05 19:00:13 -0600310 def _HandleCommandGit(self, in_args):
311 """Handle execution of a git command
312
313 This uses a hacked-up parser.
314
315 Args:
316 in_args: Arguments after 'git' from the command line
317 """
318 git_args = [] # Top-level arguments to git itself
319 sub_cmd = None # Git sub-command selected
320 args = [] # Arguments to the git sub-command
321 for arg in in_args:
322 if sub_cmd:
323 args.append(arg)
324 elif arg[0] == '-':
325 git_args.append(arg)
326 else:
Simon Glass8e959562014-09-05 19:00:20 -0600327 if git_args and git_args[-1] in ['--git-dir', '--work-tree']:
328 git_args.append(arg)
329 else:
330 sub_cmd = arg
Simon Glassed098bb2014-09-05 19:00:13 -0600331 if sub_cmd == 'config':
Simon Glass8e959562014-09-05 19:00:20 -0600332 return self._HandleCommandGitConfig(args)
Simon Glassed098bb2014-09-05 19:00:13 -0600333 elif sub_cmd == 'log':
334 return self._HandleCommandGitLog(args)
Simon Glass8e959562014-09-05 19:00:20 -0600335 elif sub_cmd == 'clone':
336 return command.CommandResult(return_code=0)
337 elif sub_cmd == 'checkout':
338 return command.CommandResult(return_code=0)
Alper Nebi Yasakfede44a2020-09-03 15:51:03 +0300339 elif sub_cmd == 'worktree':
340 return command.CommandResult(return_code=0)
Simon Glassed098bb2014-09-05 19:00:13 -0600341
342 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600343 print('git', git_args, sub_cmd, args)
Simon Glassed098bb2014-09-05 19:00:13 -0600344 sys.exit(1)
345
346 def _HandleCommandNm(self, args):
347 return command.CommandResult(return_code=0)
348
349 def _HandleCommandObjdump(self, args):
350 return command.CommandResult(return_code=0)
351
Alex Kiernanf07ed232018-05-31 04:48:33 +0000352 def _HandleCommandObjcopy(self, args):
353 return command.CommandResult(return_code=0)
354
Simon Glassed098bb2014-09-05 19:00:13 -0600355 def _HandleCommandSize(self, args):
356 return command.CommandResult(return_code=0)
357
358 def _HandleCommand(self, **kwargs):
359 """Handle a command execution.
360
361 The command is in kwargs['pipe-list'], as a list of pipes, each a
362 list of commands. The command should be emulated as required for
363 testing purposes.
364
365 Returns:
366 A CommandResult object
367 """
368 pipe_list = kwargs['pipe_list']
Simon Glass8e959562014-09-05 19:00:20 -0600369 wc = False
Simon Glassed098bb2014-09-05 19:00:13 -0600370 if len(pipe_list) != 1:
Simon Glass8e959562014-09-05 19:00:20 -0600371 if pipe_list[1] == ['wc', '-l']:
372 wc = True
373 else:
Simon Glassc78ed662019-10-31 07:42:53 -0600374 print('invalid pipe', kwargs)
Simon Glass8e959562014-09-05 19:00:20 -0600375 sys.exit(1)
Simon Glassed098bb2014-09-05 19:00:13 -0600376 cmd = pipe_list[0][0]
377 args = pipe_list[0][1:]
Simon Glass8e959562014-09-05 19:00:20 -0600378 result = None
Simon Glassed098bb2014-09-05 19:00:13 -0600379 if cmd == 'git':
Simon Glass8e959562014-09-05 19:00:20 -0600380 result = self._HandleCommandGit(args)
Simon Glassed098bb2014-09-05 19:00:13 -0600381 elif cmd == './scripts/show-gnu-make':
382 return command.CommandResult(return_code=0, stdout='make')
Simon Glass8e959562014-09-05 19:00:20 -0600383 elif cmd.endswith('nm'):
Simon Glassed098bb2014-09-05 19:00:13 -0600384 return self._HandleCommandNm(args)
Simon Glass8e959562014-09-05 19:00:20 -0600385 elif cmd.endswith('objdump'):
Simon Glassed098bb2014-09-05 19:00:13 -0600386 return self._HandleCommandObjdump(args)
Alex Kiernanf07ed232018-05-31 04:48:33 +0000387 elif cmd.endswith('objcopy'):
388 return self._HandleCommandObjcopy(args)
Simon Glass8e959562014-09-05 19:00:20 -0600389 elif cmd.endswith( 'size'):
Simon Glassed098bb2014-09-05 19:00:13 -0600390 return self._HandleCommandSize(args)
391
Simon Glass8e959562014-09-05 19:00:20 -0600392 if not result:
393 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600394 print('unknown command', kwargs)
Simon Glass8e959562014-09-05 19:00:20 -0600395 sys.exit(1)
396
397 if wc:
398 result.stdout = len(result.stdout.splitlines())
399 return result
Simon Glassed098bb2014-09-05 19:00:13 -0600400
401 def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
402 """Handle execution of 'make'
403
404 Args:
405 commit: Commit object that is being built
406 brd: Board object that is being built
407 stage: Stage that we are at (mrproper, config, build)
408 cwd: Directory where make should be run
409 args: Arguments to pass to make
Simon Glass840be732022-01-29 14:14:05 -0700410 kwargs: Arguments to pass to command.run_pipe()
Simon Glassed098bb2014-09-05 19:00:13 -0600411 """
Simon Glass8e959562014-09-05 19:00:20 -0600412 self._make_calls += 1
Simon Glassed098bb2014-09-05 19:00:13 -0600413 if stage == 'mrproper':
414 return command.CommandResult(return_code=0)
415 elif stage == 'config':
416 return command.CommandResult(return_code=0,
417 combined='Test configuration complete')
418 elif stage == 'build':
Simon Glass8e959562014-09-05 19:00:20 -0600419 stderr = ''
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600420 out_dir = ''
421 for arg in args:
422 if arg.startswith('O='):
423 out_dir = arg[2:]
424 fname = os.path.join(cwd or '', out_dir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -0700425 tools.write_file(fname, b'U-Boot')
Simon Glass8e959562014-09-05 19:00:20 -0600426 if type(commit) is not str:
427 stderr = self._error.get((brd.target, commit.sequence))
428 if stderr:
429 return command.CommandResult(return_code=1, stderr=stderr)
Simon Glassed098bb2014-09-05 19:00:13 -0600430 return command.CommandResult(return_code=0)
431
432 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600433 print('make', stage)
Simon Glassed098bb2014-09-05 19:00:13 -0600434 sys.exit(1)
435
Simon Glass8e959562014-09-05 19:00:20 -0600436 # Example function to print output lines
437 def print_lines(self, lines):
Simon Glassc78ed662019-10-31 07:42:53 -0600438 print(len(lines))
Simon Glass8e959562014-09-05 19:00:20 -0600439 for line in lines:
Simon Glassc78ed662019-10-31 07:42:53 -0600440 print(line)
Simon Glass02811582022-01-29 14:14:18 -0700441 #self.print_lines(terminal.get_print_test_lines())
Simon Glass8e959562014-09-05 19:00:20 -0600442
Simon Glasscbd36582014-09-05 19:00:16 -0600443 def testNoBoards(self):
444 """Test that buildman aborts when there are no boards"""
445 self._boards = board.Boards()
446 with self.assertRaises(SystemExit):
447 self._RunControl()
448
Simon Glassed098bb2014-09-05 19:00:13 -0600449 def testCurrentSource(self):
450 """Very simple test to invoke buildman on the current source"""
Simon Glass8e959562014-09-05 19:00:20 -0600451 self.setupToolchains();
Tom Rinie95eddc2019-10-07 17:17:36 -0400452 self._RunControl('-o', self._output_dir)
Simon Glass02811582022-01-29 14:14:18 -0700453 lines = terminal.get_print_test_lines()
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600454 self.assertIn('Building current source for %d boards' % len(BOARDS),
Simon Glass8e959562014-09-05 19:00:20 -0600455 lines[0].text)
456
457 def testBadBranch(self):
458 """Test that we can detect an invalid branch"""
459 with self.assertRaises(ValueError):
460 self._RunControl('-b', 'badbranch')
461
462 def testBadToolchain(self):
463 """Test that missing toolchains are detected"""
464 self.setupToolchains();
Tom Rinie95eddc2019-10-07 17:17:36 -0400465 ret_code = self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass02811582022-01-29 14:14:18 -0700466 lines = terminal.get_print_test_lines()
Simon Glass8e959562014-09-05 19:00:20 -0600467
468 # Buildman always builds the upstream commit as well
469 self.assertIn('Building %d commits for %d boards' %
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600470 (self._commits, len(BOARDS)), lines[0].text)
Simon Glass8e959562014-09-05 19:00:20 -0600471 self.assertEqual(self._builder.count, self._total_builds)
472
473 # Only sandbox should succeed, the others don't have toolchains
474 self.assertEqual(self._builder.fail,
475 self._total_builds - self._commits)
Simon Glasse4cd5062020-04-09 10:49:45 -0600476 self.assertEqual(ret_code, 100)
Simon Glass8e959562014-09-05 19:00:20 -0600477
478 for commit in range(self._commits):
Simon Glass127a2392022-07-11 19:04:02 -0600479 for brd in self._boards.get_list():
Simon Glass8132f982022-07-11 19:03:57 -0600480 if brd.arch != 'sandbox':
481 errfile = self._builder.GetErrFile(commit, brd.target)
Simon Glass8e959562014-09-05 19:00:20 -0600482 fd = open(errfile)
483 self.assertEqual(fd.readlines(),
Simon Glass8132f982022-07-11 19:03:57 -0600484 ['No tool chain for %s\n' % brd.arch])
Simon Glass8e959562014-09-05 19:00:20 -0600485 fd.close()
486
487 def testBranch(self):
488 """Test building a branch with all toolchains present"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400489 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600490 self.assertEqual(self._builder.count, self._total_builds)
491 self.assertEqual(self._builder.fail, 0)
492
493 def testCount(self):
494 """Test building a specific number of commitst"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400495 self._RunControl('-b', TEST_BRANCH, '-c2', '-o', self._output_dir)
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600496 self.assertEqual(self._builder.count, 2 * len(BOARDS))
Simon Glass8e959562014-09-05 19:00:20 -0600497 self.assertEqual(self._builder.fail, 0)
Simon Glass6029af12020-04-09 15:08:51 -0600498 # Each board has a config, and then one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600499 self.assertEqual(self._make_calls, len(BOARDS) * (1 + 2))
Simon Glass8e959562014-09-05 19:00:20 -0600500
501 def testIncremental(self):
502 """Test building a branch twice - the second time should do nothing"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400503 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600504
505 # Each board has a mrproper, config, and then one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600506 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
Simon Glass8e959562014-09-05 19:00:20 -0600507 self._make_calls = 0
Tom Rinie95eddc2019-10-07 17:17:36 -0400508 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
Simon Glass8e959562014-09-05 19:00:20 -0600509 self.assertEqual(self._make_calls, 0)
510 self.assertEqual(self._builder.count, self._total_builds)
511 self.assertEqual(self._builder.fail, 0)
512
513 def testForceBuild(self):
514 """The -f flag should force a rebuild"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400515 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600516 self._make_calls = 0
Tom Rinie95eddc2019-10-07 17:17:36 -0400517 self._RunControl('-b', TEST_BRANCH, '-f', '-o', self._output_dir, clean_dir=False)
Simon Glass6029af12020-04-09 15:08:51 -0600518 # Each board has a config and one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600519 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
Simon Glass8e959562014-09-05 19:00:20 -0600520
521 def testForceReconfigure(self):
522 """The -f flag should force a rebuild"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400523 self._RunControl('-b', TEST_BRANCH, '-C', '-o', self._output_dir)
Simon Glass6029af12020-04-09 15:08:51 -0600524 # Each commit has a config and make
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600525 self.assertEqual(self._make_calls, len(BOARDS) * self._commits * 2)
Simon Glass6029af12020-04-09 15:08:51 -0600526
Simon Glass6029af12020-04-09 15:08:51 -0600527 def testMrproper(self):
528 """The -f flag should force a rebuild"""
529 self._RunControl('-b', TEST_BRANCH, '-m', '-o', self._output_dir)
530 # Each board has a mkproper, config and then one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600531 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 2))
Simon Glass8e959562014-09-05 19:00:20 -0600532
533 def testErrors(self):
534 """Test handling of build errors"""
535 self._error['board2', 1] = 'fred\n'
Tom Rinie95eddc2019-10-07 17:17:36 -0400536 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600537 self.assertEqual(self._builder.count, self._total_builds)
538 self.assertEqual(self._builder.fail, 1)
539
540 # Remove the error. This should have no effect since the commit will
541 # not be rebuilt
542 del self._error['board2', 1]
543 self._make_calls = 0
Tom Rinie95eddc2019-10-07 17:17:36 -0400544 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
Simon Glass8e959562014-09-05 19:00:20 -0600545 self.assertEqual(self._builder.count, self._total_builds)
546 self.assertEqual(self._make_calls, 0)
547 self.assertEqual(self._builder.fail, 1)
548
549 # Now use the -F flag to force rebuild of the bad commit
Tom Rinie95eddc2019-10-07 17:17:36 -0400550 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, '-F', clean_dir=False)
Simon Glass8e959562014-09-05 19:00:20 -0600551 self.assertEqual(self._builder.count, self._total_builds)
552 self.assertEqual(self._builder.fail, 0)
Simon Glass6029af12020-04-09 15:08:51 -0600553 self.assertEqual(self._make_calls, 2)
Simon Glass4aeceb92014-09-05 19:00:22 -0600554
555 def testBranchWithSlash(self):
556 """Test building a branch with a '/' in the name"""
557 self._test_branch = '/__dev/__testbranch'
558 self._RunControl('-b', self._test_branch, clean_dir=False)
559 self.assertEqual(self._builder.count, self._total_builds)
560 self.assertEqual(self._builder.fail, 0)
Lothar Waßmannce6df922018-04-08 05:14:11 -0600561
Simon Glassff48a212020-04-17 17:51:33 -0600562 def testEnvironment(self):
563 """Test that the done and environment files are written to out-env"""
564 self._RunControl('-o', self._output_dir)
565 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
566 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
567 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
568
Simon Glass93008e22021-04-11 16:27:28 +1200569 def testEnvironmentUnicode(self):
570 """Test there are no unicode errors when the env has non-ASCII chars"""
571 try:
572 varname = b'buildman_test_var'
573 os.environb[varname] = b'strange\x80chars'
574 self.assertEqual(0, self._RunControl('-o', self._output_dir))
575 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
576 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
577 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
578 finally:
579 del os.environb[varname]
580
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600581 def testWorkInOutput(self):
582 """Test the -w option which should write directly to the output dir"""
583 board_list = board.Boards()
Simon Glass127a2392022-07-11 19:04:02 -0600584 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600585 self._RunControl('-o', self._output_dir, '-w', clean_dir=False,
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600586 brds=board_list)
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600587 self.assertTrue(
588 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
Simon Glasse3c85ab2020-04-17 17:51:34 -0600589 self.assertTrue(
590 os.path.exists(os.path.join(self._output_dir, 'done')))
591 self.assertTrue(
592 os.path.exists(os.path.join(self._output_dir, 'out-env')))
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600593
594 def testWorkInOutputFail(self):
595 """Test the -w option failures"""
596 with self.assertRaises(SystemExit) as e:
597 self._RunControl('-o', self._output_dir, '-w', clean_dir=False)
598 self.assertIn("single board", str(e.exception))
599 self.assertFalse(
600 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
601
602 board_list = board.Boards()
Simon Glass127a2392022-07-11 19:04:02 -0600603 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600604 with self.assertRaises(SystemExit) as e:
605 self._RunControl('-b', self._test_branch, '-o', self._output_dir,
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600606 '-w', clean_dir=False, brds=board_list)
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600607 self.assertIn("single commit", str(e.exception))
Simon Glassd9c98632020-04-17 17:51:32 -0600608
609 board_list = board.Boards()
Simon Glass127a2392022-07-11 19:04:02 -0600610 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassd9c98632020-04-17 17:51:32 -0600611 with self.assertRaises(SystemExit) as e:
612 self._RunControl('-w', clean_dir=False)
613 self.assertIn("specify -o", str(e.exception))
Simon Glass9bf9a722021-04-11 16:27:27 +1200614
615 def testThreadExceptions(self):
616 """Test that exceptions in threads are reported"""
617 with test_util.capture_sys_output() as (stdout, stderr):
618 self.assertEqual(102, self._RunControl('-o', self._output_dir,
619 test_thread_exceptions=True))
Simon Glass9bac1672022-01-22 05:07:32 -0700620 self.assertIn(
621 'Thread exception (use -T0 to run without threads): test exception',
622 stdout.getvalue())