2 # subunit: extensions to Python unittest to get test results from subprocesses.
3 # Copyright (C) 2009 Robert Collins <robertc@robertcollins.net>
5 # Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
6 # license at the users choice. A copy of both licenses are available in the
7 # project source as Apache-2.0 and BSD. You may not use this file except in
8 # compliance with one of these two licences.
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # license you chose for the specific language governing permissions and
14 # limitations under that license.
20 from testtools import TestCase
21 from testtools.testresult.doubles import ExtendedTestResult
24 import subunit.iso8601 as iso8601
25 import subunit.test_results
28 class LoggingDecorator(subunit.test_results.HookedTestResultDecorator):
30 def __init__(self, decorated):
32 super(LoggingDecorator, self).__init__(decorated)
34 def _before_event(self):
38 class AssertBeforeTestResult(LoggingDecorator):
39 """A TestResult for checking preconditions."""
41 def __init__(self, decorated, test):
43 super(AssertBeforeTestResult, self).__init__(decorated)
45 def _before_event(self):
46 self.test.assertEqual(1, self.earlier._calls)
47 super(AssertBeforeTestResult, self)._before_event()
50 class TimeCapturingResult(unittest.TestResult):
53 super(TimeCapturingResult, self).__init__()
56 def time(self, a_datetime):
57 self._calls.append(a_datetime)
60 class TestHookedTestResultDecorator(unittest.TestCase):
64 terminal = unittest.TestResult()
65 # Asserts that the call was made to self.result before asserter was
67 asserter = AssertBeforeTestResult(terminal, self)
68 # The result object we call, which much increase its call count.
69 self.result = LoggingDecorator(asserter)
70 asserter.earlier = self.result
71 self.decorated = asserter
74 # The hook in self.result must have been called
75 self.assertEqual(1, self.result._calls)
76 # The hook in asserter must have been called too, otherwise the
77 # assertion about ordering won't have completed.
78 self.assertEqual(1, self.decorated._calls)
80 def test_startTest(self):
81 self.result.startTest(self)
83 def test_startTestRun(self):
84 self.result.startTestRun()
86 def test_stopTest(self):
87 self.result.stopTest(self)
89 def test_stopTestRun(self):
90 self.result.stopTestRun()
92 def test_addError(self):
93 self.result.addError(self, subunit.RemoteError())
95 def test_addError_details(self):
96 self.result.addError(self, details={})
98 def test_addFailure(self):
99 self.result.addFailure(self, subunit.RemoteError())
101 def test_addFailure_details(self):
102 self.result.addFailure(self, details={})
104 def test_addSuccess(self):
105 self.result.addSuccess(self)
107 def test_addSuccess_details(self):
108 self.result.addSuccess(self, details={})
110 def test_addSkip(self):
111 self.result.addSkip(self, "foo")
113 def test_addSkip_details(self):
114 self.result.addSkip(self, details={})
116 def test_addExpectedFailure(self):
117 self.result.addExpectedFailure(self, subunit.RemoteError())
119 def test_addExpectedFailure_details(self):
120 self.result.addExpectedFailure(self, details={})
122 def test_addUnexpectedSuccess(self):
123 self.result.addUnexpectedSuccess(self)
125 def test_addUnexpectedSuccess_details(self):
126 self.result.addUnexpectedSuccess(self, details={})
128 def test_progress(self):
129 self.result.progress(1, subunit.PROGRESS_SET)
131 def test_wasSuccessful(self):
132 self.result.wasSuccessful()
134 def test_shouldStop(self):
135 self.result.shouldStop
141 self.result.time(None)
144 class TestAutoTimingTestResultDecorator(unittest.TestCase):
147 # And end to the chain which captures time events.
148 terminal = TimeCapturingResult()
149 # The result object under test.
150 self.result = subunit.test_results.AutoTimingTestResultDecorator(
152 self.decorated = terminal
154 def test_without_time_calls_time_is_called_and_not_None(self):
155 self.result.startTest(self)
156 self.assertEqual(1, len(self.decorated._calls))
157 self.assertNotEqual(None, self.decorated._calls[0])
159 def test_no_time_from_progress(self):
160 self.result.progress(1, subunit.PROGRESS_CUR)
161 self.assertEqual(0, len(self.decorated._calls))
163 def test_no_time_from_shouldStop(self):
164 self.decorated.stop()
165 self.result.shouldStop
166 self.assertEqual(0, len(self.decorated._calls))
168 def test_calling_time_inhibits_automatic_time(self):
169 # Calling time() outputs a time signal immediately and prevents
170 # automatically adding one when other methods are called.
171 time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.Utc())
172 self.result.time(time)
173 self.result.startTest(self)
174 self.result.stopTest(self)
175 self.assertEqual(1, len(self.decorated._calls))
176 self.assertEqual(time, self.decorated._calls[0])
178 def test_calling_time_None_enables_automatic_time(self):
179 time = datetime.datetime(2009,10,11,12,13,14,15, iso8601.Utc())
180 self.result.time(time)
181 self.assertEqual(1, len(self.decorated._calls))
182 self.assertEqual(time, self.decorated._calls[0])
183 # Calling None passes the None through, in case other results care.
184 self.result.time(None)
185 self.assertEqual(2, len(self.decorated._calls))
186 self.assertEqual(None, self.decorated._calls[1])
187 # Calling other methods doesn't generate an automatic time event.
188 self.result.startTest(self)
189 self.assertEqual(3, len(self.decorated._calls))
190 self.assertNotEqual(None, self.decorated._calls[2])
193 class TestTagCollapsingDecorator(TestCase):
195 def test_tags_forwarded_outside_of_tests(self):
196 result = ExtendedTestResult()
197 tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
198 tag_collapser.tags(set(['a', 'b']), set())
200 [('tags', set(['a', 'b']), set([]))], result._events)
202 def test_tags_collapsed_inside_of_tests(self):
203 result = ExtendedTestResult()
204 tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
205 test = subunit.RemotedTestCase('foo')
206 tag_collapser.startTest(test)
207 tag_collapser.tags(set(['a']), set())
208 tag_collapser.tags(set(['b']), set(['a']))
209 tag_collapser.tags(set(['c']), set())
210 tag_collapser.stopTest(test)
212 [('startTest', test),
213 ('tags', set(['b', 'c']), set(['a'])),
217 def test_tags_collapsed_inside_of_tests_different_ordering(self):
218 result = ExtendedTestResult()
219 tag_collapser = subunit.test_results.TagCollapsingDecorator(result)
220 test = subunit.RemotedTestCase('foo')
221 tag_collapser.startTest(test)
222 tag_collapser.tags(set(), set(['a']))
223 tag_collapser.tags(set(['a', 'b']), set())
224 tag_collapser.tags(set(['c']), set())
225 tag_collapser.stopTest(test)
227 [('startTest', test),
228 ('tags', set(['a', 'b', 'c']), set()),
233 class TestTimeCollapsingDecorator(TestCase):
237 return datetime.datetime(
238 2000, 1, self.getUniqueInteger(), tzinfo=iso8601.UTC)
240 def test_initial_time_forwarded(self):
241 # We always forward the first time event we see.
242 result = ExtendedTestResult()
243 tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
244 a_time = self.make_time()
245 tag_collapser.time(a_time)
246 self.assertEquals([('time', a_time)], result._events)
248 def test_time_collapsed_to_first_and_last(self):
249 # If there are many consecutive time events, only the first and last
251 result = ExtendedTestResult()
252 tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
253 times = [self.make_time() for i in range(5)]
255 tag_collapser.time(a_time)
256 tag_collapser.startTest(subunit.RemotedTestCase('foo'))
258 [('time', times[0]), ('time', times[-1])], result._events[:-1])
260 def test_only_one_time_sent(self):
261 # If we receive a single time event followed by a non-time event, we
262 # send exactly one time event.
263 result = ExtendedTestResult()
264 tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
265 a_time = self.make_time()
266 tag_collapser.time(a_time)
267 tag_collapser.startTest(subunit.RemotedTestCase('foo'))
268 self.assertEquals([('time', a_time)], result._events[:-1])
270 def test_duplicate_times_not_sent(self):
271 # Many time events with the exact same time are collapsed into one
273 result = ExtendedTestResult()
274 tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
275 a_time = self.make_time()
277 tag_collapser.time(a_time)
278 tag_collapser.startTest(subunit.RemotedTestCase('foo'))
279 self.assertEquals([('time', a_time)], result._events[:-1])
281 def test_no_times_inserted(self):
282 result = ExtendedTestResult()
283 tag_collapser = subunit.test_results.TimeCollapsingDecorator(result)
284 a_time = self.make_time()
285 tag_collapser.time(a_time)
286 foo = subunit.RemotedTestCase('foo')
287 tag_collapser.startTest(foo)
288 tag_collapser.addSuccess(foo)
289 tag_collapser.stopTest(foo)
294 ('stopTest', foo)], result._events)
298 loader = subunit.tests.TestUtil.TestLoader()
299 result = loader.loadTestsFromName(__name__)