Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions packages/cli/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ import {
} from '../qa/vitest-filter-preflight/src/index.js';
import { integrationTestFiles, unitTestFiles } from './vitest-tiers.js';
import { readTierMode } from '../../scripts/nightly-tiers.mjs';
import { memoryBackedTmpEnv } from '../../scripts/vitest-memory-tmp.mjs';

// The two tiers, DERIVED from what the files DO — never written down — over
// the population `OS_TEST_TIERS` selects. `vitest-tiers.ts` holds the
Expand Down Expand Up @@ -989,6 +990,15 @@ export default defineConfig({
// `server.deps.external` entry above; each repeats the console-intercept
// disarm — and, since #17647, the registry log level — because a
// root-level value is inert under projects.
//
// Each project's `env` also moves `os.tmpdir()` onto /dev/shm when it is
// writable (#27306; scripts/vitest-memory-tmp.mjs). Every SQLite database
// these tests migrate lives in an `mkdtemp` under it. Measured on the
// fifteen `organization-ownership-*` files beside four `dd … conv=fsync`
// writers on the same disk: 145 of 232 cases timed out and the run took
// 1,809 s, against 232 of 232 green in 68 s idle; with TMPDIR there, under
// the same writers, 232 of 232 green in 59 s. Assertions and budgets are
// untouched.
projects: [
{
extends: true,
Expand All @@ -999,7 +1009,9 @@ export default defineConfig({
// #13517: quiet the registry's per-item registration chatter — the
// engine's own `OS_REGISTRY_LOG` seam, not a change to its shipped
// default. Enforced by scripts/check-registry-log-declared.mjs.
env: { OS_REGISTRY_LOG: 'warn' },
// TMPDIR on a memory-backed directory (#27306): see the root `test`
// block's note above `projects`.
env: { OS_REGISTRY_LOG: 'warn', ...memoryBackedTmpEnv() },
include: UNIT_FILES,
},
},
Expand All @@ -1012,7 +1024,9 @@ export default defineConfig({
// #13517: quiet the registry's per-item registration chatter — the
// engine's own `OS_REGISTRY_LOG` seam, not a change to its shipped
// default. Enforced by scripts/check-registry-log-declared.mjs.
env: { OS_REGISTRY_LOG: 'warn' },
// TMPDIR on a memory-backed directory (#27306): see the root `test`
// block's note above `projects`.
env: { OS_REGISTRY_LOG: 'warn', ...memoryBackedTmpEnv() },
include: INTEGRATION_FILES,
},
},
Expand Down
13 changes: 13 additions & 0 deletions packages/create-objectstack/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { configDefaults, defineConfig } from 'vitest/config';
import { memoryBackedTmpEnv } from '../../scripts/vitest-memory-tmp.mjs';

const HERE = path.dirname(fileURLToPath(import.meta.url));

Expand Down Expand Up @@ -38,5 +39,17 @@ export default defineConfig({
// its `test` script.
exclude: [...configDefaults.exclude, 'src/templates/**'],
environment: 'node',
// Every scaffold these tests write lands under `os.tmpdir()`, children
// included (#27306; scripts/vitest-memory-tmp.mjs). The cost on a loaded
// disk is not fsync (four calls in the whole suite, by strace) but creating
// and removing those trees: ~4,000 unlinks and ~6,000 rmdirs. Measured
// beside four `dd … conv=fsync` writers on the same disk: all 301 cases
// passed, but the `afterAll` that removes the scaffolded tree in
// `created-summary.test.ts` and `scaffold-skills-single-copy.test.ts` ran
// past its 10 s hook budget, so two files failed with no failed case, and
// the run took 236 s against 21 s idle. With TMPDIR there, under the same
// writers, 19 of 19 files green in 20 s. Assertions and budgets are
// untouched.
env: memoryBackedTmpEnv(),
},
});
10 changes: 10 additions & 0 deletions packages/drivers/driver-sql/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import { defineConfig } from 'vitest/config';
import path from 'path';
import { memoryBackedTmpEnv } from '../../../scripts/vitest-memory-tmp.mjs';

export default defineConfig({
test: {
Expand All @@ -14,6 +15,15 @@ export default defineConfig({
disableConsoleIntercept: true,
globals: true,
environment: 'node',
// TMPDIR on /dev/shm when it is writable (#27306;
// scripts/vitest-memory-tmp.mjs): the SQLite file tests open their
// databases in an `mkdtemp` under it. Measured on the 23 test files that
// do, beside four `dd … conv=fsync` writers on the same disk: 3 of 192
// cases timed out (posture conformance, reclaim-space, declared-index
// retired keys) and the run took 98 s, against all green in 28 s idle; with
// TMPDIR there, under the same writers, all green in 28 s. Assertions and
// budgets are untouched.
env: memoryBackedTmpEnv(),
// #9350: the live-dialect files each own a schema (Postgres) / database
// (MySQL), named in the connection so knex's `client.database()` and the
// session cannot disagree. That naming has an ordering constraint — MySQL
Expand Down
21 changes: 5 additions & 16 deletions packages/drivers/driver-sqlite-wasm/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

import { accessSync, constants } from 'node:fs';
import { defineConfig } from 'vitest/config';
import { memoryBackedTmpEnv } from '../../../scripts/vitest-memory-tmp.mjs';

// This package re-exported the shared preset until #27076. It now carries one
// key of its own, `test.env.TMPDIR`, so the preset's one setting is restated
Expand All @@ -27,20 +27,9 @@ import { defineConfig } from 'vitest/config';
// tmpfs the same code still runs, `sync()` included: what each case asserts
// (bytes on disk, atomic rename, the flush chain, the failure report) is
// unchanged; only the wait for unrelated I/O is gone. Budgets, fixtures and
// cases are untouched.
//
// A host without a writable /dev/shm (macOS, Windows) keeps its own temp
// directory: the setting is absent there, not an error.
const MEMORY_BACKED_TMP = '/dev/shm';

function writableDir(dir: string): boolean {
try {
accessSync(dir, constants.W_OK);
return true;
} catch {
return false;
}
}
// cases are untouched. The setting itself, and the host without a writable
// /dev/shm, live in scripts/vitest-memory-tmp.mjs, shared since #27306 with the
// other packages whose tests write SQLite files under `os.tmpdir()`.

export default defineConfig({
test: {
Expand All @@ -51,6 +40,6 @@ export default defineConfig({
// Mechanism + measured costs: examples/app-showcase/vitest.config.ts.
// Enforced repo-wide by scripts/check-console-intercept-disarm.mjs.
disableConsoleIntercept: true,
env: writableDir(MEMORY_BACKED_TMP) ? { TMPDIR: MEMORY_BACKED_TMP } : {},
env: memoryBackedTmpEnv(),
},
});
31 changes: 29 additions & 2 deletions packages/drivers/driver-turso/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

// This package needs nothing beyond the shared preset; see its header.
export { default } from '../../../vitest.preset.mts';
import { defineConfig } from 'vitest/config';
import { memoryBackedTmpEnv } from '../../../scripts/vitest-memory-tmp.mjs';

// This package re-exported the shared preset until #27306. It now carries one
// key of its own, `test.env`, so the preset's one setting is restated below (a
// merged or wrapped preset is not what scripts/check-console-intercept-disarm.mjs
// judges in a config's place). Every other key stays on vitest's defaults, as
// under the bare re-export.
//
// `test.env` moves `os.tmpdir()` onto /dev/shm when it is writable
// (scripts/vitest-memory-tmp.mjs): the local and remote-face tests open `file:`
// databases in an `mkdtemp` under it. Measured on the 14 test files that do,
// beside four `dd … conv=fsync` writers on the same disk: 21 of 502 cases
// timed out across 4 files (inherited-members, external-object-parity,
// drift-detection, migration-entries) and the run took 211 s, against all
// green in 17 s idle; with TMPDIR there, under the same writers, all green in
// 17 s. Assertions and budgets are untouched.
export default defineConfig({
test: {
// A late console.* must not redden a green suite (#10374): vitest's worker
// forwards console output over RPC and discards the promise, and a write
// landing after teardown's rpcDone() snapshot is rejected into an unhandled
// error — a fully green run that exits 1. Disarming removes the mechanism.
// Mechanism + measured costs: examples/app-showcase/vitest.config.ts.
// Enforced repo-wide by scripts/check-console-intercept-disarm.mjs.
disableConsoleIntercept: true,
env: memoryBackedTmpEnv(),
},
});
18 changes: 16 additions & 2 deletions packages/runtime/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
// author can see it.

import { configDefaults, defineConfig } from 'vitest/config';
import { memoryBackedTmpEnv } from '../../scripts/vitest-memory-tmp.mjs';
import { readFileSync } from 'node:fs';
import path from 'node:path';
import { parseCLI } from 'vitest/node';
Expand Down Expand Up @@ -231,6 +232,15 @@ export default defineConfig({
// Each project re-declares the root block's test options: a ROOT-level
// value is inert for a project run (measured on vitest 4.1.10, see
// check-registry-log-declared / check-console-intercept-disarm).
//
// Each project's `env` also moves `os.tmpdir()` onto /dev/shm when it is
// writable (#27306; scripts/vitest-memory-tmp.mjs): the package-lifecycle
// integration tests keep their SQLite databases in an `mkdtemp` under it.
// Measured on the 51 test files that do, beside four `dd … conv=fsync`
// writers on the same disk: 25 of 685 cases timed out across 7 files
// (package revert, uninstall, duplicate-adopt, list-commits) and the run
// took 501 s, against all green in 102 s idle; with TMPDIR there, under the
// same writers, all green in 110 s. Assertions and budgets are untouched.
projects: [
{
extends: true,
Expand All @@ -242,7 +252,9 @@ export default defineConfig({
// #13517: quiet the registry's per-item registration chatter — the
// engine's own `OS_REGISTRY_LOG` seam, not a change to its shipped
// default. Enforced by scripts/check-registry-log-declared.mjs.
env: { OS_REGISTRY_LOG: 'warn' },
// TMPDIR on a memory-backed directory (#27306): see the note above
// `projects`.
env: { OS_REGISTRY_LOG: 'warn', ...memoryBackedTmpEnv() },
// A late console.* must not redden a green suite (#10374); see the root
// block. A ROOT-level value is inert for a project run, so it is
// declared here as well (scripts/check-console-intercept-disarm.mjs).
Expand All @@ -258,7 +270,9 @@ export default defineConfig({
// #13517: quiet the registry's per-item registration chatter — the
// engine's own `OS_REGISTRY_LOG` seam, not a change to its shipped
// default. Enforced by scripts/check-registry-log-declared.mjs.
env: { OS_REGISTRY_LOG: 'warn' },
// TMPDIR on a memory-backed directory (#27306): see the note above
// `projects`.
env: { OS_REGISTRY_LOG: 'warn', ...memoryBackedTmpEnv() },
// A late console.* must not redden a green suite (#10374); see the root
// block. A ROOT-level value is inert for a project run, so it is
// declared here as well (scripts/check-console-intercept-disarm.mjs).
Expand Down
9 changes: 8 additions & 1 deletion packages/services/service-automation/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineConfig } from 'vitest/config';
import path from 'node:path';
import { memoryBackedTmpEnv } from '../../../scripts/vitest-memory-tmp.mjs';

/**
* [#12157] `plugin.ts` registers the ADR-0126 §4 activation ledger's object,
Expand Down Expand Up @@ -38,7 +39,13 @@ export default defineConfig({
// ride a bare `console.warn` this level never gates — measured green with
// the declaration in place. Enforced by
// scripts/check-registry-log-declared.mjs.
env: { OS_REGISTRY_LOG: 'warn' },
// TMPDIR on /dev/shm when it is writable (#27306;
// scripts/vitest-memory-tmp.mjs): the three tests that persist runs to a
// SQLite file do it in an `mkdtemp` under it. Measured beside four
// `dd … conv=fsync` writers on the same disk: green in that run, but the
// slowest case went from 0.31 s idle to 4.35 s, 87% of its 5 s budget, for
// no change in the code; with TMPDIR there, under the same writers, 0.34 s.
env: { OS_REGISTRY_LOG: 'warn', ...memoryBackedTmpEnv() },
},
resolve: {
alias: [
Expand Down
11 changes: 9 additions & 2 deletions packages/services/service-package/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

// This config exists for three settings; everything else stays on vitest's
// This config exists for four settings; everything else stays on vitest's
// defaults, deliberately — a key added here re-specifies behaviour for every
// test file in the package (packages/cli/vitest.config.ts's header records the
// incident that taught that).
import { defineConfig } from 'vitest/config';
import path from 'node:path';
import { memoryBackedTmpEnv } from '../../../scripts/vitest-memory-tmp.mjs';

export default defineConfig({
test: {
Expand All @@ -19,7 +20,13 @@ export default defineConfig({
// #13517: quiet the registry's per-item registration chatter — the
// engine's own `OS_REGISTRY_LOG` seam, not a change to its shipped
// default. Enforced by scripts/check-registry-log-declared.mjs.
env: { OS_REGISTRY_LOG: 'warn' },
// TMPDIR on /dev/shm when it is writable (#27306;
// scripts/vitest-memory-tmp.mjs): the two tests that open a SQLite file do
// it in an `mkdtemp` under it. Measured beside four `dd … conv=fsync`
// writers on the same disk: green in that run, but the slowest case went
// from 0.17 s idle to 3.04 s, 61% of its 5 s budget, for no change in the
// code; with TMPDIR there, under the same writers, 0.15 s.
env: { OS_REGISTRY_LOG: 'warn', ...memoryBackedTmpEnv() },
},
resolve: {
// `hydration-convert-forward.test.ts` boots the REAL engine, protocol and
Expand Down
11 changes: 11 additions & 0 deletions scripts/vitest-memory-tmp.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Types for `vitest-memory-tmp.mjs`, published for the package vitest configs
// that import it. Several of those configs sit inside a package's
// `tsconfig.test.json` program, where an untyped `.mjs` import is TS7016. See
// `invoked-as.d.mts` for why this pairing exists; this file follows the same
// convention, and `check:declaration-mirrors` holds it in step with the module.

/**
* The `test.env` that moves `os.tmpdir()` onto /dev/shm:
* `{ TMPDIR: '/dev/shm' }` when that directory is writable, `{}` otherwise.
*/
export function memoryBackedTmpEnv(): Record<string, string>;
51 changes: 51 additions & 0 deletions scripts/vitest-memory-tmp.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.

import { accessSync, constants } from 'node:fs';

// The `test.env` that moves a suite's temp directory onto a memory-backed
// filesystem. A package whose tests write real files under `os.tmpdir()`
// (SQLite databases, scaffolded projects) spreads it into its own config:
//
// test: { disableConsoleIntercept: true, env: memoryBackedTmpEnv() }
//
// Not a key of `vitest.preset.mts`: that preset carries one setting for every
// re-exporter, and this one belongs only to the suites whose wall time was
// measured against the disk. A `.mjs` with a hand-written `.d.mts` beside it,
// like `tsup-drop-sources-content.mjs`: a package's `tsconfig.test.json`
// type-checks its `vitest.config.ts`, where a `.mts` import path is TS5097
// and an untyped `.mjs` one is TS7016 (`check:declaration-mirrors` holds the
// pair in step).
//
// ## Why
//
// On a Test Core shard the runner's disk is shared by every suite running
// beside this one. A case that fsyncs a file, or a hook that unlinks a tree of
// them, waits for that disk's queue, so its wall time measures the
// neighbours' I/O, not the code. Measured with four `dd … conv=fsync` writers
// on the same disk (#27076, #27306): cases that take tens of milliseconds idle
// took seconds and timed out, while with TMPDIR on /dev/shm under the same
// writers they stayed at their idle times. The code under test runs unchanged
// on a tmpfs, `fsync` included; only the wait for unrelated I/O is gone.
//
// `os.tmpdir()` reads TMPDIR on every call, and a child process the suite
// spawns inherits it, so the one setting covers both. A path built from the
// package or the repository does not follow TMPDIR and is not moved by this.
//
// A host without a writable /dev/shm (macOS, Windows) keeps its own temp
// directory: the setting is absent there, not an error.
const MEMORY_BACKED_TMP = '/dev/shm';

/** @param {string} dir */
function writableDir(dir) {
try {
accessSync(dir, constants.W_OK);
return true;
} catch {
return false;
}
}

/** @returns {Record<string, string>} */
export function memoryBackedTmpEnv() {
return writableDir(MEMORY_BACKED_TMP) ? { TMPDIR: MEMORY_BACKED_TMP } : {};
}
Loading