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