From a2c23e9419478cc49d06634732e626a55eec6d66 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Tue, 5 Mar 2024 16:59:30 +0000 Subject: [PATCH] Initial 'qmk test-c' functionality (#23038) --- .github/workflows/unit_test.yml | 2 +- docs/cli_commands.md | 36 ++++++++++++++++++++++ lib/python/qmk/cli/__init__.py | 1 + lib/python/qmk/cli/test/__init__.py | 0 lib/python/qmk/cli/test/c.py | 47 +++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 lib/python/qmk/cli/test/__init__.py create mode 100644 lib/python/qmk/cli/test/c.py diff --git a/.github/workflows/unit_test.yml b/.github/workflows/unit_test.yml index eec8c8b5fc2..a834053a76c 100644 --- a/.github/workflows/unit_test.yml +++ b/.github/workflows/unit_test.yml @@ -32,4 +32,4 @@ jobs: - name: Install dependencies run: pip3 install -r requirements-dev.txt - name: Run tests - run: make test:all + run: qmk test-c diff --git a/docs/cli_commands.md b/docs/cli_commands.md index cf174949afb..60268122caf 100644 --- a/docs/cli_commands.md +++ b/docs/cli_commands.md @@ -791,3 +791,39 @@ This command converts a TTF font to an intermediate format for editing, before c This command converts an intermediate font image to the QFF File Format. See the [Quantum Painter](quantum_painter.md?id=quantum-painter-cli) documentation for more information on this command. +## `qmk test-c` + +This command runs the C unit test suite. If you make changes to C code you should ensure this runs successfully. + +**Usage**: + +``` +qmk test-c [-h] [-t TEST] [-l] [-c] [-e ENV] [-j PARALLEL] + +options: + -h, --help show this help message and exit + -t TEST, --test TEST Test to run from the available list. Supports wildcard globs. May be passed multiple times. + -l, --list List available tests. + -c, --clean Remove object files before compiling. + -e ENV, --env ENV Set a variable to be passed to make. May be passed multiple times. + -j PARALLEL, --parallel PARALLEL + Set the number of parallel make jobs; 0 means unlimited. +``` + +**Examples**: + +Run entire test suite: + + qmk test-c + +List available tests: + + qmk test-c --list + +Run matching test: + + qmk test-c --test unicode* + +Run single test: + + qmk test-c --test basic diff --git a/lib/python/qmk/cli/__init__.py b/lib/python/qmk/cli/__init__.py index e4a8166349b..6d05a5fc21c 100644 --- a/lib/python/qmk/cli/__init__.py +++ b/lib/python/qmk/cli/__init__.py @@ -81,6 +81,7 @@ subcommands = [ 'qmk.cli.new.keymap', 'qmk.cli.painter', 'qmk.cli.pytest', + 'qmk.cli.test.c', 'qmk.cli.userspace.add', 'qmk.cli.userspace.compile', 'qmk.cli.userspace.doctor', diff --git a/lib/python/qmk/cli/test/__init__.py b/lib/python/qmk/cli/test/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/python/qmk/cli/test/c.py b/lib/python/qmk/cli/test/c.py new file mode 100644 index 00000000000..7a4e20d5e69 --- /dev/null +++ b/lib/python/qmk/cli/test/c.py @@ -0,0 +1,47 @@ +import fnmatch +import re +from subprocess import DEVNULL + +from milc import cli + +from qmk.commands import find_make, get_make_parallel_args, build_environment + + +@cli.argument('-j', '--parallel', type=int, default=1, help="Set the number of parallel make jobs; 0 means unlimited.") +@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help="Set a variable to be passed to make. May be passed multiple times.") +@cli.argument('-c', '--clean', arg_only=True, action='store_true', help="Remove object files before compiling.") +@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available tests.') +@cli.argument('-t', '--test', arg_only=True, action='append', default=[], help="Test to run from the available list. Supports wildcard globs. May be passed multiple times.") +@cli.subcommand("QMK C Unit Tests.", hidden=False if cli.config.user.developer else True) +def test_c(cli): + """Run native unit tests. + """ + list_tests = cli.run([find_make(), 'list-tests', 'SILENT=true']) + available_tests = sorted(list_tests.stdout.strip().split()) + + if cli.args.list: + return print("\n".join(available_tests)) + + # expand any wildcards + filtered_tests = set() + for test in cli.args.test: + regex = re.compile(fnmatch.translate(test)) + filtered_tests |= set(filter(regex.match, available_tests)) + + for invalid in filtered_tests - set(available_tests): + cli.log.warning(f'Invalid test provided: {invalid}') + + # convert test names to build targets + targets = list(map(lambda x: f'test:{x}', filtered_tests or ['all'])) + + if cli.args.clean: + targets.insert(0, 'clean') + + # Add in the environment vars + for key, value in build_environment(cli.args.env).items(): + targets.append(f'{key}={value}') + + command = [find_make(), *get_make_parallel_args(cli.config.test_c.parallel), *targets] + + cli.log.info('Compiling tests with {fg_cyan}%s', ' '.join(command)) + return cli.run(command, capture_output=False, stdin=DEVNULL).returncode