-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtestrunner.py
201 lines (159 loc) · 7.37 KB
/
testrunner.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import os
import sys
import nose
from nose.config import Config, all_config_files
from nose.plugins.manager import DefaultPluginManager
from django.core.management.base import BaseCommand
from django.test import utils
try:
from django.test.simple import DjangoTestSuiteRunner
except ImportError:
from djangosanetesting.runnercompat import DjangoTestSuiteRunner
from djangosanetesting.noseplugins import (
DjangoPlugin,
DjangoLiveServerPlugin, SeleniumPlugin, CherryPyLiveServerPlugin,
DjangoTranslationPlugin,
ResultPlugin,
)
__all__ = ("DstNoseTestSuiteRunner",)
"""
Act as Django test runner, but use nose. Enable common django-sane-testing
plugins by default.
You can use
DST_NOSE_ARGS = ['list', 'of', 'args']
in settings.py for arguments that you always want passed to nose.
Test runners themselves are basically copypasted from django-nose project.
(C) Jeff Balogh and contributors, released under BSD license.
Thanks and kudos.
Modified for django-sane-testing by Almad.
"""
def activate_plugin(plugin, argv=None):
argv = argv or sys.argv
if plugin.activation_parameter not in argv:
argv.append(plugin.activation_parameter)
try:
any
except NameError:
def any(iterable):
for element in iterable:
if element:
return True
return False
OPTION_TRANSLATION = {'--failfast': '-x'}
class DstNoseTestSuiteRunner(DjangoTestSuiteRunner):
def run_suite(self, nose_argv=None):
"""Test runner that invokes nose."""
# Prepare django for testing.
from django.conf import settings
utils.setup_test_environment()
old_db_name = settings.DATABASE_NAME
result_plugin = ResultPlugin()
plugins = [DjangoPlugin(), SeleniumPlugin(), DjangoTranslationPlugin(), result_plugin]
if getattr(settings, 'CHERRYPY_TEST_SERVER', False):
plugins.append(CherryPyLiveServerPlugin())
else:
plugins.append(DjangoLiveServerPlugin())
# Do not pretend it's a production environment.
# settings.DEBUG = False
# We pass nose a list of arguments that looks like sys.argv, but customize
# to avoid unknown django arguments.
for plugin in _get_plugins_from_settings():
plugins_to_add.append(plugin)
# activate all required plugins
activate_plugin(DjangoPlugin, nose_argv)
activate_plugin(SeleniumPlugin, nose_argv)
activate_plugin(DjangoTranslationPlugin, nose_argv)
# activate_plugin(ResultPlugin, nose_argv)
if getattr(settings, 'CHERRYPY_TEST_SERVER', False):
activate_plugin(CherryPyLiveServerPlugin, nose_argv)
else:
activate_plugin(DjangoLiveServerPlugin, nose_argv)
# Skip over 'manage.py test' and any arguments handled by django.
django_opts = ['--noinput']
for opt in BaseCommand.option_list:
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))
if self.verbosity >= 2:
print ' '.join(nose_argv)
test_program = nose.core.TestProgram(argv=nose_argv, exit=False,
addplugins=plugins)
# FIXME: ResultPlugin is working not exactly as advertised in django-nose
# multiple instance problem, find workaround
# result = result_plugin.result
# return len(result.failures) + len(result.errors)
return not test_program.success
def run_tests(self, test_labels, extra_tests=None):
"""
Run the unit tests for all the test names in the provided list.
Test names specified may be file or module names, and may optionally
indicate the test case to run by separating the module or file name
from the test case name with a colon. Filenames may be relative or
absolute. Examples:
runner.run_tests('test.module')
runner.run_tests('another.test:TestCase.test_method')
runner.run_tests('a.test:TestCase')
runner.run_tests('/path/to/test/file.py:test_function')
Returns the number of tests that failed.
"""
from django.conf import settings
nose_argv = ['nosetests', '--verbosity', str(self.verbosity)] + list(test_labels)
if hasattr(settings, 'NOSE_ARGS'):
nose_argv.extend(settings.NOSE_ARGS)
# Skip over 'manage.py test' and any arguments handled by django.
django_opts = ['--noinput']
for opt in BaseCommand.option_list:
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))
if self.verbosity >= 2:
print ' '.join(nose_argv)
result = self.run_suite(nose_argv)
### FIXME
class SimpleResult(object): pass
res = SimpleResult()
res.failures = ['1'] if result else []
res.errors = []
# suite_result expects the suite as the first argument. Fake it.
return self.suite_result({}, res)
def _get_options():
"""Return all nose options that don't conflict with django options."""
cfg_files = nose.core.all_config_files()
manager = nose.core.DefaultPluginManager()
config = nose.core.Config(env=os.environ, files=cfg_files, plugins=manager)
options = config.getParser().option_list
django_opts = [opt.dest for opt in BaseCommand.option_list] + ['version']
return tuple(o for o in options if o.dest not in django_opts and
o.action != 'help')
def _get_plugins_from_settings():
from django.conf import 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)
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))
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))
yield p_class()
# Replace the builtin command options with the merged django/nose options.
DstNoseTestSuiteRunner.options = _get_options()
DstNoseTestSuiteRunner.__test__ = False
def run_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=None):
test_runner = DstNoseTestSuiteRunner(verbosity=verbosity, interactive=interactive, failfast=failfast)
return test_runner.run_tests(test_labels, extra_tests=extra_tests)