~ruther/qmk_firmware

a2c23e9419478cc49d06634732e626a55eec6d66 — Joel Challis 1 year, 1 month ago bbeb9c1
Initial 'qmk test-c' functionality (#23038)

M .github/workflows/unit_test.yml => .github/workflows/unit_test.yml +1 -1
@@ 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

M docs/cli_commands.md => docs/cli_commands.md +36 -0
@@ 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

M lib/python/qmk/cli/__init__.py => lib/python/qmk/cli/__init__.py +1 -0
@@ 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',

A lib/python/qmk/cli/test/__init__.py => lib/python/qmk/cli/test/__init__.py +0 -0
A lib/python/qmk/cli/test/c.py => lib/python/qmk/cli/test/c.py +47 -0
@@ 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

Do not follow this link