1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
# Copyright 2010 OpenStack, LLC
6
# Licensed under the Apache License, Version 2.0 (the "License");
7
# you may not use this file except in compliance with the License.
8
# You may obtain a copy of the License at
10
# http://www.apache.org/licenses/LICENSE-2.0
12
# Unless required by applicable law or agreed to in writing, software
13
# distributed under the License is distributed on an "AS IS" BASIS,
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
# See the License for the specific language governing permissions and
16
# limitations under the License.
18
# Colorizer Code is borrowed from Twisted:
19
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
21
# Permission is hereby granted, free of charge, to any person obtaining
22
# a copy of this software and associated documentation files (the
23
# "Software"), to deal in the Software without restriction, including
24
# without limitation the rights to use, copy, modify, merge, publish,
25
# distribute, sublicense, and/or sell copies of the Software, and to
26
# permit persons to whom the Software is furnished to do so, subject to
27
# the following conditions:
29
# The above copyright notice and this permission notice shall be
30
# included in all copies or substantial portions of the Software.
32
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46
from nose import result
48
from nose import config
51
class _AnsiColorizer(object):
53
A colorizer is an object that loosely wraps around a stream, allowing
54
callers to write text to the stream in a particular color.
56
Colorizer classes must implement C{supported()} and C{write(text, color)}.
58
_colors = dict(black=30, red=31, green=32, yellow=33,
59
blue=34, magenta=35, cyan=36, white=37)
61
def __init__(self, stream):
64
def supported(cls, stream=sys.stdout):
66
A class method that returns True if the current platform supports
67
coloring terminal output using this method. Returns False otherwise.
69
if not stream.isatty():
70
return False # auto color only on TTYs
78
return curses.tigetnum("colors") > 2
81
return curses.tigetnum("colors") > 2
84
# guess false in case of error
86
supported = classmethod(supported)
88
def write(self, text, color):
90
Write the given text to the stream in the given color.
92
@param text: Text to be written to the stream.
94
@param color: A string label for a color. e.g. 'red', 'white'.
96
color = self._colors[color]
97
self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
100
class _Win32Colorizer(object):
102
See _AnsiColorizer docstring.
104
def __init__(self, stream):
105
from win32console import GetStdHandle, STD_OUT_HANDLE, \
106
FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
108
red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
109
FOREGROUND_BLUE, FOREGROUND_INTENSITY)
111
self.screenBuffer = GetStdHandle(STD_OUT_HANDLE)
113
'normal': red | green | blue,
115
'green': green | bold,
117
'yellow': red | green | bold,
118
'magenta': red | blue | bold,
119
'cyan': green | blue | bold,
120
'white': red | green | blue | bold}
122
def supported(cls, stream=sys.stdout):
125
screenBuffer = win32console.GetStdHandle(
126
win32console.STD_OUT_HANDLE)
131
screenBuffer.SetConsoleTextAttribute(
132
win32console.FOREGROUND_RED |
133
win32console.FOREGROUND_GREEN |
134
win32console.FOREGROUND_BLUE)
135
except pywintypes.error:
139
supported = classmethod(supported)
141
def write(self, text, color):
142
color = self._colors[color]
143
self.screenBuffer.SetConsoleTextAttribute(color)
144
self.stream.write(text)
145
self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
148
class _NullColorizer(object):
150
See _AnsiColorizer docstring.
152
def __init__(self, stream):
155
def supported(cls, stream=sys.stdout):
157
supported = classmethod(supported)
159
def write(self, text, color):
160
self.stream.write(text)
163
class QuantumTestResult(result.TextTestResult):
164
def __init__(self, *args, **kw):
165
result.TextTestResult.__init__(self, *args, **kw)
166
self._last_case = None
167
self.colorizer = None
168
# NOTE(vish, tfukushima): reset stdout for the terminal check
169
stdout = sys.__stdout__
170
for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
171
if colorizer.supported():
172
self.colorizer = colorizer(self.stream)
176
def getDescription(self, test):
179
# NOTE(vish, tfukushima): copied from unittest with edit to add color
180
def addSuccess(self, test):
181
unittest.TestResult.addSuccess(self, test)
183
self.colorizer.write("OK", 'green')
184
self.stream.writeln()
186
self.stream.write('.')
189
# NOTE(vish, tfukushima): copied from unittest with edit to add color
190
def addFailure(self, test, err):
191
unittest.TestResult.addFailure(self, test, err)
193
self.colorizer.write("FAIL", 'red')
194
self.stream.writeln()
196
self.stream.write('F')
199
# NOTE(vish, tfukushima): copied from unittest with edit to add color
200
def addError(self, test, err):
201
"""Overrides normal addError to add support for errorClasses.
202
If the exception is a registered class, the error will be added
203
to the list for that class, not errors.
205
stream = getattr(self, 'stream', None)
208
exc_info = self._exc_info_to_string(err, test)
210
# This is for compatibility with Python 2.3.
211
exc_info = self._exc_info_to_string(err)
212
for cls, (storage, label, isfail) in self.errorClasses.items():
213
if result.isclass(ec) and issubclass(ec, cls):
216
storage.append((test, exc_info))
217
# Might get patched into a streamless result
218
if stream is not None:
221
detail = result._exception_details(err[1])
223
message.append(detail)
224
stream.writeln(": ".join(message))
226
stream.write(label[:1])
228
self.errors.append((test, exc_info))
230
if stream is not None:
232
self.colorizer.write("ERROR", 'red')
233
self.stream.writeln()
237
def startTest(self, test):
238
unittest.TestResult.startTest(self, test)
239
current_case = test.test.__class__.__name__
242
if current_case != self._last_case:
243
self.stream.writeln(current_case)
244
self._last_case = current_case
247
' %s' % str(test.test._testMethodName).ljust(60))
251
class QuantumTestRunner(core.TextTestRunner):
252
def _makeResult(self):
253
return QuantumTestResult(self.stream,
260
logger = logging.getLogger()
261
hdlr = logging.StreamHandler()
262
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
263
hdlr.setFormatter(formatter)
264
logger.addHandler(hdlr)
265
logger.setLevel(logging.DEBUG)
267
runner = QuantumTestRunner(stream=c.stream,
268
verbosity=c.verbosity,
270
return not core.run(config=c, testRunner=runner)
272
# describes parameters used by different unit/functional tests
273
# a plugin-specific testing mechanism should import this dictionary
274
# and override the values in it if needed (e.g., run_tests.py in
275
# quantum/plugins/openvswitch/ )
277
"plugin_name": "quantum.plugins.SamplePlugin.FakePlugin",