~ubuntu-branches/ubuntu/oneiric/nova/oneiric-updates

1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
1
#!/usr/bin/env python
1 by Soren Hansen
Import upstream version 0.9.1~bzr204
2
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
4
# Copyright 2010 United States Government as represented by the
5
# Administrator of the National Aeronautics and Space Administration.
6
# All Rights Reserved.
7
#
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
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
1 by Soren Hansen
Import upstream version 0.9.1~bzr204
11
#
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
12
#        http://www.apache.org/licenses/LICENSE-2.0
1 by Soren Hansen
Import upstream version 0.9.1~bzr204
13
#
14
#    Unless required by applicable law or agreed to in writing, software
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
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
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
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
44
    python run_tests.py
45
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
55
56
"""
57
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
58
import gettext
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
59
import heapq
1 by Soren Hansen
Import upstream version 0.9.1~bzr204
60
import os
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
61
import unittest
1 by Soren Hansen
Import upstream version 0.9.1~bzr204
62
import sys
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
63
import time
1 by Soren Hansen
Import upstream version 0.9.1~bzr204
64
1.1.14 by Chuck Short
Import upstream version 2011.2~bzr925
65
gettext.install('nova', unicode=1)
66
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
67
from nose import config
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
68
from nose import core
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
69
from nose import result
70
1.1.8 by Chuck Short
Import upstream version 2011.2~bzr663
71
from nova import log as logging
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
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)
1.1.8 by Chuck Short
Import upstream version 2011.2~bzr663
185
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
186
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
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
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
196
class NovaTestResult(result.TextTestResult):
197
    def __init__(self, *args, **kw):
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
198
        self.show_elapsed = kw.pop('show_elapsed')
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
199
        result.TextTestResult.__init__(self, *args, **kw)
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
200
        self.num_slow_tests = 5
201
        self.slow_tests = []  # this is a fixed-sized heap
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
202
        self._last_case = None
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
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
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
212
1.1.21 by Chuck Short
Import upstream version 2011.3~d2
213
        # NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate
214
        # error results in it failing to be initialized later. Otherwise,
1.1.20 by Chuck Short
Import upstream version 2011.3~d2~20110623.1209
215
        # _handleElapsedTime will fail, causing the wrong error message to
216
        # be outputted.
1.1.21 by Chuck Short
Import upstream version 2011.3~d2
217
        self.start_time = time.time()
1.1.20 by Chuck Short
Import upstream version 2011.3~d2~20110623.1209
218
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
219
    def getDescription(self, test):
220
        return str(test)
221
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
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
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
245
    # NOTE(vish): copied from unittest with edit to add color
246
    def addSuccess(self, test):
247
        unittest.TestResult.addSuccess(self, test)
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
248
        self._handleElapsedTime(test)
249
        self._writeResult(test, 'OK', 'green', '.', True)
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
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)
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
254
        self._handleElapsedTime(test)
255
        self._writeResult(test, 'FAIL', 'red', 'F', False)
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
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
        """
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
263
        self._handleElapsedTime(test)
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
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:
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
290
            self._writeResult(test, 'ERROR', 'red', 'E', False)
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
291
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
292
    def startTest(self, test):
293
        unittest.TestResult.startTest(self, test)
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
294
        self.start_time = time.time()
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
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):
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
308
    def __init__(self, *args, **kwargs):
309
        self.show_elapsed = kwargs.pop('show_elapsed')
310
        core.TextTestRunner.__init__(self, *args, **kwargs)
311
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
312
    def _makeResult(self):
313
        return NovaTestResult(self.stream,
314
                              self.descriptions,
315
                              self.verbosity,
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
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_
1.1.1 by Soren Hansen
Import upstream version 0.9.1~bzr265
336
1 by Soren Hansen
Import upstream version 0.9.1~bzr204
337
338
if __name__ == '__main__':
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
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
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
342
    show_elapsed = True
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
343
    argv = []
344
    for x in sys.argv:
345
        if x.startswith('test_'):
346
            argv.append('nova.tests.%s' % x)
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
347
        elif x.startswith('--hide-elapsed'):
348
            show_elapsed = False
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
349
        else:
350
            argv.append(x)
351
352
    testdir = os.path.abspath(os.path.join("nova", "tests"))
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
353
    c = config.Config(stream=sys.stdout,
354
                      env=os.environ,
355
                      verbosity=3,
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
356
                      workingDir=testdir,
1.1.5 by Chuck Short
Import upstream version 2011.1~bzr597
357
                      plugins=core.DefaultPluginManager())
358
359
    runner = NovaTestRunner(stream=c.stream,
360
                            verbosity=c.verbosity,
1.1.19 by Chuck Short
Import upstream version 2011.3~d2~20110617.1191
361
                            config=c,
362
                            show_elapsed=show_elapsed)
1.1.10 by Chuck Short
Import upstream version 2011.2~bzr740
363
    sys.exit(not core.run(config=c, testRunner=runner, argv=argv))