Skip to content

Improve unit test times with isolated parallel workers#225

Open
rolandocortez wants to merge 5 commits into
mainfrom
feat/improve-test-times
Open

Improve unit test times with isolated parallel workers#225
rolandocortez wants to merge 5 commits into
mainfrom
feat/improve-test-times

Conversation

@rolandocortez

@rolandocortez rolandocortez commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Purpose

Reduce the CI test critical path by optimizing both the unit-test and E2E workflows.

For unit tests, independent model conversions now run in parallel with two pytest-xdist workers. Each worker uses an isolated temporary workspace for downloaded models, subprocess execution, conversion outputs, validation, and cleanup.

For E2E tests, the 101 public cases are divided into three duration-balanced serial shards. Tests that use the same physical model weights remain in the same shard, allowing weights to be reused without introducing unsafe concurrent access to shared conversion resources.

Specification

Unit tests

  • Added pytest-xdist as a development dependency.
  • Added one session-scoped temporary workspace per pytest worker.
  • Updated unit tests to:
    • download models into the current worker workspace
    • pass absolute model paths to the CLI
    • run conversion subprocesses from the worker workspace
    • validate NNArchive outputs from the corresponding workspace
  • Updated cleanup fixtures to:
    • clean isolated workspace artifacts for unit tests
    • preserve the existing repository-root behavior for E2E tests
  • Updated the unit-test CI workflow to use:
    • -n 2
    • --dist load
    • session-level weight reuse
  • Preserved subprocess coverage collection by setting absolute coverage paths before starting conversions from the isolated workspaces.
  • Updated the unit-test documentation with the parallel command.

E2E tests

  • Added a checked-in E2E shard assignment generated from hosted CI timing data.
  • Split the 101 public E2E cases into three shards:
    • shard 0: 33 tests
    • shard 1: 35 tests
    • shard 2: 33 tests
  • Kept model weight groups together so the current assignment does not duplicate physical model weights across shards.
  • Kept execution serial inside each shard to avoid unsafe concurrent access to conversion outputs and shared tooling resources.
  • Retained downloaded weights for the duration of each shard.
  • Kept the three private E2E tests on shard 0 only.
  • Added explicit pytest options:
    • --e2e-shard-index
    • --e2e-shard-count
  • The validated and checked-in assignment currently supports three shards.
  • Unsupported shard counts fail with a clear message requesting that a new assignment be generated.
  • Updated the E2E GitHub Actions matrix to run the three shards independently on each supported operating system.

Dependencies & Potential Impact

Adds:

  • pytest-xdist>=3.6.1

The unit-test workflow deliberately uses two workers instead of -n auto.

In the controlled unit-test benchmark, mean peak aggregate process-tree RSS increased from approximately 1253 MiB during serial execution to 2321 MiB with two workers. This measurement may count some shared pages more than once, but it still supports using a fixed worker count to avoid unnecessary memory pressure on CI runners.

The isolated unit-test workspaces can contain the same model in more than one worker when both workers need it. The measured parallel runs contained 11 weight files representing 10 unique models, with yolov8n present in both workspaces.

E2E sharding increases the number of concurrent CI jobs, but reduces the expected wall-clock critical path. The three-shard assignment was selected from profiling results and keeps the current physical weight groups isolated between shards.

Based on the successful hosted-run profile, the estimated public E2E critical-path reduction is approximately 65%. The actual runtime improvement will be confirmed by the hosted CI runs for this PR.

The coverage workflow also updated media/coverage_badge.svg automatically.

No product code or vendored submodules are modified.

Testing & Validation

Unit-test benchmark

A controlled ABBA comparison was run against the same 27-test conversion suite, using a fresh temporary workspace for every execution:

Mode Mean wall time
Serial 179.72 s
2 workers 97.07 s

Results:

  • 82.65 seconds saved on average
  • 45.99% runtime reduction
  • 1.85x speedup

Final local validation

  • All 29 current unit tests passed with two workers.
    • 27 existing conversion tests
    • 2 E2E shard-assignment tests
  • All 9 telemetry tests passed after rebasing onto the latest main.
  • Workflow-equivalent coverage and non-coverage unit-test executions passed.
  • All 104 E2E cases collect successfully.
  • The three public shard collections contain:
    • 33 tests
    • 35 tests
    • 33 tests
  • The public shard union contains exactly 101 tests.
  • No public test appears in more than one shard.
  • The default shard count selects the validated three-shard assignment.
  • Unsupported shard counts are rejected with a clear regeneration message.
  • Representative yolov8n E2E conversion passed with both subtests.
  • Existing --delete-weights-now behavior remains compatible.
  • Python 3.8 dependency resolution selects pytest-xdist 3.6.1.
  • Pre-commit and git diff --check passed.
  • Repository isolation checks passed without leaving weights or conversion outputs in the working tree.

The hosted CI matrix will provide the final runtime comparison across Ubuntu, Windows, and macOS.

Deployment Plan

No deployment required.

Rollout:

Rollback:

  • revert the E2E sharding commit to restore one serial E2E job per operating system
  • revert the unit-test parallelization changes to restore serial unit-test execution and per-test weight cleanup

Monitoring:

  • unit-test runtime across the CI matrix
  • E2E shard balance and total wall-clock runtime
  • CI runner usage and queueing
  • memory-related failures or instability
  • model-download and DNS failures
  • coverage generation
  • reviewer feedback on shard configurability and workspace isolation

AI Usage

Assisted-by: ChatGPT

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Unit tests now run in parallel with worker-specific temporary workspaces, while E2E tests are divided across three deterministic shards. CI coverage paths, artifact cleanup, archive validation, and workflow gating are updated for parallel execution.

Changes

Test Execution

Layer / File(s) Summary
Unit-test workspace isolation
tests/conftest.py, tests/helper_functions.py, tests/test_unittests.py
Tests prepare models, run the CLI, clean artifacts, and validate archives within worker-specific workspaces.
Unit-test parallel CI execution
.github/workflows/unittests.yaml, requirements-dev.txt, tests/README.md
Development requirements, CI commands, coverage paths, and documented commands enable two-worker pytest execution with updated weight handling.
Deterministic E2E sharding
tests/e2e_shards.py
A pytest plugin validates shard options and selects nodeids assigned to the requested shard.
E2E workflow shard wiring
.github/workflows/end2end_tests.yaml
The workflow runs three shard matrix entries, passes shard arguments to pytest, and gates private-model setup and execution to the first eligible shard.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested reviewers: klemen1999

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant Pytest
  participant E2EShardPlugin
  GitHubActions->>Pytest: pass shard index and shard count
  Pytest->>E2EShardPlugin: collect E2E tests
  E2EShardPlugin->>Pytest: retain assigned nodeids and deselect others
  Pytest->>GitHubActions: execute selected shard
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 81.25% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: faster unit tests via isolated parallel pytest workers.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/improve-test-times

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@rolandocortez
rolandocortez requested a review from klemen1999 July 15, 2026 19:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/README.md (1)

21-25: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add language identifier to fenced code block.

Specifying a language for the fenced code block improves syntax highlighting and resolves a markdownlint warning.

♻️ Proposed fix
-```
+```bash
 pytest tests/test_unittests.py -n 2 --dist load --download-weights --log-cli-level=INFO --log-file=out.log --log-file-level=DEBUG
</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @tests/README.md around lines 21 - 25, Update the fenced command block in the
tests README to specify the bash language identifier, preserving the existing
pytest command unchanged.


</details>

<!-- cr-comment:v1:46eb3ed9d26b48d1f77dd292 -->

_Source: Linters/SAST tools_

</blockquote></details>

</blockquote></details>

<details>
<summary>🤖 Prompt for all review comments with AI agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @tests/README.md:

  • Around line 21-25: Update the fenced command block in the tests README to
    specify the bash language identifier, preserving the existing pytest command
    unchanged.

</details>

---

<details>
<summary>ℹ️ Review info</summary>

<details>
<summary>⚙️ Run configuration</summary>

**Configuration used**: defaults

**Review profile**: CHILL

**Plan**: Pro

**Run ID**: `789f4b11-3fad-4515-ba03-2ef80ad207b3`

</details>

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 01b44ad8dbefbcceaedbcaef48950f2ab04a354a and 649c30a36d666eec02a0cd601fa4f0301967b36b.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `.github/workflows/unittests.yaml`
* `requirements-dev.txt`
* `tests/README.md`
* `tests/conftest.py`
* `tests/helper_functions.py`
* `tests/test_unittests.py`

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
1944 1131 58% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: f338409 by action🐍

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown

Test Results

  6 files    6 suites   15m 14s ⏱️
 29 tests  29 ✅ 0 💤 0 ❌
174 runs  174 ✅ 0 💤 0 ❌

Results for commit f338409.

♻️ This comment has been updated with latest results.

@rolandocortez
rolandocortez force-pushed the feat/improve-test-times branch from 4486f07 to f77bed7 Compare July 17, 2026 13:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/test_unittests.py (1)

101-103: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Include CLI output in assertion failure messages.

When a negative test fails (e.g., the CLI succeeds when it was expected to fail, or fails with an unexpected error code), the assertion message only prints the exit codes. Appending result.stdout to these messages will make debugging CI failures significantly easier by revealing the actual error or unexpected output.

  • tests/test_unittests.py#L101-L103: Append \nOutput:\n{result.stdout} to the assertion message string.
  • tests/test_unittests.py#L150-L152: Append \nOutput:\n{result.stdout} to the assertion message string.
  • tests/test_unittests.py#L196-L198: Append \nOutput:\n{result.stdout} to the assertion message string.
  • tests/test_unittests.py#L238-L240: Append \nOutput:\n{result.stdout} to the assertion message string.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_unittests.py` around lines 101 - 103, Append the CLI output to
each exit-code assertion failure message in tests/test_unittests.py at lines
101-103, 150-152, 196-198, and 238-240, using result.stdout with the “Output”
label. Update the assertion messages associated with the affected test checks
while preserving their existing exit-code details.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/test_unittests.py`:
- Around line 101-103: Append the CLI output to each exit-code assertion failure
message in tests/test_unittests.py at lines 101-103, 150-152, 196-198, and
238-240, using result.stdout with the “Output” label. Update the assertion
messages associated with the affected test checks while preserving their
existing exit-code details.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: afc547ca-3750-449a-9767-84840e0372af

📥 Commits

Reviewing files that changed from the base of the PR and between 649c30a and f77bed7.

⛔ Files ignored due to path filters (1)
  • media/coverage_badge.svg is excluded by !**/*.svg
📒 Files selected for processing (8)
  • .github/workflows/end2end_tests.yaml
  • .github/workflows/unittests.yaml
  • requirements-dev.txt
  • tests/README.md
  • tests/conftest.py
  • tests/e2e_shards.py
  • tests/helper_functions.py
  • tests/test_unittests.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • requirements-dev.txt
  • tests/helper_functions.py
  • .github/workflows/unittests.yaml
  • tests/README.md

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants