2 # Copyright (c) 2014 Google, Inc
4 # SPDX-License-Identifier: GPL-2.0+
23 # Buildman settings file
31 chroot=/home/sjg/c/chroot
32 vboot=USE_STDINT=1 VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
33 chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
34 chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
35 chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
39 ['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', ''],
42 ['Active', 'powerpc', 'mpc5xx', '', 'Tester', 'PowerPC board 2', 'board3', ''],
43 ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
46 commit_shortlog = """4aca821 patman: Avoid changing the order of tags
47 39403bb patman: Use --no-pager' to stop git from forking a pager
48 db6e6f2 patman: Remove the -a option
49 f2ccf03 patman: Correct unit tests to run correctly
50 1d097f9 patman: Fix indentation in terminal.py
51 d073747 patman: Support the 'reverse' option for 'git log
54 commit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd
55 Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
56 Date: Fri Aug 22 19:12:41 2014 +0900
58 buildman: refactor help message
60 "buildman [options]" is displayed by default.
62 Append the rest of help messages to parser.usage
63 instead of replacing it.
65 Besides, "-b <branch>" is not mandatory since commit fea5858e.
66 Drop it from the usage.
68 Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
70 """commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8
71 Author: Simon Glass <sjg@chromium.org>
72 Date: Thu Aug 14 16:48:25 2014 -0600
74 patman: Support the 'reverse' option for 'git log'
76 This option is currently not supported, but needs to be, for buildman to
80 - Add new patch to fix the 'reverse' bug
83 Change-Id: I79078f792e8b390b8a1272a8023537821d45feda
84 Reported-by: York Sun <yorksun@freescale.com>
85 Signed-off-by: Simon Glass <sjg@chromium.org>
88 """commit 1d097f9ab487c5019152fd47bda126839f3bf9fc
89 Author: Simon Glass <sjg@chromium.org>
90 Date: Sat Aug 9 11:44:32 2014 -0600
92 patman: Fix indentation in terminal.py
94 This code came from a different project with 2-character indentation. Fix
98 - Add new patch to fix indentation in teminal.py
100 Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34
101 Signed-off-by: Simon Glass <sjg@chromium.org>
104 """commit f2ccf03869d1e152c836515a3ceb83cdfe04a105
105 Author: Simon Glass <sjg@chromium.org>
106 Date: Sat Aug 9 11:08:24 2014 -0600
108 patman: Correct unit tests to run correctly
110 It seems that doctest behaves differently now, and some of the unit tests
111 do not run. Adjust the tests to work correctly.
113 ./tools/patman/patman --test
114 <unittest.result.TestResult run=10 errors=0 failures=0>
117 - Add new patch to fix patman unit tests
119 Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b
122 """commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c
123 Author: Simon Glass <sjg@chromium.org>
124 Date: Sat Aug 9 12:06:02 2014 -0600
126 patman: Remove the -a option
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.
133 - Add new patch to remove patman's -a option
135 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
136 Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc
139 """commit 39403bb4f838153028a6f21ca30bf100f3791133
140 Author: Simon Glass <sjg@chromium.org>
141 Date: Thu Aug 14 21:50:52 2014 -0600
143 patman: Use --no-pager' to stop git from forking a pager
146 """commit 4aca821e27e97925c039e69fd37375b09c6f129c
147 Author: Simon Glass <sjg@chromium.org>
148 Date: Fri Aug 22 15:57:39 2014 -0600
150 patman: Avoid changing the order of tags
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.
157 - Add new patch to avoid changing the order of tags
159 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
160 Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db
163 TEST_BRANCH = '__testbranch'
165 class TestFunctional(unittest.TestCase):
166 """Functional test for buildman.
168 This aims to test from just below the invocation of buildman (parsing
169 of arguments) to 'make' and 'git' invocation. It is not a true
170 emd-to-end test, as it mocks git, make and the tool chain. But this
171 makes it easier to detect when the builder is doing the wrong thing,
172 since in many cases this test code will fail. For example, only a
173 very limited subset of 'git' arguments is supported - anything
174 unexpected will fail.
177 self._base_dir = tempfile.mkdtemp()
178 self._git_dir = os.path.join(self._base_dir, 'src')
179 self._buildman_pathname = sys.argv[0]
180 self._buildman_dir = os.path.dirname(sys.argv[0])
181 command.test_result = self._HandleCommand
182 self.setupToolchains()
183 self._toolchains.Add('arm-gcc', test=False)
184 self._toolchains.Add('powerpc-gcc', test=False)
185 bsettings.Setup(None)
186 bsettings.AddFile(settings_data)
187 self._boards = board.Boards()
189 self._boards.AddBoard(board.Board(*brd))
191 # Directories where the source been cloned
192 self._clone_dirs = []
193 self._commits = len(commit_shortlog.splitlines()) + 1
194 self._total_builds = self._commits * len(boards)
196 # Number of calls to make
199 # Map of [board, commit] to error messages
202 self._test_branch = TEST_BRANCH
204 # Avoid sending any output and clear all terminal output
205 terminal.SetPrintTestMode()
206 terminal.GetPrintTestLines()
209 shutil.rmtree(self._base_dir)
211 def setupToolchains(self):
212 self._toolchains = toolchain.Toolchains()
213 self._toolchains.Add('gcc', test=False)
215 def _RunBuildman(self, *args):
216 return command.RunPipe([[self._buildman_pathname] + list(args)],
217 capture=True, capture_stderr=True)
219 def _RunControl(self, *args, **kwargs):
220 sys.argv = [sys.argv[0]] + list(args)
221 options, args = cmdline.ParseArgs()
222 result = control.DoBuildman(options, args, toolchains=self._toolchains,
223 make_func=self._HandleMake, boards=self._boards,
224 clean_dir=kwargs.get('clean_dir', True))
225 self._builder = control.builder
228 def testFullHelp(self):
229 command.test_result = None
230 result = self._RunBuildman('-H')
231 help_file = os.path.join(self._buildman_dir, 'README')
232 self.assertEqual(len(result.stdout), os.path.getsize(help_file))
233 self.assertEqual(0, len(result.stderr))
234 self.assertEqual(0, result.return_code)
237 command.test_result = None
238 result = self._RunBuildman('-h')
239 help_file = os.path.join(self._buildman_dir, 'README')
240 self.assertTrue(len(result.stdout) > 1000)
241 self.assertEqual(0, len(result.stderr))
242 self.assertEqual(0, result.return_code)
244 def testGitSetup(self):
245 """Test gitutils.Setup(), from outside the module itself"""
246 command.test_result = command.CommandResult(return_code=1)
248 self.assertEqual(gitutil.use_no_decorate, False)
250 command.test_result = command.CommandResult(return_code=0)
252 self.assertEqual(gitutil.use_no_decorate, True)
254 def _HandleCommandGitLog(self, args):
256 return command.CommandResult(return_code=0)
257 elif args[-1] == 'upstream/master..%s' % self._test_branch:
258 return command.CommandResult(return_code=0, stdout=commit_shortlog)
259 elif args[:3] == ['--no-color', '--no-decorate', '--reverse']:
260 if args[-1] == self._test_branch:
261 count = int(args[3][2:])
262 return command.CommandResult(return_code=0,
263 stdout=''.join(commit_log[:count]))
265 # Not handled, so abort
266 print 'git log', args
269 def _HandleCommandGitConfig(self, args):
271 if config == 'sendemail.aliasesfile':
272 return command.CommandResult(return_code=0)
273 elif config.startswith('branch.badbranch'):
274 return command.CommandResult(return_code=1)
275 elif config == 'branch.%s.remote' % self._test_branch:
276 return command.CommandResult(return_code=0, stdout='upstream\n')
277 elif config == 'branch.%s.merge' % self._test_branch:
278 return command.CommandResult(return_code=0,
279 stdout='refs/heads/master\n')
281 # Not handled, so abort
282 print 'git config', args
285 def _HandleCommandGit(self, in_args):
286 """Handle execution of a git command
288 This uses a hacked-up parser.
291 in_args: Arguments after 'git' from the command line
293 git_args = [] # Top-level arguments to git itself
294 sub_cmd = None # Git sub-command selected
295 args = [] # Arguments to the git sub-command
302 if git_args and git_args[-1] in ['--git-dir', '--work-tree']:
306 if sub_cmd == 'config':
307 return self._HandleCommandGitConfig(args)
308 elif sub_cmd == 'log':
309 return self._HandleCommandGitLog(args)
310 elif sub_cmd == 'clone':
311 return command.CommandResult(return_code=0)
312 elif sub_cmd == 'checkout':
313 return command.CommandResult(return_code=0)
315 # Not handled, so abort
316 print 'git', git_args, sub_cmd, args
319 def _HandleCommandNm(self, args):
320 return command.CommandResult(return_code=0)
322 def _HandleCommandObjdump(self, args):
323 return command.CommandResult(return_code=0)
325 def _HandleCommandSize(self, args):
326 return command.CommandResult(return_code=0)
328 def _HandleCommand(self, **kwargs):
329 """Handle a command execution.
331 The command is in kwargs['pipe-list'], as a list of pipes, each a
332 list of commands. The command should be emulated as required for
336 A CommandResult object
338 pipe_list = kwargs['pipe_list']
340 if len(pipe_list) != 1:
341 if pipe_list[1] == ['wc', '-l']:
344 print 'invalid pipe', kwargs
346 cmd = pipe_list[0][0]
347 args = pipe_list[0][1:]
350 result = self._HandleCommandGit(args)
351 elif cmd == './scripts/show-gnu-make':
352 return command.CommandResult(return_code=0, stdout='make')
353 elif cmd.endswith('nm'):
354 return self._HandleCommandNm(args)
355 elif cmd.endswith('objdump'):
356 return self._HandleCommandObjdump(args)
357 elif cmd.endswith( 'size'):
358 return self._HandleCommandSize(args)
361 # Not handled, so abort
362 print 'unknown command', kwargs
366 result.stdout = len(result.stdout.splitlines())
369 def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
370 """Handle execution of 'make'
373 commit: Commit object that is being built
374 brd: Board object that is being built
375 stage: Stage that we are at (mrproper, config, build)
376 cwd: Directory where make should be run
377 args: Arguments to pass to make
378 kwargs: Arguments to pass to command.RunPipe()
380 self._make_calls += 1
381 if stage == 'mrproper':
382 return command.CommandResult(return_code=0)
383 elif stage == 'config':
384 return command.CommandResult(return_code=0,
385 combined='Test configuration complete')
386 elif stage == 'build':
388 if type(commit) is not str:
389 stderr = self._error.get((brd.target, commit.sequence))
391 return command.CommandResult(return_code=1, stderr=stderr)
392 return command.CommandResult(return_code=0)
394 # Not handled, so abort
398 # Example function to print output lines
399 def print_lines(self, lines):
403 #self.print_lines(terminal.GetPrintTestLines())
405 def testNoBoards(self):
406 """Test that buildman aborts when there are no boards"""
407 self._boards = board.Boards()
408 with self.assertRaises(SystemExit):
411 def testCurrentSource(self):
412 """Very simple test to invoke buildman on the current source"""
413 self.setupToolchains();
415 lines = terminal.GetPrintTestLines()
416 self.assertIn('Building current source for %d boards' % len(boards),
419 def testBadBranch(self):
420 """Test that we can detect an invalid branch"""
421 with self.assertRaises(ValueError):
422 self._RunControl('-b', 'badbranch')
424 def testBadToolchain(self):
425 """Test that missing toolchains are detected"""
426 self.setupToolchains();
427 ret_code = self._RunControl('-b', TEST_BRANCH)
428 lines = terminal.GetPrintTestLines()
430 # Buildman always builds the upstream commit as well
431 self.assertIn('Building %d commits for %d boards' %
432 (self._commits, len(boards)), lines[0].text)
433 self.assertEqual(self._builder.count, self._total_builds)
435 # Only sandbox should succeed, the others don't have toolchains
436 self.assertEqual(self._builder.fail,
437 self._total_builds - self._commits)
438 self.assertEqual(ret_code, 128)
440 for commit in range(self._commits):
441 for board in self._boards.GetList():
442 if board.arch != 'sandbox':
443 errfile = self._builder.GetErrFile(commit, board.target)
445 self.assertEqual(fd.readlines(),
446 ['No tool chain for %s\n' % board.arch])
449 def testBranch(self):
450 """Test building a branch with all toolchains present"""
451 self._RunControl('-b', TEST_BRANCH)
452 self.assertEqual(self._builder.count, self._total_builds)
453 self.assertEqual(self._builder.fail, 0)
456 """Test building a specific number of commitst"""
457 self._RunControl('-b', TEST_BRANCH, '-c2')
458 self.assertEqual(self._builder.count, 2 * len(boards))
459 self.assertEqual(self._builder.fail, 0)
460 # Each board has a mrproper, config, and then one make per commit
461 self.assertEqual(self._make_calls, len(boards) * (2 + 2))
463 def testIncremental(self):
464 """Test building a branch twice - the second time should do nothing"""
465 self._RunControl('-b', TEST_BRANCH)
467 # Each board has a mrproper, config, and then one make per commit
468 self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
470 self._RunControl('-b', TEST_BRANCH, clean_dir=False)
471 self.assertEqual(self._make_calls, 0)
472 self.assertEqual(self._builder.count, self._total_builds)
473 self.assertEqual(self._builder.fail, 0)
475 def testForceBuild(self):
476 """The -f flag should force a rebuild"""
477 self._RunControl('-b', TEST_BRANCH)
479 self._RunControl('-b', TEST_BRANCH, '-f', clean_dir=False)
480 # Each board has a mrproper, config, and then one make per commit
481 self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
483 def testForceReconfigure(self):
484 """The -f flag should force a rebuild"""
485 self._RunControl('-b', TEST_BRANCH, '-C')
486 # Each commit has a mrproper, config and make
487 self.assertEqual(self._make_calls, len(boards) * self._commits * 3)
489 def testErrors(self):
490 """Test handling of build errors"""
491 self._error['board2', 1] = 'fred\n'
492 self._RunControl('-b', TEST_BRANCH)
493 self.assertEqual(self._builder.count, self._total_builds)
494 self.assertEqual(self._builder.fail, 1)
496 # Remove the error. This should have no effect since the commit will
498 del self._error['board2', 1]
500 self._RunControl('-b', TEST_BRANCH, clean_dir=False)
501 self.assertEqual(self._builder.count, self._total_builds)
502 self.assertEqual(self._make_calls, 0)
503 self.assertEqual(self._builder.fail, 1)
505 # Now use the -F flag to force rebuild of the bad commit
506 self._RunControl('-b', TEST_BRANCH, '-F', clean_dir=False)
507 self.assertEqual(self._builder.count, self._total_builds)
508 self.assertEqual(self._builder.fail, 0)
509 self.assertEqual(self._make_calls, 3)
511 def testBranchWithSlash(self):
512 """Test building a branch with a '/' in the name"""
513 self._test_branch = '/__dev/__testbranch'
514 self._RunControl('-b', self._test_branch, clean_dir=False)
515 self.assertEqual(self._builder.count, self._total_builds)
516 self.assertEqual(self._builder.fail, 0)