buildman: Avoid looking at config file or toolchains in tests
[karo-tx-uboot.git] / tools / buildman / func_test.py
1 #
2 # Copyright (c) 2014 Google, Inc
3 #
4 # SPDX-License-Identifier:      GPL-2.0+
5 #
6
7 import os
8 import shutil
9 import sys
10 import tempfile
11 import unittest
12
13 import bsettings
14 import cmdline
15 import command
16 import control
17 import gitutil
18 import terminal
19 import toolchain
20
21 settings_data = '''
22 # Buildman settings file
23
24 [toolchain]
25
26 [toolchain-alias]
27
28 [make-flags]
29 src=/home/sjg/c/src
30 chroot=/home/sjg/c/chroot
31 vboot=USE_STDINT=1 VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
32 chromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
33 chromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
34 chromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
35 '''
36
37 class 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'))