~percona-core/percona-server/5.5

« back to all changes in this revision

Viewing changes to python-for-subunit2junitxml/testtools/run.py

  • Committer: Stewart Smith
  • Date: 2011-10-05 06:14:21 UTC
  • mto: This revision was merged to the branch mainline in revision 177.
  • Revision ID: stewart@flamingspork.com-20111005061421-fp6t1wuey0a8iw1a
add subunit2junitxml and needed libraries

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2009 testtools developers. See LICENSE for details.
 
2
 
 
3
"""python -m testtools.run testspec [testspec...]
 
4
 
 
5
Run some tests with the testtools extended API.
 
6
 
 
7
For instance, to run the testtools test suite.
 
8
 $ python -m testtools.run testtools.tests.test_suite
 
9
"""
 
10
 
 
11
import os
 
12
import unittest
 
13
import sys
 
14
 
 
15
from testtools import TextTestResult
 
16
from testtools.compat import classtypes, istext, unicode_output_stream
 
17
from testtools.testsuite import iterate_tests
 
18
 
 
19
 
 
20
defaultTestLoader = unittest.defaultTestLoader
 
21
defaultTestLoaderCls = unittest.TestLoader
 
22
 
 
23
if getattr(defaultTestLoader, 'discover', None) is None:
 
24
    try:
 
25
        import discover
 
26
        defaultTestLoader = discover.DiscoveringTestLoader()
 
27
        defaultTestLoaderCls = discover.DiscoveringTestLoader
 
28
        have_discover = True
 
29
    except ImportError:
 
30
        have_discover = False
 
31
else:
 
32
    have_discover = True
 
33
 
 
34
 
 
35
class TestToolsTestRunner(object):
 
36
    """ A thunk object to support unittest.TestProgram."""
 
37
 
 
38
    def __init__(self, stdout):
 
39
        self.stdout = stdout
 
40
 
 
41
    def run(self, test):
 
42
        "Run the given test case or test suite."
 
43
        result = TextTestResult(unicode_output_stream(self.stdout))
 
44
        result.startTestRun()
 
45
        try:
 
46
            return test.run(result)
 
47
        finally:
 
48
            result.stopTestRun()
 
49
 
 
50
 
 
51
####################
 
52
# Taken from python 2.7 and slightly modified for compatibility with
 
53
# older versions. Delete when 2.7 is the oldest supported version.
 
54
# Modifications:
 
55
#  - Use have_discover to raise an error if the user tries to use
 
56
#    discovery on an old version and doesn't have discover installed.
 
57
#  - If --catch is given check that installHandler is available, as
 
58
#    it won't be on old python versions.
 
59
#  - print calls have been been made single-source python3 compatibile.
 
60
#  - exception handling likewise.
 
61
#  - The default help has been changed to USAGE_AS_MAIN and USAGE_FROM_MODULE
 
62
#    removed.
 
63
#  - A tweak has been added to detect 'python -m *.run' and use a
 
64
#    better progName in that case.
 
65
#  - self.module is more comprehensively set to None when being invoked from
 
66
#    the commandline - __name__ is used as a sentinel value.
 
67
#  - --list has been added which can list tests (should be upstreamed).
 
68
#  - --load-list has been added which can reduce the tests used (should be
 
69
#    upstreamed).
 
70
#  - The limitation of using getopt is declared to the user.
 
71
 
 
72
FAILFAST     = "  -f, --failfast   Stop on first failure\n"
 
73
CATCHBREAK   = "  -c, --catch      Catch control-C and display results\n"
 
74
BUFFEROUTPUT = "  -b, --buffer     Buffer stdout and stderr during test runs\n"
 
75
 
 
76
USAGE_AS_MAIN = """\
 
77
Usage: %(progName)s [options] [tests]
 
78
 
 
79
Options:
 
80
  -h, --help       Show this message
 
81
  -v, --verbose    Verbose output
 
82
  -q, --quiet      Minimal output
 
83
  -l, --list       List tests rather than executing them.
 
84
  --load-list      Specifies a file containing test ids, only tests matching
 
85
                   those ids are executed.
 
86
%(failfast)s%(catchbreak)s%(buffer)s
 
87
Examples:
 
88
  %(progName)s test_module               - run tests from test_module
 
89
  %(progName)s module.TestClass          - run tests from module.TestClass
 
90
  %(progName)s module.Class.test_method  - run specified test method
 
91
 
 
92
All options must come before [tests].  [tests] can be a list of any number of
 
93
test modules, classes and test methods.
 
94
 
 
95
Alternative Usage: %(progName)s discover [options]
 
96
 
 
97
Options:
 
98
  -v, --verbose    Verbose output
 
99
%(failfast)s%(catchbreak)s%(buffer)s  -s directory     Directory to start discovery ('.' default)
 
100
  -p pattern       Pattern to match test files ('test*.py' default)
 
101
  -t directory     Top level directory of project (default to
 
102
                   start directory)
 
103
  -l, --list       List tests rather than executing them.
 
104
  --load-list      Specifies a file containing test ids, only tests matching
 
105
                   those ids are executed.
 
106
 
 
107
For test discovery all test modules must be importable from the top
 
108
level directory of the project.
 
109
"""
 
110
 
 
111
 
 
112
class TestProgram(object):
 
113
    """A command-line program that runs a set of tests; this is primarily
 
114
       for making test modules conveniently executable.
 
115
    """
 
116
    USAGE = USAGE_AS_MAIN
 
117
 
 
118
    # defaults for testing
 
119
    failfast = catchbreak = buffer = progName = None
 
120
 
 
121
    def __init__(self, module=__name__, defaultTest=None, argv=None,
 
122
                    testRunner=None, testLoader=defaultTestLoader,
 
123
                    exit=True, verbosity=1, failfast=None, catchbreak=None,
 
124
                    buffer=None, stdout=None):
 
125
        if module == __name__:
 
126
            self.module = None
 
127
        elif istext(module):
 
128
            self.module = __import__(module)
 
129
            for part in module.split('.')[1:]:
 
130
                self.module = getattr(self.module, part)
 
131
        else:
 
132
            self.module = module
 
133
        if argv is None:
 
134
            argv = sys.argv
 
135
        if stdout is None:
 
136
            stdout = sys.stdout
 
137
 
 
138
        self.exit = exit
 
139
        self.failfast = failfast
 
140
        self.catchbreak = catchbreak
 
141
        self.verbosity = verbosity
 
142
        self.buffer = buffer
 
143
        self.defaultTest = defaultTest
 
144
        self.listtests = False
 
145
        self.load_list = None
 
146
        self.testRunner = testRunner
 
147
        self.testLoader = testLoader
 
148
        progName = argv[0]
 
149
        if progName.endswith('%srun.py' % os.path.sep):
 
150
            elements = progName.split(os.path.sep)
 
151
            progName = '%s.run' % elements[-2]
 
152
        else:
 
153
            progName = os.path.basename(argv[0])
 
154
        self.progName = progName
 
155
        self.parseArgs(argv)
 
156
        if self.load_list:
 
157
            # TODO: preserve existing suites (like testresources does in
 
158
            # OptimisingTestSuite.add, but with a standard protocol).
 
159
            # This is needed because the load_tests hook allows arbitrary
 
160
            # suites, even if that is rarely used.
 
161
            source = file(self.load_list, 'rb')
 
162
            try:
 
163
                lines = source.readlines()
 
164
            finally:
 
165
                source.close()
 
166
            test_ids = set(line.strip() for line in lines)
 
167
            filtered = unittest.TestSuite()
 
168
            for test in iterate_tests(self.test):
 
169
                if test.id() in test_ids:
 
170
                    filtered.addTest(test)
 
171
            self.test = filtered
 
172
        if not self.listtests:
 
173
            self.runTests()
 
174
        else:
 
175
            for test in iterate_tests(self.test):
 
176
                stdout.write('%s\n' % test.id())
 
177
 
 
178
    def usageExit(self, msg=None):
 
179
        if msg:
 
180
            print(msg)
 
181
        usage = {'progName': self.progName, 'catchbreak': '', 'failfast': '',
 
182
                 'buffer': ''}
 
183
        if self.failfast != False:
 
184
            usage['failfast'] = FAILFAST
 
185
        if self.catchbreak != False:
 
186
            usage['catchbreak'] = CATCHBREAK
 
187
        if self.buffer != False:
 
188
            usage['buffer'] = BUFFEROUTPUT
 
189
        print(self.USAGE % usage)
 
190
        sys.exit(2)
 
191
 
 
192
    def parseArgs(self, argv):
 
193
        if len(argv) > 1 and argv[1].lower() == 'discover':
 
194
            self._do_discovery(argv[2:])
 
195
            return
 
196
 
 
197
        import getopt
 
198
        long_opts = ['help', 'verbose', 'quiet', 'failfast', 'catch', 'buffer',
 
199
            'list', 'load-list=']
 
200
        try:
 
201
            options, args = getopt.getopt(argv[1:], 'hHvqfcbl', long_opts)
 
202
            for opt, value in options:
 
203
                if opt in ('-h','-H','--help'):
 
204
                    self.usageExit()
 
205
                if opt in ('-q','--quiet'):
 
206
                    self.verbosity = 0
 
207
                if opt in ('-v','--verbose'):
 
208
                    self.verbosity = 2
 
209
                if opt in ('-f','--failfast'):
 
210
                    if self.failfast is None:
 
211
                        self.failfast = True
 
212
                    # Should this raise an exception if -f is not valid?
 
213
                if opt in ('-c','--catch'):
 
214
                    if self.catchbreak is None:
 
215
                        self.catchbreak = True
 
216
                    # Should this raise an exception if -c is not valid?
 
217
                if opt in ('-b','--buffer'):
 
218
                    if self.buffer is None:
 
219
                        self.buffer = True
 
220
                    # Should this raise an exception if -b is not valid?
 
221
                if opt in ('-l', '--list'):
 
222
                    self.listtests = True
 
223
                if opt == '--load-list':
 
224
                    self.load_list = value
 
225
            if len(args) == 0 and self.defaultTest is None:
 
226
                # createTests will load tests from self.module
 
227
                self.testNames = None
 
228
            elif len(args) > 0:
 
229
                self.testNames = args
 
230
            else:
 
231
                self.testNames = (self.defaultTest,)
 
232
            self.createTests()
 
233
        except getopt.error:
 
234
            self.usageExit(sys.exc_info()[1])
 
235
 
 
236
    def createTests(self):
 
237
        if self.testNames is None:
 
238
            self.test = self.testLoader.loadTestsFromModule(self.module)
 
239
        else:
 
240
            self.test = self.testLoader.loadTestsFromNames(self.testNames,
 
241
                                                           self.module)
 
242
 
 
243
    def _do_discovery(self, argv, Loader=defaultTestLoaderCls):
 
244
        # handle command line args for test discovery
 
245
        if not have_discover:
 
246
            raise AssertionError("Unable to use discovery, must use python 2.7 "
 
247
                    "or greater, or install the discover package.")
 
248
        self.progName = '%s discover' % self.progName
 
249
        import optparse
 
250
        parser = optparse.OptionParser()
 
251
        parser.prog = self.progName
 
252
        parser.add_option('-v', '--verbose', dest='verbose', default=False,
 
253
                          help='Verbose output', action='store_true')
 
254
        if self.failfast != False:
 
255
            parser.add_option('-f', '--failfast', dest='failfast', default=False,
 
256
                              help='Stop on first fail or error',
 
257
                              action='store_true')
 
258
        if self.catchbreak != False:
 
259
            parser.add_option('-c', '--catch', dest='catchbreak', default=False,
 
260
                              help='Catch ctrl-C and display results so far',
 
261
                              action='store_true')
 
262
        if self.buffer != False:
 
263
            parser.add_option('-b', '--buffer', dest='buffer', default=False,
 
264
                              help='Buffer stdout and stderr during tests',
 
265
                              action='store_true')
 
266
        parser.add_option('-s', '--start-directory', dest='start', default='.',
 
267
                          help="Directory to start discovery ('.' default)")
 
268
        parser.add_option('-p', '--pattern', dest='pattern', default='test*.py',
 
269
                          help="Pattern to match tests ('test*.py' default)")
 
270
        parser.add_option('-t', '--top-level-directory', dest='top', default=None,
 
271
                          help='Top level directory of project (defaults to start directory)')
 
272
        parser.add_option('-l', '--list', dest='listtests', default=False,
 
273
                          help='List tests rather than running them.')
 
274
        parser.add_option('--load-list', dest='load_list', default=None,
 
275
                          help='Specify a filename containing the test ids to use.')
 
276
 
 
277
        options, args = parser.parse_args(argv)
 
278
        if len(args) > 3:
 
279
            self.usageExit()
 
280
 
 
281
        for name, value in zip(('start', 'pattern', 'top'), args):
 
282
            setattr(options, name, value)
 
283
 
 
284
        # only set options from the parsing here
 
285
        # if they weren't set explicitly in the constructor
 
286
        if self.failfast is None:
 
287
            self.failfast = options.failfast
 
288
        if self.catchbreak is None:
 
289
            self.catchbreak = options.catchbreak
 
290
        if self.buffer is None:
 
291
            self.buffer = options.buffer
 
292
        self.listtests = options.listtests
 
293
        self.load_list = options.load_list
 
294
 
 
295
        if options.verbose:
 
296
            self.verbosity = 2
 
297
 
 
298
        start_dir = options.start
 
299
        pattern = options.pattern
 
300
        top_level_dir = options.top
 
301
 
 
302
        loader = Loader()
 
303
        self.test = loader.discover(start_dir, pattern, top_level_dir)
 
304
 
 
305
    def runTests(self):
 
306
        if (self.catchbreak
 
307
            and getattr(unittest, 'installHandler', None) is not None):
 
308
            unittest.installHandler()
 
309
        if self.testRunner is None:
 
310
            self.testRunner = runner.TextTestRunner
 
311
        if isinstance(self.testRunner, classtypes()):
 
312
            try:
 
313
                testRunner = self.testRunner(verbosity=self.verbosity,
 
314
                                             failfast=self.failfast,
 
315
                                             buffer=self.buffer)
 
316
            except TypeError:
 
317
                # didn't accept the verbosity, buffer or failfast arguments
 
318
                testRunner = self.testRunner()
 
319
        else:
 
320
            # it is assumed to be a TestRunner instance
 
321
            testRunner = self.testRunner
 
322
        self.result = testRunner.run(self.test)
 
323
        if self.exit:
 
324
            sys.exit(not self.result.wasSuccessful())
 
325
################
 
326
 
 
327
def main(argv, stdout):
 
328
    runner = TestToolsTestRunner(stdout)
 
329
    program = TestProgram(argv=argv, testRunner=runner, stdout=stdout)
 
330
 
 
331
if __name__ == '__main__':
 
332
    main(sys.argv, sys.stdout)