mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled worldwide name index; wide views show named points. ALPR reads an hourly OpenStreetMap extract. Cockpit place context and country, state and US county outlines use bundled Natural Earth and US Census data. Overpass is used only when an operator configures their own endpoint. Also: camera arrivals no longer land below ground, keyless terrain retries on 429, bounded relation geometry for configured Overpass, one shared OpenStreetMap credit, and separate tile and API transports for embedders. Co-authored-by: theGaryLarson <[email protected]> Co-authored-by: Milan Khanal <[email protected]>
378 lines
14 KiB
JavaScript
378 lines
14 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { readLayerSource } from '../testSupport/readLayerSource.mjs';
|
|
import test from 'node:test';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import { getTrafficTimingDiagnostics } from './traffic.js';
|
|
|
|
const SOURCE = readLayerSource(new URL('./traffic.js', import.meta.url), 'utf8').replace(/parts\.(?:model|timing)\./g, '');
|
|
|
|
function functionBody(name) {
|
|
const declaration = `function ${name}(`;
|
|
const start = SOURCE.indexOf(declaration);
|
|
assert.notEqual(start, -1, `${declaration} must exist`);
|
|
const signatureStart = SOURCE.indexOf('(', start);
|
|
let signatureDepth = 0;
|
|
let signatureEnd = -1;
|
|
for (let i = signatureStart; i < SOURCE.length; i++) {
|
|
if (SOURCE[i] === '(') signatureDepth++;
|
|
if (SOURCE[i] === ')' && --signatureDepth === 0) {
|
|
signatureEnd = i;
|
|
break;
|
|
}
|
|
}
|
|
assert.notEqual(signatureEnd, -1, `${name} signature must terminate`);
|
|
const open = SOURCE.indexOf('{', signatureEnd);
|
|
let depth = 0;
|
|
let quote = null;
|
|
let escaped = false;
|
|
let lineComment = false;
|
|
let blockComment = false;
|
|
for (let i = open; i < SOURCE.length; i++) {
|
|
const char = SOURCE[i];
|
|
const next = SOURCE[i + 1];
|
|
if (lineComment) {
|
|
if (char === '\n') lineComment = false;
|
|
continue;
|
|
}
|
|
if (blockComment) {
|
|
if (char === '*' && next === '/') {
|
|
blockComment = false;
|
|
i++;
|
|
}
|
|
continue;
|
|
}
|
|
if (quote) {
|
|
if (escaped) {
|
|
escaped = false;
|
|
} else if (char === '\\') {
|
|
escaped = true;
|
|
} else if (char === quote) {
|
|
quote = null;
|
|
}
|
|
continue;
|
|
}
|
|
if (char === '/' && next === '/') {
|
|
lineComment = true;
|
|
i++;
|
|
continue;
|
|
}
|
|
if (char === '/' && next === '*') {
|
|
blockComment = true;
|
|
i++;
|
|
continue;
|
|
}
|
|
if (char === '\'' || char === '"' || char === '`') {
|
|
quote = char;
|
|
continue;
|
|
}
|
|
if (char === '{') depth++;
|
|
if (char === '}' && --depth === 0) return SOURCE.slice(open + 1, i);
|
|
}
|
|
throw new Error(`unterminated ${name} body`);
|
|
}
|
|
|
|
function canonicalSemanticBody(body) {
|
|
return body
|
|
.replace(/\/\* TRACE_ONLY_BEGIN \*\/[\s\S]*?\/\* TRACE_ONLY_END \*\//g, '')
|
|
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
.replace(/\/\/[^\n]*/g, '')
|
|
.replace(/\s+/g, '')
|
|
.replace(
|
|
'if(!overpassData||!overpassData.elements){return[];}',
|
|
'if(!overpassData||!overpassData.elements)return[];',
|
|
);
|
|
}
|
|
|
|
function traceOnlyBlocks(body) {
|
|
return [...body.matchAll(
|
|
/\/\* TRACE_ONLY_BEGIN \*\/([\s\S]*?)\/\* TRACE_ONLY_END \*\//g,
|
|
)].map((match) => match[1]);
|
|
}
|
|
|
|
function eventChannel() {
|
|
const listeners = new Set();
|
|
return {
|
|
addEventListener(listener) {
|
|
listeners.add(listener);
|
|
return () => listeners.delete(listener);
|
|
},
|
|
removeEventListener(listener) {
|
|
listeners.delete(listener);
|
|
},
|
|
raise() {
|
|
for (const listener of [...listeners]) listener();
|
|
},
|
|
listenerCount() {
|
|
return listeners.size;
|
|
},
|
|
};
|
|
}
|
|
|
|
test('traffic timing stays inert when the DEV flag is off under bare Node', () => {
|
|
assert.deepEqual(getTrafficTimingDiagnostics(), {
|
|
enabled: false,
|
|
marksInstalled: 0,
|
|
traceObjectsCreated: 0,
|
|
uncorrelatedTracesDropped: 0,
|
|
});
|
|
assert.equal(
|
|
performance.getEntriesByType('mark').filter((entry) => entry.name.startsWith('traffic:')).length,
|
|
0,
|
|
);
|
|
});
|
|
|
|
test('traffic timing pairs real ordering to the scheduling change and guards re-arms', async () => {
|
|
const { createServer } = await import('vite');
|
|
const originalWindow = globalThis.window;
|
|
const originalDocument = globalThis.document;
|
|
const originalFetch = globalThis.fetch;
|
|
const originalSetTimeout = globalThis.setTimeout;
|
|
const originalClearTimeout = globalThis.clearTimeout;
|
|
const originalSetInterval = globalThis.setInterval;
|
|
const originalClearInterval = globalThis.clearInterval;
|
|
const originalLog = console.log;
|
|
const originalWarn = console.warn;
|
|
const timeouts = new Map();
|
|
let timerId = 0;
|
|
let server;
|
|
let trafficLayer;
|
|
let viewer;
|
|
|
|
const pendingDebounce = () => {
|
|
const matches = [...timeouts].filter(([, timer]) => timer.delay === 320);
|
|
assert.equal(matches.length, 1, 'exactly one traffic debounce must be pending');
|
|
return matches[0];
|
|
};
|
|
|
|
const runDebouncedLoad = async () => {
|
|
const match = pendingDebounce();
|
|
const [id, timer] = match;
|
|
timeouts.delete(id);
|
|
await timer.callback();
|
|
};
|
|
|
|
try {
|
|
globalThis.window = {
|
|
location: { search: '?trafficDebug=1' },
|
|
addEventListener() {},
|
|
};
|
|
server = await createServer({
|
|
root: fileURLToPath(new URL('../..', import.meta.url)),
|
|
configFile: false,
|
|
appType: 'custom',
|
|
logLevel: 'silent',
|
|
server: { middlewareMode: true },
|
|
plugins: [{
|
|
name: 'traffic-timing-test-hooks',
|
|
transform(code, id) {
|
|
if (!id.endsWith('/src/layers/traffic/index.js')) return null;
|
|
return code.replace('return Object.assign(', 'return Object.assign({ __trafficTimingTestHooks: { currentAnchor: () => state._trafficTimingCurrentAnchor } },');
|
|
},
|
|
}],
|
|
});
|
|
const traffic = await server.ssrLoadModule('/src/data/traffic.js');
|
|
trafficLayer = traffic.default;
|
|
|
|
globalThis.document = { documentElement: { dataset: {} } };
|
|
globalThis.fetch = async (url) => ({
|
|
ok: true,
|
|
status: 200,
|
|
headers: { get: () => null },
|
|
json: async () => (String(url).includes('/api/tomtom/status')
|
|
? { hasKey: false }
|
|
: { elements: [] }),
|
|
});
|
|
globalThis.setTimeout = (callback, delay) => {
|
|
const id = ++timerId;
|
|
timeouts.set(id, { callback, delay });
|
|
return id;
|
|
};
|
|
globalThis.clearTimeout = (id) => timeouts.delete(id);
|
|
globalThis.setInterval = () => ++timerId;
|
|
globalThis.clearInterval = () => {};
|
|
console.log = () => {};
|
|
console.warn = () => {};
|
|
|
|
const preRender = eventChannel();
|
|
const postRender = eventChannel();
|
|
const moveEnd = eventChannel();
|
|
const changed = eventChannel();
|
|
let longitude = -97.74;
|
|
const camera = {
|
|
moveEnd,
|
|
changed,
|
|
percentageChanged: 0.5,
|
|
positionCartographic: { latitude: Math.PI / 6, longitude: 0, height: 5000 },
|
|
computeViewRectangle() {
|
|
const west = longitude * Math.PI / 180;
|
|
return {
|
|
south: 30 * Math.PI / 180,
|
|
west,
|
|
north: 30.02 * Math.PI / 180,
|
|
east: west + 0.02 * Math.PI / 180,
|
|
};
|
|
},
|
|
};
|
|
const setLongitude = (next) => {
|
|
longitude = next;
|
|
camera.positionCartographic.longitude = next * Math.PI / 180;
|
|
};
|
|
setLongitude(longitude);
|
|
viewer = {
|
|
camera,
|
|
scene: {
|
|
canvas: { clientWidth: 0, clientHeight: 0, width: 0, height: 0 },
|
|
preRender,
|
|
postRender,
|
|
primitives: { add: (primitive) => primitive, remove: () => true },
|
|
},
|
|
};
|
|
|
|
performance.clearMarks();
|
|
performance.clearMeasures();
|
|
trafficLayer.init(viewer);
|
|
performance.mark('traffic:stale:mark');
|
|
performance.measure('traffic:stale:measure', {
|
|
start: 'traffic:stale:mark',
|
|
end: 'traffic:stale:mark',
|
|
});
|
|
trafficLayer.enable(viewer);
|
|
assert.equal(traffic.getTrafficTimingDiagnostics().marksInstalled, 1);
|
|
assert.equal(moveEnd.listenerCount(), 2, 'one timing listener plus the production arrival check');
|
|
assert.equal(performance.getEntriesByName('traffic:stale:mark').length, 0);
|
|
assert.equal(performance.getEntriesByName('traffic:stale:measure').length, 0);
|
|
|
|
// Real Cesium ordering: changed arms the 320 ms debounce, its load fires,
|
|
// and moveEnd arrives only after cameraEventWaitTime (~500 ms).
|
|
setLongitude(-97.72);
|
|
changed.raise();
|
|
const anchorA = traffic.default.__trafficTimingTestHooks.currentAnchor();
|
|
const anchorMarkA = performance.getEntriesByType('mark').find((entry) => (
|
|
entry.detail?.segment === 'last-camera-change'
|
|
&& entry.detail?.interactionId === anchorA.interactionId
|
|
));
|
|
assert.ok(anchorMarkA, 'the scheduling camera change must be marked');
|
|
assert.equal(anchorMarkA.startTime, anchorA.timestamp);
|
|
await runDebouncedLoad();
|
|
assert.deepEqual(traffic.getTrafficTimingDiagnostics(), {
|
|
enabled: true,
|
|
marksInstalled: 1,
|
|
traceObjectsCreated: 1,
|
|
uncorrelatedTracesDropped: 0,
|
|
});
|
|
assert.equal(traffic.default.__trafficTimingTestHooks.currentAnchor(), null);
|
|
const fetchFromA = performance.getEntriesByType('measure').find((entry) => (
|
|
entry.detail?.segment === 'last-camera-change-to-fetch-start'
|
|
&& entry.detail?.interactionId === anchorA.interactionId
|
|
));
|
|
assert.ok(fetchFromA, 'the load must pair to A without waiting for moveEnd');
|
|
assert.equal(fetchFromA.startTime, anchorA.timestamp);
|
|
assert.equal(fetchFromA.detail.cameraChangeTimestamp, anchorA.timestamp);
|
|
|
|
moveEnd.raise();
|
|
const diagnosticMoveEnd = performance.getEntriesByType('mark').find((entry) => (
|
|
entry.detail?.segment === 'camera-move-end'
|
|
));
|
|
assert.ok(diagnosticMoveEnd, 'the late moveEnd diagnostic mark must still be emitted');
|
|
assert.equal(diagnosticMoveEnd.detail.diagnosticOnly, true);
|
|
assert.equal(diagnosticMoveEnd.detail.fetchWaitsForMoveEnd, false);
|
|
assert.ok(
|
|
diagnosticMoveEnd.startTime >= fetchFromA.startTime + fetchFromA.duration,
|
|
'the real-ordering pin requires fetch-start to precede late moveEnd',
|
|
);
|
|
|
|
// Re-arm B with C before B fires. clearTimeout cancels B in normal event-
|
|
// loop ordering; invoking the saved callback models an already-queued race
|
|
// and must count one drop without consuming C's current anchor.
|
|
setLongitude(-97.70);
|
|
changed.raise();
|
|
const anchorB = traffic.default.__trafficTimingTestHooks.currentAnchor();
|
|
const [timerBId, timerB] = pendingDebounce();
|
|
setLongitude(-97.68);
|
|
changed.raise();
|
|
const anchorC = traffic.default.__trafficTimingTestHooks.currentAnchor();
|
|
assert.notEqual(anchorC.interactionId, anchorB.interactionId);
|
|
assert.equal(timeouts.has(timerBId), false, 'the ordinary debounce path must cancel B');
|
|
await timerB.callback();
|
|
assert.equal(
|
|
traffic.default.__trafficTimingTestHooks.currentAnchor().interactionId,
|
|
anchorC.interactionId,
|
|
'a stale callback must not consume the newer C anchor',
|
|
);
|
|
await runDebouncedLoad();
|
|
|
|
assert.deepEqual(traffic.getTrafficTimingDiagnostics(), {
|
|
enabled: true,
|
|
marksInstalled: 1,
|
|
traceObjectsCreated: 2,
|
|
uncorrelatedTracesDropped: 1,
|
|
});
|
|
assert.ok(performance.getEntriesByType('measure').some((entry) => (
|
|
entry.detail?.interactionId === anchorC.interactionId
|
|
)));
|
|
assert.ok(performance.getEntriesByType('measure').every((entry) => (
|
|
entry.detail?.interactionId !== anchorB.interactionId
|
|
)), 'the canceled/stale B load must never emit a correlated trace');
|
|
} finally {
|
|
trafficLayer?.disable(viewer);
|
|
await server?.close();
|
|
globalThis.window = originalWindow;
|
|
globalThis.document = originalDocument;
|
|
globalThis.fetch = originalFetch;
|
|
globalThis.setTimeout = originalSetTimeout;
|
|
globalThis.clearTimeout = originalClearTimeout;
|
|
globalThis.setInterval = originalSetInterval;
|
|
globalThis.clearInterval = originalClearInterval;
|
|
console.log = originalLog;
|
|
console.warn = originalWarn;
|
|
performance.clearMarks();
|
|
performance.clearMeasures();
|
|
}
|
|
});
|
|
|
|
test('the timed parser stays source-equivalent to the production parser', () => {
|
|
assert.equal(
|
|
canonicalSemanticBody(functionBody('parseRoadsTimed')),
|
|
canonicalSemanticBody(functionBody('parseRoads')),
|
|
'parseRoadsTimed may add only TRACE_ONLY blocks; production operations and order must match',
|
|
);
|
|
|
|
const traceBlocks = traceOnlyBlocks(functionBody('parseRoadsTimed'));
|
|
assert.ok(traceBlocks.length > 0, 'parseRoadsTimed must retain explicit trace-only blocks');
|
|
const traceSource = traceBlocks.join('\n');
|
|
// This is deliberately a reasonable lexical tripwire, not an AST proof: it
|
|
// recognizes direct assignments and common mutator calls, but cannot prove
|
|
// safety through aliases or computed properties. Source equivalence above
|
|
// remains the independent operation-order backstop.
|
|
const assignedIdentifiers = [...traceSource.matchAll(
|
|
/\b([A-Za-z_$][\w$]*)\s*(?:\+\+|--|\+=|-=|\*=|\/=|%=|=(?!=|>))/g,
|
|
)].map((match) => match[1]);
|
|
const mutatedIdentifiers = [...traceSource.matchAll(
|
|
/\b([A-Za-z_$][\w$]*)\.(?:add|delete|clear|set|push|pop|shift|unshift|splice|sort|reverse|copyWithin|fill)\s*\(/g,
|
|
)].map((match) => match[1]);
|
|
const unsafeWrites = [...assignedIdentifiers, ...mutatedIdentifiers]
|
|
.filter((identifier) => identifier !== 'trace' && !identifier.startsWith('_trafficTiming'));
|
|
assert.deepEqual(
|
|
unsafeWrites,
|
|
[],
|
|
'TRACE_ONLY blocks may write only trace or _trafficTiming* instrumentation state',
|
|
);
|
|
assert.doesNotMatch(
|
|
traceSource,
|
|
/\b(?:roads|coords|waypoints|segmentDist)\s*(?:\.(?:add|delete|clear|set|push|pop|shift|unshift|splice|sort|reverse|copyWithin|fill)\s*\(|\+\+|--|\+=|-=|\*=|\/=|%=|=(?!=|>))/,
|
|
'TRACE_ONLY blocks must not mutate parser production collections',
|
|
);
|
|
|
|
assert.doesNotMatch(SOURCE, /function fetchRoadsTimed\s*\(/);
|
|
assert.doesNotMatch(SOURCE, /function renderRoadsForAltitudeTimed\s*\(/);
|
|
const fetchBody = canonicalSemanticBody(functionBody('readRoads'));
|
|
assert.equal(fetchBody.includes('response.text()'), false);
|
|
assert.equal(fetchBody.includes('JSON.parse('), false);
|
|
const okCheck = fetchBody.indexOf('if(!response.ok)');
|
|
const responseJson = fetchBody.indexOf('response.json()');
|
|
assert.notEqual(okCheck, -1);
|
|
assert.notEqual(responseJson, -1);
|
|
assert.ok(okCheck < responseJson);
|
|
});
|