[740] | 1 | # Python module for parsing and generating the Subunit protocol
|
---|
| 2 | # (Samba-specific)
|
---|
| 3 | # Copyright (C) 2008-2009 Jelmer Vernooij <jelmer@samba.org>
|
---|
| 4 | #
|
---|
| 5 | # This program is free software; you can redistribute it and/or modify
|
---|
| 6 | # it under the terms of the GNU General Public License as published by
|
---|
| 7 | # the Free Software Foundation; either version 3 of the License, or
|
---|
| 8 | # (at your option) any later version.
|
---|
| 9 |
|
---|
| 10 | # This program is distributed in the hope that it will be useful,
|
---|
| 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 13 | # GNU General Public License for more details.
|
---|
| 14 |
|
---|
| 15 | # You should have received a copy of the GNU General Public License
|
---|
| 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
| 17 |
|
---|
| 18 | __all__ = ['parse_results']
|
---|
| 19 |
|
---|
| 20 | import re
|
---|
| 21 | import sys
|
---|
| 22 | import subunit
|
---|
| 23 | import subunit.iso8601
|
---|
| 24 | import testtools
|
---|
| 25 |
|
---|
| 26 | VALID_RESULTS = ['success', 'successful', 'failure', 'fail', 'skip', 'knownfail', 'error', 'xfail', 'skip-testsuite', 'testsuite-failure', 'testsuite-xfail', 'testsuite-success', 'testsuite-error']
|
---|
| 27 |
|
---|
| 28 | class TestsuiteEnabledTestResult(testtools.testresult.TestResult):
|
---|
| 29 |
|
---|
| 30 | def start_testsuite(self, name):
|
---|
| 31 | raise NotImplementedError(self.start_testsuite)
|
---|
| 32 |
|
---|
| 33 |
|
---|
| 34 | def parse_results(msg_ops, statistics, fh):
|
---|
| 35 | exitcode = 0
|
---|
| 36 | expected_fail = 0
|
---|
| 37 | open_tests = {}
|
---|
| 38 |
|
---|
| 39 | while fh:
|
---|
| 40 | l = fh.readline()
|
---|
| 41 | if l == "":
|
---|
| 42 | break
|
---|
| 43 | parts = l.split(None, 1)
|
---|
| 44 | if not len(parts) == 2 or not l.startswith(parts[0]):
|
---|
| 45 | msg_ops.output_msg(l)
|
---|
| 46 | continue
|
---|
| 47 | command = parts[0].rstrip(":")
|
---|
| 48 | arg = parts[1]
|
---|
| 49 | if command in ("test", "testing"):
|
---|
| 50 | msg_ops.control_msg(l)
|
---|
| 51 | name = arg.rstrip()
|
---|
| 52 | test = subunit.RemotedTestCase(name)
|
---|
| 53 | if name in open_tests:
|
---|
| 54 | msg_ops.addError(open_tests.pop(name), subunit.RemoteError(u"Test already running"))
|
---|
| 55 | msg_ops.startTest(test)
|
---|
| 56 | open_tests[name] = test
|
---|
| 57 | elif command == "time":
|
---|
| 58 | msg_ops.control_msg(l)
|
---|
| 59 | try:
|
---|
| 60 | dt = subunit.iso8601.parse_date(arg.rstrip("\n"))
|
---|
| 61 | except TypeError, e:
|
---|
| 62 | print "Unable to parse time line: %s" % arg.rstrip("\n")
|
---|
| 63 | else:
|
---|
| 64 | msg_ops.time(dt)
|
---|
| 65 | elif command in VALID_RESULTS:
|
---|
| 66 | msg_ops.control_msg(l)
|
---|
| 67 | result = command
|
---|
| 68 | grp = re.match("(.*?)( \[)?([ \t]*)( multipart)?\n", arg)
|
---|
| 69 | (testname, hasreason) = (grp.group(1), grp.group(2))
|
---|
| 70 | if hasreason:
|
---|
| 71 | reason = ""
|
---|
| 72 | # reason may be specified in next lines
|
---|
| 73 | terminated = False
|
---|
| 74 | while fh:
|
---|
| 75 | l = fh.readline()
|
---|
| 76 | if l == "":
|
---|
| 77 | break
|
---|
| 78 | msg_ops.control_msg(l)
|
---|
| 79 | if l == "]\n":
|
---|
| 80 | terminated = True
|
---|
| 81 | break
|
---|
| 82 | else:
|
---|
| 83 | reason += l
|
---|
| 84 |
|
---|
| 85 | remote_error = subunit.RemoteError(reason.decode("utf-8"))
|
---|
| 86 |
|
---|
| 87 | if not terminated:
|
---|
| 88 | statistics['TESTS_ERROR']+=1
|
---|
| 89 | msg_ops.addError(subunit.RemotedTestCase(testname), subunit.RemoteError(u"reason (%s) interrupted" % result))
|
---|
| 90 | return 1
|
---|
| 91 | else:
|
---|
| 92 | reason = None
|
---|
| 93 | remote_error = subunit.RemoteError(u"No reason specified")
|
---|
| 94 | if result in ("success", "successful"):
|
---|
| 95 | try:
|
---|
| 96 | test = open_tests.pop(testname)
|
---|
| 97 | except KeyError:
|
---|
| 98 | statistics['TESTS_ERROR']+=1
|
---|
| 99 | exitcode = 1
|
---|
| 100 | msg_ops.addError(subunit.RemotedTestCase(testname), subunit.RemoteError(u"Test was never started"))
|
---|
| 101 | else:
|
---|
| 102 | statistics['TESTS_EXPECTED_OK']+=1
|
---|
| 103 | msg_ops.addSuccess(test)
|
---|
| 104 | elif result in ("xfail", "knownfail"):
|
---|
| 105 | try:
|
---|
| 106 | test = open_tests.pop(testname)
|
---|
| 107 | except KeyError:
|
---|
| 108 | statistics['TESTS_ERROR']+=1
|
---|
| 109 | exitcode = 1
|
---|
| 110 | msg_ops.addError(subunit.RemotedTestCase(testname), subunit.RemoteError(u"Test was never started"))
|
---|
| 111 | else:
|
---|
| 112 | statistics['TESTS_EXPECTED_FAIL']+=1
|
---|
| 113 | msg_ops.addExpectedFailure(test, remote_error)
|
---|
| 114 | expected_fail+=1
|
---|
| 115 | elif result in ("failure", "fail"):
|
---|
| 116 | try:
|
---|
| 117 | test = open_tests.pop(testname)
|
---|
| 118 | except KeyError:
|
---|
| 119 | statistics['TESTS_ERROR']+=1
|
---|
| 120 | exitcode = 1
|
---|
| 121 | msg_ops.addError(subunit.RemotedTestCase(testname), subunit.RemoteError(u"Test was never started"))
|
---|
| 122 | else:
|
---|
| 123 | statistics['TESTS_UNEXPECTED_FAIL']+=1
|
---|
| 124 | exitcode = 1
|
---|
| 125 | msg_ops.addFailure(test, remote_error)
|
---|
| 126 | elif result == "skip":
|
---|
| 127 | statistics['TESTS_SKIP']+=1
|
---|
| 128 | # Allow tests to be skipped without prior announcement of test
|
---|
| 129 | try:
|
---|
| 130 | test = open_tests.pop(testname)
|
---|
| 131 | except KeyError:
|
---|
| 132 | test = subunit.RemotedTestCase(testname)
|
---|
| 133 | msg_ops.addSkip(test, reason)
|
---|
| 134 | elif result == "error":
|
---|
| 135 | statistics['TESTS_ERROR']+=1
|
---|
| 136 | exitcode = 1
|
---|
| 137 | try:
|
---|
| 138 | test = open_tests.pop(testname)
|
---|
| 139 | except KeyError:
|
---|
| 140 | test = subunit.RemotedTestCase(testname)
|
---|
| 141 | msg_ops.addError(test, remote_error)
|
---|
| 142 | elif result == "skip-testsuite":
|
---|
| 143 | msg_ops.skip_testsuite(testname)
|
---|
| 144 | elif result == "testsuite-success":
|
---|
| 145 | msg_ops.end_testsuite(testname, "success", reason)
|
---|
| 146 | elif result == "testsuite-failure":
|
---|
| 147 | msg_ops.end_testsuite(testname, "failure", reason)
|
---|
| 148 | exitcode = 1
|
---|
| 149 | elif result == "testsuite-xfail":
|
---|
| 150 | msg_ops.end_testsuite(testname, "xfail", reason)
|
---|
| 151 | elif result == "testsuite-error":
|
---|
| 152 | msg_ops.end_testsuite(testname, "error", reason)
|
---|
| 153 | exitcode = 1
|
---|
| 154 | else:
|
---|
| 155 | raise AssertionError("Recognized but unhandled result %r" %
|
---|
| 156 | result)
|
---|
| 157 | elif command == "testsuite":
|
---|
| 158 | msg_ops.start_testsuite(arg.strip())
|
---|
| 159 | elif command == "progress":
|
---|
| 160 | arg = arg.strip()
|
---|
| 161 | if arg == "pop":
|
---|
| 162 | msg_ops.progress(None, subunit.PROGRESS_POP)
|
---|
| 163 | elif arg == "push":
|
---|
| 164 | msg_ops.progress(None, subunit.PROGRESS_PUSH)
|
---|
| 165 | elif arg[0] in '+-':
|
---|
| 166 | msg_ops.progress(int(arg), subunit.PROGRESS_CUR)
|
---|
| 167 | else:
|
---|
| 168 | msg_ops.progress(int(arg), subunit.PROGRESS_SET)
|
---|
| 169 | else:
|
---|
| 170 | msg_ops.output_msg(l)
|
---|
| 171 |
|
---|
| 172 | while open_tests:
|
---|
| 173 | test = subunit.RemotedTestCase(open_tests.popitem()[1])
|
---|
| 174 | msg_ops.addError(test, subunit.RemoteError(u"was started but never finished!"))
|
---|
| 175 | statistics['TESTS_ERROR']+=1
|
---|
| 176 | exitcode = 1
|
---|
| 177 |
|
---|
| 178 | return exitcode
|
---|
| 179 |
|
---|
| 180 |
|
---|
| 181 | class SubunitOps(subunit.TestProtocolClient,TestsuiteEnabledTestResult):
|
---|
| 182 |
|
---|
| 183 | # The following are Samba extensions:
|
---|
| 184 | def start_testsuite(self, name):
|
---|
| 185 | self._stream.write("testsuite: %s\n" % name)
|
---|
| 186 |
|
---|
| 187 | def skip_testsuite(self, name, reason=None):
|
---|
| 188 | if reason:
|
---|
| 189 | self._stream.write("skip-testsuite: %s [\n%s\n]\n" % (name, reason))
|
---|
| 190 | else:
|
---|
| 191 | self._stream.write("skip-testsuite: %s\n" % name)
|
---|
| 192 |
|
---|
| 193 | def end_testsuite(self, name, result, reason=None):
|
---|
| 194 | if reason:
|
---|
| 195 | self._stream.write("testsuite-%s: %s [\n%s\n]\n" % (result, name, reason))
|
---|
| 196 | else:
|
---|
| 197 | self._stream.write("testsuite-%s: %s\n" % (result, name))
|
---|
| 198 |
|
---|
| 199 | def output_msg(self, msg):
|
---|
| 200 | self._stream.write(msg)
|
---|
| 201 |
|
---|
| 202 |
|
---|
| 203 | def read_test_regexes(name):
|
---|
| 204 | ret = {}
|
---|
| 205 | f = open(name, 'r')
|
---|
| 206 | try:
|
---|
| 207 | for l in f:
|
---|
| 208 | l = l.strip()
|
---|
| 209 | if l == "" or l[0] == "#":
|
---|
| 210 | continue
|
---|
| 211 | if "#" in l:
|
---|
| 212 | (regex, reason) = l.split("#", 1)
|
---|
| 213 | ret[regex.strip()] = reason.strip()
|
---|
| 214 | else:
|
---|
| 215 | ret[l] = None
|
---|
| 216 | finally:
|
---|
| 217 | f.close()
|
---|
| 218 | return ret
|
---|
| 219 |
|
---|
| 220 |
|
---|
| 221 | def find_in_list(regexes, fullname):
|
---|
| 222 | for regex, reason in regexes.iteritems():
|
---|
| 223 | if re.match(regex, fullname):
|
---|
| 224 | if reason is None:
|
---|
| 225 | return ""
|
---|
| 226 | return reason
|
---|
| 227 | return None
|
---|
| 228 |
|
---|
| 229 |
|
---|
| 230 | class ImmediateFail(Exception):
|
---|
| 231 | """Raised to abort immediately."""
|
---|
| 232 |
|
---|
| 233 | def __init__(self):
|
---|
| 234 | super(ImmediateFail, self).__init__("test failed and fail_immediately set")
|
---|
| 235 |
|
---|
| 236 |
|
---|
| 237 | class FilterOps(testtools.testresult.TestResult):
|
---|
| 238 |
|
---|
| 239 | def control_msg(self, msg):
|
---|
| 240 | pass # We regenerate control messages, so ignore this
|
---|
| 241 |
|
---|
| 242 | def time(self, time):
|
---|
| 243 | self._ops.time(time)
|
---|
| 244 |
|
---|
| 245 | def progress(self, delta, whence):
|
---|
| 246 | self._ops.progress(delta, whence)
|
---|
| 247 |
|
---|
| 248 | def output_msg(self, msg):
|
---|
| 249 | if self.output is None:
|
---|
| 250 | sys.stdout.write(msg)
|
---|
| 251 | else:
|
---|
| 252 | self.output+=msg
|
---|
| 253 |
|
---|
| 254 | def startTest(self, test):
|
---|
| 255 | self.seen_output = True
|
---|
| 256 | test = self._add_prefix(test)
|
---|
| 257 | if self.strip_ok_output:
|
---|
| 258 | self.output = ""
|
---|
| 259 |
|
---|
| 260 | self._ops.startTest(test)
|
---|
| 261 |
|
---|
| 262 | def _add_prefix(self, test):
|
---|
| 263 | if self.prefix is not None:
|
---|
| 264 | return subunit.RemotedTestCase(self.prefix + test.id())
|
---|
| 265 | else:
|
---|
| 266 | return test
|
---|
| 267 |
|
---|
| 268 | def addError(self, test, details=None):
|
---|
| 269 | test = self._add_prefix(test)
|
---|
| 270 | self.error_added+=1
|
---|
| 271 | self.total_error+=1
|
---|
| 272 | self._ops.addError(test, details)
|
---|
| 273 | self.output = None
|
---|
| 274 | if self.fail_immediately:
|
---|
| 275 | raise ImmediateFail()
|
---|
| 276 |
|
---|
| 277 | def addSkip(self, test, details=None):
|
---|
| 278 | self.seen_output = True
|
---|
| 279 | test = self._add_prefix(test)
|
---|
| 280 | self._ops.addSkip(test, details)
|
---|
| 281 | self.output = None
|
---|
| 282 |
|
---|
| 283 | def addExpectedFailure(self, test, details=None):
|
---|
| 284 | test = self._add_prefix(test)
|
---|
| 285 | self._ops.addExpectedFailure(test, details)
|
---|
| 286 | self.output = None
|
---|
| 287 |
|
---|
| 288 | def addFailure(self, test, details=None):
|
---|
| 289 | test = self._add_prefix(test)
|
---|
| 290 | xfail_reason = find_in_list(self.expected_failures, test.id())
|
---|
| 291 | if xfail_reason is not None:
|
---|
| 292 | self.xfail_added+=1
|
---|
| 293 | self.total_xfail+=1
|
---|
| 294 | if details is not None:
|
---|
| 295 | details = subunit.RemoteError(unicode(details[1]) + xfail_reason.decode("utf-8"))
|
---|
| 296 | else:
|
---|
| 297 | details = subunit.RemoteError(xfail_reason.decode("utf-8"))
|
---|
| 298 | self._ops.addExpectedFailure(test, details)
|
---|
| 299 | else:
|
---|
| 300 | self.fail_added+=1
|
---|
| 301 | self.total_fail+=1
|
---|
| 302 | self._ops.addFailure(test, details)
|
---|
| 303 | if self.output:
|
---|
| 304 | self._ops.output_msg(self.output)
|
---|
| 305 | if self.fail_immediately:
|
---|
| 306 | raise ImmediateFail()
|
---|
| 307 | self.output = None
|
---|
| 308 |
|
---|
| 309 | def addSuccess(self, test, details=None):
|
---|
| 310 | test = self._add_prefix(test)
|
---|
| 311 | self._ops.addSuccess(test, details)
|
---|
| 312 | self.output = None
|
---|
| 313 |
|
---|
| 314 | def skip_testsuite(self, name, reason=None):
|
---|
| 315 | self._ops.skip_testsuite(name, reason)
|
---|
| 316 |
|
---|
| 317 | def start_testsuite(self, name):
|
---|
| 318 | self._ops.start_testsuite(name)
|
---|
| 319 | self.error_added = 0
|
---|
| 320 | self.fail_added = 0
|
---|
| 321 | self.xfail_added = 0
|
---|
| 322 |
|
---|
| 323 | def end_testsuite(self, name, result, reason=None):
|
---|
| 324 | xfail = False
|
---|
| 325 |
|
---|
| 326 | if self.xfail_added > 0:
|
---|
| 327 | xfail = True
|
---|
| 328 | if self.fail_added > 0 or self.error_added > 0:
|
---|
| 329 | xfail = False
|
---|
| 330 |
|
---|
| 331 | if xfail and result in ("fail", "failure"):
|
---|
| 332 | result = "xfail"
|
---|
| 333 |
|
---|
| 334 | if self.fail_added > 0 and result != "failure":
|
---|
| 335 | result = "failure"
|
---|
| 336 | if reason is None:
|
---|
| 337 | reason = "Subunit/Filter Reason"
|
---|
| 338 | reason += "\n failures[%d]" % self.fail_added
|
---|
| 339 |
|
---|
| 340 | if self.error_added > 0 and result != "error":
|
---|
| 341 | result = "error"
|
---|
| 342 | if reason is None:
|
---|
| 343 | reason = "Subunit/Filter Reason"
|
---|
| 344 | reason += "\n errors[%d]" % self.error_added
|
---|
| 345 |
|
---|
| 346 | self._ops.end_testsuite(name, result, reason)
|
---|
| 347 |
|
---|
| 348 | def __init__(self, out, prefix=None, expected_failures=None,
|
---|
| 349 | strip_ok_output=False, fail_immediately=False):
|
---|
| 350 | self._ops = out
|
---|
| 351 | self.seen_output = False
|
---|
| 352 | self.output = None
|
---|
| 353 | self.prefix = prefix
|
---|
| 354 | if expected_failures is not None:
|
---|
| 355 | self.expected_failures = expected_failures
|
---|
| 356 | else:
|
---|
| 357 | self.expected_failures = {}
|
---|
| 358 | self.strip_ok_output = strip_ok_output
|
---|
| 359 | self.xfail_added = 0
|
---|
| 360 | self.fail_added = 0
|
---|
| 361 | self.total_xfail = 0
|
---|
| 362 | self.total_error = 0
|
---|
| 363 | self.total_fail = 0
|
---|
| 364 | self.error_added = 0
|
---|
| 365 | self.fail_immediately = fail_immediately
|
---|
| 366 |
|
---|
| 367 |
|
---|
| 368 | class PlainFormatter(TestsuiteEnabledTestResult):
|
---|
| 369 |
|
---|
| 370 | def __init__(self, verbose, immediate, statistics,
|
---|
| 371 | totaltests=None):
|
---|
| 372 | super(PlainFormatter, self).__init__()
|
---|
| 373 | self.verbose = verbose
|
---|
| 374 | self.immediate = immediate
|
---|
| 375 | self.statistics = statistics
|
---|
| 376 | self.start_time = None
|
---|
| 377 | self.test_output = {}
|
---|
| 378 | self.suitesfailed = []
|
---|
| 379 | self.suites_ok = 0
|
---|
| 380 | self.skips = {}
|
---|
| 381 | self.index = 0
|
---|
| 382 | self.name = None
|
---|
| 383 | self._progress_level = 0
|
---|
| 384 | self.totalsuites = totaltests
|
---|
| 385 | self.last_time = None
|
---|
| 386 |
|
---|
| 387 | @staticmethod
|
---|
| 388 | def _format_time(delta):
|
---|
| 389 | minutes, seconds = divmod(delta.seconds, 60)
|
---|
| 390 | hours, minutes = divmod(minutes, 60)
|
---|
| 391 | ret = ""
|
---|
| 392 | if hours:
|
---|
| 393 | ret += "%dh" % hours
|
---|
| 394 | if minutes:
|
---|
| 395 | ret += "%dm" % minutes
|
---|
| 396 | ret += "%ds" % seconds
|
---|
| 397 | return ret
|
---|
| 398 |
|
---|
| 399 | def progress(self, offset, whence):
|
---|
| 400 | if whence == subunit.PROGRESS_POP:
|
---|
| 401 | self._progress_level -= 1
|
---|
| 402 | elif whence == subunit.PROGRESS_PUSH:
|
---|
| 403 | self._progress_level += 1
|
---|
| 404 | elif whence == subunit.PROGRESS_SET:
|
---|
| 405 | if self._progress_level == 0:
|
---|
| 406 | self.totalsuites = offset
|
---|
| 407 | elif whence == subunit.PROGRESS_CUR:
|
---|
| 408 | raise NotImplementedError
|
---|
| 409 |
|
---|
| 410 | def time(self, dt):
|
---|
| 411 | if self.start_time is None:
|
---|
| 412 | self.start_time = dt
|
---|
| 413 | self.last_time = dt
|
---|
| 414 |
|
---|
| 415 | def start_testsuite(self, name):
|
---|
| 416 | self.index += 1
|
---|
| 417 | self.name = name
|
---|
| 418 |
|
---|
| 419 | if not self.verbose:
|
---|
| 420 | self.test_output[name] = ""
|
---|
| 421 |
|
---|
| 422 | out = "[%d" % self.index
|
---|
| 423 | if self.totalsuites is not None:
|
---|
| 424 | out += "/%d" % self.totalsuites
|
---|
| 425 | if self.start_time is not None:
|
---|
| 426 | out += " in " + self._format_time(self.last_time - self.start_time)
|
---|
| 427 | if self.suitesfailed:
|
---|
| 428 | out += ", %d errors" % (len(self.suitesfailed),)
|
---|
| 429 | out += "] %s" % name
|
---|
| 430 | if self.immediate:
|
---|
| 431 | sys.stdout.write(out + "\n")
|
---|
| 432 | else:
|
---|
| 433 | sys.stdout.write(out + ": ")
|
---|
| 434 |
|
---|
| 435 | def output_msg(self, output):
|
---|
| 436 | if self.verbose:
|
---|
| 437 | sys.stdout.write(output)
|
---|
| 438 | elif self.name is not None:
|
---|
| 439 | self.test_output[self.name] += output
|
---|
| 440 | else:
|
---|
| 441 | sys.stdout.write(output)
|
---|
| 442 |
|
---|
| 443 | def control_msg(self, output):
|
---|
| 444 | pass
|
---|
| 445 |
|
---|
| 446 | def end_testsuite(self, name, result, reason):
|
---|
| 447 | out = ""
|
---|
| 448 | unexpected = False
|
---|
| 449 |
|
---|
| 450 | if not name in self.test_output:
|
---|
| 451 | print "no output for name[%s]" % name
|
---|
| 452 |
|
---|
| 453 | if result in ("success", "xfail"):
|
---|
| 454 | self.suites_ok+=1
|
---|
| 455 | else:
|
---|
| 456 | self.output_msg("ERROR: Testsuite[%s]\n" % name)
|
---|
| 457 | if reason is not None:
|
---|
| 458 | self.output_msg("REASON: %s\n" % (reason,))
|
---|
| 459 | self.suitesfailed.append(name)
|
---|
| 460 | if self.immediate and not self.verbose and name in self.test_output:
|
---|
| 461 | out += self.test_output[name]
|
---|
| 462 | unexpected = True
|
---|
| 463 |
|
---|
| 464 | if not self.immediate:
|
---|
| 465 | if not unexpected:
|
---|
| 466 | out += " ok\n"
|
---|
| 467 | else:
|
---|
| 468 | out += " " + result.upper() + "\n"
|
---|
| 469 |
|
---|
| 470 | sys.stdout.write(out)
|
---|
| 471 |
|
---|
| 472 | def startTest(self, test):
|
---|
| 473 | pass
|
---|
| 474 |
|
---|
| 475 | def addSuccess(self, test):
|
---|
| 476 | self.end_test(test.id(), "success", False)
|
---|
| 477 |
|
---|
| 478 | def addError(self, test, details=None):
|
---|
| 479 | self.end_test(test.id(), "error", True, details)
|
---|
| 480 |
|
---|
| 481 | def addFailure(self, test, details=None):
|
---|
| 482 | self.end_test(test.id(), "failure", True, details)
|
---|
| 483 |
|
---|
| 484 | def addSkip(self, test, details=None):
|
---|
| 485 | self.end_test(test.id(), "skip", False, details)
|
---|
| 486 |
|
---|
| 487 | def addExpectedFail(self, test, details=None):
|
---|
| 488 | self.end_test(test.id(), "xfail", False, details)
|
---|
| 489 |
|
---|
| 490 | def end_test(self, testname, result, unexpected, reason=None):
|
---|
| 491 | if not unexpected:
|
---|
| 492 | self.test_output[self.name] = ""
|
---|
| 493 | if not self.immediate:
|
---|
| 494 | sys.stdout.write({
|
---|
| 495 | 'failure': 'f',
|
---|
| 496 | 'xfail': 'X',
|
---|
| 497 | 'skip': 's',
|
---|
| 498 | 'success': '.'}.get(result, "?(%s)" % result))
|
---|
| 499 | return
|
---|
| 500 |
|
---|
| 501 | if not self.name in self.test_output:
|
---|
| 502 | self.test_output[self.name] = ""
|
---|
| 503 |
|
---|
| 504 | self.test_output[self.name] += "UNEXPECTED(%s): %s\n" % (result, testname)
|
---|
| 505 | if reason is not None:
|
---|
| 506 | self.test_output[self.name] += "REASON: %s\n" % (unicode(reason[1]).encode("utf-8").strip(),)
|
---|
| 507 |
|
---|
| 508 | if self.immediate and not self.verbose:
|
---|
| 509 | print self.test_output[self.name]
|
---|
| 510 | self.test_output[self.name] = ""
|
---|
| 511 |
|
---|
| 512 | if not self.immediate:
|
---|
| 513 | sys.stdout.write({
|
---|
| 514 | 'error': 'E',
|
---|
| 515 | 'failure': 'F',
|
---|
| 516 | 'success': 'S'}.get(result, "?"))
|
---|
| 517 |
|
---|
| 518 | def write_summary(self, path):
|
---|
| 519 | f = open(path, 'w+')
|
---|
| 520 |
|
---|
| 521 | if self.suitesfailed:
|
---|
| 522 | f.write("= Failed tests =\n")
|
---|
| 523 |
|
---|
| 524 | for suite in self.suitesfailed:
|
---|
| 525 | f.write("== %s ==\n" % suite)
|
---|
| 526 | if suite in self.test_output:
|
---|
| 527 | f.write(self.test_output[suite]+"\n\n")
|
---|
| 528 |
|
---|
| 529 | f.write("\n")
|
---|
| 530 |
|
---|
| 531 | if not self.immediate and not self.verbose:
|
---|
| 532 | for suite in self.suitesfailed:
|
---|
| 533 | print "=" * 78
|
---|
| 534 | print "FAIL: %s" % suite
|
---|
| 535 | if suite in self.test_output:
|
---|
| 536 | print self.test_output[suite]
|
---|
| 537 | print ""
|
---|
| 538 |
|
---|
| 539 | f.write("= Skipped tests =\n")
|
---|
| 540 | for reason in self.skips.keys():
|
---|
| 541 | f.write(reason + "\n")
|
---|
| 542 | for name in self.skips[reason]:
|
---|
| 543 | f.write("\t%s\n" % name)
|
---|
| 544 | f.write("\n")
|
---|
| 545 | f.close()
|
---|
| 546 |
|
---|
| 547 | if (not self.suitesfailed and
|
---|
| 548 | not self.statistics['TESTS_UNEXPECTED_FAIL'] and
|
---|
| 549 | not self.statistics['TESTS_ERROR']):
|
---|
| 550 | ok = (self.statistics['TESTS_EXPECTED_OK'] +
|
---|
| 551 | self.statistics['TESTS_EXPECTED_FAIL'])
|
---|
| 552 | print "\nALL OK (%d tests in %d testsuites)" % (ok, self.suites_ok)
|
---|
| 553 | else:
|
---|
| 554 | print "\nFAILED (%d failures and %d errors in %d testsuites)" % (
|
---|
| 555 | self.statistics['TESTS_UNEXPECTED_FAIL'],
|
---|
| 556 | self.statistics['TESTS_ERROR'],
|
---|
| 557 | len(self.suitesfailed))
|
---|
| 558 |
|
---|
| 559 | def skip_testsuite(self, name, reason="UNKNOWN"):
|
---|
| 560 | self.skips.setdefault(reason, []).append(name)
|
---|
| 561 | if self.totalsuites:
|
---|
| 562 | self.totalsuites-=1
|
---|