# Adapted from unittest2/loader.py from the unittest2 plugins branch.
# This module contains some code copied from unittest2/loader.py and other
# code developed in reference to that module and others within unittest2.
# unittest2 is Copyright (c) 2001-2010 Python Software Foundation; All
# Rights Reserved. See: http://docs.python.org/license.html
import logging
import traceback
import unittest
from nose2 import events
from nose2._vendor import six
log = logging.getLogger(__name__)
__unittest = True
[docs]class PluggableTestLoader(object):
"""Test loader that defers all loading to plugins
:param session: Test run session.
.. attribute :: suiteClass
Suite class to use. Default: :class:`unittest.TestSuite`.
"""
suiteClass = unittest.TestSuite
def __init__(self, session):
self.session = session
[docs] def loadTestsFromModule(self, module):
"""Load tests from module.
Fires :func:`loadTestsFromModule` hook.
"""
evt = events.LoadFromModuleEvent(self, module)
result = self.session.hooks.loadTestsFromModule(evt)
if evt.handled:
suite = result or self.suiteClass()
else:
suite = self.suiteClass(evt.extraTests)
filterevt = events.ModuleSuiteEvent(self, module, suite)
result = self.session.hooks.moduleLoadedSuite(filterevt)
if result:
return result or self.suiteClass()
return filterevt.suite
[docs] def loadTestsFromNames(self, testNames, module=None):
"""Load tests from test names.
Fires :func:`loadTestsFromNames` hook.
"""
event = events.LoadFromNamesEvent(self, testNames, module)
result = self.session.hooks.loadTestsFromNames(event)
log.debug("loadTestsFromNames event %s result %s", event, result)
if event.handled:
suites = result or []
else:
if event.names:
suites = [self.loadTestsFromName(name, module) for name in event.names]
elif module:
suites = self.loadTestsFromModule(module)
if event.extraTests:
suites.extend(event.extraTests)
return self.suiteClass(suites)
[docs] def loadTestsFromName(self, name, module=None):
"""Load tests from test name.
Fires :func:`loadTestsFromName` hook.
"""
log.debug("loadTestsFromName %s/%s", name, module)
event = events.LoadFromNameEvent(self, name, module)
result = self.session.hooks.loadTestsFromName(event)
if event.handled:
suite = result or self.suiteClass()
return suite
return self.suiteClass(event.extraTests)
[docs] def failedImport(self, name):
"""Make test case representing a failed import."""
message = "Failed to import test module: %s" % name
if hasattr(traceback, "format_exc"):
# Python 2.3 compatibility
# format_exc returns two frames of discover.py as well XXX ?
message += "\n%s" % traceback.format_exc()
return self._makeFailedTest("ModuleImportFailure", name, ImportError(message))
[docs] def failedLoadTests(self, name, exception):
"""Make test case representing a failed test load."""
return self._makeFailedTest("LoadTestsFailure", name, exception)
[docs] def sortTestMethodsUsing(self, name):
"""Sort key for test case test methods."""
return name.lower()
[docs] def discover(self, start_dir=None, pattern=None):
"""Compatibility shim for ``load_tests`` protocol."""
try:
oldsd = self.session.startDir
self.session.startDir = start_dir
return self.loadTestsFromNames([])
finally:
self.session.startDir = oldsd
def _makeFailedTest(self, classname, methodname, exception):
def testFailure(self):
if isinstance(exception, Exception):
raise exception
else:
# exception tuple (type, value, traceback)
six.reraise(*exception)
attrs = {methodname: testFailure}
TestClass = type(classname, (unittest.TestCase,), attrs)
return self.suiteClass((TestClass(methodname),))
def __repr__(self):
return "<%s>" % self.__class__.__name__