Initial Commit
This commit is contained in:
137
packages/media/audio/audio-core/tests/property.test.ts
Normal file
137
packages/media/audio/audio-core/tests/property.test.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import * as fc from "fast-check";
|
||||
import { AudioDocument } from "../src/document/document.js";
|
||||
import { BLOCK_SIZE } from "../src/page/page.js";
|
||||
import { PlanEngine } from "../src/plan/plan-engine.js";
|
||||
import { OpRegistry } from "../src/registry/op-registry.js";
|
||||
|
||||
function makeDoc(
|
||||
sourceLength: number,
|
||||
channels: number,
|
||||
sampleRate: number
|
||||
): AudioDocument {
|
||||
const pageSize = 48000;
|
||||
const pages = [];
|
||||
for (let s = 0; s < sourceLength; s += pageSize) {
|
||||
const len = Math.min(pageSize, sourceLength - s);
|
||||
pages.push({
|
||||
startSample: s,
|
||||
channelData: Array.from(
|
||||
{ length: channels },
|
||||
() => new Float32Array(len)
|
||||
),
|
||||
});
|
||||
}
|
||||
return new AudioDocument({
|
||||
sampleRate,
|
||||
channels,
|
||||
sourceLength,
|
||||
pages,
|
||||
edits: [],
|
||||
});
|
||||
}
|
||||
|
||||
describe("Property: identity edit preserves plan length", () => {
|
||||
it("passes with random source lengths and sample rates", {
|
||||
timeout: 30000,
|
||||
}, () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc.integer({ min: 1000, max: 500_000 }),
|
||||
fc.integer({ min: 1, max: 8 }),
|
||||
fc.integer({ min: 8000, max: 192_000 }),
|
||||
(sourceLength, channels, sampleRate) => {
|
||||
const doc = makeDoc(sourceLength, channels, sampleRate);
|
||||
const registry = OpRegistry.createWithDefaults();
|
||||
const engine = new PlanEngine(registry);
|
||||
const before = engine.compile(doc, true);
|
||||
doc.run({ type: "speed", opts: { rate: 1 } });
|
||||
const after = engine.compile(doc, true);
|
||||
expect(after.totalLen).toBe(before.totalLen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Property: plan compilation is deterministic", () => {
|
||||
it("plan compilation is deterministic", { timeout: 30000 }, () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc.integer({ min: 1000, max: 200_000 }),
|
||||
fc.integer({ min: 1, max: 4 }),
|
||||
fc.integer({ min: 8000, max: 96_000 }),
|
||||
fc.integer({ min: 1, max: 4 }),
|
||||
(sourceLength, channels, sampleRate, repeatTimes) => {
|
||||
const doc = makeDoc(sourceLength, channels, sampleRate);
|
||||
doc.run({ type: "repeat", opts: { times: repeatTimes } });
|
||||
doc.run({ type: "gain", opts: { gain: 0.5 } });
|
||||
const registry = OpRegistry.createWithDefaults();
|
||||
const engine = new PlanEngine(registry);
|
||||
const first = engine.compile(doc, true);
|
||||
const second = engine.compile(doc, true);
|
||||
expect(second).toEqual(first);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Property: stream yields exact total sample count", () => {
|
||||
it("stream yields exact total sample count", {
|
||||
timeout: 30000,
|
||||
}, async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
fc.integer({ min: 1000, max: 200_000 }),
|
||||
fc.integer({ min: 1, max: 4 }),
|
||||
fc.integer({ min: 1, max: BLOCK_SIZE }),
|
||||
async (sourceLength, channels, blockSize) => {
|
||||
const doc = makeDoc(sourceLength, channels, 48000);
|
||||
const registry = OpRegistry.createWithDefaults();
|
||||
const engine = new PlanEngine(registry);
|
||||
const plan = engine.compile(doc, false);
|
||||
let totalSamples = 0;
|
||||
for await (const block of doc.stream({ blockSize })) {
|
||||
totalSamples += block[0]?.length ?? 0;
|
||||
}
|
||||
expect(totalSamples).toBe(plan.totalLen);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Property: clone preserves structure with new id", () => {
|
||||
it("clone toJSON equals original toJSON", { timeout: 30000 }, () => {
|
||||
// The property under test is structural — `clone().toJSON()` equals
|
||||
// the original's `toJSON()` modulo `id`. The doc's *size* is
|
||||
// irrelevant to that invariant, so cap inputs small to keep the
|
||||
// fast-check sweep inside the 30 s budget. The other three
|
||||
// properties in this file deliberately exercise large source
|
||||
// lengths because they probe size-dependent code paths (plan
|
||||
// compilation, streaming); this one does not.
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc.integer({ min: 1000, max: 10_000 }),
|
||||
fc.integer({ min: 1, max: 4 }),
|
||||
fc.integer({ min: 8000, max: 96_000 }),
|
||||
(sourceLength, channels, sampleRate) => {
|
||||
const doc = makeDoc(sourceLength, channels, sampleRate);
|
||||
doc.run({
|
||||
type: "trim",
|
||||
opts: { start: 0, end: sourceLength },
|
||||
});
|
||||
const cloned = doc.clone();
|
||||
const { id: originalId, ...originalRest } = doc.toJSON();
|
||||
const { id: clonedId, ...clonedRest } = cloned.toJSON();
|
||||
// The clone gets a fresh id but is otherwise an exact
|
||||
// structural copy — toJSON matches on every other field.
|
||||
expect(clonedId).not.toBe(originalId);
|
||||
expect(clonedRest).toEqual(originalRest);
|
||||
}
|
||||
),
|
||||
{ numRuns: 25 }
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user