diff --git a/unix-ffi/unittest/test_unittest.py b/unix-ffi/unittest/test_unittest.py index 8e108995a..ed5173ac2 100644 --- a/unix-ffi/unittest/test_unittest.py +++ b/unix-ffi/unittest/test_unittest.py @@ -160,10 +160,14 @@ def test_subtest_even(self): class TestUnittestSetup(unittest.TestCase): class_setup_var = 0 - def setUpClass(self): + @classmethod + def setUpClass(cls): + assert cls is TestUnittestSetup TestUnittestSetup.class_setup_var += 1 - def tearDownClass(self): + @classmethod + def tearDownClass(cls): + assert cls is TestUnittestSetup # Not sure how to actually test this, but we can check (in the test case below) # that it hasn't been run already at least. TestUnittestSetup.class_setup_var = -1 diff --git a/unix-ffi/unittest/unittest.py b/unix-ffi/unittest/unittest.py index b61686981..a19727ecc 100644 --- a/unix-ffi/unittest/unittest.py +++ b/unix-ffi/unittest/unittest.py @@ -418,23 +418,23 @@ def run_one(test_function): set_up_class() - if hasattr(o, "runTest"): - name = str(o) - run_one(o.runTest) - return - - for name in dir(o): - if name.startswith("test"): - m = getattr(o, name) - if not callable(m): - continue - run_one(m) - - if callable(o): - name = o.__name__ - run_one(o) - - tear_down_class() + try: + if hasattr(o, "runTest"): + name = str(o) + run_one(o.runTest) + else: + for name in dir(o): + if name.startswith("test"): + m = getattr(o, name) + if not callable(m): + continue + run_one(m) + + if callable(o): + name = o.__name__ + run_one(o) + finally: + tear_down_class() return exceptions @@ -444,11 +444,11 @@ def _test_cases(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): + elif tn.startswith("test") and callable(c): yield c -def run_module(runner, module, path, top): +def run_module(runner, module, *extra_paths): if not module: raise ValueError("Empty module name") @@ -457,13 +457,12 @@ def run_module(runner, module, path, top): sys.modules.update(__modules__) 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) + for path in (path for path in reversed(extra_paths) if path): + sys.path.insert(0, str(path)) + try: - suite = TestSuite(module) m = __import__(module) if isinstance(module, str) else module + suite = TestSuite(m.__name__) for c in _test_cases(m): suite.addTest(c) result = runner.run(suite) @@ -481,7 +480,16 @@ def discover(runner: TestRunner): return discover(runner=runner) -def main(module="__main__", testRunner=None): +def split_path(path): + path = path.replace("\\", "/") + if "/" in path: + return path.rsplit("/", 1) + else: + return ".", path + + +def main(module="__main__", **kwargs): + testRunner = kwargs.get("testRunner", None) if testRunner: if isinstance(testRunner, type): runner = testRunner() @@ -490,29 +498,34 @@ def main(module="__main__", testRunner=None): else: runner = TestRunner() - if len(sys.argv) <= 1: - result = discover(runner) - elif sys.argv[0].split(".")[0] == "unittest" and sys.argv[1] == "discover": - result = discover(runner) + if module is not None: + result = run_module(runner, module) 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) + argn = len(sys.argv) + # See https://docs.python.org/3/library/unittest.html#test-discovery: + # python -m unittest is the equivalent of python -m unittest discover. + if not argn or ( + sys.argv[0].split(".")[0] == "unittest" + and (argn == 1 or (argn >= 1 and sys.argv[1] == "discover")) + ): + result = discover(runner) + # Remaining arguments should be test specifiers. + else: + for test_spec in sys.argv[1:]: + try: + uos.stat(test_spec) + # test_spec is a local file, run it directly + path, fname = split_path(test_spec) + modname = fname.rsplit(".", 1)[0] + result = run_module(runner, modname, path) + + except OSError: + # Not a file, treat as import name + result = run_module(runner, test_spec, ".") # Terminate with non zero return code in case of failures sys.exit(result.failuresNum or result.errorsNum) if __name__ == "__main__": - main() + main(None)