blob: 6d1557293f03740848fb5df3745488d942ea65a2 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassd4144e42014-09-05 19:00:13 -06002# Copyright (c) 2014 Google, Inc
3#
Simon Glassd4144e42014-09-05 19:00:13 -06004
5import os
6import shutil
7import sys
8import tempfile
9import unittest
10
Simon Glass0ede00f2020-04-17 18:09:02 -060011from buildman import board
Simon Glassc52bd222022-07-11 19:04:03 -060012from buildman import boards
Simon Glass0ede00f2020-04-17 18:09:02 -060013from buildman import bsettings
14from buildman import cmdline
15from buildman import control
16from buildman import toolchain
Simon Glassbf776672020-04-17 18:09:04 -060017from patman import command
18from patman import gitutil
19from patman import terminal
Simon Glass8116c782021-04-11 16:27:27 +120020from patman import test_util
Simon Glassbf776672020-04-17 18:09:04 -060021from patman import tools
Simon Glassd4144e42014-09-05 19:00:13 -060022
Simon Glass8b985ee2014-09-05 19:00:15 -060023settings_data = '''
24# Buildman settings file
Tom Rinid7713ad2022-11-09 19:14:53 -070025[global]
Simon Glass8b985ee2014-09-05 19:00:15 -060026
27[toolchain]
28
29[toolchain-alias]
30
31[make-flags]
32src=/home/sjg/c/src
33chroot=/home/sjg/c/chroot
Masahiro Yamada98655432018-08-06 20:47:38 +090034vboot=VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
Simon Glass8b985ee2014-09-05 19:00:15 -060035chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
36chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
37chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
38'''
39
Simon Glass938fa372022-07-11 19:03:58 -060040BOARDS = [
Simon Glass823e60b2014-09-05 19:00:16 -060041 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''],
42 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
43 ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
Simon Glass823e60b2014-09-05 19:00:16 -060044 ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
45]
46
Simon Glassdfb7e932014-09-05 19:00:20 -060047commit_shortlog = """4aca821 patman: Avoid changing the order of tags
4839403bb patman: Use --no-pager' to stop git from forking a pager
49db6e6f2 patman: Remove the -a option
50f2ccf03 patman: Correct unit tests to run correctly
511d097f9 patman: Fix indentation in terminal.py
52d073747 patman: Support the 'reverse' option for 'git log
53"""
54
55commit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd
56Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
57Date: Fri Aug 22 19:12:41 2014 +0900
58
59 buildman: refactor help message
60
61 "buildman [options]" is displayed by default.
62
63 Append the rest of help messages to parser.usage
64 instead of replacing it.
65
66 Besides, "-b <branch>" is not mandatory since commit fea5858e.
67 Drop it from the usage.
68
69 Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
70""",
71"""commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8
72Author: Simon Glass <sjg@chromium.org>
73Date: Thu Aug 14 16:48:25 2014 -0600
74
75 patman: Support the 'reverse' option for 'git log'
76
77 This option is currently not supported, but needs to be, for buildman to
78 operate as expected.
79
80 Series-changes: 7
81 - Add new patch to fix the 'reverse' bug
82
Simon Glass950a2312014-09-05 19:00:23 -060083 Series-version: 8
Simon Glassdfb7e932014-09-05 19:00:20 -060084
85 Change-Id: I79078f792e8b390b8a1272a8023537821d45feda
86 Reported-by: York Sun <yorksun@freescale.com>
87 Signed-off-by: Simon Glass <sjg@chromium.org>
88
89""",
90"""commit 1d097f9ab487c5019152fd47bda126839f3bf9fc
91Author: Simon Glass <sjg@chromium.org>
92Date: Sat Aug 9 11:44:32 2014 -0600
93
94 patman: Fix indentation in terminal.py
95
96 This code came from a different project with 2-character indentation. Fix
97 it for U-Boot.
98
99 Series-changes: 6
100 - Add new patch to fix indentation in teminal.py
101
102 Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34
103 Signed-off-by: Simon Glass <sjg@chromium.org>
104
105""",
106"""commit f2ccf03869d1e152c836515a3ceb83cdfe04a105
107Author: Simon Glass <sjg@chromium.org>
108Date: Sat Aug 9 11:08:24 2014 -0600
109
110 patman: Correct unit tests to run correctly
111
112 It seems that doctest behaves differently now, and some of the unit tests
113 do not run. Adjust the tests to work correctly.
114
115 ./tools/patman/patman --test
116 <unittest.result.TestResult run=10 errors=0 failures=0>
117
118 Series-changes: 6
119 - Add new patch to fix patman unit tests
120
121 Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b
122
123""",
124"""commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c
125Author: Simon Glass <sjg@chromium.org>
126Date: Sat Aug 9 12:06:02 2014 -0600
127
128 patman: Remove the -a option
129
130 It seems that this is no longer needed, since checkpatch.pl will catch
131 whitespace problems in patches. Also the option is not widely used, so
132 it seems safe to just remove it.
133
134 Series-changes: 6
135 - Add new patch to remove patman's -a option
136
137 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
138 Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc
139
140""",
141"""commit 39403bb4f838153028a6f21ca30bf100f3791133
142Author: Simon Glass <sjg@chromium.org>
143Date: Thu Aug 14 21:50:52 2014 -0600
144
145 patman: Use --no-pager' to stop git from forking a pager
146
147""",
148"""commit 4aca821e27e97925c039e69fd37375b09c6f129c
149Author: Simon Glass <sjg@chromium.org>
150Date: Fri Aug 22 15:57:39 2014 -0600
151
152 patman: Avoid changing the order of tags
153
154 patman collects tags that it sees in the commit and places them nicely
155 sorted at the end of the patch. However, this is not really necessary and
156 in fact is apparently not desirable.
157
158 Series-changes: 9
159 - Add new patch to avoid changing the order of tags
160
Simon Glass950a2312014-09-05 19:00:23 -0600161 Series-version: 9
162
Simon Glassdfb7e932014-09-05 19:00:20 -0600163 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
164 Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db
165"""]
166
167TEST_BRANCH = '__testbranch'
168
Simon Glassd4144e42014-09-05 19:00:13 -0600169class TestFunctional(unittest.TestCase):
170 """Functional test for buildman.
171
172 This aims to test from just below the invocation of buildman (parsing
173 of arguments) to 'make' and 'git' invocation. It is not a true
174 emd-to-end test, as it mocks git, make and the tool chain. But this
175 makes it easier to detect when the builder is doing the wrong thing,
176 since in many cases this test code will fail. For example, only a
177 very limited subset of 'git' arguments is supported - anything
178 unexpected will fail.
179 """
180 def setUp(self):
181 self._base_dir = tempfile.mkdtemp()
Tom Riniaae62582019-10-07 17:17:36 -0400182 self._output_dir = tempfile.mkdtemp()
Simon Glassd4144e42014-09-05 19:00:13 -0600183 self._git_dir = os.path.join(self._base_dir, 'src')
184 self._buildman_pathname = sys.argv[0]
Simon Glassbd6f5d92016-07-27 20:33:00 -0600185 self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
Simon Glassd4144e42014-09-05 19:00:13 -0600186 command.test_result = self._HandleCommand
Simon Glass19133b72022-01-22 05:07:31 -0700187 bsettings.Setup(None)
188 bsettings.AddFile(settings_data)
Simon Glassdfb7e932014-09-05 19:00:20 -0600189 self.setupToolchains()
190 self._toolchains.Add('arm-gcc', test=False)
191 self._toolchains.Add('powerpc-gcc', test=False)
Simon Glassc52bd222022-07-11 19:04:03 -0600192 self._boards = boards.Boards()
Simon Glass938fa372022-07-11 19:03:58 -0600193 for brd in BOARDS:
Simon Glass6014db62022-07-11 19:04:02 -0600194 self._boards.add_board(board.Board(*brd))
Simon Glassd4144e42014-09-05 19:00:13 -0600195
Simon Glassdfb7e932014-09-05 19:00:20 -0600196 # Directories where the source been cloned
197 self._clone_dirs = []
198 self._commits = len(commit_shortlog.splitlines()) + 1
Simon Glass938fa372022-07-11 19:03:58 -0600199 self._total_builds = self._commits * len(BOARDS)
Simon Glassdfb7e932014-09-05 19:00:20 -0600200
201 # Number of calls to make
202 self._make_calls = 0
203
204 # Map of [board, commit] to error messages
205 self._error = {}
206
Simon Glassf7582ce2014-09-05 19:00:22 -0600207 self._test_branch = TEST_BRANCH
208
Tom Rinid7713ad2022-11-09 19:14:53 -0700209 # Set to True to report missing blobs
210 self._missing = False
211
Simon Glassdfb7e932014-09-05 19:00:20 -0600212 # Avoid sending any output and clear all terminal output
Simon Glass098b10f2022-01-29 14:14:18 -0700213 terminal.set_print_test_mode()
214 terminal.get_print_test_lines()
Simon Glassdfb7e932014-09-05 19:00:20 -0600215
Simon Glassd4144e42014-09-05 19:00:13 -0600216 def tearDown(self):
217 shutil.rmtree(self._base_dir)
Simon Glass5f319fa2022-11-09 19:14:52 -0700218 shutil.rmtree(self._output_dir)
Simon Glassd4144e42014-09-05 19:00:13 -0600219
Simon Glassdfb7e932014-09-05 19:00:20 -0600220 def setupToolchains(self):
221 self._toolchains = toolchain.Toolchains()
222 self._toolchains.Add('gcc', test=False)
223
Simon Glassd4144e42014-09-05 19:00:13 -0600224 def _RunBuildman(self, *args):
Simon Glassd9800692022-01-29 14:14:05 -0700225 return command.run_pipe([[self._buildman_pathname] + list(args)],
Simon Glassd4144e42014-09-05 19:00:13 -0600226 capture=True, capture_stderr=True)
227
Simon Glass938fa372022-07-11 19:03:58 -0600228 def _RunControl(self, *args, brds=None, clean_dir=False,
Simon Glass8116c782021-04-11 16:27:27 +1200229 test_thread_exceptions=False):
Simon Glass24993312021-04-11 16:27:25 +1200230 """Run buildman
231
232 Args:
233 args: List of arguments to pass
Simon Glass938fa372022-07-11 19:03:58 -0600234 brds: Boards object
Simon Glass24993312021-04-11 16:27:25 +1200235 clean_dir: Used for tests only, indicates that the existing output_dir
236 should be removed before starting the build
Simon Glass8116c782021-04-11 16:27:27 +1200237 test_thread_exceptions: Uses for tests only, True to make the threads
238 raise an exception instead of reporting their result. This simulates
239 a failure in the code somewhere
Simon Glass24993312021-04-11 16:27:25 +1200240
241 Returns:
242 result code from buildman
243 """
Simon Glassd4144e42014-09-05 19:00:13 -0600244 sys.argv = [sys.argv[0]] + list(args)
245 options, args = cmdline.ParseArgs()
Simon Glassdfb7e932014-09-05 19:00:20 -0600246 result = control.DoBuildman(options, args, toolchains=self._toolchains,
Simon Glasscc2c0d12022-07-11 19:04:00 -0600247 make_func=self._HandleMake, brds=brds or self._boards,
Simon Glass8116c782021-04-11 16:27:27 +1200248 clean_dir=clean_dir,
249 test_thread_exceptions=test_thread_exceptions)
Simon Glassdfb7e932014-09-05 19:00:20 -0600250 self._builder = control.builder
251 return result
Simon Glassd4144e42014-09-05 19:00:13 -0600252
253 def testFullHelp(self):
254 command.test_result = None
255 result = self._RunBuildman('-H')
Simon Glass74df4912022-11-09 19:14:43 -0700256 help_file = os.path.join(self._buildman_dir, 'README.rst')
Tom Rini3759df02018-01-16 15:29:50 -0500257 # Remove possible extraneous strings
258 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
259 gothelp = result.stdout.replace(extra, '')
260 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glassd4144e42014-09-05 19:00:13 -0600261 self.assertEqual(0, len(result.stderr))
262 self.assertEqual(0, result.return_code)
263
264 def testHelp(self):
265 command.test_result = None
266 result = self._RunBuildman('-h')
Simon Glass74df4912022-11-09 19:14:43 -0700267 help_file = os.path.join(self._buildman_dir, 'README.rst')
Simon Glassd4144e42014-09-05 19:00:13 -0600268 self.assertTrue(len(result.stdout) > 1000)
269 self.assertEqual(0, len(result.stderr))
270 self.assertEqual(0, result.return_code)
271
272 def testGitSetup(self):
273 """Test gitutils.Setup(), from outside the module itself"""
274 command.test_result = command.CommandResult(return_code=1)
Simon Glass0157b182022-01-29 14:14:11 -0700275 gitutil.setup()
Simon Glassd4144e42014-09-05 19:00:13 -0600276 self.assertEqual(gitutil.use_no_decorate, False)
277
278 command.test_result = command.CommandResult(return_code=0)
Simon Glass0157b182022-01-29 14:14:11 -0700279 gitutil.setup()
Simon Glassd4144e42014-09-05 19:00:13 -0600280 self.assertEqual(gitutil.use_no_decorate, True)
281
282 def _HandleCommandGitLog(self, args):
Simon Glassd4c85722016-03-12 18:50:31 -0700283 if args[-1] == '--':
284 args = args[:-1]
Simon Glassd4144e42014-09-05 19:00:13 -0600285 if '-n0' in args:
286 return command.CommandResult(return_code=0)
Simon Glassf7582ce2014-09-05 19:00:22 -0600287 elif args[-1] == 'upstream/master..%s' % self._test_branch:
Simon Glassdfb7e932014-09-05 19:00:20 -0600288 return command.CommandResult(return_code=0, stdout=commit_shortlog)
289 elif args[:3] == ['--no-color', '--no-decorate', '--reverse']:
Simon Glassf7582ce2014-09-05 19:00:22 -0600290 if args[-1] == self._test_branch:
Simon Glassdfb7e932014-09-05 19:00:20 -0600291 count = int(args[3][2:])
292 return command.CommandResult(return_code=0,
293 stdout=''.join(commit_log[:count]))
Simon Glassd4144e42014-09-05 19:00:13 -0600294
295 # Not handled, so abort
Simon Glassc05aa032019-10-31 07:42:53 -0600296 print('git log', args)
Simon Glassd4144e42014-09-05 19:00:13 -0600297 sys.exit(1)
298
Simon Glassdfb7e932014-09-05 19:00:20 -0600299 def _HandleCommandGitConfig(self, args):
300 config = args[0]
301 if config == 'sendemail.aliasesfile':
302 return command.CommandResult(return_code=0)
303 elif config.startswith('branch.badbranch'):
304 return command.CommandResult(return_code=1)
Simon Glassf7582ce2014-09-05 19:00:22 -0600305 elif config == 'branch.%s.remote' % self._test_branch:
Simon Glassdfb7e932014-09-05 19:00:20 -0600306 return command.CommandResult(return_code=0, stdout='upstream\n')
Simon Glassf7582ce2014-09-05 19:00:22 -0600307 elif config == 'branch.%s.merge' % self._test_branch:
Simon Glassdfb7e932014-09-05 19:00:20 -0600308 return command.CommandResult(return_code=0,
309 stdout='refs/heads/master\n')
310
311 # Not handled, so abort
Simon Glassc05aa032019-10-31 07:42:53 -0600312 print('git config', args)
Simon Glassdfb7e932014-09-05 19:00:20 -0600313 sys.exit(1)
314
Simon Glassd4144e42014-09-05 19:00:13 -0600315 def _HandleCommandGit(self, in_args):
316 """Handle execution of a git command
317
318 This uses a hacked-up parser.
319
320 Args:
321 in_args: Arguments after 'git' from the command line
322 """
323 git_args = [] # Top-level arguments to git itself
324 sub_cmd = None # Git sub-command selected
325 args = [] # Arguments to the git sub-command
326 for arg in in_args:
327 if sub_cmd:
328 args.append(arg)
329 elif arg[0] == '-':
330 git_args.append(arg)
331 else:
Simon Glassdfb7e932014-09-05 19:00:20 -0600332 if git_args and git_args[-1] in ['--git-dir', '--work-tree']:
333 git_args.append(arg)
334 else:
335 sub_cmd = arg
Simon Glassd4144e42014-09-05 19:00:13 -0600336 if sub_cmd == 'config':
Simon Glassdfb7e932014-09-05 19:00:20 -0600337 return self._HandleCommandGitConfig(args)
Simon Glassd4144e42014-09-05 19:00:13 -0600338 elif sub_cmd == 'log':
339 return self._HandleCommandGitLog(args)
Simon Glassdfb7e932014-09-05 19:00:20 -0600340 elif sub_cmd == 'clone':
341 return command.CommandResult(return_code=0)
342 elif sub_cmd == 'checkout':
343 return command.CommandResult(return_code=0)
Alper Nebi Yasak76de29f2020-09-03 15:51:03 +0300344 elif sub_cmd == 'worktree':
345 return command.CommandResult(return_code=0)
Simon Glassd4144e42014-09-05 19:00:13 -0600346
347 # Not handled, so abort
Simon Glassc05aa032019-10-31 07:42:53 -0600348 print('git', git_args, sub_cmd, args)
Simon Glassd4144e42014-09-05 19:00:13 -0600349 sys.exit(1)
350
351 def _HandleCommandNm(self, args):
352 return command.CommandResult(return_code=0)
353
354 def _HandleCommandObjdump(self, args):
355 return command.CommandResult(return_code=0)
356
Alex Kiernan0ddc5102018-05-31 04:48:33 +0000357 def _HandleCommandObjcopy(self, args):
358 return command.CommandResult(return_code=0)
359
Simon Glassd4144e42014-09-05 19:00:13 -0600360 def _HandleCommandSize(self, args):
361 return command.CommandResult(return_code=0)
362
363 def _HandleCommand(self, **kwargs):
364 """Handle a command execution.
365
366 The command is in kwargs['pipe-list'], as a list of pipes, each a
367 list of commands. The command should be emulated as required for
368 testing purposes.
369
370 Returns:
371 A CommandResult object
372 """
373 pipe_list = kwargs['pipe_list']
Simon Glassdfb7e932014-09-05 19:00:20 -0600374 wc = False
Simon Glassd4144e42014-09-05 19:00:13 -0600375 if len(pipe_list) != 1:
Simon Glassdfb7e932014-09-05 19:00:20 -0600376 if pipe_list[1] == ['wc', '-l']:
377 wc = True
378 else:
Simon Glassc05aa032019-10-31 07:42:53 -0600379 print('invalid pipe', kwargs)
Simon Glassdfb7e932014-09-05 19:00:20 -0600380 sys.exit(1)
Simon Glassd4144e42014-09-05 19:00:13 -0600381 cmd = pipe_list[0][0]
382 args = pipe_list[0][1:]
Simon Glassdfb7e932014-09-05 19:00:20 -0600383 result = None
Simon Glassd4144e42014-09-05 19:00:13 -0600384 if cmd == 'git':
Simon Glassdfb7e932014-09-05 19:00:20 -0600385 result = self._HandleCommandGit(args)
Simon Glassd4144e42014-09-05 19:00:13 -0600386 elif cmd == './scripts/show-gnu-make':
387 return command.CommandResult(return_code=0, stdout='make')
Simon Glassdfb7e932014-09-05 19:00:20 -0600388 elif cmd.endswith('nm'):
Simon Glassd4144e42014-09-05 19:00:13 -0600389 return self._HandleCommandNm(args)
Simon Glassdfb7e932014-09-05 19:00:20 -0600390 elif cmd.endswith('objdump'):
Simon Glassd4144e42014-09-05 19:00:13 -0600391 return self._HandleCommandObjdump(args)
Alex Kiernan0ddc5102018-05-31 04:48:33 +0000392 elif cmd.endswith('objcopy'):
393 return self._HandleCommandObjcopy(args)
Simon Glassdfb7e932014-09-05 19:00:20 -0600394 elif cmd.endswith( 'size'):
Simon Glassd4144e42014-09-05 19:00:13 -0600395 return self._HandleCommandSize(args)
396
Simon Glassdfb7e932014-09-05 19:00:20 -0600397 if not result:
398 # Not handled, so abort
Simon Glassc05aa032019-10-31 07:42:53 -0600399 print('unknown command', kwargs)
Simon Glassdfb7e932014-09-05 19:00:20 -0600400 sys.exit(1)
401
402 if wc:
403 result.stdout = len(result.stdout.splitlines())
404 return result
Simon Glassd4144e42014-09-05 19:00:13 -0600405
406 def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
407 """Handle execution of 'make'
408
409 Args:
410 commit: Commit object that is being built
411 brd: Board object that is being built
412 stage: Stage that we are at (mrproper, config, build)
413 cwd: Directory where make should be run
414 args: Arguments to pass to make
Simon Glassd9800692022-01-29 14:14:05 -0700415 kwargs: Arguments to pass to command.run_pipe()
Simon Glassd4144e42014-09-05 19:00:13 -0600416 """
Simon Glassdfb7e932014-09-05 19:00:20 -0600417 self._make_calls += 1
Simon Glassd4144e42014-09-05 19:00:13 -0600418 if stage == 'mrproper':
419 return command.CommandResult(return_code=0)
420 elif stage == 'config':
421 return command.CommandResult(return_code=0,
422 combined='Test configuration complete')
423 elif stage == 'build':
Simon Glassdfb7e932014-09-05 19:00:20 -0600424 stderr = ''
Simon Glassd829f122020-03-18 09:42:42 -0600425 out_dir = ''
426 for arg in args:
427 if arg.startswith('O='):
428 out_dir = arg[2:]
429 fname = os.path.join(cwd or '', out_dir, 'u-boot')
Simon Glassc1aa66e2022-01-29 14:14:04 -0700430 tools.write_file(fname, b'U-Boot')
Tom Rinid7713ad2022-11-09 19:14:53 -0700431
432 # Handle missing blobs
433 if self._missing:
434 if 'BINMAN_ALLOW_MISSING=1' in args:
435 stderr = '''+Image 'main-section' is missing external blobs and is non-functional: intel-descriptor intel-ifwi intel-fsp-m intel-fsp-s intel-vbt
436Image 'main-section' has faked external blobs and is non-functional: descriptor.bin fsp_m.bin fsp_s.bin vbt.bin
437
438Some images are invalid'''
439 else:
440 stderr = "binman: Filename 'fsp.bin' not found in input path"
441 elif type(commit) is not str:
Simon Glassdfb7e932014-09-05 19:00:20 -0600442 stderr = self._error.get((brd.target, commit.sequence))
Tom Rinid7713ad2022-11-09 19:14:53 -0700443
Simon Glassdfb7e932014-09-05 19:00:20 -0600444 if stderr:
Tom Rinid7713ad2022-11-09 19:14:53 -0700445 return command.CommandResult(return_code=2, stderr=stderr)
Simon Glassd4144e42014-09-05 19:00:13 -0600446 return command.CommandResult(return_code=0)
447
448 # Not handled, so abort
Simon Glassc05aa032019-10-31 07:42:53 -0600449 print('make', stage)
Simon Glassd4144e42014-09-05 19:00:13 -0600450 sys.exit(1)
451
Simon Glassdfb7e932014-09-05 19:00:20 -0600452 # Example function to print output lines
453 def print_lines(self, lines):
Simon Glassc05aa032019-10-31 07:42:53 -0600454 print(len(lines))
Simon Glassdfb7e932014-09-05 19:00:20 -0600455 for line in lines:
Simon Glassc05aa032019-10-31 07:42:53 -0600456 print(line)
Simon Glass098b10f2022-01-29 14:14:18 -0700457 #self.print_lines(terminal.get_print_test_lines())
Simon Glassdfb7e932014-09-05 19:00:20 -0600458
Simon Glass823e60b2014-09-05 19:00:16 -0600459 def testNoBoards(self):
460 """Test that buildman aborts when there are no boards"""
Simon Glassc52bd222022-07-11 19:04:03 -0600461 self._boards = boards.Boards()
Simon Glass823e60b2014-09-05 19:00:16 -0600462 with self.assertRaises(SystemExit):
463 self._RunControl()
464
Simon Glassd4144e42014-09-05 19:00:13 -0600465 def testCurrentSource(self):
466 """Very simple test to invoke buildman on the current source"""
Simon Glassdfb7e932014-09-05 19:00:20 -0600467 self.setupToolchains();
Tom Riniaae62582019-10-07 17:17:36 -0400468 self._RunControl('-o', self._output_dir)
Simon Glass098b10f2022-01-29 14:14:18 -0700469 lines = terminal.get_print_test_lines()
Simon Glass938fa372022-07-11 19:03:58 -0600470 self.assertIn('Building current source for %d boards' % len(BOARDS),
Simon Glassdfb7e932014-09-05 19:00:20 -0600471 lines[0].text)
472
473 def testBadBranch(self):
474 """Test that we can detect an invalid branch"""
475 with self.assertRaises(ValueError):
476 self._RunControl('-b', 'badbranch')
477
478 def testBadToolchain(self):
479 """Test that missing toolchains are detected"""
480 self.setupToolchains();
Tom Riniaae62582019-10-07 17:17:36 -0400481 ret_code = self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass098b10f2022-01-29 14:14:18 -0700482 lines = terminal.get_print_test_lines()
Simon Glassdfb7e932014-09-05 19:00:20 -0600483
484 # Buildman always builds the upstream commit as well
485 self.assertIn('Building %d commits for %d boards' %
Simon Glass938fa372022-07-11 19:03:58 -0600486 (self._commits, len(BOARDS)), lines[0].text)
Simon Glassdfb7e932014-09-05 19:00:20 -0600487 self.assertEqual(self._builder.count, self._total_builds)
488
489 # Only sandbox should succeed, the others don't have toolchains
490 self.assertEqual(self._builder.fail,
491 self._total_builds - self._commits)
Simon Glassb1e5e6d2020-04-09 10:49:45 -0600492 self.assertEqual(ret_code, 100)
Simon Glassdfb7e932014-09-05 19:00:20 -0600493
494 for commit in range(self._commits):
Simon Glass6014db62022-07-11 19:04:02 -0600495 for brd in self._boards.get_list():
Simon Glassf4ed4702022-07-11 19:03:57 -0600496 if brd.arch != 'sandbox':
497 errfile = self._builder.GetErrFile(commit, brd.target)
Simon Glassdfb7e932014-09-05 19:00:20 -0600498 fd = open(errfile)
499 self.assertEqual(fd.readlines(),
Simon Glassf4ed4702022-07-11 19:03:57 -0600500 ['No tool chain for %s\n' % brd.arch])
Simon Glassdfb7e932014-09-05 19:00:20 -0600501 fd.close()
502
503 def testBranch(self):
504 """Test building a branch with all toolchains present"""
Tom Riniaae62582019-10-07 17:17:36 -0400505 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glassdfb7e932014-09-05 19:00:20 -0600506 self.assertEqual(self._builder.count, self._total_builds)
507 self.assertEqual(self._builder.fail, 0)
508
509 def testCount(self):
510 """Test building a specific number of commitst"""
Tom Riniaae62582019-10-07 17:17:36 -0400511 self._RunControl('-b', TEST_BRANCH, '-c2', '-o', self._output_dir)
Simon Glass938fa372022-07-11 19:03:58 -0600512 self.assertEqual(self._builder.count, 2 * len(BOARDS))
Simon Glassdfb7e932014-09-05 19:00:20 -0600513 self.assertEqual(self._builder.fail, 0)
Simon Glasseb70a2c2020-04-09 15:08:51 -0600514 # Each board has a config, and then one make per commit
Simon Glass938fa372022-07-11 19:03:58 -0600515 self.assertEqual(self._make_calls, len(BOARDS) * (1 + 2))
Simon Glassdfb7e932014-09-05 19:00:20 -0600516
517 def testIncremental(self):
518 """Test building a branch twice - the second time should do nothing"""
Tom Riniaae62582019-10-07 17:17:36 -0400519 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glassdfb7e932014-09-05 19:00:20 -0600520
521 # Each board has a mrproper, config, and then one make per commit
Simon Glass938fa372022-07-11 19:03:58 -0600522 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
Simon Glassdfb7e932014-09-05 19:00:20 -0600523 self._make_calls = 0
Tom Riniaae62582019-10-07 17:17:36 -0400524 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
Simon Glassdfb7e932014-09-05 19:00:20 -0600525 self.assertEqual(self._make_calls, 0)
526 self.assertEqual(self._builder.count, self._total_builds)
527 self.assertEqual(self._builder.fail, 0)
528
529 def testForceBuild(self):
530 """The -f flag should force a rebuild"""
Tom Riniaae62582019-10-07 17:17:36 -0400531 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glassdfb7e932014-09-05 19:00:20 -0600532 self._make_calls = 0
Tom Riniaae62582019-10-07 17:17:36 -0400533 self._RunControl('-b', TEST_BRANCH, '-f', '-o', self._output_dir, clean_dir=False)
Simon Glasseb70a2c2020-04-09 15:08:51 -0600534 # Each board has a config and one make per commit
Simon Glass938fa372022-07-11 19:03:58 -0600535 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
Simon Glassdfb7e932014-09-05 19:00:20 -0600536
537 def testForceReconfigure(self):
538 """The -f flag should force a rebuild"""
Tom Riniaae62582019-10-07 17:17:36 -0400539 self._RunControl('-b', TEST_BRANCH, '-C', '-o', self._output_dir)
Simon Glasseb70a2c2020-04-09 15:08:51 -0600540 # Each commit has a config and make
Simon Glass938fa372022-07-11 19:03:58 -0600541 self.assertEqual(self._make_calls, len(BOARDS) * self._commits * 2)
Simon Glasseb70a2c2020-04-09 15:08:51 -0600542
Simon Glasseb70a2c2020-04-09 15:08:51 -0600543 def testMrproper(self):
544 """The -f flag should force a rebuild"""
545 self._RunControl('-b', TEST_BRANCH, '-m', '-o', self._output_dir)
546 # Each board has a mkproper, config and then one make per commit
Simon Glass938fa372022-07-11 19:03:58 -0600547 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 2))
Simon Glassdfb7e932014-09-05 19:00:20 -0600548
549 def testErrors(self):
550 """Test handling of build errors"""
551 self._error['board2', 1] = 'fred\n'
Tom Riniaae62582019-10-07 17:17:36 -0400552 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glassdfb7e932014-09-05 19:00:20 -0600553 self.assertEqual(self._builder.count, self._total_builds)
554 self.assertEqual(self._builder.fail, 1)
555
556 # Remove the error. This should have no effect since the commit will
557 # not be rebuilt
558 del self._error['board2', 1]
559 self._make_calls = 0
Tom Riniaae62582019-10-07 17:17:36 -0400560 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
Simon Glassdfb7e932014-09-05 19:00:20 -0600561 self.assertEqual(self._builder.count, self._total_builds)
562 self.assertEqual(self._make_calls, 0)
563 self.assertEqual(self._builder.fail, 1)
564
565 # Now use the -F flag to force rebuild of the bad commit
Tom Riniaae62582019-10-07 17:17:36 -0400566 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, '-F', clean_dir=False)
Simon Glassdfb7e932014-09-05 19:00:20 -0600567 self.assertEqual(self._builder.count, self._total_builds)
568 self.assertEqual(self._builder.fail, 0)
Simon Glasseb70a2c2020-04-09 15:08:51 -0600569 self.assertEqual(self._make_calls, 2)
Simon Glassf7582ce2014-09-05 19:00:22 -0600570
571 def testBranchWithSlash(self):
572 """Test building a branch with a '/' in the name"""
573 self._test_branch = '/__dev/__testbranch'
574 self._RunControl('-b', self._test_branch, clean_dir=False)
575 self.assertEqual(self._builder.count, self._total_builds)
576 self.assertEqual(self._builder.fail, 0)
Lothar Waßmann409fc022018-04-08 05:14:11 -0600577
Simon Glass166a98a2020-04-17 17:51:33 -0600578 def testEnvironment(self):
579 """Test that the done and environment files are written to out-env"""
580 self._RunControl('-o', self._output_dir)
581 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
582 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
583 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
584
Simon Glassf1a83ab2021-04-11 16:27:28 +1200585 def testEnvironmentUnicode(self):
586 """Test there are no unicode errors when the env has non-ASCII chars"""
587 try:
588 varname = b'buildman_test_var'
589 os.environb[varname] = b'strange\x80chars'
590 self.assertEqual(0, self._RunControl('-o', self._output_dir))
591 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
592 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
593 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
594 finally:
595 del os.environb[varname]
596
Simon Glassd829f122020-03-18 09:42:42 -0600597 def testWorkInOutput(self):
598 """Test the -w option which should write directly to the output dir"""
Simon Glassc52bd222022-07-11 19:04:03 -0600599 board_list = boards.Boards()
Simon Glass6014db62022-07-11 19:04:02 -0600600 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassd829f122020-03-18 09:42:42 -0600601 self._RunControl('-o', self._output_dir, '-w', clean_dir=False,
Simon Glass938fa372022-07-11 19:03:58 -0600602 brds=board_list)
Simon Glassd829f122020-03-18 09:42:42 -0600603 self.assertTrue(
604 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
Simon Glass60b285f2020-04-17 17:51:34 -0600605 self.assertTrue(
606 os.path.exists(os.path.join(self._output_dir, 'done')))
607 self.assertTrue(
608 os.path.exists(os.path.join(self._output_dir, 'out-env')))
Simon Glassd829f122020-03-18 09:42:42 -0600609
610 def testWorkInOutputFail(self):
611 """Test the -w option failures"""
612 with self.assertRaises(SystemExit) as e:
613 self._RunControl('-o', self._output_dir, '-w', clean_dir=False)
614 self.assertIn("single board", str(e.exception))
615 self.assertFalse(
616 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
617
Simon Glassc52bd222022-07-11 19:04:03 -0600618 board_list = boards.Boards()
Simon Glass6014db62022-07-11 19:04:02 -0600619 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassd829f122020-03-18 09:42:42 -0600620 with self.assertRaises(SystemExit) as e:
621 self._RunControl('-b', self._test_branch, '-o', self._output_dir,
Simon Glass938fa372022-07-11 19:03:58 -0600622 '-w', clean_dir=False, brds=board_list)
Simon Glassd829f122020-03-18 09:42:42 -0600623 self.assertIn("single commit", str(e.exception))
Simon Glass88daaef2020-04-17 17:51:32 -0600624
Simon Glassc52bd222022-07-11 19:04:03 -0600625 board_list = boards.Boards()
Simon Glass6014db62022-07-11 19:04:02 -0600626 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glass88daaef2020-04-17 17:51:32 -0600627 with self.assertRaises(SystemExit) as e:
628 self._RunControl('-w', clean_dir=False)
629 self.assertIn("specify -o", str(e.exception))
Simon Glass8116c782021-04-11 16:27:27 +1200630
631 def testThreadExceptions(self):
632 """Test that exceptions in threads are reported"""
633 with test_util.capture_sys_output() as (stdout, stderr):
634 self.assertEqual(102, self._RunControl('-o', self._output_dir,
635 test_thread_exceptions=True))
Simon Glass8ca09312022-01-22 05:07:32 -0700636 self.assertIn(
637 'Thread exception (use -T0 to run without threads): test exception',
638 stdout.getvalue())
Tom Rinid7713ad2022-11-09 19:14:53 -0700639
640 def testBlobs(self):
641 """Test handling of missing blobs"""
642 self._missing = True
643
644 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
645 errfile = os.path.join(board0_dir, 'err')
646 logfile = os.path.join(board0_dir, 'log')
647
648 # We expect failure when there are missing blobs
649 result = self._RunControl('board0', '-o', self._output_dir)
650 self.assertEqual(100, result)
651 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
652 self.assertTrue(os.path.exists(errfile))
653 self.assertIn(b"Filename 'fsp.bin' not found in input path",
654 tools.read_file(errfile))
655
656 def testBlobsAllowMissing(self):
657 """Allow missing blobs - still failure but a different exit code"""
658 self._missing = True
659 result = self._RunControl('board0', '-o', self._output_dir, '-M',
660 clean_dir=True)
661 self.assertEqual(101, result)
662 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
663 errfile = os.path.join(board0_dir, 'err')
664 self.assertTrue(os.path.exists(errfile))
665 self.assertIn(b'Some images are invalid', tools.read_file(errfile))
666
667 def testBlobsWarning(self):
668 """Allow missing blobs and ignore warnings"""
669 self._missing = True
670 result = self._RunControl('board0', '-o', self._output_dir, '-MW')
671 self.assertEqual(0, result)
672 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
673 errfile = os.path.join(board0_dir, 'err')
674 self.assertIn(b'Some images are invalid', tools.read_file(errfile))
675
676 def testBlobSettings(self):
677 """Test with no settings"""
678 self.assertEqual(False,
679 control.get_allow_missing(False, False, 1, False))
680 self.assertEqual(True,
681 control.get_allow_missing(True, False, 1, False))
682 self.assertEqual(False,
683 control.get_allow_missing(True, True, 1, False))
684
685 def testBlobSettingsAlways(self):
686 """Test the 'always' policy"""
687 bsettings.SetItem('global', 'allow-missing', 'always')
688 self.assertEqual(True,
689 control.get_allow_missing(False, False, 1, False))
690 self.assertEqual(False,
691 control.get_allow_missing(False, True, 1, False))
692
693 def testBlobSettingsBranch(self):
694 """Test the 'branch' policy"""
695 bsettings.SetItem('global', 'allow-missing', 'branch')
696 self.assertEqual(False,
697 control.get_allow_missing(False, False, 1, False))
698 self.assertEqual(True,
699 control.get_allow_missing(False, False, 1, True))
700 self.assertEqual(False,
701 control.get_allow_missing(False, True, 1, True))
702
703 def testBlobSettingsMultiple(self):
704 """Test the 'multiple' policy"""
705 bsettings.SetItem('global', 'allow-missing', 'multiple')
706 self.assertEqual(False,
707 control.get_allow_missing(False, False, 1, False))
708 self.assertEqual(True,
709 control.get_allow_missing(False, False, 2, False))
710 self.assertEqual(False,
711 control.get_allow_missing(False, True, 2, False))
712
713 def testBlobSettingsBranchMultiple(self):
714 """Test the 'branch multiple' policy"""
715 bsettings.SetItem('global', 'allow-missing', 'branch multiple')
716 self.assertEqual(False,
717 control.get_allow_missing(False, False, 1, False))
718 self.assertEqual(True,
719 control.get_allow_missing(False, False, 1, True))
720 self.assertEqual(True,
721 control.get_allow_missing(False, False, 2, False))
722 self.assertEqual(True,
723 control.get_allow_missing(False, False, 2, True))
724 self.assertEqual(False,
725 control.get_allow_missing(False, True, 2, True))
Simon Glasscd37d5b2023-02-21 12:40:27 -0700726
Simon Glass93202d72023-02-21 12:40:28 -0700727 def check_command(self, *extra_args):
728 """Run a command with the extra arguments and return the commands used
729
730 Args:
731 extra_args (list of str): List of extra arguments
732
733 Returns:
734 list of str: Lines returned in the out-cmd file
735 """
736 self._RunControl('-o', self._output_dir, *extra_args)
Simon Glasscd37d5b2023-02-21 12:40:27 -0700737 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
738 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
739 cmd_fname = os.path.join(board0_dir, 'out-cmd')
740 self.assertTrue(os.path.exists(cmd_fname))
741 data = tools.read_file(cmd_fname)
Simon Glass93202d72023-02-21 12:40:28 -0700742 return data.splitlines()
743
744 def testCmdFile(self):
745 """Test that the -cmd-out file is produced"""
746 lines = self.check_command()
Simon Glasscd37d5b2023-02-21 12:40:27 -0700747 self.assertEqual(2, len(lines))
748 self.assertRegex(lines[0], b'make O=/.*board0_defconfig')
749 self.assertRegex(lines[0], b'make O=/.*-s.*')
Simon Glass93202d72023-02-21 12:40:28 -0700750
751 def testNoLto(self):
752 """Test that the --no-lto flag works"""
753 lines = self.check_command('-L')
754 self.assertIn(b'NO_LTO=1', lines[0])
755