unittest: Add discover function.

pull/488/head
Andrew Leech 2022-03-18 17:32:25 +11:00
rodzic 9d9ca3d59b
commit a7b2f63117
4 zmienionych plików z 141 dodań i 15 usunięć

Wyświetl plik

@ -1,3 +1,4 @@
srctype = micropython-lib
type = module
version = 0.3.2
depends = argparse, fnmatch

Wyświetl plik

@ -21,4 +21,5 @@ setup(
license="MIT",
cmdclass={"sdist": sdist_upip.sdist},
py_modules=["unittest"],
install_requires=["micropython-argparse", "micropython-fnmatch"],
)

Wyświetl plik

@ -1,4 +1,5 @@
import sys
import uos
try:
import io
@ -280,6 +281,15 @@ class TestResult:
self.failuresNum,
)
def __add__(self, other):
self.errorsNum += other.errorsNum
self.failuresNum += other.failuresNum
self.skippedNum += other.skippedNum
self.testsRun += other.testsRun
self.errors.extend(other.errors)
self.failures.extend(other.failures)
return self
def capture_exc(e):
buf = io.StringIO()
@ -290,7 +300,6 @@ def capture_exc(e):
return buf.getvalue()
# TODO: Uncompliant
def run_suite(c, test_result, suite_name=""):
if isinstance(c, TestSuite):
c.run(test_result)
@ -343,7 +352,7 @@ def run_suite(c, test_result, suite_name=""):
return
for name in dir(o):
if name.startswith("test_"):
if name.startswith("test"):
m = getattr(o, name)
if not callable(m):
continue
@ -356,20 +365,65 @@ def run_suite(c, test_result, suite_name=""):
return exceptions
def main(module="__main__"):
def test_cases(m):
for tn in dir(m):
c = getattr(m, tn)
if isinstance(c, object) and isinstance(c, type) and issubclass(c, TestCase):
yield c
elif tn.startswith("test_") and callable(c):
yield c
def _test_cases(mod):
for tn in dir(mod):
c = getattr(mod, tn)
if isinstance(c, object) and isinstance(c, type) and issubclass(c, TestCase):
yield c
elif tn.startswith("test_") and callable(c):
yield c
m = __import__(module) if isinstance(module, str) else module
suite = TestSuite(m.__name__)
for c in test_cases(m):
suite.addTest(c)
def run_module(runner, module, path, top):
sys_path_initial = sys.path[:]
# Add script dir and top dir to import path
sys.path.insert(0, str(path))
if top:
sys.path.insert(1, top)
try:
suite = TestSuite(module)
m = __import__(module) if isinstance(module, str) else module
for c in _test_cases(m):
suite.addTest(c)
result = runner.run(suite)
return result
finally:
sys.path[:] = sys_path_initial
def discover(runner: TestRunner):
from unittest_discover import discover
return discover(runner=runner)
def main(module="__main__"):
runner = TestRunner()
result = runner.run(suite)
if len(sys.argv) <= 1:
result = discover(runner)
elif sys.argv[0].split(".")[0] == "unittest" and sys.argv[1] == "discover":
result = discover(runner)
else:
for test_spec in sys.argv[1:]:
try:
uos.stat(test_spec)
# test_spec is a local file, run it directly
if "/" in test_spec:
path, fname = test_spec.rsplit("/", 1)
else:
path, fname = ".", test_spec
modname = fname.rsplit(".", 1)[0]
result = run_module(runner, modname, path, None)
except OSError:
# Not a file, treat as import name
result = run_module(runner, test_spec, ".", None)
# Terminate with non zero return code in case of failures
sys.exit(result.failuresNum or result.errorsNum)
if __name__ == "__main__":
main()

Wyświetl plik

@ -0,0 +1,70 @@
import argparse
import sys
import uos
from fnmatch import fnmatch
from unittest import TestRunner, TestResult, run_module
def discover(runner: TestRunner):
"""
Implements discover function inspired by https://docs.python.org/3/library/unittest.html#test-discovery
"""
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,
)
def run_all_in_dir(runner: TestRunner, path: str, pattern: str, top: str):
DIR_TYPE = 0x4000
result = TestResult()
for fname, type, *_ in uos.ilistdir(path):
if fname in ("..", "."):
continue
if type == DIR_TYPE:
result += run_all_in_dir(
runner=runner,
path="/".join((path, fname)),
pattern=pattern,
top=top,
)
if fnmatch(fname, pattern):
modname = fname[: fname.rfind(".")]
result += run_module(runner, modname, path, top)
return result