Skip to content
This repository was archived by the owner on Aug 7, 2026. It is now read-only.

Commit e241048

Browse files
thewtexclaude
andcommitted
test: cover generateData's per-dtype element width
Closes the gap noted when the dead `bytesPerElement` table was removed: nothing asserted that generateData selects the right typed-array constructor for each dtype. The existing checks could not see it, because a wrong constructor still returns exactly sizeBytes bytes of non-zero data. Adds a `generateData element width` block with two kinds of assertion. Per dtype, the returned bytes are re-viewed through the constructor the dtype is expected to use, and the opening run is checked for the shape generateData actually writes: sin(i * 0.01) scaled to the dtype's range climbs from zero to its first peak at element 157, so read at the correct width the series starts at 0, ends the quarter period above 0, and never falls back. Read at any other width the same bytes reinterpret into unrelated values and lose that shape. Across dtypes, the byte patterns at a fixed size must all differ, which pins the weaker property that the dtype argument affects the output at all. The expected dtype -> constructor mapping is restated in the test rather than exported from data.ts on purpose. Importing the table the implementation uses would make the assertion tautological; restating it means a change there fails here instead of being mirrored automatically. Verified by mutation rather than by inspection: - constructors swapped so every dtype builds an Int8Array, amplitudes left correct: the three non-int8 width tests fail, int8 correctly still passes, and every pre-existing test in the file passes - which is the gap. - generateData made to ignore `type` for both constructor and amplitude: the same three fail plus the distinctness test. Note the distinctness test caught nothing the width tests missed under either mutant. Kept because it states the "dtype must matter" invariant directly and runs in 31ms, but it is subsumed on current evidence. Benchmark suite goes 43 -> 48 tests. `vp check` clean; benchmark typecheck unchanged at 8 pre-existing TS2351 CodecConstructor errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent ee1772c commit e241048

1 file changed

Lines changed: 81 additions & 1 deletion

File tree

‎benchmark/src/data.test.ts‎

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,35 @@
11
import { describe, expect, it } from "vitest";
2-
import { DTYPES, SIZES, generateAllData, generateData } from "./data.js";
2+
import { DTYPES, SIZES, generateAllData, generateData, type DType } from "./data.js";
3+
4+
interface TypedArrayCtor {
5+
new (
6+
buffer: ArrayBufferLike,
7+
byteOffset: number,
8+
length: number,
9+
): { readonly length: number; [index: number]: number };
10+
readonly BYTES_PER_ELEMENT: number;
11+
readonly name: string;
12+
}
13+
14+
/**
15+
* The element width each dtype must be generated at, restated here rather than
16+
* imported from `data.js`. Pinning the mapping independently is the point: if
17+
* that module's constructor table changes, this fails instead of mirroring it.
18+
*/
19+
const ELEMENT_VIEWS: Record<DType, TypedArrayCtor> = {
20+
int8: Int8Array,
21+
int16: Int16Array,
22+
float32: Float32Array,
23+
float64: Float64Array,
24+
};
25+
26+
function sameBytes(a: Uint8Array, b: Uint8Array): boolean {
27+
if (a.byteLength !== b.byteLength) return false;
28+
for (let i = 0; i < a.byteLength; i++) {
29+
if (a[i] !== b[i]) return false;
30+
}
31+
return true;
32+
}
333

434
describe("generateData", () => {
535
for (const dtype of DTYPES) {
@@ -19,6 +49,56 @@ describe("generateData", () => {
1949
}
2050
});
2151

52+
describe("generateData element width", () => {
53+
const sizeBytes = SIZES["1MB"];
54+
55+
// generateData fills element i with sin(i * 0.01) scaled to the dtype's
56+
// range, so the opening run climbs from zero to its first peak at i = 157.
57+
const QUARTER_PERIOD = Math.floor(Math.PI / 2 / 0.01);
58+
59+
for (const dtype of DTYPES) {
60+
const View = ELEMENT_VIEWS[dtype];
61+
const expectedElements = sizeBytes / View.BYTES_PER_ELEMENT;
62+
63+
it(`lays ${dtype} out as ${View.name} elements`, () => {
64+
const buf = generateData(dtype, sizeBytes);
65+
expect(buf.byteLength % View.BYTES_PER_ELEMENT).toBe(0);
66+
67+
const view = new View(buf.buffer, buf.byteOffset, expectedElements);
68+
expect(view.length).toBe(expectedElements);
69+
70+
// Read at the correct width the ramp starts at zero and never falls back
71+
// before the peak. Read at any other width the same bytes reinterpret
72+
// into unrelated values and lose that shape — which a byteLength check
73+
// cannot see, since a wrong constructor still yields sizeBytes bytes.
74+
expect(view[0]).toBe(0);
75+
expect(view[QUARTER_PERIOD]).toBeGreaterThan(0);
76+
77+
let firstDrop = -1;
78+
for (let i = 1; i <= QUARTER_PERIOD; i++) {
79+
if (view[i] < view[i - 1]) {
80+
firstDrop = i;
81+
break;
82+
}
83+
}
84+
expect(firstDrop, `ramp fell back at element ${firstDrop}`).toBe(-1);
85+
});
86+
}
87+
88+
it("gives every dtype a distinct byte pattern at the same size", () => {
89+
const buffers = DTYPES.map((dtype) => generateData(dtype, sizeBytes));
90+
91+
for (let i = 0; i < buffers.length; i++) {
92+
for (let j = i + 1; j < buffers.length; j++) {
93+
expect(
94+
sameBytes(buffers[i], buffers[j]),
95+
`${DTYPES[i]} and ${DTYPES[j]} produced identical bytes`,
96+
).toBe(false);
97+
}
98+
}
99+
});
100+
});
101+
22102
describe("generateAllData", () => {
23103
it("returns 4 dtypes × 2 sizes = 8 entries", () => {
24104
const data = generateAllData();

0 commit comments

Comments
 (0)