Files
gods-eye-view/src/layers/transit/lifecycle.test.mjs
T
2026-10-23 12:12:27 +01:00

3985 lines
127 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import * as Cesium from 'cesium';
import { createTransitLayer } from './index.js';
import {
FEED_EVICT_AFTER_MS,
FEED_STALE_AFTER_MS,
MISSED_POLLS_TO_DROP,
TRANSIT_POLL_MS,
VEHICLE_MAX_FIX_AGE_MS,
} from './policy.js';
import { FLOOR_WARM_PER_POLL } from './policy.js';
import {
FUTURE_STAMP_TOLERANCE_MS,
ORDER_REJECTS_BEFORE_RESET,
} from './ingestion.js';
/**
* Fixes in these tests are AGE_MS old when they arrive, which makes the lag
* the feed earns equal to the lag a fifteen-second reporter earns on its own
* — one number, `LAG_MS`, and no slew — so the timelines below are exact.
*/
const AGE_MS = 15_000;
const LAG_MS = AGE_MS + TRANSIT_POLL_MS + 5_000;
/** A fix stamped AGE_MS before now, the way a live feed delivers them. */
const reported = () => (Date.now() - AGE_MS) / 1000;
const lat = (entry) =>
Cesium.Math.toDegrees(
Cesium.Cartographic.fromCartesian(entry.marker.position).latitude,
);
import {
cameraSensitivityClaims,
claimCameraSensitivity,
releaseCameraSensitivity,
} from '../../data/cameraSensitivity.js';
import {
_resetRenderGovernorForTest,
getRenderGovernorDiagnostics,
governorRequestRender,
holdContinuousRender,
installRenderGovernor,
releaseContinuousRender,
uninstallRenderGovernor,
} from '../../renderGovernor.js';
const BOSTON = { lat: 42.3601, lon: -71.0589 };
/** One decoded snapshot in the shape `/api/transit/vehicles/<id>` returns. */
function snapshot(
feedId,
name,
vehicles,
{ fetchedAt, feedTimestamp = null } = {},
) {
return {
feedId,
name,
fetchedAt,
feedTimestamp,
version: '2.0',
entityCount: vehicles.length,
truncated: false,
count: vehicles.length,
vehicles,
};
}
/** A vehicle record as the proxy emits it, with its timestamp source resolved. */
function vehicle(id, lat, lon, atSeconds, extra = {}) {
return {
id,
lat,
lon,
bearing: null,
speedMps: null,
timestamp: atSeconds,
timestampSource: 'vehicle',
routeId: '1',
tripId: null,
directionId: null,
label: null,
stopId: null,
status: null,
occupancy: null,
...extra,
};
}
/**
* A Transit layer wired to a stub scene and a scripted proxy.
*
* The render governor is the real one, so "does this layer keep the render loop
* awake?" is answered by the same code the app runs.
*/
/**
* A stand-in for the app's shared ground-floor system: warm cells, the coarse
* grid, and a lowest-neighbour read. `floorAt` decides what a cell resolves to
* when it is warmed; returning undefined models terrain that will not answer.
* @param {(lat: number, lon: number) => number|undefined} floorAt
*/
function groundStub(floorAt) {
const warm = new Map();
const warmed = [];
const coarseFloorCoord = (lat, lon) => ({
lat: Number(lat.toFixed(3)),
lon: Number(lon.toFixed(3)),
});
const key = (c) => `${c.lat},${c.lon}`;
const cachedGroundFloor = (lat, lon) => {
const value = warm.get(key(coarseFloorCoord(lat, lon)));
return value === undefined ? null : value;
};
return {
GROUND_FLOOR_LIFT_M: 1.5,
coarseFloorCoord,
cachedGroundFloor,
neighborFloorM(cell) {
let lowest = null;
let resolved = 0;
for (let dLat = -1; dLat <= 1; dLat += 1) {
for (let dLon = -1; dLon <= 1; dLon += 1) {
if (dLat === 0 && dLon === 0) continue;
const h = cachedGroundFloor(
cell.lat + dLat * 0.001,
cell.lon + dLon * 0.001,
);
if (h === null) continue;
resolved += 1;
if (lowest === null || h < lowest) lowest = h;
}
}
return resolved >= 3 ? lowest : null;
},
warmGroundFloor(points) {
warmed.push(...points);
for (const point of points) {
const value = floorAt(point.lat, point.lon);
if (Number.isFinite(value)) {
warm.set(key(coarseFloorCoord(point.lat, point.lon)), value);
}
}
},
_warm: warm,
_warmed: warmed,
_key: (lat, lon) => key(coarseFloorCoord(lat, lon)),
};
}
function harness(
t,
{
source,
altitude = 4_000,
floorAt = () => 12,
at = BOSTON,
view = {
west: at.lon - 0.1,
south: at.lat - 0.1,
east: at.lon + 0.1,
north: at.lat + 0.1,
},
} = {},
) {
const viewRect = { ...view };
t.mock.timers.enable({
apis: ['Date', 'setTimeout'],
now: 1_789_000_000_000,
});
for (const name of [
'HTMLCanvasElement',
'HTMLImageElement',
'ImageBitmap',
'OffscreenCanvas',
]) {
const prior = globalThis[name];
globalThis[name] = class {};
t.after(() => {
if (prior === undefined) delete globalThis[name];
else globalThis[name] = prior;
});
}
t.mock.method(performance, 'now', () => Date.now() - 1_789_000_000_000);
const originalFetch = globalThis.fetch;
const originalDocument = globalThis.document;
const primitives = [];
const sprites = new Map();
const pickOwners = new Map();
const overlaySources = new Map();
const credits = [];
/** @type {Map<string, (url: string) => {status:number, body?:object, headers?:object}>} */
const responders = new Map();
const requested = [];
globalThis.document = Object.assign(new EventTarget(), {
visibilityState: 'visible',
documentElement: { dataset: {} },
});
// The style and vision events arrive on `window`, which Node does not have.
const originalWindow = globalThis.window;
globalThis.window = new EventTarget();
const preRender = new Cesium.Event();
const cameraChanged = new Cesium.Event();
const viewer = {
camera: {
changed: cameraChanged,
percentageChanged: 0.5,
positionCartographic: {
height: altitude,
latitude: Cesium.Math.toRadians(at.lat),
longitude: Cesium.Math.toRadians(at.lon),
},
computeViewRectangle() {
return Cesium.Rectangle.fromDegrees(
viewRect.west,
viewRect.south,
viewRect.east,
viewRect.north,
);
},
},
scene: {
canvas: { addEventListener() {}, removeEventListener() {} },
globe: { ellipsoid: Cesium.Ellipsoid.WGS84 },
pick: () => undefined,
requestRenderMode: false,
maximumRenderTimeChange: 0,
requestRender() {},
preRender,
primitives: {
add(primitive) {
primitives.push(primitive);
return primitive;
},
remove(primitive) {
const index = primitives.indexOf(primitive);
if (index >= 0) primitives.splice(index, 1);
return index >= 0;
},
contains: (primitive) => primitives.includes(primitive),
raiseToTop() {},
},
},
};
globalThis.fetch = async (url) => {
requested.push(String(url));
const feedId = String(url).split('/').pop();
const responder = responders.get(feedId);
const answer = responder
? responder(String(url))
: { status: 504, body: { error: 'Transit feed unavailable' } };
return {
ok: answer.status >= 200 && answer.status < 300,
status: answer.status,
headers: { get: (name) => answer.headers?.[name.toLowerCase()] ?? null },
json: async () => answer.body,
};
};
const ground = groundStub(floorAt);
/** Every call the floor cycle makes to the rendered-mesh sampler. */
const meshCalls = [];
const layer = createTransitLayer({
source,
services: {
ground,
mesh: {
sampleMeshFloorCells(scene, points, options) {
meshCalls.push({ scene, points: [...points], options });
},
},
render: {
governorRequestRender,
holdContinuousRender,
releaseContinuousRender,
},
sprites: {
registerSpriteCollection: (id, collection) =>
sprites.set(id, collection),
unregisterSpriteCollection: (id) => sprites.delete(id),
restoreSpriteOrder() {},
},
picking: {
registerPickOwner: (id, predicate) => pickOwners.set(id, predicate),
unregisterPickOwner: (id) => pickOwners.delete(id),
},
overlays: {
setOverlayEntries: (id, entries) => overlaySources.set(id, entries),
setOverlaySourceVisible() {},
clearOverlaySource: (id) => overlaySources.delete(id),
},
credits: {
registerDynamicCredit: (_viewer, credit) => credits.push(credit?.key),
transitFeedCredit: (feed) => ({
key: `transit-${feed.id}`,
html: feed.name,
}),
},
},
});
installRenderGovernor(viewer);
layer.init(viewer);
t.after(() => {
try {
layer.destroy(viewer);
} catch {
/* a test may already have destroyed it */
}
uninstallRenderGovernor(viewer);
_resetRenderGovernorForTest();
globalThis.fetch = originalFetch;
if (originalDocument === undefined) delete globalThis.document;
else globalThis.document = originalDocument;
if (originalWindow === undefined) delete globalThis.window;
else globalThis.window = originalWindow;
});
return {
layer,
viewer,
primitives,
sprites,
pickOwners,
overlaySources,
credits,
requested,
serve(feedId, responder) {
responders.set(feedId, responder);
},
silence(feedId) {
responders.delete(feedId);
},
state: () => layer._transitStateForTest(),
vehicles: () => [...layer._transitStateForTest()._vehicles.values()],
frame() {
preRender.raiseEvent();
},
advance(ms) {
t.mock.timers.tick(ms);
},
/**
* Let wall time pass the way it does in the app: with frames along the
* way. The display clock only advances on frames, so a test that moves
* the clock without them models a tab in the background, not a viewer.
* @param {number} ms
* @param {number} [step]
*/
run(ms, step = 1_000) {
const until = Date.now() + ms;
while (Date.now() + step <= until) {
t.mock.timers.tick(step);
preRender.raiseEvent();
}
if (Date.now() < until) {
t.mock.timers.tick(until - Date.now());
preRender.raiseEvent();
}
},
holds: () => getRenderGovernorDiagnostics().holds,
ground,
meshCalls,
/**
* Run the floor cycle the way its timer would, then a frame. Ground
* resolution is a network round trip in the real app and deliberately
* lives off the render path, so a vehicle with no floor yet is not drawn
* until a re-read picks one up.
* @param {number} [rounds]
*/
settle(rounds = 4) {
for (let i = 0; i < rounds; i += 1) {
const parts = layer._transitPartsForTest();
parts.height.rereadFloors();
// The shown/hidden sweep is throttled in the app; running it here
// keeps the clock out of it, so a test about glide timing is not also
// a test about how long a sweep waits.
parts.rendering.refreshVisibility();
t.mock.timers.tick(16);
preRender.raiseEvent();
}
},
/** What the style manager dispatches when the user picks a preset. */
style(name) {
globalThis.window.dispatchEvent(
new CustomEvent('gev:style-change', { detail: { style: name } }),
);
},
/** What the style manager dispatches after every sync, cockpit included. */
vision(style, cockpit) {
globalThis.window.dispatchEvent(
new CustomEvent('gev:vision-change', { detail: { style, cockpit } }),
);
},
/** Move the camera's view rectangle, the way a real camera move would. */
lookAt(next) {
Object.assign(viewRect, next);
cameraChanged.raiseEvent();
},
};
}
test('a vehicle is played back a fixed lag behind its fixes, at 1x', async (t) => {
const app = harness(t);
const start = Date.now();
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, reported())],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
const [entry] = app.vehicles();
assert.equal(entry.to.lat, 42.36);
assert.equal(
entry.sample.fromSeq,
entry.sample.toSeq,
'a brand-new vehicle holds at its only fix',
);
assert.equal(app.state()._moving.has(entry), false);
assert.ok(
Math.abs(Date.now() - entry.playT - LAG_MS) < 1,
'its display clock starts a full lag behind',
);
// A later poll gives it a new fix. Nothing is drawn yet: the display clock
// has not reached the first fix.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.362, -71.06, reported())],
{
fetchedAt: Date.now(),
},
),
}));
await app.layer.update();
assert.ok(
entry.wakeTimer != null,
'one wake is scheduled at the first motion boundary',
);
app.frame();
assert.ok(Math.abs(lat(entry) - 42.36) < 1e-6, 'still at the first fix');
// The clock reaches the first fix LAG_MS after the wall time it was
// reported at; halfway along the fifteen seconds to the second fix the
// vehicle is drawn halfway between them.
const firstFixAt = start - AGE_MS;
app.run(firstFixAt + LAG_MS + TRANSIT_POLL_MS / 2 - Date.now());
assert.ok(Math.abs(lat(entry) - 42.361) < 1e-4, `halfway, got ${lat(entry)}`);
// Past the second fix it holds exactly there and stops being animated.
app.run(TRANSIT_POLL_MS);
assert.ok(Math.abs(lat(entry) - 42.362) < 1e-6);
assert.equal(app.state()._moving.has(entry), false, 'held, not animated');
});
test('a vehicle the feed stops reporting is removed, and its primitive with it', async (t) => {
const app = harness(t);
const now = Date.now();
const both = [
vehicle('bus-1', 42.36, -71.06, now / 1000),
vehicle('bus-2', 42.37, -71.07, now / 1000),
];
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', both, { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.vehicles().length, 2);
const collection = app.sprites.get('transit');
assert.equal(collection.length, 2);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.361, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
// One missed poll is tolerated (feeds drop a vehicle for a beat), two is not.
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
assert.equal(app.vehicles().length, 2);
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
assert.deepEqual(
app.vehicles().map((entry) => entry.record.id),
['bus-1'],
);
assert.equal(
collection.length + app.sprites.get('transit-motion').length,
1,
'the removed vehicle released its point primitive',
);
});
test('one feed stale beside one fresh reads DEGRADED, not nominal', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
const state = app.state();
// Pretend a second feed is also in range and is being served from the proxy's
// stale cache — exactly the mixed case the old `every(stale)` call missed.
state._activeFeeds.set('ovapi-nl', {
id: 'ovapi-nl',
name: 'OVapi',
region: 'Netherlands',
});
state._feedStatus.set('ovapi-nl', {
count: 4,
lastUpdate: Date.now(),
error: null,
stale: true,
pollSeq: 1,
loading: false,
});
const mixed = app.layer.getStats();
assert.equal(mixed.stale, false, 'not everything is stale');
assert.equal(mixed.degraded, true, 'but the view is part live and part not');
// When every feed is stale the row says STALE outright.
state._feedStatus.get('mbta').stale = true;
const allStale = app.layer.getStats();
assert.equal(allStale.stale, true);
});
test('a feed that goes silent has its vehicles evicted instead of frozen on the globe', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.vehicles().length, 1);
// The proxy's stale window expires and the feed stops answering at all.
app.silence('mbta');
app.advance(FEED_EVICT_AFTER_MS + 1_000);
await app.layer.update();
assert.equal(app.vehicles().length, 0, 'the last fleet is not left standing');
assert.equal(app.sprites.get('transit').length, 0);
const stats = app.layer.getStats();
assert.equal(stats.count, 0);
assert.equal(stats.stale, true);
});
test('inside a fresh snapshot, only the vehicle fixes that are too old are dropped', async (t) => {
const app = harness(t);
const now = Date.now();
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('fresh', 42.36, -71.06, Date.now() / 1000),
// A live feed really does carry these: a vehicle parked since this
// morning, still listed, still reporting its last known position.
vehicle(
'ancient',
42.37,
-71.07,
(Date.now() - 4 * 60 * 60 * 1000) / 1000,
),
// A clock ahead of ours is treated as current, never as negative age.
vehicle('future', 42.38, -71.08, (Date.now() + 60_000) / 1000),
],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.deepEqual(
app
.vehicles()
.map((entry) => entry.record.id)
.sort(),
['fresh', 'future'],
);
// The same vehicles, unrefreshed, age out of the layer on a later sweep.
app.silence('mbta');
app.advance(VEHICLE_MAX_FIX_AGE_MS + 1_000);
await app.layer.update();
assert.equal(app.vehicles().length, 0);
assert.equal(now <= Date.now(), true);
});
test('a snapshot the proxy replayed from its own cache for six minutes is refused, not drawn', async (t) => {
const app = harness(t);
const fetchedAt = Date.now();
const vehicles = [
vehicle('bus-1', 42.36, -71.06, fetchedAt / 1000),
vehicle('bus-2', 42.37, -71.07, fetchedAt / 1000),
];
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', vehicles, { fetchedAt }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.vehicles().length, 2);
// The operator stops answering. The proxy keeps handing back the SAME
// snapshot every 15 s with STALE-ERROR; receiving bytes on a fresh cadence is
// not the same as the buses being where they say.
app.serve('mbta', () => ({
status: 200,
headers: { 'x-gev-cache': 'STALE-ERROR' },
body: snapshot('mbta', 'MBTA', vehicles, { fetchedAt }),
}));
for (let tick = 0; tick < 24; tick += 1) {
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
}
assert.ok(
Date.now() - fetchedAt > FEED_EVICT_AFTER_MS,
'six minutes have passed',
);
assert.equal(
app.vehicles().length,
0,
'the replayed fleet is gone, not frozen',
);
assert.equal(app.sprites.get('transit').length, 0);
const stats = app.layer.getStats();
assert.equal(stats.count, 0);
assert.equal(stats.stale, true);
assert.match(stats.coverage, /\(\d+s old\)/);
assert.equal(
app.holds().includes('transit'),
false,
'and it holds no frames',
);
});
test('disable aborts in-flight work and a response that lands late is ignored', async (t) => {
const app = harness(t);
let release;
const pending = new Promise((resolve) => {
release = resolve;
});
for (const name of [
'HTMLCanvasElement',
'HTMLImageElement',
'ImageBitmap',
'OffscreenCanvas',
]) {
const prior = globalThis[name];
globalThis[name] = class {};
t.after(() => {
if (prior === undefined) delete globalThis[name];
else globalThis[name] = prior;
});
}
t.mock.method(performance, 'now', () => Date.now() - 1_789_000_000_000);
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url) => {
await pending;
return originalFetch(url);
};
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
const inFlight = app.layer.update();
app.layer.disable(app.viewer);
release();
await inFlight;
assert.equal(
app.vehicles().length,
0,
'a poll from the disabled generation never lands',
);
assert.equal(app.state()._inFlight.size, 0);
assert.equal(app.holds().includes('transit'), false);
// Re-enabling starts a clean generation and does take data.
globalThis.fetch = originalFetch;
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.vehicles().length, 1);
});
test('a vehicle waits for its floor rather than standing on the ellipsoid', async (t) => {
// The shared ground-floor system answers from the rendered mesh cell or the
// real DEM cell; until one of them is warm there is no honest height to draw
// at. Ellipsoid zero is not street level, and drawing there and dropping
// afterwards is exactly the floating the owner reported.
let terrainReady = false;
const app = harness(t, { floorAt: () => (terrainReady ? 47 : undefined) });
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
const entry = app.vehicles()[0];
assert.equal(entry.heightResolved, false);
app.settle();
assert.equal(entry.heightPending, true, 'it has nothing to stand on');
assert.equal(entry.marker.show, false, 'so it is not drawn');
// Terrain arrives; the next re-read picks the cell up and the vehicle
// appears, standing on it.
terrainReady = true;
app.settle();
assert.equal(entry.heightResolved, true);
assert.equal(entry.heightM, 47 + 1.5, 'the floor plus the shared lift');
assert.equal(entry.marker.show, true);
// Driving into a new cell asks that cell rather than keeping the one it was
// born in.
app.advance(TRANSIT_POLL_MS);
const cellBefore = entry.heightCell;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.362, -71.06, Date.now() / 1000)],
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
assert.notEqual(app.vehicles()[0].heightCell, cellBefore);
});
test('the render hold follows real motion, and is released when everything settles', async (t) => {
const app = harness(t);
const start = Date.now();
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, reported())],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
// The first poll draws each vehicle at its fix; there is nothing to play,
// and ground resolution runs off the render path, so the loop is never held.
assert.equal(
app.holds().includes('transit'),
false,
'a fleet that is not moving does not hold the loop',
);
app.frame();
assert.equal(app.holds().includes('transit'), false);
assert.equal(app.viewer.scene.requestRenderMode, true);
// A second fix IS motion to come: the vehicle is armed and the loop held.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.362, -71.06, reported())],
{
fetchedAt: Date.now(),
},
),
}));
await app.layer.update();
const firstFixAt = start - AGE_MS;
app.run(firstFixAt + LAG_MS + TRANSIT_POLL_MS / 2 - Date.now());
assert.ok(
app.holds().includes('transit'),
'a moving vehicle keeps the loop awake',
);
assert.equal(app.viewer.scene.requestRenderMode, false);
// Past the second fix it holds and the loop is released again.
app.run(TRANSIT_POLL_MS);
assert.equal(
app.holds().includes('transit'),
false,
'a settled fleet does not hold the GPU',
);
assert.equal(app.viewer.scene.requestRenderMode, true);
});
test('disable hands back the camera sensitivity it borrowed', async (t) => {
const app = harness(t);
assert.equal(app.viewer.camera.percentageChanged, 0.5);
app.layer.enable(app.viewer);
assert.equal(
app.viewer.camera.percentageChanged,
0.05,
'the layer needs a finer camera trigger',
);
app.layer.disable(app.viewer);
assert.equal(app.viewer.camera.percentageChanged, 0.5, 'and gives it back');
});
test('disable leaves the fine camera trigger another layer asked for', async (t) => {
// The sequence that broke it: two layers ask for the SAME sensitivity, so
// the value on the camera cannot say who owns it. Transit used to compare
// the number, find its own 0.05 still there, and hand the coarse default
// back while Bikeshare was still driving off the fine one.
const app = harness(t);
assert.equal(app.viewer.camera.percentageChanged, 0.5);
app.layer.enable(app.viewer);
assert.equal(app.viewer.camera.percentageChanged, 0.05);
claimCameraSensitivity(app.viewer.camera, 'bikeshare', 0.05);
app.layer.disable(app.viewer);
assert.equal(
app.viewer.camera.percentageChanged,
0.05,
'bikeshare is still enabled, so the fine trigger stays',
);
assert.deepEqual(cameraSensitivityClaims(app.viewer.camera), ['bikeshare']);
releaseCameraSensitivity(app.viewer.camera, 'bikeshare');
assert.equal(
app.viewer.camera.percentageChanged,
0.5,
'and the original returns once the last claim is gone',
);
assert.deepEqual(cameraSensitivityClaims(app.viewer.camera), []);
});
test('a finer claim wins while it stands, and the coarser one survives it', async (t) => {
const app = harness(t);
app.layer.enable(app.viewer);
claimCameraSensitivity(app.viewer.camera, 'traffic', 0.01);
assert.equal(app.viewer.camera.percentageChanged, 0.01);
releaseCameraSensitivity(app.viewer.camera, 'traffic');
assert.equal(
app.viewer.camera.percentageChanged,
0.05,
'transit still needs its own trigger',
);
app.layer.disable(app.viewer);
assert.equal(app.viewer.camera.percentageChanged, 0.5);
});
test('destroy releases every shared registration this layer took', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.ok(app.sprites.has('transit'));
assert.ok(app.pickOwners.has('transit'));
assert.equal(app.primitives.length, 2);
app.layer.destroy(app.viewer);
assert.equal(
app.sprites.has('transit'),
false,
'the sprite collection is unregistered',
);
assert.equal(
app.pickOwners.has('transit'),
false,
'the pick owner is released',
);
assert.equal(
app.primitives.length,
0,
'the primitive collection leaves the scene',
);
assert.equal(app.holds().includes('transit'), false);
assert.equal(app.state()._viewer, null);
assert.equal(app.state()._dataManager, null);
});
test('two layers built from the factory share no state', async (t) => {
const app = harness(t);
const second = createTransitLayer({
services: {
ground: groundStub(() => 12),
render: {
governorRequestRender,
holdContinuousRender,
releaseContinuousRender,
},
sprites: {
registerSpriteCollection() {},
unregisterSpriteCollection() {},
restoreSpriteOrder() {},
},
picking: { registerPickOwner() {}, unregisterPickOwner() {} },
overlays: {
setOverlayEntries() {},
setOverlaySourceVisible() {},
clearOverlaySource() {},
},
credits: {
registerDynamicCredit() {},
transitFeedCredit: (feed) => ({ key: feed.id, html: feed.name }),
},
},
});
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.vehicles().length, 1);
assert.equal(second._transitStateForTest()._vehicles.size, 0);
assert.notEqual(second._transitStateForTest(), app.state());
assert.equal(second.getStats().count, 0);
});
test('a proxy cooldown reaches the row as a time, not just a shrug', async (t) => {
const app = harness(t);
// The proxy answers 503 with how long it intends to leave the operator alone.
app.serve('mbta', () => ({
status: 503,
body: { error: 'Transit feed unavailable', feedId: 'mbta', retryInSec: 60 },
}));
app.layer.enable(app.viewer);
await app.layer.update();
const stats = app.layer.getStats();
assert.equal(stats.count, 0);
assert.match(String(stats.error), /MBTA/);
assert.equal(stats.retryInSec, 60);
// A successful poll clears both the error and the countdown.
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
const recovered = app.layer.getStats();
assert.equal(recovered.error, null);
assert.equal(recovered.retryInSec, undefined);
});
test('the coverage text ages a quiet feed by the same rule the row state uses', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, Date.now() / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.layer.getStats().coverage, 'MBTA 1');
// Past the stale threshold the text names the age, and the row agrees.
app.silence('mbta');
app.advance(FEED_STALE_AFTER_MS + 10_000);
const stale = app.layer.getStats();
assert.match(stale.coverage, /^MBTA 1 \(\d+s old\)$/);
assert.equal(stale.stale, true);
});
test('a parked fleet that keeps reporting the same position stops asking for frames', async (t) => {
const app = harness(t);
// A depot at night: the feed answers on time, every 15 s, with coordinates
// that do not change. Each of those is a real observation — the bus is
// still there — but there is nothing to draw, and drawing it anyway kept
// the render loop awake all night.
const parked = () => [
vehicle('bus-1', 42.36, -71.06, reported()),
vehicle('bus-2', 42.37, -71.07, reported()),
];
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', parked(), { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.frame();
assert.equal(
app.holds().includes('transit'),
false,
'nothing moved on the first poll',
);
for (let poll = 0; poll < 4; poll += 1) {
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
app.frame();
assert.equal(
app.holds().includes('transit'),
false,
`identical poll ${poll + 1} must not count as motion`,
);
assert.equal(app.state()._moving.size, 0);
}
assert.equal(app.viewer.scene.requestRenderMode, true);
assert.equal(app.vehicles().length, 2, 'and the fleet is still on screen');
// One vehicle finally moves: that IS work, and the hold comes back.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('bus-1', 42.3605, -71.06, reported()),
vehicle('bus-2', 42.37, -71.07, reported()),
],
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
assert.equal(
app.holds().includes('transit'),
false,
'departure remains buffered until its boundary',
);
app.advance(6000);
app.frame();
assert.ok(
app.holds().includes('transit'),
'departure wakes the loop at its boundary',
);
app.advance(TRANSIT_POLL_MS / 2);
app.frame();
assert.ok(app.holds().includes('transit'));
assert.equal(
app.state()._moving.size,
1,
'only the vehicle that actually moved is animating',
);
});
test('the last vehicle leaving mid-segment takes the render hold with it', async (t) => {
const app = harness(t);
const fetchedAt = Date.now();
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.36, -71.06, reported())],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
// Second poll: the vehicle has somewhere to go, so the hold is taken.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus-1', 42.362, -71.06, reported())],
{
fetchedAt: Date.now(),
},
),
}));
await app.layer.update();
app.advance(TRANSIT_POLL_MS / 3);
app.frame();
assert.ok(app.holds().includes('transit'), 'in motion, so the loop is held');
assert.equal(app.state()._moving.has(app.vehicles()[0]), true);
// The feed dies. The sweep removes the last vehicle while it is still
// in motion, and the poll that discovers this fails — so no frame follows to
// notice the fleet is gone. The hold has to be released by the eviction.
app.silence('mbta');
app.advance(FEED_EVICT_AFTER_MS + 1_000);
await app.layer.update();
assert.equal(app.vehicles().length, 0);
assert.equal(
app.holds().includes('transit'),
false,
'no hold may outlive the fleet that justified it',
);
assert.equal(app.viewer.scene.requestRenderMode, true);
assert.ok(Date.now() - fetchedAt > FEED_EVICT_AFTER_MS);
});
test('only the vehicles near the view are animated frame by frame', async (t) => {
const app = harness(t);
const near = vehicle('near', 42.36, -71.06, reported());
// Far enough away to be outside any plausible inflation of the harness view
// rectangle, but well inside the feed's coverage circle.
const far = vehicle('far', 42.62, -71.44, reported());
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [near, far], { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.frame();
// Both move on the next poll; only the one in view is armed at all.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('near', 42.3625, -71.06, reported()),
vehicle('far', 42.6225, -71.44, reported()),
],
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
const byId = new Map(app.vehicles().map((entry) => [entry.record.id, entry]));
app.advance(6000);
app.frame();
assert.equal(
app.state()._moving.size,
1,
'a poll arms only the vehicle in view',
);
assert.ok(app.state()._moving.has(byId.get('near')));
assert.equal(
byId.get('far').marker.show,
false,
'the off-screen vehicle is hidden',
);
app.advance(TRANSIT_POLL_MS / 4);
app.frame();
assert.equal(
app.state()._moving.has(byId.get('far')),
false,
'and is never animated',
);
assert.ok(
app.state()._moving.has(byId.get('near')),
'the one in view keeps playing',
);
// When the view reaches it, its clock is re-synced and the frame pass places
// it: nothing is owed for motion nobody watched.
app.lookAt({ west: -71.54, south: 42.52, east: -71.34, north: 42.72 });
// Two floor passes: it was never warmed while hidden, and a warmed cell
// answers on the pass after the one that asked.
app.settle(2);
app.advance(250);
app.frame();
assert.notEqual(byId.get('far').marker.show, false, 'now shown');
assert.ok(
Math.abs(lat(byId.get('far')) - 42.62) < 1e-3,
'placed on its history',
);
});
test('a wide oblique view does not hide the fleet that is plainly in it', async (t) => {
// Codex's reproduction: a high oblique camera over Europe sees roughly
// -78°..88° of longitude. Padding each endpoint on its own wrapped that span
// into a five-degree slice of the Pacific, so every Dutch vehicle failed the
// view gate — hidden, unanimated, and invisible to DETECT — while filling
// the screen.
const app = harness(t, {
at: { lat: 52.09, lon: 5.12 },
altitude: 1_500_000,
view: { west: -78.084, south: 20, east: 88.084, north: 84 },
});
const now = Date.now();
const utrecht = vehicle('nl-1', 52.09, 5.12, now / 1000);
const rotterdam = vehicle('nl-2', 51.92, 4.48, now / 1000);
app.serve('ovapi-nl', () => ({
status: 200,
body: snapshot('ovapi-nl', 'OVapi', [utrecht, rotterdam], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.advance(250);
app.frame();
const shown = app.vehicles().filter((entry) => entry.marker?.show !== false);
assert.equal(shown.length, 2, 'both vehicles are in the view the camera has');
assert.equal(
app.layer.getDetectableObjects({ maxCount: 50, seed: 0 }).length,
2,
'and DETECT can see them',
);
});
test('a lone visible vehicle is offered to DETECT out of a large hidden fleet', async (t) => {
// The stride used to run over the WHOLE fleet and discard the hidden ones
// afterwards, so one visible bus among thousands could fall outside the
// sample and go unlabelled while the overlay had capacity to spare.
const app = harness(t);
const now = Date.now();
const vehicles = [];
for (let i = 0; i < 400; i += 1) {
// Far outside the harness view rectangle, inside MBTA coverage.
vehicles.push(vehicle(`away-${i}`, 42.6 + i * 0.001, -71.45, now / 1000));
// Deliberately NOT first in the map: an ordinal stride happens to pick
// index zero, which would hide the fault rather than show it.
if (i === 0) vehicles.push(vehicle('visible', 42.361, -71.058, now / 1000));
}
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', vehicles, { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const contacts = app.layer.getDetectableObjects({ maxCount: 8, seed: 0 });
assert.equal(contacts.length, 1, 'only the visible one is a candidate');
assert.equal(contacts[0].sourceId, 'mbta:visible');
});
test('removing one vehicle does not hand every label to its neighbour', async (t) => {
// Striding by position meant that one bus leaving at the front renumbered
// everyone behind it: the sampled set flipped from even indices to odd, and
// not one of the previous identities survived.
const app = harness(t, {
view: { west: -71.3, south: 42.2, east: -70.8, north: 42.5 },
});
const now = Date.now();
const fleet = [];
for (let i = 0; i < 40; i += 1) {
fleet.push(vehicle(`bus-${i}`, 42.35 + i * 0.001, -71.06, now / 1000));
}
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', fleet, { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const before = app.layer
.getDetectableObjects({ maxCount: 10, seed: 0 })
.map((contact) => contact.sourceId);
assert.equal(before.length, 10);
// The first vehicle stops being reported; everyone else is unchanged. It
// takes MISSED_POLLS_TO_DROP polls of silence for it to actually leave.
for (let poll = 0; poll < MISSED_POLLS_TO_DROP + 1; poll += 1) {
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
fleet.slice(1).map((v) => ({ ...v, timestamp: Date.now() / 1000 })),
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
app.frame();
}
assert.equal(
app.vehicles().some((entry) => entry.record.id === 'bus-0'),
false,
'the vehicle really did leave',
);
const after = new Set(
app.layer
.getDetectableObjects({ maxCount: 10, seed: 0 })
.map((contact) => contact.sourceId),
);
const survived = before.filter(
(id) => id !== 'mbta:bus-0' && after.has(id),
).length;
const expected = before.filter((id) => id !== 'mbta:bus-0').length;
assert.equal(
survived,
expected,
'every still-present labelled vehicle keeps its label',
);
});
test('a vehicle inside the view drives out of it instead of vanishing', async (t) => {
// A fix placing the DESTINATION just past the padded edge used to hide the
// marker on the spot — while the vehicle was still drawn well inside the
// view — and throw its glide away in the same frame.
const app = harness(t, {
view: { west: -71.062, south: 42.3585, east: -71.058, north: 42.3605 },
});
const now = Date.now();
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.36, -71.06, now / 1000)], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
assert.notEqual(entry.marker.show, false, 'it starts on screen');
// Next fix is beyond the inflated rectangle; the vehicle is still drawn
// inside it.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('b', 42.3625, -71.06, Date.now() / 1000)],
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
app.frame();
assert.notEqual(
entry.marker.show,
false,
'it is still on screen, because it still is',
);
assert.ok(
entry.wakeTimer != null || app.state()._moving.has(entry),
'and it has a departure wake rather than teleporting',
);
});
test('the selected card lands where the vehicle stopped, not short of it', async (t) => {
// The card is refreshed on a 250 ms throttle. The last tick before a
// segment ends left it a few metres behind, and then the render hold was
// released with no frame scheduled to close the gap.
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.36, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
// Its floor resolves, so the marker is shown: a hidden vehicle has no card.
app.settle();
const entry = app.vehicles()[0];
app.layer._transitPartsForTest().selection.selectVehicle(entry.key);
app.advance(TRANSIT_POLL_MS);
// Stamped exactly one poll after the first fix, so the vehicle's own lag
// equals the feed's and the clock runs at exactly 1x through the segment.
const secondFixS = (entry.fixes[0].t + TRANSIT_POLL_MS) / 1000;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('b', 42.3625, -71.0625, secondFixS)],
{
fetchedAt: Date.now(),
},
),
}));
await app.layer.update();
// Codex's sequence: a card refresh 200 ms before the segment completes, then
// the settling frame — which falls INSIDE the 250 ms card throttle, so
// nothing but a forced refresh can close the gap.
// The segment ends when the clock reaches the second fix, a lag after it.
const segmentEndsAt = entry.fixes[1].t + LAG_MS;
app.run(segmentEndsAt - 200 - Date.now());
// The settling frame — whichever frame that turns out to be — falls INSIDE
// the card throttle, so only the forced refresh can close the gap.
let frames = 0;
while (app.state()._moving.has(entry) && frames < 60) {
app.advance(16);
app.frame();
frames += 1;
}
assert.equal(app.state()._moving.has(entry), false, 'settled');
assert.equal(
app.state()._selectedCardAt,
Date.now(),
'the settling frame forced a card refresh, throttle or no throttle',
);
const card = app.overlaySources.get('transit-selected')?.[0];
assert.ok(card, 'the card is still on screen');
const anchor =
typeof card.position === 'function' ? card.position() : card.position;
const drift = Cesium.Cartesian3.distance(anchor, entry.marker.position);
assert.ok(
drift < 0.5,
`the card sits on the vehicle it follows (was ${drift.toFixed(2)} m behind)`,
);
});
test('a parked fleet over unsampled ground off screen stops asking for frames', async (t) => {
// 200 stationary vehicles in distinct off-screen cells whose terrain never
// resolves used to keep the height queue non-empty for ever, and a non-empty
// queue is a held render loop over a view with nothing moving in it.
const app = harness(t, { floorAt: () => undefined });
const now = Date.now();
const parked = [];
for (let i = 0; i < 200; i += 1) {
parked.push(vehicle(`p-${i}`, 42.5 + i * 0.003, -71.4, now / 1000));
}
const serve = () => ({
status: 200,
body: snapshot('mbta', 'MBTA', parked, { fetchedAt: Date.now() }),
});
app.serve('mbta', serve);
app.layer.enable(app.viewer);
await app.layer.update();
app.frame();
// Eight polls — two minutes — with nothing moving and nothing in view.
for (let poll = 0; poll < 8; poll += 1) {
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', serve);
await app.layer.update();
for (let f = 0; f < 4; f += 1) {
app.advance(100);
app.frame();
}
}
assert.equal(
app.ground._warmed.length,
0,
'no ground was asked for on behalf of vehicles nobody can see',
);
assert.equal(
app.holds().includes('transit'),
false,
'and the render loop is not held over an empty view',
);
});
test('a height that arrives from a neighbour lifts a stationary vehicle too', async (t) => {
// A cache hit updated heightM but the billboard endpoints only refreshed on
// a lat/lon change, so a bus that had not moved kept drawing at ellipsoid
// height beside a neighbour standing on the street.
const app = harness(t, { floorAt: () => 125 });
const now = Date.now();
const parked = vehicle('still', 42.36, -71.06, now / 1000);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [parked], { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
const entry = app.vehicles()[0];
// Pretend the floor landed for this cell while the vehicle was absent: the
// shared cache knows the height, the entry does not.
entry.heightM = 0;
entry.heightResolved = false;
entry.heightPending = false;
app.ground._warm.set(app.ground._key(42.36, -71.06), 125);
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('still', 42.36, -71.06, Date.now() / 1000)],
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
app.frame();
const carto = Cesium.Cartographic.fromCartesian(entry.marker.position);
assert.ok(
Math.abs(carto.height - 126.5) < 1,
`the vehicle stands on the ground its cell reported (drew at ${carto.height.toFixed(1)} m)`,
);
});
test('a feed answering 304 every poll stays healthy instead of ageing out', async (t) => {
// The proxy keeps serving the body it holds, whose fetch time stops
// advancing. Reading that as "when did we last hear from them" marked a feed
// whose every request succeeded as DEGRADED at 90 s and emptied it at five
// minutes.
const app = harness(t);
const firstFetch = Date.now();
const body = snapshot(
'mbta',
'MBTA',
[vehicle('b', 42.36, -71.06, firstFetch / 1000)],
{ fetchedAt: firstFetch },
);
app.serve('mbta', () => ({
status: 200,
body,
headers: { 'x-transit-contact': String(Date.now()) },
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.vehicles().length, 1);
// Twenty-four polls — six minutes — of successful revalidation.
for (let poll = 0; poll < 24; poll += 1) {
app.advance(16_000);
app.serve('mbta', () => ({
status: 200,
body,
headers: { 'x-transit-contact': String(Date.now()) },
}));
await app.layer.update();
}
const stats = app.layer.getStats();
assert.equal(app.vehicles().length, 1, 'the vehicle is still on the globe');
assert.equal(stats.stale, false, 'and the feed does not read as gone quiet');
});
test('a feed that has genuinely stopped answering still ages out', async (t) => {
// The other half of the same rule: a contact time that does NOT advance is
// still silence, and silence must empty the map.
const app = harness(t);
const firstFetch = Date.now();
const body = snapshot(
'mbta',
'MBTA',
[vehicle('b', 42.36, -71.06, firstFetch / 1000)],
{ fetchedAt: firstFetch },
);
app.serve('mbta', () => ({
status: 200,
body,
headers: { 'x-transit-contact': String(firstFetch) },
}));
app.layer.enable(app.viewer);
await app.layer.update();
assert.equal(app.vehicles().length, 1);
app.advance(FEED_EVICT_AFTER_MS + 30_000);
app.serve('mbta', () => ({
status: 200,
body,
headers: { 'x-transit-contact': String(firstFetch) },
}));
await app.layer.update();
assert.equal(app.vehicles().length, 0, 'a silent feed empties the globe');
});
test('a vehicle with no surface yet waits instead of floating', async (t) => {
// Ellipsoid zero is not street level — in Boston it is about thirty metres
// underground — so a marker drawn at +3 m while its sample is pending sits
// above its final height and then drops. The owner saw that as floating.
let allowSamples = false;
const app = harness(t, {
floorAt: () => (allowSamples ? 30 : undefined),
});
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('b', 42.36, -71.06, Date.now() / 1000)],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.frame();
const entry = app.vehicles()[0];
assert.equal(
entry.heightPending,
true,
'it knows it has nothing to stand on',
);
assert.equal(entry.marker.show, false, 'so it is not drawn');
allowSamples = true;
app.settle();
assert.equal(entry.heightPending, false);
assert.equal(entry.marker.show, true, 'and appears once the ground answers');
const carto = Cesium.Cartographic.fromCartesian(entry.marker.position);
assert.ok(
Math.abs(carto.height - 31.5) < 1,
`standing on its floor plus the shared lift, got ${carto.height.toFixed(1)} m`,
);
});
test('a new vehicle borrows the lowest surface resolved nearby', async (t) => {
// The first visible frame uses the best prior available rather than a flat
// +3 m, and the prior is biased LOW so a marker can only rise onto its own
// surface, never appear above it and fall.
const app = harness(t, { floorAt: () => 40 });
const early = [
vehicle('a', 42.36, -71.06, Date.now() / 1000),
vehicle('b', 42.361, -71.06, Date.now() / 1000),
vehicle('c', 42.36, -71.061, Date.now() / 1000),
vehicle('d', 42.361, -71.061, Date.now() / 1000),
];
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', early, { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
// A second vehicle appears a block away, in a cell of its own whose
// neighbours are warm. The shared floor wants at least three resolved
// neighbours before it will speak for a cold cell.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
...early.map((v) => ({ ...v, timestamp: Date.now() / 1000 })),
// Its own cell is cold; the three around it are warm.
vehicle('second', 42.3605, -71.0605, Date.now() / 1000),
],
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
const second = app.vehicles().find((e) => e.record.id === 'second');
assert.equal(
second.heightPending,
false,
'it has ground to stand on at once',
);
assert.equal(second.heightM, 41.5, 'borrowed from the neighbourhood');
app.advance(250);
app.frame();
assert.equal(second.marker.show, true);
});
test('only the vehicles in view have their ground warmed', async (t) => {
// Warming is a network round trip through the shared resolver. A parked
// metropolitan fleet off screen used to keep a queue — and with it the
// render loop — alive indefinitely over an empty view.
const app = harness(t, { floorAt: () => undefined });
const warmed = app.ground._warmed;
const fleet = [vehicle('near', 42.36, -71.06, Date.now() / 1000)];
for (let i = 0; i < 50; i += 1) {
fleet.push(
vehicle(`far-${i}`, 42.6 + i * 0.002, -71.45, Date.now() / 1000),
);
}
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', fleet, { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
assert.ok(warmed.length > 0, 'the one in view is warmed');
const offScreen = warmed.filter((point) => point.lat > 42.5);
assert.equal(offScreen.length, 0, 'and nothing off screen is');
assert.equal(
app.holds().includes('transit'),
false,
'a fleet waiting for ground is not a fleet worth burning frames on',
);
});
test('a segment climbs from the ground it left to the ground it reaches', async (t) => {
// Rebuilding both ends of a segment at the destination's altitude lifted the
// vehicle onto the new height on the FIRST frame: it flew up, then
// travelled. With the display running a lag behind, the trap is subtler —
// the floor is asked for the NEWEST fix while an older one is still being
// drawn — so each fix carries its own floor.
const app = harness(t, { floorAt: (lat) => (lat > 42.3615 ? 100 : 10) });
const start = Date.now();
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.36, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
assert.ok(Math.abs(entry.heightM - 11.5) < 1, 'it starts on the low ground');
// The destination's surface is already known — a hundred metres uphill — so
// the poll resolves it immediately, on the fix that stands there.
app.ground._warm.set(app.ground._key(42.362, -71.06), 100);
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.362, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
await app.layer.update();
assert.ok(Math.abs(entry.heightM - 101.5) < 1, 'the destination is uphill');
const height = () =>
Cesium.Cartographic.fromCartesian(entry.marker.position).height;
app.run(400);
assert.ok(
height() < 30,
`still on the ground it is drawn on, got ${height().toFixed(1)} m`,
);
// Halfway along the segment it is halfway up; at the end, on the high ground.
const firstFixAt = start - AGE_MS;
app.run(firstFixAt + LAG_MS + TRANSIT_POLL_MS / 2 - Date.now());
assert.ok(
Math.abs(height() - 11.5) < 3,
`at the corridor floor, got ${height().toFixed(1)} m`,
);
app.run(TRANSIT_POLL_MS);
assert.ok(
Math.abs(height() - 101.5) < 1.5,
`and arrives on the high ground, got ${height().toFixed(1)} m`,
);
});
test('a lone impossible fix leaves the displayed vehicle and health untouched', async (t) => {
const app = harness(t, { floorAt: () => 5 });
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.36, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
// Eleven kilometres in one poll: 2,600 km/h.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.46, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
await app.layer.update();
assert.equal(
entry.sample.fromSeq,
entry.sample.toSeq,
'no segment was started',
);
assert.equal(app.state()._moving.has(entry), false);
assert.equal(entry.fixes.length, 1, 'the accepted history is preserved');
assert.equal(entry.resets, 0, 'one bad fix cannot reset playback');
assert.deepEqual(
[entry.to.lat, entry.to.lon],
[42.36, -71.06],
'it remains at its accepted position',
);
assert.ok(Math.abs(lat(entry) - 42.36) < 1e-6);
});
test('the detection metric says what the display is doing, not what the feed claims', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('m', 42.36, -71.06, Date.now() / 1000, {
routeId: 'Green-C',
status: 'STOPPED_AT',
stopId: '49001',
}),
],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
// It moves; the feed still says STOPPED_AT.
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('m', 42.362, -71.06, Date.now() / 1000, {
routeId: 'Green-C',
status: 'STOPPED_AT',
stopId: '49001',
}),
],
{ fetchedAt: Date.now() },
),
}));
await app.layer.update();
app.advance(2_000);
app.frame();
const [contact] = app.layer.getDetectableObjects({ maxCount: 5, seed: 0 });
assert.ok(contact, 'the vehicle is a contact');
assert.equal(
contact.metric.includes('STOPPED'),
false,
`a moving vehicle is not stopped, got "${contact.metric}"`,
);
assert.equal(contact.id, 'GREEN-C');
assert.ok(
contact.klass.includes('METRO') || contact.klass.includes('TRAM'),
`the class carries the mode, got "${contact.klass}"`,
);
});
test('the card keeps the operator claim as a report and says the mode plainly', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('m', 42.36, -71.06, Date.now() / 1000, {
routeId: 'Red',
status: 'STOPPED_AT',
stopId: '49001',
}),
],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
app.layer._transitPartsForTest().selection.selectVehicle(entry.key);
const card = app.overlaySources.get('transit-selected')?.[0];
assert.ok(card, 'a card is shown');
const text = card.details.join(' | ');
assert.match(text, /Subway/, 'the mode is said in words');
assert.match(text, /Last report: stopped at stop 49001/);
assert.match(
text,
/Shown at street level/,
'and the surface caveat is stated',
);
});
test('a vehicle reporting every 30 s moves continuously at its own speed', async (t) => {
// Measured on the live MBTA feed: half the fleet updates every thirty
// seconds while the browser polls every fifteen. Gliding over a fixed poll
// interval drew that half-minute of travel in fifteen seconds and then left
// the fleet perfectly still for the other fifteen — 46 vehicles moving, then
// zero, then 43, then zero. Played back a lag behind, the segment takes the
// thirty seconds the fixes describe and runs straight into the next one.
const app = harness(t, { floorAt: () => 5 });
const start = Date.now();
const firstFixAt = start - AGE_MS;
let fixAt = firstFixAt;
let north = 42.36;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('b', north, -71.06, fixAt / 1000)],
{
fetchedAt: Date.now(),
},
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
// Polls at 15 s (a repeat), 30 s (moved 0.0025°), 45 s (repeat), 60 s (moved)…
const samples = [];
// Each poll lands at the end of the previous fifteen frames, the way the
// manager tick does in the app: frames run continuously between polls.
for (let poll = 1; poll <= 6; poll += 1) {
if (poll % 2 === 0) {
fixAt += 2 * TRANSIT_POLL_MS;
north += 0.0025;
}
await app.layer.update();
for (let f = 0; f < 15; f += 1) {
app.advance(1_000);
app.frame();
samples.push({ at: Date.now(), lat: lat(entry), playT: entry.playT });
}
}
// A thirty-second reporter under a thirty-five-second prior holds once,
// before its first segment: nobody knows its cadence until the second fix
// arrives, and the hold is what earns it the longer lag. From the first
// frame it moves, the clock runs at exactly one second per second with no
// dead interval, and the drawn speed is the fix speed.
const firstMove = samples.findIndex(
(s, i) => i > 0 && s.lat !== samples[i - 1].lat,
);
assert.ok(firstMove > 0, 'it starts moving');
const playing = samples.filter(
(s, i) => i >= firstMove && s.playT < entry.fixes.at(-1).t,
);
assert.ok(
playing.length >= 40,
`long enough to judge (${playing.length} samples)`,
);
for (let i = 1; i < playing.length; i += 1) {
const dPlay = playing[i].playT - playing[i - 1].playT;
const dWall = playing[i].at - playing[i - 1].at;
// 1x, or the 0.95x it runs at while banking the longer lag it has just
// learned it needs — never a freeze, never a rush.
assert.ok(
dPlay <= dWall + 1 && dPlay >= 0.95 * dWall - 1,
`no freeze and no rush (${dPlay} ms of play in ${dWall} ms)`,
);
const metresPerS =
((playing[i].lat - playing[i - 1].lat) * 111_320) / (dWall / 1000);
const fixSpeed = (0.0025 * 111_320) / ((2 * TRANSIT_POLL_MS) / 1000);
assert.ok(
metresPerS <= fixSpeed * 1.01 + 0.05 &&
metresPerS >= fixSpeed * 0.94 - 0.05,
`at the fix speed: ${metresPerS.toFixed(2)} vs ${fixSpeed.toFixed(2)} m/s`,
);
}
});
test('the selected card anchors to the live marker every frame and republishes text only on change', async (t) => {
// The anchor was a clone refreshed on a 250 ms throttle under a 60 Hz
// sprite, so the card stepped behind a smooth vehicle. It is a getter now,
// read by the overlay host on every paint; the text keeps its throttle and
// is re-sent only when it has changed.
const app = harness(t);
const start = Date.now();
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.36, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
// Its floor resolves, so the marker is shown: a hidden vehicle has no card.
app.settle();
const entry = app.vehicles()[0];
const published = [];
app.layer._setTransitOverlayHostForTest({
setEntries: (id, entries) => published.push(entries[0]),
setVisible() {},
clearSource() {},
});
app.layer._transitPartsForTest().selection.selectVehicle(entry.key);
assert.equal(published.length, 1);
const card = published[0];
assert.equal(typeof card.position, 'function', 'the anchor is a getter');
app.advance(TRANSIT_POLL_MS);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('b', 42.3625, -71.0625, reported())],
{
fetchedAt: Date.now(),
},
),
}));
await app.layer.update();
// Frames at 16 ms through the segment: the anchor never lags the marker,
// whether or not a card refresh happened to run.
const firstFixAt = start - AGE_MS;
app.advance(firstFixAt + LAG_MS + 2_000 - Date.now());
let worst = 0;
for (let f = 0; f < 60; f += 1) {
app.advance(16);
app.frame();
const anchor = card.position();
worst = Math.max(
worst,
Cesium.Cartesian3.distance(anchor, entry.marker.position),
);
}
assert.ok(
worst < 1e-6,
`the anchor is the marker's own position (worst gap ${worst} m)`,
);
assert.ok(app.state()._moving.has(entry), 'and it was moving throughout');
// The text: throttled, and republished only when it changes. Sixty frames
// of one second is sixty throttle windows, but the card copy only changes
// when the reported age or the motion state does.
const before = published.length;
for (let f = 0; f < 5; f += 1) {
app.advance(16);
app.frame();
}
assert.equal(
published.length,
before,
'identical text within a second is not re-sent',
);
// A hidden marker anchors nothing.
entry.marker.show = false;
assert.equal(card.position(), null);
});
test('DETECT gets the same candidate list back until something could have changed it', async (t) => {
// Detection pulls the list on every paint. It used to be a fleet scan with
// an allocation and, above the cap, a sort — on the demo path, sixty times
// a second. The list is rebuilt on polls, visibility sweeps, removals and
// selection, and handed back as-is between them.
const app = harness(t);
const fleet = () =>
Array.from({ length: 40 }, (_, i) =>
vehicle(`v${i}`, 42.36 + i * 0.0002, -71.06, reported(), {
routeId: `${i}`,
}),
);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', fleet(), { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const first = app.layer.getDetectableObjects({ maxCount: 10, seed: 7 });
assert.equal(first.length, 10);
const again = app.layer.getDetectableObjects({ maxCount: 10, seed: 7 });
assert.equal(again, first, 'the same array, not a rebuilt one');
app.layer.getDetectableObjects({ maxCount: 5, seed: 7 });
app.advance(250);
app.layer._transitPartsForTest().queries.refreshDetectCache();
const other = app.layer.getDetectableObjects({ maxCount: 5, seed: 7 });
assert.notEqual(other, first, 'a different cap is a different list');
assert.equal(other.length, 5);
// Positions are the markers' own Cartesians: live, no copy.
const entry = app.vehicles().find((v) => v.key === first[0].sourceId);
assert.equal(first[0].position, entry.marker.position);
assert.equal(first[0].id, entry.record.routeId);
// Selecting a vehicle changes what the list says, so it is rebuilt.
app.layer._transitPartsForTest().selection.selectVehicle(first[0].sourceId);
app.layer.getDetectableObjects({ maxCount: 10, seed: 7 });
app.advance(250);
app.layer._transitPartsForTest().queries.refreshDetectCache();
const afterSelect = app.layer.getDetectableObjects({ maxCount: 10, seed: 7 });
assert.notEqual(afterSelect, first);
assert.equal(afterSelect[0].skipLabel, true);
// A poll rebuilds it; removing an UNSELECTED vehicle leaves the chosen set.
const chosenBefore = afterSelect.map((c) => c.sourceId);
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
app.settle();
app.layer._transitPartsForTest().queries.refreshDetectCache();
const afterPoll = app.layer.getDetectableObjects({ maxCount: 10, seed: 7 });
assert.notEqual(afterPoll, afterSelect, 'rebuilt after the poll');
assert.deepEqual(
afterPoll.map((c) => c.sourceId),
chosenBefore,
'same stable selection',
);
});
test('under FLIR and NVG every sprite is white-hot with a dark halo, and selection cannot undo it', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('bus', 42.36, -71.06, reported(), { routeId: '742' }),
vehicle('metro', 42.361, -71.06, reported(), { routeId: 'Red' }),
vehicle('tram', 42.362, -71.06, reported(), { routeId: 'Green-E' }),
],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const byRoute = new Map(app.vehicles().map((v) => [v.record.routeId, v]));
const bus = byRoute.get('742');
const plainImage = bus.marker.image;
const plainWidth = bus.marker.width;
const modeColour = bus.marker.color.clone();
app.style('thermal');
for (const entry of app.vehicles()) {
assert.ok(
Cesium.Color.equals(entry.marker.color, Cesium.Color.WHITE),
`${entry.record.routeId} is white-hot`,
);
assert.notEqual(entry.marker.image, plainImage, 'a haloed raster');
assert.match(
Buffer.from(entry.marker.image.split(',')[1], 'base64').toString(),
/#05080C/,
'with the dark ring',
);
assert.ok(entry.marker.width > plainWidth * 1.2, 'and larger');
}
// Selecting under FLIR grows the sprite and keeps it white — cyan would dim it.
app.layer._transitPartsForTest().selection.selectVehicle(bus.key);
assert.ok(
Cesium.Color.equals(bus.marker.color, Cesium.Color.WHITE),
'still white when selected',
);
assert.ok(
bus.marker.width > byRoute.get('Red').marker.width,
'and bigger than its neighbours',
);
app.layer._transitPartsForTest().selection.clearSelection();
assert.ok(
Cesium.Color.equals(bus.marker.color, Cesium.Color.WHITE),
'and white when deselected',
);
assert.equal(bus.marker.width, byRoute.get('Red').marker.width);
// Back to normal: the shipped look, exactly.
app.style('normal');
assert.ok(
Cesium.Color.equals(bus.marker.color, modeColour),
'mode colour restored',
);
assert.equal(bus.marker.image, plainImage, 'plain raster restored');
assert.equal(bus.marker.width, plainWidth, 'shipped size restored');
// A style that is not a sensor changes nothing.
app.style('anime');
assert.equal(bus.marker.image, plainImage);
});
test('a cockpit vision override wins over the map style while it lasts, and destroy stops listening', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.36, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
const plain = entry.marker.image;
app.vision('nvg', true);
assert.ok(
Cesium.Color.equals(entry.marker.color, Cesium.Color.WHITE),
'cockpit NVG restyles',
);
app.style('normal');
assert.ok(
Cesium.Color.equals(entry.marker.color, Cesium.Color.WHITE),
'the map style cannot undo a live cockpit override',
);
app.vision('normal', false);
assert.equal(
entry.marker.image,
plain,
'leaving the cockpit restores the map style',
);
assert.equal(app.state()._styleListeners.length, 2);
app.layer.destroy(app.viewer);
assert.equal(app.state()._styleListeners.length, 0, 'listeners removed');
assert.doesNotThrow(
() => app.style('thermal'),
"and a later event is nobody's business",
);
});
test('DETECT contacts carry a bracket tier per mode, keyed or keyless alike', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('bus', 42.36, -71.06, reported(), { routeId: '742' }),
vehicle('metro', 42.361, -71.06, reported(), { routeId: 'Red' }),
vehicle('tram', 42.362, -71.06, reported(), { routeId: 'Green-E' }),
],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const tiers = new Map(
app.layer.getDetectableObjects().map((c) => [c.id, c.tier]),
);
assert.equal(tiers.get('742'), 'transit_bus');
assert.equal(tiers.get('RED'), 'transit_subway');
assert.equal(tiers.get('GREEN-E'), 'transit_tram');
});
test('the floor cycle samples the rendered mesh for the cells it warms, with its own sprites excluded', async (t) => {
// A transit-only session never warmed a mesh floor: the layer warmed the
// DEM and stopped, so under a photogrammetric city it drew on bare earth
// seventeen metres down. The same cells now go to the application's mesh
// sampler after every warm — bounded by the sampler's own per-call cap and
// proximity gate — with the layer's billboards excluded so a vertical probe
// cannot land on a bus.
const app = harness(t, { floorAt: () => 5 });
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('a', 42.36, -71.06, reported()),
vehicle('b', 42.37, -71.07, reported()),
vehicle('far', 42.62, -71.44, reported()),
],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
assert.ok(
app.meshCalls.length >= 1,
'the sampler was asked after the floor resolved',
);
const call = app.meshCalls[0];
assert.equal(call.scene, app.viewer.scene);
assert.deepEqual(
call.points.map((p) => p.lat).sort(),
[42.36, 42.37],
'the in-view cells, and not the one off screen',
);
assert.equal(
call.options.excludeObjects.length,
2,
'both in-view billboards excluded',
);
assert.ok(
Math.abs(call.options.viewerLat - BOSTON.lat) < 1e-6,
'viewer subpoint in degrees',
);
assert.ok(Math.abs(call.options.viewerLon - BOSTON.lon) < 1e-6);
});
test('a failing prefix of cells cannot starve the vehicle behind it of a floor', async (t) => {
// Codex's reproduction: 301 visible cold entries, the first 300 in cells
// that never resolve. The budget used to take the first 300 unresolved
// vehicles in map order every cycle, so the 301st was never even asked for.
const count = FLOOR_WARM_PER_POLL + 1;
const app = harness(t, {
// Only the LAST vehicle's cell ever answers.
floorAt: (lat) =>
Math.abs(lat - (42.36 + (count - 1) * 0.002)) < 1e-9 ? 12 : undefined,
view: { west: -71.2, south: 42.3, east: -70.9, north: 43.2 },
});
const fleet = Array.from({ length: count }, (_, i) =>
vehicle(`v${i}`, 42.36 + i * 0.002, -71.06, reported()),
);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', fleet, { fetchedAt: Date.now() }),
}));
app.layer.enable(app.viewer);
await app.layer.update();
// The unique-cell budget resets per poll; the next poll rotates priority.
app.settle(3);
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
app.settle(3);
const last = app.vehicles().find((v) => v.record.id === `v${count - 1}`);
assert.equal(last.heightResolved, true, 'the 301st vehicle got its floor');
assert.ok(Math.abs(last.heightM - 13.5) < 1e-9);
assert.ok(
app.ground._warmed.some((p) => Math.abs(p.lat - last.record.lat) < 1e-9),
'because its cell was actually asked for',
);
});
test('a resolved floor still adopts a later mesh sample or a regime change on a parked vehicle', async (t) => {
// Codex's reproduction: resolve the DEM at 10 m, then the shared floor
// changes to a 50 m mesh sample; the parked entry kept 11.5 m for ever
// because ingestion only re-read a resolved height when the cell changed.
const app = harness(t, { floorAt: () => 10 });
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('p', 42.36, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
assert.ok(Math.abs(entry.heightM - 11.5) < 1e-9, 'DEM floor, lifted');
const height = () =>
Cesium.Cartographic.fromCartesian(entry.marker.position).height;
assert.ok(Math.abs(height() - 11.5) < 0.5);
// The mesh lands on the same cell.
app.ground._warm.set(app.ground._key(42.36, -71.06), 50);
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
app.frame();
assert.ok(
Math.abs(entry.heightM - 51.5) < 1e-9,
'the parked vehicle adopts the mesh floor',
);
assert.ok(Math.abs(height() - 51.5) < 0.5, 'and is drawn on it');
// Leaving the photoreal regime: the shared floor answers the DEM again.
app.ground._warm.set(app.ground._key(42.36, -71.06), 10);
app.advance(TRANSIT_POLL_MS);
await app.layer.update();
app.frame();
assert.ok(Math.abs(height() - 11.5) < 0.5, 'and follows it back down');
});
test('a stamp an hour ahead is bounded, so the honest fixes that follow are not refused', async (t) => {
// Codex's reproduction: the first fix stamped one hour ahead, then a
// corrected clock and normal travel. The marker stayed at its first
// position for as long as the wrong clock stayed newer than real time, and
// survived eviction because every refused snapshot refreshed its record.
const app = harness(t, { floorAt: () => 5 });
const ahead = (Date.now() + 3_600_000) / 1000;
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('c', 42.36, -71.06, ahead)], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
assert.ok(
entry.fixes[0].t <= Date.now() + FUTURE_STAMP_TOLERANCE_MS,
'the stamp is bounded by the fetch time',
);
// The clock is corrected; the bus travels north 200 m per poll.
let north = 42.36;
for (let poll = 1; poll <= 4; poll += 1) {
app.advance(TRANSIT_POLL_MS);
north += 0.0018;
const fixAtS = (Date.now() - 2_000) / 1000;
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('c', north, -71.06, fixAtS)], {
fetchedAt: Date.now(),
}),
}));
await app.layer.update();
}
assert.ok(
entry.fixes.length >= 3,
`honest fixes were accepted (${entry.fixes.length} in history)`,
);
assert.equal(entry.track.pendingCount, 0);
app.run(120_000);
assert.ok(
Math.abs(lat(entry) - 42.36) > 0.003,
'and the marker has moved on',
);
});
test('a run of out-of-order fixes restarts the history instead of freezing the vehicle', async (t) => {
const app = harness(t, { floorAt: () => 5 });
const later = (Date.now() + 5_000) / 1000; // within tolerance, but newer than what follows
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('o', 42.36, -71.06, later)], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
const firstRecord = entry.record;
// The feed's clock steps BACK: every following stamp is older than the first.
for (let poll = 1; poll <= ORDER_REJECTS_BEFORE_RESET; poll += 1) {
app.advance(TRANSIT_POLL_MS);
const stale = later - 60 + poll;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('o', 42.365, -71.06, stale, { label: `poll${poll}` })],
{
fetchedAt: Date.now(),
},
),
}));
await app.layer.update();
if (poll < ORDER_REJECTS_BEFORE_RESET) {
assert.equal(
entry.record,
firstRecord,
`refusal ${poll} does not refresh the record`,
);
assert.equal(entry.track.pendingCount, poll);
}
}
assert.equal(
entry.track.pendingCount,
0,
'reset after three advancing observations',
);
assert.equal(entry.fixes.length, 2);
app.settle();
assert.equal(entry.resets, 1);
assert.ok(
Math.abs(lat(entry) - 42.365) < 1e-6,
'placed at the fix it restarted from',
);
});
test('selected trail and marker share revised start floors, with unknown history left as gaps', async (t) => {
const app = harness(t, { floorAt: () => 10 });
let position = 42.36;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('trail', position, -71.06, reported())],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
app.advance(15000);
position += 0.0001;
await app.layer.update();
app.settle();
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
parts.selection.selectVehicle(entry.key);
app.run(8000, 16);
let trail = parts.trails.diagnostics();
assert.ok(trail.segments > 0);
assert.equal(trail.entitiesAdded, 0);
assert.ok(
Cesium.Cartesian3.equals(
trail.head.positions.at(-1),
entry.marker.position,
),
);
const body = trail.body,
revision = trail.revision;
app.run(1000, 16);
assert.equal(parts.trails.diagnostics().body, body);
app.ground._warm.set(app.ground._key(42.36, -71.06), 30);
parts.height.rereadFloors();
app.frame();
trail = parts.trails.diagnostics();
assert.ok(trail.revision > revision);
assert.ok(
Cesium.Cartesian3.equals(
trail.head.positions.at(-1),
entry.marker.position,
),
);
assert.ok(
Cesium.Cartographic.fromCartesian(entry.marker.position).height > 30,
);
assert.ok(entry.trailVertices <= 640);
parts.selection.clearSelection();
assert.equal(parts.trails.diagnostics(), null);
});
test('selection restores retained history without changing clock or health; churn aborts late reads', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [vehicle('b', 42.36, -71.06, reported())], {
fetchedAt: Date.now(),
}),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
const liveT = entry.fixes[0].t;
const payload = {
version: 1,
feedId: 'mbta',
vehicleId: 'b',
oldestT: liveT - 30000,
newestT: liveT,
truncated: false,
epochs: [{ id: 1, trip: '', route: '1', mode: 'bus' }],
fixes: [
[liveT - 30000, 42.359, -71.06, 1, 1],
[liveT - 15000, 42.3595, -71.06, 1, 1],
[liveT, 42.37, -71.06, 1, 1],
],
};
const priorFetch = globalThis.fetch;
let complete,
signal,
requests = 0;
globalThis.fetch = (url, options) => {
if (!String(url).includes('/trail/')) return priorFetch(url, options);
requests++;
signal = options.signal;
return new Promise((resolve) => {
complete = () => resolve(new Response(JSON.stringify(payload)));
});
};
const health = entry.track.acceptedAt,
clock = entry.playT;
parts.selection.selectVehicle(entry.key);
complete();
for (let i = 0; i < 20; i++) await Promise.resolve();
assert.equal(entry.track.count, 3);
assert.equal(
entry.fixes.at(-1).lat,
42.36,
'live position wins the conflict',
);
assert.equal(entry.track.acceptedAt, health);
assert.equal(entry.playT, clock);
assert.equal(requests, 1);
parts.selection.selectVehicle(entry.key);
assert.equal(requests, 2);
parts.selection.clearSelection();
assert.equal(signal.aborted, true);
const count = entry.track.count;
complete();
for (let i = 0; i < 20; i++) await Promise.resolve();
assert.equal(entry.track.count, count);
assert.equal(parts.trails.requestDiagnostics().pending, false);
parts.selection.selectVehicle(entry.key);
app.layer.disable(app.viewer);
assert.equal(signal.aborted, true);
complete();
for (let i = 0; i < 20; i++) await Promise.resolve();
assert.equal(app.vehicles().length, 0);
});
test('a long selected history shortens its oldest geometry before sacrificing the active corridor', async (t) => {
const app = harness(t);
const parts = app.layer._transitPartsForTest();
const { mergeHistory, seek } = await import('../../data/contactPlayback.js');
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('long', 42.36, -71.06, reported(), { routeId: 'CR-1' })],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0],
newest = entry.fixes[0].t;
mergeHistory(
entry.track,
Array.from({ length: 100 }, (_, i) => ({
t: newest - (99 - i) * 15000,
lat: 42.36 - (99 - i) * 0.009,
lon: -71.06,
epoch: entry.currentEpoch,
})),
);
for (let i = 0; i < 1000; i++)
app.ground._warm.set(app.ground._key(41.45 + i * 0.001, -71.06), 12);
seek(entry.track, newest - 7500, {
wallNowMs: Date.now(),
monoNowMs: performance.now(),
});
parts.rendering.sampleIdle(entry);
parts.selection.selectVehicle(entry.key);
assert.equal(entry.trailTruncated, true);
assert.ok(entry.trailVertices <= 640);
assert.ok(
entry.displayPaths.has(entry.sample.fromSeq),
'active path is prepared first',
);
assert.ok(
entry.trailSegments.every(
(segment, i, list) => i === 0 || list[i - 1].fromT <= segment.fromT,
),
);
});
test('sparse rail reports preserve a bounded marker corridor with and without selection', async (t) => {
const app = harness(t);
const parts = app.layer._transitPartsForTest();
const { seek } = await import('../../data/contactPlayback.js');
let latitude = 42.36 - 16000 / 111320;
let timestamp = reported();
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('sparse', latitude, -71.06, timestamp, { routeId: 'CR-1' })],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
app.advance(180000);
latitude = 42.36;
timestamp += 180;
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
assert.equal(entry.mode, 'rail');
assert.equal(entry.track.count, 2, 'both 88.9 m/s reports pass admission');
assert.equal(entry.fixes[1].t - entry.fixes[0].t, 180000);
for (let i = 0; i < 1500; i++)
app.ground._warm.set(app.ground._key(42.2 + i * 0.0002, -71.06), 12);
seek(entry.track, entry.fixes[0].t + 90000, {
wallNowMs: Date.now(),
monoNowMs: performance.now(),
});
parts.rendering.sampleIdle(entry);
for (const selected of [false, true, false]) {
if (selected) parts.selection.selectVehicle(entry.key);
else parts.selection.clearSelection();
parts.trails.prepareEntry(entry);
const path = entry.displayPaths.get(entry.sample.fromSeq);
assert.ok(path, 'active corridor survives the body cap');
assert.ok(path.positions.length <= 640);
const position = parts.trails.samplePosition(
entry,
entry.sample,
new Cesium.Cartesian3(),
true,
);
assert.ok(
position && [position.x, position.y, position.z].every(Number.isFinite),
);
parts.rendering.placeSample(entry, true);
assert.equal(entry.marker.show, true);
assert.ok(Cesium.Cartesian3.equals(entry.marker.position, position));
assert.ok(entry.trailVertices <= 640);
}
});
test('missing rectangle still rejects the far side, and frustum rejection wins', async (t) => {
const app = harness(t, { altitude: 100_000 });
app.viewer.camera.computeViewRectangle = () => undefined;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[
vehicle('near', 42.36, -71.06, reported()),
vehicle('far', -42.36, 108.94, reported()),
],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.advance(250);
app.frame();
assert.deepEqual(
[...app.state()._visible].map((e) => e.record.id),
['near'],
);
app.viewer.camera.frustum = {
computeCullingVolume: () => ({
computeVisibility: () => Cesium.Intersect.OUTSIDE,
}),
};
app.advance(250);
app.layer._transitPartsForTest().rendering.refreshVisibility();
assert.equal(app.state()._visible.size, 0);
assert.equal(app.state()._moving.size, 0);
assert.equal(app.holds().includes('transit'), false);
});
test('motion transitions migrate billboards and update cached positions; disable clears owners', async (t) => {
const app = harness(t);
let latitude = 42.36;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus', latitude, -71.06, reported())],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
const old = entry.marker;
app.layer.getDetectableObjects();
app.advance(15000);
latitude += 0.001;
await app.layer.update();
app.run(8000, 16);
assert.equal(entry.markerCollection, app.state()._animatedMarkers);
assert.notEqual(entry.marker, old);
assert.equal(entry.detectContact.position, entry.marker.position);
app.run(30000, 16);
assert.equal(entry.markerCollection, app.state()._markers);
const parts = app.layer._transitPartsForTest();
const contacts = app.layer.getDetectableObjects();
const metric = contacts[0].metric;
for (let i = 0; i < 20; i++) {
assert.equal(app.layer.getDetectableObjects(), contacts);
assert.equal(contacts[0].metric, metric);
}
parts.rendering.requestVisibility();
app.layer.disable(app.viewer);
assert.equal(app.state()._detectCache, null);
assert.equal(entry.detectContact, null);
assert.equal(app.state()._visible.size, 0);
assert.equal(app.state()._visibilityTimer, null);
assert.equal(app.state()._maintenanceTimer, null);
assert.equal(entry.wakeTimer, null);
assert.equal(app.state()._inFlight.size, 0);
assert.equal(parts.trails.requestDiagnostics().pending, false);
});
test('every styling path retains the mode palette and exact unpadded size', async (t) => {
const app = harness(t);
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus', 42.36, -71.06, reported(), { routeId: '741' })],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
const check = (hex, display) => {
assert.equal(entry.marker.color.toCssHexString().toUpperCase(), hex);
const svg = Buffer.from(
entry.marker.image.split(',')[1],
'base64',
).toString();
const width = Number(svg.match(/viewBox="-\d+ -\d+ (\d+)/)[1]);
assert.ok(Math.abs((entry.marker.width * 96) / width - display) < 1e-9);
};
check('#5EF08A', 20);
parts.selection.selectVehicle(entry.key);
check('#5EF08A', 30);
parts.rendering.paintMode(entry, 'subway');
check('#FF4538', 30);
app.style('thermal');
check('#FFFFFF', 39);
parts.selection.clearSelection();
check('#FFFFFF', 26);
app.vision('nvg', true);
check('#FFFFFF', 26);
parts.selection.selectVehicle(entry.key);
check('#FFFFFF', 39);
app.vision('normal', false);
check('#FF4538', 30);
parts.selection.clearSelection();
check('#FF4538', 20);
});
test('QA fleets at both budgets exercise real marker motion', async (t) => {
const app = harness(t, { altitude: 30000 });
app.layer.enable(app.viewer);
await app.layer.update();
for (const count of [800, 3000]) {
const loaded = app.layer._loadTransitFleetForTest(count, BOSTON, 1);
assert.equal(loaded.count, count);
assert.equal(loaded.moving, count);
assert.equal(loaded.fixBytes, count * 128 * 48);
const entry = app.vehicles()[0];
const before = Cesium.Cartesian3.clone(entry.marker.position);
app.advance(100);
app.frame();
assert.ok(Cesium.Cartesian3.distance(before, entry.marker.position) > 0.09);
}
});
test('visible motion is recullable without a camera event and releases its hold offscreen', async (t) => {
const app = harness(t, { altitude: 30000 });
app.layer.enable(app.viewer);
await app.layer.update();
app.layer._loadTransitFleetForTest(1, BOSTON, 1);
assert.equal(app.state()._moving.size, 1);
app.viewer.camera.frustum = {
computeCullingVolume: () => ({
computeVisibility: () => Cesium.Intersect.OUTSIDE,
}),
};
app.layer._transitPartsForTest().rendering.maintainPresentation();
app.advance(250);
assert.equal(app.state()._moving.size, 0);
assert.equal(app.state()._renderHeld, false);
});
test('mesh refinement reaches all 100 retained DEM cells within five poll budgets', async (t) => {
const { createHeight } = await import('./height.js');
t.mock.timers.enable({ apis: ['setTimeout'] });
let mono = 0;
t.mock.method(performance, 'now', () => mono);
const cells = Array.from({ length: 100 }, (_, i) => ({
lat: i / 1000,
lon: 0,
}));
const entry = { key: 'selected', to: cells[0], fixes: [cells.at(-1)] };
const probed = new Set();
const state = {
_enabled: true,
_selectedKey: entry.key,
_vehicles: new Map([[entry.key, entry]]),
_floorCursor: 0,
_heightDirty: new Set(),
_viewer: { camera: { positionCartographic: { height: 100 } } },
};
const height = createHeight({
state,
services: {
render: { governorRequestRender() {} },
ground: {
coarseFloorCoord: (lat, lon) => ({ lat, lon }),
cachedGroundFloor: () => 10,
GROUND_FLOOR_LIFT_M: 1.5,
},
mesh: {
sampleMeshFloorCells(scene, points) {
for (const point of points) probed.add(point.cell);
},
},
},
parts: {
trails: {
prepareEntry(e, collect) {
if (collect)
for (const point of cells)
collect(point, height.requestHeight(point.lat, point.lon));
},
},
rendering: { requestVisibility() {} },
},
});
t.after(() => height.clear());
for (let poll = 0; poll < 5; poll++) {
mono = poll * TRANSIT_POLL_MS;
height.anchorFloors();
}
assert.equal(probed.size, 100);
});
test('selection churn releases past geometry on every retained vehicle', async (t) => {
const app = harness(t, { floorAt: () => 10 });
const { mergeHistory, seek } = await import('../../data/contactPlayback.js');
const parts = app.layer._transitPartsForTest();
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
Array.from({ length: 5 }, (_, i) =>
vehicle(`churn-${i}`, 42.36, -71.06, reported()),
),
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
for (const entry of app.vehicles()) {
const newest = entry.fixes[0].t;
mergeHistory(
entry.track,
Array.from({ length: 100 }, (_, i) => ({
t: newest - (99 - i) * 5000,
lat: 42.36 - (99 - i) * 0.0001,
lon: -71.06,
epoch: entry.currentEpoch,
})),
);
for (let i = 0; i < 20; i++)
app.ground._warm.set(app.ground._key(42.345 + i * 0.001, -71.06), 10);
seek(entry.track, newest - 7500, {
wallNowMs: Date.now(),
monoNowMs: performance.now(),
});
parts.rendering.sampleIdle(entry);
parts.selection.selectVehicle(entry.key);
assert.ok(entry.trailSegments.length > 50);
parts.selection.clearSelection();
for (const retained of app.vehicles()) {
assert.equal(retained.trailSegments?.length || 0, 0);
assert.equal(retained.trailVertices || 0, 0);
assert.ok(
[...retained.displayPaths.keys()].every(
(seq) => seq >= retained.sample.fromSeq,
),
);
}
assert.equal(parts.trails.diagnostics(), null);
assert.equal(
entry.displayPaths.size,
2,
'only active and future corridors remain',
);
seek(entry.track, newest - 1000, {
wallNowMs: Date.now(),
monoNowMs: performance.now(),
});
parts.rendering.sampleIdle(entry);
assert.equal(
entry.displayPaths.size,
1,
'marker playback releases completed corridors',
);
}
});
test('tab visibility synchronizes playback before the returning frame and removes its listener', async (t) => {
const app = harness(t, { floorAt: () => 10 });
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(1, BOSTON, 5);
app.settle();
const entry = app.vehicles()[0];
app.frame();
const before = entry.playT;
const resets = entry.track.resets;
document.visibilityState = 'hidden';
document.dispatchEvent(new Event('visibilitychange'));
app.advance(30000);
document.visibilityState = 'visible';
document.dispatchEvent(new Event('visibilitychange'));
assert.equal(entry.track.resetReason, 're-entry');
assert.equal(entry.track.resets, resets + 1);
assert.ok(entry.playT > before);
assert.equal(entry.playT, Date.now() - entry.track.targetDelayMs);
const synchronized = entry.playT;
app.frame();
assert.equal(
entry.playT,
synchronized,
'first frame does not replay hidden elapsed time',
);
document.dispatchEvent(new Event('visibilitychange'));
assert.equal(entry.track.resets, resets + 1, 'visible duplicate is a no-op');
const remove = t.mock.method(document, 'removeEventListener');
app.layer.disable(app.viewer);
assert.ok(
remove.mock.calls.some((call) => call.arguments[0] === 'visibilitychange'),
);
});
for (const bounds of ['null', 'stale']) {
test(`browser visibility starts an injected fleet with ${bounds} bounds and an unsampled hidden marker`, async (t) => {
const app = harness(t);
app.viewer.camera.frustum = {
computeCullingVolume: () => ({
computeVisibility: () => Cesium.Intersect.INSIDE,
}),
};
app.layer.enable(app.viewer);
const loaded = app.layer._loadTransitFleetForTest(4, BOSTON, 70, [
'Red',
'741',
'742',
'Green-E',
]);
const state = app.state(),
parts = app.layer._transitPartsForTest();
state._viewBounds =
bounds === 'null' ? null : { south: 0, north: 1, west: 0, east: 1 };
for (const entry of app.vehicles()) {
state._visible.delete(entry);
state._moving.delete(entry);
entry.marker.show = false;
entry.sample = null;
}
state._visibilityAt = 0;
parts.rendering.refreshVisibility();
assert.equal(state._visible.size, loaded.count);
assert.equal(state._moving.size, loaded.count);
assert.equal(app.viewer.scene.requestRenderMode, false);
const entry = app.vehicles()[0],
before = Cesium.Cartesian3.clone(entry.marker.position);
app.run(1000, 16);
assert.ok(Cesium.Cartesian3.distance(before, entry.marker.position) > 0.9);
});
}
test('keyless loaded terrain resolves a pending surface without needing a frame first', async (t) => {
const app = harness(t, { floorAt: () => undefined });
let floor;
app.viewer.scene.globe.show = true;
app.viewer.scene.globe.getHeight = () => floor;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('bus', 42.36, -71.06, reported())],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
assert.equal(entry.heightPending, true);
assert.equal(entry.marker.show, false);
floor = 12;
parts.height.rereadFloors();
app.state()._visibilityAt = 0;
parts.rendering.refreshVisibility();
assert.equal(entry.heightPending, false);
assert.equal(entry.surfaceReady, true);
assert.equal(entry.marker.show, true);
});
test('scripted keyless fleet keeps its known floor through selection and floor rereads', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.viewer.scene.globe.show = true;
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(4, BOSTON, 70);
const parts = app.layer._transitPartsForTest(),
entry = app.vehicles()[0];
parts.selection.selectVehicle(entry.key);
parts.height.rereadFloors();
app.run(1000, 16);
assert.equal(entry.surfaceReady, true);
assert.equal(entry.marker.show, true);
assert.ok(entry.trailSegments.length > 0);
assert.ok(app.state()._moving.has(entry));
});
for (const count of [800, 3000]) {
test(`real perspective frustum admits all ${count} moving budget fixtures`, async (t) => {
const altitude = count === 800 ? 600 : 30000;
const app = harness(t, { altitude, floorAt: () => undefined });
const camera = app.viewer.camera;
camera.positionWC = Cesium.Cartesian3.fromDegrees(
BOSTON.lon,
BOSTON.lat,
altitude,
);
camera.directionWC = Cesium.Cartesian3.negate(
Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(
camera.positionWC,
new Cesium.Cartesian3(),
),
new Cesium.Cartesian3(),
);
const enu = Cesium.Transforms.eastNorthUpToFixedFrame(camera.positionWC);
camera.upWC = Cesium.Matrix4.getColumn(enu, 1, new Cesium.Cartesian3());
camera.frustum = new Cesium.PerspectiveFrustum({
fov: Math.PI / 3,
aspectRatio: 1920 / 1080,
near: 1,
far: 100000,
});
app.layer.enable(app.viewer);
const loaded = app.layer._loadTransitFleetForTest(
count,
BOSTON,
count === 800 ? 5 : 120,
);
assert.equal(loaded.moving, count);
assert.equal(app.state()._visible.size, count);
const entry = app.vehicles()[0],
before = Cesium.Cartesian3.clone(entry.marker.position);
app.run(1000, 16);
assert.ok(Cesium.Cartesian3.distance(before, entry.marker.position) > 0.9);
assert.equal(app.viewer.scene.requestRenderMode, false);
});
}
test('click handler selects a migrated scripted billboard and prepares a valid depth-fail trail', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(4, BOSTON, 70);
const entry = app.vehicles()[0];
assert.equal(entry.markerCollection, app.state()._animatedMarkers);
app.viewer.scene.pick = () => ({ primitive: entry.marker, id: entry.key });
app
.state()
._clickHandler.getInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)({
position: new Cesium.Cartesian2(500, 400),
});
assert.equal(app.state()._selectedKey, entry.key);
const body = app.layer._transitPartsForTest().trails.diagnostics().body;
assert.ok(body.geometryInstances.length > 0);
body._batchTableAttributeIndices = { color: 0, depthFailColor: 1 };
assert.doesNotThrow(() =>
Cesium.Primitive._updateColorAttribute(
body,
body.depthFailAppearance.vertexShaderSource,
true,
),
);
assert.ok(entry.trailSegments.length > 0);
app.run(1000, 16);
assert.equal(entry.marker.show, true);
});
test('intercepted scripted reports resolve their explicit floor and wake without a preRender bootstrap', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.layer._setTransitFixtureFloorsForTest(['mbta:sim-straight'], 20);
let latitude = BOSTON.lat;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('sim-straight', latitude, BOSTON.lon, reported())],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.advance(15000);
latitude += 0.001;
await app.layer.update();
app.advance(8000);
const entry = app.vehicles()[0];
assert.equal(entry.heightPending, false);
assert.equal(entry.surfaceReady, true);
assert.equal(entry.marker.show, true);
assert.ok(app.state()._moving.has(entry));
assert.equal(app.viewer.scene.requestRenderMode, false);
const before = Cesium.Cartesian3.clone(entry.marker.position);
app.run(1000, 16);
assert.ok(Cesium.Cartesian3.distance(before, entry.marker.position) > 1);
app.layer.disable(app.viewer);
assert.equal(app.state()._qaFixtureFloors.size, 0);
});
test('hot path reuses sample outputs and reads camera altitude once per frame', async (t) => {
const h = harness(t);
await h.layer.enable(h.viewer);
h.layer._loadTransitFleetForTest(800, BOSTON, 5);
const state = h.state();
const parts = h.layer._transitPartsForTest();
const entry = state._vehicles.values().next().value;
const out = new Cesium.Cartesian3();
assert.equal(parts.trails.samplePosition(entry, entry.sample, out), out);
const sample = entry.sample;
const carto = h.viewer.camera.positionCartographic;
let reads = 0;
Object.defineProperty(h.viewer.camera, 'positionCartographic', {
configurable: true,
get() {
reads++;
return carto;
},
});
h.advance(17);
h.frame();
assert.equal(entry.sample, sample);
assert.ok(reads <= 2, `camera altitude reads/frame ${reads}, limit 2`);
const contacts = h.layer.getDetectableObjects();
const contact = contacts[0];
for (let i = 0; i < 100; i++) {
state._detectRevision++;
assert.equal(h.layer.getDetectableObjects(), contacts);
assert.equal(contacts[0], contact);
}
});
test('scripted straight and changing delay preserve ordinary-frame speed through polls', async (t) => {
const h = harness(t, { altitude: 600 });
const start = Date.now();
const modes = ['straight', 'gap', 'dup'];
h.serve('mbta', () => {
const elapsed = (Date.now() - start) / 1000;
return {
status: 200,
body: snapshot(
'mbta',
'MBTA',
modes.map((id) => {
let sec =
Math.floor(elapsed / (id === 'dup' ? 30 : 10)) *
(id === 'dup' ? 30 : 10);
if (id === 'gap' && sec >= 40 && sec < 130) sec = 40;
return vehicle(
id,
BOSTON.lat + (sec * 8) / 111320,
BOSTON.lon,
(start + sec * 1000) / 1000,
);
}),
{ fetchedAt: Date.now() },
),
};
});
await h.layer.enable(h.viewer);
let prior = new Map(),
worst = 0,
pair = null,
samples = 0;
for (let ms = 17; ms <= 200000; ms += 17) {
h.advance(17);
if (ms % 15011 === 0) await h.layer.update();
h.frame();
for (const e of h.vehicles()) {
const a = prior.get(e.key),
s = e.sample,
p = e.marker.position;
const b = {
x: p.x,
y: p.y,
z: p.z,
t: s.displayT,
from: s.fromSeq,
to: s.toSeq,
speed: s.segmentSpeedMps,
phase: s.phase,
};
if (
a &&
a.phase === 'playing' &&
b.phase === 'playing' &&
a.from === b.from &&
a.to === b.to &&
a.speed > 0 &&
b.t > a.t
) {
const error = Math.abs(
Math.hypot(b.x - a.x, b.y - a.y, b.z - a.z) /
((b.t - a.t) / 1000) /
a.speed -
1,
);
samples++;
if (error > worst) {
worst = error;
pair = { key: e.key, ms, a, b };
}
}
prior.set(e.key, b);
}
}
assert.ok(samples > 1000);
assert.ok(worst < 0.02, JSON.stringify({ worst, pair, samples }));
});
test('a lost prepared surface releases moving playback on that frame', async (t) => {
const h = harness(t);
await h.layer.enable(h.viewer);
h.layer._loadTransitFleetForTest(1, BOSTON, 5);
const parts = h.layer._transitPartsForTest();
assert.equal(h.state()._moving.size, 1);
h.state()._heightDirty.clear();
clearTimeout(h.state()._visibilityTimer);
h.state()._visibilityTimer = null;
t.mock.method(parts.trails, 'samplePosition', () => null);
h.advance(17);
h.frame();
assert.equal(h.state()._moving.size, 0);
assert.equal(h.state()._renderHeld, false);
});
test('sensor brackets clear the padded hot body and retain their contact during style changes', async (t) => {
const h = harness(t);
await h.layer.enable(h.viewer);
h.layer._loadTransitFleetForTest(1, BOSTON, 5);
const parts = h.layer._transitPartsForTest(),
entry = h.vehicles()[0];
const contact = h.layer.getDetectableObjects()[0];
for (const style of ['thermal', 'surveillance', 'noir', 'normal']) {
parts.rendering.setStylePreset(style);
assert.equal(entry.detectContact, contact);
assert.ok(contact.bracketHalfWidth >= entry.marker.width / 2 + 2, style);
assert.ok(contact.bracketHalfHeight >= entry.marker.height / 2 + 2, style);
}
});
test('selected anchor rejects nonfinite marker coordinates before sampling and during surface changes', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(4, BOSTON, 70);
const entry = app.vehicles()[0];
app.layer._transitPartsForTest().selection.selectVehicle(entry.key);
const card = app.overlaySources.get('transit-selected')[0];
assert.ok(card.position());
const position = Cesium.Cartesian3.clone(entry.marker.position);
entry.marker.position = new Cesium.Cartesian3(NaN, 0, 0);
assert.equal(card.position(), null);
entry.marker.position = position;
assert.equal(card.position(), entry.marker.position);
entry.marker.show = false;
assert.equal(card.position(), null);
});
test('preset clicks preserve both pick id paths through moving and stationary collection migration', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(4, BOSTON, 70);
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
for (const style of ['normal', 'thermal', 'surveillance', 'noir', 'retro'])
for (const animated of [true, false]) {
parts.rendering.setStylePreset(style);
entry.sample.phase = animated ? 'playing' : 'held';
entry.sample.segmentSpeedMps = animated ? 1 : 0;
entry.sample.nextWakeMonoMs = Infinity;
entry.sample.segmentCourseDeg = entry.courseDeg;
parts.rendering.schedulePlayback(entry);
assert.equal(
entry.markerCollection,
animated ? app.state()._animatedMarkers : app.state()._markers,
);
assert.equal(entry.marker.id, entry.key);
for (const pick of [{ id: entry.key }, { primitive: entry.marker }]) {
app.viewer.scene.pick = () => pick;
app
.state()
._clickHandler.getInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)(
{ position: new Cesium.Cartesian2(500, 400) },
);
assert.equal(app.state()._selectedKey, entry.key);
parts.selection.clearSelection();
}
}
});
test('QA backwards seek reconstructs the pruned fixture paths before allocation measurement', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(4, BOSTON, 70);
app.run(10000, 16);
const entry = app.vehicles()[0];
entry.displayPaths.clear();
app.layer._resetTransitFixtureClockForTest();
assert.ok(
entry.displayPaths.size <= 6,
'rewind retains only the active and future fixture corridor',
);
assert.equal(entry.marker.show, true);
assert.equal(app.state()._moving.size, 4);
const before = Cesium.Cartesian3.clone(entry.marker.position);
app.run(1000, 16);
assert.ok(Cesium.Cartesian3.distance(before, entry.marker.position) > 0.9);
});
test('sensor clicks map displayed pixels into the pre-shader pick buffer', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(4, BOSTON, 70);
const entry = app.vehicles()[0],
scene = app.viewer.scene;
let actual;
scene.pick = (p) => {
actual = { x: p.x, y: p.y };
return { id: entry.key };
};
scene.canvas = {
width: 1920,
height: 1080,
clientWidth: 1920,
clientHeight: 1080,
};
const uniforms = { intensity: 1, pixelation: 1 };
scene.postProcessStages = {
getStageByName: () => ({ enabled: true, uniforms }),
};
const click = app
.state()
._clickHandler.getInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
app.layer._transitPartsForTest().rendering.setStylePreset('surveillance');
click({ position: new Cesium.Cartesian2(1440, 540) });
assert.ok(Math.abs(actual.x - 1472) < 1e-6);
assert.equal(actual.y, 540);
app.layer._transitPartsForTest().rendering.setStylePreset('thermal');
uniforms.pixelation = 1.5;
click({ position: new Cesium.Cartesian2(100.9, 200.1) });
assert.ok(Math.abs(actual.x - 100.5) < 1e-6);
assert.ok(Math.abs(actual.y - 201) < 1e-6);
assert.equal(app.state()._selectedKey, entry.key);
});
test('rejected route metadata stays on the accepted bus', async (t) => {
const app = harness(t);
let routeId = '741',
latitude = 42.36;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('b', latitude, -71.06, reported(), { routeId })],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
const image = entry.marker.image;
app.advance(TRANSIT_POLL_MS);
routeId = 'Red';
latitude = 43.36;
await app.layer.update();
assert.equal(entry.mode, 'bus');
assert.equal(entry.record.routeId, '741');
assert.equal(entry.marker.image, image);
assert.equal(entry.currentEpoch, 1);
assert.equal(entry.track.count, 1);
assert.doesNotMatch(
app.layer
.getDetectableObjects()
.map((c) => c.metric)
.join(' '),
/METRO/,
);
});
test('reject then selected history backfill cannot resurrect a 111 km displacement', async (t) => {
const app = harness(t);
let latitude = 42.36;
app.serve('mbta', () => ({
status: 200,
body: snapshot(
'mbta',
'MBTA',
[vehicle('b', latitude, -71.06, reported())],
{ fetchedAt: Date.now() },
),
}));
app.layer.enable(app.viewer);
await app.layer.update();
app.settle();
const entry = app.vehicles()[0];
app.advance(15000);
latitude += 0.001;
await app.layer.update();
app.settle();
const accepted = entry.fixes.map((f) => [f.t, f.lat, f.lon, 1, 1]);
app.advance(15000);
latitude = 43.36;
await app.layer.update();
assert.equal(entry.track.count, 2);
const pending = entry.track.pending;
const clock = entry.playT;
const priorFetch = globalThis.fetch;
globalThis.fetch = (url, options) =>
String(url).includes('/trail/')
? Promise.resolve(
new Response(
JSON.stringify({
version: 1,
feedId: 'mbta',
vehicleId: 'b',
oldestT: accepted[0][0],
newestT: pending.t,
truncated: false,
epochs: [{ id: 1, trip: '', route: '1', mode: 'bus' }],
fixes: [...accepted, [pending.t, latitude, -71.06, 1, 1]],
}),
),
)
: priorFetch(url, options);
const parts = app.layer._transitPartsForTest();
parts.selection.selectVehicle(entry.key);
for (let i = 0; i < 30; i++) await Promise.resolve();
assert.equal(entry.track.count, 2);
assert.equal(entry.playT, clock);
assert.equal(entry.track.pending, pending);
app.run(60000);
assert.ok(Math.abs(lat(entry) - 42.361) * 111320 < 1);
assert.equal(entry.track.resets, 0);
});
test('actual Traffic, Bikeshare and Transit lifecycles retain sensitivity in every enable/disable order', async (t) => {
const { createLifecycle: trafficLifecycle } =
await import('../traffic/lifecycle.js');
const { createLifecycle: bikeLifecycle } =
await import('../bikeshare/lifecycle.js');
const app = harness(t);
const noop = () => {};
const traffic = trafficLifecycle({
state: { _pointCollection: {}, _loadGeneration: 0 },
services: {
render: { holdContinuousRender: noop, releaseContinuousRender: noop },
},
source: {},
parts: {
flow: { ensureFlowStatus: noop },
animation: { animate: noop, clearDots: noop, syncRenderDemand: noop },
viewport: { onCameraChanged: noop },
ingestion: { cancelActiveFetch: noop },
},
}).methods;
const bike = bikeLifecycle({
state: {
_pointCollection: {},
_overlayHost: { setVisible: noop },
_cityRuntime: new Map(),
_failedCityIds: new Set(),
},
services: {
sprites: { restoreSpriteOrder: noop },
picking: { registerPickOwner: noop, unregisterPickOwner: noop },
},
source: {},
parts: {
selection: {
_installClickHandler: noop,
_clearSelection: noop,
_onKeyDown: noop,
},
viewport: {
onCameraChanged: noop,
runProximityCheck: noop,
deactivateAllCities: noop,
},
ingestion: { abortAllInFlight: noop },
},
}).methods;
app.viewer.camera.moveEnd = new Cesium.Event();
const layers = { traffic, bikeshare: bike, transit: app.layer };
const orders = [
['traffic', 'bikeshare', 'transit'],
['traffic', 'transit', 'bikeshare'],
['bikeshare', 'traffic', 'transit'],
['bikeshare', 'transit', 'traffic'],
['transit', 'traffic', 'bikeshare'],
['transit', 'bikeshare', 'traffic'],
];
try {
for (const enable of orders)
for (const disable of orders) {
const active = new Set();
for (const id of enable) {
layers[id].enable(app.viewer);
active.add(id);
assert.equal(app.viewer.camera.percentageChanged, 0.05);
assert.deepEqual(
new Set(cameraSensitivityClaims(app.viewer.camera)),
active,
);
}
for (const id of disable) {
layers[id].disable(app.viewer);
active.delete(id);
assert.equal(
app.viewer.camera.percentageChanged,
active.size ? 0.05 : 0.5,
`${enable} / ${disable} after ${id}`,
);
assert.deepEqual(
new Set(cameraSensitivityClaims(app.viewer.camera)),
active,
);
}
}
} finally {
for (const layer of Object.values(layers)) layer.disable(app.viewer);
}
});
test('moving and stationary transit stay adjacent above CCTV and Bikeshare during migration', async (t) => {
const {
registerSpriteCollection,
unregisterSpriteCollection,
restoreSpriteOrder,
} = await import('../../data/spriteOrder.js');
const app = harness(t);
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(1, BOSTON, 70);
const cctv = {},
bike = {},
flights = {};
app.primitives.push(cctv, bike, flights);
app.viewer.scene.primitives.raiseToTop = (c) => {
app.primitives.splice(app.primitives.indexOf(c), 1);
app.primitives.push(c);
};
const collections = new Map([
...app.sprites,
['cctv', cctv],
['bikeshare', bike],
['flights', flights],
]);
for (const [id, collection] of collections)
registerSpriteCollection(id, collection);
t.after(() => {
for (const [id, c] of collections) unregisterSpriteCollection(id, c);
});
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
for (const moving of [true, false, true]) {
entry.sample.phase = moving ? 'playing' : 'held';
entry.sample.segmentSpeedMps = moving ? 1 : 0;
entry.sample.segmentCourseDeg = NaN;
parts.rendering.schedulePlayback(entry);
restoreSpriteOrder(app.viewer);
const active = app.primitives.indexOf(entry.markerCollection);
assert.ok(active > app.primitives.indexOf(cctv));
assert.ok(active > app.primitives.indexOf(bike));
assert.ok(active < app.primitives.indexOf(flights));
assert.equal(
Math.abs(
app.primitives.indexOf(app.state()._markers) -
app.primitives.indexOf(app.state()._animatedMarkers),
),
1,
);
assert.equal(entry.marker.id, entry.key);
}
});
test('transit polls the supplied source and cancels it when disabled', async (t) => {
const calls = [];
const source = {
async requestSnapshot(feedId, { signal }) {
calls.push({ feedId, signal });
return Response.json(
snapshot(
feedId,
'MBTA',
[vehicle('injected', 42.36, -71.06, reported())],
{ fetchedAt: Date.now() },
),
);
},
async getHistory() {
throw new Error('No selected vehicle');
},
};
const app = harness(t, { source });
globalThis.fetch = () => {
throw new Error('Unexpected global fetch');
};
app.layer.enable(app.viewer);
await app.layer.update();
assert.ok(calls.some(({ feedId }) => feedId === 'mbta'));
assert.ok(app.vehicles().some((entry) => entry.record.id === 'injected'));
const before = calls.length;
app.layer.disable(app.viewer);
await app.layer.update();
assert.equal(calls.length, before);
});
test('selected trail QA fixture survives repeated live CapMetro snapshots', async (t) => {
const { TRAIL_VISIBILITY_FEED } =
await import('../../../scripts/qa-transit-scenes.mjs');
const { getRegisteredTransitFeed } =
await import('../../data/transitFeeds.js');
const app = harness(t, { at: { lat: 30.267, lon: -97.7431 } });
app.layer.enable(app.viewer);
const { ingestion, selection } = app.layer._transitPartsForTest();
const now = Date.now();
ingestion.applySnapshot(
TRAIL_VISIBILITY_FEED,
{
fetchedAt: now,
vehicles: [vehicle('trail-visible', 30.267, -97.7431, now / 1000)],
},
{ stale: false },
);
const key = `${TRAIL_VISIBILITY_FEED.id}:trail-visible`;
selection.selectVehicle(key);
assert.equal(app.state()._selectedKey, key);
for (let poll = 0; poll <= MISSED_POLLS_TO_DROP; poll++) {
ingestion.applySnapshot(
getRegisteredTransitFeed('capmetro-austin'),
{
fetchedAt: now + poll,
vehicles: [vehicle('live-bus', 30.268, -97.7431, now / 1000)],
},
{ stale: false },
);
}
assert.ok(
app.state()._vehicles.has(key),
'live polls must not evict the selected fixture',
);
assert.equal(app.state()._selectedKey, key);
assert.ok(
app.state()._vehicles.has('capmetro-austin:live-bus'),
'live ingestion still runs',
);
});
for (const jump of ['setView', 'flyTo']) {
test(`${jump} recovers a cold hidden vehicle within one visibility interval`, async (t) => {
const app = harness(t, { floorAt: () => undefined });
let floor;
app.viewer.scene.globe.show = true;
app.viewer.scene.globe.getHeight = () => floor;
app.serve('mbta', () => ({
status: 200,
body: snapshot('mbta', 'MBTA', [
vehicle('cold', 42.36, -71.06, reported()),
]),
}));
app.layer.enable(app.viewer);
await app.layer.update();
const entry = app.vehicles()[0];
assert.equal(entry.marker.show, false);
assert.equal(entry.heightPending, true);
// Surface tiles are now loaded at the camera destination. No poll, floor
// timer or preRender is allowed to bootstrap the visibility recovery.
clearTimeout(app.state()._floorTimer);
app.state()._floorTimer = null;
app.viewer.camera[jump] = () => {
floor = 12;
app.viewer.camera.changed.raiseEvent();
};
app.viewer.camera[jump]();
app.advance(250);
assert.equal(entry.marker.show, true);
assert.equal(entry.heightPending, false);
assert.equal(entry.surfaceReady, true);
assert.equal(entry.visibility.heightPending, false);
assert.equal(entry.visibility.surfaceReady, true);
});
}
test('selected history is prepared while its head is hidden and remains shown outside the head frustum', async (t) => {
const app = harness(t);
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(1, BOSTON);
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
entry.marker.show = false;
app.state()._visible.delete(entry);
app.state()._moving.delete(entry);
parts.selection.selectVehicle(entry.key);
const trail = parts.trails.diagnostics();
assert.ok(trail.segments > 0);
assert.ok(trail.body);
assert.equal(trail.body.show, true);
parts.trails.update();
assert.equal(trail.body.show, true);
app.state()._vehicles.delete(entry.key);
parts.trails.update();
assert.equal(trail.body.show, false);
app.state()._vehicles.set(entry.key, entry);
});
test('selected draped history prepares geometry before surface heights resolve', async (t) => {
const app = harness(t, { floorAt: () => undefined });
app.layer.enable(app.viewer);
app.layer._loadTransitFleetForTest(1, BOSTON);
const entry = app.vehicles()[0],
parts = app.layer._transitPartsForTest();
delete entry.qaFloorM;
entry.marker.show = false;
entry.heightPending = true;
// Exercise the real GroundPolylinePrimitive branch, without a WebGL context.
const width = Cesium.ContextLimits._maximumAliasedLineWidth;
Cesium.ContextLimits._maximumAliasedLineWidth = 1;
t.after(() => {
Cesium.ContextLimits._maximumAliasedLineWidth = width;
});
app.viewer.scene.frameState = { context: { depthTexture: true } };
app.viewer.scene.groundPrimitives = app.viewer.scene.primitives;
parts.selection.selectVehicle(entry.key);
const trail = parts.trails.diagnostics();
assert.equal(trail.ground, true);
assert.ok(trail.body instanceof Cesium.GroundPolylinePrimitive);
assert.ok(trail.segments > 0);
assert.equal(trail.body.show, true);
assert.equal(
entry.displayPaths.size,
0,
'unresolved marker corridors remain unavailable',
);
});
// A camera pose can change without a viewport-membership revision (notably
// a top-down orbit). Exercise the actual frame pass with an otherwise idle fleet.
test('parked transit reprojects held and reported courses at the bounded camera cadence', async (t) => {
const { createRendering } = await import('./rendering.js');
t.mock.timers.enable({ apis: ['Date'], now: 1000 });
const position = Cesium.Cartesian3.fromDegrees(0, 0, 20);
const cameraPosition = Cesium.Cartesian3.fromDegrees(0, 0, 1000);
const scene = {
frameState: { mode: Cesium.SceneMode.SCENE3D },
canvas: { clientWidth: 1280, clientHeight: 800 },
camera: {
positionWC: cameraPosition,
heading: 0,
pitch: -Math.PI / 2,
roll: 0,
frustum: new Cesium.PerspectiveFrustum({
fov: Math.PI / 3,
aspectRatio: 1.6,
near: 1,
far: 1e7,
}),
},
};
const pose = (heading) => {
const camera = scene.camera;
camera.heading = heading;
camera.rightWC = new Cesium.Cartesian3(
0,
Math.cos(heading),
-Math.sin(heading),
);
camera.upWC = new Cesium.Cartesian3(
0,
Math.sin(heading),
Math.cos(heading),
);
camera.viewMatrix = Cesium.Matrix4.computeView(
cameraPosition,
new Cesium.Cartesian3(-1, 0, 0),
camera.upWC,
camera.rightWC,
new Cesium.Matrix4(),
);
};
pose(0);
const make = (key, courseDeg, bearing) => ({
key,
courseDeg,
record: { bearing },
sample: { segmentCourseDeg: NaN },
marker: { position, rotation: 0.7, show: true },
});
const held = make('held', 90, 270);
const reportedOnly = make('reported', null, 90);
const unknown = make('unknown', null, null);
const hidden = make('hidden', 90, 90);
const state = {
_viewer: { scene },
_enabled: true,
_vehicles: new Map(
[held, reportedOnly, unknown, hidden].map((e) => [e.key, e]),
),
_visible: new Set([held, reportedOnly, unknown]),
_moving: new Set(),
_heightDirty: new Set(),
_rotationAt: 0,
_rotationRevision: -1,
_cameraRevision: 0,
_rotationDirty: false,
};
const noop = () => {};
const rendering = createRendering({
state,
services: {
render: {
governorRequestRender: noop,
holdContinuousRender: noop,
releaseContinuousRender: noop,
},
},
parts: { height: { nearGround: () => false }, trails: { update: noop } },
});
rendering.onPreRender();
const before = [held.marker.rotation, reportedOnly.marker.rotation];
pose(Math.PI / 2);
t.mock.timers.tick(100);
rendering.onPreRender();
assert.equal(
held.marker.rotation,
before[0],
'no rotation pass before 200 ms',
);
t.mock.timers.tick(100);
rendering.onPreRender();
for (const [i, entry] of [held, reportedOnly].entries()) {
const delta = Math.atan2(
Math.sin(entry.marker.rotation - before[i]),
Math.cos(entry.marker.rotation - before[i]),
);
assert.ok(
Math.abs(delta - Math.PI / 2) < 0.01,
`${entry.key}: rotation delta ${delta}`,
);
assert.equal(entry.courseDeg, 90);
}
assert.equal(
unknown.marker.rotation,
0,
'no course means screen-up, never a stale angle',
);
assert.equal(
hidden.marker.rotation,
0.7,
'hidden vehicles are not projected',
);
state._selectedKey = unknown.key;
unknown.marker.rotation = 0.7;
rendering.onPreRender();
assert.equal(
unknown.marker.rotation,
0,
'selected unknown course also stays screen-up',
);
});