test: enforce simulation and render budgets
This commit is contained in:
@@ -38,6 +38,10 @@ const grant = (): IceConfigGrant => ({
|
||||
describe("ICE configuration wire contract", () => {
|
||||
it("accepts exact requests and bounded ephemeral grants", () => {
|
||||
assert.equal(parseIceConfigRequest(request()).ok, true);
|
||||
assert.equal(parseIceConfigRequest({
|
||||
...request(),
|
||||
credential: { ...request().credential, sessionId: "_opaque-session", participantId: "-opaque-participant" },
|
||||
}).ok, true, "accepts every server-generated base64url prefix");
|
||||
assert.equal(parseIceConfigResponse(JSON.parse(JSON.stringify(grant()))).ok, true);
|
||||
assert.equal(isIceConfigGrantActive(grant(), 300_000), true);
|
||||
assert.equal(isIceConfigGrantActive(grant(), 601_000), false);
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import { ActorController, type ActorControllerOptions } from "../actors/controller.ts";
|
||||
import { AircraftController, type AircraftWaypoint } from "../aircraft/controller.ts";
|
||||
import { Plan } from "../interiors/plan.ts";
|
||||
import type { Level, Office, Room, Wall } from "../interiors/types.ts";
|
||||
import { createWalker } from "../interiors/walker.ts";
|
||||
import CALIFORNIA_TRANSPORT from "../transport/california.ts";
|
||||
import { VehicleController } from "../transport/vehicleController.ts";
|
||||
import { buildRoutePath, VehicleSimulation } from "../transport/vehicleSim.ts";
|
||||
|
||||
const SOAK_TIMEOUT_MS = 5_000;
|
||||
const HOUR_AT_TEN_HZ = 36_000;
|
||||
|
||||
function finiteNumbers(value: unknown, path = "state"): void {
|
||||
if (typeof value === "number") {
|
||||
assert.ok(Number.isFinite(value), `${path} must remain finite`);
|
||||
return;
|
||||
}
|
||||
if (typeof value !== "object" || value === null) return;
|
||||
for (const [key, nested] of Object.entries(value)) finiteNumbers(nested, `${path}.${key}`);
|
||||
}
|
||||
|
||||
function inside(value: number, minimum: number, maximum: number, label: string): void {
|
||||
assert.ok(value >= minimum && value <= maximum, `${label} ${value} outside [${minimum}, ${maximum}]`);
|
||||
}
|
||||
|
||||
function countForwardWrap(previous: number, next: number): number {
|
||||
return previous > 0.9 && next < 0.1 ? 1 : 0;
|
||||
}
|
||||
|
||||
describe("accelerated long-duration deterministic simulation", () => {
|
||||
it("runs both California routes and ambient traffic through repeated completions", { timeout: SOAK_TIMEOUT_MS }, () => {
|
||||
for (const routeId of ["la-sf-us-101", "la-sf-i-5"] as const) {
|
||||
const path = buildRoutePath(CALIFORNIA_TRANSPORT, routeId);
|
||||
const first = new VehicleSimulation(CALIFORNIA_TRANSPORT, { routeId, count: 24, seed: 115, timeScale: 900 });
|
||||
const replay = new VehicleSimulation(CALIFORNIA_TRANSPORT, { routeId, count: 24, seed: 115, timeScale: 900 });
|
||||
let wraps = 0;
|
||||
let prior = first.poses()[0]?.progress ?? 0;
|
||||
|
||||
// 15 rendered minutes at the production 900x traffic scale is more than
|
||||
// nine simulated days, advanced in bounded 20 Hz fixed steps.
|
||||
for (let frame = 0; frame < 3_600; frame += 1) {
|
||||
first.tick(0.25);
|
||||
replay.tick(0.25);
|
||||
const hero = first.poses()[0];
|
||||
assert.ok(hero);
|
||||
wraps += countForwardWrap(prior, hero.progress);
|
||||
prior = hero.progress;
|
||||
if (frame % 60 !== 0) continue;
|
||||
for (const pose of first.poses()) {
|
||||
finiteNumbers(pose, `${routeId}.${pose.id}`);
|
||||
inside(pose.progress, 0, 1, `${pose.id}.progress`);
|
||||
inside(pose.distanceM, 0, path.lengthM, `${pose.id}.distanceM`);
|
||||
inside(pose.lat, 32, 43, `${pose.id}.lat`);
|
||||
inside(pose.lng, -125, -113, `${pose.id}.lng`);
|
||||
}
|
||||
}
|
||||
assert.ok(wraps >= 10, `${routeId} hero completed only ${wraps} circuits`);
|
||||
assert.deepEqual(first.poses(), replay.poses(), `${routeId} seeded traffic drifted on replay`);
|
||||
}
|
||||
});
|
||||
|
||||
it("drives the playable vehicle for an accelerated hour without drift or escape", { timeout: SOAK_TIMEOUT_MS }, () => {
|
||||
const options = {
|
||||
routeId: "la-sf-us-101",
|
||||
fixedStepSeconds: 0.1,
|
||||
initialSpeedMps: 20,
|
||||
travelScale: 900,
|
||||
guardrailOffsetM: 5,
|
||||
} as const;
|
||||
const first = new VehicleController(CALIFORNIA_TRANSPORT, options);
|
||||
const replay = new VehicleController(CALIFORNIA_TRANSPORT, options);
|
||||
const path = buildRoutePath(CALIFORNIA_TRANSPORT, options.routeId);
|
||||
let prior = first.state().progress;
|
||||
let completions = 0;
|
||||
|
||||
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
|
||||
const actions = step % 1_200 < 60
|
||||
? { throttle: 0.75, steering: Math.sin(step / 20) * 0.35 }
|
||||
: step % 1_200 === 60 ? { modeRequest: "assisted" as const } : undefined;
|
||||
first.stepFixed(actions);
|
||||
replay.stepFixed(actions);
|
||||
const state = first.state();
|
||||
completions += countForwardWrap(prior, state.progress);
|
||||
prior = state.progress;
|
||||
if (step % 300 !== 0) continue;
|
||||
finiteNumbers(state);
|
||||
inside(state.progress, 0, 1, "vehicle.progress");
|
||||
inside(state.distanceM, 0, path.lengthM, "vehicle.distanceM");
|
||||
inside(state.lateralOffsetM, -5, 5, "vehicle.lateralOffsetM");
|
||||
inside(state.speedMps, 0, 58, "vehicle.speedMps");
|
||||
}
|
||||
assert.ok(completions >= 20, `playable vehicle completed only ${completions} route circuits`);
|
||||
assert.ok(first.state().elapsedSteps === HOUR_AT_TEN_HZ);
|
||||
assert.deepEqual(first.snapshot(), replay.snapshot());
|
||||
});
|
||||
|
||||
it("flies a crow for an accelerated hour inside strict 3D bounds", { timeout: SOAK_TIMEOUT_MS }, () => {
|
||||
const options: ActorControllerOptions = {
|
||||
kind: "crow",
|
||||
mode: "flight",
|
||||
identity: { id: "soak-crow", displayName: "Soak Crow", authenticated: false, profile: {} },
|
||||
position: { x: 0, y: 25, z: 0 },
|
||||
groundY: 0,
|
||||
minFlightAltitude: 5,
|
||||
maxFlightAltitude: 50,
|
||||
horizontalBounds: { minX: -200, maxX: 200, minZ: -200, maxZ: 200 },
|
||||
fixedStepSeconds: 0.1,
|
||||
};
|
||||
const first = new ActorController(options);
|
||||
const replay = new ActorController(options);
|
||||
const visited = new Set<string>();
|
||||
|
||||
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
|
||||
const phase = step % 1_800;
|
||||
const actions = {
|
||||
forward: 0.65 + 0.3 * Math.sin(step / 173),
|
||||
turn: phase < 900 ? 0.23 : -0.31,
|
||||
pitch: Math.sin(step / 257) * 0.7,
|
||||
climb: Math.sin(step / 401) * 0.8,
|
||||
glide: phase >= 1_500,
|
||||
};
|
||||
first.stepFixed(actions);
|
||||
replay.stepFixed(actions);
|
||||
const state = first.state();
|
||||
if (step % 120 !== 0) continue;
|
||||
finiteNumbers(state);
|
||||
inside(state.x, -200, 200, "crow.x");
|
||||
inside(state.z, -200, 200, "crow.z");
|
||||
inside(state.y, 5, 50, "crow.y");
|
||||
inside(state.posePhase, 0, Math.PI * 2, "crow.posePhase");
|
||||
visited.add(`${Math.round(state.x / 20)}:${Math.round(state.z / 20)}:${Math.round(state.y / 5)}`);
|
||||
}
|
||||
assert.ok(first.state().distanceM > 20_000,
|
||||
`crow accumulated only ${first.state().distanceM.toFixed(1)} m of flight distance`);
|
||||
assert.ok(visited.size > 25, `crow explored only ${visited.size} coarse cells`);
|
||||
assert.deepEqual(first.snapshot(), replay.snapshot());
|
||||
});
|
||||
|
||||
it("walks against office walls for an accelerated hour without tunnelling or sticking", { timeout: SOAK_TIMEOUT_MS }, () => {
|
||||
const room: Room = {
|
||||
id: "soak-room", name: "Soak Room", floor: "floor" as never,
|
||||
outline: [{ x: 0, z: 0 }, { x: 30, z: 0 }, { x: 30, z: 20 }, { x: 0, z: 20 }],
|
||||
};
|
||||
const walls: Wall[] = [
|
||||
{ id: "vertical", from: { x: 15, z: 0 }, to: { x: 15, z: 20 },
|
||||
openings: [{ kind: "door", start: 8.5, width: 3, sill: 0, head: 2.2 }] },
|
||||
{ id: "horizontal", from: { x: 0, z: 10 }, to: { x: 12, z: 10 } },
|
||||
];
|
||||
const level: Level = {
|
||||
id: "ground", name: "Ground", elevation: 0, wallHeight: 3, wallThickness: 0.12,
|
||||
floorplan: { rooms: [room], walls },
|
||||
};
|
||||
const plan = new Plan({ id: "soak-office", name: "Soak Office", levels: [level], viewpoints: [] } as Office,
|
||||
{ warn: false });
|
||||
const create = () => createWalker(plan, {
|
||||
levelId: "ground", position: { x: 4, z: 4 }, radius: 0.3,
|
||||
speed: 2.4, fixedStep: 0.1, maxCatchUpSteps: 1,
|
||||
});
|
||||
const first = create();
|
||||
const replay = create();
|
||||
const directions = [
|
||||
{ x: 1, z: 0.23 }, { x: 0.18, z: 1 }, { x: -1, z: -0.17 }, { x: -0.11, z: -1 },
|
||||
] as const;
|
||||
const visited = new Set<string>();
|
||||
|
||||
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
|
||||
const action = directions[Math.floor(step / 450) % directions.length]!;
|
||||
const a = first.tick(0.1, action);
|
||||
replay.tick(0.1, action);
|
||||
if (step % 30 === 0) visited.add(`${Math.round(a.position.x)}:${Math.round(a.position.z)}`);
|
||||
if (step % 120 !== 0) continue;
|
||||
finiteNumbers(a);
|
||||
inside(a.position.x, 0.3, 29.7, "walker.x");
|
||||
inside(a.position.z, 0.3, 19.7, "walker.z");
|
||||
assert.equal(plan.blocked("ground", a.position, a.position, 0.3), false, "walker entered collision geometry");
|
||||
}
|
||||
assert.ok(first.state().distance > 200,
|
||||
`walker accumulated only ${first.state().distance.toFixed(1)} m of distance`);
|
||||
assert.ok(visited.size > 20, `walker visited only ${visited.size} cells`);
|
||||
assert.deepEqual(first.state(), replay.state());
|
||||
});
|
||||
|
||||
it("circuits an assisted aircraft route for an accelerated hour within its envelope", { timeout: SOAK_TIMEOUT_MS }, () => {
|
||||
const route: readonly AircraftWaypoint[] = [
|
||||
{ id: "west", lat: 34.05, lng: -118.28, altitudeM: 1_200 },
|
||||
{ id: "north", lat: 34.13, lng: -118.20, altitudeM: 1_650 },
|
||||
{ id: "east", lat: 34.05, lng: -118.12, altitudeM: 1_350 },
|
||||
];
|
||||
const options = {
|
||||
route,
|
||||
initialPosition: route[0],
|
||||
initialAltitudeM: 1_200,
|
||||
initialHeadingDeg: 45,
|
||||
initialSpeedMps: 55,
|
||||
fixedStepSeconds: 0.1,
|
||||
} as const;
|
||||
const first = new AircraftController(options);
|
||||
const replay = new AircraftController(options);
|
||||
let priorIndex = first.state().routeWaypointIndex;
|
||||
let waypointTransitions = 0;
|
||||
let routeCompletions = 0;
|
||||
|
||||
for (let step = 0; step < HOUR_AT_TEN_HZ; step += 1) {
|
||||
first.stepFixed();
|
||||
replay.stepFixed();
|
||||
const state = first.state();
|
||||
if (state.routeWaypointIndex !== priorIndex) {
|
||||
waypointTransitions += 1;
|
||||
if (priorIndex === route.length - 1 && state.routeWaypointIndex === 0) routeCompletions += 1;
|
||||
priorIndex = state.routeWaypointIndex;
|
||||
}
|
||||
if (step % 120 !== 0) continue;
|
||||
finiteNumbers(state);
|
||||
inside(state.lat, 32.4, 42.1, "aircraft.lat");
|
||||
inside(state.lng, -124.6, -114, "aircraft.lng");
|
||||
inside(state.altitudeM, 75, 6_000, "aircraft.altitudeM");
|
||||
inside(state.speedMps, 20, 95, "aircraft.speedMps");
|
||||
inside(state.headingDeg, 0, 360, "aircraft.headingDeg");
|
||||
}
|
||||
assert.ok(waypointTransitions >= 6, `aircraft made only ${waypointTransitions} waypoint transitions`);
|
||||
assert.ok(routeCompletions >= 2, `aircraft completed only ${routeCompletions} circuits`);
|
||||
assert.equal(first.state().elapsedSteps, HOUR_AT_TEN_HZ);
|
||||
assert.deepEqual(first.snapshot(), replay.snapshot());
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user