class TeamcityTestListener(TestListener): def __init__(self): self.messages = TeamcityServiceMessages(prepend_linebreak=True) def on_test_suite_start(self, test_suite): self.messages.testMatrixEntered() self.messages.testCount(len(test_suite.test_cases)) def on_test_suite_finish(self, test_suite): pass def on_test_class_start(self, test_class): self.messages.testSuiteStarted(suiteName=test_class.full_name) def on_test_class_finish(self, test_class): self.messages.testSuiteFinished(suiteName=test_class.full_name) def on_test_group_start(self, test_group): if not hasattr(test_group.test_class, "is_group_feature_used") or test_group.test_class.is_group_feature_used: self.messages.testSuiteStarted(suiteName=test_group.name) def on_test_group_finish(self, test_group): if not hasattr(test_group.test_class, "is_group_feature_used") or test_group.test_class.is_group_feature_used: self.messages.testSuiteFinished(suiteName=test_group.name) def on_test_case_start(self, test_case): self.messages.testStarted(testName=test_case.name, location=test_case.location) def on_test_case_finish(self, test_case): if test_case.status == TestCaseStatus.FAILED: self.messages.testFailed(testName=test_case.name) elif test_case.status == TestCaseStatus.SKIPPED: self.messages.testIgnored(testName=test_case.name) self.messages.testFinished(testName=test_case.name, duration=int(test_case.elapsed_time * 1000.0))
class TeamcityTestResult(TestResult): """ Set ``_jb_do_not_call_enter_matrix`` to prevent it from runnig "enter matrix" """ def __init__(self, stream=sys.stdout, *args, **kwargs): TestResult.__init__(self) for arg, value in kwargs.items(): setattr(self, arg, value) self.output = stream self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True) if not "_jb_do_not_call_enter_matrix" in os.environ: self.messages.testMatrixEntered() self.current_failed = False self.current_suite = None self.subtest_suite = None def find_first(self, val): quot = val[0] count = 1 quote_ind = val[count:].find(quot) while quote_ind != -1 and val[count + quote_ind - 1] == "\\": count = count + quote_ind + 1 quote_ind = val[count:].find(quot) return val[0 : quote_ind + count + 1] def find_second(self, val): val_index = val.find("!=") if val_index != -1: count = 1 val = val[val_index + 2 :].strip() quot = val[0] quote_ind = val[count:].find(quot) while quote_ind != -1 and val[count + quote_ind - 1] == "\\": count = count + quote_ind + 1 quote_ind = val[count:].find(quot) return val[0 : quote_ind + count + 1] else: quot = val[-1] quote_ind = val[: len(val) - 1].rfind(quot) while quote_ind != -1 and val[quote_ind - 1] == "\\": quote_ind = val[: quote_ind - 1].rfind(quot) return val[quote_ind:] def formatErr(self, err): exctype, value, tb = err return "".join(traceback.format_exception(exctype, value, tb)) def getTestName(self, test, is_subtest=False): if is_subtest: test_name = self.getTestName(test.test_case) return "{} {}".format(test_name, test._subDescription()) if hasattr(test, "_testMethodName"): if test._testMethodName == "runTest": return str(test) return test._testMethodName else: test_name = str(test) whitespace_index = test_name.index(" ") if whitespace_index != -1: test_name = test_name[:whitespace_index] return test_name def getTestId(self, test): return test.id def addSuccess(self, test): TestResult.addSuccess(self, test) def addError(self, test, err): location = self.init_suite(test) self.current_failed = True TestResult.addError(self, test, err) err = self._exc_info_to_string(err, test) self.messages.testStarted(self.getTestName(test), location=location) self.messages.testError(self.getTestName(test), message="Error", details=err, duration=self.__getDuration(test)) def find_error_value(self, err): error_value = traceback.extract_tb(err) error_value = error_value[-1][-1] return error_value.split("assert")[-1].strip() def addFailure(self, test, err): location = self.init_suite(test) self.current_failed = True TestResult.addFailure(self, test, err) error_value = smart_str(err[1]) if not len(error_value): # means it's test function and we have to extract value from traceback error_value = self.find_error_value(err[2]) self_find_first = self.find_first(error_value) self_find_second = self.find_second(error_value) quotes = ["'", '"'] if ( self_find_first[0] == self_find_first[-1] and self_find_first[0] in quotes and self_find_second[0] == self_find_second[-1] and self_find_second[0] in quotes ): # let's unescape strings to show sexy multiline diff in PyCharm. # By default all caret return chars are escaped by testing framework first = self._unescape(self_find_first) second = self._unescape(self_find_second) else: first = second = "" err = self._exc_info_to_string(err, test) self.messages.testStarted(self.getTestName(test), location=location) duration = self.__getDuration(test) self.messages.testFailed( self.getTestName(test), message="Failure", details=err, expected=first, actual=second, duration=duration ) def addSkip(self, test, reason): self.init_suite(test) self.current_failed = True self.messages.testIgnored(self.getTestName(test), message=reason) def _getSuite(self, test): try: suite = strclass(test.suite) suite_location = test.suite.location location = test.suite.abs_location if hasattr(test, "lineno"): location = location + ":" + str(test.lineno) else: location = location + ":" + str(test.test.lineno) except AttributeError: import inspect try: source_file = inspect.getsourcefile(test.__class__) if source_file: source_dir_splitted = source_file.split("/")[:-1] source_dir = "/".join(source_dir_splitted) + "/" else: source_dir = "" except TypeError: source_dir = "" suite = strclass(test.__class__) suite_location = "python_uttestid://" + source_dir + suite location = "python_uttestid://" + source_dir + str(test.id()) return (suite, location, suite_location) def startTest(self, test): self.current_failed = False setattr(test, "startTime", datetime.datetime.now()) def init_suite(self, test): suite, location, suite_location = self._getSuite(test) if suite != self.current_suite: if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = suite self.messages.testSuiteStarted(self.current_suite, location=suite_location) return location def stopTest(self, test): duration = self.__getDuration(test) if not self.subtest_suite: if not self.current_failed: location = self.init_suite(test) self.messages.testStarted(self.getTestName(test), location=location) self.messages.testFinished(self.getTestName(test), duration=int(duration)) else: self.messages.testSuiteFinished(self.subtest_suite) self.subtest_suite = None def __getDuration(self, test): start = getattr(test, "startTime", datetime.datetime.now()) assert isinstance( start, datetime.datetime ), "You testcase has property named 'startTime' (value {0}). Please, rename it".format(start) d = datetime.datetime.now() - start duration = d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000 return duration def addSubTest(self, test, subtest, err): location = self.init_suite(test) suite_name = self.getTestName(test) # + " (subTests)" if not self.subtest_suite: self.subtest_suite = suite_name self.messages.testSuiteStarted(self.subtest_suite, location=location) else: if suite_name != self.subtest_suite: self.messages.testSuiteFinished(self.subtest_suite) self.subtest_suite = suite_name self.messages.testSuiteStarted(self.subtest_suite, location=location) name = self.getTestName(subtest, True) if err is not None: error = self._exc_info_to_string(err, test) self.messages.testStarted(name) self.messages.testFailed(name, message="Failure", details=error, duration=None) else: self.messages.testStarted(name) self.messages.testFinished(name) def endLastSuite(self): if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = None def _unescape(self, text): # do not use text.decode('string_escape'), it leads to problems with different string encodings given return text.replace("\\n", "\n")
class TeamcityTestResult(TestResult): def __init__(self, stream=sys.stdout, *args, **kwargs): TestResult.__init__(self) for arg, value in kwargs.items(): setattr(self, arg, value) self.output = stream self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True) self.messages.testMatrixEntered() self.current_failed = False self.current_suite = None self.subtest_suite = None def find_first(self, val): quot = val[0] count = 1 quote_ind = val[count:].find(quot) while quote_ind != -1 and val[count + quote_ind - 1] == "\\": count = count + quote_ind + 1 quote_ind = val[count:].find(quot) return val[0:quote_ind + count + 1] def find_second(self, val): val_index = val.find("!=") if val_index != -1: count = 1 val = val[val_index + 2:].strip() quot = val[0] quote_ind = val[count:].find(quot) while quote_ind != -1 and val[count + quote_ind - 1] == "\\": count = count + quote_ind + 1 quote_ind = val[count:].find(quot) return val[0:quote_ind + count + 1] else: quot = val[-1] quote_ind = val[:len(val) - 1].rfind(quot) while quote_ind != -1 and val[quote_ind - 1] == "\\": quote_ind = val[:quote_ind - 1].rfind(quot) return val[quote_ind:] def formatErr(self, err): exctype, value, tb = err return ''.join(traceback.format_exception(exctype, value, tb)) def getTestName(self, test, is_subtest=False): if is_subtest: test_name = self.getTestName(test.test_case) return "{} {}".format(test_name, test._subDescription()) if hasattr(test, '_testMethodName'): if test._testMethodName == "runTest": return str(test) return test._testMethodName else: test_name = str(test) whitespace_index = test_name.index(" ") if whitespace_index != -1: test_name = test_name[:whitespace_index] return test_name def getTestId(self, test): return test.id def addSuccess(self, test): TestResult.addSuccess(self, test) def addError(self, test, err): self.init_suite(test) self.current_failed = True TestResult.addError(self, test, err) err = self._exc_info_to_string(err, test) self.messages.testStarted(self.getTestName(test)) self.messages.testError(self.getTestName(test), message='Error', details=err) def find_error_value(self, err): error_value = traceback.extract_tb(err) error_value = error_value[-1][-1] return error_value.split('assert')[-1].strip() def addFailure(self, test, err): self.init_suite(test) self.current_failed = True TestResult.addFailure(self, test, err) error_value = smart_str(err[1]) if not len(error_value): # means it's test function and we have to extract value from traceback error_value = self.find_error_value(err[2]) self_find_first = self.find_first(error_value) self_find_second = self.find_second(error_value) quotes = ["'", '"'] if (self_find_first[0] == self_find_first[-1] and self_find_first[0] in quotes and self_find_second[0] == self_find_second[-1] and self_find_second[0] in quotes): # let's unescape strings to show sexy multiline diff in PyCharm. # By default all caret return chars are escaped by testing framework first = self._unescape(self_find_first) second = self._unescape(self_find_second) else: first = second = "" err = self._exc_info_to_string(err, test) self.messages.testStarted(self.getTestName(test)) self.messages.testFailed(self.getTestName(test), message='Failure', details=err, expected=first, actual=second) def addSkip(self, test, reason): self.init_suite(test) self.current_failed = True self.messages.testIgnored(self.getTestName(test), message=reason) def __getSuite(self, test): if hasattr(test, "suite"): suite = strclass(test.suite) suite_location = test.suite.location location = test.suite.abs_location if hasattr(test, "lineno"): location = location + ":" + str(test.lineno) else: location = location + ":" + str(test.test.lineno) else: import inspect try: source_file = inspect.getsourcefile(test.__class__) if source_file: source_dir_splitted = source_file.split("/")[:-1] source_dir = "/".join(source_dir_splitted) + "/" else: source_dir = "" except TypeError: source_dir = "" suite = strclass(test.__class__) suite_location = "python_uttestid://" + source_dir + suite location = "python_uttestid://" + source_dir + str(test.id()) return (suite, location, suite_location) def startTest(self, test): self.current_failed = False setattr(test, "startTime", datetime.datetime.now()) def init_suite(self, test): suite, location, suite_location = self.__getSuite(test) if suite != self.current_suite: if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = suite self.messages.testSuiteStarted(self.current_suite, location=suite_location) return location def stopTest(self, test): start = getattr(test, "startTime", datetime.datetime.now()) d = datetime.datetime.now() - start duration = d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000 if not self.subtest_suite: if not self.current_failed: location = self.init_suite(test) self.messages.testStarted(self.getTestName(test), location=location) self.messages.testFinished(self.getTestName(test), duration=int(duration)) else: self.messages.testSuiteFinished(self.subtest_suite) self.subtest_suite = None def addSubTest(self, test, subtest, err): suite_name = self.getTestName(test) # + " (subTests)" if not self.subtest_suite: self.subtest_suite = suite_name self.messages.testSuiteStarted(self.subtest_suite) else: if suite_name != self.subtest_suite: self.messages.testSuiteFinished(self.subtest_suite) self.subtest_suite = suite_name self.messages.testSuiteStarted(self.subtest_suite) name = self.getTestName(subtest, True) if err is not None: error = self._exc_info_to_string(err, test) self.messages.testStarted(name) self.messages.testFailed(name, message='Failure', details=error) else: self.messages.testStarted(name) self.messages.testFinished(name) def endLastSuite(self): if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = None def _unescape(self, text): # do not use text.decode('string_escape'), it leads to problems with different string encodings given return text.replace("\\n", "\n")
class TeamcityPlugin(ErrorClassPlugin, TextTestResult, TeamcityTestResult): """ TeamcityTest plugin for nose tests """ name = "TeamcityPlugin" enabled = True def __init__(self, stream=sys.stderr, descriptions=None, verbosity=1, config=None, errorClasses=None): super(TeamcityPlugin, self).__init__() if errorClasses is None: errorClasses = {} self.errorClasses = errorClasses if config is None: config = Config() self.config = config self.output = stream self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True) self.messages.testMatrixEntered() self.current_suite = None TextTestResult.__init__(self, stream, descriptions, verbosity, config, errorClasses) TeamcityTestResult.__init__(self, stream) def configure(self, options, conf): if not self.can_configure: return self.conf = conf def _is_failure(self, test): try: return isinstance(test.test, Failure) except AttributeError: return False def addError(self, test, err): exctype, value, tb = err err = self.formatErr(err) if self._is_failure(test): self.messages.testError(_ERROR_TEST_NAME, message='Error', details=err, duration=self.__getDuration(test)) return if exctype == SkipTest: self.messages.testIgnored(self.getTestName(test), message='Skip') else: self.messages.testError(self.getTestName(test), message='Error', details=err, duration=self.__getDuration(test)) def formatErr(self, err): exctype, value, tb = err if isinstance(value, str): try: value = exctype(value) except TypeError: pass return ''.join(traceback.format_exception(exctype, value, tb)) def is_gen(self, test): if hasattr(test, "test") and hasattr(test.test, "descriptor"): if test.test.descriptor is not None: return True return False def getTestName(self, test): if hasattr(test, "error_context"): return test.error_context test_name_full = str(test) if self.is_gen(test): return test_name_full ind_1 = test_name_full.rfind('(') if ind_1 != -1: return test_name_full[:ind_1] return test_name_full def addFailure(self, test, err): err = self.formatErr(err) self.messages.testFailed(self.getTestName(test), message='Failure', details=err) def addSkip(self, test, reason): self.messages.testIgnored(self.getTestName(test), message=reason) def _getSuite(self, test): if hasattr(test, "suite"): suite = strclass(test.suite) suite_location = test.suite.location location = test.suite.abs_location if hasattr(test, "lineno"): location = location + ":" + str(test.lineno) else: location = location + ":" + str(test.test.lineno) else: suite = strclass(test.__class__) suite_location = "python_nosetestid://" + suite try: from nose.util import func_lineno if hasattr(test.test, "descriptor") and test.test.descriptor: suite_location = "file://" + self.test_address( test.test.descriptor) location = suite_location + ":" + str( func_lineno(test.test.descriptor)) else: suite_location = "file://" + self.test_address( test.test.test) location = "file://" + self.test_address( test.test.test) + ":" + str(func_lineno( test.test.test)) except: test_id = test.id() suite_id = test_id[:test_id.rfind(".")] suite_location = "python_nosetestid://" + str(suite_id) location = "python_nosetestid://" + str(test_id) return (location, suite_location) def test_address(self, test): if hasattr(test, "address"): return test.address()[0] t = type(test) file = None import types, os if (t == types.FunctionType or issubclass(t, type) or t == type or isclass(test)): module = getattr(test, '__module__', None) if module is not None: m = sys.modules[module] file = getattr(m, '__file__', None) if file is not None: file = os.path.abspath(file) if file.endswith("pyc"): file = file[:-1] return file raise TypeError("I don't know what %s is (%s)" % (test, t)) def getSuiteName(self, test): test_name_full = str(test) ind_1 = test_name_full.rfind('(') if self.is_gen(test) and ind_1 != -1: ind = test_name_full[:ind_1].rfind('.') if ind != -1: return test_name_full[:ind] if ind_1 != -1: return test_name_full[ind_1 + 1:-1] ind = test_name_full.rfind('.') if ind != -1: return test_name_full[:test_name_full.rfind(".")] return test_name_full def startTest(self, test): location, suite_location = self._getSuite(test) if self._is_failure(test): self.messages.testStarted(_ERROR_TEST_NAME, location=suite_location) return suite = self.getSuiteName(test) if suite != self.current_suite: if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = suite self.messages.testSuiteStarted(self.current_suite, location=suite_location) setattr(test, "startTime", datetime.datetime.now()) self.messages.testStarted(self.getTestName(test), location=location) def stopTest(self, test): duration = self.__getDuration(test) if self._is_failure(test): return # Finish reported by testError self.messages.testFinished(self.getTestName(test), duration=int(duration)) def __getDuration(self, test): start = getattr(test, "startTime", datetime.datetime.now()) d = datetime.datetime.now() - start duration = d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000 return duration def finalize(self, result): if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = None
class TeamcityPlugin(ErrorClassPlugin, TextTestResult, TeamcityTestResult): """ TeamcityTest plugin for nose tests """ name = "TeamcityPlugin" enabled = True def __init__(self, stream=sys.stderr, descriptions=None, verbosity=1, config=None, errorClasses=None): super(TeamcityPlugin, self).__init__() if errorClasses is None: errorClasses = {} self.errorClasses = errorClasses if config is None: config = Config() self.config = config self.output = stream self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True) self.messages.testMatrixEntered() self.current_suite = None TextTestResult.__init__(self, stream, descriptions, verbosity, config, errorClasses) TeamcityTestResult.__init__(self, stream) def configure(self, options, conf): if not self.can_configure: return self.conf = conf def addError(self, test, err): exctype, value, tb = err err = self.formatErr(err) if exctype == SkipTest: self.messages.testIgnored(self.getTestName(test), message='Skip') else: self.messages.testError(self.getTestName(test), message='Error', details=err, duration=self.__getDuration(test)) def formatErr(self, err): exctype, value, tb = err if isinstance(value, str): try: value = exctype(value) except TypeError: pass return ''.join(traceback.format_exception(exctype, value, tb)) def is_gen(self, test): if hasattr(test, "test") and hasattr(test.test, "descriptor"): if test.test.descriptor is not None: return True return False def getTestName(self, test): if hasattr(test, "error_context"): return test.error_context test_name_full = str(test) if self.is_gen(test): return test_name_full ind_1 = test_name_full.rfind('(') if ind_1 != -1: return test_name_full[:ind_1] return test_name_full def addFailure(self, test, err): err = self.formatErr(err) self.messages.testFailed(self.getTestName(test), message='Failure', details=err) def addSkip(self, test, reason): self.messages.testIgnored(self.getTestName(test), message=reason) def _getSuite(self, test): if hasattr(test, "suite"): suite = strclass(test.suite) suite_location = test.suite.location location = test.suite.abs_location if hasattr(test, "lineno"): location = location + ":" + str(test.lineno) else: location = location + ":" + str(test.test.lineno) else: suite = strclass(test.__class__) suite_location = "python_nosetestid://" + suite try: from nose.util import func_lineno if hasattr(test.test, "descriptor") and test.test.descriptor: suite_location = "file://" + self.test_address( test.test.descriptor) location = suite_location + ":" + str( func_lineno(test.test.descriptor)) else: suite_location = "file://" + self.test_address( test.test.test) location = "file://" + self.test_address( test.test.test) + ":" + str(func_lineno(test.test.test)) except: test_id = test.id() suite_id = test_id[:test_id.rfind(".")] suite_location = "python_nosetestid://" + str(suite_id) location = "python_nosetestid://" + str(test_id) return (location, suite_location) def test_address(self, test): if hasattr(test, "address"): return test.address()[0] t = type(test) file = None import types, os if (t == types.FunctionType or issubclass(t, type) or t == type or isclass(test)): module = getattr(test, '__module__', None) if module is not None: m = sys.modules[module] file = getattr(m, '__file__', None) if file is not None: file = os.path.abspath(file) if file.endswith("pyc"): file = file[:-1] return file raise TypeError("I don't know what %s is (%s)" % (test, t)) def getSuiteName(self, test): test_name_full = str(test) ind_1 = test_name_full.rfind('(') if self.is_gen(test) and ind_1 != -1: ind = test_name_full[:ind_1].rfind('.') if ind != -1: return test_name_full[:ind] if ind_1 != -1: return test_name_full[ind_1 + 1: -1] ind = test_name_full.rfind('.') if ind != -1: return test_name_full[:test_name_full.rfind(".")] return test_name_full def startTest(self, test): location, suite_location = self._getSuite(test) suite = self.getSuiteName(test) if suite != self.current_suite: if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = suite self.messages.testSuiteStarted(self.current_suite, location=suite_location) setattr(test, "startTime", datetime.datetime.now()) self.messages.testStarted(self.getTestName(test), location=location) def stopTest(self, test): duration = self.__getDuration(test) self.messages.testFinished(self.getTestName(test), duration=int(duration)) def __getDuration(self, test): start = getattr(test, "startTime", datetime.datetime.now()) d = datetime.datetime.now() - start duration = d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000 return duration def finalize(self, result): if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = None
class TeamcityTestResult(TestResult): def __init__(self, stream=sys.stdout, *args, **kwargs): TestResult.__init__(self) for arg, value in kwargs.items(): setattr(self, arg, value) self.output = stream self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True) self.messages.testMatrixEntered() self.current_suite = None def find_first(self, val): quot = val[0] count = 1 quote_ind = val[count:].find(quot) while val[count+quote_ind-1] == "\\" and quote_ind != -1: count = count + quote_ind + 1 quote_ind = val[count:].find(quot) return val[0:quote_ind+count+1] def find_second(self, val): val_index = val.find("!=") if val_index != -1: count = 1 val = val[val_index+2:].strip() quot = val[0] quote_ind = val[count:].find(quot) while val[count+quote_ind-1] == "\\" and quote_ind != -1: count = count + quote_ind + 1 quote_ind = val[count:].find(quot) return val[0:quote_ind+count+1] else: quot = val[-1] count = 0 quote_ind = val[:len(val)-count-1].rfind(quot) while val[quote_ind-1] == "\\": quote_ind = val[:quote_ind-1].rfind(quot) return val[quote_ind:] def formatErr(self, err): exctype, value, tb = err return ''.join(traceback.format_exception(exctype, value, tb)) def getTestName(self, test): if hasattr(test, '_testMethodName'): if test._testMethodName == "runTest": return str(test) return test._testMethodName else: test_name = str(test) whitespace_index = test_name.index(" ") if whitespace_index != -1: test_name = test_name[:whitespace_index] return test_name def getTestId(self, test): return test.id def addSuccess(self, test): TestResult.addSuccess(self, test) def addError(self, test, err): TestResult.addError(self, test, err) err = self._exc_info_to_string(err, test) self.messages.testError(self.getTestName(test), message='Error', details=err) def find_error_value(self, err): error_value = traceback.extract_tb(err) error_value = error_value[-1][-1] return error_value.split('assert')[-1].strip() def addFailure(self, test, err): TestResult.addFailure(self, test, err) error_value = smart_str(err[1]) if not len(error_value): # means it's test function and we have to extract value from traceback error_value = self.find_error_value(err[2]) self_find_first = self.find_first(error_value) self_find_second = self.find_second(error_value) quotes = ["'", '"'] if (self_find_first[0] == self_find_first[-1] and self_find_first[0] in quotes and self_find_second[0] == self_find_second[-1] and self_find_second[0] in quotes): # let's unescape strings to show sexy multiline diff in PyCharm. # By default all caret return chars are escaped by testing framework first = self._unescape(self_find_first) second = self._unescape(self_find_second) else: first = second = "" err = self._exc_info_to_string(err, test) self.messages.testFailed(self.getTestName(test), message='Failure', details=err, expected=first, actual=second) def addSkip(self, test, reason): self.messages.testIgnored(self.getTestName(test), message=reason) def __getSuite(self, test): if hasattr(test, "suite"): suite = strclass(test.suite) suite_location = test.suite.location location = test.suite.abs_location if hasattr(test, "lineno"): location = location + ":" + str(test.lineno) else: location = location + ":" + str(test.test.lineno) else: import inspect try: source_dir_splitted = inspect.getsourcefile(test.__class__).split("/")[:-1] source_dir = "/".join(source_dir_splitted) + "/" except TypeError: source_dir = "" suite = strclass(test.__class__) suite_location = "python_uttestid://" + source_dir + suite location = "python_uttestid://" + source_dir + str(test.id()) return (suite, location, suite_location) def startTest(self, test): suite, location, suite_location = self.__getSuite(test) if suite != self.current_suite: if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = suite self.messages.testSuiteStarted(self.current_suite, location=suite_location) setattr(test, "startTime", datetime.datetime.now()) self.messages.testStarted(self.getTestName(test), location=location) def stopTest(self, test): start = getattr(test, "startTime", datetime.datetime.now()) d = datetime.datetime.now() - start duration=d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000 self.messages.testFinished(self.getTestName(test), duration=int(duration)) def endLastSuite(self): if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = None def _unescape(self, text): # do not use text.decode('string_escape'), it leads to problems with different string encodings given return text.replace("\\n", "\n")