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)) |