1from tcunittest import TeamcityTestRunner, TeamcityTestResult 2from tcmessages import TeamcityServiceMessages 3import sys 4from pycharm_run_utils import adjust_django_sys_path 5from django.test.utils import get_runner 6 7adjust_django_sys_path() 8 9from django.conf import settings 10 11def is_nosetest(settings): 12 """ 13 Checks if Django configured to work with nosetest 14 15 :param settings: django settings 16 :return: True if django should works with NoseTest runner of its inheritor 17 """ 18 try: 19 runner = get_runner(settings) 20 from django_nose import NoseTestSuiteRunner 21 if issubclass(runner, NoseTestSuiteRunner): 22 return True 23 except (AttributeError, ImportError): 24 pass 25 return False 26 27 28from django.test.testcases import TestCase 29from django import VERSION 30 31if is_nosetest(settings): 32 from nose_utils import TeamcityNoseRunner 33 34# See: https://docs.djangoproject.com/en/1.8/releases/1.7/#django-utils-unittest 35# django.utils.unittest provided uniform access to the unittest2 library on all Python versions. 36# Since unittest2 became the standard library's unittest module in Python 2.7, 37# and Django 1.7 drops support for older Python versions, this module isn't useful anymore. 38# It has been deprecated. Use unittest instead. 39if VERSION >= (1,7): 40 import unittest 41else: 42 from django.utils import unittest 43 44def get_test_suite_runner(): 45 if hasattr(settings, "TEST_RUNNER"): 46 from django.test.utils import get_runner 47 48 class TempSettings: 49 TEST_RUNNER = settings.TEST_RUNNER 50 51 return get_runner(TempSettings) 52 53try: 54 if VERSION >= (1,6): 55 from django.test.runner import DiscoverRunner as DjangoSuiteRunner 56 else: 57 from django.test.simple import DjangoTestSuiteRunner as DjangoSuiteRunner 58 59 from inspect import isfunction 60 61 SUITE_RUNNER = get_test_suite_runner() 62 if isfunction(SUITE_RUNNER): 63 import sys 64 65 sys.stderr.write( 66 "WARNING: TEST_RUNNER variable is ignored. PyCharm test runner supports " 67 "only class-like TEST_RUNNER valiables. Use Tools->run manage.py tasks.\n") 68 SUITE_RUNNER = None 69 BaseSuiteRunner = SUITE_RUNNER or DjangoSuiteRunner 70 71 class BaseRunner(TeamcityTestRunner, BaseSuiteRunner): 72 def __init__(self, stream=sys.stdout, **options): 73 TeamcityTestRunner.__init__(self, stream) 74 BaseSuiteRunner.__init__(self, **options) 75 76except ImportError: 77 # for Django <= 1.1 compatibility 78 class BaseRunner(TeamcityTestRunner): 79 def __init__(self, stream=sys.stdout, **options): 80 TeamcityTestRunner.__init__(self, stream) 81 82 83def strclass(cls): 84 if not cls.__name__: 85 return cls.__module__ 86 return "%s.%s" % (cls.__module__, cls.__name__) 87 88class DjangoTeamcityTestResult(TeamcityTestResult): 89 def __init__(self, *args, **kwargs): 90 super(DjangoTeamcityTestResult, self).__init__(**kwargs) 91 92 def _getSuite(self, test): 93 if hasattr(test, "suite"): 94 suite = strclass(test.suite) 95 suite_location = test.suite.location 96 location = test.suite.abs_location 97 if hasattr(test, "lineno"): 98 location = location + ":" + str(test.lineno) 99 else: 100 location = location + ":" + str(test.test.lineno) 101 else: 102 103 suite = strclass(test.__class__) 104 suite_location = "django_testid://" + suite 105 location = "django_testid://" + str(test.id()) 106 107 return (suite, location, suite_location) 108 109 110class DjangoTeamcityTestRunner(BaseRunner): 111 def __init__(self, stream=sys.stdout, **options): 112 super(DjangoTeamcityTestRunner, self).__init__(stream, **options) 113 self.options = options 114 115 def _makeResult(self, **kwargs): 116 return DjangoTeamcityTestResult(self.stream, **kwargs) 117 118 def build_suite(self, *args, **kwargs): 119 EXCLUDED_APPS = getattr(settings, 'TEST_EXCLUDE', []) 120 suite = super(DjangoTeamcityTestRunner, self).build_suite(*args, **kwargs) 121 if not args[0] and not getattr(settings, 'RUN_ALL_TESTS', False): 122 tests = [] 123 for case in suite: 124 pkg = case.__class__.__module__.split('.')[0] 125 if pkg not in EXCLUDED_APPS: 126 tests.append(case) 127 suite._tests = tests 128 return suite 129 130 def run_suite(self, suite, **kwargs): 131 if is_nosetest(settings): 132 from django_nose.plugin import DjangoSetUpPlugin, ResultPlugin 133 from django_nose.runner import _get_plugins_from_settings 134 from nose.config import Config 135 import nose 136 137 result_plugin = ResultPlugin() 138 plugins_to_add = [DjangoSetUpPlugin(self), result_plugin] 139 140 config = Config(plugins=nose.core.DefaultPluginManager()) 141 config.plugins.addPlugins(extraplugins=plugins_to_add) 142 143 for plugin in _get_plugins_from_settings(): 144 plugins_to_add.append(plugin) 145 nose.core.TestProgram(argv=suite, exit=False, addplugins=plugins_to_add, 146 testRunner=TeamcityNoseRunner(config=config)) 147 return result_plugin.result 148 149 else: 150 self.options.update(kwargs) 151 return TeamcityTestRunner.run(self, suite, **self.options) 152 153 def run_tests(self, test_labels, extra_tests=None, **kwargs): 154 if is_nosetest(settings): 155 return super(DjangoTeamcityTestRunner, self).run_tests(test_labels, extra_tests) 156 return super(DjangoTeamcityTestRunner, self).run_tests(test_labels, extra_tests, **kwargs) 157 158 159def partition_suite(suite, classes, bins): 160 """ 161 Partitions a test suite by test type. 162 163 classes is a sequence of types 164 bins is a sequence of TestSuites, one more than classes 165 166 Tests of type classes[i] are added to bins[i], 167 tests with no match found in classes are place in bins[-1] 168 """ 169 for test in suite: 170 if isinstance(test, unittest.TestSuite): 171 partition_suite(test, classes, bins) 172 else: 173 for i in range(len(classes)): 174 if isinstance(test, classes[i]): 175 bins[i].addTest(test) 176 break 177 else: 178 bins[-1].addTest(test) 179 180 181def reorder_suite(suite, classes): 182 """ 183 Reorders a test suite by test type. 184 185 classes is a sequence of types 186 187 All tests of type clases[0] are placed first, then tests of type classes[1], etc. 188 Tests with no match in classes are placed last. 189 """ 190 class_count = len(classes) 191 bins = [unittest.TestSuite() for i in range(class_count + 1)] 192 partition_suite(suite, classes, bins) 193 for i in range(class_count): 194 bins[0].addTests(bins[i + 1]) 195 return bins[0] 196 197 198def run_the_old_way(extra_tests, kwargs, test_labels, verbosity): 199 from django.test.simple import build_suite, build_test, get_app, get_apps, \ 200 setup_test_environment, teardown_test_environment 201 202 setup_test_environment() 203 settings.DEBUG = False 204 suite = unittest.TestSuite() 205 if test_labels: 206 for label in test_labels: 207 if '.' in label: 208 suite.addTest(build_test(label)) 209 else: 210 app = get_app(label) 211 suite.addTest(build_suite(app)) 212 else: 213 for app in get_apps(): 214 suite.addTest(build_suite(app)) 215 for test in extra_tests: 216 suite.addTest(test) 217 suite = reorder_suite(suite, (TestCase,)) 218 old_name = settings.DATABASE_NAME 219 from django.db import connection 220 221 connection.creation.create_test_db(verbosity, autoclobber=False) 222 result = DjangoTeamcityTestRunner().run(suite, **kwargs) 223 connection.creation.destroy_test_db(old_name, verbosity) 224 teardown_test_environment() 225 return len(result.failures) + len(result.errors) 226 227 228def run_tests(test_labels, verbosity=1, interactive=False, extra_tests=[], 229 **kwargs): 230 """ 231 Run the unit tests for all the test labels in the provided list. 232 Labels must be of the form: 233 - app.TestClass.test_method 234 Run a single specific test method 235 - app.TestClass 236 Run all the test methods in a given class 237 - app 238 Search for doctests and unittests in the named application. 239 240 When looking for tests, the test runner will look in the models and 241 tests modules for the application. 242 243 A list of 'extra' tests may also be provided; these tests 244 will be added to the test suite. 245 246 Returns the number of tests that failed. 247 """ 248 options = { 249 'verbosity': verbosity, 250 'interactive': interactive 251 } 252 options.update(kwargs) 253 TeamcityServiceMessages(sys.stdout).testMatrixEntered() 254 return DjangoTeamcityTestRunner(**options).run_tests(test_labels, 255 extra_tests=extra_tests, **options)