kopia lustrzana https://github.com/micropython/micropython-lib
147 wiersze
4.2 KiB
Python
147 wiersze
4.2 KiB
Python
# Extension for "unittest" that adds the ability to run via "micropython -m unittest".
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
from fnmatch import fnmatch
|
|
from micropython import const
|
|
|
|
from unittest import TestRunner, TestResult, TestSuite
|
|
|
|
|
|
# Run a single test in a clean environment.
|
|
def _run_test_module(runner: TestRunner, module_name: str, *extra_paths: list[str]):
|
|
module_snapshot = {k: v for k, v in sys.modules.items()}
|
|
path_snapshot = sys.path[:]
|
|
try:
|
|
for path in reversed(extra_paths):
|
|
if path:
|
|
sys.path.insert(0, path)
|
|
|
|
module = __import__(module_name)
|
|
suite = TestSuite(module_name)
|
|
suite._load_module(module)
|
|
return runner.run(suite)
|
|
finally:
|
|
sys.path[:] = path_snapshot
|
|
sys.modules.clear()
|
|
sys.modules.update(module_snapshot)
|
|
|
|
|
|
_DIR_TYPE = const(0x4000)
|
|
|
|
|
|
def _run_all_in_dir(runner: TestRunner, path: str, pattern: str, top: str):
|
|
result = TestResult()
|
|
for fname, ftype, *_ in os.ilistdir(path):
|
|
if fname in ("..", "."):
|
|
continue
|
|
if ftype == _DIR_TYPE:
|
|
result += _run_all_in_dir(
|
|
runner=runner,
|
|
path="/".join((path, fname)),
|
|
pattern=pattern,
|
|
top=top,
|
|
)
|
|
if fnmatch(fname, pattern):
|
|
module_name = fname.rsplit(".", 1)[0]
|
|
result += _run_test_module(runner, module_name, path, top)
|
|
return result
|
|
|
|
|
|
# Implements discovery inspired by https://docs.python.org/3/library/unittest.html#test-discovery
|
|
def _discover(runner: TestRunner):
|
|
parser = argparse.ArgumentParser()
|
|
# parser.add_argument(
|
|
# "-v",
|
|
# "--verbose",
|
|
# action="store_true",
|
|
# help="Verbose output",
|
|
# )
|
|
parser.add_argument(
|
|
"-s",
|
|
"--start-directory",
|
|
dest="start",
|
|
default=".",
|
|
help="Directory to start discovery",
|
|
)
|
|
parser.add_argument(
|
|
"-p",
|
|
"--pattern ",
|
|
dest="pattern",
|
|
default="test*.py",
|
|
help="Pattern to match test files",
|
|
)
|
|
parser.add_argument(
|
|
"-t",
|
|
"--top-level-directory",
|
|
dest="top",
|
|
help="Top level directory of project (defaults to start directory)",
|
|
)
|
|
args = parser.parse_args(args=sys.argv[2:])
|
|
|
|
path = args.start
|
|
top = args.top or path
|
|
|
|
return _run_all_in_dir(
|
|
runner=runner,
|
|
path=path,
|
|
pattern=args.pattern,
|
|
top=top,
|
|
)
|
|
|
|
|
|
# TODO: Use os.path for path handling.
|
|
PATH_SEP = getattr(os, "sep", "/")
|
|
|
|
|
|
# foo/bar/x.y.z --> foo/bar, x.y
|
|
def _dirname_filename_no_ext(path):
|
|
# Workaround: The Windows port currently reports "/" for os.sep
|
|
# (and MicroPython doesn't have os.altsep). So for now just
|
|
# always work with os.sep (i.e. "/").
|
|
path = path.replace("\\", PATH_SEP)
|
|
|
|
split = path.rsplit(PATH_SEP, 1)
|
|
if len(split) == 1:
|
|
dirname, filename = "", split[0]
|
|
else:
|
|
dirname, filename = split
|
|
return dirname, filename.rsplit(".", 1)[0]
|
|
|
|
|
|
def discover_main():
|
|
runner = TestRunner()
|
|
|
|
if len(sys.argv) == 1 or (
|
|
len(sys.argv) >= 2
|
|
and _dirname_filename_no_ext(sys.argv[0])[1] == "unittest"
|
|
and sys.argv[1] == "discover"
|
|
):
|
|
# No args, or `python -m unittest discover ...`.
|
|
result = _discover(runner)
|
|
else:
|
|
result = TestResult()
|
|
for test_spec in sys.argv[1:]:
|
|
try:
|
|
os.stat(test_spec)
|
|
# File exists, strip extension and import with its parent directory in sys.path.
|
|
dirname, module_name = _dirname_filename_no_ext(test_spec)
|
|
res = _run_test_module(runner, module_name, dirname)
|
|
except OSError:
|
|
# Not a file, treat as named module to import.
|
|
res = _run_test_module(runner, test_spec)
|
|
|
|
result += res
|
|
|
|
if not result.testsRun:
|
|
# If tests are run their results are already printed.
|
|
# Ensure an appropriate output is printed if no tests are found.
|
|
runner.run(TestSuite())
|
|
|
|
# Terminate with non zero return code in case of failures.
|
|
sys.exit(result.failuresNum + result.errorsNum)
|
|
|
|
|
|
discover_main()
|