Conversation
There was a problem hiding this comment.
Pull request overview
This PR improves the reliability of QuPath end-to-end tests by isolating install/uninstall scenarios into independent test cases and centralizing QuPath state cleanup into a shared fixture; it also cleans up a confusing download error log message.
Changes:
- Parameterize
test_cli_install_and_uninstallso each platform install/uninstall is retried independently. - Add a shared
qupath_save_restorefixture and use it across CLI/GUI QuPath tests to restore pre-test installation state. - Remove a trailing
=from the QuPath download failure log message.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
tests/aignostics/qupath/gui_test.py |
Switch GUI tests to rely on the shared save/restore fixture instead of inline uninstall/reinstall logic. |
tests/aignostics/qupath/conftest.py |
Introduce qupath_save_restore fixture to uninstall before tests and restore after when needed. |
tests/aignostics/qupath/cli_test.py |
Parameterize install/uninstall test and adopt the shared save/restore fixture across CLI tests. |
src/aignostics/qupath/_service.py |
Clean up download error message formatting. |
|
Claude finished @olivermeyer's task in 1m 48s —— View job ✅ Review Update: QuPath Test Improvements VerifiedI've reviewed the latest changes to address the flaky QuPath test issue. The author has successfully addressed the critical error handling concern raised in the previous review. 📋 Review Progress
✅ Critical Checks - All Pass1. Error Handling in Fixture ✅ FIXEDThe critical issue from the previous review has been properly addressed: Location: What was fixed:
# Uninstall check - validates exit code is 0 (success) or 2 (not installed)
assert result.exit_code in {0, 2}, (
f"Unexpected exit code {result.exit_code} from 'qupath uninstall': {result.output}"
)
# Reinstall check - fails test if reinstall doesn't succeed
if reinstall_result.exit_code != 0:
pytest.fail(
f"Failed to reinstall QuPath in qupath_save_restore fixture "
f"(exit code {reinstall_result.exit_code}). Output:\n{reinstall_result.output}"
)Why this matters: Sequential tests ( 2. Test Markers ✅ All Correct$ uv run pytest -m "not unit and not integration and not e2e" --collect-only tests/aignostics/qupath/
collected 9 items / 9 deselected / 0 selectedResult: ✅ All QuPath tests have proper category markers ( All tests correctly marked with:
3. Fixture Integration ✅ ProperGood: The fixture now uses the shared @pytest.fixture
def qupath_save_restore(runner: CliRunner) -> Generator[None, None, None]:This ensures consistency with the rest of the test suite (uses the same 4. Type Annotations ✅ CorrectThe fixture has proper type annotations: def qupath_save_restore(runner: CliRunner) -> Generator[None, None, None]:🎯 PR Changes Summary1. Parametrization for Independent Retries ✅ ExcellentCommit: 225413a Before: Single test iterating 4 platforms → any failure retries all 4 @pytest.mark.parametrize("platform_config", _INSTALL_UNINSTALL_PLATFORM_CONFIGS)
def test_cli_install_and_uninstall(runner: CliRunner, qupath_save_restore: None, platform_config: dict):Benefits:
2. DRY Fixture Extraction ✅ Clean RefactoringCommit: 9198d12 Eliminated duplication across 6 test functions:
Impact: ~30 lines of duplicated code → single 17-line fixture 3. Error Message Cleanup ✅ Clear ImprovementCommit: c4ce923 Before: Simple fix, but important for debugging clarity. 📊 Quality MetricsCode Coverage
Static Analysis
🎓 Architecture Compliance✅ Test Strategy: Correctly using 💡 Minor Suggestion (Optional - Not Blocking)The fixture could benefit from a docstring explaining the restore behavior: @pytest.fixture
def qupath_save_restore(runner: CliRunner) -> Generator[None, None, None]:
"""Uninstall QuPath for clean test state, restore if it was previously installed.
This fixture ensures tests start with a clean QuPath installation state:
1. Uninstalls QuPath before test (exit code 0=was installed, 2=already uninstalled)
2. Runs the test
3. Reinstalls QuPath if it was installed before (preserves developer environment)
Raises:
AssertionError: If uninstall fails with unexpected exit code
pytest.Failed: If reinstall fails (critical for sequential tests)
"""This is minor and not blocking - the current implementation is clear enough. 📌 Final RecommendationStatus: ✅ Approved - Ready to Merge All critical issues from the previous review have been addressed:
This PR meaningfully improves test reliability through:
Excellent work! 🚀 The changes demonstrate strong engineering practices:
|
8e8ea89 to
c4ce923
Compare
✅ Code Review: Fix Flaky QuPath TestI've completed a thorough review of this PR that addresses flaky QuPath testing. This is excellent work that meaningfully improves test reliability and maintainability. 🎯 SummaryStatus: ✅ Approved - All critical checks pass This PR implements three focused improvements:
✅ Critical Checks (All Pass)1. Test Markers ✅Result: All tests properly marked with # Verified: No unmarked tests found
$ uv run pytest -m "not unit and not integration and not e2e" --collect-only tests/aignostics/qupath/
collected 9 items / 9 deselected / 0 selected2. Code Quality ✅Result: All linting and type checking passes $ make lint
All checks passed!
161 files already formatted
0 errors, 0 warnings, 0 informations
Success: no issues found in 81 source files3. Conventional Commits ✅Result: All commits follow proper format 🎉 Excellent Improvements1. Parametrization for Independent Retries (d89a75d)Before: Single test iterating 4 platforms → if platform 3 fails, retry from platform 1 @pytest.mark.parametrize("platform_config", _INSTALL_UNINSTALL_PLATFORM_CONFIGS)
def test_cli_install_and_uninstall(runner: CliRunner, qupath_save_restore: None, platform_config: dict) -> None:
# Each platform config gets its own test instance with independent retry logicImpact:
Smart timeout reduction: 10 min → 5 min makes sense since each platform now runs independently. 2. DRY Fixture Extraction (a0b3197)Before: 30+ lines duplicated across 5 test functions Eliminated duplication in:
Type annotation: ✅ Properly annotated as 3. Clean Error Message (8e8ea89)Before: Simple but important for debugging clarity. 🔍 Review FindingsCritical Issue: Missing Error Handling in Fixture
|
c4ce923 to
2274524
Compare
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2274524 to
ebfe1ee
Compare
Codecov Report❌ Patch coverage is
❌ Your patch status has failed because the patch coverage (0.00%) is below the target coverage (75.00%). You can increase the patch coverage or adjust the target coverage.
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ebfe1ee to
c2ea7f7
Compare
|
|
CodeCov and SonarCloud are failing because of 0% of coverage on new code, but new code is simply fixing a log line. Safe to merge regardless. |
|
LGTM, tests of this kind are always annoying |


test_cli_install_and_uninstallis still flaky despite adding retries to it. This could be because we iterate over various installations in the same test instance - if any of these downloads fails, we retry all installations. This PR changes this behaviour by parameterising the test instead of iterating, so that each installation can be retried independently (see 57e8d09).Additionally, the CLI and GUI tests for QuPath all have the same mechanism to re-install QuPath after the test if it was installed before the test. I move this to a fixture in e115699. Note that this is still somewhat inefficient: we could technically only uninstall/reinstall once per session instead of once per test. I leave that as a future improvement should it be needed.
Finally in c2ea7f7 I remove a trailing equal sign from a log which was confusing while troubleshooting this.