Skip to content

Commit de6303c

Browse files
committed
test(scripts): floor measure-test-shard-timings' self-test on a registered battery
The 22 bare `if (...) throw ...` assertions in `selfTest()` are each wrapped verbatim in the `check(() => { ... })` thunk PR #15198 landed: the condition is carried in untouched, so no `!` can be dropped while the case still registers and still passes. Zero named section banners, so one hoisted battery (the PR #14896 / #15003 / #15217 shape), floored at the measured 22 with the roster's own size pinned at 1. The floor throws, matching this file's own sink, and is evaluated before the verdict so the success line can only be printed by a run that actually ran its cases. Part of #13799 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
1 parent 5b09356 commit de6303c

1 file changed

Lines changed: 191 additions & 38 deletions

File tree

scripts/measure-test-shard-timings.mjs

Lines changed: 191 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,75 @@ export function buildDataset({ perSummary, fileCounts, provenance }) {
174174
};
175175
}
176176

177+
// -- The self-test's own battery roster and floor (#13489) ------------------
178+
//
179+
// `--self-test` reaching its verdict used to be this self-test's ONLY success
180+
// condition, so "every case held" and "the cases never ran" printed the same
181+
// line. Closed the way PR #13487 validated on check-doc-authoring: what is
182+
// pinned is the registered NAMES, not a number.
183+
//
184+
// This file's assertions are bare `throw`s rather than calls to an assertion
185+
// helper, so they are counted by the `check(() => { ... })` THUNK PR #15198
186+
// measured: the existing `if (...) throw ...` is carried into the thunk
187+
// VERBATIM and the condition is never touched. Routing these through a boolean
188+
// helper instead would mean inverting 22 failure conditions by hand, and a
189+
// dropped `!` yields an assertion that still registers its case and still
190+
// passes -- invisible to the very floor being installed here. The throw still
191+
// propagates: this file fails fast on the FIRST broken assertion, as it always
192+
// has.
193+
//
194+
// This file declares ONE battery, opened at the top of the self-test body. Its
195+
// blocks are headed by unmarked prose comments -- it carries ZERO named section
196+
// banners, fewer than the two the sectioning criterion needs, and a comment is
197+
// NOT promoted to a section head (that is a judgement per comment this
198+
// transplant does not make). The hoisted single battery is the shape PR #14896,
199+
// PR #15003 and PR #15217 landed for exactly this case.
200+
//
201+
// The count is a FLOOR, not an equality -- adding cases is ordinary work and
202+
// must not red. A battery BELOW its floor means cases stopped running; the
203+
// remedy is to find what stopped registering, never to lower the number.
204+
const SELF_TEST_BATTERIES = Object.freeze({
205+
'measure-test-shard-timings self-test': 22,
206+
});
207+
208+
// DELETING an entry silences that battery's floor exactly as effectively as
209+
// zeroing it, so the roster's own size is pinned too.
210+
const SELF_TEST_BATTERY_FLOOR = 1;
211+
212+
// The key an assertion is filed under when no battery is open. It is not a
213+
// declared battery, so it reds by the same set difference rather than silently
214+
// inflating whichever battery happened to run last.
215+
const UNATTRIBUTED_BATTERY = '(no battery open)';
216+
177217
// Returned by `selfTest()` only after its verdict is printed. The dispatch
178218
// refuses anything else: a `return` that leaves the function above that line
179219
// prints nothing and still exits 0 — a self-test that never finished, reported
180220
// as one that passed (#13798).
181221
const SELF_TEST_VERDICT = 'measure-test-shard-timings self-test reached its verdict';
182222

183223
function selfTest() {
224+
// The battery ledger this self-test's floor is evaluated against (#13489).
225+
// `battery()` opens a battery; every assertion below is attributed to the one
226+
// most recently opened, so a section that stops running stops registering and
227+
// names ITSELF at the floor rather than going quiet.
228+
const batterySeen = new Map();
229+
let openBattery = null;
230+
const battery = (name) => {
231+
openBattery = name;
232+
};
233+
const registerCase = () => {
234+
const b = openBattery ?? UNATTRIBUTED_BATTERY;
235+
batterySeen.set(b, (batterySeen.get(b) ?? 0) + 1);
236+
};
237+
battery('measure-test-shard-timings self-test');
238+
// The thunk: it registers the case and then runs the existing site VERBATIM,
239+
// so no assertion condition is inverted or rewritten and the sink keeps its
240+
// own semantics. Registration happens whether or not the site fires, which is
241+
// what makes the count a floor on cases RUN rather than a count of failures.
242+
const check = (fn) => {
243+
registerCase();
244+
fn();
245+
};
184246
const summary = (tasks) => ({ tasks });
185247
const testTask = (pkg, start, end, status = 'MISS', exitCode = 0) => ({
186248
taskId: `${pkg}#test`,
@@ -190,9 +252,15 @@ function selfTest() {
190252
execution: { startTime: start, endTime: end, exitCode },
191253
});
192254

193-
if (median([3]) !== 3) throw new Error('median: single value');
194-
if (median([5, 1, 3]) !== 3) throw new Error('median: odd length is not order-dependent');
195-
if (median([1, 2, 3, 4]) !== 2.5) throw new Error('median: even length averages the middle pair');
255+
check(() => {
256+
if (median([3]) !== 3) throw new Error('median: single value');
257+
});
258+
check(() => {
259+
if (median([5, 1, 3]) !== 3) throw new Error('median: odd length is not order-dependent');
260+
});
261+
check(() => {
262+
if (median([1, 2, 3, 4]) !== 2.5) throw new Error('median: even length averages the middle pair');
263+
});
196264

197265
// A build task in the same summary must not be read as a test duration.
198266
const mixed = samplesFromSummary(
@@ -202,23 +270,37 @@ function selfTest() {
202270
]),
203271
'f'
204272
);
205-
if (mixed.samples.get('a') !== 2) throw new Error(`task filter: got ${mixed.samples.get('a')}`);
206-
if (mixed.samples.size !== 1) throw new Error('task filter: a non-test task was sampled');
273+
check(() => {
274+
if (mixed.samples.get('a') !== 2) throw new Error(`task filter: got ${mixed.samples.get('a')}`);
275+
});
276+
check(() => {
277+
if (mixed.samples.size !== 1) throw new Error('task filter: a non-test task was sampled');
278+
});
207279

208280
// THE ONE THAT MATTERS: a cache HIT is skipped, never recorded as ~0s.
209281
// The control leg first -- a genuine 40ms MISS is a legitimate measurement,
210282
// so only the HIT/MISS pair below proves the skip is about the cache status
211283
// and not about the window being short.
212284
const shortMiss = samplesFromSummary(summary([testTask('a', 0, 40)]), 'f');
213-
if (shortMiss.samples.get('a') !== 0.04) throw new Error('cache: a short MISS was not recorded');
285+
check(() => {
286+
if (shortMiss.samples.get('a') !== 0.04) throw new Error('cache: a short MISS was not recorded');
287+
});
214288
const withHit = samplesFromSummary(summary([testTask('a', 0, 40, 'HIT'), testTask('b', 0, 60_000)]), 'f');
215-
if (withHit.samples.has('a')) throw new Error('cache: a HIT was recorded as a measurement');
216-
if (!withHit.skippedCached.includes('a')) throw new Error('cache: a HIT was not reported as skipped');
217-
if (withHit.samples.get('b') !== 60) throw new Error('cache: the MISS beside it was lost');
289+
check(() => {
290+
if (withHit.samples.has('a')) throw new Error('cache: a HIT was recorded as a measurement');
291+
});
292+
check(() => {
293+
if (!withHit.skippedCached.includes('a')) throw new Error('cache: a HIT was not reported as skipped');
294+
});
295+
check(() => {
296+
if (withHit.samples.get('b') !== 60) throw new Error('cache: the MISS beside it was lost');
297+
});
218298

219299
// A failed suite stopped early; its window is not the package's cost.
220300
const failed = samplesFromSummary(summary([testTask('a', 0, 500, 'MISS', 1)]), 'f');
221-
if (failed.samples.has('a')) throw new Error('exit: a failed suite was recorded as a duration');
301+
check(() => {
302+
if (failed.samples.has('a')) throw new Error('exit: a failed suite was recorded as a duration');
303+
});
222304

223305
const threw = (fn) => {
224306
try {
@@ -228,13 +310,19 @@ function selfTest() {
228310
return true;
229311
}
230312
};
231-
if (!threw(() => samplesFromSummary({}, 'f'))) throw new Error('shape: a non-summary was accepted');
232-
if (!threw(() => samplesFromSummary(summary([{ task: 'test', package: '', cache: { status: 'MISS' } }]), 'f'))) {
233-
throw new Error('shape: a nameless test task was accepted');
234-
}
235-
if (!threw(() => samplesFromSummary(summary([{ task: 'test', package: 'a', cache: { status: 'MISS' } }]), 'f'))) {
236-
throw new Error('shape: a test task with no execution window was accepted');
237-
}
313+
check(() => {
314+
if (!threw(() => samplesFromSummary({}, 'f'))) throw new Error('shape: a non-summary was accepted');
315+
});
316+
check(() => {
317+
if (!threw(() => samplesFromSummary(summary([{ task: 'test', package: '', cache: { status: 'MISS' } }]), 'f'))) {
318+
throw new Error('shape: a nameless test task was accepted');
319+
}
320+
});
321+
check(() => {
322+
if (!threw(() => samplesFromSummary(summary([{ task: 'test', package: 'a', cache: { status: 'MISS' } }]), 'f'))) {
323+
throw new Error('shape: a test task with no execution window was accepted');
324+
}
325+
});
238326

239327
// Merging: the same package sampled by several shards collapses to its median.
240328
const merged = buildDataset({
@@ -246,43 +334,108 @@ function selfTest() {
246334
fileCounts: new Map([['a', 10], ['big', 50]]),
247335
provenance: { measuredAt: 'test' },
248336
});
249-
if (merged.packages.a !== 20) throw new Error(`merge: expected the median 20, got ${merged.packages.a}`);
337+
check(() => {
338+
if (merged.packages.a !== 20) throw new Error(`merge: expected the median 20, got ${merged.packages.a}`);
339+
});
250340
// rates: a -> 20/10 = 2, big -> 100/50 = 2 => 2
251-
if (merged.secondsPerTestFileFallback !== 2) {
252-
throw new Error(`fallback rate: got ${merged.secondsPerTestFileFallback}`);
253-
}
341+
check(() => {
342+
if (merged.secondsPerTestFileFallback !== 2) {
343+
throw new Error(`fallback rate: got ${merged.secondsPerTestFileFallback}`);
344+
}
345+
});
254346
// Packages too small to vote on the rate are excluded from it but still kept.
255347
const tiny = buildDataset({
256348
perSummary: [samplesFromSummary(summary([testTask('a', 0, 10_000), testTask('t', 0, 300)]), 'f')],
257349
fileCounts: new Map([['a', 5], ['t', 1]]),
258350
provenance: {},
259351
});
260-
if (tiny.secondsPerTestFileFallback !== 2) throw new Error(`rate: a 0.3s/1-file package voted (${tiny.secondsPerTestFileFallback})`);
261-
if (tiny.packages.t !== 0.3) throw new Error('rate: the small package was dropped from the dataset');
352+
check(() => {
353+
if (tiny.secondsPerTestFileFallback !== 2) throw new Error(`rate: a 0.3s/1-file package voted (${tiny.secondsPerTestFileFallback})`);
354+
});
355+
check(() => {
356+
if (tiny.packages.t !== 0.3) throw new Error('rate: the small package was dropped from the dataset');
357+
});
262358

263-
if (!threw(() =>
264-
buildDataset({
265-
perSummary: [samplesFromSummary(summary([testTask('a', 0, 40, 'HIT')]), 'f')],
266-
fileCounts: new Map(),
267-
provenance: {},
268-
})
269-
)) {
270-
throw new Error('an all-cached run produced a dataset instead of refusing');
271-
}
359+
check(() => {
360+
if (!threw(() =>
361+
buildDataset({
362+
perSummary: [samplesFromSummary(summary([testTask('a', 0, 40, 'HIT')]), 'f')],
363+
fileCounts: new Map(),
364+
provenance: {},
365+
})
366+
)) {
367+
throw new Error('an all-cached run produced a dataset instead of refusing');
368+
}
369+
});
272370

273371
// Workspace resolution, at the depth that actually caught a defect. A
274372
// one-level scan resolves `packages/*` and returns null for the ~60% of the
275373
// workspace that lives under `packages/drivers/*`, `packages/services/*` and
276374
// seven more roots -- silently, as "this package has no test files", which
277375
// then skews the fallback rate toward whichever half sits at depth 1.
278376
const nested = packageDirForName('@objectstack/driver-turso');
279-
if (nested === null) throw new Error('workspace: a package nested under packages/drivers/ did not resolve');
280-
if (path.relative(REPO_ROOT, nested).split(path.sep).length < 3) {
281-
throw new Error(`workspace: expected a nested path, got ${nested}`);
282-
}
283-
if (countTestFiles(nested) === 0) throw new Error('workspace: the resolved nested package reports no test files');
377+
check(() => {
378+
if (nested === null) throw new Error('workspace: a package nested under packages/drivers/ did not resolve');
379+
});
380+
check(() => {
381+
if (path.relative(REPO_ROOT, nested).split(path.sep).length < 3) {
382+
throw new Error(`workspace: expected a nested path, got ${nested}`);
383+
}
384+
});
385+
check(() => {
386+
if (countTestFiles(nested) === 0) throw new Error('workspace: the resolved nested package reports no test files');
387+
});
284388
const flat = packageDirForName('@objectstack/spec');
285-
if (flat === null || path.basename(flat) !== 'spec') throw new Error('workspace: a depth-1 package stopped resolving');
389+
check(() => {
390+
if (flat === null || path.basename(flat) !== 'spec') throw new Error('workspace: a depth-1 package stopped resolving');
391+
});
392+
393+
// -- The floor: every declared battery RAN, and ran its cases (#13489) ----
394+
//
395+
// Evaluated after every battery has had its chance and BEFORE the verdict, so
396+
// the success line below can only be printed by a run in which the set of
397+
// batteries that registered cases EQUALS the set declared. A set difference
398+
// names WHICH battery stopped; a count says only that something did.
399+
//
400+
// It THROWS rather than collecting into a `failures` array because that is
401+
// how every other assertion in this file reports: the dispatch below turns an
402+
// unfinished self-test into a non-zero exit, and a floor breach is exactly
403+
// that -- a self-test that did not run what it claims to run.
404+
const floorFailures = [];
405+
const declaredBatteries = Object.keys(SELF_TEST_BATTERIES);
406+
if (declaredBatteries.length < SELF_TEST_BATTERY_FLOOR) {
407+
floorFailures.push(
408+
`SELF_TEST_BATTERIES declares ${declaredBatteries.length} batteries, below the pinned ` +
409+
`${SELF_TEST_BATTERY_FLOOR} -- a battery deleted from the roster takes its own floor with it.`
410+
);
411+
}
412+
for (const [name, count] of batterySeen) {
413+
if (declaredBatteries.includes(name)) continue;
414+
floorFailures.push(
415+
`self-test battery "${name}" registered ${count} case(s) but is not declared in ` +
416+
'SELF_TEST_BATTERIES -- a case attributed to no declared battery is one nothing floors.'
417+
);
418+
}
419+
for (const name of declaredBatteries) {
420+
const count = batterySeen.get(name) ?? 0;
421+
if (count >= SELF_TEST_BATTERIES[name]) continue;
422+
floorFailures.push(
423+
count === 0
424+
? `self-test battery "${name}" DID NOT RUN -- 0 cases registered, ${SELF_TEST_BATTERIES[name]} pinned. ` +
425+
'The verdict below would have claimed those cases hold.'
426+
: `self-test battery "${name}" registered ${count} case(s), below its pinned floor of ` +
427+
`${SELF_TEST_BATTERIES[name]} -- cases that used to run no longer do.`
428+
);
429+
}
430+
if (floorFailures.length > 0) {
431+
throw new Error(
432+
`measure-test-shard-timings self-test floor (${floorFailures.length} breach(es)):\n` +
433+
floorFailures.map((f) => ` - ${f}`).join('\n') +
434+
'\n A battery at or below its floor means cases STOPPED RUNNING -- the battery is the bug, ' +
435+
'not the number. Find what stopped registering (an early return, a deleted block, a guard ' +
436+
'that now skips) and restore it.'
437+
);
438+
}
286439

287440
console.log('measure-test-shard-timings: self-test OK');
288441

0 commit comments

Comments
 (0)