Merge lp:~allenap/testtools/gather-details into lp:~testtools-committers/testtools/trunk

Proposed by Gavin Panella
Status: Merged
Merged at revision: 190
Proposed branch: lp:~allenap/testtools/gather-details
Merge into: lp:~testtools-committers/testtools/trunk
Diff against target: 126 lines (+79/-19)
2 files modified
testtools/testcase.py (+42/-19)
testtools/tests/test_fixturesupport.py (+37/-0)
To merge this branch: bzr merge lp:~allenap/testtools/gather-details
Reviewer Review Type Date Requested Status
Jonathan Lange Approve
Review via email: mp+62326@code.launchpad.net

Description of the change

This branch does three things:

- Moves TestCase._gather_details() out into a standalone
  function. This is motivated by wanting to use it in python-fixtures
  to gather details from fixtures used by other fixtures.

- Ensures that details are gathered from fixtures even when there is a
  failure in fixture.setUp().

- Fixes a closure bug when gathering more than one detail from a
  fixture. Previously the same content was being reported for all
  details because the variable that content_callback closed over was
  being updated each time round the loop in _gather_details().

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

Wow, thanks!

 * Let's keep copy_content private for now, renaming it to _copy_content.
 * Add gather_details to __all__
 * Tests are good.

I'll merge this with tweaks + NEWS updates.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'testtools/testcase.py'
2--- testtools/testcase.py 2011-05-12 23:01:56 +0000
3+++ testtools/testcase.py 2011-05-25 15:13:25 +0000
4@@ -99,6 +99,39 @@
5 return decorator
6
7
8+def copy_content(content_object):
9+ """Make a copy of the given content object.
10+
11+ The content within `content_object` is iterated and saved. This is useful
12+ when the source of the content is volatile, a log file in a temporary
13+ directory for example.
14+
15+ :param content_object: A `content.Content` instance.
16+ :return: A `content.Content` instance with the same mime-type as
17+ `content_object` and a non-volatile copy of its content.
18+ """
19+ content_bytes = list(content_object.iter_bytes())
20+ content_callback = lambda: content_bytes
21+ return content.Content(content_object.content_type, content_callback)
22+
23+
24+def gather_details(source, target):
25+ """Merge the details from `source` into `target`.
26+
27+ :param source: A *detailed* object from which details will be gathered.
28+ :param target: A *detailed* object into which details will be gathered.
29+ """
30+ source_details = source.getDetails()
31+ target_details = target.getDetails()
32+ for name, content_object in source_details.items():
33+ new_name = name
34+ disambiguator = itertools.count(1)
35+ while new_name in target_details:
36+ new_name = '%s-%d' % (name, advance_iterator(disambiguator))
37+ name = new_name
38+ target.addDetail(name, copy_content(content_object))
39+
40+
41 class TestCase(unittest.TestCase):
42 """Extensions to the basic TestCase.
43
44@@ -514,25 +547,15 @@
45 :return: The fixture, after setting it up and scheduling a cleanup for
46 it.
47 """
48- fixture.setUp()
49- self.addCleanup(fixture.cleanUp)
50- self.addCleanup(self._gather_details, fixture.getDetails)
51- return fixture
52-
53- def _gather_details(self, getDetails):
54- """Merge the details from getDetails() into self.getDetails()."""
55- details = getDetails()
56- my_details = self.getDetails()
57- for name, content_object in details.items():
58- new_name = name
59- disambiguator = itertools.count(1)
60- while new_name in my_details:
61- new_name = '%s-%d' % (name, advance_iterator(disambiguator))
62- name = new_name
63- content_bytes = list(content_object.iter_bytes())
64- content_callback = lambda:content_bytes
65- self.addDetail(name,
66- content.Content(content_object.content_type, content_callback))
67+ try:
68+ fixture.setUp()
69+ except:
70+ gather_details(fixture, self)
71+ raise
72+ else:
73+ self.addCleanup(fixture.cleanUp)
74+ self.addCleanup(gather_details, fixture, self)
75+ return fixture
76
77 def setUp(self):
78 super(TestCase, self).setUp()
79
80=== modified file 'testtools/tests/test_fixturesupport.py'
81--- testtools/tests/test_fixturesupport.py 2011-01-22 17:56:00 +0000
82+++ testtools/tests/test_fixturesupport.py 2011-05-25 15:13:25 +0000
83@@ -73,6 +73,43 @@
84 self.assertEqual('content available until cleanUp',
85 ''.join(details['content-1'].iter_text()))
86
87+ def test_useFixture_multiple_details_captured(self):
88+ class DetailsFixture(fixtures.Fixture):
89+ def setUp(self):
90+ fixtures.Fixture.setUp(self)
91+ self.addDetail('aaa', content.text_content("foo"))
92+ self.addDetail('bbb', content.text_content("bar"))
93+ fixture = DetailsFixture()
94+ class SimpleTest(TestCase):
95+ def test_foo(self):
96+ self.useFixture(fixture)
97+ result = ExtendedTestResult()
98+ SimpleTest('test_foo').run(result)
99+ self.assertEqual('addSuccess', result._events[-2][0])
100+ details = result._events[-2][2]
101+ self.assertEqual(['aaa', 'bbb'], sorted(details))
102+ self.assertEqual('foo', ''.join(details['aaa'].iter_text()))
103+ self.assertEqual('bar', ''.join(details['bbb'].iter_text()))
104+
105+ def test_useFixture_details_captured_from_setUp(self):
106+ # Details added during fixture set-up are gathered even if setUp()
107+ # fails with an exception.
108+ class BrokenFixture(fixtures.Fixture):
109+ def setUp(self):
110+ fixtures.Fixture.setUp(self)
111+ self.addDetail('content', content.text_content("foobar"))
112+ raise Exception()
113+ fixture = BrokenFixture()
114+ class SimpleTest(TestCase):
115+ def test_foo(self):
116+ self.useFixture(fixture)
117+ result = ExtendedTestResult()
118+ SimpleTest('test_foo').run(result)
119+ self.assertEqual('addError', result._events[-2][0])
120+ details = result._events[-2][2]
121+ self.assertEqual(['content', 'traceback'], sorted(details))
122+ self.assertEqual('foobar', ''.join(details['content'].iter_text()))
123+
124
125 def test_suite():
126 from unittest import TestLoader

Subscribers

People subscribed via source and target branches