From 205d3d10e38c62a3366d382439d8a7ab040f9c98 Mon Sep 17 00:00:00 2001 From: Philipp Kolberg <39984529+PKizzle@users.noreply.github.com> Date: Thu, 5 Oct 2023 20:13:38 +0200 Subject: [PATCH] root: Support PyCharm's test runner (#7074) * Initial commit. * Use Django's test runner as basis * Skip already correctly formatted test labels --- authentik/root/test_runner.py | 61 ++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/authentik/root/test_runner.py b/authentik/root/test_runner.py index 5141613b0..02425f016 100644 --- a/authentik/root/test_runner.py +++ b/authentik/root/test_runner.py @@ -1,8 +1,10 @@ """Integrate ./manage.py test with pytest""" +import os from argparse import ArgumentParser from unittest import TestCase from django.conf import settings +from django.test.runner import DiscoverRunner from authentik.lib.config import CONFIG from authentik.lib.sentry import sentry_init @@ -12,13 +14,11 @@ from tests.e2e.utils import get_docker_tag TestCase.maxDiff = None -class PytestTestRunner: # pragma: no cover +class PytestTestRunner(DiscoverRunner): # pragma: no cover """Runs pytest to discover and run tests.""" def __init__(self, verbosity=1, failfast=False, keepdb=False, **kwargs): - self.verbosity = verbosity - self.failfast = failfast - self.keepdb = keepdb + super().__init__(verbosity, failfast, keepdb, **kwargs) self.args = [] if self.failfast: @@ -47,16 +47,61 @@ class PytestTestRunner: # pragma: no cover @classmethod def add_arguments(cls, parser: ArgumentParser): """Add more pytest-specific arguments""" - parser.add_argument("--randomly-seed", type=int) - parser.add_argument("--keepdb", action="store_true") + parser.add_argument( + "--randomly-seed", + type=int, + help="Set the seed that pytest-randomly uses (int), or pass the special value 'last'" + "to reuse the seed from the previous run." + "Default behaviour: use random.Random().getrandbits(32), so the seed is" + "different on each run.", + ) + parser.add_argument( + "--keepdb", action="store_true", help="Preserves the test DB between runs." + ) - def run_tests(self, test_labels): + def run_tests(self, test_labels, extra_tests=None, **kwargs): """Run pytest and return the exitcode. It translates some of Django's test command option to pytest's. + It is supported to only run specific classes and methods using + a dotted module name i.e. foo.bar[.Class[.method]] + + The extra_tests argument has been deprecated since Django 5.x + It is kept for compatibility with PyCharm's Django test runner. """ + for label in test_labels: + valid_label_found = False + label_as_path = os.path.abspath(label) + # File path has been specified + if os.path.exists(label_as_path): + self.args.append(label_as_path) + valid_label_found = True + else: + # Already correctly formatted test found (file_path::class::method) + if "::" in label: + self.args.append(label) + valid_label_found = True + # Convert dotted module path to file_path::class::method + else: + path_pieces = label.split(".") + # Check whether only class or class and method are specified + for i in range(-1, -3, -1): + path = os.path.join(*path_pieces[:i]) + ".py" + label_as_path = os.path.abspath(path) + if os.path.exists(label_as_path): + path_method = label_as_path + "::" + "::".join(path_pieces[i:]) + self.args.append(path_method) + valid_label_found = True + break + + if not valid_label_found: + raise RuntimeError( + f"One of the test labels: {label!r}, " + f"is not supported. Use a dotted module name or " + f"path instead." + ) + import pytest - self.args.extend(test_labels) return pytest.main(self.args)