blob: f12e99663496c4a2ad30f453e15fc835a41f4176 [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
Simon Glass20751d62022-07-11 19:04:03 -060012from buildman import boards
Simon Glassf0d9c102020-04-17 18:09:02 -060013from buildman import bsettings
14from buildman import cmdline
15from buildman import control
16from buildman import toolchain
Simon Glassa997ea52020-04-17 18:09:04 -060017from patman import command
18from patman import gitutil
19from patman import terminal
Simon Glass9bf9a722021-04-11 16:27:27 +120020from patman import test_util
Simon Glassa997ea52020-04-17 18:09:04 -060021from patman import tools
Simon Glassed098bb2014-09-05 19:00:13 -060022
Simon Glass5e0441d2014-09-05 19:00:15 -060023settings_data = '''
24# Buildman settings file
25
26[toolchain]
27
28[toolchain-alias]
29
30[make-flags]
31src=/home/sjg/c/src
32chroot=/home/sjg/c/chroot
Masahiro Yamada72e545a2018-08-06 20:47:38 +090033vboot=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 -060034chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
35chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
36chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
37'''
38
Simon Glassd4c6c8a2022-07-11 19:03:58 -060039BOARDS = [
Simon Glasscbd36582014-09-05 19:00:16 -060040 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''],
41 ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
42 ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
Simon Glasscbd36582014-09-05 19:00:16 -060043 ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
44]
45
Simon Glass8e959562014-09-05 19:00:20 -060046commit_shortlog = """4aca821 patman: Avoid changing the order of tags
4739403bb patman: Use --no-pager' to stop git from forking a pager
48db6e6f2 patman: Remove the -a option
49f2ccf03 patman: Correct unit tests to run correctly
501d097f9 patman: Fix indentation in terminal.py
51d073747 patman: Support the 'reverse' option for 'git log
52"""
53
54commit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd
55Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
56Date: Fri Aug 22 19:12:41 2014 +0900
57
58 buildman: refactor help message
59
60 "buildman [options]" is displayed by default.
61
62 Append the rest of help messages to parser.usage
63 instead of replacing it.
64
65 Besides, "-b <branch>" is not mandatory since commit fea5858e.
66 Drop it from the usage.
67
68 Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
69""",
70"""commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8
71Author: Simon Glass <sjg@chromium.org>
72Date: Thu Aug 14 16:48:25 2014 -0600
73
74 patman: Support the 'reverse' option for 'git log'
75
76 This option is currently not supported, but needs to be, for buildman to
77 operate as expected.
78
79 Series-changes: 7
80 - Add new patch to fix the 'reverse' bug
81
Simon Glass359b55a62014-09-05 19:00:23 -060082 Series-version: 8
Simon Glass8e959562014-09-05 19:00:20 -060083
84 Change-Id: I79078f792e8b390b8a1272a8023537821d45feda
85 Reported-by: York Sun <yorksun@freescale.com>
86 Signed-off-by: Simon Glass <sjg@chromium.org>
87
88""",
89"""commit 1d097f9ab487c5019152fd47bda126839f3bf9fc
90Author: Simon Glass <sjg@chromium.org>
91Date: Sat Aug 9 11:44:32 2014 -0600
92
93 patman: Fix indentation in terminal.py
94
95 This code came from a different project with 2-character indentation. Fix
96 it for U-Boot.
97
98 Series-changes: 6
99 - Add new patch to fix indentation in teminal.py
100
101 Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34
102 Signed-off-by: Simon Glass <sjg@chromium.org>
103
104""",
105"""commit f2ccf03869d1e152c836515a3ceb83cdfe04a105
106Author: Simon Glass <sjg@chromium.org>
107Date: Sat Aug 9 11:08:24 2014 -0600
108
109 patman: Correct unit tests to run correctly
110
111 It seems that doctest behaves differently now, and some of the unit tests
112 do not run. Adjust the tests to work correctly.
113
114 ./tools/patman/patman --test
115 <unittest.result.TestResult run=10 errors=0 failures=0>
116
117 Series-changes: 6
118 - Add new patch to fix patman unit tests
119
120 Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b
121
122""",
123"""commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c
124Author: Simon Glass <sjg@chromium.org>
125Date: Sat Aug 9 12:06:02 2014 -0600
126
127 patman: Remove the -a option
128
129 It seems that this is no longer needed, since checkpatch.pl will catch
130 whitespace problems in patches. Also the option is not widely used, so
131 it seems safe to just remove it.
132
133 Series-changes: 6
134 - Add new patch to remove patman's -a option
135
136 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
137 Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc
138
139""",
140"""commit 39403bb4f838153028a6f21ca30bf100f3791133
141Author: Simon Glass <sjg@chromium.org>
142Date: Thu Aug 14 21:50:52 2014 -0600
143
144 patman: Use --no-pager' to stop git from forking a pager
145
146""",
147"""commit 4aca821e27e97925c039e69fd37375b09c6f129c
148Author: Simon Glass <sjg@chromium.org>
149Date: Fri Aug 22 15:57:39 2014 -0600
150
151 patman: Avoid changing the order of tags
152
153 patman collects tags that it sees in the commit and places them nicely
154 sorted at the end of the patch. However, this is not really necessary and
155 in fact is apparently not desirable.
156
157 Series-changes: 9
158 - Add new patch to avoid changing the order of tags
159
Simon Glass359b55a62014-09-05 19:00:23 -0600160 Series-version: 9
161
Simon Glass8e959562014-09-05 19:00:20 -0600162 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
163 Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db
164"""]
165
166TEST_BRANCH = '__testbranch'
167
Simon Glassed098bb2014-09-05 19:00:13 -0600168class TestFunctional(unittest.TestCase):
169 """Functional test for buildman.
170
171 This aims to test from just below the invocation of buildman (parsing
172 of arguments) to 'make' and 'git' invocation. It is not a true
173 emd-to-end test, as it mocks git, make and the tool chain. But this
174 makes it easier to detect when the builder is doing the wrong thing,
175 since in many cases this test code will fail. For example, only a
176 very limited subset of 'git' arguments is supported - anything
177 unexpected will fail.
178 """
179 def setUp(self):
180 self._base_dir = tempfile.mkdtemp()
Tom Rinie95eddc2019-10-07 17:17:36 -0400181 self._output_dir = tempfile.mkdtemp()
Simon Glassed098bb2014-09-05 19:00:13 -0600182 self._git_dir = os.path.join(self._base_dir, 'src')
183 self._buildman_pathname = sys.argv[0]
Simon Glass5d4a7872016-07-27 20:33:00 -0600184 self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
Simon Glassed098bb2014-09-05 19:00:13 -0600185 command.test_result = self._HandleCommand
Simon Glass22901f92022-01-22 05:07:31 -0700186 bsettings.Setup(None)
187 bsettings.AddFile(settings_data)
Simon Glass8e959562014-09-05 19:00:20 -0600188 self.setupToolchains()
189 self._toolchains.Add('arm-gcc', test=False)
190 self._toolchains.Add('powerpc-gcc', test=False)
Simon Glass20751d62022-07-11 19:04:03 -0600191 self._boards = boards.Boards()
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600192 for brd in BOARDS:
Simon Glass127a2392022-07-11 19:04:02 -0600193 self._boards.add_board(board.Board(*brd))
Simon Glassed098bb2014-09-05 19:00:13 -0600194
Simon Glass8e959562014-09-05 19:00:20 -0600195 # Directories where the source been cloned
196 self._clone_dirs = []
197 self._commits = len(commit_shortlog.splitlines()) + 1
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600198 self._total_builds = self._commits * len(BOARDS)
Simon Glass8e959562014-09-05 19:00:20 -0600199
200 # Number of calls to make
201 self._make_calls = 0
202
203 # Map of [board, commit] to error messages
204 self._error = {}
205
Simon Glass4aeceb92014-09-05 19:00:22 -0600206 self._test_branch = TEST_BRANCH
207
Simon Glass8e959562014-09-05 19:00:20 -0600208 # Avoid sending any output and clear all terminal output
Simon Glass02811582022-01-29 14:14:18 -0700209 terminal.set_print_test_mode()
210 terminal.get_print_test_lines()
Simon Glass8e959562014-09-05 19:00:20 -0600211
Simon Glassed098bb2014-09-05 19:00:13 -0600212 def tearDown(self):
213 shutil.rmtree(self._base_dir)
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600214 #shutil.rmtree(self._output_dir)
Simon Glassed098bb2014-09-05 19:00:13 -0600215
Simon Glass8e959562014-09-05 19:00:20 -0600216 def setupToolchains(self):
217 self._toolchains = toolchain.Toolchains()
218 self._toolchains.Add('gcc', test=False)
219
Simon Glassed098bb2014-09-05 19:00:13 -0600220 def _RunBuildman(self, *args):
Simon Glass840be732022-01-29 14:14:05 -0700221 return command.run_pipe([[self._buildman_pathname] + list(args)],
Simon Glassed098bb2014-09-05 19:00:13 -0600222 capture=True, capture_stderr=True)
223
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600224 def _RunControl(self, *args, brds=None, clean_dir=False,
Simon Glass9bf9a722021-04-11 16:27:27 +1200225 test_thread_exceptions=False):
Simon Glassa29b3ea2021-04-11 16:27:25 +1200226 """Run buildman
227
228 Args:
229 args: List of arguments to pass
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600230 brds: Boards object
Simon Glassa29b3ea2021-04-11 16:27:25 +1200231 clean_dir: Used for tests only, indicates that the existing output_dir
232 should be removed before starting the build
Simon Glass9bf9a722021-04-11 16:27:27 +1200233 test_thread_exceptions: Uses for tests only, True to make the threads
234 raise an exception instead of reporting their result. This simulates
235 a failure in the code somewhere
Simon Glassa29b3ea2021-04-11 16:27:25 +1200236
237 Returns:
238 result code from buildman
239 """
Simon Glassed098bb2014-09-05 19:00:13 -0600240 sys.argv = [sys.argv[0]] + list(args)
241 options, args = cmdline.ParseArgs()
Simon Glass8e959562014-09-05 19:00:20 -0600242 result = control.DoBuildman(options, args, toolchains=self._toolchains,
Simon Glass5df45222022-07-11 19:04:00 -0600243 make_func=self._HandleMake, brds=brds or self._boards,
Simon Glass9bf9a722021-04-11 16:27:27 +1200244 clean_dir=clean_dir,
245 test_thread_exceptions=test_thread_exceptions)
Simon Glass8e959562014-09-05 19:00:20 -0600246 self._builder = control.builder
247 return result
Simon Glassed098bb2014-09-05 19:00:13 -0600248
249 def testFullHelp(self):
250 command.test_result = None
251 result = self._RunBuildman('-H')
252 help_file = os.path.join(self._buildman_dir, 'README')
Tom Rinic3c0b6d2018-01-16 15:29:50 -0500253 # Remove possible extraneous strings
254 extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
255 gothelp = result.stdout.replace(extra, '')
256 self.assertEqual(len(gothelp), os.path.getsize(help_file))
Simon Glassed098bb2014-09-05 19:00:13 -0600257 self.assertEqual(0, len(result.stderr))
258 self.assertEqual(0, result.return_code)
259
260 def testHelp(self):
261 command.test_result = None
262 result = self._RunBuildman('-h')
263 help_file = os.path.join(self._buildman_dir, 'README')
264 self.assertTrue(len(result.stdout) > 1000)
265 self.assertEqual(0, len(result.stderr))
266 self.assertEqual(0, result.return_code)
267
268 def testGitSetup(self):
269 """Test gitutils.Setup(), from outside the module itself"""
270 command.test_result = command.CommandResult(return_code=1)
Simon Glass761648b2022-01-29 14:14:11 -0700271 gitutil.setup()
Simon Glassed098bb2014-09-05 19:00:13 -0600272 self.assertEqual(gitutil.use_no_decorate, False)
273
274 command.test_result = command.CommandResult(return_code=0)
Simon Glass761648b2022-01-29 14:14:11 -0700275 gitutil.setup()
Simon Glassed098bb2014-09-05 19:00:13 -0600276 self.assertEqual(gitutil.use_no_decorate, True)
277
278 def _HandleCommandGitLog(self, args):
Simon Glass642e9a62016-03-12 18:50:31 -0700279 if args[-1] == '--':
280 args = args[:-1]
Simon Glassed098bb2014-09-05 19:00:13 -0600281 if '-n0' in args:
282 return command.CommandResult(return_code=0)
Simon Glass4aeceb92014-09-05 19:00:22 -0600283 elif args[-1] == 'upstream/master..%s' % self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600284 return command.CommandResult(return_code=0, stdout=commit_shortlog)
285 elif args[:3] == ['--no-color', '--no-decorate', '--reverse']:
Simon Glass4aeceb92014-09-05 19:00:22 -0600286 if args[-1] == self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600287 count = int(args[3][2:])
288 return command.CommandResult(return_code=0,
289 stdout=''.join(commit_log[:count]))
Simon Glassed098bb2014-09-05 19:00:13 -0600290
291 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600292 print('git log', args)
Simon Glassed098bb2014-09-05 19:00:13 -0600293 sys.exit(1)
294
Simon Glass8e959562014-09-05 19:00:20 -0600295 def _HandleCommandGitConfig(self, args):
296 config = args[0]
297 if config == 'sendemail.aliasesfile':
298 return command.CommandResult(return_code=0)
299 elif config.startswith('branch.badbranch'):
300 return command.CommandResult(return_code=1)
Simon Glass4aeceb92014-09-05 19:00:22 -0600301 elif config == 'branch.%s.remote' % self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600302 return command.CommandResult(return_code=0, stdout='upstream\n')
Simon Glass4aeceb92014-09-05 19:00:22 -0600303 elif config == 'branch.%s.merge' % self._test_branch:
Simon Glass8e959562014-09-05 19:00:20 -0600304 return command.CommandResult(return_code=0,
305 stdout='refs/heads/master\n')
306
307 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600308 print('git config', args)
Simon Glass8e959562014-09-05 19:00:20 -0600309 sys.exit(1)
310
Simon Glassed098bb2014-09-05 19:00:13 -0600311 def _HandleCommandGit(self, in_args):
312 """Handle execution of a git command
313
314 This uses a hacked-up parser.
315
316 Args:
317 in_args: Arguments after 'git' from the command line
318 """
319 git_args = [] # Top-level arguments to git itself
320 sub_cmd = None # Git sub-command selected
321 args = [] # Arguments to the git sub-command
322 for arg in in_args:
323 if sub_cmd:
324 args.append(arg)
325 elif arg[0] == '-':
326 git_args.append(arg)
327 else:
Simon Glass8e959562014-09-05 19:00:20 -0600328 if git_args and git_args[-1] in ['--git-dir', '--work-tree']:
329 git_args.append(arg)
330 else:
331 sub_cmd = arg
Simon Glassed098bb2014-09-05 19:00:13 -0600332 if sub_cmd == 'config':
Simon Glass8e959562014-09-05 19:00:20 -0600333 return self._HandleCommandGitConfig(args)
Simon Glassed098bb2014-09-05 19:00:13 -0600334 elif sub_cmd == 'log':
335 return self._HandleCommandGitLog(args)
Simon Glass8e959562014-09-05 19:00:20 -0600336 elif sub_cmd == 'clone':
337 return command.CommandResult(return_code=0)
338 elif sub_cmd == 'checkout':
339 return command.CommandResult(return_code=0)
Alper Nebi Yasakfede44a2020-09-03 15:51:03 +0300340 elif sub_cmd == 'worktree':
341 return command.CommandResult(return_code=0)
Simon Glassed098bb2014-09-05 19:00:13 -0600342
343 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600344 print('git', git_args, sub_cmd, args)
Simon Glassed098bb2014-09-05 19:00:13 -0600345 sys.exit(1)
346
347 def _HandleCommandNm(self, args):
348 return command.CommandResult(return_code=0)
349
350 def _HandleCommandObjdump(self, args):
351 return command.CommandResult(return_code=0)
352
Alex Kiernanf07ed232018-05-31 04:48:33 +0000353 def _HandleCommandObjcopy(self, args):
354 return command.CommandResult(return_code=0)
355
Simon Glassed098bb2014-09-05 19:00:13 -0600356 def _HandleCommandSize(self, args):
357 return command.CommandResult(return_code=0)
358
359 def _HandleCommand(self, **kwargs):
360 """Handle a command execution.
361
362 The command is in kwargs['pipe-list'], as a list of pipes, each a
363 list of commands. The command should be emulated as required for
364 testing purposes.
365
366 Returns:
367 A CommandResult object
368 """
369 pipe_list = kwargs['pipe_list']
Simon Glass8e959562014-09-05 19:00:20 -0600370 wc = False
Simon Glassed098bb2014-09-05 19:00:13 -0600371 if len(pipe_list) != 1:
Simon Glass8e959562014-09-05 19:00:20 -0600372 if pipe_list[1] == ['wc', '-l']:
373 wc = True
374 else:
Simon Glassc78ed662019-10-31 07:42:53 -0600375 print('invalid pipe', kwargs)
Simon Glass8e959562014-09-05 19:00:20 -0600376 sys.exit(1)
Simon Glassed098bb2014-09-05 19:00:13 -0600377 cmd = pipe_list[0][0]
378 args = pipe_list[0][1:]
Simon Glass8e959562014-09-05 19:00:20 -0600379 result = None
Simon Glassed098bb2014-09-05 19:00:13 -0600380 if cmd == 'git':
Simon Glass8e959562014-09-05 19:00:20 -0600381 result = self._HandleCommandGit(args)
Simon Glassed098bb2014-09-05 19:00:13 -0600382 elif cmd == './scripts/show-gnu-make':
383 return command.CommandResult(return_code=0, stdout='make')
Simon Glass8e959562014-09-05 19:00:20 -0600384 elif cmd.endswith('nm'):
Simon Glassed098bb2014-09-05 19:00:13 -0600385 return self._HandleCommandNm(args)
Simon Glass8e959562014-09-05 19:00:20 -0600386 elif cmd.endswith('objdump'):
Simon Glassed098bb2014-09-05 19:00:13 -0600387 return self._HandleCommandObjdump(args)
Alex Kiernanf07ed232018-05-31 04:48:33 +0000388 elif cmd.endswith('objcopy'):
389 return self._HandleCommandObjcopy(args)
Simon Glass8e959562014-09-05 19:00:20 -0600390 elif cmd.endswith( 'size'):
Simon Glassed098bb2014-09-05 19:00:13 -0600391 return self._HandleCommandSize(args)
392
Simon Glass8e959562014-09-05 19:00:20 -0600393 if not result:
394 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600395 print('unknown command', kwargs)
Simon Glass8e959562014-09-05 19:00:20 -0600396 sys.exit(1)
397
398 if wc:
399 result.stdout = len(result.stdout.splitlines())
400 return result
Simon Glassed098bb2014-09-05 19:00:13 -0600401
402 def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
403 """Handle execution of 'make'
404
405 Args:
406 commit: Commit object that is being built
407 brd: Board object that is being built
408 stage: Stage that we are at (mrproper, config, build)
409 cwd: Directory where make should be run
410 args: Arguments to pass to make
Simon Glass840be732022-01-29 14:14:05 -0700411 kwargs: Arguments to pass to command.run_pipe()
Simon Glassed098bb2014-09-05 19:00:13 -0600412 """
Simon Glass8e959562014-09-05 19:00:20 -0600413 self._make_calls += 1
Simon Glassed098bb2014-09-05 19:00:13 -0600414 if stage == 'mrproper':
415 return command.CommandResult(return_code=0)
416 elif stage == 'config':
417 return command.CommandResult(return_code=0,
418 combined='Test configuration complete')
419 elif stage == 'build':
Simon Glass8e959562014-09-05 19:00:20 -0600420 stderr = ''
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600421 out_dir = ''
422 for arg in args:
423 if arg.startswith('O='):
424 out_dir = arg[2:]
425 fname = os.path.join(cwd or '', out_dir, 'u-boot')
Simon Glass80025522022-01-29 14:14:04 -0700426 tools.write_file(fname, b'U-Boot')
Simon Glass8e959562014-09-05 19:00:20 -0600427 if type(commit) is not str:
428 stderr = self._error.get((brd.target, commit.sequence))
429 if stderr:
430 return command.CommandResult(return_code=1, stderr=stderr)
Simon Glassed098bb2014-09-05 19:00:13 -0600431 return command.CommandResult(return_code=0)
432
433 # Not handled, so abort
Simon Glassc78ed662019-10-31 07:42:53 -0600434 print('make', stage)
Simon Glassed098bb2014-09-05 19:00:13 -0600435 sys.exit(1)
436
Simon Glass8e959562014-09-05 19:00:20 -0600437 # Example function to print output lines
438 def print_lines(self, lines):
Simon Glassc78ed662019-10-31 07:42:53 -0600439 print(len(lines))
Simon Glass8e959562014-09-05 19:00:20 -0600440 for line in lines:
Simon Glassc78ed662019-10-31 07:42:53 -0600441 print(line)
Simon Glass02811582022-01-29 14:14:18 -0700442 #self.print_lines(terminal.get_print_test_lines())
Simon Glass8e959562014-09-05 19:00:20 -0600443
Simon Glasscbd36582014-09-05 19:00:16 -0600444 def testNoBoards(self):
445 """Test that buildman aborts when there are no boards"""
Simon Glass20751d62022-07-11 19:04:03 -0600446 self._boards = boards.Boards()
Simon Glasscbd36582014-09-05 19:00:16 -0600447 with self.assertRaises(SystemExit):
448 self._RunControl()
449
Simon Glassed098bb2014-09-05 19:00:13 -0600450 def testCurrentSource(self):
451 """Very simple test to invoke buildman on the current source"""
Simon Glass8e959562014-09-05 19:00:20 -0600452 self.setupToolchains();
Tom Rinie95eddc2019-10-07 17:17:36 -0400453 self._RunControl('-o', self._output_dir)
Simon Glass02811582022-01-29 14:14:18 -0700454 lines = terminal.get_print_test_lines()
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600455 self.assertIn('Building current source for %d boards' % len(BOARDS),
Simon Glass8e959562014-09-05 19:00:20 -0600456 lines[0].text)
457
458 def testBadBranch(self):
459 """Test that we can detect an invalid branch"""
460 with self.assertRaises(ValueError):
461 self._RunControl('-b', 'badbranch')
462
463 def testBadToolchain(self):
464 """Test that missing toolchains are detected"""
465 self.setupToolchains();
Tom Rinie95eddc2019-10-07 17:17:36 -0400466 ret_code = self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass02811582022-01-29 14:14:18 -0700467 lines = terminal.get_print_test_lines()
Simon Glass8e959562014-09-05 19:00:20 -0600468
469 # Buildman always builds the upstream commit as well
470 self.assertIn('Building %d commits for %d boards' %
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600471 (self._commits, len(BOARDS)), lines[0].text)
Simon Glass8e959562014-09-05 19:00:20 -0600472 self.assertEqual(self._builder.count, self._total_builds)
473
474 # Only sandbox should succeed, the others don't have toolchains
475 self.assertEqual(self._builder.fail,
476 self._total_builds - self._commits)
Simon Glasse4cd5062020-04-09 10:49:45 -0600477 self.assertEqual(ret_code, 100)
Simon Glass8e959562014-09-05 19:00:20 -0600478
479 for commit in range(self._commits):
Simon Glass127a2392022-07-11 19:04:02 -0600480 for brd in self._boards.get_list():
Simon Glass8132f982022-07-11 19:03:57 -0600481 if brd.arch != 'sandbox':
482 errfile = self._builder.GetErrFile(commit, brd.target)
Simon Glass8e959562014-09-05 19:00:20 -0600483 fd = open(errfile)
484 self.assertEqual(fd.readlines(),
Simon Glass8132f982022-07-11 19:03:57 -0600485 ['No tool chain for %s\n' % brd.arch])
Simon Glass8e959562014-09-05 19:00:20 -0600486 fd.close()
487
488 def testBranch(self):
489 """Test building a branch with all toolchains present"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400490 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600491 self.assertEqual(self._builder.count, self._total_builds)
492 self.assertEqual(self._builder.fail, 0)
493
494 def testCount(self):
495 """Test building a specific number of commitst"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400496 self._RunControl('-b', TEST_BRANCH, '-c2', '-o', self._output_dir)
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600497 self.assertEqual(self._builder.count, 2 * len(BOARDS))
Simon Glass8e959562014-09-05 19:00:20 -0600498 self.assertEqual(self._builder.fail, 0)
Simon Glass6029af12020-04-09 15:08:51 -0600499 # Each board has a config, and then one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600500 self.assertEqual(self._make_calls, len(BOARDS) * (1 + 2))
Simon Glass8e959562014-09-05 19:00:20 -0600501
502 def testIncremental(self):
503 """Test building a branch twice - the second time should do nothing"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400504 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600505
506 # Each board has a mrproper, config, and then one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600507 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
Simon Glass8e959562014-09-05 19:00:20 -0600508 self._make_calls = 0
Tom Rinie95eddc2019-10-07 17:17:36 -0400509 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
Simon Glass8e959562014-09-05 19:00:20 -0600510 self.assertEqual(self._make_calls, 0)
511 self.assertEqual(self._builder.count, self._total_builds)
512 self.assertEqual(self._builder.fail, 0)
513
514 def testForceBuild(self):
515 """The -f flag should force a rebuild"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400516 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600517 self._make_calls = 0
Tom Rinie95eddc2019-10-07 17:17:36 -0400518 self._RunControl('-b', TEST_BRANCH, '-f', '-o', self._output_dir, clean_dir=False)
Simon Glass6029af12020-04-09 15:08:51 -0600519 # Each board has a config and one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600520 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
Simon Glass8e959562014-09-05 19:00:20 -0600521
522 def testForceReconfigure(self):
523 """The -f flag should force a rebuild"""
Tom Rinie95eddc2019-10-07 17:17:36 -0400524 self._RunControl('-b', TEST_BRANCH, '-C', '-o', self._output_dir)
Simon Glass6029af12020-04-09 15:08:51 -0600525 # Each commit has a config and make
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600526 self.assertEqual(self._make_calls, len(BOARDS) * self._commits * 2)
Simon Glass6029af12020-04-09 15:08:51 -0600527
Simon Glass6029af12020-04-09 15:08:51 -0600528 def testMrproper(self):
529 """The -f flag should force a rebuild"""
530 self._RunControl('-b', TEST_BRANCH, '-m', '-o', self._output_dir)
531 # Each board has a mkproper, config and then one make per commit
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600532 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 2))
Simon Glass8e959562014-09-05 19:00:20 -0600533
534 def testErrors(self):
535 """Test handling of build errors"""
536 self._error['board2', 1] = 'fred\n'
Tom Rinie95eddc2019-10-07 17:17:36 -0400537 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
Simon Glass8e959562014-09-05 19:00:20 -0600538 self.assertEqual(self._builder.count, self._total_builds)
539 self.assertEqual(self._builder.fail, 1)
540
541 # Remove the error. This should have no effect since the commit will
542 # not be rebuilt
543 del self._error['board2', 1]
544 self._make_calls = 0
Tom Rinie95eddc2019-10-07 17:17:36 -0400545 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
Simon Glass8e959562014-09-05 19:00:20 -0600546 self.assertEqual(self._builder.count, self._total_builds)
547 self.assertEqual(self._make_calls, 0)
548 self.assertEqual(self._builder.fail, 1)
549
550 # Now use the -F flag to force rebuild of the bad commit
Tom Rinie95eddc2019-10-07 17:17:36 -0400551 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, '-F', clean_dir=False)
Simon Glass8e959562014-09-05 19:00:20 -0600552 self.assertEqual(self._builder.count, self._total_builds)
553 self.assertEqual(self._builder.fail, 0)
Simon Glass6029af12020-04-09 15:08:51 -0600554 self.assertEqual(self._make_calls, 2)
Simon Glass4aeceb92014-09-05 19:00:22 -0600555
556 def testBranchWithSlash(self):
557 """Test building a branch with a '/' in the name"""
558 self._test_branch = '/__dev/__testbranch'
559 self._RunControl('-b', self._test_branch, clean_dir=False)
560 self.assertEqual(self._builder.count, self._total_builds)
561 self.assertEqual(self._builder.fail, 0)
Lothar Waßmannce6df922018-04-08 05:14:11 -0600562
Simon Glassff48a212020-04-17 17:51:33 -0600563 def testEnvironment(self):
564 """Test that the done and environment files are written to out-env"""
565 self._RunControl('-o', self._output_dir)
566 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
567 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
568 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
569
Simon Glass93008e22021-04-11 16:27:28 +1200570 def testEnvironmentUnicode(self):
571 """Test there are no unicode errors when the env has non-ASCII chars"""
572 try:
573 varname = b'buildman_test_var'
574 os.environb[varname] = b'strange\x80chars'
575 self.assertEqual(0, self._RunControl('-o', self._output_dir))
576 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
577 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
578 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
579 finally:
580 del os.environb[varname]
581
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600582 def testWorkInOutput(self):
583 """Test the -w option which should write directly to the output dir"""
Simon Glass20751d62022-07-11 19:04:03 -0600584 board_list = boards.Boards()
Simon Glass127a2392022-07-11 19:04:02 -0600585 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600586 self._RunControl('-o', self._output_dir, '-w', clean_dir=False,
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600587 brds=board_list)
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600588 self.assertTrue(
589 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
Simon Glasse3c85ab2020-04-17 17:51:34 -0600590 self.assertTrue(
591 os.path.exists(os.path.join(self._output_dir, 'done')))
592 self.assertTrue(
593 os.path.exists(os.path.join(self._output_dir, 'out-env')))
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600594
595 def testWorkInOutputFail(self):
596 """Test the -w option failures"""
597 with self.assertRaises(SystemExit) as e:
598 self._RunControl('-o', self._output_dir, '-w', clean_dir=False)
599 self.assertIn("single board", str(e.exception))
600 self.assertFalse(
601 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
602
Simon Glass20751d62022-07-11 19:04:03 -0600603 board_list = boards.Boards()
Simon Glass127a2392022-07-11 19:04:02 -0600604 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600605 with self.assertRaises(SystemExit) as e:
606 self._RunControl('-b', self._test_branch, '-o', self._output_dir,
Simon Glassd4c6c8a2022-07-11 19:03:58 -0600607 '-w', clean_dir=False, brds=board_list)
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600608 self.assertIn("single commit", str(e.exception))
Simon Glassd9c98632020-04-17 17:51:32 -0600609
Simon Glass20751d62022-07-11 19:04:03 -0600610 board_list = boards.Boards()
Simon Glass127a2392022-07-11 19:04:02 -0600611 board_list.add_board(board.Board(*BOARDS[0]))
Simon Glassd9c98632020-04-17 17:51:32 -0600612 with self.assertRaises(SystemExit) as e:
613 self._RunControl('-w', clean_dir=False)
614 self.assertIn("specify -o", str(e.exception))
Simon Glass9bf9a722021-04-11 16:27:27 +1200615
616 def testThreadExceptions(self):
617 """Test that exceptions in threads are reported"""
618 with test_util.capture_sys_output() as (stdout, stderr):
619 self.assertEqual(102, self._RunControl('-o', self._output_dir,
620 test_thread_exceptions=True))
Simon Glass9bac1672022-01-22 05:07:32 -0700621 self.assertIn(
622 'Thread exception (use -T0 to run without threads): test exception',
623 stdout.getvalue())