from tcunittest import TeamcityTestRunner, TeamcityTestResult from tcmessages import TeamcityServiceMessages import sys from pycharm_run_utils import adjust_django_sys_path adjust_django_sys_path() from django.conf import settings if hasattr(settings, "TEST_RUNNER") and "NoseTestSuiteRunner" in settings.TEST_RUNNER: from nose_utils import TeamcityNoseRunner from django.test.testcases import TestCase from django import VERSION try: from django.utils import unittest except ImportError: import unittest def get_test_suite_runner(): if hasattr(settings, "TEST_RUNNER"): from django.test.utils import get_runner class TempSettings: TEST_RUNNER = settings.TEST_RUNNER return get_runner(TempSettings) try: from django.test.simple import DjangoTestSuiteRunner from inspect import isfunction SUITE_RUNNER = get_test_suite_runner() if isfunction(SUITE_RUNNER): import sys sys.stderr.write( "WARNING: TEST_RUNNER variable is ignored. PyCharm test runner supports " "only class-like TEST_RUNNER valiables. Use Tools->run manage.py tasks.\n") SUITE_RUNNER = None BaseSuiteRunner = SUITE_RUNNER or DjangoTestSuiteRunner class BaseRunner(TeamcityTestRunner, BaseSuiteRunner): def __init__(self, stream=sys.stdout, **options): TeamcityTestRunner.__init__(self, stream) BaseSuiteRunner.__init__(self, **options) except ImportError: # for Django <= 1.1 compatibility class BaseRunner(TeamcityTestRunner): def __init__(self, stream=sys.stdout, **options): TeamcityTestRunner.__init__(self, stream) def strclass(cls): if not cls.__name__: return cls.__module__ return "%s.%s" % (cls.__module__, cls.__name__) class DjangoTeamcityTestResult(TeamcityTestResult): def __init__(self, *args, **kwargs): super(DjangoTeamcityTestResult, self).__init__(**kwargs) def _getSuite(self, test): if hasattr(test, "suite"): suite = strclass(test.suite) suite_location = test.suite.location location = test.suite.abs_location if hasattr(test, "lineno"): location = location + ":" + str(test.lineno) else: location = location + ":" + str(test.test.lineno) else: suite = strclass(test.__class__) suite_location = "django_testid://" + suite location = "django_testid://" + str(test.id()) return (suite, location, suite_location) class DjangoTeamcityTestRunner(BaseRunner): def __init__(self, stream=sys.stdout, **options): super(DjangoTeamcityTestRunner, self).__init__(stream, **options) self.options = options def _makeResult(self, **kwargs): return DjangoTeamcityTestResult(self.stream, **kwargs) def build_suite(self, *args, **kwargs): EXCLUDED_APPS = getattr(settings, 'TEST_EXCLUDE', []) suite = super(DjangoTeamcityTestRunner, self).build_suite(*args, **kwargs) if not args[0] and not getattr(settings, 'RUN_ALL_TESTS', False): tests = [] for case in suite: pkg = case.__class__.__module__.split('.')[0] if pkg not in EXCLUDED_APPS: tests.append(case) suite._tests = tests return suite def run_suite(self, suite, **kwargs): if hasattr(settings, "TEST_RUNNER") and "NoseTestSuiteRunner" in settings.TEST_RUNNER: from django_nose.plugin import DjangoSetUpPlugin, ResultPlugin from django_nose.runner import _get_plugins_from_settings from nose.config import Config import nose result_plugin = ResultPlugin() plugins_to_add = [DjangoSetUpPlugin(self), result_plugin] config = Config(plugins=nose.core.DefaultPluginManager()) config.plugins.addPlugins(extraplugins=plugins_to_add) for plugin in _get_plugins_from_settings(): plugins_to_add.append(plugin) nose.core.TestProgram(argv=suite, exit=False, addplugins=plugins_to_add, testRunner=TeamcityNoseRunner(config=config)) return result_plugin.result else: self.options.update(kwargs) return TeamcityTestRunner.run(self, suite, **self.options) def run_tests(self, test_labels, extra_tests=None, **kwargs): if hasattr(settings, "TEST_RUNNER") and "NoseTestSuiteRunner" in settings.TEST_RUNNER: return super(DjangoTeamcityTestRunner, self).run_tests(test_labels, extra_tests) return super(DjangoTeamcityTestRunner, self).run_tests(test_labels, extra_tests, **kwargs) def partition_suite(suite, classes, bins): """ Partitions a test suite by test type. classes is a sequence of types bins is a sequence of TestSuites, one more than classes Tests of type classes[i] are added to bins[i], tests with no match found in classes are place in bins[-1] """ for test in suite: if isinstance(test, unittest.TestSuite): partition_suite(test, classes, bins) else: for i in range(len(classes)): if isinstance(test, classes[i]): bins[i].addTest(test) break else: bins[-1].addTest(test) def reorder_suite(suite, classes): """ Reorders a test suite by test type. classes is a sequence of types All tests of type clases[0] are placed first, then tests of type classes[1], etc. Tests with no match in classes are placed last. """ class_count = len(classes) bins = [unittest.TestSuite() for i in range(class_count + 1)] partition_suite(suite, classes, bins) for i in range(class_count): bins[0].addTests(bins[i + 1]) return bins[0] def run_the_old_way(extra_tests, kwargs, test_labels, verbosity): from django.test.simple import build_suite, build_test, get_app, get_apps, \ setup_test_environment, teardown_test_environment setup_test_environment() settings.DEBUG = False suite = unittest.TestSuite() if test_labels: for label in test_labels: if '.' in label: suite.addTest(build_test(label)) else: app = get_app(label) suite.addTest(build_suite(app)) else: for app in get_apps(): suite.addTest(build_suite(app)) for test in extra_tests: suite.addTest(test) suite = reorder_suite(suite, (TestCase,)) old_name = settings.DATABASE_NAME from django.db import connection connection.creation.create_test_db(verbosity, autoclobber=False) result = DjangoTeamcityTestRunner().run(suite, **kwargs) connection.creation.destroy_test_db(old_name, verbosity) teardown_test_environment() return len(result.failures) + len(result.errors) def run_tests(test_labels, verbosity=1, interactive=False, extra_tests=[], **kwargs): """ Run the unit tests for all the test labels in the provided list. Labels must be of the form: - app.TestClass.test_method Run a single specific test method - app.TestClass Run all the test methods in a given class - app Search for doctests and unittests in the named application. When looking for tests, the test runner will look in the models and tests modules for the application. A list of 'extra' tests may also be provided; these tests will be added to the test suite. Returns the number of tests that failed. """ options = { 'verbosity': verbosity, 'interactive': interactive } options.update(kwargs) TeamcityServiceMessages(sys.stdout).testMatrixEntered() if VERSION[1] > 1: return DjangoTeamcityTestRunner(**options).run_tests(test_labels, extra_tests=extra_tests, **options) return run_the_old_way(extra_tests, options, test_labels, verbosity)