Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9ed86c1

Browse files
committed
unittest: Move back to python-stdlib.
In order to make this more suitable for non-unix ports, the discovery functionality is moved to a separate 'extension' module which can be optionally installed. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
1 parent 0765f59 commit 9ed86c1

File tree

13 files changed

+284
-224
lines changed

13 files changed

+284
-224
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
metadata(version="0.1.0")
2+
3+
require("argparse")
4+
require("fnmatch")
5+
require("unittest")
6+
7+
module("unittest_discover.py")
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Module that is used in both test_isolated_1.py and test_isolated_2.py.
2+
# The module should be clean reloaded for each.
3+
4+
state = None
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import unittest
2+
import isolated
3+
4+
5+
class TestUnittestIsolated1(unittest.TestCase):
6+
def test_NotChangedByOtherTest(self):
7+
self.assertIsNone(isolated.state)
8+
isolated.state = True
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import unittest
2+
import isolated
3+
4+
5+
class TestUnittestIsolated2(unittest.TestCase):
6+
def test_NotChangedByOtherTest(self):
7+
self.assertIsNone(isolated.state)
8+
isolated.state = True
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Extension for "unittest" that adds the ability to run via "micropython -m unittest".
2+
3+
import argparse
4+
import os
5+
import sys
6+
from fnmatch import fnmatch
7+
from micropython import const
8+
9+
from unittest import TestRunner, TestResult, TestSuite
10+
11+
12+
# Run a single test in a clean environment.
13+
def _run_test_module(runner: TestRunner, module_name: str, *extra_paths: list[str]):
14+
module_snapshot = {k: v for k, v in sys.modules.items()}
15+
path_snapshot = sys.path[:]
16+
try:
17+
for path in reversed(extra_paths):
18+
if path:
19+
sys.path.insert(0, path)
20+
21+
module = __import__(module_name)
22+
suite = TestSuite(module_name)
23+
suite._load_module(module)
24+
return runner.run(suite)
25+
finally:
26+
sys.path[:] = path_snapshot
27+
sys.modules.clear()
28+
sys.modules.update(module_snapshot)
29+
30+
31+
_DIR_TYPE = const(0x4000)
32+
33+
34+
def _run_all_in_dir(runner: TestRunner, path: str, pattern: str, top: str):
35+
result = TestResult()
36+
for fname, ftype, *_ in os.ilistdir(path):
37+
if fname in ("..", "."):
38+
continue
39+
if ftype == _DIR_TYPE:
40+
result += _run_all_in_dir(
41+
runner=runner,
42+
path="/".join((path, fname)),
43+
pattern=pattern,
44+
top=top,
45+
)
46+
if fnmatch(fname, pattern):
47+
module_name = fname.rsplit(".", 1)[0]
48+
result += _run_test_module(runner, module_name, path, top)
49+
return result
50+
51+
52+
# Implements discovery inspired by https://docs.python.org/3/library/unittest.html#test-discovery
53+
def _discover(runner: TestRunner):
54+
parser = argparse.ArgumentParser()
55+
# parser.add_argument(
56+
# "-v",
57+
# "--verbose",
58+
# action="store_true",
59+
# help="Verbose output",
60+
# )
61+
parser.add_argument(
62+
"-s",
63+
"--start-directory",
64+
dest="start",
65+
default=".",
66+
help="Directory to start discovery",
67+
)
68+
parser.add_argument(
69+
"-p",
70+
"--pattern ",
71+
dest="pattern",
72+
default="test*.py",
73+
help="Pattern to match test files",
74+
)
75+
parser.add_argument(
76+
"-t",
77+
"--top-level-directory",
78+
dest="top",
79+
help="Top level directory of project (defaults to start directory)",
80+
)
81+
args = parser.parse_args(args=sys.argv[2:])
82+
83+
path = args.start
84+
top = args.top or path
85+
86+
return _run_all_in_dir(
87+
runner=runner,
88+
path=path,
89+
pattern=args.pattern,
90+
top=top,
91+
)
92+
93+
94+
# Could make this require os.path.
95+
PATH_SEP = getattr(os, "sep", "/")
96+
97+
98+
# foo/bar/x.y.z --> foo/bar, x.y
99+
def _dirname_filename_no_ext(path):
100+
split = path.rsplit(PATH_SEP, 1)
101+
if len(split) == 1:
102+
dirname, filename = "", split[0]
103+
else:
104+
dirname, filename = split
105+
return dirname.replace(PATH_SEP, "/"), filename.rsplit(".", 1)[0]
106+
107+
108+
# This is called from unittest when __name__ == "__main__".
109+
def discover_main():
110+
failures = 0
111+
runner = TestRunner()
112+
113+
if len(sys.argv) == 1 or (
114+
len(sys.argv) >= 2
115+
and _dirname_filename_no_ext(sys.argv[0])[1] == "unittest"
116+
and sys.argv[1] == "discover"
117+
):
118+
# No args, or `python -m unittest discover ...`.
119+
result = _discover(runner)
120+
failures += result.failuresNum or result.errorsNum
121+
else:
122+
for test_spec in sys.argv[1:]:
123+
try:
124+
os.stat(test_spec)
125+
# File exists, strip extension and import with its parent directory in sys.path.
126+
dirname, module_name = _dirname_filename_no_ext(test_spec)
127+
result = _run_test_module(runner, module_name, dirname)
128+
except OSError:
129+
# Not a file, treat as named module to import.
130+
result = _run_test_module(runner, test_spec)
131+
132+
failures += result.failuresNum or result.errorsNum
133+
134+
# Terminate with non zero return code in case of failures.
135+
sys.exit(failures)

‎python-stdlib/unittest/manifest.py‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
metadata(version="0.10.0")
2+
3+
module("unittest.py")

‎unix-ffi/unittest/test_unittest.py‎ renamed to ‎python-stdlib/unittest/tests/test_assertions.py‎

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import unittest
2-
from test_unittest_isolated import global_context
32

43

54
class TestUnittestAssertions(unittest.TestCase):
@@ -143,11 +142,6 @@ def testInner():
143142
else:
144143
self.fail("Unexpected success was not detected")
145144

146-
def test_NotChangedByOtherTest(self):
147-
global global_context
148-
assert global_context is None
149-
global_context = True
150-
151145
def test_subtest_even(self):
152146
"""
153147
Test that numbers between 0 and 5 are all even.
@@ -157,24 +151,5 @@ def test_subtest_even(self):
157151
self.assertEqual(i % 2, 0)
158152

159153

160-
class TestUnittestSetup(unittest.TestCase):
161-
class_setup_var = 0
162-
163-
def setUpClass(self):
164-
TestUnittestSetup.class_setup_var += 1
165-
166-
def tearDownClass(self):
167-
# Not sure how to actually test this, but we can check (in the test case below)
168-
# that it hasn't been run already at least.
169-
TestUnittestSetup.class_setup_var = -1
170-
171-
def testSetUpTearDownClass_1(self):
172-
assert TestUnittestSetup.class_setup_var == 1, TestUnittestSetup.class_setup_var
173-
174-
def testSetUpTearDownClass_2(self):
175-
# Test this twice, as if setUpClass() gets run like setUp() it would be run twice
176-
assert TestUnittestSetup.class_setup_var == 1, TestUnittestSetup.class_setup_var
177-
178-
179154
if __name__ == "__main__":
180155
unittest.main()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import unittest
2+
3+
4+
class TestWithRunTest(unittest.TestCase):
5+
run = False
6+
7+
def runTest(self):
8+
TestWithRunTest.run = True
9+
10+
def testRunTest(self):
11+
self.fail()
12+
13+
@staticmethod
14+
def tearDownClass():
15+
if not TestWithRunTest.run:
16+
raise ValueError()
17+
18+
19+
def test_func():
20+
pass
21+
22+
23+
@unittest.expectedFailure
24+
def test_foo():
25+
raise ValueError()
26+
27+
28+
if __name__ == "__main__":
29+
unittest.main()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import unittest
2+
3+
4+
class TestUnittestSetup(unittest.TestCase):
5+
class_setup_var = 0
6+
7+
@classmethod
8+
def setUpClass(cls):
9+
assert cls is TestUnittestSetup
10+
TestUnittestSetup.class_setup_var += 1
11+
12+
@classmethod
13+
def tearDownClass(cls):
14+
assert cls is TestUnittestSetup
15+
# Not sure how to actually test this, but we can check (in the test case below)
16+
# that it hasn't been run already at least.
17+
TestUnittestSetup.class_setup_var = -1
18+
19+
def testSetUpTearDownClass_1(self):
20+
assert TestUnittestSetup.class_setup_var == 1, TestUnittestSetup.class_setup_var
21+
22+
def testSetUpTearDownClass_2(self):
23+
# Test this twice, as if setUpClass() gets run like setUp() it would be run twice
24+
assert TestUnittestSetup.class_setup_var == 1, TestUnittestSetup.class_setup_var
25+
26+
27+
if __name__ == "__main__":
28+
unittest.main()

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /