1# 2# Copyright (c) 2014 Google, Inc 3# 4# SPDX-License-Identifier: GPL-2.0+ 5# 6 7import os 8import shutil 9import sys 10import tempfile 11import unittest 12 13import board 14import bsettings 15import cmdline 16import command 17import control 18import gitutil 19import terminal 20import toolchain 21 22settings_data = ''' 23# Buildman settings file 24 25[toolchain] 26 27[toolchain-alias] 28 29[make-flags] 30src=/home/sjg/c/src 31chroot=/home/sjg/c/chroot 32vboot=USE_STDINT=1 VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference 33chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot} 34chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot} 35chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot} 36''' 37 38boards = [ 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', ''], 44] 45 46class TestFunctional(unittest.TestCase): 47 """Functional test for buildman. 48 49 This aims to test from just below the invocation of buildman (parsing 50 of arguments) to 'make' and 'git' invocation. It is not a true 51 emd-to-end test, as it mocks git, make and the tool chain. But this 52 makes it easier to detect when the builder is doing the wrong thing, 53 since in many cases this test code will fail. For example, only a 54 very limited subset of 'git' arguments is supported - anything 55 unexpected will fail. 56 """ 57 def setUp(self): 58 self._base_dir = tempfile.mkdtemp() 59 self._git_dir = os.path.join(self._base_dir, 'src') 60 self._buildman_pathname = sys.argv[0] 61 self._buildman_dir = os.path.dirname(sys.argv[0]) 62 command.test_result = self._HandleCommand 63 self._toolchains = toolchain.Toolchains() 64 self._toolchains.Add('gcc', test=False) 65 bsettings.Setup(None) 66 bsettings.AddFile(settings_data) 67 self._boards = board.Boards() 68 for brd in boards: 69 self._boards.AddBoard(board.Board(*brd)) 70 71 def tearDown(self): 72 shutil.rmtree(self._base_dir) 73 74 def _RunBuildman(self, *args): 75 return command.RunPipe([[self._buildman_pathname] + list(args)], 76 capture=True, capture_stderr=True) 77 78 def _RunControl(self, *args): 79 sys.argv = [sys.argv[0]] + list(args) 80 options, args = cmdline.ParseArgs() 81 return control.DoBuildman(options, args, toolchains=self._toolchains, 82 make_func=self._HandleMake, boards=self._boards) 83 84 def testFullHelp(self): 85 command.test_result = None 86 result = self._RunBuildman('-H') 87 help_file = os.path.join(self._buildman_dir, 'README') 88 self.assertEqual(len(result.stdout), os.path.getsize(help_file)) 89 self.assertEqual(0, len(result.stderr)) 90 self.assertEqual(0, result.return_code) 91 92 def testHelp(self): 93 command.test_result = None 94 result = self._RunBuildman('-h') 95 help_file = os.path.join(self._buildman_dir, 'README') 96 self.assertTrue(len(result.stdout) > 1000) 97 self.assertEqual(0, len(result.stderr)) 98 self.assertEqual(0, result.return_code) 99 100 def testGitSetup(self): 101 """Test gitutils.Setup(), from outside the module itself""" 102 command.test_result = command.CommandResult(return_code=1) 103 gitutil.Setup() 104 self.assertEqual(gitutil.use_no_decorate, False) 105 106 command.test_result = command.CommandResult(return_code=0) 107 gitutil.Setup() 108 self.assertEqual(gitutil.use_no_decorate, True) 109 110 def _HandleCommandGitLog(self, args): 111 if '-n0' in args: 112 return command.CommandResult(return_code=0) 113 114 # Not handled, so abort 115 print 'git log', args 116 sys.exit(1) 117 118 def _HandleCommandGit(self, in_args): 119 """Handle execution of a git command 120 121 This uses a hacked-up parser. 122 123 Args: 124 in_args: Arguments after 'git' from the command line 125 """ 126 git_args = [] # Top-level arguments to git itself 127 sub_cmd = None # Git sub-command selected 128 args = [] # Arguments to the git sub-command 129 for arg in in_args: 130 if sub_cmd: 131 args.append(arg) 132 elif arg[0] == '-': 133 git_args.append(arg) 134 else: 135 sub_cmd = arg 136 if sub_cmd == 'config': 137 return command.CommandResult(return_code=0) 138 elif sub_cmd == 'log': 139 return self._HandleCommandGitLog(args) 140 141 # Not handled, so abort 142 print 'git', git_args, sub_cmd, args 143 sys.exit(1) 144 145 def _HandleCommandNm(self, args): 146 return command.CommandResult(return_code=0) 147 148 def _HandleCommandObjdump(self, args): 149 return command.CommandResult(return_code=0) 150 151 def _HandleCommandSize(self, args): 152 return command.CommandResult(return_code=0) 153 154 def _HandleCommand(self, **kwargs): 155 """Handle a command execution. 156 157 The command is in kwargs['pipe-list'], as a list of pipes, each a 158 list of commands. The command should be emulated as required for 159 testing purposes. 160 161 Returns: 162 A CommandResult object 163 """ 164 pipe_list = kwargs['pipe_list'] 165 if len(pipe_list) != 1: 166 print 'invalid pipe', kwargs 167 sys.exit(1) 168 cmd = pipe_list[0][0] 169 args = pipe_list[0][1:] 170 if cmd == 'git': 171 return self._HandleCommandGit(args) 172 elif cmd == './scripts/show-gnu-make': 173 return command.CommandResult(return_code=0, stdout='make') 174 elif cmd == 'nm': 175 return self._HandleCommandNm(args) 176 elif cmd == 'objdump': 177 return self._HandleCommandObjdump(args) 178 elif cmd == 'size': 179 return self._HandleCommandSize(args) 180 181 # Not handled, so abort 182 print 'unknown command', kwargs 183 sys.exit(1) 184 return command.CommandResult(return_code=0) 185 186 def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs): 187 """Handle execution of 'make' 188 189 Args: 190 commit: Commit object that is being built 191 brd: Board object that is being built 192 stage: Stage that we are at (mrproper, config, build) 193 cwd: Directory where make should be run 194 args: Arguments to pass to make 195 kwargs: Arguments to pass to command.RunPipe() 196 """ 197 if stage == 'mrproper': 198 return command.CommandResult(return_code=0) 199 elif stage == 'config': 200 return command.CommandResult(return_code=0, 201 combined='Test configuration complete') 202 elif stage == 'build': 203 return command.CommandResult(return_code=0) 204 205 # Not handled, so abort 206 print 'make', stage 207 sys.exit(1) 208 209 def testNoBoards(self): 210 """Test that buildman aborts when there are no boards""" 211 self._boards = board.Boards() 212 with self.assertRaises(SystemExit): 213 self._RunControl() 214 215 def testCurrentSource(self): 216 """Very simple test to invoke buildman on the current source""" 217 self._RunControl() 218 lines = terminal.GetPrintTestLines() 219 self.assertTrue(lines[0].text.startswith('Building current source')) 220