Skip to content

Commit af5e860

Browse files
committed
perf(test): deduplicate contracts and balance CI workloads
1 parent a6dad2a commit af5e860

44 files changed

Lines changed: 1251 additions & 2974 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Set up tests
2+
description: Install the pinned toolchain and optional browser with the same runner policy.
3+
inputs:
4+
browser:
5+
description: Install Chromium for browser integration tests.
6+
default: 'false'
7+
runs:
8+
using: composite
9+
steps:
10+
# Preserve scanning for fork PRs. Maintainer jobs may exclude generated paths
11+
# on their ephemeral runner; refusal warns and never skips a check.
12+
- name: Exclude build paths from Windows Defender
13+
if: >-
14+
${{ runner.os == 'Windows'
15+
&& (github.event_name == 'push'
16+
|| github.event.pull_request.head.repo.full_name == github.repository) }}
17+
shell: pwsh
18+
run: |
19+
foreach ($path in @('${{ github.workspace }}', '${{ runner.temp }}')) {
20+
try {
21+
Add-MpPreference -ExclusionPath $path -ErrorAction Stop
22+
Write-Host "Defender exclusion added: $path"
23+
} catch {
24+
Write-Host "::warning::could not add Defender exclusion for ${path}: $_"
25+
}
26+
}
27+
- uses: voidzero-dev/setup-vp@2dec1e33f4ab2c6d5bce1b0c4607961bb1a3f7a1 # v1.12.0
28+
with:
29+
node-version-file: .node-version
30+
cache: true
31+
- run: vp exec playwright install chromium
32+
shell: bash
33+
if: ${{ inputs.browser == 'true' && (runner.os != 'Linux' || github.repository == 'voidzero-dev/void') }}
34+
- run: vp exec playwright install --with-deps chromium
35+
shell: bash
36+
if: ${{ inputs.browser == 'true' && runner.os == 'Linux' && github.repository != 'voidzero-dev/void' }}

‎.github/workflows/ci.yml‎

Lines changed: 25 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@ on:
1616
permissions: {}
1717

1818
jobs:
19+
checks:
20+
name: build and typecheck (${{ matrix.os }})
21+
runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository == 'voidzero-dev/void' && 'blacksmith-4vcpu-ubuntu-2404' || matrix.os }}
22+
permissions:
23+
contents: read
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
os: '${{ ((github.event_name == ''push'' || contains(github.event.pull_request.labels.*.name, ''test: windows'')) && !inputs.skip_windows) && fromJSON(''["ubuntu-latest","windows-latest"]'') || fromJSON(''["ubuntu-latest"]'') }}'
28+
steps:
29+
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
30+
with:
31+
persist-credentials: false
32+
- uses: ./.github/actions/test-setup
33+
# Build every playground once per OS for complete build/type coverage.
34+
# Integration tests build their own selected fixtures in separate checkouts.
35+
- run: vp run build:all
36+
- run: vp run check
37+
1938
test:
2039
name: test (${{ matrix.os }}, ${{ matrix.shard }})
2140
runs-on: ${{ matrix.os == 'ubuntu-latest' && github.repository == 'voidzero-dev/void' && 'blacksmith-4vcpu-ubuntu-2404' || matrix.os }}
@@ -30,52 +49,16 @@ jobs:
3049
# Windows runs on branch/tag pushes and on PRs labeled "test: windows",
3150
# unless a reusable workflow caller explicitly disables it.
3251
os: '${{ ((github.event_name == ''push'' || contains(github.event.pull_request.labels.*.name, ''test: windows'')) && !inputs.skip_windows) && fromJSON(''["ubuntu-latest","windows-latest"]'') || fromJSON(''["ubuntu-latest"]'') }}'
33-
# Unit tests and two integration shards run on separate runners. Each integration
34-
# shard builds its own checkout first, so generated playground state is never shared.
52+
# Weighted integration shards own separate checkouts and build only the
53+
# fixtures they exercise. No generated playground state crosses runners.
3554
shard: [unit, '1/2', '2/2']
3655
steps:
3756
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
3857
with:
3958
persist-credentials: false
40-
# Defender's real-time scan is synchronous on every file write, and this suite writes a lot of
41-
# them (dependency install, ~26 playground builds, per-worker temp dirs). Excluding the paths
42-
# we generate is purely a speed measure: it never changes what runs.
43-
#
44-
# Skipped for fork pull requests, the only case where the checkout is code no maintainer has
45-
# pushed. Not because scanning is a boundary for this job — it has no secrets, a read-only
46-
# token, an ephemeral runner, and already executes the PR's own build and test code by design
47-
# — but because leaving the scan on for untrusted checkouts costs us nothing: Windows runs on
48-
# a PR only when a maintainer applies the `test: windows` label, so this is rare, while main
49-
# pushes and maintainer branches (the everyday path) keep the speedup.
50-
#
51-
# Warns instead of failing, so a runner image that refuses the call still runs the suite.
52-
- name: Exclude build paths from Windows Defender
53-
if: >-
54-
${{ contains(matrix.os, 'windows')
55-
&& (github.event_name == 'push'
56-
|| github.event.pull_request.head.repo.full_name == github.repository) }}
57-
shell: pwsh
58-
run: |
59-
foreach ($path in @('${{ github.workspace }}', '${{ runner.temp }}')) {
60-
try {
61-
Add-MpPreference -ExclusionPath $path -ErrorAction Stop
62-
Write-Host "Defender exclusion added: $path"
63-
} catch {
64-
Write-Host "::warning::could not add Defender exclusion for ${path}: $_"
65-
}
66-
}
67-
68-
- uses: voidzero-dev/setup-vp@2dec1e33f4ab2c6d5bce1b0c4607961bb1a3f7a1 # v1.12.0
59+
- uses: ./.github/actions/test-setup
6960
with:
70-
node-version-file: .node-version
71-
cache: true
72-
73-
# The upstream Linux image and Windows already provide browser libraries.
74-
# Forks use GitHub's Ubuntu image and install Chromium's system dependencies.
75-
- run: vp exec playwright install chromium
76-
if: ${{ matrix.shard != 'unit' && (runner.os != 'Linux' || github.repository == 'voidzero-dev/void') }}
77-
- run: vp exec playwright install --with-deps chromium
78-
if: ${{ matrix.shard != 'unit' && runner.os == 'Linux' && github.repository != 'voidzero-dev/void' }}
61+
browser: ${{ matrix.shard != 'unit' }}
7962

8063
- run: vp check
8164
if: ${{ matrix.shard == 'unit' }}
@@ -89,12 +72,9 @@ jobs:
8972
- run: vp test run --project unit
9073
if: ${{ matrix.shard == 'unit' }}
9174

92-
- run: vp run build:all
75+
- run: vp run build
9376
if: ${{ matrix.shard != 'unit' }}
9477

95-
- run: vp run check
96-
if: ${{ matrix.shard == '1/2' }}
97-
9878
- run: vp test run --project parallel-integration --shard=${{ matrix.shard }}
9979
if: ${{ matrix.shard != 'unit' }}
10080

@@ -143,6 +123,7 @@ jobs:
143123
runs-on: ubuntu-latest
144124
if: always()
145125
needs:
126+
- checks
146127
- test
147128
- live-e2e
148129
steps:

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ vp run dev:docs # local docs dev server
123123

124124
See [TESTING.md](./TESTING.md) for targeted verification and the account-neutral, non-destructive
125125
native Cloudflare live E2E procedure.
126+
The [test-suite performance audit](./test/PERFORMANCE.md) records duplicate coverage, scheduling,
127+
and measured end-to-end verification times.
126128

127129
Build the public packages and smoke-test their actual tarballs outside the workspace:
128130

‎TESTING.md‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ vp test run packages/void
99
vp run check
1010
```
1111

12+
The named shortcuts select the current test projects:
13+
14+
```sh
15+
vp run test:unit
16+
vp run test:integration
17+
vp run test:metaframeworks
18+
vp run test:pages:react-vue
19+
vp run test:pages:svelte-solid
20+
vp run test:platform
21+
```
22+
1223
Run the complete SDK and platform verification before handing off a finished change:
1324

1425
```sh
@@ -18,6 +29,40 @@ vpr test:all
1829
`vpr test:all` formats, lints, builds, typechecks, and tests the merged monorepo. Investigate slow
1930
or hanging tests when they occur instead of routinely increasing timeouts.
2031

32+
Platform projects run first; SDK unit and integration files then share a worker pool. This keeps
33+
database engines from competing with all framework compilers at startup. Keep module isolation
34+
enabled: these suites mock globals, keep database state, and exercise real process/lock boundaries.
35+
The full verification command builds before running the platform's type-aware lint, so it also
36+
works without a pre-existing dashboard `.void/tsconfig.json`.
37+
38+
## Suite organization and CI scheduling
39+
40+
CI builds and typechecks every playground once per OS in a dedicated required job. Integration
41+
shards build the public packages and then only the fixtures their tests use; they do not depend
42+
on generated output from another job. The `done` check requires both the tests and build/type
43+
checks. Linux and Windows coverage and the credentialed live-deploy gate are unchanged.
44+
45+
Integration sharding uses [measured duration estimates](test/integration-timings.ts), rather than
46+
filename hashes. [The sequencer](test/sequencer.ts) packs the longest files first and starts costly
47+
fixtures early. Estimates affect scheduling only: new files receive a fallback estimate, and
48+
membership tests ensure every discovered file appears exactly once. Refresh the estimates when
49+
the suite's costs change materially. Seeded file shuffling remains available for isolation checks.
50+
51+
The four adapter suites share their client and island test definitions in `test/`, while retaining
52+
each framework's own dev, preview, SSR, hydration, and HMR execution. Before integrations start,
53+
their sources are snapshotted so another test cloning a fixture cannot capture an in-flight HMR
54+
edit. Dependencies stay linked to the installed workspace, and snapshots are removed afterward.
55+
56+
Use production functions in unit tests. A copy of a hook's implementation inside a test cannot
57+
catch regressions in the real hook. Shared form-value equality has one direct suite, and actual
58+
form interactions run in the adapter browser matrix.
59+
60+
The in-memory API deletion tests schedule the fixed container-orphan sweep delay immediately.
61+
They still execute both sweeps and all provider/database assertions. Production timing and the
62+
real subprocess/lock deadline tests retain their actual timing semantics.
63+
64+
See [the performance audit](test/PERFORMANCE.md) for baseline measurements, changes, and validation.
65+
2166
## Prepublish Test
2267

2368
Packed tests install the current tarballs into fresh applications through a temporary local

‎package.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
"lint": "vp lint",
1616
"prepare": "vp config",
1717
"test": "vp test run",
18-
"test:integration": "vp test run --project core-markdown",
19-
"test:metaframeworks": "vp test run --project metaframeworks",
20-
"test:pages:react-vue": "vp test run --project pages-react-vue",
21-
"test:pages:svelte-solid": "vp test run --project pages-svelte-solid",
18+
"test:integration": "vp test run --project parallel-integration",
19+
"test:metaframeworks": "vp test run --project parallel-integration analog.test.ts astro.test.ts nuxt.test.ts react-router.test.ts sveltekit.test.ts tanstack-start.test.ts vinext-app.test.ts vinext-pages.test.ts",
20+
"test:pages:react-vue": "vp test run --project parallel-integration packages/react packages/vue",
21+
"test:pages:svelte-solid": "vp test run --project parallel-integration packages/svelte packages/solid",
2222
"test:platform": "vp test run --project 'platform-*'",
2323
"test:unit": "vp test run --project unit"
2424
},

‎packages/react/README.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,9 @@ export const loader = defineHandler(() => ({ title: 'Home' }));
219219

220220
## Tests
221221

222-
- `test/unit/use-form.test.ts`
223222
- `test/unit/link.test.ts`
224223
- `test/integration/react-pages-client.test.ts`
225224

226225
Cross-package SSR/protocol coverage for React pages mode also lives in `packages/void/test/integration/*`.
226+
227+
The [shared client](../../test/pages-client-contract.ts) and [island](../../test/pages-island-contract.ts) contracts run against this adapter in both dev and preview. Shared form equality is tested directly in [Void](../void/test/unit/form-value-equality.test.ts); tests do not copy the hook implementation.

0 commit comments

Comments
 (0)