diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/buildman/README | 49 | ||||
-rw-r--r-- | tools/buildman/builder.py | 14 | ||||
-rw-r--r-- | tools/buildman/builderthread.py | 32 | ||||
-rw-r--r-- | tools/buildman/cfgutil.py | 235 | ||||
-rw-r--r-- | tools/buildman/cmdline.py | 4 | ||||
-rw-r--r-- | tools/buildman/control.py | 6 | ||||
-rw-r--r-- | tools/buildman/func_test.py | 8 | ||||
-rwxr-xr-x | tools/buildman/main.py | 29 | ||||
-rw-r--r-- | tools/buildman/test.py | 123 | ||||
-rwxr-xr-x | tools/dtoc/main.py | 2 | ||||
-rwxr-xr-x | tools/patman/main.py | 20 | ||||
-rw-r--r-- | tools/patman/test_util.py | 17 |
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') |