Merge lp:~jamesh/storm/bug-331905-test-runner into lp:storm
- bug-331905-test-runner
- Merge into trunk
Proposed by
James Henstridge
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~jamesh/storm/bug-331905-test-runner |
Merge into: | lp:storm |
Diff against target: |
337 lines (+43/-226) 3 files modified
test (+40/-93) tests/conftest.py (+0/-133) tests/helper.py (+3/-0) |
To merge this branch: | bzr merge lp:~jamesh/storm/bug-331905-test-runner |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Thomas Herve (community) | Approve | ||
Jamu Kakar (community) | Approve | ||
Review via email: mp+10106@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
James Henstridge (jamesh) wrote : | # |
Revision history for this message
Jamu Kakar (jkakar) wrote : | # |
Thanks for this, +1!
review:
Approve
Revision history for this message
Thomas Herve (therve) wrote : | # |
One flake: test:25: 'new' imported but unused.
+1!
review:
Approve
- 331. By James Henstridge
-
Remove unneeded import of "new" module.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'test' | |||
2 | --- test 2009-03-05 21:53:12 +0000 | |||
3 | +++ test 2009-11-09 03:29:09 +0000 | |||
4 | @@ -22,7 +22,6 @@ | |||
5 | 22 | import optparse | 22 | import optparse |
6 | 23 | import unittest | 23 | import unittest |
7 | 24 | import doctest | 24 | import doctest |
8 | 25 | import new | ||
9 | 26 | import sys | 25 | import sys |
10 | 27 | import os | 26 | import os |
11 | 28 | 27 | ||
12 | @@ -35,14 +34,12 @@ | |||
13 | 35 | @param testpaths: If provided, only tests in the given sequence will | 34 | @param testpaths: If provided, only tests in the given sequence will |
14 | 36 | be considered. If not provided, all tests are | 35 | be considered. If not provided, all tests are |
15 | 37 | considered. | 36 | considered. |
18 | 38 | @return: (unittests, doctests) tuple, with lists of unittests and | 37 | @return: a test suite containing the requested tests. |
17 | 39 | doctests found, respectively. | ||
19 | 40 | """ | 38 | """ |
20 | 39 | suite = unittest.TestSuite() | ||
21 | 41 | topdir = os.path.abspath(os.path.dirname(__file__)) | 40 | topdir = os.path.abspath(os.path.dirname(__file__)) |
22 | 42 | testdir = os.path.dirname(tests.__file__) | 41 | testdir = os.path.dirname(tests.__file__) |
23 | 43 | testpaths = set(testpaths) | 42 | testpaths = set(testpaths) |
24 | 44 | unittests = [] | ||
25 | 45 | doctests = [] | ||
26 | 46 | for root, dirnames, filenames in os.walk(testdir): | 43 | for root, dirnames, filenames in os.walk(testdir): |
27 | 47 | for filename in filenames: | 44 | for filename in filenames: |
28 | 48 | filepath = os.path.join(root, filename) | 45 | filepath = os.path.join(root, filename) |
29 | @@ -62,17 +59,28 @@ | |||
30 | 62 | continue | 59 | continue |
31 | 63 | 60 | ||
32 | 64 | if filename.endswith(".py"): | 61 | if filename.endswith(".py"): |
40 | 65 | unittests.append(relpath) | 62 | modpath = relpath.replace(os.path.sep, ".")[:-3] |
41 | 66 | elif relpath == os.path.join("tests", "zope", "README.txt"): | 63 | module = __import__(modpath, None, None, [""]) |
42 | 67 | # Special case the inclusion of the Zope-dependent | 64 | suite.addTest( |
43 | 68 | # ZStorm doctest. | 65 | unittest.defaultTestLoader.loadTestsFromModule(module)) |
37 | 69 | from tests.zope import has_zope | ||
38 | 70 | if has_zope: | ||
39 | 71 | doctests.append(relpath) | ||
44 | 72 | elif filename.endswith(".txt"): | 66 | elif filename.endswith(".txt"): |
46 | 73 | doctests.append(relpath) | 67 | load_test = True |
47 | 68 | if relpath == os.path.join("tests", "zope", "README.txt"): | ||
48 | 69 | # Special case the inclusion of the Zope-dependent | ||
49 | 70 | # ZStorm doctest. | ||
50 | 71 | from tests.zope import has_zope | ||
51 | 72 | load_test = has_zope | ||
52 | 73 | if load_test: | ||
53 | 74 | parent_path = os.path.dirname(relpath).replace( | ||
54 | 75 | os.path.sep, ".") | ||
55 | 76 | parent_module = __import__(parent_path, None, None, [""]) | ||
56 | 77 | suite.addTest(doctest.DocFileSuite( | ||
57 | 78 | os.path.basename(relpath), | ||
58 | 79 | module_relative=True, | ||
59 | 80 | package=parent_module, | ||
60 | 81 | optionflags=doctest.ELLIPSIS)) | ||
61 | 74 | 82 | ||
63 | 75 | return unittests, doctests | 83 | return suite |
64 | 76 | 84 | ||
65 | 77 | 85 | ||
66 | 78 | def parse_sys_argv(): | 86 | def parse_sys_argv(): |
67 | @@ -85,27 +93,7 @@ | |||
68 | 85 | del sys.argv[i] | 93 | del sys.argv[i] |
69 | 86 | return testpaths | 94 | return testpaths |
70 | 87 | 95 | ||
92 | 88 | 96 | def test_with_runner(runner): | |
72 | 89 | def test_with_trial(): | ||
73 | 90 | from twisted.scripts import trial | ||
74 | 91 | unittests, doctests = find_tests(parse_sys_argv()) | ||
75 | 92 | sys.argv.extend(unittests) | ||
76 | 93 | trial.run() | ||
77 | 94 | |||
78 | 95 | |||
79 | 96 | def test_with_py_test(): | ||
80 | 97 | import py | ||
81 | 98 | dirname = os.path.dirname(__file__) | ||
82 | 99 | unittests, doctests = find_tests(parse_sys_argv()) | ||
83 | 100 | sys.argv.extend(unittests) | ||
84 | 101 | sys.argv.extend(doctests) | ||
85 | 102 | # For timestamp checking when looping: | ||
86 | 103 | sys.argv.append(os.path.join(os.path.dirname(__file__), "storm/")) | ||
87 | 104 | py.test.cmdline.main() | ||
88 | 105 | |||
89 | 106 | |||
90 | 107 | def test_with_unittest(): | ||
91 | 108 | |||
93 | 109 | usage = "test.py [options] [<test filename>, ...]" | 97 | usage = "test.py [options] [<test filename>, ...]" |
94 | 110 | 98 | ||
95 | 111 | parser = optparse.OptionParser(usage=usage) | 99 | parser = optparse.OptionParser(usage=usage) |
96 | @@ -113,66 +101,25 @@ | |||
97 | 113 | parser.add_option('--verbose', action='store_true') | 101 | parser.add_option('--verbose', action='store_true') |
98 | 114 | opts, args = parser.parse_args() | 102 | opts, args = parser.parse_args() |
99 | 115 | 103 | ||
100 | 116 | runner = unittest.TextTestRunner() | ||
101 | 117 | |||
102 | 118 | if opts.verbose: | 104 | if opts.verbose: |
103 | 119 | runner.verbosity = 2 | 105 | runner.verbosity = 2 |
104 | 120 | 106 | ||
160 | 121 | loader = unittest.TestLoader() | 107 | suite = find_tests(args) |
161 | 122 | 108 | result = runner.run(suite) | |
162 | 123 | unittests, doctests = find_tests(args) | 109 | return not result.wasSuccessful() |
163 | 124 | 110 | ||
164 | 125 | class Summary: | 111 | |
165 | 126 | def __init__(self): | 112 | def test_with_trial(): |
166 | 127 | self.total_failures = 0 | 113 | from twisted.trial.reporter import TreeReporter |
167 | 128 | self.total_errors = 0 | 114 | from twisted.trial.runner import TrialRunner |
168 | 129 | self.total_tests = 0 | 115 | runner = TrialRunner(reporterFactory=TreeReporter, realTimeErrors=True) |
169 | 130 | def __call__(self, tests, failures, errors): | 116 | return test_with_runner(runner) |
170 | 131 | self.total_tests += tests | 117 | |
171 | 132 | self.total_failures += failures | 118 | |
172 | 133 | self.total_errors += errors | 119 | def test_with_unittest(): |
173 | 134 | sys.stderr.write("(tests=%d, failures=%d, errors=%d)\n" % | 120 | runner = unittest.TextTestRunner() |
174 | 135 | (tests, failures, errors)) | 121 | return test_with_runner(runner) |
175 | 136 | 122 | ||
121 | 137 | unittest_summary = Summary() | ||
122 | 138 | doctest_summary = Summary() | ||
123 | 139 | |||
124 | 140 | if unittests: | ||
125 | 141 | sys.stderr.write("Running unittests...\n") | ||
126 | 142 | for relpath in unittests: | ||
127 | 143 | sys.stderr.write("[%s]\n" % relpath) | ||
128 | 144 | modpath = relpath.replace(os.path.sep, '.')[:-3] | ||
129 | 145 | module = __import__(modpath, None, None, [""]) | ||
130 | 146 | test = loader.loadTestsFromModule(module) | ||
131 | 147 | result = runner.run(test) | ||
132 | 148 | unittest_summary(test.countTestCases(), | ||
133 | 149 | len(result.failures), len(result.errors)) | ||
134 | 150 | sys.stderr.write("\n") | ||
135 | 151 | |||
136 | 152 | if doctests: | ||
137 | 153 | sys.stderr.write("Running doctests...\n") | ||
138 | 154 | doctest_flags = doctest.ELLIPSIS | ||
139 | 155 | for relpath in doctests: | ||
140 | 156 | sys.stderr.write("[%s]\n" % relpath) | ||
141 | 157 | failures, total = doctest.testfile(relpath, | ||
142 | 158 | optionflags=doctest_flags) | ||
143 | 159 | doctest_summary(total, failures, 0) | ||
144 | 160 | sys.stderr.write("\n") | ||
145 | 161 | |||
146 | 162 | sys.stderr.write("Total test cases: %d\n" % unittest_summary.total_tests) | ||
147 | 163 | sys.stderr.write("Total doctests: %d\n" % doctest_summary.total_tests) | ||
148 | 164 | sys.stderr.write("Total failures: %d\n" % | ||
149 | 165 | (unittest_summary.total_failures + | ||
150 | 166 | doctest_summary.total_failures)) | ||
151 | 167 | sys.stderr.write("Total errors: %d\n" % (unittest_summary.total_errors + | ||
152 | 168 | doctest_summary.total_errors)) | ||
153 | 169 | |||
154 | 170 | failed = bool(unittest_summary.total_failures or | ||
155 | 171 | unittest_summary.total_errors or | ||
156 | 172 | doctest_summary.total_failures or | ||
157 | 173 | doctest_summary.total_errors) | ||
158 | 174 | |||
159 | 175 | sys.exit(failed) | ||
176 | 176 | 123 | ||
177 | 177 | if __name__ == "__main__": | 124 | if __name__ == "__main__": |
178 | 178 | runner = os.environ.get("STORM_TEST_RUNNER") | 125 | runner = os.environ.get("STORM_TEST_RUNNER") |
179 | @@ -181,6 +128,6 @@ | |||
180 | 181 | runner_func = globals().get("test_with_%s" % runner.replace(".", "_")) | 128 | runner_func = globals().get("test_with_%s" % runner.replace(".", "_")) |
181 | 182 | if not runner_func: | 129 | if not runner_func: |
182 | 183 | sys.exit("Test runner not found: %s" % runner) | 130 | sys.exit("Test runner not found: %s" % runner) |
184 | 184 | runner_func() | 131 | sys.exit(runner_func()) |
185 | 185 | 132 | ||
186 | 186 | # vim:ts=4:sw=4:et | 133 | # vim:ts=4:sw=4:et |
187 | 187 | 134 | ||
188 | === removed file 'tests/conftest.py' | |||
189 | --- tests/conftest.py 2007-07-06 16:37:49 +0000 | |||
190 | +++ tests/conftest.py 1970-01-01 00:00:00 +0000 | |||
191 | @@ -1,133 +0,0 @@ | |||
192 | 1 | # | ||
193 | 2 | # Copyright (c) 2006, 2007 Canonical | ||
194 | 3 | # | ||
195 | 4 | # Written by Gustavo Niemeyer <gustavo@niemeyer.net> | ||
196 | 5 | # | ||
197 | 6 | # This file is part of Storm Object Relational Mapper. | ||
198 | 7 | # | ||
199 | 8 | # Storm is free software; you can redistribute it and/or modify | ||
200 | 9 | # it under the terms of the GNU Lesser General Public License as | ||
201 | 10 | # published by the Free Software Foundation; either version 2.1 of | ||
202 | 11 | # the License, or (at your option) any later version. | ||
203 | 12 | # | ||
204 | 13 | # Storm is distributed in the hope that it will be useful, | ||
205 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
206 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
207 | 16 | # GNU Lesser General Public License for more details. | ||
208 | 17 | # | ||
209 | 18 | # You should have received a copy of the GNU Lesser General Public License | ||
210 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
211 | 20 | # | ||
212 | 21 | """ | ||
213 | 22 | Machinery to make py.test interpret standard unittest.TestCase classes. | ||
214 | 23 | """ | ||
215 | 24 | from unittest import TestCase, TestResult | ||
216 | 25 | import doctest | ||
217 | 26 | import sys | ||
218 | 27 | |||
219 | 28 | import py.test.collect | ||
220 | 29 | import py.test.compat | ||
221 | 30 | import py.test | ||
222 | 31 | |||
223 | 32 | |||
224 | 33 | class PyTestResult(TestResult): | ||
225 | 34 | def addFailure(self, test, exc_info): | ||
226 | 35 | traceback = exc_info[2] | ||
227 | 36 | while traceback.tb_next: | ||
228 | 37 | traceback = traceback.tb_next | ||
229 | 38 | locals = traceback.tb_frame.f_locals | ||
230 | 39 | if "msg" in locals or "excClass" in locals: | ||
231 | 40 | locals["__tracebackhide__"] = True | ||
232 | 41 | msg = str(exc_info[1]) | ||
233 | 42 | if not msg: | ||
234 | 43 | if "expr" in locals and "msg" in locals: | ||
235 | 44 | msg = repr(locals["expr"]) | ||
236 | 45 | else: | ||
237 | 46 | msg = "!?" | ||
238 | 47 | raise py.test.Item.Failed, py.test.Item.Failed(msg=msg), exc_info[2] | ||
239 | 48 | addError = addFailure | ||
240 | 49 | |||
241 | 50 | class PyTestCase(TestCase): | ||
242 | 51 | def __init__(self, methodName="setUp"): | ||
243 | 52 | super(PyTestCase, self).__init__(methodName) | ||
244 | 53 | |||
245 | 54 | class Function(py.test.Function): | ||
246 | 55 | def execute(self, target, *args): | ||
247 | 56 | __tracebackhide__ = True | ||
248 | 57 | self = target.im_self | ||
249 | 58 | self.__init__(target.__name__) | ||
250 | 59 | self.run(PyTestResult()) | ||
251 | 60 | |||
252 | 61 | class PyDocTest(py.test.collect.Module): | ||
253 | 62 | def __init__(self, fspath, parent=None): | ||
254 | 63 | super(PyDocTest, self).__init__(fspath.basename, parent) | ||
255 | 64 | self.fspath = fspath | ||
256 | 65 | self._obj = None | ||
257 | 66 | |||
258 | 67 | def run(self): | ||
259 | 68 | return [self.name] | ||
260 | 69 | |||
261 | 70 | def join(self, name): | ||
262 | 71 | return self.Function(name, parent=self, obj=self.fspath) | ||
263 | 72 | |||
264 | 73 | class Function(py.test.Function): | ||
265 | 74 | def getpathlineno(self): | ||
266 | 75 | code = py.code.Code(self.failed) | ||
267 | 76 | return code.path, code.firstlineno | ||
268 | 77 | |||
269 | 78 | def failed(self, msg): | ||
270 | 79 | raise self.Failed(msg) | ||
271 | 80 | |||
272 | 81 | def execute(self, fspath): | ||
273 | 82 | failures, total = doctest.testfile(str(fspath), | ||
274 | 83 | module_relative=False, | ||
275 | 84 | optionflags=doctest.ELLIPSIS) | ||
276 | 85 | if failures: | ||
277 | 86 | __tracebackhide__ = True | ||
278 | 87 | self.failed("%d doctest cases" % failures) | ||
279 | 88 | |||
280 | 89 | class UnitTestModule(py.test.collect.Module): | ||
281 | 90 | def buildname2items(self): | ||
282 | 91 | d = {} | ||
283 | 92 | for name in dir(self.obj): | ||
284 | 93 | testclass = None | ||
285 | 94 | obj = getattr(self.obj, name) | ||
286 | 95 | |||
287 | 96 | try: | ||
288 | 97 | if issubclass(obj, (TestCase, PyTestCase)): | ||
289 | 98 | testclass = obj | ||
290 | 99 | except TypeError: | ||
291 | 100 | pass | ||
292 | 101 | |||
293 | 102 | if testclass: | ||
294 | 103 | d[name] = self.Class(name, parent=self) | ||
295 | 104 | if not issubclass(testclass, PyTestCase): | ||
296 | 105 | queue = [testclass] | ||
297 | 106 | while queue: | ||
298 | 107 | testclass = queue.pop(0) | ||
299 | 108 | if TestCase in testclass.__bases__: | ||
300 | 109 | bases = list(testclass.__bases__) | ||
301 | 110 | bases[bases.index(TestCase)] = PyTestCase | ||
302 | 111 | testclass.__bases__ = tuple(bases) | ||
303 | 112 | break | ||
304 | 113 | queue.extend(testclass.__bases__) | ||
305 | 114 | return d | ||
306 | 115 | |||
307 | 116 | class UnitTestDirectory(py.test.collect.Directory): | ||
308 | 117 | def __init__(self, *args, **kwargs): | ||
309 | 118 | if getattr(self.__class__, "__first_run__", True): | ||
310 | 119 | self.__class__.__first_run__ = False | ||
311 | 120 | super(UnitTestDirectory, self).__init__(*args, **kwargs) | ||
312 | 121 | |||
313 | 122 | def filefilter(self, path): | ||
314 | 123 | return path.check(fnmatch="*.py") and path.basename != "conftest.py" | ||
315 | 124 | |||
316 | 125 | def makeitem(self, basename, filefilter=None, recfilter=None): | ||
317 | 126 | path = self.fspath.join(basename) | ||
318 | 127 | if path.check(fnmatch="*.txt"): | ||
319 | 128 | return PyDocTest(path, parent=self) | ||
320 | 129 | return super(UnitTestDirectory, self).makeitem(basename, | ||
321 | 130 | filefilter, recfilter) | ||
322 | 131 | |||
323 | 132 | Module = UnitTestModule | ||
324 | 133 | Directory = UnitTestDirectory | ||
325 | 134 | 0 | ||
326 | === modified file 'tests/helper.py' | |||
327 | --- tests/helper.py 2008-10-02 15:47:18 +0000 | |||
328 | +++ tests/helper.py 2009-11-09 03:29:09 +0000 | |||
329 | @@ -67,6 +67,9 @@ | |||
330 | 67 | break | 67 | break |
331 | 68 | is_supported = getattr(self, "is_supported", None) | 68 | is_supported = getattr(self, "is_supported", None) |
332 | 69 | if is_supported is not None and not is_supported(): | 69 | if is_supported is not None and not is_supported(): |
333 | 70 | if hasattr(result, "addSkip"): | ||
334 | 71 | result.startTest(self) | ||
335 | 72 | result.addSkip(self, "Test not supported") | ||
336 | 70 | return | 73 | return |
337 | 71 | unittest.TestCase.run(self, result) | 74 | unittest.TestCase.run(self, result) |
338 | 72 | 75 |
Clean up the test harness a bit.
* Remove support for running the tests under py.test, as it appears to have broken a while back with no one noticing.
* Collect tests in the same way for both the pyunit and trial test runners. This should also allow Trial to handle the doctests.