buildman: Add a functional test
[platform/kernel/u-boot.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 cmdline
14 import command
15 import control
16 import gitutil
17 import terminal
18 import toolchain
19
20 class 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'))