|
6 | 6 | import signal
|
7 | 7 | from typing import Any, ClassVar
|
8 | 8 |
|
| 9 | +from testscenarios import multiply_scenarios |
| 10 | + |
9 | 11 | from testtools import (
|
10 | 12 | TestCase,
|
11 | 13 | TestResult,
|
|
83 | 85 | _get_global_publisher_and_observers = None # type: ignore[assignment]
|
84 | 86 |
|
85 | 87 |
|
86 |
| -class X: |
87 |
| - """Tests that we run as part of our tests, nested to avoid discovery.""" |
| 88 | +# Flattened test classes to avoid PyPy compilation crash with nested classes |
| 89 | +# Prefixed with _ to avoid pytest discovery |
| 90 | + |
| 91 | + |
| 92 | +class _XBase(TestCase): |
| 93 | + def setUp(self): |
| 94 | + super().setUp() |
| 95 | + self.calls = ["setUp"] |
| 96 | + self.addCleanup(self.calls.append, "clean-up") |
| 97 | + |
| 98 | + def test_something(self): |
| 99 | + self.calls.append("test") |
| 100 | + |
| 101 | + def tearDown(self): |
| 102 | + self.calls.append("tearDown") |
| 103 | + super().tearDown() |
| 104 | + |
88 | 105 |
|
89 |
| - # XXX: After testing-cabal/testtools#165 lands, fix up all of these to be |
90 |
| - # scenario tests for RunTest. |
| 106 | +class _XBaseExceptionRaised(_XBase): |
| 107 | + expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] |
| 108 | + expected_results: ClassVar[list] = [("addError", SystemExit)] |
91 | 109 |
|
92 |
| - class Base(TestCase): |
93 |
| - def setUp(self): |
94 |
| - super(X.Base, self).setUp() |
95 |
| - self.calls = ["setUp"] |
96 |
| - self.addCleanup(self.calls.append, "clean-up") |
| 110 | + def test_something(self): |
| 111 | + raise SystemExit(0) |
97 | 112 |
|
98 |
| - def test_something(self): |
99 |
| - self.calls.append("test") |
100 | 113 |
|
101 |
| - def tearDown(self): |
102 |
| - self.calls.append("tearDown") |
103 |
| - super(X.Base, self).tearDown() |
| 114 | +class _XErrorInSetup(_XBase): |
| 115 | + expected_calls: ClassVar[list] = ["setUp", "clean-up"] |
| 116 | + expected_results: ClassVar[list] = [("addError", RuntimeError)] |
104 | 117 |
|
105 |
| - class BaseExceptionRaised(Base): |
106 |
| - expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] |
107 |
| - expected_results: ClassVar[list] = [("addError", SystemExit)] |
| 118 | + def setUp(self): |
| 119 | + super().setUp() |
| 120 | + raise RuntimeError("Error in setUp") |
108 | 121 |
|
109 |
| - def test_something(self): |
110 |
| - raise SystemExit(0) |
111 | 122 |
|
112 |
| - class ErrorInSetup(Base): |
113 |
| - expected_calls: ClassVar[list] = ["setUp", "clean-up"] |
114 |
| - expected_results: ClassVar[list] = [("addError", RuntimeError)] |
| 123 | +class _XErrorInTest(_XBase): |
| 124 | + expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] |
| 125 | + expected_results: ClassVar[list] = [("addError", RuntimeError)] |
115 | 126 |
|
116 |
| - def setUp(self): |
117 |
| - super(X.ErrorInSetup, self).setUp() |
118 |
| - raise RuntimeError("Error in setUp") |
| 127 | + def test_something(self): |
| 128 | + raise RuntimeError("Error in test") |
119 | 129 |
|
120 |
| - class ErrorInTest(Base): |
121 |
| - expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] |
122 |
| - expected_results: ClassVar[list] = [("addError", RuntimeError)] |
123 | 130 |
|
124 |
| - def test_something(self): |
125 |
| - raise RuntimeError("Error in test") |
| 131 | +class _XFailureInTest(_XBase): |
| 132 | + expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] |
| 133 | + expected_results: ClassVar[list] = [("addFailure", AssertionError)] |
126 | 134 |
|
127 |
| - class FailureInTest(Base): |
128 |
| - expected_calls: ClassVar[list] = ["setUp", "tearDown", "clean-up"] |
129 |
| - expected_results: ClassVar[list] = [("addFailure", AssertionError)] |
| 135 | + def test_something(self): |
| 136 | + self.fail("test failed") |
130 | 137 |
|
131 |
| - def test_something(self): |
132 |
| - self.fail("test failed") |
133 | 138 |
|
134 |
| - class ErrorInTearDown(Base): |
135 |
| - expected_calls: ClassVar[list] = ["setUp", "test", "clean-up"] |
136 |
| - expected_results: ClassVar[list] = [("addError", RuntimeError)] |
| 139 | +class _XErrorInTearDown(_XBase): |
| 140 | + expected_calls: ClassVar[list] = ["setUp", "test", "clean-up"] |
| 141 | + expected_results: ClassVar[list] = [("addError", RuntimeError)] |
137 | 142 |
|
138 |
| - def tearDown(self): |
139 |
| - raise RuntimeError("Error in tearDown") |
| 143 | + def tearDown(self): |
| 144 | + raise RuntimeError("Error in tearDown") |
140 | 145 |
|
141 |
| - class ErrorInCleanup(Base): |
142 |
| - expected_calls: ClassVar[list] = ["setUp", "test", "tearDown", "clean-up"] |
143 |
| - expected_results: ClassVar[list] = [("addError", ZeroDivisionError)] |
144 | 146 |
|
145 |
| - def test_something(self): |
146 |
| - self.calls.append("test") |
147 |
| - self.addCleanup(lambda: 1 / 0) |
| 147 | +class _XErrorInCleanup(_XBase): |
| 148 | + expected_calls: ClassVar[list] = ["setUp", "test", "tearDown", "clean-up"] |
| 149 | + expected_results: ClassVar[list] = [("addError", ZeroDivisionError)] |
148 | 150 |
|
149 |
| - class ExpectThatFailure(Base): |
150 |
| - """Calling expectThat with a failing match fails the test.""" |
| 151 | + def test_something(self): |
| 152 | + self.calls.append("test") |
| 153 | + self.addCleanup(lambda: 1 / 0) |
151 | 154 |
|
152 |
| - expected_calls: ClassVar[list] = ["setUp", "test", "tearDown", "clean-up"] |
153 |
| - expected_results: ClassVar[list] = [("addFailure", AssertionError)] |
154 | 155 |
|
155 |
| - def test_something(self): |
156 |
| - self.calls.append("test") |
157 |
| - self.expectThat(object(), Is(object())) |
| 156 | +class _XExpectThatFailure(_XBase): |
| 157 | + """Calling expectThat with a failing match fails the test.""" |
158 | 158 |
|
159 |
| - class TestIntegration(NeedsTwistedTestCase): |
160 |
| - # These attributes are set dynamically in test generation |
161 |
| - test_factory: Any = None |
162 |
| - runner: Any = None |
| 159 | + expected_calls: ClassVar[list] = ["setUp", "test", "tearDown", "clean-up"] |
| 160 | + expected_results: ClassVar[list] = [("addFailure", AssertionError)] |
163 | 161 |
|
164 |
| - def assertResultsMatch(self, test, result): |
165 |
| - events = list(result._events) |
166 |
| - self.assertEqual(("startTest", test), events.pop(0)) |
167 |
| - for expected_result in test.expected_results: |
168 |
| - result = events.pop(0) |
169 |
| - if len(expected_result) == 1: |
170 |
| - self.assertEqual((expected_result[0], test), result) |
171 |
| - else: |
172 |
| - self.assertEqual((expected_result[0], test), result[:2]) |
173 |
| - error_type = expected_result[1] |
174 |
| - self.assertIn(error_type.__name__, str(result[2])) |
175 |
| - self.assertEqual([("stopTest", test)], events) |
| 162 | + def test_something(self): |
| 163 | + self.calls.append("test") |
| 164 | + self.expectThat(object(), Is(object())) |
176 | 165 |
|
177 |
| - def test_runner(self): |
178 |
| - result = ExtendedTestResult() |
179 |
| - test = self.test_factory("test_something", runTest=self.runner) |
180 |
| - if self.test_factory is X.BaseExceptionRaised: |
181 |
| - self.assertRaises(SystemExit, test.run, result) |
| 166 | + |
| 167 | +class _XTestIntegration(NeedsTwistedTestCase): |
| 168 | + # These attributes are set dynamically in test generation |
| 169 | + test_factory: Any = None |
| 170 | + runner: Any = None |
| 171 | + |
| 172 | + def assertResultsMatch(self, test, result): |
| 173 | + events = list(result._events) |
| 174 | + self.assertEqual(("startTest", test), events.pop(0)) |
| 175 | + for expected_result in test.expected_results: |
| 176 | + result = events.pop(0) |
| 177 | + if len(expected_result) == 1: |
| 178 | + self.assertEqual((expected_result[0], test), result) |
182 | 179 | else:
|
183 |
| - test.run(result) |
184 |
| - self.assertEqual(test.calls, self.test_factory.expected_calls) |
185 |
| - self.assertResultsMatch(test, result) |
186 |
| - |
187 |
| - |
188 |
| -def make_integration_tests(): |
189 |
| - from unittest import TestSuite |
190 |
| - |
191 |
| - from testtools import clone_test_with_new_id |
192 |
| - |
193 |
| - runners = [ |
194 |
| - ("RunTest", RunTest), |
195 |
| - ("SynchronousDeferredRunTest", SynchronousDeferredRunTest), |
196 |
| - ("AsynchronousDeferredRunTest", AsynchronousDeferredRunTest), |
197 |
| - ] |
198 |
| - |
199 |
| - tests = [ |
200 |
| - X.BaseExceptionRaised, |
201 |
| - X.ErrorInSetup, |
202 |
| - X.ErrorInTest, |
203 |
| - X.ErrorInTearDown, |
204 |
| - X.FailureInTest, |
205 |
| - X.ErrorInCleanup, |
206 |
| - X.ExpectThatFailure, |
207 |
| - ] |
208 |
| - base_test = X.TestIntegration("test_runner") |
209 |
| - integration_tests = [] |
210 |
| - for runner_name, runner in runners: |
211 |
| - for test in tests: |
212 |
| - new_test = clone_test_with_new_id( |
213 |
| - base_test, |
214 |
| - f"{base_test.id()}({runner_name}, {test.__name__})", |
215 |
| - ) |
216 |
| - new_test.test_factory = test |
217 |
| - new_test.runner = runner |
218 |
| - integration_tests.append(new_test) |
219 |
| - return TestSuite(integration_tests) |
| 180 | + self.assertEqual((expected_result[0], test), result[:2]) |
| 181 | + error_type = expected_result[1] |
| 182 | + self.assertIn(error_type.__name__, str(result[2])) |
| 183 | + self.assertEqual([("stopTest", test)], events) |
| 184 | + |
| 185 | + def test_runner(self): |
| 186 | + result = ExtendedTestResult() |
| 187 | + test = self.test_factory("test_something", runTest=self.runner) |
| 188 | + if self.test_factory is _XBaseExceptionRaised: |
| 189 | + self.assertRaises(SystemExit, test.run, result) |
| 190 | + else: |
| 191 | + test.run(result) |
| 192 | + self.assertEqual(test.calls, self.test_factory.expected_calls) |
| 193 | + self.assertResultsMatch(test, result) |
| 194 | + |
| 195 | + |
| 196 | +class TestRunTestIntegration(NeedsTwistedTestCase): |
| 197 | + """Integration tests for different runner and test case combinations.""" |
| 198 | + |
| 199 | + # These attributes are provided by testscenarios |
| 200 | + runner: Any |
| 201 | + test_factory: Any |
| 202 | + |
| 203 | + scenarios = multiply_scenarios( |
| 204 | + [ # Runner scenarios |
| 205 | + ("RunTest", {"runner": RunTest}), |
| 206 | + ("SynchronousDeferredRunTest", {"runner": SynchronousDeferredRunTest}), |
| 207 | + ("AsynchronousDeferredRunTest", {"runner": AsynchronousDeferredRunTest}), |
| 208 | + ], |
| 209 | + [ # Test case scenarios |
| 210 | + ("BaseExceptionRaised", {"test_factory": _XBaseExceptionRaised}), |
| 211 | + ("ErrorInSetup", {"test_factory": _XErrorInSetup}), |
| 212 | + ("ErrorInTest", {"test_factory": _XErrorInTest}), |
| 213 | + ("ErrorInTearDown", {"test_factory": _XErrorInTearDown}), |
| 214 | + ("FailureInTest", {"test_factory": _XFailureInTest}), |
| 215 | + ("ErrorInCleanup", {"test_factory": _XErrorInCleanup}), |
| 216 | + ("ExpectThatFailure", {"test_factory": _XExpectThatFailure}), |
| 217 | + ], |
| 218 | + ) |
| 219 | + |
| 220 | + def assertResultsMatch(self, test, result): |
| 221 | + events = list(result._events) |
| 222 | + self.assertEqual(("startTest", test), events.pop(0)) |
| 223 | + for expected_result in test.expected_results: |
| 224 | + result = events.pop(0) |
| 225 | + if len(expected_result) == 1: |
| 226 | + self.assertEqual((expected_result[0], test), result) |
| 227 | + else: |
| 228 | + self.assertEqual((expected_result[0], test), result[:2]) |
| 229 | + error_type = expected_result[1] |
| 230 | + self.assertIn(error_type.__name__, str(result[2])) |
| 231 | + self.assertEqual([("stopTest", test)], events) |
| 232 | + |
| 233 | + def test_runner(self): |
| 234 | + """Test that each runner handles each test case scenario correctly.""" |
| 235 | + result = ExtendedTestResult() |
| 236 | + test = self.test_factory("test_something", runTest=self.runner) |
| 237 | + if self.test_factory is _XBaseExceptionRaised: |
| 238 | + self.assertRaises(SystemExit, test.run, result) |
| 239 | + else: |
| 240 | + test.run(result) |
| 241 | + self.assertEqual(test.calls, self.test_factory.expected_calls) |
| 242 | + self.assertResultsMatch(test, result) |
220 | 243 |
|
221 | 244 |
|
222 | 245 | class TestSynchronousDeferredRunTest(NeedsTwistedTestCase):
|
@@ -1142,5 +1165,6 @@ def test_suite():
|
1142 | 1165 |
|
1143 | 1166 |
|
1144 | 1167 | def load_tests(loader, tests, pattern):
|
1145 |
| - tests.addTest(make_integration_tests()) |
1146 |
| - return tests |
| 1168 | + from testscenarios import load_tests_apply_scenarios |
| 1169 | + |
| 1170 | + return load_tests_apply_scenarios(loader, tests, pattern) |
0 commit comments