~0x44/nova/extdoc

« back to all changes in this revision

Viewing changes to run_tests.py

  • Committer: NTT PF Lab.
  • Date: 2010-12-24 11:38:49 UTC
  • mto: This revision was merged to the branch mainline in revision 564.
  • Revision ID: openstack@lab.ntt.co.jp-20101224113849-z9nemzmki17bxnvw
SupportĀ IPv6

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
2
 
4
3
# Copyright 2010 United States Government as represented by the
5
4
# Administrator of the National Aeronautics and Space Administration.
6
5
# All Rights Reserved.
7
6
#
8
 
#    Licensed under the Apache License, Version 2.0 (the "License");
9
 
#    you may not use this file except in compliance with the License.
10
 
#    You may obtain a copy of the License at
 
7
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
8
#    not use this file except in compliance with the License. You may obtain
 
9
#    a copy of the License at
11
10
#
12
 
#        http://www.apache.org/licenses/LICENSE-2.0
 
11
#         http://www.apache.org/licenses/LICENSE-2.0
13
12
#
14
13
#    Unless required by applicable law or agreed to in writing, software
15
 
#    distributed under the License is distributed on an "AS IS" BASIS,
16
 
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 
#    See the License for the specific language governing permissions and
18
 
#    limitations under the License.
19
 
 
20
 
# Colorizer Code is borrowed from Twisted:
21
 
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
22
 
#
23
 
#    Permission is hereby granted, free of charge, to any person obtaining
24
 
#    a copy of this software and associated documentation files (the
25
 
#    "Software"), to deal in the Software without restriction, including
26
 
#    without limitation the rights to use, copy, modify, merge, publish,
27
 
#    distribute, sublicense, and/or sell copies of the Software, and to
28
 
#    permit persons to whom the Software is furnished to do so, subject to
29
 
#    the following conditions:
30
 
#
31
 
#    The above copyright notice and this permission notice shall be
32
 
#    included in all copies or substantial portions of the Software.
33
 
#
34
 
#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
35
 
#    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
36
 
#    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
37
 
#    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
38
 
#    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
39
 
#    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
40
 
#    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41
 
"""Unittest runner for Nova.
42
 
 
43
 
To run all tests
 
14
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
15
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
16
#    License for the specific language governing permissions and limitations
 
17
#    under the License.
 
18
 
 
19
"""
 
20
This is our basic test running framework based on Twisted's Trial.
 
21
 
 
22
Usage Examples:
 
23
 
 
24
    # to run all the tests
44
25
    python run_tests.py
45
26
 
46
 
To run a single test:
47
 
    python run_tests.py test_compute:ComputeTestCase.test_run_terminate
48
 
 
49
 
To run a single test module:
50
 
    python run_tests.py test_compute
51
 
 
52
 
    or
53
 
 
54
 
    python run_tests.py api.test_wsgi
 
27
    # to run a specific test suite imported here
 
28
    python run_tests.py NodeConnectionTestCase
 
29
 
 
30
    # to run a specific test imported here
 
31
    python run_tests.py NodeConnectionTestCase.test_reboot
 
32
 
 
33
    # to run some test suites elsewhere
 
34
    python run_tests.py nova.tests.node_unittest
 
35
    python run_tests.py nova.tests.node_unittest.NodeConnectionTestCase
 
36
 
 
37
Due to our use of multiprocessing it we frequently get some ignorable
 
38
'Interrupted system call' exceptions after test completion.
55
39
 
56
40
"""
57
41
 
 
42
import __main__
58
43
import gettext
59
 
import heapq
60
44
import os
61
 
import unittest
62
45
import sys
63
 
import time
64
46
 
65
47
gettext.install('nova', unicode=1)
66
48
 
67
 
from nose import config
68
 
from nose import core
69
 
from nose import result
70
 
 
71
 
from nova import log as logging
72
 
 
73
 
 
74
 
class _AnsiColorizer(object):
75
 
    """
76
 
    A colorizer is an object that loosely wraps around a stream, allowing
77
 
    callers to write text to the stream in a particular color.
78
 
 
79
 
    Colorizer classes must implement C{supported()} and C{write(text, color)}.
80
 
    """
81
 
    _colors = dict(black=30, red=31, green=32, yellow=33,
82
 
                   blue=34, magenta=35, cyan=36, white=37)
83
 
 
84
 
    def __init__(self, stream):
85
 
        self.stream = stream
86
 
 
87
 
    def supported(cls, stream=sys.stdout):
88
 
        """
89
 
        A class method that returns True if the current platform supports
90
 
        coloring terminal output using this method. Returns False otherwise.
91
 
        """
92
 
        if not stream.isatty():
93
 
            return False  # auto color only on TTYs
94
 
        try:
95
 
            import curses
96
 
        except ImportError:
97
 
            return False
98
 
        else:
99
 
            try:
100
 
                try:
101
 
                    return curses.tigetnum("colors") > 2
102
 
                except curses.error:
103
 
                    curses.setupterm()
104
 
                    return curses.tigetnum("colors") > 2
105
 
            except:
106
 
                raise
107
 
                # guess false in case of error
108
 
                return False
109
 
    supported = classmethod(supported)
110
 
 
111
 
    def write(self, text, color):
112
 
        """
113
 
        Write the given text to the stream in the given color.
114
 
 
115
 
        @param text: Text to be written to the stream.
116
 
 
117
 
        @param color: A string label for a color. e.g. 'red', 'white'.
118
 
        """
119
 
        color = self._colors[color]
120
 
        self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
121
 
 
122
 
 
123
 
class _Win32Colorizer(object):
124
 
    """
125
 
    See _AnsiColorizer docstring.
126
 
    """
127
 
    def __init__(self, stream):
128
 
        from win32console import GetStdHandle, STD_OUT_HANDLE, \
129
 
             FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
130
 
             FOREGROUND_INTENSITY
131
 
        red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
132
 
                                  FOREGROUND_BLUE, FOREGROUND_INTENSITY)
133
 
        self.stream = stream
134
 
        self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
135
 
        self._colors = {
136
 
            'normal': red | green | blue,
137
 
            'red': red | bold,
138
 
            'green': green | bold,
139
 
            'blue': blue | bold,
140
 
            'yellow': red | green | bold,
141
 
            'magenta': red | blue | bold,
142
 
            'cyan': green | blue | bold,
143
 
            'white': red | green | blue | bold
144
 
            }
145
 
 
146
 
    def supported(cls, stream=sys.stdout):
147
 
        try:
148
 
            import win32console
149
 
            screenBuffer = win32console.GetStdHandle(
150
 
                win32console.STD_OUT_HANDLE)
151
 
        except ImportError:
152
 
            return False
153
 
        import pywintypes
154
 
        try:
155
 
            screenBuffer.SetConsoleTextAttribute(
156
 
                win32console.FOREGROUND_RED |
157
 
                win32console.FOREGROUND_GREEN |
158
 
                win32console.FOREGROUND_BLUE)
159
 
        except pywintypes.error:
160
 
            return False
161
 
        else:
162
 
            return True
163
 
    supported = classmethod(supported)
164
 
 
165
 
    def write(self, text, color):
166
 
        color = self._colors[color]
167
 
        self.screenBuffer.SetConsoleTextAttribute(color)
168
 
        self.stream.write(text)
169
 
        self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
170
 
 
171
 
 
172
 
class _NullColorizer(object):
173
 
    """
174
 
    See _AnsiColorizer docstring.
175
 
    """
176
 
    def __init__(self, stream):
177
 
        self.stream = stream
178
 
 
179
 
    def supported(cls, stream=sys.stdout):
180
 
        return True
181
 
    supported = classmethod(supported)
182
 
 
183
 
    def write(self, text, color):
184
 
        self.stream.write(text)
185
 
 
186
 
 
187
 
def get_elapsed_time_color(elapsed_time):
188
 
    if elapsed_time > 1.0:
189
 
        return 'red'
190
 
    elif elapsed_time > 0.25:
191
 
        return 'yellow'
192
 
    else:
193
 
        return 'green'
194
 
 
195
 
 
196
 
class NovaTestResult(result.TextTestResult):
197
 
    def __init__(self, *args, **kw):
198
 
        self.show_elapsed = kw.pop('show_elapsed')
199
 
        result.TextTestResult.__init__(self, *args, **kw)
200
 
        self.num_slow_tests = 5
201
 
        self.slow_tests = []  # this is a fixed-sized heap
202
 
        self._last_case = None
203
 
        self.colorizer = None
204
 
        # NOTE(vish): reset stdout for the terminal check
205
 
        stdout = sys.stdout
206
 
        sys.stdout = sys.__stdout__
207
 
        for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
208
 
            if colorizer.supported():
209
 
                self.colorizer = colorizer(self.stream)
210
 
                break
211
 
        sys.stdout = stdout
212
 
 
213
 
        # NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate
214
 
        # error results in it failing to be initialized later. Otherwise,
215
 
        # _handleElapsedTime will fail, causing the wrong error message to
216
 
        # be outputted.
217
 
        self.start_time = time.time()
218
 
 
219
 
    def getDescription(self, test):
220
 
        return str(test)
221
 
 
222
 
    def _handleElapsedTime(self, test):
223
 
        self.elapsed_time = time.time() - self.start_time
224
 
        item = (self.elapsed_time, test)
225
 
        # Record only the n-slowest tests using heap
226
 
        if len(self.slow_tests) >= self.num_slow_tests:
227
 
            heapq.heappushpop(self.slow_tests, item)
228
 
        else:
229
 
            heapq.heappush(self.slow_tests, item)
230
 
 
231
 
    def _writeElapsedTime(self, test):
232
 
        color = get_elapsed_time_color(self.elapsed_time)
233
 
        self.colorizer.write("  %.2f" % self.elapsed_time, color)
234
 
 
235
 
    def _writeResult(self, test, long_result, color, short_result, success):
236
 
        if self.showAll:
237
 
            self.colorizer.write(long_result, color)
238
 
            if self.show_elapsed and success:
239
 
                self._writeElapsedTime(test)
240
 
            self.stream.writeln()
241
 
        elif self.dots:
242
 
            self.stream.write(short_result)
243
 
            self.stream.flush()
244
 
 
245
 
    # NOTE(vish): copied from unittest with edit to add color
246
 
    def addSuccess(self, test):
247
 
        unittest.TestResult.addSuccess(self, test)
248
 
        self._handleElapsedTime(test)
249
 
        self._writeResult(test, 'OK', 'green', '.', True)
250
 
 
251
 
    # NOTE(vish): copied from unittest with edit to add color
252
 
    def addFailure(self, test, err):
253
 
        unittest.TestResult.addFailure(self, test, err)
254
 
        self._handleElapsedTime(test)
255
 
        self._writeResult(test, 'FAIL', 'red', 'F', False)
256
 
 
257
 
    # NOTE(vish): copied from nose with edit to add color
258
 
    def addError(self, test, err):
259
 
        """Overrides normal addError to add support for
260
 
        errorClasses. If the exception is a registered class, the
261
 
        error will be added to the list for that class, not errors.
262
 
        """
263
 
        self._handleElapsedTime(test)
264
 
        stream = getattr(self, 'stream', None)
265
 
        ec, ev, tb = err
266
 
        try:
267
 
            exc_info = self._exc_info_to_string(err, test)
268
 
        except TypeError:
269
 
            # 2.3 compat
270
 
            exc_info = self._exc_info_to_string(err)
271
 
        for cls, (storage, label, isfail) in self.errorClasses.items():
272
 
            if result.isclass(ec) and issubclass(ec, cls):
273
 
                if isfail:
274
 
                    test.passed = False
275
 
                storage.append((test, exc_info))
276
 
                # Might get patched into a streamless result
277
 
                if stream is not None:
278
 
                    if self.showAll:
279
 
                        message = [label]
280
 
                        detail = result._exception_detail(err[1])
281
 
                        if detail:
282
 
                            message.append(detail)
283
 
                        stream.writeln(": ".join(message))
284
 
                    elif self.dots:
285
 
                        stream.write(label[:1])
286
 
                return
287
 
        self.errors.append((test, exc_info))
288
 
        test.passed = False
289
 
        if stream is not None:
290
 
            self._writeResult(test, 'ERROR', 'red', 'E', False)
291
 
 
292
 
    def startTest(self, test):
293
 
        unittest.TestResult.startTest(self, test)
294
 
        self.start_time = time.time()
295
 
        current_case = test.test.__class__.__name__
296
 
 
297
 
        if self.showAll:
298
 
            if current_case != self._last_case:
299
 
                self.stream.writeln(current_case)
300
 
                self._last_case = current_case
301
 
 
302
 
            self.stream.write(
303
 
                '    %s' % str(test.test._testMethodName).ljust(60))
304
 
            self.stream.flush()
305
 
 
306
 
 
307
 
class NovaTestRunner(core.TextTestRunner):
308
 
    def __init__(self, *args, **kwargs):
309
 
        self.show_elapsed = kwargs.pop('show_elapsed')
310
 
        core.TextTestRunner.__init__(self, *args, **kwargs)
311
 
 
312
 
    def _makeResult(self):
313
 
        return NovaTestResult(self.stream,
314
 
                              self.descriptions,
315
 
                              self.verbosity,
316
 
                              self.config,
317
 
                              show_elapsed=self.show_elapsed)
318
 
 
319
 
    def _writeSlowTests(self, result_):
320
 
        # Pare out 'fast' tests
321
 
        slow_tests = [item for item in result_.slow_tests
322
 
                      if get_elapsed_time_color(item[0]) != 'green']
323
 
        if slow_tests:
324
 
            slow_total_time = sum(item[0] for item in slow_tests)
325
 
            self.stream.writeln("Slowest %i tests took %.2f secs:"
326
 
                                % (len(slow_tests), slow_total_time))
327
 
            for elapsed_time, test in sorted(slow_tests, reverse=True):
328
 
                time_str = "%.2f" % elapsed_time
329
 
                self.stream.writeln("    %s %s" % (time_str.ljust(10), test))
330
 
 
331
 
    def run(self, test):
332
 
        result_ = core.TextTestRunner.run(self, test)
333
 
        if self.show_elapsed:
334
 
            self._writeSlowTests(result_)
335
 
        return result_
 
49
from twisted.scripts import trial as trial_script
 
50
 
 
51
from nova import flags
 
52
from nova import twistd
 
53
 
 
54
from nova.tests.access_unittest import *
 
55
from nova.tests.api_unittest import *
 
56
from nova.tests.auth_unittest import *
 
57
from nova.tests.cloud_unittest import *
 
58
from nova.tests.compute_unittest import *
 
59
from nova.tests.flags_unittest import *
 
60
from nova.tests.misc_unittest import *
 
61
from nova.tests.network_unittest import *
 
62
from nova.tests.objectstore_unittest import *
 
63
from nova.tests.process_unittest import *
 
64
from nova.tests.quota_unittest import *
 
65
from nova.tests.rpc_unittest import *
 
66
from nova.tests.scheduler_unittest import *
 
67
from nova.tests.service_unittest import *
 
68
from nova.tests.twistd_unittest import *
 
69
from nova.tests.validator_unittest import *
 
70
from nova.tests.virt_unittest import *
 
71
from nova.tests.virt_unittest import *
 
72
from nova.tests.volume_unittest import *
 
73
 
 
74
 
 
75
FLAGS = flags.FLAGS
 
76
flags.DEFINE_bool('flush_db', True,
 
77
                  'Flush the database before running fake tests')
 
78
flags.DEFINE_string('tests_stderr', 'run_tests.err.log',
 
79
                    'Path to where to pipe STDERR during test runs.'
 
80
                    ' Default = "run_tests.err.log"')
336
81
 
337
82
 
338
83
if __name__ == '__main__':
339
 
    logging.setup()
340
 
    # If any argument looks like a test name but doesn't have "nova.tests" in
341
 
    # front of it, automatically add that so we don't have to type as much
342
 
    show_elapsed = True
343
 
    argv = []
344
 
    for x in sys.argv:
345
 
        if x.startswith('test_'):
346
 
            argv.append('nova.tests.%s' % x)
347
 
        elif x.startswith('--hide-elapsed'):
348
 
            show_elapsed = False
349
 
        else:
350
 
            argv.append(x)
351
 
 
352
 
    testdir = os.path.abspath(os.path.join("nova", "tests"))
353
 
    c = config.Config(stream=sys.stdout,
354
 
                      env=os.environ,
355
 
                      verbosity=3,
356
 
                      workingDir=testdir,
357
 
                      plugins=core.DefaultPluginManager())
358
 
 
359
 
    runner = NovaTestRunner(stream=c.stream,
360
 
                            verbosity=c.verbosity,
361
 
                            config=c,
362
 
                            show_elapsed=show_elapsed)
363
 
    sys.exit(not core.run(config=c, testRunner=runner, argv=argv))
 
84
    OptionsClass = twistd.WrapTwistedOptions(trial_script.Options)
 
85
    config = OptionsClass()
 
86
    argv = config.parseOptions()
 
87
 
 
88
    FLAGS.verbose = True
 
89
 
 
90
    # TODO(termie): these should make a call instead of doing work on import
 
91
    if FLAGS.fake_tests:
 
92
        from nova.tests.fake_flags import *
 
93
    else:
 
94
        from nova.tests.real_flags import *
 
95
 
 
96
    # Establish redirect for STDERR
 
97
    sys.stderr.flush()
 
98
    err = open(FLAGS.tests_stderr, 'w+', 0)
 
99
    os.dup2(err.fileno(), sys.stderr.fileno())
 
100
 
 
101
    if len(argv) == 1 and len(config['tests']) == 0:
 
102
        # If no tests were specified run the ones imported in this file
 
103
        # NOTE(termie): "tests" is not a flag, just some Trial related stuff
 
104
        config['tests'].update(['__main__'])
 
105
    elif len(config['tests']):
 
106
        # If we specified tests check first whether they are in __main__
 
107
        for arg in config['tests']:
 
108
            key = arg.split('.')[0]
 
109
            if hasattr(__main__, key):
 
110
                config['tests'].remove(arg)
 
111
                config['tests'].add('__main__.%s' % arg)
 
112
 
 
113
    trial_script._initialDebugSetup(config)
 
114
    trialRunner = trial_script._makeRunner(config)
 
115
    suite = trial_script._getSuite(config)
 
116
    if config['until-failure']:
 
117
        test_result = trialRunner.runUntilFailure(suite)
 
118
    else:
 
119
        test_result = trialRunner.run(suite)
 
120
    if config.tracer:
 
121
        sys.settrace(None)
 
122
        results = config.tracer.results()
 
123
        results.write_results(show_missing=1, summary=False,
 
124
                              coverdir=config.coverdir)
 
125
    sys.exit(not test_result.wasSuccessful())