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