summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/buildman/README49
-rw-r--r--tools/buildman/builder.py14
-rw-r--r--tools/buildman/builderthread.py32
-rw-r--r--tools/buildman/cfgutil.py235
-rw-r--r--tools/buildman/cmdline.py4
-rw-r--r--tools/buildman/control.py6
-rw-r--r--tools/buildman/func_test.py8
-rwxr-xr-xtools/buildman/main.py29
-rw-r--r--tools/buildman/test.py123
-rwxr-xr-xtools/dtoc/main.py2
-rwxr-xr-xtools/patman/main.py20
-rw-r--r--tools/patman/test_util.py17
12 files changed, 489 insertions, 50 deletions
diff --git a/tools/buildman/README b/tools/buildman/README
index ce277884326..bafb3b065ce 100644
--- a/tools/buildman/README
+++ b/tools/buildman/README
@@ -1095,6 +1095,55 @@ This will write the full build into /tmp/build including object files. You must
specify the output directory with -o when using -w.
+Changing the configuration
+==========================
+
+Sometimes it is useful to change the CONFIG options for a build on the fly. This
+can be used to build a board (or multiple) with a few changes to see the impact.
+The -a option supports this:
+
+ -a <cfg>
+
+where <cfg> is a CONFIG option (with or without the CONFIG_ prefix) to enable.
+For example:
+
+ buildman -a CMD_SETEXPR_FMT
+
+will build with CONFIG_CMD_SETEXPR_FMT enabled.
+
+You can disable options by preceding them with tilde (~). You can specify the
+-a option multiple times:
+
+ buildman -a CMD_SETEXPR_FMT -a ~CMDLINE
+
+Some options have values, in which case you can change them:
+
+ buildman -a 'BOOTCOMMAND="echo hello"' CONFIG_SYS_LOAD_ADDR=0x1000
+
+Note that you must put quotes around string options and the whole thing must be
+in single quotes, to make sure the shell leave it alone.
+
+If you try to set an option that does not exist, or that cannot be changed for
+some other reason (e.g. it is 'selected' by another option), then buildman
+shows an error:
+
+ buildman --board sandbox -a FRED
+ Building current source for 1 boards (1 thread, 32 jobs per thread)
+ 0 0 0 /1 -1 (starting)errs
+ Some CONFIG adjustments did not take effect. This may be because
+ the request CONFIGs do not exist or conflict with others.
+
+ Failed adjustments:
+
+ FRED Missing expected line: CONFIG_FRED=y
+
+
+One major caveat with this feature with branches (-b) is that buildman does not
+name the output directories differently when you change the configuration, so
+doing the same build again with different configuration will not trigger a
+rebuild. You can use -f to work around that.
+
+
Other options
=============
diff --git a/tools/buildman/builder.py b/tools/buildman/builder.py
index 122f0d14065..720bbb2cf4d 100644
--- a/tools/buildman/builder.py
+++ b/tools/buildman/builder.py
@@ -250,7 +250,7 @@ class Builder:
mrproper=False, per_board_out_dir=False,
config_only=False, squash_config_y=False,
warnings_as_errors=False, work_in_output=False,
- test_thread_exceptions=False):
+ test_thread_exceptions=False, adjust_cfg=None):
"""Create a new Builder object
Args:
@@ -280,6 +280,15 @@ class Builder:
test_thread_exceptions: Uses for tests only, True to make the
threads raise an exception instead of reporting their result.
This simulates a failure in the code somewhere
+ adjust_cfg_list (list of str): List of changes to make to .config
+ file before building. Each is one of (where C is the config
+ option with or without the CONFIG_ prefix)
+
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig
+
"""
self.toolchains = toolchains
self.base_dir = base_dir
@@ -315,6 +324,8 @@ class Builder:
self.squash_config_y = squash_config_y
self.config_filenames = BASE_CONFIG_FILENAMES
self.work_in_output = work_in_output
+ self.adjust_cfg = adjust_cfg
+
if not self.squash_config_y:
self.config_filenames += EXTRA_CONFIG_FILENAMES
self._terminated = False
@@ -1747,6 +1758,7 @@ class Builder:
job.commits = commits
job.keep_outputs = keep_outputs
job.work_in_output = self.work_in_output
+ job.adjust_cfg = self.adjust_cfg
job.step = self._step
if self.num_threads:
self.queue.put(job)
diff --git a/tools/buildman/builderthread.py b/tools/buildman/builderthread.py
index 3e450e40670..ecb285c0bfa 100644
--- a/tools/buildman/builderthread.py
+++ b/tools/buildman/builderthread.py
@@ -9,6 +9,7 @@ import shutil
import sys
import threading
+from buildman import cfgutil
from patman import command
from patman import gitutil
@@ -130,7 +131,8 @@ class BuilderThread(threading.Thread):
**kwargs)
def RunCommit(self, commit_upto, brd, work_dir, do_config, config_only,
- force_build, force_build_failures, work_in_output):
+ force_build, force_build_failures, work_in_output,
+ adjust_cfg):
"""Build a particular commit.
If the build is already done, and we are not forcing a build, we skip
@@ -147,6 +149,13 @@ class BuilderThread(threading.Thread):
failure
work_in_output: Use the output directory as the work directory and
don't write to a separate output directory.
+ adjust_cfg (list of str): List of changes to make to .config file
+ before building. Each is one of (where C is either CONFIG_xxx
+ or just xxx):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig
Returns:
tuple containing:
@@ -261,7 +270,8 @@ class BuilderThread(threading.Thread):
os.remove(fname)
# If we need to reconfigure, do that now
- if do_config:
+ cfg_file = os.path.join(out_dir, '.config')
+ if do_config or adjust_cfg:
config_out = ''
if self.mrproper:
result = self.Make(commit, brd, 'mrproper', cwd,
@@ -271,11 +281,19 @@ class BuilderThread(threading.Thread):
*(args + config_args), env=env)
config_out += result.combined
do_config = False # No need to configure next time
+ if adjust_cfg:
+ cfgutil.adjust_cfg_file(cfg_file, adjust_cfg)
if result.return_code == 0:
if config_only:
args.append('cfg')
result = self.Make(commit, brd, 'build', cwd, *args,
env=env)
+ if adjust_cfg:
+ errs = cfgutil.check_cfg_file(cfg_file, adjust_cfg)
+ if errs:
+ print('errs', errs)
+ result.stderr += errs
+ result.return_code = 1
result.stderr = result.stderr.replace(src_dir + '/', '')
if self.builder.verbose_build:
result.stdout = config_out + result.stdout
@@ -486,7 +504,7 @@ class BuilderThread(threading.Thread):
work_dir, do_config, self.builder.config_only,
force_build or self.builder.force_build,
self.builder.force_build_failures,
- work_in_output=job.work_in_output)
+ job.work_in_output, job.adjust_cfg)
failed = result.return_code or result.stderr
did_config = do_config
if failed and not do_config:
@@ -495,7 +513,7 @@ class BuilderThread(threading.Thread):
if self.builder.force_config_on_failure:
result, request_config = self.RunCommit(commit_upto,
brd, work_dir, True, False, True, False,
- work_in_output=job.work_in_output)
+ job.work_in_output, job.adjust_cfg)
did_config = True
if not self.builder.force_reconfig:
do_config = request_config
@@ -540,8 +558,8 @@ class BuilderThread(threading.Thread):
# Just build the currently checked-out build
result, request_config = self.RunCommit(None, brd, work_dir, True,
self.builder.config_only, True,
- self.builder.force_build_failures,
- work_in_output=job.work_in_output)
+ self.builder.force_build_failures, job.work_in_output,
+ job.adjust_cfg)
result.commit_upto = 0
self._WriteResult(result, job.keep_outputs, job.work_in_output)
self._SendResult(result)
@@ -557,6 +575,6 @@ class BuilderThread(threading.Thread):
try:
self.RunJob(job)
except Exception as e:
- print('Thread exception:', e)
+ print('Thread exception (use -T0 to run without threads):', e)
self.builder.thread_exceptions.append(e)
self.builder.queue.task_done()
diff --git a/tools/buildman/cfgutil.py b/tools/buildman/cfgutil.py
new file mode 100644
index 00000000000..4eba50868f5
--- /dev/null
+++ b/tools/buildman/cfgutil.py
@@ -0,0 +1,235 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2022 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+#
+
+"""Utility functions for dealing with Kconfig .confing files"""
+
+import re
+
+from patman import tools
+
+RE_LINE = re.compile(r'(# )?CONFIG_([A-Z0-9_]+)(=(.*)| is not set)')
+RE_CFG = re.compile(r'(~?)(CONFIG_)?([A-Z0-9_]+)(=.*)?')
+
+def make_cfg_line(opt, adj):
+ """Make a new config line for an option
+
+ Args:
+ opt (str): Option to process, without CONFIG_ prefix
+ adj (str): Adjustment to make (C is config option without prefix):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig)
+
+ Returns:
+ str: New line to use, one of:
+ CONFIG_opt=y - option is enabled
+ # CONFIG_opt is not set - option is disabled
+ CONFIG_opt=val - option is getting a new value (val is
+ in quotes if this is a string)
+ """
+ if adj[0] == '~':
+ return f'# CONFIG_{opt} is not set'
+ if '=' in adj:
+ return f'CONFIG_{adj}'
+ return f'CONFIG_{opt}=y'
+
+def adjust_cfg_line(line, adjust_cfg, done=None):
+ """Make an adjustment to a single of line from a .config file
+
+ This processes a .config line, producing a new line if a change for this
+ CONFIG is requested in adjust_cfg
+
+ Args:
+ line (str): line to process, e.g. '# CONFIG_FRED is not set' or
+ 'CONFIG_FRED=y' or 'CONFIG_FRED=0x123' or 'CONFIG_FRED="fred"'
+ adjust_cfg (dict of str): Changes to make to .config file before
+ building:
+ key: str config to change, without the CONFIG_ prefix, e.g.
+ FRED
+ value: str change to make (C is config option without prefix):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig)
+ done (set of set): Adds the config option to this set if it is changed
+ in some way. This is used to track which ones have been processed.
+ None to skip.
+
+ Returns:
+ tuple:
+ str: New string for this line (maybe unchanged)
+ str: Adjustment string that was used
+ """
+ out_line = line
+ m_line = RE_LINE.match(line)
+ adj = None
+ if m_line:
+ _, opt, _, _ = m_line.groups()
+ adj = adjust_cfg.get(opt)
+ if adj:
+ out_line = make_cfg_line(opt, adj)
+ if done is not None:
+ done.add(opt)
+
+ return out_line, adj
+
+def adjust_cfg_lines(lines, adjust_cfg):
+ """Make adjustments to a list of lines from a .config file
+
+ Args:
+ lines (list of str): List of lines to process
+ adjust_cfg (dict of str): Changes to make to .config file before
+ building:
+ key: str config to change, without the CONFIG_ prefix, e.g.
+ FRED
+ value: str change to make (C is config option without prefix):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig)
+
+ Returns:
+ list of str: New list of lines resulting from the processing
+ """
+ out_lines = []
+ done = set()
+ for line in lines:
+ out_line, _ = adjust_cfg_line(line, adjust_cfg, done)
+ out_lines.append(out_line)
+
+ for opt in adjust_cfg:
+ if opt not in done:
+ adj = adjust_cfg.get(opt)
+ out_line = make_cfg_line(opt, adj)
+ out_lines.append(out_line)
+
+ return out_lines
+
+def adjust_cfg_file(fname, adjust_cfg):
+ """Make adjustments to a .config file
+
+ Args:
+ fname (str): Filename of .config file to change
+ adjust_cfg (dict of str): Changes to make to .config file before
+ building:
+ key: str config to change, without the CONFIG_ prefix, e.g.
+ FRED
+ value: str change to make (C is config option without prefix):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig)
+ """
+ lines = tools.ReadFile(fname, binary=False).splitlines()
+ out_lines = adjust_cfg_lines(lines, adjust_cfg)
+ out = '\n'.join(out_lines) + '\n'
+ tools.WriteFile(fname, out, binary=False)
+
+def convert_list_to_dict(adjust_cfg_list):
+ """Convert a list of config changes into the dict used by adjust_cfg_file()
+
+ Args:
+ adjust_cfg_list (list of str): List of changes to make to .config file
+ before building. Each is one of (where C is the config option with
+ or without the CONFIG_ prefix)
+
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig
+
+ Returns:
+ dict of str: Changes to make to .config file before building:
+ key: str config to change, without the CONFIG_ prefix, e.g. FRED
+ value: str change to make (C is config option without prefix):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig)
+
+ Raises:
+ ValueError: if an item in adjust_cfg_list has invalid syntax
+ """
+ result = {}
+ for cfg in adjust_cfg_list or []:
+ m_cfg = RE_CFG.match(cfg)
+ if not m_cfg:
+ raise ValueError(f"Invalid CONFIG adjustment '{cfg}'")
+ negate, _, opt, val = m_cfg.groups()
+ result[opt] = f'%s{opt}%s' % (negate or '', val or '')
+
+ return result
+
+def check_cfg_lines(lines, adjust_cfg):
+ """Check that lines do not conflict with the requested changes
+
+ If a line enables a CONFIG which was requested to be disabled, etc., then
+ this is an error. This function finds such errors.
+
+ Args:
+ lines (list of str): List of lines to process
+ adjust_cfg (dict of str): Changes to make to .config file before
+ building:
+ key: str config to change, without the CONFIG_ prefix, e.g.
+ FRED
+ value: str change to make (C is config option without prefix):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig)
+
+ Returns:
+ list of tuple: list of errors, each a tuple:
+ str: cfg adjustment requested
+ str: line of the config that conflicts
+ """
+ bad = []
+ done = set()
+ for line in lines:
+ out_line, adj = adjust_cfg_line(line, adjust_cfg, done)
+ if out_line != line:
+ bad.append([adj, line])
+
+ for opt in adjust_cfg:
+ if opt not in done:
+ adj = adjust_cfg.get(opt)
+ out_line = make_cfg_line(opt, adj)
+ bad.append([adj, f'Missing expected line: {out_line}'])
+
+ return bad
+
+def check_cfg_file(fname, adjust_cfg):
+ """Check that a config file has been adjusted according to adjust_cfg
+
+ Args:
+ fname (str): Filename of .config file to change
+ adjust_cfg (dict of str): Changes to make to .config file before
+ building:
+ key: str config to change, without the CONFIG_ prefix, e.g.
+ FRED
+ value: str change to make (C is config option without prefix):
+ C to enable C
+ ~C to disable C
+ C=val to set the value of C (val must have quotes if C is
+ a string Kconfig)
+
+ Returns:
+ str: None if OK, else an error string listing the problems
+ """
+ lines = tools.ReadFile(fname, binary=False).splitlines()
+ bad_cfgs = check_cfg_lines(lines, adjust_cfg)
+ if bad_cfgs:
+ out = [f'{cfg:20} {line}' for cfg, line in bad_cfgs]
+ content = '\\n'.join(out)
+ return f'''
+Some CONFIG adjustments did not take effect. This may be because
+the request CONFIGs do not exist or conflict with others.
+
+Failed adjustments:
+
+{content}
+'''
+ return None
diff --git a/tools/buildman/cmdline.py b/tools/buildman/cmdline.py
index 274b5ac3f45..8586bdf3b58 100644
--- a/tools/buildman/cmdline.py
+++ b/tools/buildman/cmdline.py
@@ -13,6 +13,8 @@ def ParseArgs():
args: command lin arguments
"""
parser = OptionParser()
+ parser.add_option('-a', '--adjust-cfg', type=str, action='append',
+ help='Adjust the Kconfig settings in .config before building')
parser.add_option('-A', '--print-prefix', action='store_true',
help='Print the tool-chain prefix for a board (CROSS_COMPILE=)')
parser.add_option('-b', '--branch', type='string',
@@ -32,6 +34,8 @@ def ParseArgs():
help='Show detailed size delta for each board in the -S summary')
parser.add_option('-D', '--config-only', action='store_true', default=False,
help="Don't build, just configure each commit")
+ parser.add_option('--debug', action='store_true',
+ help='Enabling debugging (provides a full traceback on error)')
parser.add_option('-e', '--show_errors', action='store_true',
default=False, help='Show errors and warnings')
parser.add_option('-E', '--warnings-as-errors', action='store_true',
diff --git a/tools/buildman/control.py b/tools/buildman/control.py
index fd9664c85d8..eee81130663 100644
--- a/tools/buildman/control.py
+++ b/tools/buildman/control.py
@@ -10,6 +10,7 @@ import sys
from buildman import board
from buildman import bsettings
+from buildman import cfgutil
from buildman import toolchain
from buildman.builder import Builder
from patman import command
@@ -321,6 +322,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
output_dir = os.path.join(options.output_dir, dirname)
if clean_dir and os.path.exists(output_dir):
shutil.rmtree(output_dir)
+ adjust_cfg = cfgutil.convert_list_to_dict(options.adjust_cfg)
+
builder = Builder(toolchains, output_dir, options.git_dir,
options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
show_unknown=options.show_unknown, step=options.step,
@@ -332,7 +335,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
squash_config_y=not options.preserve_config_y,
warnings_as_errors=options.warnings_as_errors,
work_in_output=options.work_in_output,
- test_thread_exceptions=test_thread_exceptions)
+ test_thread_exceptions=test_thread_exceptions,
+ adjust_cfg=adjust_cfg)
builder.force_config_on_failure = not options.quick
if make_func:
builder.do_make = make_func
diff --git a/tools/buildman/func_test.py b/tools/buildman/func_test.py
index 7edbee0652f..c2e0b0b5c62 100644
--- a/tools/buildman/func_test.py
+++ b/tools/buildman/func_test.py
@@ -182,11 +182,11 @@ class TestFunctional(unittest.TestCase):
self._buildman_pathname = sys.argv[0]
self._buildman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
command.test_result = self._HandleCommand
+ bsettings.Setup(None)
+ bsettings.AddFile(settings_data)
self.setupToolchains()
self._toolchains.Add('arm-gcc', test=False)
self._toolchains.Add('powerpc-gcc', test=False)
- bsettings.Setup(None)
- bsettings.AddFile(settings_data)
self._boards = board.Boards()
for brd in boards:
self._boards.AddBoard(board.Board(*brd))
@@ -623,4 +623,6 @@ class TestFunctional(unittest.TestCase):
with test_util.capture_sys_output() as (stdout, stderr):
self.assertEqual(102, self._RunControl('-o', self._output_dir,
test_thread_exceptions=True))
- self.assertIn('Thread exception: test exception', stdout.getvalue())
+ self.assertIn(
+ 'Thread exception (use -T0 to run without threads): test exception',
+ stdout.getvalue())
diff --git a/tools/buildman/main.py b/tools/buildman/main.py
index 2b714739a20..c6af311a69b 100755
--- a/tools/buildman/main.py
+++ b/tools/buildman/main.py
@@ -27,36 +27,35 @@ from buildman import toolchain
from patman import patchstream
from patman import gitutil
from patman import terminal
+from patman import test_util
-def RunTests(skip_net_tests):
+def RunTests(skip_net_tests, verboose, args):
import func_test
import test
import doctest
result = unittest.TestResult()
- for module in ['buildman.toolchain', 'patman.gitutil']:
- suite = doctest.DocTestSuite(module)
- suite.run(result)
-
- sys.argv = [sys.argv[0]]
+ test_name = args and args[0] or None
if skip_net_tests:
test.use_network = False
- for module in (test.TestBuild, func_test.TestFunctional):
- suite = unittest.TestLoader().loadTestsFromTestCase(module)
- suite.run(result)
- print(result)
- for test, err in result.errors:
- print(err)
- for test, err in result.failures:
- print(err)
+ # Run the entry tests first ,since these need to be the first to import the
+ # 'entry' module.
+ test_util.RunTestSuites(
+ result, False, verboose, False, None, test_name, [],
+ [test.TestBuild, func_test.TestFunctional,
+ 'buildman.toolchain', 'patman.gitutil'])
+ return test_util.ReportResult('buildman', test_name, result)
options, args = cmdline.ParseArgs()
+if not options.debug:
+ sys.tracebacklimit = 0
+
# Run our meagre tests
if options.test:
- RunTests(options.skip_net_tests)
+ RunTests(options.skip_net_tests, options.verbose, args)
# Build selected commits for selected boards
else:
diff --git a/tools/buildman/test.py b/tools/buildman/test.py
index b9c65c0d326..2751377e879 100644
--- a/tools/buildman/test.py
+++ b/tools/buildman/test.py
@@ -12,6 +12,7 @@ import unittest
from buildman import board
from buildman import bsettings
from buildman import builder
+from buildman import cfgutil
from buildman import control
from buildman import toolchain
from patman import commit
@@ -624,5 +625,127 @@ class TestBuild(unittest.TestCase):
expected = set([os.path.join(base_dir, f) for f in to_remove])
self.assertEqual(expected, result)
+ def test_adjust_cfg_nop(self):
+ """check various adjustments of config that are nops"""
+ # enable an enabled CONFIG
+ self.assertEqual(
+ 'CONFIG_FRED=y',
+ cfgutil.adjust_cfg_line('CONFIG_FRED=y', {'FRED':'FRED'})[0])
+
+ # disable a disabled CONFIG
+ self.assertEqual(
+ '# CONFIG_FRED is not set',
+ cfgutil.adjust_cfg_line(
+ '# CONFIG_FRED is not set', {'FRED':'~FRED'})[0])
+
+ # use the adjust_cfg_lines() function
+ self.assertEqual(
+ ['CONFIG_FRED=y'],
+ cfgutil.adjust_cfg_lines(['CONFIG_FRED=y'], {'FRED':'FRED'}))
+ self.assertEqual(
+ ['# CONFIG_FRED is not set'],
+ cfgutil.adjust_cfg_lines(['CONFIG_FRED=y'], {'FRED':'~FRED'}))
+
+ # handling an empty line
+ self.assertEqual('#', cfgutil.adjust_cfg_line('#', {'FRED':'~FRED'})[0])
+
+ def test_adjust_cfg(self):
+ """check various adjustments of config"""
+ # disable a CONFIG
+ self.assertEqual(
+ '# CONFIG_FRED is not set',
+ cfgutil.adjust_cfg_line('CONFIG_FRED=1' , {'FRED':'~FRED'})[0])
+
+ # enable a disabled CONFIG
+ self.assertEqual(
+ 'CONFIG_FRED=y',
+ cfgutil.adjust_cfg_line(
+ '# CONFIG_FRED is not set', {'FRED':'FRED'})[0])
+
+ # enable a CONFIG that doesn't exist
+ self.assertEqual(
+ ['CONFIG_FRED=y'],
+ cfgutil.adjust_cfg_lines([], {'FRED':'FRED'}))
+
+ # disable a CONFIG that doesn't exist
+ self.assertEqual(
+ ['# CONFIG_FRED is not set'],
+ cfgutil.adjust_cfg_lines([], {'FRED':'~FRED'}))
+
+ # disable a value CONFIG
+ self.assertEqual(
+ '# CONFIG_FRED is not set',
+ cfgutil.adjust_cfg_line('CONFIG_FRED="fred"' , {'FRED':'~FRED'})[0])
+
+ # setting a value CONFIG
+ self.assertEqual(
+ 'CONFIG_FRED="fred"',
+ cfgutil.adjust_cfg_line('# CONFIG_FRED is not set' ,
+ {'FRED':'FRED="fred"'})[0])
+
+ # changing a value CONFIG
+ self.assertEqual(
+ 'CONFIG_FRED="fred"',
+ cfgutil.adjust_cfg_line('CONFIG_FRED="ernie"' ,
+ {'FRED':'FRED="fred"'})[0])
+
+ # setting a value for a CONFIG that doesn't exist
+ self.assertEqual(
+ ['CONFIG_FRED="fred"'],
+ cfgutil.adjust_cfg_lines([], {'FRED':'FRED="fred"'}))
+
+ def test_convert_adjust_cfg_list(self):
+ """Check conversion of the list of changes into a dict"""
+ self.assertEqual({}, cfgutil.convert_list_to_dict(None))
+
+ expect = {
+ 'FRED':'FRED',
+ 'MARY':'~MARY',
+ 'JOHN':'JOHN=0x123',
+ 'ALICE':'ALICE="alice"',
+ 'AMY':'AMY',
+ 'ABE':'~ABE',
+ 'MARK':'MARK=0x456',
+ 'ANNA':'ANNA="anna"',
+ }
+ actual = cfgutil.convert_list_to_dict(
+ ['FRED', '~MARY', 'JOHN=0x123', 'ALICE="alice"',
+ 'CONFIG_AMY', '~CONFIG_ABE', 'CONFIG_MARK=0x456',
+ 'CONFIG_ANNA="anna"'])
+ self.assertEqual(expect, actual)
+
+ def test_check_cfg_file(self):
+ """Test check_cfg_file detects conflicts as expected"""
+ # Check failure to disable CONFIG
+ result = cfgutil.check_cfg_lines(['CONFIG_FRED=1'], {'FRED':'~FRED'})
+ self.assertEqual([['~FRED', 'CONFIG_FRED=1']], result)
+
+ result = cfgutil.check_cfg_lines(
+ ['CONFIG_FRED=1', 'CONFIG_MARY="mary"'], {'FRED':'~FRED'})
+ self.assertEqual([['~FRED', 'CONFIG_FRED=1']], result)
+
+ result = cfgutil.check_cfg_lines(
+ ['CONFIG_FRED=1', 'CONFIG_MARY="mary"'], {'MARY':'~MARY'})
+ self.assertEqual([['~MARY', 'CONFIG_MARY="mary"']], result)
+
+ # Check failure to enable CONFIG
+ result = cfgutil.check_cfg_lines(
+ ['# CONFIG_FRED is not set'], {'FRED':'FRED'})
+ self.assertEqual([['FRED', '# CONFIG_FRED is not set']], result)
+
+ # Check failure to set CONFIG value
+ result = cfgutil.check_cfg_lines(
+ ['# CONFIG_FRED is not set', 'CONFIG_MARY="not"'],
+ {'MARY':'MARY="mary"', 'FRED':'FRED'})
+ self.assertEqual([
+ ['FRED', '# CONFIG_FRED is not set'],
+ ['MARY="mary"', 'CONFIG_MARY="not"']], result)
+
+ # Check failure to add CONFIG value
+ result = cfgutil.check_cfg_lines([], {'MARY':'MARY="mary"'})
+ self.assertEqual([
+ ['MARY="mary"', 'Missing expected line: CONFIG_MARY="mary"']], result)
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tools/dtoc/main.py b/tools/dtoc/main.py
index 6f9b526bd74..276cfadf5a3 100755
--- a/tools/dtoc/main.py
+++ b/tools/dtoc/main.py
@@ -58,7 +58,7 @@ def run_tests(processes, args):
test_util.RunTestSuites(
result, debug=True, verbosity=1, test_preserve_dirs=False,
processes=processes, test_name=test_name, toolpath=[],
- test_class_list=[test_dtoc.TestDtoc,test_src_scan.TestSrcScan])
+ class_and_module_list=[test_dtoc.TestDtoc,test_src_scan.TestSrcScan])
return test_util.ReportResult('binman', test_name, result)
diff --git a/tools/patman/main.py b/tools/patman/main.py
index e5be28e3316..c01ae36e9f9 100755
--- a/tools/patman/main.py
+++ b/tools/patman/main.py
@@ -134,23 +134,11 @@ if args.cmd == 'test':
import doctest
from patman import func_test
- sys.argv = [sys.argv[0]]
result = unittest.TestResult()
- suite = unittest.TestSuite()
- loader = unittest.TestLoader()
- for module in (test_checkpatch.TestPatch, func_test.TestFunctional):
- if args.testname:
- try:
- suite.addTests(loader.loadTestsFromName(args.testname, module))
- except AttributeError:
- continue
- else:
- suite.addTests(loader.loadTestsFromTestCase(module))
- suite.run(result)
-
- for module in ['gitutil', 'settings', 'terminal']:
- suite = doctest.DocTestSuite(module)
- suite.run(result)
+ test_util.RunTestSuites(
+ result, False, False, False, None, None, None,
+ [test_checkpatch.TestPatch, func_test.TestFunctional,
+ 'gitutil', 'settings', 'terminal'])
sys.exit(test_util.ReportResult('patman', args.testname, result))
diff --git a/tools/patman/test_util.py b/tools/patman/test_util.py
index 4e261755dc6..9654e7319c1 100644
--- a/tools/patman/test_util.py
+++ b/tools/patman/test_util.py
@@ -4,6 +4,7 @@
#
from contextlib import contextmanager
+import doctest
import glob
import multiprocessing
import os
@@ -139,7 +140,7 @@ def ReportResult(toolname:str, test_name: str, result: unittest.TestResult):
def RunTestSuites(result, debug, verbosity, test_preserve_dirs, processes,
- test_name, toolpath, test_class_list):
+ test_name, toolpath, class_and_module_list):
"""Run a series of test suites and collect the results
Args:
@@ -154,11 +155,13 @@ def RunTestSuites(result, debug, verbosity, test_preserve_dirs, processes,
processes: Number of processes to use to run tests (None=same as #CPUs)
test_name: Name of test to run, or None for all
toolpath: List of paths to use for tools
- test_class_list: List of test classes to run
+ class_and_module_list: List of test classes (type class) and module
+ names (type str) to run
"""
- for module in []:
- suite = doctest.DocTestSuite(module)
- suite.run(result)
+ for module in class_and_module_list:
+ if isinstance(module, str) and (not test_name or test_name == module):
+ suite = doctest.DocTestSuite(module)
+ suite.run(result)
sys.argv = [sys.argv[0]]
if debug:
@@ -171,7 +174,9 @@ def RunTestSuites(result, debug, verbosity, test_preserve_dirs, processes,
suite = unittest.TestSuite()
loader = unittest.TestLoader()
- for module in test_class_list:
+ for module in class_and_module_list:
+ if isinstance(module, str):
+ continue
# Test the test module about our arguments, if it is interested
if hasattr(module, 'setup_test_args'):
setup_test_args = getattr(module, 'setup_test_args')