diff --git a/python/helpers/pycharm/django_test_runner.py b/python/helpers/pycharm/django_test_runner.py index 707ff9702c0b..c25f827372ff 100644 --- a/python/helpers/pycharm/django_test_runner.py +++ b/python/helpers/pycharm/django_test_runner.py @@ -9,35 +9,39 @@ import sys 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") + + 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) + TeamcityTestRunner.__init__(self, stream) BaseSuiteRunner.__init__(self) except ImportError: class BaseRunner(TeamcityTestRunner): def __init__(self, stream=sys.stdout, **options): - TeamcityTestRunner.__init__(self,stream) + TeamcityTestRunner.__init__(self, stream) class DjangoTeamcityTestRunner(BaseRunner): def __init__(self, stream=sys.stdout, **options): - BaseRunner.__init__(self, stream) - #self.interactive = False + super(DjangoTeamcityTestRunner, self).__init__(stream) def build_suite(self, *args, **kwargs): EXCLUDED_APPS = getattr(settings, 'TEST_EXCLUDE', []) @@ -69,100 +73,113 @@ class DjangoTeamcityTestRunner(BaseRunner): from nose_utils import TeamcityNoseRunner - nose.core.TestProgram(argv=suite, exit=False, testRunner=TeamcityNoseRunner(config=config)) + nose.core.TestProgram(argv=suite, exit=False, + testRunner=TeamcityNoseRunner(config=config)) return result_plugin.result else: return TeamcityTestRunner.run(self, suite, **kwargs) + 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. + """ + Partitions a test suite by test type. - classes is a sequence of types - bins is a sequence of TestSuites, one more than classes + 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) - 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. + """ + Reorders a test suite by test type. - classes is a sequence of types + 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] + 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_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. +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. - A list of 'extra' tests may also be provided; these tests - will be added to the test suite. + When looking for tests, the test runner will look in the models and + tests modules for the application. - Returns the number of tests that failed. - """ - TeamcityServiceMessages(sys.stdout).testMatrixEntered() - if django.VERSION[1] > 1: - return DjangoTeamcityTestRunner().run_tests(test_labels, extra_tests=extra_tests, **kwargs) + A list of 'extra' tests may also be provided; these tests + will be added to the test suite. - setup_test_environment() + Returns the number of tests that failed. + """ + TeamcityServiceMessages(sys.stdout).testMatrixEntered() + if django.VERSION[1] > 1: + return DjangoTeamcityTestRunner().run_tests(test_labels, + extra_tests=extra_tests, **kwargs) - settings.DEBUG = False - suite = unittest.TestSuite() + setup_test_environment() - 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)) + settings.DEBUG = False + suite = unittest.TestSuite() - for test in extra_tests: - suite.addTest(test) + 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)) - suite = reorder_suite(suite, (TestCase,)) + for test in extra_tests: + suite.addTest(test) - old_name = settings.DATABASE_NAME - from django.db import connection - connection.creation.create_test_db(verbosity, autoclobber=False) + suite = reorder_suite(suite, (TestCase,)) - result = DjangoTeamcityTestRunner().run(suite, **kwargs) - connection.creation.destroy_test_db(old_name, verbosity) + old_name = settings.DATABASE_NAME + from django.db import connection - teardown_test_environment() + connection.creation.create_test_db(verbosity, autoclobber=False) - return len(result.failures) + len(result.errors) + result = DjangoTeamcityTestRunner().run(suite, **kwargs) + connection.creation.destroy_test_db(old_name, verbosity) + + teardown_test_environment() + + return len(result.failures) + len(result.errors) diff --git a/python/helpers/pycharm/nose_utils.py b/python/helpers/pycharm/nose_utils.py index 07bd0bab9030..d1ff5d5f1fab 100644 --- a/python/helpers/pycharm/nose_utils.py +++ b/python/helpers/pycharm/nose_utils.py @@ -1,27 +1,45 @@ from tcmessages import TeamcityServiceMessages import sys, traceback, datetime +import unittest from tcunittest import strclass +from tcunittest import TeamcityTestResult try: from nose.util import isclass # backwards compat from nose.plugins.base import Plugin + from nose.config import Config + from nose.result import TextTestResult except (Exception, ): e = sys.exc_info()[1] - raise NameError("Something went wrong, do you have nosetest installed? I got this error: %s" % e) + raise NameError( + "Something went wrong, do you have nosetest installed? I got this error: %s" % e) -class TeamcityPlugin(Plugin): +class TeamcityPlugin(Plugin, TextTestResult, TeamcityTestResult): """ TeamcityTest plugin for nose tests """ name = "TeamcityPlugin" enabled = True - def __init__(self): - self.messages = TeamcityServiceMessages(sys.stderr, prepend_linebreak=True) - self.messages.testMatrixEntered() - self.current_suite = None + def __init__(self, stream=sys.stderr, descriptions=None, verbosity=1, + config=None, errorClasses=None): super(TeamcityPlugin, self).__init__() + if errorClasses is None: + errorClasses = {} + + self.errorClasses = errorClasses + if config is None: + config = Config() + self.config = config + self.output = stream + self.messages = TeamcityServiceMessages(self.output, + prepend_linebreak=True) + self.messages.testMatrixEntered() + self.current_suite = None + TextTestResult.__init__(self, stream, descriptions, verbosity, config, + errorClasses) + TeamcityTestResult.__init__(self, stream) def configure(self, options, conf): if not self.can_configure: @@ -32,7 +50,7 @@ class TeamcityPlugin(Plugin): def addError(self, test, err): err = self.formatErr(err) self.messages.testError(self.getTestName(test), - message='Error', details=err) + message='Error', details=err) def formatErr(self, err): @@ -66,7 +84,7 @@ class TeamcityPlugin(Plugin): err = self.formatErr(err) self.messages.testFailed(self.getTestName(test), - message='Failure', details=err) + message='Failure', details=err) def addSkip(self, test, reason): @@ -89,11 +107,15 @@ class TeamcityPlugin(Plugin): from nose_helper.util import func_lineno if hasattr(test.test, "descriptor") and test.test.descriptor: - suite_location = "file://" + self.test_address(test.test.descriptor) - location = suite_location + ":" + str(func_lineno(test.test.descriptor)) + suite_location = "file://" + self.test_address( + test.test.descriptor) + location = suite_location + ":" + str( + func_lineno(test.test.descriptor)) else: - suite_location = "file://" + self.test_address(test.test.test) - location = "file://" + self.test_address(test.test.test) + ":" + str(func_lineno(test.test.test)) + suite_location = "file://" + self.test_address( + test.test.test) + location = "file://" + self.test_address( + test.test.test) + ":" + str(func_lineno(test.test.test)) except: test_id = test.id() suite_id = test_id[:test_id.rfind(".")] @@ -149,7 +171,8 @@ class TeamcityPlugin(Plugin): if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = suite - self.messages.testSuiteStarted(self.current_suite, location=suite_location) + self.messages.testSuiteStarted(self.current_suite, + location=suite_location) setattr(test, "startTime", datetime.datetime.now()) self.messages.testStarted(self.getTestName(test), location=location) @@ -158,10 +181,55 @@ class TeamcityPlugin(Plugin): start = getattr(test, "startTime", datetime.datetime.now()) d = datetime.datetime.now() - start duration = d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000 - self.messages.testFinished(self.getTestName(test), duration=int(duration)) + self.messages.testFinished(self.getTestName(test), + duration=int(duration)) def finalize(self, result): if self.current_suite: self.messages.testSuiteFinished(self.current_suite) self.current_suite = None + + +class TeamcityNoseRunner(unittest.TextTestRunner): + """Test runner that supports teamcity output + """ + + def __init__(self, stream=sys.stdout, descriptions=1, verbosity=1, + config=None): + if config is None: + config = Config() + self.config = config + + unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity) + + + def _makeResult(self): + return TeamcityPlugin(self.stream, + self.descriptions, + self.verbosity, + self.config) + + def run(self, test): + """Overrides to provide plugin hooks and defer all output to + the test result class. + """ + #for 2.5 compat + plugins = self.config.plugins + plugins.configure(self.config.options, self.config) + plugins.begin() + wrapper = plugins.prepareTest(test) + if wrapper is not None: + test = wrapper + + # plugins can decorate or capture the output stream + wrapped = self.config.plugins.setOutputStream(self.stream) + if wrapped is not None: + self.stream = wrapped + + result = self._makeResult() + test(result) + result.endLastSuite() + plugins.finalize(result) + + return result \ No newline at end of file