mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 19:26:48 +00:00
test: process tests run outside the --parallel pool
The pool workers were made non-daemonic so the operations process tests could start workers, but a terminated pool (Ctrl-C, --failfast) left those workers running. The test runner now keeps the test cases tagged "processes" out of the pool and runs them in the main process after it. A WAL that cannot be written back before cloning now stops the run instead of giving clones without tables. One CI leg runs the suite with --parallel. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8e23138ef7
commit
c4ab14db7f
4 changed files with 20 additions and 17 deletions
5
.github/workflows/test.yml
vendored
5
.github/workflows/test.yml
vendored
|
|
@ -95,6 +95,11 @@ jobs:
|
|||
coverage run manage.py test
|
||||
coverage report --skip-covered
|
||||
|
||||
# The project test runner (webvirtcloud/test_runner.py) on one leg
|
||||
- name: Run Test Suite In Parallel
|
||||
if: matrix.python-version == '3.13' && matrix.database == 'sqlite'
|
||||
run: python manage.py test --parallel
|
||||
|
||||
# The per-file report of one leg, to see what a change left untested
|
||||
- name: Store Coverage Report
|
||||
if: always() && matrix.python-version == '3.12' && matrix.database == 'sqlite'
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import time
|
|||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import connections
|
||||
from django.test import TransactionTestCase
|
||||
from django.test import TransactionTestCase, tag
|
||||
|
||||
from operations import claims, jobs
|
||||
from operations.models import Claim, Operation, WorkerLease
|
||||
|
|
@ -49,6 +49,7 @@ def _submit(user_pk, results):
|
|||
os._exit(0)
|
||||
|
||||
|
||||
@tag("processes") # run outside the --parallel pool (webvirtcloud/test_runner.py)
|
||||
class ProcessTestCase(TransactionTestCase):
|
||||
def setUp(self):
|
||||
self.ctx = multiprocessing.get_context("fork")
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ LOGIN_URL = "/accounts/login/"
|
|||
if sys.argv[1:2] == ["test"]:
|
||||
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
|
||||
|
||||
# --parallel on SQLite (WAL) and with the process tests
|
||||
# manage.py test --parallel on SQLite (WAL)
|
||||
TEST_RUNNER = "webvirtcloud.test_runner.TestRunner"
|
||||
|
||||
# Failed logins (django-axes): a user is locked for the address the logins
|
||||
|
|
|
|||
|
|
@ -1,23 +1,18 @@
|
|||
"""manage.py test --parallel on the SQLite database in WAL mode."""
|
||||
|
||||
import multiprocessing
|
||||
|
||||
from django.test.runner import DiscoverRunner, ParallelTestSuite
|
||||
|
||||
|
||||
def _init_worker(*args, **kwargs):
|
||||
# Pool processes are daemonic, and a daemonic process may have no
|
||||
# children; the operations process tests start workers of their own.
|
||||
multiprocessing.current_process().daemon = False
|
||||
ParallelTestSuite.init_worker(*args, **kwargs)
|
||||
|
||||
|
||||
class _ParallelTestSuite(ParallelTestSuite):
|
||||
init_worker = _init_worker
|
||||
from django.test.runner import DiscoverRunner
|
||||
|
||||
|
||||
class TestRunner(DiscoverRunner):
|
||||
parallel_test_suite = _ParallelTestSuite
|
||||
def build_suite(self, *args, **kwargs):
|
||||
suite = super().build_suite(*args, **kwargs)
|
||||
if not isinstance(suite, self.parallel_test_suite):
|
||||
return suite
|
||||
# Pool workers are daemonic and may start no processes: the test
|
||||
# cases tagged "processes" run in this process after the pool.
|
||||
serial = [case for case in suite.subsuites if "processes" in getattr(next(iter(case)), "tags", ())]
|
||||
suite.subsuites = [case for case in suite.subsuites if case not in serial]
|
||||
return self.test_suite([suite, *serial])
|
||||
|
||||
def setup_databases(self, **kwargs):
|
||||
# Django clones a SQLite test database by copying its main file, but
|
||||
|
|
@ -32,6 +27,8 @@ class TestRunner(DiscoverRunner):
|
|||
if connection.vendor == "sqlite":
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("PRAGMA wal_checkpoint(TRUNCATE)")
|
||||
if cursor.fetchone()[0]:
|
||||
raise RuntimeError("The test database's WAL could not be written back before cloning")
|
||||
for index in range(parallel):
|
||||
connection.creation.clone_test_db(
|
||||
suffix=str(index + 1), verbosity=self.verbosity, keepdb=self.keepdb
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue