xref: /openbmc/u-boot/tools/buildman/func_test.py (revision 823e60b62a98061a662536093d46f8f26f6d318f)
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