1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2014 Google, Inc
11 from buildman import board
12 from buildman import boards
13 from buildman import bsettings
14 from buildman import cmdline
15 from buildman import control
16 from buildman import toolchain
17 from patman import command
18 from patman import gitutil
19 from patman import terminal
20 from patman import test_util
21 from patman import tools
24 # Buildman settings file
33 chroot=/home/sjg/c/chroot
34 vboot=VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
35 chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
36 chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
37 chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
41 ['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', ''],
44 ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
47 commit_shortlog = """4aca821 patman: Avoid changing the order of tags
48 39403bb patman: Use --no-pager' to stop git from forking a pager
49 db6e6f2 patman: Remove the -a option
50 f2ccf03 patman: Correct unit tests to run correctly
51 1d097f9 patman: Fix indentation in terminal.py
52 d073747 patman: Support the 'reverse' option for 'git log
55 commit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd
56 Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
57 Date: Fri Aug 22 19:12:41 2014 +0900
59 buildman: refactor help message
61 "buildman [options]" is displayed by default.
63 Append the rest of help messages to parser.usage
64 instead of replacing it.
66 Besides, "-b <branch>" is not mandatory since commit fea5858e.
67 Drop it from the usage.
69 Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
71 """commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8
72 Author: Simon Glass <sjg@chromium.org>
73 Date: Thu Aug 14 16:48:25 2014 -0600
75 patman: Support the 'reverse' option for 'git log'
77 This option is currently not supported, but needs to be, for buildman to
81 - Add new patch to fix the 'reverse' bug
85 Change-Id: I79078f792e8b390b8a1272a8023537821d45feda
86 Reported-by: York Sun <yorksun@freescale.com>
87 Signed-off-by: Simon Glass <sjg@chromium.org>
90 """commit 1d097f9ab487c5019152fd47bda126839f3bf9fc
91 Author: Simon Glass <sjg@chromium.org>
92 Date: Sat Aug 9 11:44:32 2014 -0600
94 patman: Fix indentation in terminal.py
96 This code came from a different project with 2-character indentation. Fix
100 - Add new patch to fix indentation in teminal.py
102 Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34
103 Signed-off-by: Simon Glass <sjg@chromium.org>
106 """commit f2ccf03869d1e152c836515a3ceb83cdfe04a105
107 Author: Simon Glass <sjg@chromium.org>
108 Date: Sat Aug 9 11:08:24 2014 -0600
110 patman: Correct unit tests to run correctly
112 It seems that doctest behaves differently now, and some of the unit tests
113 do not run. Adjust the tests to work correctly.
115 ./tools/patman/patman --test
116 <unittest.result.TestResult run=10 errors=0 failures=0>
119 - Add new patch to fix patman unit tests
121 Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b
124 """commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c
125 Author: Simon Glass <sjg@chromium.org>
126 Date: Sat Aug 9 12:06:02 2014 -0600
128 patman: Remove the -a option
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.
135 - Add new patch to remove patman's -a option
137 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
138 Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc
141 """commit 39403bb4f838153028a6f21ca30bf100f3791133
142 Author: Simon Glass <sjg@chromium.org>
143 Date: Thu Aug 14 21:50:52 2014 -0600
145 patman: Use --no-pager' to stop git from forking a pager
148 """commit 4aca821e27e97925c039e69fd37375b09c6f129c
149 Author: Simon Glass <sjg@chromium.org>
150 Date: Fri Aug 22 15:57:39 2014 -0600
152 patman: Avoid changing the order of tags
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.
159 - Add new patch to avoid changing the order of tags
163 Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
164 Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db
167 TEST_BRANCH = '__testbranch'
169 class TestFunctional(unittest.TestCase):
170 """Functional test for buildman.
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.
181 self._base_dir = tempfile.mkdtemp()
182 self._output_dir = tempfile.mkdtemp()
183 self._git_dir = os.path.join(self._base_dir, 'src')
184 self._buildman_pathname = sys.argv[0]
185 self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
186 command.test_result = self._HandleCommand
187 bsettings.Setup(None)
188 bsettings.AddFile(settings_data)
189 self.setupToolchains()
190 self._toolchains.Add('arm-gcc', test=False)
191 self._toolchains.Add('powerpc-gcc', test=False)
192 self._boards = boards.Boards()
194 self._boards.add_board(board.Board(*brd))
196 # Directories where the source been cloned
197 self._clone_dirs = []
198 self._commits = len(commit_shortlog.splitlines()) + 1
199 self._total_builds = self._commits * len(BOARDS)
201 # Number of calls to make
204 # Map of [board, commit] to error messages
207 self._test_branch = TEST_BRANCH
209 # Set to True to report missing blobs
210 self._missing = False
212 # Avoid sending any output and clear all terminal output
213 terminal.set_print_test_mode()
214 terminal.get_print_test_lines()
217 shutil.rmtree(self._base_dir)
218 shutil.rmtree(self._output_dir)
220 def setupToolchains(self):
221 self._toolchains = toolchain.Toolchains()
222 self._toolchains.Add('gcc', test=False)
224 def _RunBuildman(self, *args):
225 return command.run_pipe([[self._buildman_pathname] + list(args)],
226 capture=True, capture_stderr=True)
228 def _RunControl(self, *args, brds=None, clean_dir=False,
229 test_thread_exceptions=False):
233 args: List of arguments to pass
235 clean_dir: Used for tests only, indicates that the existing output_dir
236 should be removed before starting the build
237 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
242 result code from buildman
244 sys.argv = [sys.argv[0]] + list(args)
245 options, args = cmdline.ParseArgs()
246 result = control.DoBuildman(options, args, toolchains=self._toolchains,
247 make_func=self._HandleMake, brds=brds or self._boards,
249 test_thread_exceptions=test_thread_exceptions)
250 self._builder = control.builder
253 def testFullHelp(self):
254 command.test_result = None
255 result = self._RunBuildman('-H')
256 help_file = os.path.join(self._buildman_dir, 'README.rst')
257 # 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))
261 self.assertEqual(0, len(result.stderr))
262 self.assertEqual(0, result.return_code)
265 command.test_result = None
266 result = self._RunBuildman('-h')
267 help_file = os.path.join(self._buildman_dir, 'README.rst')
268 self.assertTrue(len(result.stdout) > 1000)
269 self.assertEqual(0, len(result.stderr))
270 self.assertEqual(0, result.return_code)
272 def testGitSetup(self):
273 """Test gitutils.Setup(), from outside the module itself"""
274 command.test_result = command.CommandResult(return_code=1)
276 self.assertEqual(gitutil.use_no_decorate, False)
278 command.test_result = command.CommandResult(return_code=0)
280 self.assertEqual(gitutil.use_no_decorate, True)
282 def _HandleCommandGitLog(self, args):
286 return command.CommandResult(return_code=0)
287 elif args[-1] == 'upstream/master..%s' % self._test_branch:
288 return command.CommandResult(return_code=0, stdout=commit_shortlog)
289 elif args[:3] == ['--no-color', '--no-decorate', '--reverse']:
290 if args[-1] == self._test_branch:
291 count = int(args[3][2:])
292 return command.CommandResult(return_code=0,
293 stdout=''.join(commit_log[:count]))
295 # Not handled, so abort
296 print('git log', args)
299 def _HandleCommandGitConfig(self, args):
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)
305 elif config == 'branch.%s.remote' % self._test_branch:
306 return command.CommandResult(return_code=0, stdout='upstream\n')
307 elif config == 'branch.%s.merge' % self._test_branch:
308 return command.CommandResult(return_code=0,
309 stdout='refs/heads/master\n')
311 # Not handled, so abort
312 print('git config', args)
315 def _HandleCommandGit(self, in_args):
316 """Handle execution of a git command
318 This uses a hacked-up parser.
321 in_args: Arguments after 'git' from the command line
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
332 if git_args and git_args[-1] in ['--git-dir', '--work-tree']:
336 if sub_cmd == 'config':
337 return self._HandleCommandGitConfig(args)
338 elif sub_cmd == 'log':
339 return self._HandleCommandGitLog(args)
340 elif sub_cmd == 'clone':
341 return command.CommandResult(return_code=0)
342 elif sub_cmd == 'checkout':
343 return command.CommandResult(return_code=0)
344 elif sub_cmd == 'worktree':
345 return command.CommandResult(return_code=0)
347 # Not handled, so abort
348 print('git', git_args, sub_cmd, args)
351 def _HandleCommandNm(self, args):
352 return command.CommandResult(return_code=0)
354 def _HandleCommandObjdump(self, args):
355 return command.CommandResult(return_code=0)
357 def _HandleCommandObjcopy(self, args):
358 return command.CommandResult(return_code=0)
360 def _HandleCommandSize(self, args):
361 return command.CommandResult(return_code=0)
363 def _HandleCommand(self, **kwargs):
364 """Handle a command execution.
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
371 A CommandResult object
373 pipe_list = kwargs['pipe_list']
375 if len(pipe_list) != 1:
376 if pipe_list[1] == ['wc', '-l']:
379 print('invalid pipe', kwargs)
381 cmd = pipe_list[0][0]
382 args = pipe_list[0][1:]
385 result = self._HandleCommandGit(args)
386 elif cmd == './scripts/show-gnu-make':
387 return command.CommandResult(return_code=0, stdout='make')
388 elif cmd.endswith('nm'):
389 return self._HandleCommandNm(args)
390 elif cmd.endswith('objdump'):
391 return self._HandleCommandObjdump(args)
392 elif cmd.endswith('objcopy'):
393 return self._HandleCommandObjcopy(args)
394 elif cmd.endswith( 'size'):
395 return self._HandleCommandSize(args)
398 # Not handled, so abort
399 print('unknown command', kwargs)
403 result.stdout = len(result.stdout.splitlines())
406 def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
407 """Handle execution of 'make'
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
415 kwargs: Arguments to pass to command.run_pipe()
417 self._make_calls += 1
420 if arg.startswith('O='):
422 if stage == 'mrproper':
423 return command.CommandResult(return_code=0)
424 elif stage == 'config':
425 fname = os.path.join(cwd or '', out_dir, '.config')
426 tools.write_file(fname, b'CONFIG_SOMETHING=1')
427 return command.CommandResult(return_code=0,
428 combined='Test configuration complete')
429 elif stage == 'build':
431 fname = os.path.join(cwd or '', out_dir, 'u-boot')
432 tools.write_file(fname, b'U-Boot')
434 # Handle missing blobs
436 if 'BINMAN_ALLOW_MISSING=1' in args:
437 stderr = '''+Image 'main-section' is missing external blobs and is non-functional: intel-descriptor intel-ifwi intel-fsp-m intel-fsp-s intel-vbt
438 Image 'main-section' has faked external blobs and is non-functional: descriptor.bin fsp_m.bin fsp_s.bin vbt.bin
440 Some images are invalid'''
442 stderr = "binman: Filename 'fsp.bin' not found in input path"
443 elif type(commit) is not str:
444 stderr = self._error.get((brd.target, commit.sequence))
447 return command.CommandResult(return_code=2, stderr=stderr)
448 return command.CommandResult(return_code=0)
450 # Not handled, so abort
454 # Example function to print output lines
455 def print_lines(self, lines):
459 #self.print_lines(terminal.get_print_test_lines())
461 def testNoBoards(self):
462 """Test that buildman aborts when there are no boards"""
463 self._boards = boards.Boards()
464 with self.assertRaises(SystemExit):
467 def testCurrentSource(self):
468 """Very simple test to invoke buildman on the current source"""
469 self.setupToolchains();
470 self._RunControl('-o', self._output_dir)
471 lines = terminal.get_print_test_lines()
472 self.assertIn('Building current source for %d boards' % len(BOARDS),
475 def testBadBranch(self):
476 """Test that we can detect an invalid branch"""
477 with self.assertRaises(ValueError):
478 self._RunControl('-b', 'badbranch')
480 def testBadToolchain(self):
481 """Test that missing toolchains are detected"""
482 self.setupToolchains();
483 ret_code = self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
484 lines = terminal.get_print_test_lines()
486 # Buildman always builds the upstream commit as well
487 self.assertIn('Building %d commits for %d boards' %
488 (self._commits, len(BOARDS)), lines[0].text)
489 self.assertEqual(self._builder.count, self._total_builds)
491 # Only sandbox should succeed, the others don't have toolchains
492 self.assertEqual(self._builder.fail,
493 self._total_builds - self._commits)
494 self.assertEqual(ret_code, 100)
496 for commit in range(self._commits):
497 for brd in self._boards.get_list():
498 if brd.arch != 'sandbox':
499 errfile = self._builder.GetErrFile(commit, brd.target)
501 self.assertEqual(fd.readlines(),
502 ['No tool chain for %s\n' % brd.arch])
505 def testBranch(self):
506 """Test building a branch with all toolchains present"""
507 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
508 self.assertEqual(self._builder.count, self._total_builds)
509 self.assertEqual(self._builder.fail, 0)
512 """Test building a specific number of commitst"""
513 self._RunControl('-b', TEST_BRANCH, '-c2', '-o', self._output_dir)
514 self.assertEqual(self._builder.count, 2 * len(BOARDS))
515 self.assertEqual(self._builder.fail, 0)
516 # Each board has a config, and then one make per commit
517 self.assertEqual(self._make_calls, len(BOARDS) * (1 + 2))
519 def testIncremental(self):
520 """Test building a branch twice - the second time should do nothing"""
521 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
523 # Each board has a mrproper, config, and then one make per commit
524 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
526 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
527 self.assertEqual(self._make_calls, 0)
528 self.assertEqual(self._builder.count, self._total_builds)
529 self.assertEqual(self._builder.fail, 0)
531 def testForceBuild(self):
532 """The -f flag should force a rebuild"""
533 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
535 self._RunControl('-b', TEST_BRANCH, '-f', '-o', self._output_dir, clean_dir=False)
536 # Each board has a config and one make per commit
537 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 1))
539 def testForceReconfigure(self):
540 """The -f flag should force a rebuild"""
541 self._RunControl('-b', TEST_BRANCH, '-C', '-o', self._output_dir)
542 # Each commit has a config and make
543 self.assertEqual(self._make_calls, len(BOARDS) * self._commits * 2)
545 def testMrproper(self):
546 """The -f flag should force a rebuild"""
547 self._RunControl('-b', TEST_BRANCH, '-m', '-o', self._output_dir)
548 # Each board has a mkproper, config and then one make per commit
549 self.assertEqual(self._make_calls, len(BOARDS) * (self._commits + 2))
551 def testErrors(self):
552 """Test handling of build errors"""
553 self._error['board2', 1] = 'fred\n'
554 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir)
555 self.assertEqual(self._builder.count, self._total_builds)
556 self.assertEqual(self._builder.fail, 1)
558 # Remove the error. This should have no effect since the commit will
560 del self._error['board2', 1]
562 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, clean_dir=False)
563 self.assertEqual(self._builder.count, self._total_builds)
564 self.assertEqual(self._make_calls, 0)
565 self.assertEqual(self._builder.fail, 1)
567 # Now use the -F flag to force rebuild of the bad commit
568 self._RunControl('-b', TEST_BRANCH, '-o', self._output_dir, '-F', clean_dir=False)
569 self.assertEqual(self._builder.count, self._total_builds)
570 self.assertEqual(self._builder.fail, 0)
571 self.assertEqual(self._make_calls, 2)
573 def testBranchWithSlash(self):
574 """Test building a branch with a '/' in the name"""
575 self._test_branch = '/__dev/__testbranch'
576 self._RunControl('-b', self._test_branch, clean_dir=False)
577 self.assertEqual(self._builder.count, self._total_builds)
578 self.assertEqual(self._builder.fail, 0)
580 def testEnvironment(self):
581 """Test that the done and environment files are written to out-env"""
582 self._RunControl('-o', self._output_dir)
583 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
584 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
585 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
587 def testEnvironmentUnicode(self):
588 """Test there are no unicode errors when the env has non-ASCII chars"""
590 varname = b'buildman_test_var'
591 os.environb[varname] = b'strange\x80chars'
592 self.assertEqual(0, self._RunControl('-o', self._output_dir))
593 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
594 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
595 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'out-env')))
597 del os.environb[varname]
599 def testWorkInOutput(self):
600 """Test the -w option which should write directly to the output dir"""
601 board_list = boards.Boards()
602 board_list.add_board(board.Board(*BOARDS[0]))
603 self._RunControl('-o', self._output_dir, '-w', clean_dir=False,
606 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
608 os.path.exists(os.path.join(self._output_dir, 'done')))
610 os.path.exists(os.path.join(self._output_dir, 'out-env')))
612 def testWorkInOutputFail(self):
613 """Test the -w option failures"""
614 with self.assertRaises(SystemExit) as e:
615 self._RunControl('-o', self._output_dir, '-w', clean_dir=False)
616 self.assertIn("single board", str(e.exception))
618 os.path.exists(os.path.join(self._output_dir, 'u-boot')))
620 board_list = boards.Boards()
621 board_list.add_board(board.Board(*BOARDS[0]))
622 with self.assertRaises(SystemExit) as e:
623 self._RunControl('-b', self._test_branch, '-o', self._output_dir,
624 '-w', clean_dir=False, brds=board_list)
625 self.assertIn("single commit", str(e.exception))
627 board_list = boards.Boards()
628 board_list.add_board(board.Board(*BOARDS[0]))
629 with self.assertRaises(SystemExit) as e:
630 self._RunControl('-w', clean_dir=False)
631 self.assertIn("specify -o", str(e.exception))
633 def testThreadExceptions(self):
634 """Test that exceptions in threads are reported"""
635 with test_util.capture_sys_output() as (stdout, stderr):
636 self.assertEqual(102, self._RunControl('-o', self._output_dir,
637 test_thread_exceptions=True))
639 'Thread exception (use -T0 to run without threads): test exception',
643 """Test handling of missing blobs"""
646 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
647 errfile = os.path.join(board0_dir, 'err')
648 logfile = os.path.join(board0_dir, 'log')
650 # We expect failure when there are missing blobs
651 result = self._RunControl('board0', '-o', self._output_dir)
652 self.assertEqual(100, result)
653 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
654 self.assertTrue(os.path.exists(errfile))
655 self.assertIn(b"Filename 'fsp.bin' not found in input path",
656 tools.read_file(errfile))
658 def testBlobsAllowMissing(self):
659 """Allow missing blobs - still failure but a different exit code"""
661 result = self._RunControl('board0', '-o', self._output_dir, '-M',
663 self.assertEqual(101, result)
664 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
665 errfile = os.path.join(board0_dir, 'err')
666 self.assertTrue(os.path.exists(errfile))
667 self.assertIn(b'Some images are invalid', tools.read_file(errfile))
669 def testBlobsWarning(self):
670 """Allow missing blobs and ignore warnings"""
672 result = self._RunControl('board0', '-o', self._output_dir, '-MW')
673 self.assertEqual(0, result)
674 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
675 errfile = os.path.join(board0_dir, 'err')
676 self.assertIn(b'Some images are invalid', tools.read_file(errfile))
678 def testBlobSettings(self):
679 """Test with no settings"""
680 self.assertEqual(False,
681 control.get_allow_missing(False, False, 1, False))
682 self.assertEqual(True,
683 control.get_allow_missing(True, False, 1, False))
684 self.assertEqual(False,
685 control.get_allow_missing(True, True, 1, False))
687 def testBlobSettingsAlways(self):
688 """Test the 'always' policy"""
689 bsettings.SetItem('global', 'allow-missing', 'always')
690 self.assertEqual(True,
691 control.get_allow_missing(False, False, 1, False))
692 self.assertEqual(False,
693 control.get_allow_missing(False, True, 1, False))
695 def testBlobSettingsBranch(self):
696 """Test the 'branch' policy"""
697 bsettings.SetItem('global', 'allow-missing', 'branch')
698 self.assertEqual(False,
699 control.get_allow_missing(False, False, 1, False))
700 self.assertEqual(True,
701 control.get_allow_missing(False, False, 1, True))
702 self.assertEqual(False,
703 control.get_allow_missing(False, True, 1, True))
705 def testBlobSettingsMultiple(self):
706 """Test the 'multiple' policy"""
707 bsettings.SetItem('global', 'allow-missing', 'multiple')
708 self.assertEqual(False,
709 control.get_allow_missing(False, False, 1, False))
710 self.assertEqual(True,
711 control.get_allow_missing(False, False, 2, False))
712 self.assertEqual(False,
713 control.get_allow_missing(False, True, 2, False))
715 def testBlobSettingsBranchMultiple(self):
716 """Test the 'branch multiple' policy"""
717 bsettings.SetItem('global', 'allow-missing', 'branch multiple')
718 self.assertEqual(False,
719 control.get_allow_missing(False, False, 1, False))
720 self.assertEqual(True,
721 control.get_allow_missing(False, False, 1, True))
722 self.assertEqual(True,
723 control.get_allow_missing(False, False, 2, False))
724 self.assertEqual(True,
725 control.get_allow_missing(False, False, 2, True))
726 self.assertEqual(False,
727 control.get_allow_missing(False, True, 2, True))
729 def check_command(self, *extra_args):
730 """Run a command with the extra arguments and return the commands used
733 extra_args (list of str): List of extra arguments
736 list of str: Lines returned in the out-cmd file
738 self._RunControl('-o', self._output_dir, *extra_args)
739 board0_dir = os.path.join(self._output_dir, 'current', 'board0')
740 self.assertTrue(os.path.exists(os.path.join(board0_dir, 'done')))
741 cmd_fname = os.path.join(board0_dir, 'out-cmd')
742 self.assertTrue(os.path.exists(cmd_fname))
743 data = tools.read_file(cmd_fname)
745 config_fname = os.path.join(board0_dir, '.config')
746 self.assertTrue(os.path.exists(config_fname))
747 cfg_data = tools.read_file(config_fname)
749 return data.splitlines(), cfg_data
751 def testCmdFile(self):
752 """Test that the -cmd-out file is produced"""
753 lines = self.check_command()[0]
754 self.assertEqual(2, len(lines))
755 self.assertRegex(lines[0], b'make O=/.*board0_defconfig')
756 self.assertRegex(lines[0], b'make O=/.*-s.*')
759 """Test that the --no-lto flag works"""
760 lines = self.check_command('-L')[0]
761 self.assertIn(b'NO_LTO=1', lines[0])
763 def testReproducible(self):
764 """Test that the -r flag works"""
765 lines, cfg_data = self.check_command('-r')
766 self.assertIn(b'SOURCE_DATE_EPOCH=0', lines[0])
768 # We should see CONFIG_LOCALVERSION_AUTO unset
769 self.assertEqual(b'''CONFIG_SOMETHING=1
770 # CONFIG_LOCALVERSION_AUTO is not set
773 with test_util.capture_sys_output() as (stdout, stderr):
774 lines, cfg_data = self.check_command('-r', '-a', 'LOCALVERSION')
775 self.assertIn(b'SOURCE_DATE_EPOCH=0', lines[0])
777 # We should see CONFIG_LOCALVERSION_AUTO unset
778 self.assertEqual(b'''CONFIG_SOMETHING=1
779 CONFIG_LOCALVERSION=y
781 self.assertIn('Not dropping LOCALVERSION_AUTO', stdout.getvalue())