From 8883fb656b0e528735e1d794f69c46c3e37b1032 Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Thu, 11 Sep 2025 15:16:05 -0400 Subject: [PATCH] feat(tests): add duration caching for pytest-split - Cache test durations for optimized splitting --- .github/workflows/tests.yml | 20 ++++++ .github/workflows/update-test-durations.yml | 71 +++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 .github/workflows/update-test-durations.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a5b860c9e..0f4c1ae31 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,14 +45,34 @@ jobs: - name: Install the project run: uv sync --all-groups --all-extras + - name: Restore test durations + uses: actions/cache/restore@v4 + with: + path: .test_durations_py* + key: test-durations-py${{ matrix.python-version }}- + restore-keys: | + test-durations-py${{ matrix.python-version }}- + - name: Run tests (group ${{ matrix.group }} of 8) run: | + PYTHON_VERSION_SAFE=$(echo "${{ matrix.python-version }}" | tr '.' '_') + DURATION_FILE=".test_durations_py${PYTHON_VERSION_SAFE}" + + if [ -f "$DURATION_FILE" ]; then + echo "Using cached test durations for optimal splitting" + DURATIONS_ARG="--durations-path=${DURATION_FILE}" + else + echo "No cached durations found, tests will be split evenly" + DURATIONS_ARG="" + fi + uv run pytest \ --block-network \ --timeout=30 \ -vv \ --splits 8 \ --group ${{ matrix.group }} \ + $DURATIONS_ARG \ --durations=10 \ -n auto \ --maxfail=3 diff --git a/.github/workflows/update-test-durations.yml b/.github/workflows/update-test-durations.yml new file mode 100644 index 000000000..daa1decfd --- /dev/null +++ b/.github/workflows/update-test-durations.yml @@ -0,0 +1,71 @@ +name: Update Test Durations + +on: + push: + branches: + - main + paths: + - 'tests/**/*.py' + workflow_dispatch: + +permissions: + contents: read + +jobs: + update-durations: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.10', '3.11', '3.12', '3.13'] + env: + OPENAI_API_KEY: fake-api-key + PYTHONUNBUFFERED: 1 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Restore global uv cache + id: cache-restore + uses: actions/cache/restore@v4 + with: + path: | + ~/.cache/uv + ~/.local/share/uv + .venv + key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }} + restore-keys: | + uv-main-py${{ matrix.python-version }}- + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + version: "0.8.4" + python-version: ${{ matrix.python-version }} + enable-cache: false + + - name: Install the project + run: uv sync --all-groups --all-extras + + - name: Run all tests and store durations + run: | + PYTHON_VERSION_SAFE=$(echo "${{ matrix.python-version }}" | tr '.' '_') + uv run pytest --store-durations --durations-path=.test_durations_py${PYTHON_VERSION_SAFE} -n auto + continue-on-error: true + + - name: Save durations to cache + if: always() + uses: actions/cache/save@v4 + with: + path: .test_durations_py* + key: test-durations-py${{ matrix.python-version }}-${{ github.sha }} + + - name: Save uv caches + if: steps.cache-restore.outputs.cache-hit != 'true' + uses: actions/cache/save@v4 + with: + path: | + ~/.cache/uv + ~/.local/share/uv + .venv + key: uv-main-py${{ matrix.python-version }}-${{ hashFiles('uv.lock') }} \ No newline at end of file