Skip to content

Commit 706e7d7

Browse files
feat: Add parallel test execution to speed up quality checks (#186) (#190)
* feat: Add parallel test execution to speed up quality checks Implements pytest-xdist to run tests across multiple CPU cores, dramatically reducing quality check execution time from 3-5 minutes to ~38 seconds (5-8x speedup). Performance tests are kept sequential to maintain accurate benchmarking. Resolves #186 * fix: Fix SQLite concurrency issues in acronym normalization tests Replace custom cache fixture with isolated_test_cache to prevent database conflicts during parallel test execution. Each test now gets its own isolated database preventing race conditions and readonly database errors. --------- Co-authored-by: florath-ai-assistant[bot] <Andreas.Florath@telekom.de>
1 parent 8199927 commit 706e7d7

File tree

3 files changed

+21
-41
lines changed

3 files changed

+21
-41
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ dev = [
6060
"pytest-asyncio>=0.21.0",
6161
"pytest-cov>=4.0.0",
6262
"pytest-benchmark>=4.0.0",
63+
"pytest-xdist>=3.0.0",
6364
"psutil>=5.9.0",
6465
"black>=23.0.0",
6566
"mypy>=1.0.0",

scripts/run-quality-checks.sh

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,14 @@ run_check "Example execution" python scripts/check-examples.py
9898
# 8. Mypy type checking (slower)
9999
run_check "Mypy type checking" mypy src/ --strict
100100

101-
# 9. Pytest with coverage (slowest)
102-
run_check "Pytest with coverage" pytest --cov=src --cov-report=term-missing tests/
103-
104-
# 10. Performance benchmarks (mandatory)
101+
# 9. Pytest with coverage (slowest - now parallelized)
102+
# Use -n auto to automatically detect CPU cores and run tests in parallel
103+
# Exclude performance tests to avoid pytest-xdist conflicts with benchmarks
104+
# Note: Coverage collection works correctly with pytest-xdist
105+
run_check "Pytest with coverage" pytest -n auto --cov=src --cov-report=term-missing tests/ --ignore=tests/performance/
106+
107+
# 10. Performance benchmarks (mandatory - run without parallelization)
108+
# Benchmark tests should run sequentially for accurate timing
105109
run_check "Performance benchmarks" pytest tests/performance/ --benchmark-only
106110

107111
# Final summary

tests/unit/test_acronym_normalization.py

Lines changed: 12 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,9 @@
11
# SPDX-License-Identifier: MIT
2-
import os
3-
import sqlite3
4-
from pathlib import Path
5-
62
import pytest
73

8-
from aletheia_probe.cache import (
9-
CacheManager,
10-
get_cache_manager,
11-
reset_cache_manager,
12-
set_cache_manager,
13-
)
144
from aletheia_probe.normalizer import InputNormalizer
155

166

17-
@pytest.fixture(autouse=True)
18-
def setup_cache_for_testing():
19-
"""Set up a temporary cache database for testing."""
20-
test_db_path = Path("./test_cache.db")
21-
if test_db_path.exists():
22-
test_db_path.unlink() # Ensure a clean slate
23-
24-
test_cache = CacheManager(db_path=test_db_path)
25-
set_cache_manager(test_cache)
26-
yield test_cache
27-
reset_cache_manager()
28-
if test_db_path.exists():
29-
test_db_path.unlink()
30-
31-
327
@pytest.fixture
338
def normalizer():
349
"""Fixture for InputNormalizer."""
@@ -53,18 +28,18 @@ def test_clean_text_html_unescape(normalizer):
5328
)
5429

5530

56-
def test_are_conference_names_equivalent_basic_match(setup_cache_for_testing):
31+
def test_are_conference_names_equivalent_basic_match(isolated_test_cache):
5732
"""Test _are_conference_names_equivalent with basic equivalent names."""
58-
cache = setup_cache_for_testing
33+
cache = isolated_test_cache
5934
assert cache._are_conference_names_equivalent(
6035
"Journal of Science", "Journal of Science"
6136
)
6237
assert cache._are_conference_names_equivalent("The Conference", "The Conference")
6338

6439

65-
def test_are_conference_names_equivalent_stop_words(setup_cache_for_testing):
40+
def test_are_conference_names_equivalent_stop_words(isolated_test_cache):
6641
"""Test _are_conference_names_equivalent with stop words variations."""
67-
cache = setup_cache_for_testing
42+
cache = isolated_test_cache
6843
# "and" vs "new" issue from logs
6944
name1 = "journal of process management and new technologies international"
7045
name2 = "journal of process management new technologies international"
@@ -76,18 +51,18 @@ def test_are_conference_names_equivalent_stop_words(setup_cache_for_testing):
7651

7752

7853
def test_are_conference_names_equivalent_case_and_html_entities(
79-
setup_cache_for_testing,
54+
isolated_test_cache,
8055
):
8156
"""Test _are_conference_names_equivalent with case and HTML entities."""
82-
cache = setup_cache_for_testing
57+
cache = isolated_test_cache
8358
name1 = "International Journal of Scientific Research &#038; Management Studies"
8459
name2 = "international journal of scientific research & management studies"
8560
assert cache._are_conference_names_equivalent(name1, name2)
8661

8762

88-
def test_are_conference_names_equivalent_year_and_ordinal(setup_cache_for_testing):
63+
def test_are_conference_names_equivalent_year_and_ordinal(isolated_test_cache):
8964
"""Test _are_conference_names_equivalent with year and ordinal variations."""
90-
cache = setup_cache_for_testing
65+
cache = isolated_test_cache
9166
name1 = "2023 IEEE Conference on Computer Vision"
9267
name2 = "IEEE Conference on Computer Vision"
9368
assert cache._are_conference_names_equivalent(name1, name2)
@@ -97,9 +72,9 @@ def test_are_conference_names_equivalent_year_and_ordinal(setup_cache_for_testin
9772
assert cache._are_conference_names_equivalent(name3, name4)
9873

9974

100-
def test_are_conference_names_equivalent_substrings(setup_cache_for_testing):
75+
def test_are_conference_names_equivalent_substrings(isolated_test_cache):
10176
"""Test _are_conference_names_equivalent with substring matches for longer names."""
102-
cache = setup_cache_for_testing
77+
cache = isolated_test_cache
10378
name1 = "Advances in Neural Information Processing Systems"
10479
name2 = "Neural Information Processing Systems"
10580
assert cache._are_conference_names_equivalent(name1, name2)
@@ -108,11 +83,11 @@ def test_are_conference_names_equivalent_substrings(setup_cache_for_testing):
10883
assert not cache._are_conference_names_equivalent("AI", "AAAI")
10984

11085

111-
def test_store_acronym_mapping_with_equivalent_names(setup_cache_for_testing):
86+
def test_store_acronym_mapping_with_equivalent_names(isolated_test_cache):
11287
"""
11388
Test that store_acronym_mapping does not log a warning when overwriting with an equivalent name.
11489
"""
115-
cache = setup_cache_for_testing
90+
cache = isolated_test_cache
11691
acronym = "IJSRMS"
11792
full_name1 = "international journal of scientific research & management studies"
11893
full_name2 = (

0 commit comments

Comments
 (0)