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