mirror of
https://github.com/Fishwaldo/u-boot.git
synced 2025-04-02 12:31:31 +00:00
buildman: Add a functional test
Buildman currently lacks testing in many areas, including its use of git, make and many command-line flags. Add a functional test which covers some of these areas. So far it does a fake 'build' of all boards for the current source tree. This version reads the real ~/.buildman and boards.cfg files. Future work will improve this. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
82012dd284
commit
d4144e45b4
4 changed files with 206 additions and 18 deletions
|
@ -30,27 +30,20 @@ import terminal
|
||||||
import toolchain
|
import toolchain
|
||||||
|
|
||||||
def RunTests():
|
def RunTests():
|
||||||
|
import func_test
|
||||||
import test
|
import test
|
||||||
import doctest
|
import doctest
|
||||||
|
|
||||||
result = unittest.TestResult()
|
result = unittest.TestResult()
|
||||||
for module in ['toolchain']:
|
for module in ['toolchain', 'gitutil']:
|
||||||
suite = doctest.DocTestSuite(module)
|
suite = doctest.DocTestSuite(module)
|
||||||
suite.run(result)
|
suite.run(result)
|
||||||
|
|
||||||
# TODO: Surely we can just 'print' result?
|
|
||||||
print result
|
|
||||||
for test, err in result.errors:
|
|
||||||
print err
|
|
||||||
for test, err in result.failures:
|
|
||||||
print err
|
|
||||||
|
|
||||||
sys.argv = [sys.argv[0]]
|
sys.argv = [sys.argv[0]]
|
||||||
suite = unittest.TestLoader().loadTestsFromTestCase(test.TestBuild)
|
for module in (test.TestBuild, func_test.TestFunctional):
|
||||||
result = unittest.TestResult()
|
suite = unittest.TestLoader().loadTestsFromTestCase(module)
|
||||||
suite.run(result)
|
suite.run(result)
|
||||||
|
|
||||||
# TODO: Surely we can just 'print' result?
|
|
||||||
print result
|
print result
|
||||||
for test, err in result.errors:
|
for test, err in result.errors:
|
||||||
print err
|
print err
|
||||||
|
|
|
@ -13,6 +13,7 @@ from builder import Builder
|
||||||
import gitutil
|
import gitutil
|
||||||
import patchstream
|
import patchstream
|
||||||
import terminal
|
import terminal
|
||||||
|
from terminal import Print
|
||||||
import toolchain
|
import toolchain
|
||||||
import command
|
import command
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -77,12 +78,18 @@ def ShowActions(series, why_selected, boards_selected, builder, options):
|
||||||
print ('Total boards to build for each commit: %d\n' %
|
print ('Total boards to build for each commit: %d\n' %
|
||||||
why_selected['all'])
|
why_selected['all'])
|
||||||
|
|
||||||
def DoBuildman(options, args):
|
def DoBuildman(options, args, toolchains=None, make_func=None):
|
||||||
"""The main control code for buildman
|
"""The main control code for buildman
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
options: Command line options object
|
options: Command line options object
|
||||||
args: Command line arguments (list of strings)
|
args: Command line arguments (list of strings)
|
||||||
|
toolchains: Toolchains to use - this should be a Toolchains()
|
||||||
|
object. If None, then it will be created and scanned
|
||||||
|
make_func: Make function to use for the builder. This is called
|
||||||
|
to execute 'make'. If this is None, the normal function
|
||||||
|
will be used, which calls the 'make' tool with suitable
|
||||||
|
arguments. This setting is useful for tests.
|
||||||
"""
|
"""
|
||||||
if options.full_help:
|
if options.full_help:
|
||||||
pager = os.getenv('PAGER')
|
pager = os.getenv('PAGER')
|
||||||
|
@ -97,8 +104,10 @@ def DoBuildman(options, args):
|
||||||
bsettings.Setup(options.config_file)
|
bsettings.Setup(options.config_file)
|
||||||
options.git_dir = os.path.join(options.git, '.git')
|
options.git_dir = os.path.join(options.git, '.git')
|
||||||
|
|
||||||
toolchains = toolchain.Toolchains()
|
if not toolchains:
|
||||||
toolchains.Scan(options.list_tool_chains)
|
toolchains = toolchain.Toolchains()
|
||||||
|
toolchains.GetSettings()
|
||||||
|
toolchains.Scan(options.list_tool_chains)
|
||||||
if options.list_tool_chains:
|
if options.list_tool_chains:
|
||||||
toolchains.List()
|
toolchains.List()
|
||||||
print
|
print
|
||||||
|
@ -202,6 +211,8 @@ def DoBuildman(options, args):
|
||||||
options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
|
options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
|
||||||
show_unknown=options.show_unknown, step=options.step)
|
show_unknown=options.show_unknown, step=options.step)
|
||||||
builder.force_config_on_failure = not options.quick
|
builder.force_config_on_failure = not options.quick
|
||||||
|
if make_func:
|
||||||
|
builder.do_make = make_func
|
||||||
|
|
||||||
# For a dry run, just show our actions as a sanity check
|
# For a dry run, just show our actions as a sanity check
|
||||||
if options.dry_run:
|
if options.dry_run:
|
||||||
|
@ -220,8 +231,8 @@ def DoBuildman(options, args):
|
||||||
else:
|
else:
|
||||||
commits = None
|
commits = None
|
||||||
|
|
||||||
print GetActionSummary(options.summary, commits, board_selected,
|
Print(GetActionSummary(options.summary, commits, board_selected,
|
||||||
options)
|
options))
|
||||||
|
|
||||||
builder.SetDisplayOptions(options.show_errors, options.show_sizes,
|
builder.SetDisplayOptions(options.show_errors, options.show_sizes,
|
||||||
options.show_detail, options.show_bloat,
|
options.show_detail, options.show_bloat,
|
||||||
|
|
182
tools/buildman/func_test.py
Normal file
182
tools/buildman/func_test.py
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014 Google, Inc
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import cmdline
|
||||||
|
import command
|
||||||
|
import control
|
||||||
|
import gitutil
|
||||||
|
import terminal
|
||||||
|
import toolchain
|
||||||
|
|
||||||
|
class TestFunctional(unittest.TestCase):
|
||||||
|
"""Functional test for buildman.
|
||||||
|
|
||||||
|
This aims to test from just below the invocation of buildman (parsing
|
||||||
|
of arguments) to 'make' and 'git' invocation. It is not a true
|
||||||
|
emd-to-end test, as it mocks git, make and the tool chain. But this
|
||||||
|
makes it easier to detect when the builder is doing the wrong thing,
|
||||||
|
since in many cases this test code will fail. For example, only a
|
||||||
|
very limited subset of 'git' arguments is supported - anything
|
||||||
|
unexpected will fail.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self._base_dir = tempfile.mkdtemp()
|
||||||
|
self._git_dir = os.path.join(self._base_dir, 'src')
|
||||||
|
self._buildman_pathname = sys.argv[0]
|
||||||
|
self._buildman_dir = os.path.dirname(sys.argv[0])
|
||||||
|
command.test_result = self._HandleCommand
|
||||||
|
self._toolchains = toolchain.Toolchains()
|
||||||
|
self._toolchains.Add('gcc', test=False)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self._base_dir)
|
||||||
|
|
||||||
|
def _RunBuildman(self, *args):
|
||||||
|
return command.RunPipe([[self._buildman_pathname] + list(args)],
|
||||||
|
capture=True, capture_stderr=True)
|
||||||
|
|
||||||
|
def _RunControl(self, *args):
|
||||||
|
sys.argv = [sys.argv[0]] + list(args)
|
||||||
|
options, args = cmdline.ParseArgs()
|
||||||
|
return control.DoBuildman(options, args, toolchains=self._toolchains,
|
||||||
|
make_func=self._HandleMake)
|
||||||
|
|
||||||
|
def testFullHelp(self):
|
||||||
|
command.test_result = None
|
||||||
|
result = self._RunBuildman('-H')
|
||||||
|
help_file = os.path.join(self._buildman_dir, 'README')
|
||||||
|
self.assertEqual(len(result.stdout), os.path.getsize(help_file))
|
||||||
|
self.assertEqual(0, len(result.stderr))
|
||||||
|
self.assertEqual(0, result.return_code)
|
||||||
|
|
||||||
|
def testHelp(self):
|
||||||
|
command.test_result = None
|
||||||
|
result = self._RunBuildman('-h')
|
||||||
|
help_file = os.path.join(self._buildman_dir, 'README')
|
||||||
|
self.assertTrue(len(result.stdout) > 1000)
|
||||||
|
self.assertEqual(0, len(result.stderr))
|
||||||
|
self.assertEqual(0, result.return_code)
|
||||||
|
|
||||||
|
def testGitSetup(self):
|
||||||
|
"""Test gitutils.Setup(), from outside the module itself"""
|
||||||
|
command.test_result = command.CommandResult(return_code=1)
|
||||||
|
gitutil.Setup()
|
||||||
|
self.assertEqual(gitutil.use_no_decorate, False)
|
||||||
|
|
||||||
|
command.test_result = command.CommandResult(return_code=0)
|
||||||
|
gitutil.Setup()
|
||||||
|
self.assertEqual(gitutil.use_no_decorate, True)
|
||||||
|
|
||||||
|
def _HandleCommandGitLog(self, args):
|
||||||
|
if '-n0' in args:
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
|
||||||
|
# Not handled, so abort
|
||||||
|
print 'git log', args
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _HandleCommandGit(self, in_args):
|
||||||
|
"""Handle execution of a git command
|
||||||
|
|
||||||
|
This uses a hacked-up parser.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
in_args: Arguments after 'git' from the command line
|
||||||
|
"""
|
||||||
|
git_args = [] # Top-level arguments to git itself
|
||||||
|
sub_cmd = None # Git sub-command selected
|
||||||
|
args = [] # Arguments to the git sub-command
|
||||||
|
for arg in in_args:
|
||||||
|
if sub_cmd:
|
||||||
|
args.append(arg)
|
||||||
|
elif arg[0] == '-':
|
||||||
|
git_args.append(arg)
|
||||||
|
else:
|
||||||
|
sub_cmd = arg
|
||||||
|
if sub_cmd == 'config':
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
elif sub_cmd == 'log':
|
||||||
|
return self._HandleCommandGitLog(args)
|
||||||
|
|
||||||
|
# Not handled, so abort
|
||||||
|
print 'git', git_args, sub_cmd, args
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def _HandleCommandNm(self, args):
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
|
||||||
|
def _HandleCommandObjdump(self, args):
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
|
||||||
|
def _HandleCommandSize(self, args):
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
|
||||||
|
def _HandleCommand(self, **kwargs):
|
||||||
|
"""Handle a command execution.
|
||||||
|
|
||||||
|
The command is in kwargs['pipe-list'], as a list of pipes, each a
|
||||||
|
list of commands. The command should be emulated as required for
|
||||||
|
testing purposes.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A CommandResult object
|
||||||
|
"""
|
||||||
|
pipe_list = kwargs['pipe_list']
|
||||||
|
if len(pipe_list) != 1:
|
||||||
|
print 'invalid pipe', kwargs
|
||||||
|
sys.exit(1)
|
||||||
|
cmd = pipe_list[0][0]
|
||||||
|
args = pipe_list[0][1:]
|
||||||
|
if cmd == 'git':
|
||||||
|
return self._HandleCommandGit(args)
|
||||||
|
elif cmd == './scripts/show-gnu-make':
|
||||||
|
return command.CommandResult(return_code=0, stdout='make')
|
||||||
|
elif cmd == 'nm':
|
||||||
|
return self._HandleCommandNm(args)
|
||||||
|
elif cmd == 'objdump':
|
||||||
|
return self._HandleCommandObjdump(args)
|
||||||
|
elif cmd == 'size':
|
||||||
|
return self._HandleCommandSize(args)
|
||||||
|
|
||||||
|
# Not handled, so abort
|
||||||
|
print 'unknown command', kwargs
|
||||||
|
sys.exit(1)
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
|
||||||
|
def _HandleMake(self, commit, brd, stage, cwd, *args, **kwargs):
|
||||||
|
"""Handle execution of 'make'
|
||||||
|
|
||||||
|
Args:
|
||||||
|
commit: Commit object that is being built
|
||||||
|
brd: Board object that is being built
|
||||||
|
stage: Stage that we are at (mrproper, config, build)
|
||||||
|
cwd: Directory where make should be run
|
||||||
|
args: Arguments to pass to make
|
||||||
|
kwargs: Arguments to pass to command.RunPipe()
|
||||||
|
"""
|
||||||
|
if stage == 'mrproper':
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
elif stage == 'config':
|
||||||
|
return command.CommandResult(return_code=0,
|
||||||
|
combined='Test configuration complete')
|
||||||
|
elif stage == 'build':
|
||||||
|
return command.CommandResult(return_code=0)
|
||||||
|
|
||||||
|
# Not handled, so abort
|
||||||
|
print 'make', stage
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def testCurrentSource(self):
|
||||||
|
"""Very simple test to invoke buildman on the current source"""
|
||||||
|
self._RunControl()
|
||||||
|
lines = terminal.GetPrintTestLines()
|
||||||
|
self.assertTrue(lines[0].text.startswith('Building current source'))
|
|
@ -99,6 +99,9 @@ class Toolchains:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.toolchains = {}
|
self.toolchains = {}
|
||||||
self.paths = []
|
self.paths = []
|
||||||
|
self._make_flags = dict(bsettings.GetItems('make-flags'))
|
||||||
|
|
||||||
|
def GetSettings(self):
|
||||||
toolchains = bsettings.GetItems('toolchain')
|
toolchains = bsettings.GetItems('toolchain')
|
||||||
if not toolchains:
|
if not toolchains:
|
||||||
print ("Warning: No tool chains - please add a [toolchain] section"
|
print ("Warning: No tool chains - please add a [toolchain] section"
|
||||||
|
@ -110,7 +113,6 @@ class Toolchains:
|
||||||
self.paths += glob.glob(value)
|
self.paths += glob.glob(value)
|
||||||
else:
|
else:
|
||||||
self.paths.append(value)
|
self.paths.append(value)
|
||||||
self._make_flags = dict(bsettings.GetItems('make-flags'))
|
|
||||||
|
|
||||||
def Add(self, fname, test=True, verbose=False):
|
def Add(self, fname, test=True, verbose=False):
|
||||||
"""Add a toolchain to our list
|
"""Add a toolchain to our list
|
||||||
|
|
Loading…
Add table
Reference in a new issue