1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright (c) 2014 Google, Inc 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0+ 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun 7*4882a593Smuzhiyunimport os 8*4882a593Smuzhiyunimport shutil 9*4882a593Smuzhiyunimport sys 10*4882a593Smuzhiyunimport tempfile 11*4882a593Smuzhiyunimport unittest 12*4882a593Smuzhiyun 13*4882a593Smuzhiyunimport board 14*4882a593Smuzhiyunimport bsettings 15*4882a593Smuzhiyunimport cmdline 16*4882a593Smuzhiyunimport command 17*4882a593Smuzhiyunimport control 18*4882a593Smuzhiyunimport gitutil 19*4882a593Smuzhiyunimport terminal 20*4882a593Smuzhiyunimport toolchain 21*4882a593Smuzhiyun 22*4882a593Smuzhiyunsettings_data = ''' 23*4882a593Smuzhiyun# Buildman settings file 24*4882a593Smuzhiyun 25*4882a593Smuzhiyun[toolchain] 26*4882a593Smuzhiyun 27*4882a593Smuzhiyun[toolchain-alias] 28*4882a593Smuzhiyun 29*4882a593Smuzhiyun[make-flags] 30*4882a593Smuzhiyunsrc=/home/sjg/c/src 31*4882a593Smuzhiyunchroot=/home/sjg/c/chroot 32*4882a593Smuzhiyunvboot=USE_STDINT=1 VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference 33*4882a593Smuzhiyunchromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot} 34*4882a593Smuzhiyunchromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot} 35*4882a593Smuzhiyunchromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot} 36*4882a593Smuzhiyun''' 37*4882a593Smuzhiyun 38*4882a593Smuzhiyunboards = [ 39*4882a593Smuzhiyun ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''], 40*4882a593Smuzhiyun ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''], 41*4882a593Smuzhiyun ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''], 42*4882a593Smuzhiyun ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''], 43*4882a593Smuzhiyun] 44*4882a593Smuzhiyun 45*4882a593Smuzhiyuncommit_shortlog = """4aca821 patman: Avoid changing the order of tags 46*4882a593Smuzhiyun39403bb patman: Use --no-pager' to stop git from forking a pager 47*4882a593Smuzhiyundb6e6f2 patman: Remove the -a option 48*4882a593Smuzhiyunf2ccf03 patman: Correct unit tests to run correctly 49*4882a593Smuzhiyun1d097f9 patman: Fix indentation in terminal.py 50*4882a593Smuzhiyund073747 patman: Support the 'reverse' option for 'git log 51*4882a593Smuzhiyun""" 52*4882a593Smuzhiyun 53*4882a593Smuzhiyuncommit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd 54*4882a593SmuzhiyunAuthor: Masahiro Yamada <yamada.m@jp.panasonic.com> 55*4882a593SmuzhiyunDate: Fri Aug 22 19:12:41 2014 +0900 56*4882a593Smuzhiyun 57*4882a593Smuzhiyun buildman: refactor help message 58*4882a593Smuzhiyun 59*4882a593Smuzhiyun "buildman [options]" is displayed by default. 60*4882a593Smuzhiyun 61*4882a593Smuzhiyun Append the rest of help messages to parser.usage 62*4882a593Smuzhiyun instead of replacing it. 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun Besides, "-b <branch>" is not mandatory since commit fea5858e. 65*4882a593Smuzhiyun Drop it from the usage. 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com> 68*4882a593Smuzhiyun""", 69*4882a593Smuzhiyun"""commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8 70*4882a593SmuzhiyunAuthor: Simon Glass <sjg@chromium.org> 71*4882a593SmuzhiyunDate: Thu Aug 14 16:48:25 2014 -0600 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun patman: Support the 'reverse' option for 'git log' 74*4882a593Smuzhiyun 75*4882a593Smuzhiyun This option is currently not supported, but needs to be, for buildman to 76*4882a593Smuzhiyun operate as expected. 77*4882a593Smuzhiyun 78*4882a593Smuzhiyun Series-changes: 7 79*4882a593Smuzhiyun - Add new patch to fix the 'reverse' bug 80*4882a593Smuzhiyun 81*4882a593Smuzhiyun Series-version: 8 82*4882a593Smuzhiyun 83*4882a593Smuzhiyun Change-Id: I79078f792e8b390b8a1272a8023537821d45feda 84*4882a593Smuzhiyun Reported-by: York Sun <yorksun@freescale.com> 85*4882a593Smuzhiyun Signed-off-by: Simon Glass <sjg@chromium.org> 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun""", 88*4882a593Smuzhiyun"""commit 1d097f9ab487c5019152fd47bda126839f3bf9fc 89*4882a593SmuzhiyunAuthor: Simon Glass <sjg@chromium.org> 90*4882a593SmuzhiyunDate: Sat Aug 9 11:44:32 2014 -0600 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun patman: Fix indentation in terminal.py 93*4882a593Smuzhiyun 94*4882a593Smuzhiyun This code came from a different project with 2-character indentation. Fix 95*4882a593Smuzhiyun it for U-Boot. 96*4882a593Smuzhiyun 97*4882a593Smuzhiyun Series-changes: 6 98*4882a593Smuzhiyun - Add new patch to fix indentation in teminal.py 99*4882a593Smuzhiyun 100*4882a593Smuzhiyun Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34 101*4882a593Smuzhiyun Signed-off-by: Simon Glass <sjg@chromium.org> 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun""", 104*4882a593Smuzhiyun"""commit f2ccf03869d1e152c836515a3ceb83cdfe04a105 105*4882a593SmuzhiyunAuthor: Simon Glass <sjg@chromium.org> 106*4882a593SmuzhiyunDate: Sat Aug 9 11:08:24 2014 -0600 107*4882a593Smuzhiyun 108*4882a593Smuzhiyun patman: Correct unit tests to run correctly 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun It seems that doctest behaves differently now, and some of the unit tests 111*4882a593Smuzhiyun do not run. Adjust the tests to work correctly. 112*4882a593Smuzhiyun 113*4882a593Smuzhiyun ./tools/patman/patman --test 114*4882a593Smuzhiyun <unittest.result.TestResult run=10 errors=0 failures=0> 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun Series-changes: 6 117*4882a593Smuzhiyun - Add new patch to fix patman unit tests 118*4882a593Smuzhiyun 119*4882a593Smuzhiyun Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b 120*4882a593Smuzhiyun 121*4882a593Smuzhiyun""", 122*4882a593Smuzhiyun"""commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c 123*4882a593SmuzhiyunAuthor: Simon Glass <sjg@chromium.org> 124*4882a593SmuzhiyunDate: Sat Aug 9 12:06:02 2014 -0600 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun patman: Remove the -a option 127*4882a593Smuzhiyun 128*4882a593Smuzhiyun It seems that this is no longer needed, since checkpatch.pl will catch 129*4882a593Smuzhiyun whitespace problems in patches. Also the option is not widely used, so 130*4882a593Smuzhiyun it seems safe to just remove it. 131*4882a593Smuzhiyun 132*4882a593Smuzhiyun Series-changes: 6 133*4882a593Smuzhiyun - Add new patch to remove patman's -a option 134*4882a593Smuzhiyun 135*4882a593Smuzhiyun Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com> 136*4882a593Smuzhiyun Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc 137*4882a593Smuzhiyun 138*4882a593Smuzhiyun""", 139*4882a593Smuzhiyun"""commit 39403bb4f838153028a6f21ca30bf100f3791133 140*4882a593SmuzhiyunAuthor: Simon Glass <sjg@chromium.org> 141*4882a593SmuzhiyunDate: Thu Aug 14 21:50:52 2014 -0600 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun patman: Use --no-pager' to stop git from forking a pager 144*4882a593Smuzhiyun 145*4882a593Smuzhiyun""", 146*4882a593Smuzhiyun"""commit 4aca821e27e97925c039e69fd37375b09c6f129c 147*4882a593SmuzhiyunAuthor: Simon Glass <sjg@chromium.org> 148*4882a593SmuzhiyunDate: Fri Aug 22 15:57:39 2014 -0600 149*4882a593Smuzhiyun 150*4882a593Smuzhiyun patman: Avoid changing the order of tags 151*4882a593Smuzhiyun 152*4882a593Smuzhiyun patman collects tags that it sees in the commit and places them nicely 153*4882a593Smuzhiyun sorted at the end of the patch. However, this is not really necessary and 154*4882a593Smuzhiyun in fact is apparently not desirable. 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun Series-changes: 9 157*4882a593Smuzhiyun - Add new patch to avoid changing the order of tags 158*4882a593Smuzhiyun 159*4882a593Smuzhiyun Series-version: 9 160*4882a593Smuzhiyun 161*4882a593Smuzhiyun Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com> 162*4882a593Smuzhiyun Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db 163*4882a593Smuzhiyun"""] 164*4882a593Smuzhiyun 165*4882a593SmuzhiyunTEST_BRANCH = '__testbranch' 166*4882a593Smuzhiyun 167*4882a593Smuzhiyunclass TestFunctional(unittest.TestCase): 168*4882a593Smuzhiyun """Functional test for buildman. 169*4882a593Smuzhiyun 170*4882a593Smuzhiyun This aims to test from just below the invocation of buildman (parsing 171*4882a593Smuzhiyun of arguments) to 'make' and 'git' invocation. It is not a true 172*4882a593Smuzhiyun emd-to-end test, as it mocks git, make and the tool chain. But this 173*4882a593Smuzhiyun makes it easier to detect when the builder is doing the wrong thing, 174*4882a593Smuzhiyun since in many cases this test code will fail. For example, only a 175*4882a593Smuzhiyun very limited subset of 'git' arguments is supported - anything 176*4882a593Smuzhiyun unexpected will fail. 177*4882a593Smuzhiyun """ 178*4882a593Smuzhiyun def setUp(self): 179*4882a593Smuzhiyun self._base_dir = tempfile.mkdtemp() 180*4882a593Smuzhiyun self._git_dir = os.path.join(self._base_dir, 'src') 181*4882a593Smuzhiyun self._buildman_pathname = sys.argv[0] 182*4882a593Smuzhiyun self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) 183*4882a593Smuzhiyun command.test_result = self._HandleCommand 184*4882a593Smuzhiyun self.setupToolchains() 185*4882a593Smuzhiyun self._toolchains.Add('arm-gcc', test=False) 186*4882a593Smuzhiyun self._toolchains.Add('powerpc-gcc', test=False) 187*4882a593Smuzhiyun bsettings.Setup(None) 188*4882a593Smuzhiyun bsettings.AddFile(settings_data) 189*4882a593Smuzhiyun self._boards = board.Boards() 190*4882a593Smuzhiyun for brd in boards: 191*4882a593Smuzhiyun self._boards.AddBoard(board.Board(*brd)) 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun # Directories where the source been cloned 194*4882a593Smuzhiyun self._clone_dirs = [] 195*4882a593Smuzhiyun self._commits = len(commit_shortlog.splitlines()) + 1 196*4882a593Smuzhiyun self._total_builds = self._commits * len(boards) 197*4882a593Smuzhiyun 198*4882a593Smuzhiyun # Number of calls to make 199*4882a593Smuzhiyun self._make_calls = 0 200*4882a593Smuzhiyun 201*4882a593Smuzhiyun # Map of [board, commit] to error messages 202*4882a593Smuzhiyun self._error = {} 203*4882a593Smuzhiyun 204*4882a593Smuzhiyun self._test_branch = TEST_BRANCH 205*4882a593Smuzhiyun 206*4882a593Smuzhiyun # Avoid sending any output and clear all terminal output 207*4882a593Smuzhiyun terminal.SetPrintTestMode() 208*4882a593Smuzhiyun terminal.GetPrintTestLines() 209*4882a593Smuzhiyun 210*4882a593Smuzhiyun def tearDown(self): 211*4882a593Smuzhiyun shutil.rmtree(self._base_dir) 212*4882a593Smuzhiyun 213*4882a593Smuzhiyun def setupToolchains(self): 214*4882a593Smuzhiyun self._toolchains = toolchain.Toolchains() 215*4882a593Smuzhiyun self._toolchains.Add('gcc', test=False) 216*4882a593Smuzhiyun 217*4882a593Smuzhiyun def _RunBuildman(self, *args): 218*4882a593Smuzhiyun return command.RunPipe([[self._buildman_pathname] + list(args)], 219*4882a593Smuzhiyun capture=True, capture_stderr=True) 220*4882a593Smuzhiyun 221*4882a593Smuzhiyun def _RunControl(self, *args, **kwargs): 222*4882a593Smuzhiyun sys.argv = [sys.argv[0]] + list(args) 223*4882a593Smuzhiyun options, args = cmdline.ParseArgs() 224*4882a593Smuzhiyun result = control.DoBuildman(options, args, toolchains=self._toolchains, 225*4882a593Smuzhiyun make_func=self._HandleMake, boards=self._boards, 226*4882a593Smuzhiyun clean_dir=kwargs.get('clean_dir', True)) 227*4882a593Smuzhiyun self._builder = control.builder 228*4882a593Smuzhiyun return result 229*4882a593Smuzhiyun 230*4882a593Smuzhiyun def testFullHelp(self): 231*4882a593Smuzhiyun command.test_result = None 232*4882a593Smuzhiyun result = self._RunBuildman('-H') 233*4882a593Smuzhiyun help_file = os.path.join(self._buildman_dir, 'README') 234*4882a593Smuzhiyun self.assertEqual(len(result.stdout), os.path.getsize(help_file)) 235*4882a593Smuzhiyun self.assertEqual(0, len(result.stderr)) 236*4882a593Smuzhiyun self.assertEqual(0, result.return_code) 237*4882a593Smuzhiyun 238*4882a593Smuzhiyun def testHelp(self): 239*4882a593Smuzhiyun command.test_result = None 240*4882a593Smuzhiyun result = self._RunBuildman('-h') 241*4882a593Smuzhiyun help_file = os.path.join(self._buildman_dir, 'README') 242*4882a593Smuzhiyun self.assertTrue(len(result.stdout) > 1000) 243*4882a593Smuzhiyun self.assertEqual(0, len(result.stderr)) 244*4882a593Smuzhiyun self.assertEqual(0, result.return_code) 245*4882a593Smuzhiyun 246*4882a593Smuzhiyun def testGitSetup(self): 247*4882a593Smuzhiyun """Test gitutils.Setup(), from outside the module itself""" 248*4882a593Smuzhiyun command.test_result = command.CommandResult(return_code=1) 249*4882a593Smuzhiyun gitutil.Setup() 250*4882a593Smuzhiyun self.assertEqual(gitutil.use_no_decorate, False) 251*4882a593Smuzhiyun 252*4882a593Smuzhiyun command.test_result = command.CommandResult(return_code=0) 253*4882a593Smuzhiyun gitutil.Setup() 254*4882a593Smuzhiyun self.assertEqual(gitutil.use_no_decorate, True) 255*4882a593Smuzhiyun 256*4882a593Smuzhiyun def _HandleCommandGitLog(self, args): 257*4882a593Smuzhiyun if args[-1] == '--': 258*4882a593Smuzhiyun args = args[:-1] 259*4882a593Smuzhiyun if '-n0' in args: 260*4882a593Smuzhiyun return command.CommandResult(return_code=0) 261*4882a593Smuzhiyun elif args[-1] == 'upstream/master..%s' % self._test_branch: 262*4882a593Smuzhiyun return command.CommandResult(return_code=0, stdout=commit_shortlog) 263*4882a593Smuzhiyun elif args[:3] == ['--no-color', '--no-decorate', '--reverse']: 264*4882a593Smuzhiyun if args[-1] == self._test_branch: 265*4882a593Smuzhiyun count = int(args[3][2:]) 266*4882a593Smuzhiyun return command.CommandResult(return_code=0, 267*4882a593Smuzhiyun stdout=''.join(commit_log[:count])) 268*4882a593Smuzhiyun 269*4882a593Smuzhiyun # Not handled, so abort 270*4882a593Smuzhiyun print 'git log', args 271*4882a593Smuzhiyun sys.exit(1) 272*4882a593Smuzhiyun 273*4882a593Smuzhiyun def _HandleCommandGitConfig(self, args): 274*4882a593Smuzhiyun config = args[0] 275*4882a593Smuzhiyun if config == 'sendemail.aliasesfile': 276*4882a593Smuzhiyun return command.CommandResult(return_code=0) 277*4882a593Smuzhiyun elif config.startswith('branch.badbranch'): 278*4882a593Smuzhiyun return command.CommandResult(return_code=1) 279*4882a593Smuzhiyun elif config == 'branch.%s.remote' % self._test_branch: 280*4882a593Smuzhiyun return command.CommandResult(return_code=0, stdout='upstream\n') 281*4882a593Smuzhiyun elif config == 'branch.%s.merge' % self._test_branch: 282*4882a593Smuzhiyun return command.CommandResult(return_code=0, 283*4882a593Smuzhiyun stdout='refs/heads/master\n') 284*4882a593Smuzhiyun 285*4882a593Smuzhiyun # Not handled, so abort 286*4882a593Smuzhiyun print 'git config', args 287*4882a593Smuzhiyun sys.exit(1) 288*4882a593Smuzhiyun 289*4882a593Smuzhiyun def _HandleCommandGit(self, in_args): 290*4882a593Smuzhiyun """Handle execution of a git command 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun This uses a hacked-up parser. 293*4882a593Smuzhiyun 294*4882a593Smuzhiyun Args: 295*4882a593Smuzhiyun in_args: Arguments after 'git' from the command line 296*4882a593Smuzhiyun """ 297*4882a593Smuzhiyun git_args = [] # Top-level arguments to git itself 298*4882a593Smuzhiyun sub_cmd = None # Git sub-command selected 299*4882a593Smuzhiyun args = [] # Arguments to the git sub-command 300*4882a593Smuzhiyun for arg in in_args: 301*4882a593Smuzhiyun if sub_cmd: 302*4882a593Smuzhiyun args.append(arg) 303*4882a593Smuzhiyun elif arg[0] == '-': 304*4882a593Smuzhiyun git_args.append(arg) 305*4882a593Smuzhiyun else: 306*4882a593Smuzhiyun if git_args and git_args[-1] in ['--git-dir', '--work-tree']: 307*4882a593Smuzhiyun git_args.append(arg) 308*4882a593Smuzhiyun else: 309*4882a593Smuzhiyun sub_cmd = arg 310*4882a593Smuzhiyun if sub_cmd == 'config': 311*4882a593Smuzhiyun return self._HandleCommandGitConfig(args) 312*4882a593Smuzhiyun elif sub_cmd == 'log': 313*4882a593Smuzhiyun return self._HandleCommandGitLog(args) 314*4882a593Smuzhiyun elif sub_cmd == 'clone': 315*4882a593Smuzhiyun return command.CommandResult(return_code=0) 316*4882a593Smuzhiyun elif sub_cmd == 'checkout': 317*4882a593Smuzhiyun return command.CommandResult(return_code=0) 318*4882a593Smuzhiyun 319*4882a593Smuzhiyun # Not handled, so abort 320*4882a593Smuzhiyun print 'git', git_args, sub_cmd, args 321*4882a593Smuzhiyun sys.exit(1) 322*4882a593Smuzhiyun 323*4882a593Smuzhiyun def _HandleCommandNm(self, args): 324*4882a593Smuzhiyun return command.CommandResult(return_code=0) 325*4882a593Smuzhiyun 326*4882a593Smuzhiyun def _HandleCommandObjdump(self, args): 327*4882a593Smuzhiyun return command.CommandResult(return_code=0) 328*4882a593Smuzhiyun 329*4882a593Smuzhiyun def _HandleCommandSize(self, args): 330*4882a593Smuzhiyun return command.CommandResult(return_code=0) 331*4882a593Smuzhiyun 332*4882a593Smuzhiyun def _HandleCommand(self, **kwargs): 333*4882a593Smuzhiyun """Handle a command execution. 334*4882a593Smuzhiyun 335*4882a593Smuzhiyun The command is in kwargs['pipe-list'], as a list of pipes, each a 336*4882a593Smuzhiyun list of commands. The command should be emulated as required for 337*4882a593Smuzhiyun testing purposes. 338*4882a593Smuzhiyun 339*4882a593Smuzhiyun Returns: 340*4882a593Smuzhiyun A CommandResult object 341*4882a593Smuzhiyun """ 342*4882a593Smuzhiyun pipe_list = kwargs['pipe_list'] 343*4882a593Smuzhiyun wc = False 344*4882a593Smuzhiyun if len(pipe_list) != 1: 345*4882a593Smuzhiyun if pipe_list[1] == ['wc', '-l']: 346*4882a593Smuzhiyun wc = True 347*4882a593Smuzhiyun else: 348*4882a593Smuzhiyun print 'invalid pipe', kwargs 349*4882a593Smuzhiyun sys.exit(1) 350*4882a593Smuzhiyun cmd = pipe_list[0][0] 351*4882a593Smuzhiyun args = pipe_list[0][1:] 352*4882a593Smuzhiyun result = None 353*4882a593Smuzhiyun if cmd == 'git': 354*4882a593Smuzhiyun result = self._HandleCommandGit(args) 355*4882a593Smuzhiyun elif cmd == './scripts/show-gnu-make': 356*4882a593Smuzhiyun return command.CommandResult(return_code=0, stdout='make') 357*4882a593Smuzhiyun elif cmd.endswith('nm'): 358*4882a593Smuzhiyun return self._HandleCommandNm(args) 359*4882a593Smuzhiyun elif cmd.endswith('objdump'): 360*4882a593Smuzhiyun return self._HandleCommandObjdump(args) 361*4882a593Smuzhiyun elif cmd.endswith( 'size'): 362*4882a593Smuzhiyun return self._HandleCommandSize(args) 363*4882a593Smuzhiyun 364*4882a593Smuzhiyun if not result: 365*4882a593Smuzhiyun # Not handled, so abort 366*4882a593Smuzhiyun print 'unknown command', kwargs 367*4882a593Smuzhiyun sys.exit(1) 368*4882a593Smuzhiyun 369*4882a593Smuzhiyun if wc: 370*4882a593Smuzhiyun result.stdout = len(result.stdout.splitlines()) 371*4882a593Smuzhiyun return result 372*4882a593Smuzhiyun 373*4882a593Smuzhiyun def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs): 374*4882a593Smuzhiyun """Handle execution of 'make' 375*4882a593Smuzhiyun 376*4882a593Smuzhiyun Args: 377*4882a593Smuzhiyun commit: Commit object that is being built 378*4882a593Smuzhiyun brd: Board object that is being built 379*4882a593Smuzhiyun stage: Stage that we are at (mrproper, config, build) 380*4882a593Smuzhiyun cwd: Directory where make should be run 381*4882a593Smuzhiyun args: Arguments to pass to make 382*4882a593Smuzhiyun kwargs: Arguments to pass to command.RunPipe() 383*4882a593Smuzhiyun """ 384*4882a593Smuzhiyun self._make_calls += 1 385*4882a593Smuzhiyun if stage == 'mrproper': 386*4882a593Smuzhiyun return command.CommandResult(return_code=0) 387*4882a593Smuzhiyun elif stage == 'config': 388*4882a593Smuzhiyun return command.CommandResult(return_code=0, 389*4882a593Smuzhiyun combined='Test configuration complete') 390*4882a593Smuzhiyun elif stage == 'build': 391*4882a593Smuzhiyun stderr = '' 392*4882a593Smuzhiyun if type(commit) is not str: 393*4882a593Smuzhiyun stderr = self._error.get((brd.target, commit.sequence)) 394*4882a593Smuzhiyun if stderr: 395*4882a593Smuzhiyun return command.CommandResult(return_code=1, stderr=stderr) 396*4882a593Smuzhiyun return command.CommandResult(return_code=0) 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun # Not handled, so abort 399*4882a593Smuzhiyun print 'make', stage 400*4882a593Smuzhiyun sys.exit(1) 401*4882a593Smuzhiyun 402*4882a593Smuzhiyun # Example function to print output lines 403*4882a593Smuzhiyun def print_lines(self, lines): 404*4882a593Smuzhiyun print len(lines) 405*4882a593Smuzhiyun for line in lines: 406*4882a593Smuzhiyun print line 407*4882a593Smuzhiyun #self.print_lines(terminal.GetPrintTestLines()) 408*4882a593Smuzhiyun 409*4882a593Smuzhiyun def testNoBoards(self): 410*4882a593Smuzhiyun """Test that buildman aborts when there are no boards""" 411*4882a593Smuzhiyun self._boards = board.Boards() 412*4882a593Smuzhiyun with self.assertRaises(SystemExit): 413*4882a593Smuzhiyun self._RunControl() 414*4882a593Smuzhiyun 415*4882a593Smuzhiyun def testCurrentSource(self): 416*4882a593Smuzhiyun """Very simple test to invoke buildman on the current source""" 417*4882a593Smuzhiyun self.setupToolchains(); 418*4882a593Smuzhiyun self._RunControl() 419*4882a593Smuzhiyun lines = terminal.GetPrintTestLines() 420*4882a593Smuzhiyun self.assertIn('Building current source for %d boards' % len(boards), 421*4882a593Smuzhiyun lines[0].text) 422*4882a593Smuzhiyun 423*4882a593Smuzhiyun def testBadBranch(self): 424*4882a593Smuzhiyun """Test that we can detect an invalid branch""" 425*4882a593Smuzhiyun with self.assertRaises(ValueError): 426*4882a593Smuzhiyun self._RunControl('-b', 'badbranch') 427*4882a593Smuzhiyun 428*4882a593Smuzhiyun def testBadToolchain(self): 429*4882a593Smuzhiyun """Test that missing toolchains are detected""" 430*4882a593Smuzhiyun self.setupToolchains(); 431*4882a593Smuzhiyun ret_code = self._RunControl('-b', TEST_BRANCH) 432*4882a593Smuzhiyun lines = terminal.GetPrintTestLines() 433*4882a593Smuzhiyun 434*4882a593Smuzhiyun # Buildman always builds the upstream commit as well 435*4882a593Smuzhiyun self.assertIn('Building %d commits for %d boards' % 436*4882a593Smuzhiyun (self._commits, len(boards)), lines[0].text) 437*4882a593Smuzhiyun self.assertEqual(self._builder.count, self._total_builds) 438*4882a593Smuzhiyun 439*4882a593Smuzhiyun # Only sandbox should succeed, the others don't have toolchains 440*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 441*4882a593Smuzhiyun self._total_builds - self._commits) 442*4882a593Smuzhiyun self.assertEqual(ret_code, 128) 443*4882a593Smuzhiyun 444*4882a593Smuzhiyun for commit in range(self._commits): 445*4882a593Smuzhiyun for board in self._boards.GetList(): 446*4882a593Smuzhiyun if board.arch != 'sandbox': 447*4882a593Smuzhiyun errfile = self._builder.GetErrFile(commit, board.target) 448*4882a593Smuzhiyun fd = open(errfile) 449*4882a593Smuzhiyun self.assertEqual(fd.readlines(), 450*4882a593Smuzhiyun ['No tool chain for %s\n' % board.arch]) 451*4882a593Smuzhiyun fd.close() 452*4882a593Smuzhiyun 453*4882a593Smuzhiyun def testBranch(self): 454*4882a593Smuzhiyun """Test building a branch with all toolchains present""" 455*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH) 456*4882a593Smuzhiyun self.assertEqual(self._builder.count, self._total_builds) 457*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 0) 458*4882a593Smuzhiyun 459*4882a593Smuzhiyun def testCount(self): 460*4882a593Smuzhiyun """Test building a specific number of commitst""" 461*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH, '-c2') 462*4882a593Smuzhiyun self.assertEqual(self._builder.count, 2 * len(boards)) 463*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 0) 464*4882a593Smuzhiyun # Each board has a mrproper, config, and then one make per commit 465*4882a593Smuzhiyun self.assertEqual(self._make_calls, len(boards) * (2 + 2)) 466*4882a593Smuzhiyun 467*4882a593Smuzhiyun def testIncremental(self): 468*4882a593Smuzhiyun """Test building a branch twice - the second time should do nothing""" 469*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH) 470*4882a593Smuzhiyun 471*4882a593Smuzhiyun # Each board has a mrproper, config, and then one make per commit 472*4882a593Smuzhiyun self.assertEqual(self._make_calls, len(boards) * (self._commits + 2)) 473*4882a593Smuzhiyun self._make_calls = 0 474*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH, clean_dir=False) 475*4882a593Smuzhiyun self.assertEqual(self._make_calls, 0) 476*4882a593Smuzhiyun self.assertEqual(self._builder.count, self._total_builds) 477*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 0) 478*4882a593Smuzhiyun 479*4882a593Smuzhiyun def testForceBuild(self): 480*4882a593Smuzhiyun """The -f flag should force a rebuild""" 481*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH) 482*4882a593Smuzhiyun self._make_calls = 0 483*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH, '-f', clean_dir=False) 484*4882a593Smuzhiyun # Each board has a mrproper, config, and then one make per commit 485*4882a593Smuzhiyun self.assertEqual(self._make_calls, len(boards) * (self._commits + 2)) 486*4882a593Smuzhiyun 487*4882a593Smuzhiyun def testForceReconfigure(self): 488*4882a593Smuzhiyun """The -f flag should force a rebuild""" 489*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH, '-C') 490*4882a593Smuzhiyun # Each commit has a mrproper, config and make 491*4882a593Smuzhiyun self.assertEqual(self._make_calls, len(boards) * self._commits * 3) 492*4882a593Smuzhiyun 493*4882a593Smuzhiyun def testErrors(self): 494*4882a593Smuzhiyun """Test handling of build errors""" 495*4882a593Smuzhiyun self._error['board2', 1] = 'fred\n' 496*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH) 497*4882a593Smuzhiyun self.assertEqual(self._builder.count, self._total_builds) 498*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 1) 499*4882a593Smuzhiyun 500*4882a593Smuzhiyun # Remove the error. This should have no effect since the commit will 501*4882a593Smuzhiyun # not be rebuilt 502*4882a593Smuzhiyun del self._error['board2', 1] 503*4882a593Smuzhiyun self._make_calls = 0 504*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH, clean_dir=False) 505*4882a593Smuzhiyun self.assertEqual(self._builder.count, self._total_builds) 506*4882a593Smuzhiyun self.assertEqual(self._make_calls, 0) 507*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 1) 508*4882a593Smuzhiyun 509*4882a593Smuzhiyun # Now use the -F flag to force rebuild of the bad commit 510*4882a593Smuzhiyun self._RunControl('-b', TEST_BRANCH, '-F', clean_dir=False) 511*4882a593Smuzhiyun self.assertEqual(self._builder.count, self._total_builds) 512*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 0) 513*4882a593Smuzhiyun self.assertEqual(self._make_calls, 3) 514*4882a593Smuzhiyun 515*4882a593Smuzhiyun def testBranchWithSlash(self): 516*4882a593Smuzhiyun """Test building a branch with a '/' in the name""" 517*4882a593Smuzhiyun self._test_branch = '/__dev/__testbranch' 518*4882a593Smuzhiyun self._RunControl('-b', self._test_branch, clean_dir=False) 519*4882a593Smuzhiyun self.assertEqual(self._builder.count, self._total_builds) 520*4882a593Smuzhiyun self.assertEqual(self._builder.fail, 0) 521