xref: /rk3399_rockchip-uboot/tools/buildman/func_test.py (revision f7582ce8496f19edf845d1f62c4b7f385e4cd864)
1d4144e45SSimon Glass#
2d4144e45SSimon Glass# Copyright (c) 2014 Google, Inc
3d4144e45SSimon Glass#
4d4144e45SSimon Glass# SPDX-License-Identifier:      GPL-2.0+
5d4144e45SSimon Glass#
6d4144e45SSimon Glass
7d4144e45SSimon Glassimport os
8d4144e45SSimon Glassimport shutil
9d4144e45SSimon Glassimport sys
10d4144e45SSimon Glassimport tempfile
11d4144e45SSimon Glassimport unittest
12d4144e45SSimon Glass
13823e60b6SSimon Glassimport board
148b985eebSSimon Glassimport bsettings
15d4144e45SSimon Glassimport cmdline
16d4144e45SSimon Glassimport command
17d4144e45SSimon Glassimport control
18d4144e45SSimon Glassimport gitutil
19d4144e45SSimon Glassimport terminal
20d4144e45SSimon Glassimport toolchain
21d4144e45SSimon Glass
228b985eebSSimon Glasssettings_data = '''
238b985eebSSimon Glass# Buildman settings file
248b985eebSSimon Glass
258b985eebSSimon Glass[toolchain]
268b985eebSSimon Glass
278b985eebSSimon Glass[toolchain-alias]
288b985eebSSimon Glass
298b985eebSSimon Glass[make-flags]
308b985eebSSimon Glasssrc=/home/sjg/c/src
318b985eebSSimon Glasschroot=/home/sjg/c/chroot
328b985eebSSimon Glassvboot=USE_STDINT=1 VBOOT_DEBUG=1 MAKEFLAGS_VBOOT=DEBUG=1 CFLAGS_EXTRA_VBOOT=-DUNROLL_LOOPS VBOOT_SOURCE=${src}/platform/vboot_reference
338b985eebSSimon Glasschromeos_coreboot=VBOOT=${chroot}/build/link/usr ${vboot}
348b985eebSSimon Glasschromeos_daisy=VBOOT=${chroot}/build/daisy/usr ${vboot}
358b985eebSSimon Glasschromeos_peach=VBOOT=${chroot}/build/peach_pit/usr ${vboot}
368b985eebSSimon Glass'''
378b985eebSSimon Glass
38823e60b6SSimon Glassboards = [
39823e60b6SSimon Glass    ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0',  ''],
40823e60b6SSimon Glass    ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''],
41823e60b6SSimon Glass    ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''],
42823e60b6SSimon Glass    ['Active', 'powerpc', 'mpc5xx', '', 'Tester', 'PowerPC board 2', 'board3', ''],
43823e60b6SSimon Glass    ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''],
44823e60b6SSimon Glass]
45823e60b6SSimon Glass
46dfb7e932SSimon Glasscommit_shortlog = """4aca821 patman: Avoid changing the order of tags
47dfb7e932SSimon Glass39403bb patman: Use --no-pager' to stop git from forking a pager
48dfb7e932SSimon Glassdb6e6f2 patman: Remove the -a option
49dfb7e932SSimon Glassf2ccf03 patman: Correct unit tests to run correctly
50dfb7e932SSimon Glass1d097f9 patman: Fix indentation in terminal.py
51dfb7e932SSimon Glassd073747 patman: Support the 'reverse' option for 'git log
52dfb7e932SSimon Glass"""
53dfb7e932SSimon Glass
54dfb7e932SSimon Glasscommit_log = ["""commit 7f6b8315d18f683c5181d0c3694818c1b2a20dcd
55dfb7e932SSimon GlassAuthor: Masahiro Yamada <yamada.m@jp.panasonic.com>
56dfb7e932SSimon GlassDate:   Fri Aug 22 19:12:41 2014 +0900
57dfb7e932SSimon Glass
58dfb7e932SSimon Glass    buildman: refactor help message
59dfb7e932SSimon Glass
60dfb7e932SSimon Glass    "buildman [options]" is displayed by default.
61dfb7e932SSimon Glass
62dfb7e932SSimon Glass    Append the rest of help messages to parser.usage
63dfb7e932SSimon Glass    instead of replacing it.
64dfb7e932SSimon Glass
65dfb7e932SSimon Glass    Besides, "-b <branch>" is not mandatory since commit fea5858e.
66dfb7e932SSimon Glass    Drop it from the usage.
67dfb7e932SSimon Glass
68dfb7e932SSimon Glass    Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
69dfb7e932SSimon Glass""",
70dfb7e932SSimon Glass"""commit d0737479be6baf4db5e2cdbee123e96bc5ed0ba8
71dfb7e932SSimon GlassAuthor: Simon Glass <sjg@chromium.org>
72dfb7e932SSimon GlassDate:   Thu Aug 14 16:48:25 2014 -0600
73dfb7e932SSimon Glass
74dfb7e932SSimon Glass    patman: Support the 'reverse' option for 'git log'
75dfb7e932SSimon Glass
76dfb7e932SSimon Glass    This option is currently not supported, but needs to be, for buildman to
77dfb7e932SSimon Glass    operate as expected.
78dfb7e932SSimon Glass
79dfb7e932SSimon Glass    Series-changes: 7
80dfb7e932SSimon Glass    - Add new patch to fix the 'reverse' bug
81dfb7e932SSimon Glass
82dfb7e932SSimon Glass
83dfb7e932SSimon Glass    Change-Id: I79078f792e8b390b8a1272a8023537821d45feda
84dfb7e932SSimon Glass    Reported-by: York Sun <yorksun@freescale.com>
85dfb7e932SSimon Glass    Signed-off-by: Simon Glass <sjg@chromium.org>
86dfb7e932SSimon Glass
87dfb7e932SSimon Glass""",
88dfb7e932SSimon Glass"""commit 1d097f9ab487c5019152fd47bda126839f3bf9fc
89dfb7e932SSimon GlassAuthor: Simon Glass <sjg@chromium.org>
90dfb7e932SSimon GlassDate:   Sat Aug 9 11:44:32 2014 -0600
91dfb7e932SSimon Glass
92dfb7e932SSimon Glass    patman: Fix indentation in terminal.py
93dfb7e932SSimon Glass
94dfb7e932SSimon Glass    This code came from a different project with 2-character indentation. Fix
95dfb7e932SSimon Glass    it for U-Boot.
96dfb7e932SSimon Glass
97dfb7e932SSimon Glass    Series-changes: 6
98dfb7e932SSimon Glass    - Add new patch to fix indentation in teminal.py
99dfb7e932SSimon Glass
100dfb7e932SSimon Glass    Change-Id: I5a74d2ebbb3cc12a665f5c725064009ac96e8a34
101dfb7e932SSimon Glass    Signed-off-by: Simon Glass <sjg@chromium.org>
102dfb7e932SSimon Glass
103dfb7e932SSimon Glass""",
104dfb7e932SSimon Glass"""commit f2ccf03869d1e152c836515a3ceb83cdfe04a105
105dfb7e932SSimon GlassAuthor: Simon Glass <sjg@chromium.org>
106dfb7e932SSimon GlassDate:   Sat Aug 9 11:08:24 2014 -0600
107dfb7e932SSimon Glass
108dfb7e932SSimon Glass    patman: Correct unit tests to run correctly
109dfb7e932SSimon Glass
110dfb7e932SSimon Glass    It seems that doctest behaves differently now, and some of the unit tests
111dfb7e932SSimon Glass    do not run. Adjust the tests to work correctly.
112dfb7e932SSimon Glass
113dfb7e932SSimon Glass     ./tools/patman/patman --test
114dfb7e932SSimon Glass    <unittest.result.TestResult run=10 errors=0 failures=0>
115dfb7e932SSimon Glass
116dfb7e932SSimon Glass    Series-changes: 6
117dfb7e932SSimon Glass    - Add new patch to fix patman unit tests
118dfb7e932SSimon Glass
119dfb7e932SSimon Glass    Change-Id: I3d2ca588f4933e1f9d6b1665a00e4ae58269ff3b
120dfb7e932SSimon Glass
121dfb7e932SSimon Glass""",
122dfb7e932SSimon Glass"""commit db6e6f2f9331c5a37647d6668768d4a40b8b0d1c
123dfb7e932SSimon GlassAuthor: Simon Glass <sjg@chromium.org>
124dfb7e932SSimon GlassDate:   Sat Aug 9 12:06:02 2014 -0600
125dfb7e932SSimon Glass
126dfb7e932SSimon Glass    patman: Remove the -a option
127dfb7e932SSimon Glass
128dfb7e932SSimon Glass    It seems that this is no longer needed, since checkpatch.pl will catch
129dfb7e932SSimon Glass    whitespace problems in patches. Also the option is not widely used, so
130dfb7e932SSimon Glass    it seems safe to just remove it.
131dfb7e932SSimon Glass
132dfb7e932SSimon Glass    Series-changes: 6
133dfb7e932SSimon Glass    - Add new patch to remove patman's -a option
134dfb7e932SSimon Glass
135dfb7e932SSimon Glass    Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
136dfb7e932SSimon Glass    Change-Id: I5821a1c75154e532c46513486ca40b808de7e2cc
137dfb7e932SSimon Glass
138dfb7e932SSimon Glass""",
139dfb7e932SSimon Glass"""commit 39403bb4f838153028a6f21ca30bf100f3791133
140dfb7e932SSimon GlassAuthor: Simon Glass <sjg@chromium.org>
141dfb7e932SSimon GlassDate:   Thu Aug 14 21:50:52 2014 -0600
142dfb7e932SSimon Glass
143dfb7e932SSimon Glass    patman: Use --no-pager' to stop git from forking a pager
144dfb7e932SSimon Glass
145dfb7e932SSimon Glass""",
146dfb7e932SSimon Glass"""commit 4aca821e27e97925c039e69fd37375b09c6f129c
147dfb7e932SSimon GlassAuthor: Simon Glass <sjg@chromium.org>
148dfb7e932SSimon GlassDate:   Fri Aug 22 15:57:39 2014 -0600
149dfb7e932SSimon Glass
150dfb7e932SSimon Glass    patman: Avoid changing the order of tags
151dfb7e932SSimon Glass
152dfb7e932SSimon Glass    patman collects tags that it sees in the commit and places them nicely
153dfb7e932SSimon Glass    sorted at the end of the patch. However, this is not really necessary and
154dfb7e932SSimon Glass    in fact is apparently not desirable.
155dfb7e932SSimon Glass
156dfb7e932SSimon Glass    Series-changes: 9
157dfb7e932SSimon Glass    - Add new patch to avoid changing the order of tags
158dfb7e932SSimon Glass
159dfb7e932SSimon Glass    Suggested-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
160dfb7e932SSimon Glass    Change-Id: Ib1518588c1a189ad5c3198aae76f8654aed8d0db
161dfb7e932SSimon Glass"""]
162dfb7e932SSimon Glass
163dfb7e932SSimon GlassTEST_BRANCH = '__testbranch'
164dfb7e932SSimon Glass
165d4144e45SSimon Glassclass TestFunctional(unittest.TestCase):
166d4144e45SSimon Glass    """Functional test for buildman.
167d4144e45SSimon Glass
168d4144e45SSimon Glass    This aims to test from just below the invocation of buildman (parsing
169d4144e45SSimon Glass    of arguments) to 'make' and 'git' invocation. It is not a true
170d4144e45SSimon Glass    emd-to-end test, as it mocks git, make and the tool chain. But this
171d4144e45SSimon Glass    makes it easier to detect when the builder is doing the wrong thing,
172d4144e45SSimon Glass    since in many cases this test code will fail. For example, only a
173d4144e45SSimon Glass    very limited subset of 'git' arguments is supported - anything
174d4144e45SSimon Glass    unexpected will fail.
175d4144e45SSimon Glass    """
176d4144e45SSimon Glass    def setUp(self):
177d4144e45SSimon Glass        self._base_dir = tempfile.mkdtemp()
178d4144e45SSimon Glass        self._git_dir = os.path.join(self._base_dir, 'src')
179d4144e45SSimon Glass        self._buildman_pathname = sys.argv[0]
180d4144e45SSimon Glass        self._buildman_dir = os.path.dirname(sys.argv[0])
181d4144e45SSimon Glass        command.test_result = self._HandleCommand
182dfb7e932SSimon Glass        self.setupToolchains()
183dfb7e932SSimon Glass        self._toolchains.Add('arm-gcc', test=False)
184dfb7e932SSimon Glass        self._toolchains.Add('powerpc-gcc', test=False)
1858b985eebSSimon Glass        bsettings.Setup(None)
1868b985eebSSimon Glass        bsettings.AddFile(settings_data)
187823e60b6SSimon Glass        self._boards = board.Boards()
188823e60b6SSimon Glass        for brd in boards:
189823e60b6SSimon Glass            self._boards.AddBoard(board.Board(*brd))
190d4144e45SSimon Glass
191dfb7e932SSimon Glass        # Directories where the source been cloned
192dfb7e932SSimon Glass        self._clone_dirs = []
193dfb7e932SSimon Glass        self._commits = len(commit_shortlog.splitlines()) + 1
194dfb7e932SSimon Glass        self._total_builds = self._commits * len(boards)
195dfb7e932SSimon Glass
196dfb7e932SSimon Glass        # Number of calls to make
197dfb7e932SSimon Glass        self._make_calls = 0
198dfb7e932SSimon Glass
199dfb7e932SSimon Glass        # Map of [board, commit] to error messages
200dfb7e932SSimon Glass        self._error = {}
201dfb7e932SSimon Glass
202*f7582ce8SSimon Glass        self._test_branch = TEST_BRANCH
203*f7582ce8SSimon Glass
204dfb7e932SSimon Glass        # Avoid sending any output and clear all terminal output
205dfb7e932SSimon Glass        terminal.SetPrintTestMode()
206dfb7e932SSimon Glass        terminal.GetPrintTestLines()
207dfb7e932SSimon Glass
208d4144e45SSimon Glass    def tearDown(self):
209d4144e45SSimon Glass        shutil.rmtree(self._base_dir)
210d4144e45SSimon Glass
211dfb7e932SSimon Glass    def setupToolchains(self):
212dfb7e932SSimon Glass        self._toolchains = toolchain.Toolchains()
213dfb7e932SSimon Glass        self._toolchains.Add('gcc', test=False)
214dfb7e932SSimon Glass
215d4144e45SSimon Glass    def _RunBuildman(self, *args):
216d4144e45SSimon Glass        return command.RunPipe([[self._buildman_pathname] + list(args)],
217d4144e45SSimon Glass                capture=True, capture_stderr=True)
218d4144e45SSimon Glass
219dfb7e932SSimon Glass    def _RunControl(self, *args, **kwargs):
220d4144e45SSimon Glass        sys.argv = [sys.argv[0]] + list(args)
221d4144e45SSimon Glass        options, args = cmdline.ParseArgs()
222dfb7e932SSimon Glass        result = control.DoBuildman(options, args, toolchains=self._toolchains,
223dfb7e932SSimon Glass                make_func=self._HandleMake, boards=self._boards,
224dfb7e932SSimon Glass                clean_dir=kwargs.get('clean_dir', True))
225dfb7e932SSimon Glass        self._builder = control.builder
226dfb7e932SSimon Glass        return result
227d4144e45SSimon Glass
228d4144e45SSimon Glass    def testFullHelp(self):
229d4144e45SSimon Glass        command.test_result = None
230d4144e45SSimon Glass        result = self._RunBuildman('-H')
231d4144e45SSimon Glass        help_file = os.path.join(self._buildman_dir, 'README')
232d4144e45SSimon Glass        self.assertEqual(len(result.stdout), os.path.getsize(help_file))
233d4144e45SSimon Glass        self.assertEqual(0, len(result.stderr))
234d4144e45SSimon Glass        self.assertEqual(0, result.return_code)
235d4144e45SSimon Glass
236d4144e45SSimon Glass    def testHelp(self):
237d4144e45SSimon Glass        command.test_result = None
238d4144e45SSimon Glass        result = self._RunBuildman('-h')
239d4144e45SSimon Glass        help_file = os.path.join(self._buildman_dir, 'README')
240d4144e45SSimon Glass        self.assertTrue(len(result.stdout) > 1000)
241d4144e45SSimon Glass        self.assertEqual(0, len(result.stderr))
242d4144e45SSimon Glass        self.assertEqual(0, result.return_code)
243d4144e45SSimon Glass
244d4144e45SSimon Glass    def testGitSetup(self):
245d4144e45SSimon Glass        """Test gitutils.Setup(), from outside the module itself"""
246d4144e45SSimon Glass        command.test_result = command.CommandResult(return_code=1)
247d4144e45SSimon Glass        gitutil.Setup()
248d4144e45SSimon Glass        self.assertEqual(gitutil.use_no_decorate, False)
249d4144e45SSimon Glass
250d4144e45SSimon Glass        command.test_result = command.CommandResult(return_code=0)
251d4144e45SSimon Glass        gitutil.Setup()
252d4144e45SSimon Glass        self.assertEqual(gitutil.use_no_decorate, True)
253d4144e45SSimon Glass
254d4144e45SSimon Glass    def _HandleCommandGitLog(self, args):
255d4144e45SSimon Glass        if '-n0' in args:
256d4144e45SSimon Glass            return command.CommandResult(return_code=0)
257*f7582ce8SSimon Glass        elif args[-1] == 'upstream/master..%s' % self._test_branch:
258dfb7e932SSimon Glass            return command.CommandResult(return_code=0, stdout=commit_shortlog)
259dfb7e932SSimon Glass        elif args[:3] == ['--no-color', '--no-decorate', '--reverse']:
260*f7582ce8SSimon Glass            if args[-1] == self._test_branch:
261dfb7e932SSimon Glass                count = int(args[3][2:])
262dfb7e932SSimon Glass                return command.CommandResult(return_code=0,
263dfb7e932SSimon Glass                                            stdout=''.join(commit_log[:count]))
264d4144e45SSimon Glass
265d4144e45SSimon Glass        # Not handled, so abort
266d4144e45SSimon Glass        print 'git log', args
267d4144e45SSimon Glass        sys.exit(1)
268d4144e45SSimon Glass
269dfb7e932SSimon Glass    def _HandleCommandGitConfig(self, args):
270dfb7e932SSimon Glass        config = args[0]
271dfb7e932SSimon Glass        if config == 'sendemail.aliasesfile':
272dfb7e932SSimon Glass            return command.CommandResult(return_code=0)
273dfb7e932SSimon Glass        elif config.startswith('branch.badbranch'):
274dfb7e932SSimon Glass            return command.CommandResult(return_code=1)
275*f7582ce8SSimon Glass        elif config == 'branch.%s.remote' % self._test_branch:
276dfb7e932SSimon Glass            return command.CommandResult(return_code=0, stdout='upstream\n')
277*f7582ce8SSimon Glass        elif config == 'branch.%s.merge' % self._test_branch:
278dfb7e932SSimon Glass            return command.CommandResult(return_code=0,
279dfb7e932SSimon Glass                                         stdout='refs/heads/master\n')
280dfb7e932SSimon Glass
281dfb7e932SSimon Glass        # Not handled, so abort
282dfb7e932SSimon Glass        print 'git config', args
283dfb7e932SSimon Glass        sys.exit(1)
284dfb7e932SSimon Glass
285d4144e45SSimon Glass    def _HandleCommandGit(self, in_args):
286d4144e45SSimon Glass        """Handle execution of a git command
287d4144e45SSimon Glass
288d4144e45SSimon Glass        This uses a hacked-up parser.
289d4144e45SSimon Glass
290d4144e45SSimon Glass        Args:
291d4144e45SSimon Glass            in_args: Arguments after 'git' from the command line
292d4144e45SSimon Glass        """
293d4144e45SSimon Glass        git_args = []           # Top-level arguments to git itself
294d4144e45SSimon Glass        sub_cmd = None          # Git sub-command selected
295d4144e45SSimon Glass        args = []               # Arguments to the git sub-command
296d4144e45SSimon Glass        for arg in in_args:
297d4144e45SSimon Glass            if sub_cmd:
298d4144e45SSimon Glass                args.append(arg)
299d4144e45SSimon Glass            elif arg[0] == '-':
300d4144e45SSimon Glass                git_args.append(arg)
301d4144e45SSimon Glass            else:
302dfb7e932SSimon Glass                if git_args and git_args[-1] in ['--git-dir', '--work-tree']:
303dfb7e932SSimon Glass                    git_args.append(arg)
304dfb7e932SSimon Glass                else:
305d4144e45SSimon Glass                    sub_cmd = arg
306d4144e45SSimon Glass        if sub_cmd == 'config':
307dfb7e932SSimon Glass            return self._HandleCommandGitConfig(args)
308d4144e45SSimon Glass        elif sub_cmd == 'log':
309d4144e45SSimon Glass            return self._HandleCommandGitLog(args)
310dfb7e932SSimon Glass        elif sub_cmd == 'clone':
311dfb7e932SSimon Glass            return command.CommandResult(return_code=0)
312dfb7e932SSimon Glass        elif sub_cmd == 'checkout':
313dfb7e932SSimon Glass            return command.CommandResult(return_code=0)
314d4144e45SSimon Glass
315d4144e45SSimon Glass        # Not handled, so abort
316d4144e45SSimon Glass        print 'git', git_args, sub_cmd, args
317d4144e45SSimon Glass        sys.exit(1)
318d4144e45SSimon Glass
319d4144e45SSimon Glass    def _HandleCommandNm(self, args):
320d4144e45SSimon Glass        return command.CommandResult(return_code=0)
321d4144e45SSimon Glass
322d4144e45SSimon Glass    def _HandleCommandObjdump(self, args):
323d4144e45SSimon Glass        return command.CommandResult(return_code=0)
324d4144e45SSimon Glass
325d4144e45SSimon Glass    def _HandleCommandSize(self, args):
326d4144e45SSimon Glass        return command.CommandResult(return_code=0)
327d4144e45SSimon Glass
328d4144e45SSimon Glass    def _HandleCommand(self, **kwargs):
329d4144e45SSimon Glass        """Handle a command execution.
330d4144e45SSimon Glass
331d4144e45SSimon Glass        The command is in kwargs['pipe-list'], as a list of pipes, each a
332d4144e45SSimon Glass        list of commands. The command should be emulated as required for
333d4144e45SSimon Glass        testing purposes.
334d4144e45SSimon Glass
335d4144e45SSimon Glass        Returns:
336d4144e45SSimon Glass            A CommandResult object
337d4144e45SSimon Glass        """
338d4144e45SSimon Glass        pipe_list = kwargs['pipe_list']
339dfb7e932SSimon Glass        wc = False
340d4144e45SSimon Glass        if len(pipe_list) != 1:
341dfb7e932SSimon Glass            if pipe_list[1] == ['wc', '-l']:
342dfb7e932SSimon Glass                wc = True
343dfb7e932SSimon Glass            else:
344d4144e45SSimon Glass                print 'invalid pipe', kwargs
345d4144e45SSimon Glass                sys.exit(1)
346d4144e45SSimon Glass        cmd = pipe_list[0][0]
347d4144e45SSimon Glass        args = pipe_list[0][1:]
348dfb7e932SSimon Glass        result = None
349d4144e45SSimon Glass        if cmd == 'git':
350dfb7e932SSimon Glass            result = self._HandleCommandGit(args)
351d4144e45SSimon Glass        elif cmd == './scripts/show-gnu-make':
352d4144e45SSimon Glass            return command.CommandResult(return_code=0, stdout='make')
353dfb7e932SSimon Glass        elif cmd.endswith('nm'):
354d4144e45SSimon Glass            return self._HandleCommandNm(args)
355dfb7e932SSimon Glass        elif cmd.endswith('objdump'):
356d4144e45SSimon Glass            return self._HandleCommandObjdump(args)
357dfb7e932SSimon Glass        elif cmd.endswith( 'size'):
358d4144e45SSimon Glass            return self._HandleCommandSize(args)
359d4144e45SSimon Glass
360dfb7e932SSimon Glass        if not result:
361d4144e45SSimon Glass            # Not handled, so abort
362d4144e45SSimon Glass            print 'unknown command', kwargs
363d4144e45SSimon Glass            sys.exit(1)
364dfb7e932SSimon Glass
365dfb7e932SSimon Glass        if wc:
366dfb7e932SSimon Glass            result.stdout = len(result.stdout.splitlines())
367dfb7e932SSimon Glass        return result
368d4144e45SSimon Glass
369d4144e45SSimon Glass    def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
370d4144e45SSimon Glass        """Handle execution of 'make'
371d4144e45SSimon Glass
372d4144e45SSimon Glass        Args:
373d4144e45SSimon Glass            commit: Commit object that is being built
374d4144e45SSimon Glass            brd: Board object that is being built
375d4144e45SSimon Glass            stage: Stage that we are at (mrproper, config, build)
376d4144e45SSimon Glass            cwd: Directory where make should be run
377d4144e45SSimon Glass            args: Arguments to pass to make
378d4144e45SSimon Glass            kwargs: Arguments to pass to command.RunPipe()
379d4144e45SSimon Glass        """
380dfb7e932SSimon Glass        self._make_calls += 1
381d4144e45SSimon Glass        if stage == 'mrproper':
382d4144e45SSimon Glass            return command.CommandResult(return_code=0)
383d4144e45SSimon Glass        elif stage == 'config':
384d4144e45SSimon Glass            return command.CommandResult(return_code=0,
385d4144e45SSimon Glass                    combined='Test configuration complete')
386d4144e45SSimon Glass        elif stage == 'build':
387dfb7e932SSimon Glass            stderr = ''
388dfb7e932SSimon Glass            if type(commit) is not str:
389dfb7e932SSimon Glass                stderr = self._error.get((brd.target, commit.sequence))
390dfb7e932SSimon Glass            if stderr:
391dfb7e932SSimon Glass                return command.CommandResult(return_code=1, stderr=stderr)
392d4144e45SSimon Glass            return command.CommandResult(return_code=0)
393d4144e45SSimon Glass
394d4144e45SSimon Glass        # Not handled, so abort
395d4144e45SSimon Glass        print 'make', stage
396d4144e45SSimon Glass        sys.exit(1)
397d4144e45SSimon Glass
398dfb7e932SSimon Glass    # Example function to print output lines
399dfb7e932SSimon Glass    def print_lines(self, lines):
400dfb7e932SSimon Glass        print len(lines)
401dfb7e932SSimon Glass        for line in lines:
402dfb7e932SSimon Glass            print line
403dfb7e932SSimon Glass        #self.print_lines(terminal.GetPrintTestLines())
404dfb7e932SSimon Glass
405823e60b6SSimon Glass    def testNoBoards(self):
406823e60b6SSimon Glass        """Test that buildman aborts when there are no boards"""
407823e60b6SSimon Glass        self._boards = board.Boards()
408823e60b6SSimon Glass        with self.assertRaises(SystemExit):
409823e60b6SSimon Glass            self._RunControl()
410823e60b6SSimon Glass
411d4144e45SSimon Glass    def testCurrentSource(self):
412d4144e45SSimon Glass        """Very simple test to invoke buildman on the current source"""
413dfb7e932SSimon Glass        self.setupToolchains();
414d4144e45SSimon Glass        self._RunControl()
415d4144e45SSimon Glass        lines = terminal.GetPrintTestLines()
416dfb7e932SSimon Glass        self.assertIn('Building current source for %d boards' % len(boards),
417dfb7e932SSimon Glass                      lines[0].text)
418dfb7e932SSimon Glass
419dfb7e932SSimon Glass    def testBadBranch(self):
420dfb7e932SSimon Glass        """Test that we can detect an invalid branch"""
421dfb7e932SSimon Glass        with self.assertRaises(ValueError):
422dfb7e932SSimon Glass            self._RunControl('-b', 'badbranch')
423dfb7e932SSimon Glass
424dfb7e932SSimon Glass    def testBadToolchain(self):
425dfb7e932SSimon Glass        """Test that missing toolchains are detected"""
426dfb7e932SSimon Glass        self.setupToolchains();
427dfb7e932SSimon Glass        ret_code = self._RunControl('-b', TEST_BRANCH)
428dfb7e932SSimon Glass        lines = terminal.GetPrintTestLines()
429dfb7e932SSimon Glass
430dfb7e932SSimon Glass        # Buildman always builds the upstream commit as well
431dfb7e932SSimon Glass        self.assertIn('Building %d commits for %d boards' %
432dfb7e932SSimon Glass                (self._commits, len(boards)), lines[0].text)
433dfb7e932SSimon Glass        self.assertEqual(self._builder.count, self._total_builds)
434dfb7e932SSimon Glass
435dfb7e932SSimon Glass        # Only sandbox should succeed, the others don't have toolchains
436dfb7e932SSimon Glass        self.assertEqual(self._builder.fail,
437dfb7e932SSimon Glass                         self._total_builds - self._commits)
438dfb7e932SSimon Glass        self.assertEqual(ret_code, 128)
439dfb7e932SSimon Glass
440dfb7e932SSimon Glass        for commit in range(self._commits):
441dfb7e932SSimon Glass            for board in self._boards.GetList():
442dfb7e932SSimon Glass                if board.arch != 'sandbox':
443dfb7e932SSimon Glass                  errfile = self._builder.GetErrFile(commit, board.target)
444dfb7e932SSimon Glass                  fd = open(errfile)
445dfb7e932SSimon Glass                  self.assertEqual(fd.readlines(),
446dfb7e932SSimon Glass                          ['No tool chain for %s\n' % board.arch])
447dfb7e932SSimon Glass                  fd.close()
448dfb7e932SSimon Glass
449dfb7e932SSimon Glass    def testBranch(self):
450dfb7e932SSimon Glass        """Test building a branch with all toolchains present"""
451dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH)
452dfb7e932SSimon Glass        self.assertEqual(self._builder.count, self._total_builds)
453dfb7e932SSimon Glass        self.assertEqual(self._builder.fail, 0)
454dfb7e932SSimon Glass
455dfb7e932SSimon Glass    def testCount(self):
456dfb7e932SSimon Glass        """Test building a specific number of commitst"""
457dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH, '-c2')
458dfb7e932SSimon Glass        self.assertEqual(self._builder.count, 2 * len(boards))
459dfb7e932SSimon Glass        self.assertEqual(self._builder.fail, 0)
460dfb7e932SSimon Glass        # Each board has a mrproper, config, and then one make per commit
461dfb7e932SSimon Glass        self.assertEqual(self._make_calls, len(boards) * (2 + 2))
462dfb7e932SSimon Glass
463dfb7e932SSimon Glass    def testIncremental(self):
464dfb7e932SSimon Glass        """Test building a branch twice - the second time should do nothing"""
465dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH)
466dfb7e932SSimon Glass
467dfb7e932SSimon Glass        # Each board has a mrproper, config, and then one make per commit
468dfb7e932SSimon Glass        self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
469dfb7e932SSimon Glass        self._make_calls = 0
470dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH, clean_dir=False)
471dfb7e932SSimon Glass        self.assertEqual(self._make_calls, 0)
472dfb7e932SSimon Glass        self.assertEqual(self._builder.count, self._total_builds)
473dfb7e932SSimon Glass        self.assertEqual(self._builder.fail, 0)
474dfb7e932SSimon Glass
475dfb7e932SSimon Glass    def testForceBuild(self):
476dfb7e932SSimon Glass        """The -f flag should force a rebuild"""
477dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH)
478dfb7e932SSimon Glass        self._make_calls = 0
479dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH, '-f', clean_dir=False)
480dfb7e932SSimon Glass        # Each board has a mrproper, config, and then one make per commit
481dfb7e932SSimon Glass        self.assertEqual(self._make_calls, len(boards) * (self._commits + 2))
482dfb7e932SSimon Glass
483dfb7e932SSimon Glass    def testForceReconfigure(self):
484dfb7e932SSimon Glass        """The -f flag should force a rebuild"""
485dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH, '-C')
486dfb7e932SSimon Glass        # Each commit has a mrproper, config and make
487dfb7e932SSimon Glass        self.assertEqual(self._make_calls, len(boards) * self._commits * 3)
488dfb7e932SSimon Glass
489dfb7e932SSimon Glass    def testErrors(self):
490dfb7e932SSimon Glass        """Test handling of build errors"""
491dfb7e932SSimon Glass        self._error['board2', 1] = 'fred\n'
492dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH)
493dfb7e932SSimon Glass        self.assertEqual(self._builder.count, self._total_builds)
494dfb7e932SSimon Glass        self.assertEqual(self._builder.fail, 1)
495dfb7e932SSimon Glass
496dfb7e932SSimon Glass        # Remove the error. This should have no effect since the commit will
497dfb7e932SSimon Glass        # not be rebuilt
498dfb7e932SSimon Glass        del self._error['board2', 1]
499dfb7e932SSimon Glass        self._make_calls = 0
500dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH, clean_dir=False)
501dfb7e932SSimon Glass        self.assertEqual(self._builder.count, self._total_builds)
502dfb7e932SSimon Glass        self.assertEqual(self._make_calls, 0)
503dfb7e932SSimon Glass        self.assertEqual(self._builder.fail, 1)
504dfb7e932SSimon Glass
505dfb7e932SSimon Glass        # Now use the -F flag to force rebuild of the bad commit
506dfb7e932SSimon Glass        self._RunControl('-b', TEST_BRANCH, '-F', clean_dir=False)
507dfb7e932SSimon Glass        self.assertEqual(self._builder.count, self._total_builds)
508dfb7e932SSimon Glass        self.assertEqual(self._builder.fail, 0)
509dfb7e932SSimon Glass        self.assertEqual(self._make_calls, 3)
510*f7582ce8SSimon Glass
511*f7582ce8SSimon Glass    def testBranchWithSlash(self):
512*f7582ce8SSimon Glass        """Test building a branch with a '/' in the name"""
513*f7582ce8SSimon Glass        self._test_branch = '/__dev/__testbranch'
514*f7582ce8SSimon Glass        self._RunControl('-b', self._test_branch, clean_dir=False)
515*f7582ce8SSimon Glass        self.assertEqual(self._builder.count, self._total_builds)
516*f7582ce8SSimon Glass        self.assertEqual(self._builder.fail, 0)
517