Merge lp:~lifeless/testtools/listtests into lp:~testtools-committers/testtools/trunk

Proposed by Robert Collins on 2010-11-29
Status: Merged
Merged at revision: 143
Proposed branch: lp:~lifeless/testtools/listtests
Merge into: lp:~testtools-committers/testtools/trunk
Diff against target: 256 lines (+84/-20)
4 files modified
NEWS (+9/-4)
testtools/run.py (+30/-12)
testtools/tests/__init__.py (+3/-4)
testtools/tests/test_run.py (+42/-0)
To merge this branch: bzr merge lp:~lifeless/testtools/listtests
Reviewer Review Type Date Requested Status
testtools developers 2010-11-29 Pending
Review via email: mp+42166@code.launchpad.net

Description of the Change

Support test listing. Not tested with discover, but I expect any damage is going to be minimal.

To post a comment you must log in.
lp:~lifeless/testtools/listtests updated on 2010-11-29
144. By Robert Collins on 2010-11-29

Forgot to actually assert that list worked.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-11-29 00:32:53 +0000
3+++ NEWS 2010-11-29 21:20:36 +0000
4@@ -42,6 +42,11 @@
5 * ``MatchesException`` added to the ``testtools.matchers`` module - matches
6 an exception class and parameters. (Robert Collins)
7
8+* New ``KeysEqual`` matcher. (Jonathan Lange)
9+
10+* New helpers for conditionally importing modules, ``try_import`` and
11+ ``try_imports``. (Jonathan Lange)
12+
13 * ``Raises`` added to the ``testtools.matchers`` module - matches if the
14 supplied callable raises, and delegates to an optional matcher for validation
15 of the exception. (Robert Collins)
16@@ -53,16 +58,16 @@
17 * ``testools.TestCase.useFixture`` has been added to glue with fixtures nicely.
18 (Robert Collins)
19
20+* ``testtools.run`` now supports ``-l`` to list tests rather than executing
21+ them. This is useful for integration with external test analysis/processing
22+ tools like subunit and testrepository. (Robert Collins)
23+
24 * Update documentation to say how to use testtools.run() on Python 2.4.
25 (Jonathan Lange, #501174)
26
27 * ``text_content`` conveniently converts a Python string to a Content object.
28 (Jonathan Lange, James Westby)
29
30-* New ``KeysEqual`` matcher. (Jonathan Lange)
31-
32-* New helpers for conditionally importing modules, ``try_import`` and
33- ``try_imports``. (Jonathan Lange)
34
35
36 0.9.7
37
38=== modified file 'testtools/run.py'
39--- testtools/run.py 2010-08-15 23:18:59 +0000
40+++ testtools/run.py 2010-11-29 21:20:36 +0000
41@@ -14,6 +14,7 @@
42
43 from testtools import TextTestResult
44 from testtools.compat import classtypes, istext, unicode_output_stream
45+from testtools.testsuite import iterate_tests
46
47
48 defaultTestLoader = unittest.defaultTestLoader
49@@ -34,9 +35,12 @@
50 class TestToolsTestRunner(object):
51 """ A thunk object to support unittest.TestProgram."""
52
53+ def __init__(self, stdout):
54+ self.stdout = stdout
55+
56 def run(self, test):
57 "Run the given test case or test suite."
58- result = TextTestResult(unicode_output_stream(sys.stdout))
59+ result = TextTestResult(unicode_output_stream(self.stdout))
60 result.startTestRun()
61 try:
62 return test.run(result)
63@@ -70,6 +74,7 @@
64 -h, --help Show this message
65 -v, --verbose Verbose output
66 -q, --quiet Minimal output
67+ -l, --list List tests rather than executing them.
68 %(failfast)s%(catchbreak)s%(buffer)s
69 Examples:
70 %(progName)s test_module - run tests from test_module
71@@ -87,6 +92,7 @@
72 -p pattern Pattern to match test files ('test*.py' default)
73 -t directory Top level directory of project (default to
74 start directory)
75+ -l, --list List tests rather than executing them.
76
77 For test discovery all test modules must be importable from the top
78 level directory of the project.
79@@ -102,11 +108,13 @@
80 # defaults for testing
81 failfast = catchbreak = buffer = progName = None
82
83- def __init__(self, module='__main__', defaultTest=None, argv=None,
84+ def __init__(self, module=__name__, defaultTest=None, argv=None,
85 testRunner=None, testLoader=defaultTestLoader,
86 exit=True, verbosity=1, failfast=None, catchbreak=None,
87- buffer=None):
88- if istext(module):
89+ buffer=None, stdout=None):
90+ if module == __name__:
91+ self.module = None
92+ elif istext(module):
93 self.module = __import__(module)
94 for part in module.split('.')[1:]:
95 self.module = getattr(self.module, part)
96@@ -121,6 +129,7 @@
97 self.verbosity = verbosity
98 self.buffer = buffer
99 self.defaultTest = defaultTest
100+ self.listtests = False
101 self.testRunner = testRunner
102 self.testLoader = testLoader
103 progName = argv[0]
104@@ -131,7 +140,11 @@
105 progName = os.path.basename(argv[0])
106 self.progName = progName
107 self.parseArgs(argv)
108- self.runTests()
109+ if not self.listtests:
110+ self.runTests()
111+ else:
112+ for test in iterate_tests(self.test):
113+ stdout.write('%s\n' % test.id())
114
115 def usageExit(self, msg=None):
116 if msg:
117@@ -153,9 +166,10 @@
118 return
119
120 import getopt
121- long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer']
122+ long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer',
123+ 'list']
124 try:
125- options, args = getopt.getopt(argv[1:], 'hHvqfcb', long_opts)
126+ options, args = getopt.getopt(argv[1:], 'hHvqfcbl', long_opts)
127 for opt, value in options:
128 if opt in ('-h','-H','--help'):
129 self.usageExit()
130@@ -175,14 +189,13 @@
131 if self.buffer is None:
132 self.buffer = True
133 # Should this raise an exception if -b is not valid?
134+ if opt in ('-l', '--list'):
135+ self.listtests = True
136 if len(args) == 0 and self.defaultTest is None:
137 # createTests will load tests from self.module
138 self.testNames = None
139 elif len(args) > 0:
140 self.testNames = args
141- if __name__ == '__main__':
142- # to support python -m unittest ...
143- self.module = None
144 else:
145 self.testNames = (self.defaultTest,)
146 self.createTests()
147@@ -225,6 +238,8 @@
148 help="Pattern to match tests ('test*.py' default)")
149 parser.add_option('-t', '--top-level-directory', dest='top', default=None,
150 help='Top level directory of project (defaults to start directory)')
151+ parser.add_option('-l', '--list', dest='listtests', default=False,
152+ help='List tests rather than running them.')
153
154 options, args = parser.parse_args(argv)
155 if len(args) > 3:
156@@ -241,6 +256,7 @@
157 self.catchbreak = options.catchbreak
158 if self.buffer is None:
159 self.buffer = options.buffer
160+ self.listtests = options.listtests
161
162 if options.verbose:
163 self.verbosity = 2
164@@ -274,7 +290,9 @@
165 sys.exit(not self.result.wasSuccessful())
166 ################
167
168+def main(argv, stdout):
169+ runner = TestToolsTestRunner(stdout)
170+ program = TestProgram(argv=argv, testRunner=runner, stdout=stdout)
171
172 if __name__ == '__main__':
173- runner = TestToolsTestRunner()
174- program = TestProgram(argv=sys.argv, testRunner=runner)
175+ main(sys.argv, sys.stdout)
176
177=== modified file 'testtools/tests/__init__.py'
178--- testtools/tests/__init__.py 2010-11-27 11:25:12 +0000
179+++ testtools/tests/__init__.py 2010-11-29 21:20:36 +0000
180@@ -15,13 +15,13 @@
181 test_helpers,
182 test_matchers,
183 test_monkey,
184+ test_run,
185 test_runtest,
186 test_spinner,
187 test_testtools,
188 test_testresult,
189 test_testsuite,
190 )
191- suites = []
192 modules = [
193 test_compat,
194 test_content,
195@@ -31,12 +31,11 @@
196 test_helpers,
197 test_matchers,
198 test_monkey,
199- test_runtest,
200+ test_run,
201 test_spinner,
202 test_testresult,
203 test_testsuite,
204 test_testtools,
205 ]
206- for module in modules:
207- suites.append(getattr(module, 'test_suite')())
208+ suites = map(lambda x:x.test_suite(), modules)
209 return unittest.TestSuite(suites)
210
211=== added file 'testtools/tests/test_run.py'
212--- testtools/tests/test_run.py 1970-01-01 00:00:00 +0000
213+++ testtools/tests/test_run.py 2010-11-29 21:20:36 +0000
214@@ -0,0 +1,42 @@
215+# Copyright (c) 2010 Testtools authors. See LICENSE for details.
216+
217+"""Tests for the test runner logic."""
218+
219+import StringIO
220+
221+from testtools.helpers import try_import
222+fixtures = try_import('fixtures')
223+
224+import testtools
225+from testtools import TestCase, run
226+
227+
228+class TestRun(TestCase):
229+
230+ def test_run_list(self):
231+ if fixtures is None:
232+ self.skipTest("Need fixtures")
233+ package = self.useFixture(fixtures.PythonPackage(
234+ 'runexample', [('__init__.py', """
235+from testtools import TestCase
236+
237+class TestFoo(TestCase):
238+ def test_bar(self):
239+ pass
240+ def test_quux(self):
241+ pass
242+def test_suite():
243+ from unittest import TestLoader
244+ return TestLoader().loadTestsFromName(__name__)
245+""")]))
246+ testtools.__path__.append(package.base)
247+ self.addCleanup(testtools.__path__.remove, package.base)
248+ out = StringIO.StringIO()
249+ run.main(['-l', 'testtools.runexample.test_suite'], out)
250+ self.assertEqual("""testtools.runexample.TestFoo.test_bar
251+testtools.runexample.TestFoo.test_quux
252+""", out.getvalue())
253+
254+def test_suite():
255+ from unittest import TestLoader
256+ return TestLoader().loadTestsFromName(__name__)

Subscribers

People subscribed via source and target branches