17
17
# See the License for the specific language governing permissions and
18
18
# limitations under the License.
20
# Colorizer Code is borrowed from Twisted:
21
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
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:
31
# The above copyright notice and this permission notice shall be
32
# included in all copies or substantial portions of the Software.
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.
20
41
"""Unittest runner for Nova.
47
68
from nova.tests import fake_flags
71
class _AnsiColorizer(object):
73
A colorizer is an object that loosely wraps around a stream, allowing
74
callers to write text to the stream in a particular color.
76
Colorizer classes must implement C{supported()} and C{write(text, color)}.
78
_colors = dict(black=30, red=31, green=32, yellow=33,
79
blue=34, magenta=35, cyan=36, white=37)
81
def __init__(self, stream):
84
def supported(cls, stream=sys.stdout):
86
A class method that returns True if the current platform supports
87
coloring terminal output using this method. Returns False otherwise.
89
if not stream.isatty():
90
return False # auto color only on TTYs
98
return curses.tigetnum("colors") > 2
101
return curses.tigetnum("colors") > 2
104
# guess false in case of error
106
supported = classmethod(supported)
108
def write(self, text, color):
110
Write the given text to the stream in the given color.
112
@param text: Text to be written to the stream.
114
@param color: A string label for a color. e.g. 'red', 'white'.
116
color = self._colors[color]
117
self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
120
class _Win32Colorizer(object):
122
See _AnsiColorizer docstring.
124
def __init__(self, stream):
125
from win32console import GetStdHandle, STD_OUT_HANDLE, \
126
FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
128
red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
129
FOREGROUND_BLUE, FOREGROUND_INTENSITY)
131
self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
133
'normal': red | green | blue,
135
'green': green | bold,
137
'yellow': red | green | bold,
138
'magenta': red | blue | bold,
139
'cyan': green | blue | bold,
140
'white': red | green | blue | bold
143
def supported(cls, stream=sys.stdout):
146
screenBuffer = win32console.GetStdHandle(
147
win32console.STD_OUT_HANDLE)
152
screenBuffer.SetConsoleTextAttribute(
153
win32console.FOREGROUND_RED |
154
win32console.FOREGROUND_GREEN |
155
win32console.FOREGROUND_BLUE)
156
except pywintypes.error:
160
supported = classmethod(supported)
162
def write(self, text, color):
163
color = self._colors[color]
164
self.screenBuffer.SetConsoleTextAttribute(color)
165
self.stream.write(text)
166
self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
169
class _NullColorizer(object):
171
See _AnsiColorizer docstring.
173
def __init__(self, stream):
176
def supported(cls, stream=sys.stdout):
178
supported = classmethod(supported)
180
def write(self, text, color):
181
self.stream.write(text)
50
184
class NovaTestResult(result.TextTestResult):
51
185
def __init__(self, *args, **kw):
52
186
result.TextTestResult.__init__(self, *args, **kw)
53
187
self._last_case = None
188
self.colorizer = None
189
# NOTE(vish): reset stdout for the terminal check
191
sys.stdout = sys.__stdout__
192
for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
193
if colorizer.supported():
194
self.colorizer = colorizer(self.stream)
55
198
def getDescription(self, test):
201
# NOTE(vish): copied from unittest with edit to add color
202
def addSuccess(self, test):
203
unittest.TestResult.addSuccess(self, test)
205
self.colorizer.write("OK", 'green')
206
self.stream.writeln()
208
self.stream.write('.')
211
# NOTE(vish): copied from unittest with edit to add color
212
def addFailure(self, test, err):
213
unittest.TestResult.addFailure(self, test, err)
215
self.colorizer.write("FAIL", 'red')
216
self.stream.writeln()
218
self.stream.write('F')
221
# NOTE(vish): copied from nose with edit to add color
222
def addError(self, test, err):
223
"""Overrides normal addError to add support for
224
errorClasses. If the exception is a registered class, the
225
error will be added to the list for that class, not errors.
227
stream = getattr(self, 'stream', None)
230
exc_info = self._exc_info_to_string(err, test)
233
exc_info = self._exc_info_to_string(err)
234
for cls, (storage, label, isfail) in self.errorClasses.items():
235
if result.isclass(ec) and issubclass(ec, cls):
238
storage.append((test, exc_info))
239
# Might get patched into a streamless result
240
if stream is not None:
243
detail = result._exception_detail(err[1])
245
message.append(detail)
246
stream.writeln(": ".join(message))
248
stream.write(label[:1])
250
self.errors.append((test, exc_info))
252
if stream is not None:
254
self.colorizer.write("ERROR", 'red')
255
self.stream.writeln()
58
259
def startTest(self, test):
59
260
unittest.TestResult.startTest(self, test)
60
261
current_case = test.test.__class__.__name__