Permalink
Browse files

Merge branch 'mergetest' into mjtorn-master

  • Loading branch information...
2 parents c6529e6 + b31e4a7 commit 8661f9c60abdd0513c7e8c441b4162228631d14b Markus Törnqvist committed Aug 28, 2011
Showing with 100 additions and 18 deletions.
  1. +2 −0 MANIFEST.in
  2. +82 −7 django_nose/plugin.py
  3. +15 −11 django_nose/runner.py
  4. +1 −0 setup.py
View
@@ -1,3 +1,5 @@
include LICENSE
include README.rst
+recursive-exclude django_nose *.py[co]
recursive-include testapp *
+recursive-exclude testapp *.py[co]
View
@@ -1,5 +1,11 @@
+import os.path
import sys
+from django.conf import settings
+from django.db.models.loading import get_apps, load_app
+from django.test.testcases import TransactionTestCase
+
+
class ResultPlugin(object):
"""
Captures the TestResult object for later inspection.
@@ -21,6 +27,11 @@ class DjangoSetUpPlugin(object):
Configures Django to setup and tear down the environment.
This allows coverage to report on all code imported and used during the
initialisation of the test runner.
+
+ Only sets up databases if a single class inherits from
+ ``django.test.testcases.TransactionTestCase``.
+
+ Also ensures you don't run the same test case multiple times.
"""
name = "django setup"
enabled = True
@@ -29,18 +40,82 @@ def __init__(self, runner):
super(DjangoSetUpPlugin, self).__init__()
self.runner = runner
self.sys_stdout = sys.stdout
+ self.sys_stderr = sys.stderr
+ self.needs_db = False
+ self.started = False
+ self._registry = set()
def begin(self):
- """Setup the environment"""
- sys_stdout = sys.stdout
+ self.add_apps = set()
+
+ def wantClass(self, cls):
+ if issubclass(cls, TransactionTestCase):
+ self.needs_db = True
+
+ if cls in self._registry:
+ return False
+ self._registry.add(cls)
+
+ def wantMethod(self, method):
+ if issubclass(method.im_class, TransactionTestCase):
+ self.needs_db = True
+
+ if method in self._registry:
+ return False
+ self._registry.add(method)
+
+ def wantFunction(self, function):
+ if function in self._registry:
+ return False
+ self._registry.add(function)
+
+ def beforeImport(self, filename, module):
+ # handle case of tests.models
+ if not os.path.isdir(filename):
+ filepath = os.path.dirname(filename)
+ module = module.rsplit('.', 1)[0]
+ else:
+ filepath = filename
+
+ models_path = os.path.join(filepath, 'models.py')
+ if os.path.exists(models_path):
+ self.add_apps.add(module)
+
+ # handle case of fooapp.tests, where fooapp.models exists
+ models_path = os.path.join(filepath, os.pardir, 'models.py')
+ if os.path.exists(models_path):
+ self.add_apps.add(module.rsplit('.', 1)[0])
+
+ def prepareTestRunner(self, test):
+ cur_stdout = sys.stdout
+ cur_stderr = sys.stderr
+
sys.stdout = self.sys_stdout
+ sys.stderr = self.sys_stderr
+
+ if self.add_apps:
+ for app in self.add_apps:
+ if app in settings.INSTALLED_APPS:
+ continue
+ mod = load_app(app)
+ if mod:
+ settings.INSTALLED_APPS.append(app)
+
+ get_apps()
self.runner.setup_test_environment()
- self.old_names = self.runner.setup_databases()
- sys.stdout = sys_stdout
+ if self.needs_db:
+ self.old_names = self.runner.setup_databases()
+
+ sys.stdout = cur_stdout
+ sys.stderr = cur_stderr
+
+ self.started = True
def finalize(self, result):
- """Destroy the environment"""
- self.runner.teardown_databases(self.old_names)
- self.runner.teardown_test_environment()
+ if self.started:
+ if self.needs_db:
+ self.runner.teardown_databases(self.old_names)
+
+ self.runner.teardown_test_environment()
View
@@ -65,7 +65,8 @@ def run_tests(self, test_labels, extra_tests=None):
Returns the number of tests that failed.
"""
- nose_argv = ['nosetests', '--verbosity', str(self.verbosity)] + list(test_labels)
+ nose_argv = (['nosetests', '--verbosity', str(self.verbosity)]
+ + list(test_labels))
if hasattr(settings, 'NOSE_ARGS'):
nose_argv.extend(settings.NOSE_ARGS)
@@ -75,9 +76,10 @@ def run_tests(self, test_labels, extra_tests=None):
django_opts.extend(opt._long_opts)
django_opts.extend(opt._short_opts)
- nose_argv.extend(OPTION_TRANSLATION.get(opt, opt)
- for opt in sys.argv[1:]
- if opt.startswith('-') and not any(opt.startswith(d) for d in django_opts))
+ nose_argv.extend(
+ OPTION_TRANSLATION.get(opt, opt) for opt in sys.argv[1:]
+ if opt.startswith('-')
+ and not any(opt.startswith(d) for d in django_opts))
if self.verbosity >= 1:
print ' '.join(nose_argv)
@@ -98,26 +100,28 @@ def _get_options():
return tuple(o for o in options if o.dest not in django_opts and
o.action != 'help')
+
def _get_plugins_from_settings():
if hasattr(settings, 'NOSE_PLUGINS'):
for plg_path in settings.NOSE_PLUGINS:
try:
dot = plg_path.rindex('.')
except ValueError:
- raise exceptions.ImproperlyConfigured(
- '%s isn\'t a Nose plugin module' % plg_path)
+ msg = "%s isn't a Nose plugin module" % plg_path
+ raise exceptions.ImproperlyConfigured(msg)
p_mod, p_classname = plg_path[:dot], plg_path[dot+1:]
try:
mod = import_module(p_mod)
except ImportError, e:
- raise exceptions.ImproperlyConfigured(
- 'Error importing Nose plugin module %s: "%s"' % (p_mod, e))
+ msg = ('Error importing Nose plugin module %s: "%s"' %
+ (p_mod, e))
+ raise exceptions.ImproperlyConfigured(msg)
try:
p_class = getattr(mod, p_classname)
except AttributeError:
- raise exceptions.ImproperlyConfigured(
- 'Nose plugin module "%s" does not define a "%s" class' % (
- p_mod, p_classname))
+ msg = ('Nose plugin module "%s" does not define a "%s" class' %
+ (p_mod, p_classname))
+ raise exceptions.ImproperlyConfigured(msg)
yield p_class()
# Replace the builtin command options with the merged django/nose options.
View
@@ -16,6 +16,7 @@
include_package_data=True,
zip_safe=False,
install_requires=['nose'],
+ tests_require=['Django', 'south'],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',

0 comments on commit 8661f9c

Please sign in to comment.