class KnownFailurePlugin(ErrorClassPlugin): '''Plugin that installs a KNOWNFAIL error class for the KnownFailureClass exception. When KnownFailure is raised, the exception will be logged in the knownfail attribute of the result, 'K' or 'KNOWNFAIL' (verbose) will be output, and the exception will not be counted as an error or failure.''' enabled = True knownfail = ErrorClass(KnownFailureException, label='KNOWNFAIL', isfailure=False) def options(self, parser, env=os.environ): env_opt = 'NOSE_WITHOUT_KNOWNFAIL' parser.add_option('--no-knownfail', action='store_true', dest='noKnownFail', default=env.get(env_opt, False), help='Disable special handling of KnownFailure ' 'exceptions') def configure(self, options, conf): if not self.can_configure: return self.conf = conf disable = getattr(options, 'noKnownFail', False) if disable: self.enabled = False
class KnownIssue(ExtendedPlugin, ErrorClassPlugin): """ Installs a DEPRECATED error class for the DeprecatedTest exception. Enabled by default. """ enabled = True known_issue = ErrorClass(KnownIssueTest, label='KNOWNISSUE', isfailure=True) def options(self, parser, env): """Register commandline options. """ env_opt = 'NOSE_WITHOUT_KNOWNISSUE' parser.add_option('--no-knownissue', action='store_true', dest='noKnownIssue', default=env.get(env_opt, False), help="Disable special handling of KnownIssueTest " "exceptions.") def configure(self, options, conf): """Configure plugin. """ if not self.can_configure: return self.conf = conf disable = getattr(options, 'noKnownIssue', False) if disable: self.enabled = False
class Skip(ErrorClassPlugin): """ Plugin that installs a SKIP error class for the SkipTest exception. When SkipTest is raised, the exception will be logged in the skipped attribute of the result, 'S' or 'SKIP' (verbose) will be output, and the exception will not be counted as an error or failure. """ enabled = True skipped = ErrorClass(SkipTest, label='SKIP', isfailure=False) def options(self, parser, env): """ Add my options to command line. """ env_opt = 'NOSE_WITHOUT_SKIP' parser.add_option('--no-skip', action='store_true', dest='noSkip', default=env.get(env_opt, False), help="Disable special handling of SkipTest " "exceptions.") def configure(self, options, conf): """ Configure plugin. Skip plugin is enabled by default. """ if not self.can_configure: return self.conf = conf disable = getattr(options, 'noSkip', False) if disable: self.enabled = False
class KnownFailure(ErrorClassPlugin): """Plugin that installs a KNOWNFAIL error class for the KnownFailureClass exception. When KnownFailureTest is raised, the exception will be logged in the knownfail attribute of the result, 'K' or 'KNOWNFAIL' (verbose) will be output, and the exception will not be counted as an error or failure.""" enabled = True knownfail = ErrorClass(KnownFailureTest, label="KNOWNFAIL", isfailure=False) def options(self, parser, env=os.environ): env_opt = "NOSE_WITHOUT_KNOWNFAIL" parser.add_option( "--no-knownfail", action="store_true", dest="noKnownFail", default=env.get(env_opt, False), help="Disable special handling of KnownFailureTest " "exceptions", ) def configure(self, options, conf): if not self.can_configure: return self.conf = conf disable = getattr(options, "noKnownFail", False) if disable: self.enabled = False
class TodoWarningPlugin(ErrorClassPlugin): """ Installs a TODO warning class for the TodoWarningException. When TodoWarningException is raised, the exception will be logged in the todo attribute of the result, detailed reason will be logged in warning_list, 'W' or 'Warning' (verbose) will be output, and the exception will not be counted as an error or failure. """ name = 'todo' todo = ErrorClass(TodoWarningException, label='Warning', isfailure=False) warnings = [] def formatError(self, test, err): """ Customize formatError plugin method. Add error details to warnings list to be used in finslize method. """ if not isinstance(err[1], TodoWarningException): return err reason = (err[1].reason, err[1].error) self.warnings.append(reason) ec, ev, tb = err return (ec, ev, tb) def finalize(self, result): """ Customize finalize plugin method. Logging warning messages. """ warnings.formatwarning = custom_formatwarning for i in range(len(result.todo)): # warnings output after test results warnings.warn('{}; error {} (in {})\n'.format( self.warnings[i][0], self.warnings[i][1], result.todo[i][0]))
class Deprecated(ErrorClassPlugin): """ Installs a DEPRECATED error class for the DeprecatedTest exception. Enabled by default. """ enabled = True deprecated = ErrorClass(DeprecatedTest, label='DEPRECATED', isfailure=False) def options(self, parser, env): """Register commandline options. """ env_opt = 'NOSE_WITHOUT_DEPRECATED' parser.add_option('--no-deprecated', action='store_true', dest='noDeprecated', default=env.get(env_opt, False), help="Disable special handling of DeprecatedTest " "exceptions.") def configure(self, options, conf): """Configure plugin. """ if not self.can_configure: return self.conf = conf disable = getattr(options, 'noDeprecated', False) if disable: self.enabled = False
class Deprecated(ErrorClassPlugin): """ Plugin that installs a DEPRECATED error class for the DeprecatedTest exception. Enabled by default. When DeprecatedTest is raised, the exception will be logged in the deprecated attribute of the result, 'D' or 'DEPRECATED' (verbose) will be output, and the exception will not be counted as an error or failure. """ enabled = True deprecated = ErrorClass(DeprecatedTest, label='DEPRECATED', isfailure=False) def options(self, parser, env=os.environ): env_opt = 'NOSE_WITHOUT_DEPRECATED' parser.add_option('--no-deprecated', action='store_true', dest='noDeprecated', default=env.get(env_opt, False), help="Disable special handling of DeprecatedTest " "exceptions.") def configure(self, options, conf): if not self.can_configure: return self.conf = conf disable = getattr(options, 'noDeprecated', False) if disable: self.enabled = False
class NotImplementedNoFailurePlugin(ErrorClassPlugin): enabled = True notimplemented = ErrorClass(NotImplementedError, label='NOT_IMPLEMENTED', isfailure=False) def configure(self, options, conf): # For some reason, this only works if this method exists... pass
class FreshenErrorPlugin(ErrorClassPlugin): enabled = True undefined = ErrorClass(UndefinedStepImpl, label="UNDEFINED", isfailure=False) def options(self, parser, env): # Forced to be on! pass
class TodoWarningPlugin(ErrorClassPlugin): """ Installs a TODO warning class for the Warning exception. When Warning is raised, the exception will be logged in the todo attribute of the result, 'W' or 'WarnTODO' (verbose) will be output, and the exception will not be counted as an error or failure. """ name = 'todo' todo = ErrorClass(Warning, label='WarnTODO', isfailure=False)
class Memleak(ErrorClassPlugin): """ Report memleaks (objects deemed uncollectable by python's garbage collector) on a test-by-test basis. """ name = "memleak" enabled = False memleak = ErrorClass(MemleakError, label='MEMLEAK', isfailure=True) def configure(self, options, conf): super(Memleak, self).configure(options, conf) self.config = conf # This will let other tests know about config settings. def startTest(self, test): # we want to change the ResultProxy object, not just the result, # therefore we do it here, not in prepareTestResult rp = test.test._resultForDoCleanups # we store the original func rp._ml_add_success = rp.addSuccess # and replace it rp.addSuccess = make_instancemethod(_MemleakResult.addSuccess, rp) # also add a default failure flag rp._ml_is_success = False # Finally, we store with the test the names that it has at birth, # so that we only wipe stuff created during the test. test._mda_pretest_attrs = test.test.__dict__.keys() def stopTest(self, test): rp = test.test._resultForDoCleanups # cleanup of the patched attributes as soon as possible, to minimize potential clashes rp.addSuccess = rp._ml_add_success del rp._ml_add_success attrs = [] for attrname in test._mda_pretest_attrs: try: attrs.append((attrname, getattr(test.test, attrname))) except AttributeError: pass test.test.__dict__.clear() gc.collect() latest_leaks = [ obj for obj in gc.garbage if obj not in _leakedobjs ] _leakedobjs.update(gc.garbage) # Restore the pre-test stuff for name, val in attrs: setattr(test.test, name, val) if latest_leaks: raise MemleakError("GC failed to collect the following: {}".format(latest_leaks)) elif rp._ml_is_success: rp.addSuccess(test) del rp._ml_is_success
class Memleak(ErrorClassPlugin): """Report memleaks on a test-by-test basis.""" name = "memleak" enabled = False memleak = ErrorClass(MemleakError, label='MEMLEAK', isfailure=True) def configure(self, options, conf): super(Memleak, self).configure(options, conf) self.config = conf # This will let other tests know about config settings. def beforeTest(self, test): # We register our check function and record with it the names that # the test has at birth so that later we only wipe stuff created # during testing. # We really don't want a smart dict_keys object here, as it'd be # changed during testing. test.test.addCleanup(memleak_check, test.test, list(test.test.__dict__.keys()))
class KnownFailure(ErrorClassPlugin): '''Plugin that installs a KNOWNFAIL error class for the KnownFailureClass exception. When KnownFailureTest is raised, the exception will be logged in the knownfail attribute of the result, 'K' or 'KNOWNFAIL' (verbose) will be output, and the exception will not be counted as an error or failure. This is based on numpy.testing.noseclasses.KnownFailure. ''' enabled = True knownfail = ErrorClass(KnownFailureTest, label='KNOWNFAIL', isfailure=False) def options(self, parser, env=os.environ): env_opt = 'NOSE_WITHOUT_KNOWNFAIL' parser.add_option('--no-knownfail', action='store_true', dest='noKnownFail', default=env.get(env_opt, False), help='Disable special handling of KnownFailureTest ' 'exceptions') def configure(self, options, conf): if not self.can_configure: return self.conf = conf disable = getattr(options, 'noKnownFail', False) if disable: self.enabled = False def addError(self, test, err, *zero_nine_capt_args): # Fixme (Really weird): if I don't leave empty method here, # nose gets confused and KnownFails become testing errors when # using the MplNosePlugin and MplTestCase. # The *zero_nine_capt_args captures an extra argument. There # seems to be a bug in # nose.testing.manager.ZeroNinePlugin.addError() in which a # 3rd positional argument ("capt") is passed to the plugin's # addError() method, even if one is not explicitly using the # ZeroNinePlugin. pass
class Markdown(ErrorClassPlugin): """ Add MarkdownSyntaxError and ensure proper formatting. """ mdsyntax = ErrorClass(MarkdownSyntaxError, label='MarkdownSyntaxError', isfailure=True) enabled = True def configure(self, options, conf): self.conf = conf def addError(self, test, err): """ Ensure other plugins see the error by returning nothing here. """ pass def formatError(self, test, err): """ Remove unnessecary and unhelpful traceback from error report. """ et, ev, tb = err if et.__name__ == 'MarkdownSyntaxError': return et, ev, '' return err
class RichErrorReportingPlugin(ErrorClassPlugin): """Nose plugin that installs error output handling for a richer error set (as described elsewhere in this package). """ # Standard Plugin attributes name = "rich-errors" # Define error classes that we handle #TODO Be able to customise the single letter labels broken = ErrorClass( BrokenTestException, label='BROKEN', isfailure=False ) #TODO should this be a failure? If so, we want to stop displaying tracebacks for it. excluded = ErrorClass(ExcludeTestException, label='XCLUDE', isfailure=False) #TODO better label irrelevant = ErrorClass(IrrelevantTestException, label='IRRELEVANT', isfailure=False) misconfigured = ErrorClass(InvalidTestConfigurationException, label='CONFIG_WRONG', isfailure=True) #TODO better label transient = ErrorClass(TransientTestError, label="TRANSIENT", isfailure=False) unimplemented = ErrorClass( NotImplementedError, label="UNIMPLEMENTED", isfailure=False ) #TODO should this be a failure? If so, we want to stop displaying tracebacks for it. # Use default implementation of options() and configure to enable this plugin def configure(self, options, conf): super(RichErrorReportingPlugin, self).configure(options, conf) if self.enabled: # The xunit plugin only has limited categories for errors, and # does not provide any method to configure it's behaviour. # # Therefore we monkey patch the xunit plugin so that it reports our # non-failure exceptions as skip's. The easiest way to achieve this # is to change SkipTest to be a tuple of skippable error types, so # that the issubclass test in Xunit.addError will return True. # # Note that this is *extremely* dodgy. from nose.plugins import xunit if inspect.isclass(xunit.SkipTest): logger.info( "Monkey patching xunit plugin to recognise rich errors...") new_skips = [xunit.SkipTest] for cls, (name, label, isfailure) in self.errorClasses: if not isfailure: new_skips.append(cls) xunit.SkipTest = tuple(new_skips) else: logger.warn("Unable to monkey-patch xunit plugin, since it's " \ "SkipTest definition has an unexpected type: %s", xunit.SkipTest) # Take the same approach for integrating with teamcity-messages # (another test reporting plugin). try: import teamcity.nose_report except ImportError: logger.info( "teamcity-messages plugin is not present, so it won't " "be monkey-patched", exc_info=True) else: if inspect.isclass(teamcity.nose_report.SkipTest): logger.info("Monkey patching teamcity-messages plugin to " "recognise rich errors...") new_skips = [teamcity.nose_report.SkipTest] for cls, (name, label, isfailure) in self.errorClasses: if not isfailure: new_skips.append(cls) teamcity.nose_report.SkipTest = tuple(new_skips) else: logger.warn( "Unable to monkey-patch teamcity-messages plugin, " "since it's SkipTest definition has an unexpected " "type: %s", teamcity.nose_report.SkipTest) def help(self): return "Display richer error types."
class TodoPlugin(ErrorClassPlugin): todo = ErrorClass(Todo, label='TODO', isfailure=True)
class EP(ErrorClassPlugin): xes = ErrorClass(X, label='XXX', isfailure=True)
class KnownFailureError(ErrorClassPlugin): todo = ErrorClass(KnownFailure, label='KNOWNFAIL', isfailure=True)
class NonFailureTodoPlugin(ErrorClassPlugin): name = "non-failure-todo" todo = ErrorClass(Todo, label='TODO', isfailure=False)
class TodoPlugin(ErrorClassPlugin): name = "todo" todo = ErrorClass(Todo, label='TODO', isfailure=False)