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