Files
gods-eye-view/scripts/qa-overpass-offload.mjs
T

1512 lines
53 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* qa-overpass-offload.mjs — keyless/keyed source replacement acceptance gate.
*
* Run: node scripts/qa-overpass-offload.mjs http://localhost:4173
* node scripts/qa-overpass-offload.mjs --url http://localhost:4173
*
* Flies to Austin at 2 km, enables Street Traffic, Mapped Installations and
* ALPR, then checks rendered road dots, ALPR entities, and military markers
* and polygon outlines near Camp Mabry, Fort Cavazos marker deduplication,
* and at least 150 street-view dots within -3/+25 m of the surface. Records the traffic road-source label
* and rejects every browser request to an Overpass host or public Nominatim.
* Server-side zero egress is separately pinned in src/overpassOffload.test.mjs.
* --tour measures Austin, London, Dubai, San Diego, Tokyo and São Paulo, then
* revisits Austin, with all three layers enabled. CDP records requests, decoded
* response-body bytes and transferred bytes (including headers), split by provider.
* Uses real tile responses. A second isolated page overrides only TomTom key
* availability to check OpenFreeMap roads on the Google mesh. The default run
* also draws an area annotation and requires zero console errors and, when no
* Overpass instance is configured, zero /api/overpass queries.
* --compare captures the TomTom, OpenStreetMap and Hybrid road sources at five
* fixed tilted photoreal views (Twin Peaks, Lombard Street, Loop 360, Dubai,
* Shibuya): first-dot ms, dots in view, dots within -3/+25 m of the rendered
* surface, floaters and sinkers, with qa-shots/compare-<view>-<mode>.png.
* Screenshots and a JSON result go to qa-shots/ (gitignored). Exits nonzero on
* a failed assertion. Works against keyed or keyless dev servers.
*/
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import path from 'node:path';
import puppeteer from 'puppeteer';
import { tilesForBounds } from '../src/data/tomtomTiles.js';
import { trafficDetailBounds } from '../src/layers/traffic/source.js';
const args = process.argv.slice(2);
if (args.includes('--names')) {
await import('./qa-military-names.mjs');
process.exit(0);
}
if (args.includes('--help') || args.includes('-h')) {
console.log(
'Usage: node scripts/qa-overpass-offload.mjs [--url] <dev-server-url> [--headful] [--software-gl] [--tour | --pan | --compare | --profile | --coverage | --names]\nChecks street-level mesh alignment for OpenFreeMap roads with/without TomTom flow, ALPR, Camp Mabry, Fort Cavazos, source labels and zero external Overpass/Nominatim requests. With --tour, measures a six-city session plus Austin revisit. The --pan mode checks twenty moves and the --profile mode measures cold/warm phases; --coverage checks the London ALPR row. The --compare mode captures three road sources at five identical tilted photoreal views. All modes record per-view provider requests/bytes and cache hits in qa-shots/overpass-offload-result.json (also a mode-specific result). Writes qa-shots/. Uses platform ANGLE by default; --software-gl opts into slower SwiftShader.',
);
process.exit(0);
}
const url = args.includes('--url')
? args[args.indexOf('--url') + 1]
: args.find((arg) => !arg.startsWith('--'));
if (!url || !['http:', 'https:'].includes(new URL(url).protocol))
throw new Error('Supply a running dev server URL; see --help');
const compare = args.includes('--compare');
const tour = args.includes('--tour');
const pan = args.includes('--pan');
const profile = args.includes('--profile');
const coverage = args.includes('--coverage');
const shots = path.resolve('qa-shots');
await fs.mkdir(shots, { recursive: true });
const browser = await puppeteer.launch({
headless: !args.includes('--headful'),
protocolTimeout: 300_000,
...(process.env.PUPPETEER_EXECUTABLE_PATH
? { executablePath: process.env.PUPPETEER_EXECUTABLE_PATH }
: {}),
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--use-gl=angle',
...(args.includes('--software-gl')
? ['--use-angle=swiftshader', '--enable-unsafe-swiftshader']
: []),
'--disable-dev-shm-usage',
'--disable-background-timer-throttling',
'--disable-renderer-backgrounding',
],
});
const result = {
url,
mode: compare
? 'compare'
: tour
? 'tour'
: pan
? 'pan'
: profile
? 'profile'
: coverage
? 'coverage'
: 'acceptance',
forbiddenRequests: [],
overpassRequests: [],
errors: [],
consoleErrors: [],
failedRequests: [],
traffic: null,
alpr: null,
military: null,
screenshots: [],
loadTimes: {},
};
// Assign each request to the view active when it started, even if it finishes later.
// Record both decoded body bytes and CDP transfer bytes; cached bodies are not egress.
const categories = [
'ofmTileJSON',
'ofmTiles',
'alprTileJSON',
'alprTiles',
'tomtom',
'api',
];
const requests = [];
const views = [];
let currentView;
function beginView(name, camera = null) {
currentView = { name, camera, startedAt: new Date().toISOString() };
views.push(currentView);
return currentView;
}
function categoryFor(target) {
if (target.hostname === 'tiles.openfreemap.org')
return target.pathname.endsWith('.pbf') ? 'ofmTiles' : 'ofmTileJSON';
if (
['tiles.dontgetflocked.com', 'data.dontgetflocked.com'].includes(
target.hostname,
)
)
return target.pathname.endsWith('.json') ? 'alprTileJSON' : 'alprTiles';
if (
target.origin === new URL(url).origin &&
target.pathname.startsWith('/api/tomtom/')
)
return 'tomtom';
if (
target.origin === new URL(url).origin &&
target.pathname.startsWith('/api/')
)
return 'api';
return null;
}
async function observeNetwork(targetPage) {
const client = await targetPage.createCDPSession();
const pending = new Map();
await client.send('Network.enable');
client.on('Network.requestWillBeSent', (event) => {
const target = new URL(event.request.url);
if (
target.origin === new URL(url).origin &&
target.pathname.startsWith('/api/overpass')
)
result.overpassRequests.push(
`${event.request.method} ${target.pathname}`,
);
if (
target.hostname.includes('overpass') ||
target.hostname === 'nominatim.openstreetmap.org'
)
result.forbiddenRequests.push(target.origin + target.pathname);
const category = categoryFor(target);
if (!category) return;
const entry = {
view: currentView.name,
category,
target: target.origin + target.pathname,
flowTile: target.pathname.startsWith('/api/tomtom/flow/'),
responseBytes: 0,
transferBytes: 0,
cached: false,
finished: false,
};
requests.push(entry);
pending.set(event.requestId, entry);
});
client.on('Network.requestServedFromCache', ({ requestId }) => {
const entry = pending.get(requestId);
if (entry) entry.cached = true;
});
client.on('Network.responseReceived', ({ requestId, response }) => {
const entry = pending.get(requestId);
if (!entry) return;
entry.status = response.status;
entry.cached ||= Boolean(
response.fromDiskCache ||
response.fromServiceWorker ||
response.fromPrefetchCache,
);
entry.transferBytes = response.encodedDataLength || 0;
});
client.on(
'Network.dataReceived',
({ requestId, dataLength, encodedDataLength }) => {
const entry = pending.get(requestId);
if (!entry) return;
entry.responseBytes += dataLength;
entry.transferBytes += encodedDataLength;
},
);
client.on('Network.loadingFinished', ({ requestId, encodedDataLength }) => {
const entry = pending.get(requestId);
if (!entry) return;
entry.transferBytes = entry.cached ? 0 : encodedDataLength;
entry.finished = true;
pending.delete(requestId);
});
client.on('Network.loadingFailed', ({ requestId, errorText }) => {
const entry = pending.get(requestId);
if (!entry) return;
entry.failure = errorText;
entry.finished = true;
pending.delete(requestId);
});
}
function footprintSummary() {
function summarize(entries) {
const totals = Object.fromEntries(
categories.map((category) => [
category,
{
requests: 0,
responseBytes: 0,
transferBytes: 0,
cacheHits: 0,
failures: 0,
unfinished: 0,
},
]),
);
for (const entry of entries) {
const count = totals[entry.category];
count.requests++;
count.responseBytes += entry.responseBytes;
count.transferBytes += entry.transferBytes;
count.cacheHits += Number(entry.cached);
count.failures += Number(Boolean(entry.failure) || entry.status >= 400);
count.unfinished += Number(!entry.finished);
}
totals.total = Object.fromEntries(
Object.keys(totals[categories[0]]).map((key) => [
key,
categories.reduce((sum, category) => sum + totals[category][key], 0),
]),
);
return totals;
}
const measured = views.map((view) => ({
...view,
...summarize(requests.filter((r) => r.view === view.name)),
}));
const total = summarize(requests);
const revisit = measured.find((view) => view.name === 'austin-revisit');
const summary = {
byteUnits:
'MB = 1,000,000 bytes; responseBytes = decoded body; transferBytes = CDP encoded transfer including headers, zero for browser cache hits',
scope:
'Browser requests only; TomTom counts local /api responses, not upstream calls behind the server cache/budget. Fresh browser session; existing server caches retained.',
views: measured,
tourTable: measured
.filter((v) => v.surface)
.map((v) => ({
city: v.name,
firstDotSeconds: v.firstDotsMs / 1000,
warmFirstDotSeconds: v.warmFirstDotsMs / 1000,
dotsInView: v.surface.inView,
onMesh: v.surface.onMesh,
ofmRequests: v.ofmTiles.requests,
ofmMB: v.ofmTiles.responseBytes / 1e6,
})),
total,
revisitDelta: revisit
? {
openFreeMapRequests:
revisit.ofmTileJSON.requests + revisit.ofmTiles.requests,
openFreeMapResponseBytes:
revisit.ofmTileJSON.responseBytes + revisit.ofmTiles.responseBytes,
openFreeMapTransferBytes:
revisit.ofmTileJSON.transferBytes + revisit.ofmTiles.transferBytes,
}
: null,
requests,
};
const cell = (value) =>
`${value.requests} / ${(value.responseBytes / 1e6).toFixed(3)}`;
summary.table = [...measured, { name: 'TOTAL', ...total }].map((view) => ({
view: view.name,
'OFM JSON req/MB': cell(view.ofmTileJSON),
'OFM tiles req/MB': cell(view.ofmTiles),
'ALPR JSON req/MB': cell(view.alprTileJSON),
'ALPR tiles req/MB': cell(view.alprTiles),
'TomTom req/MB': cell(view.tomtom),
'Total req/MB': cell(view.total),
'Transfer MB': (view.total.transferBytes / 1e6).toFixed(3),
'Cache hits': view.total.cacheHits,
Failures: view.total.failures,
}));
console.table(summary.table);
console.log('Austin revisit delta:', summary.revisitDelta);
if (summary.tourTable.length) console.table(summary.tourTable);
return summary;
}
let page;
try {
page = await browser.newPage();
await page.evaluateOnNewDocument(() => {
sessionStorage.setItem('gev:first-run-mission-session:v1', 'dismissed');
});
await page.setViewport({ width: 1440, height: 1000 });
beginView('startup');
await observeNetwork(page);
page.on('pageerror', (error) => result.errors.push(error.message));
page.on('console', (message) => {
if (message.type() !== 'error') return;
// A keyless server does not offer optional Google tokens. Keep every other
// HTTP or JavaScript error visible, including failures of this route other
// than its expected capability-miss 404.
const optionalTokenMiss =
message.location().url === new URL('/api/google/tiles-token', url).href &&
/^Failed to load resource: the server responded with a status of 404\b/.test(
message.text(),
);
if (!optionalTokenMiss && result.consoleErrors.length < 30)
result.consoleErrors.push(message.text());
});
page.on('requestfailed', (request) => {
if (result.failedRequests.length < 30) {
const target = new URL(request.url());
result.failedRequests.push({
target: target.origin + target.pathname,
error: request.failure()?.errorText,
});
}
});
console.log('Loading viewer...');
const navigationUrl = new URL(url);
navigationUrl.searchParams.set('welcome', '0');
navigationUrl.searchParams.set('trafficDebug', '1');
await page.goto(navigationUrl.href, {
waitUntil: 'domcontentloaded',
timeout: 60_000,
});
await page.waitForFunction(
() => window.__godsEyeView?.viewer && window.__godsEyeView?.dataManager,
{ timeout: 60_000, polling: 500 },
);
await page.waitForFunction(
() =>
document.getElementById('loading-screen')?.classList.contains('hidden'),
{ timeout: 60_000, polling: 250 },
);
// Startup restoration owns its camera flight until the loading cover is gone.
await page.evaluate(
() => window.__godsEyeView.styleManager.initialRestorePromise,
);
await page.evaluate(() =>
window.__godsEyeView.styleManager.setDetection({ enabled: false }),
);
result.renderer = await page.evaluate(() => {
const gl = window.__godsEyeView.viewer.scene.context._gl;
const debug = gl.getExtension('WEBGL_debug_renderer_info');
return debug
? gl.getParameter(debug.UNMASKED_RENDERER_WEBGL)
: gl.getParameter(gl.RENDERER);
});
await page.keyboard.press('Escape');
await page.waitForFunction(
() => !document.querySelector('#first-run-launcher:not([hidden])'),
{ timeout: 10_000 },
);
async function fly(lat, lon, height = 2000, heading = 0, pitch = -75) {
await page.evaluate(
async (view) => {
const { viewer } = window.__godsEyeView;
viewer.camera.cancelFlight();
await new Promise((resolve) =>
viewer.camera.flyTo({
destination: viewer.scene.globe.ellipsoid.cartographicToCartesian({
latitude: (view.lat * Math.PI) / 180,
longitude: (view.lon * Math.PI) / 180,
height: view.height,
}),
orientation: {
heading: (view.heading * Math.PI) / 180,
pitch: (view.pitch * Math.PI) / 180,
roll: 0,
},
duration: 0,
complete: resolve,
cancel: resolve,
}),
);
},
{ lat, lon, height, heading, pitch },
);
}
async function settleTiles() {
await page.waitForFunction(
() => {
const { scene } = window.__godsEyeView.viewer;
if (scene.globe.show) return scene.globe.tilesLoaded;
let found = false;
for (let i = 0; i < scene.primitives.length; i++) {
const primitive = scene.primitives.get(i);
if (!primitive.show || typeof primitive.tilesLoaded !== 'boolean')
continue;
found = true;
if (!primitive.tilesLoaded) return false;
}
return found;
},
{ timeout: 90_000, polling: 500 },
);
}
async function shot(name) {
await settleTiles();
assert.equal(
await page.evaluate(
() => !document.querySelector('#first-run-launcher:not([hidden])'),
),
true,
'first-launch modal dismissed',
);
await page.evaluate(async () => {
window.__godsEyeView.requestRender('qa-overpass-offload');
await new Promise((resolve) =>
requestAnimationFrame(() => requestAnimationFrame(resolve)),
);
});
const filename = `overpass-offload-${name}.png`;
await page.screenshot({ path: path.join(shots, filename) });
result.screenshots.push(filename);
}
async function enableTrafficTimed(name, allowEmpty = false) {
const milliseconds = await page.evaluate(
(allowEmpty) =>
new Promise((resolve, reject) => {
const { viewer, dataManager } = window.__godsEyeView;
const start = performance.now();
const cleanup = () => {
remove();
clearTimeout(timer);
};
const remove = viewer.scene.postRender.addEventListener(() => {
const stats = dataManager.layers.get('traffic').module.getStats();
if (stats.count <= 0) {
if (
allowEmpty &&
!stats.loading &&
stats.lastUpdate &&
performance.now() - start > 500
) {
cleanup();
resolve(null);
}
return;
}
const elapsed = performance.now() - start;
cleanup();
resolve(elapsed);
});
const timer = setTimeout(() => {
cleanup();
reject(new Error('No traffic dots within 180 seconds'));
}, 180_000);
Promise.resolve(dataManager.setEnabled('traffic', true)).catch(
(error) => {
cleanup();
reject(error);
},
);
}),
allowEmpty,
);
result.loadTimes[name] =
milliseconds === null ? null : Math.round(milliseconds);
console.log(
`${name}: ${result.loadTimes[name]} ms from enable to first rendered dots`,
);
}
async function waitForSources() {
// Let camera-move debounces fire before trusting the previous view's ready state.
await new Promise((resolve) => setTimeout(resolve, 1500));
const deadline = Date.now() + 180_000;
let quietSince = null;
while (Date.now() < deadline) {
const states = await page.evaluate(() =>
['traffic', 'military-installations', 'alpr-cameras'].map((id) => {
const layer = window.__godsEyeView.dataManager.layers.get(id);
return { id, enabled: layer.enabled, ...layer.module.getStats() };
}),
);
const busy =
states.some((state) => state.loading) ||
requests.some((r) => !r.finished);
if (busy) quietSince = null;
else quietSince ??= Date.now();
if (quietSince && Date.now() - quietSince >= 2000) return states;
await new Promise((resolve) => setTimeout(resolve, 250));
}
result.settleTimeout = {
view: currentView.name,
traffic: await page.evaluate(() =>
window.__godsEyeView.dataManager.layers
.get('traffic')
.module.getLoadingDiagnostics(),
),
unfinishedRequests: requests.filter((request) => !request.finished),
};
await fs.writeFile(
path.join(shots, `overpass-offload-${result.mode}-timeout.json`),
JSON.stringify(result.settleTimeout, null, 2),
);
throw new Error(`Sources did not settle for ${currentView.name}`);
}
async function measureSurface() {
return page.evaluate(async () => {
const C = await import('/node_modules/cesium/Build/Cesium/index.js');
const { viewer, dataManager } = window.__godsEyeView;
const { scene } = viewer;
const collections = [];
for (let i = 0; i < scene.primitives.length; i++) {
const p = scene.primitives.get(i);
if (Array.isArray(p._pointPrimitives) && p.show && p.length > 0)
collections.push(p);
}
let inView = 0,
onMesh = 0,
sampled = 0;
const deltas = [];
// Freeze the sampled positions: the animator continues while the bounded batches yield.
const positions = collections.flatMap((p) =>
Array.from({ length: p.length }, (_, i) => p.get(i))
.filter((p) => p.show)
.map((p) => C.Cartesian3.clone(p.position)),
);
for (const position of positions) {
const screen = C.SceneTransforms.worldToWindowCoordinates(
scene,
position,
);
if (
!screen ||
screen.x < 0 ||
screen.x >= scene.canvas.clientWidth ||
screen.y < 0 ||
screen.y >= scene.canvas.clientHeight
)
continue;
inView++;
const carto = C.Cartographic.fromCartesian(position);
const height = scene.globe.show
? scene.globe.getHeight(carto)
: scene.sampleHeight(carto, collections);
if (Number.isFinite(height) && Math.abs(height) <= 9000) {
sampled++;
const delta = carto.height - height;
deltas.push(delta);
if (delta >= -3 && delta <= 25) onMesh++;
}
if (inView % 24 === 0)
await new Promise((resolve) => setTimeout(resolve, 0));
}
deltas.sort((a, b) => a - b);
return {
inView,
onMesh,
sampled,
floaters: deltas.filter((delta) => delta > 25).length,
sinkers: deltas.filter((delta) => delta < -3).length,
unsampled: inView - sampled,
medianDeltaM: deltas[Math.floor(deltas.length / 2)],
stats: dataManager.layers.get('traffic').module.getStats(),
photoreal: !scene.globe.show,
};
});
}
async function tourCity(name, lat, lon, height = 450) {
const view = beginView(name, { lat, lon, height, heading: 0, pitch: -35 });
console.log(`Measuring ${name} at ${height} m...`);
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('traffic', false),
);
await fly(lat, lon, height, 0, -35);
await page.evaluate(() => performance.clearMeasures());
await enableTrafficTimed(name);
view.firstDotsMs = result.loadTimes[name];
for (const id of ['military-installations', 'alpr-cameras'])
await page.evaluate(
(layerId) => window.__godsEyeView.dataManager.setEnabled(layerId, true),
id,
);
view.layers = await waitForSources();
for (const state of view.layers) {
assert.equal(state.enabled, true, `${name}: ${state.id} enabled`);
assert.ok(!state.error, `${name}: ${state.id}: ${state.error}`);
}
const traffic = view.layers.find((layer) => layer.id === 'traffic');
assert.ok(traffic.count > 0, `${name}: road dots rendered`);
// Hybrid names OpenStreetMap alone where TomTom has no roads (Tokyo).
assert.ok(
(traffic.mode === 'live'
? ['TomTom + OpenStreetMap', 'OpenStreetMap']
: ['OpenStreetMap']
).includes(traffic.roadSource),
`${name}: road source ${traffic.roadSource}`,
);
view.roadSource = traffic.roadSource;
await shot(`tour-${name}`);
view.surface = await measureSurface();
view.phases = await page.evaluate(() =>
performance
.getEntriesByType('measure')
.filter((e) => e.name.startsWith('roads:'))
.map((e) => ({
name: e.name,
ms: e.duration,
at: e.startTime,
detail: e.detail,
})),
);
assert.ok(
view.surface.onMesh >= 150,
`${name}: >=150 on-mesh dots (got ${view.surface.onMesh})`,
);
assert.ok(
view.firstDotsMs <= (name.includes('revisit') ? 2500 : 4000),
`${name}: first-dot deadline (${view.firstDotsMs} ms)`,
);
view.layers = await waitForSources();
view.actualCamera = await page.evaluate(() => {
const camera = window.__godsEyeView.viewer.camera;
const position = camera.positionCartographic;
return {
lat: (position.latitude * 180) / Math.PI,
lon: (position.longitude * 180) / Math.PI,
height: position.height,
pitch: (camera.pitch * 180) / Math.PI,
};
});
assert.ok(
Math.abs(view.actualCamera.lat - lat) < 1e-6,
`${name}: fixed latitude`,
);
assert.ok(
Math.abs(view.actualCamera.lon - lon) < 1e-6,
`${name}: fixed longitude`,
);
assert.ok(
Math.abs(view.actualCamera.height - height) < 1,
`${name}: fixed altitude`,
);
assert.ok(
Math.abs(view.actualCamera.pitch + 35) < 0.01,
`${name}: fixed pitch`,
);
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('traffic', false),
);
await enableTrafficTimed(`${name}-warm`);
view.warmFirstDotsMs = result.loadTimes[`${name}-warm`];
assert.ok(
view.warmFirstDotsMs <= 2500,
`${name}: warm first-dot deadline (${view.warmFirstDotsMs} ms)`,
);
await waitForSources();
}
if (compare) {
// Same photoreal surface, camera and settle rules for every mode.
await page.evaluate(async () => {
const { viewer, dataManager } = window.__godsEyeView;
for (const [id, layer] of dataManager.layers)
if (layer.enabled) await dataManager.setEnabled(id, false);
viewer.scene.screenSpaceCameraController.enableCollisionDetection = false;
if (document.querySelector('#data-panel.collapsed'))
document.querySelector('[data-collapse-target="data-panel"]').click();
});
result.compare = [];
const locations = [
['twin-peaks', 37.7544, -122.4477, 350, -20],
['lombard', 37.8021, -122.4187, 250, -25],
['loop-360', 30.347, -97.799, 400, -20],
['dubai', 25.1972, 55.2744, 450, -35],
['shibuya', 35.6595, 139.7005, 450, -35],
];
const modes = ['hybrid', 'osm', 'tomtom'];
for (const [
index,
[name, lat, lon, height, pitch],
] of locations.entries()) {
// Rotate the order so every mode meets cold caches at some views;
// `order` records it (0 = first at that view).
const order = modes.map((_, i) => modes[(i + index) % modes.length]);
for (const mode of order) {
beginView(`${name}-${mode}`, { lat, lon, height, pitch, heading: 0 });
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('traffic', false),
);
await fly(lat, lon, height, 0, pitch);
await settleTiles();
await page.evaluate(
(roadMode) =>
window.__godsEyeView.dataManager.setLayerParams(
'traffic',
{ roadMode },
{ origin: 'user' },
),
mode,
);
await enableTrafficTimed(`${name}-${mode}`, true);
await waitForSources();
await settleTiles();
// Tile settling can start a height refinement pass; measure after it.
await waitForSources();
const surface = await measureSurface();
assert.equal(surface.stats.loading, false, `${name}/${mode}: settled`);
assert.equal(surface.photoreal, true, 'comparison runs on photoreal');
assert.equal(
surface.stats.roadMode,
surface.stats.mode === 'live' ? mode : 'osm',
);
const actualCamera = await page.evaluate(() => {
const camera = window.__godsEyeView.viewer.camera;
const p = camera.positionCartographic;
return {
lat: (p.latitude * 180) / Math.PI,
lon: (p.longitude * 180) / Math.PI,
height: p.height,
pitch: (camera.pitch * 180) / Math.PI,
heading: (camera.heading * 180) / Math.PI,
};
});
for (const [key, expected, tolerance] of [
['lat', lat, 1e-6],
['lon', lon, 1e-6],
['height', height, 1],
['pitch', pitch, 0.01],
])
assert.ok(
Math.abs(actualCamera[key] - expected) < tolerance,
`${name}/${mode}: fixed ${key}`,
);
const filename = `compare-${name}-${mode}.png`;
await settleTiles();
assert.equal(
await page.evaluate(
() => !document.querySelector('#first-run-launcher:not([hidden])'),
),
true,
'first-launch modal dismissed',
);
await page.evaluate(async () => {
window.__godsEyeView.requestRender('qa-overpass-offload');
await new Promise((resolve) =>
requestAnimationFrame(() => requestAnimationFrame(resolve)),
);
});
await page.screenshot({ path: path.join(shots, filename) });
result.screenshots.push(filename);
const entry = {
view: name,
mode,
order: order.indexOf(mode),
firstDotMs: result.loadTimes[`${name}-${mode}`],
dotsInView: surface.inView,
onSurface: surface.onMesh,
floaters: surface.floaters,
sinkers: surface.sinkers,
unsampled: surface.unsampled,
flowCoveragePct: surface.stats.flowCoveragePct,
source: surface.stats.roadSource,
status: surface.stats.loadingLabel,
camera: actualCamera,
screenshot: filename,
};
result.compare.push(entry);
console.log(JSON.stringify(entry));
assert.ok(
!surface.stats.error,
`${name}/${mode}: ${surface.stats.error}`,
);
}
}
// Drive the real row control and read durable state after the user action.
await page.waitForSelector(
'[data-layer-id="traffic"] [data-chip-id="roads-osm"]',
);
await page.click('[data-layer-id="traffic"] [data-chip-id="roads-osm"]');
await page.waitForFunction(
() =>
window.__godsEyeView.dataManager.layers
.get('traffic')
.module.getParams().roadMode === 'osm',
);
await page.waitForFunction(
() =>
JSON.parse(localStorage.getItem('gev:layer-state:v2'))?.o?.traffic
?.roadMode === 'osm',
);
result.rowControl = { clicked: 'osm', persisted: true };
await waitForSources();
console.table(
result.compare.map(
({
view,
mode,
order,
firstDotMs,
dotsInView,
onSurface,
floaters,
sinkers,
unsampled,
}) => ({
view,
mode,
order,
firstDotMs,
dotsInView,
onSurface,
floaters,
sinkers,
unsampled,
}),
),
);
} else if (coverage) {
// The shared London presentation gate below is also independently runnable.
} else if (profile) {
await fly(30.2672, -97.7431, 450, 0, -35);
await settleTiles();
for (const name of ['cold', 'warm']) {
beginView(name);
await page.evaluate(() => {
performance.clearMeasures();
window.__godsEyeView.dataManager.setEnabled('traffic', false);
});
await enableTrafficTimed(name);
await waitForSources();
result[name] = await page.evaluate(() =>
performance
.getEntriesByType('measure')
.filter((e) => e.name.startsWith('roads:'))
.map((e) => ({
name: e.name,
ms: e.duration,
at: e.startTime,
detail: e.detail,
})),
);
console.log(name, JSON.stringify(result[name]));
}
} else if (pan) {
const sessionBudget = 32; // <= 0.54% of the default 6,000/day upstream budget.
await page.evaluate(() => {
window.__godsEyeView.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false;
});
beginView('pan-start');
await fly(30.2672, -97.7431, 450, 0, -35);
await enableTrafficTimed('pan-start');
assert.ok(
result.loadTimes['pan-start'] <= 4000,
`Austin cold first dots <= 4 s (${result.loadTimes['pan-start']} ms)`,
);
await waitForSources();
const positions = [[0, 0]];
// Metres east/north: 10 small steps, 5 larger steps, then 5 exact returns.
const offsets = [
[200, 0],
[400, 0],
[400, 200],
[200, 200],
[0, 200],
[-200, 200],
[-200, 0],
[-200, -200],
[0, -200],
[200, -200],
[1700, -200],
[1700, 1300],
[200, 1300],
[-1300, 1300],
[-1300, -200],
];
const moves = offsets.map((p, i) => ({
p,
kind: i < 10 ? 'small' : 'large',
}));
for (const index of [0, 3, 8, 11, 15])
moves.push({
p: index === 0 ? [0, 0] : offsets[index - 1],
kind: 'return',
});
for (const [i, move] of moves.entries()) {
const [east, north] = move.p;
const lat = 30.2672 + north / 111320;
const lon =
-97.7431 + east / (111320 * Math.cos((30.2672 * Math.PI) / 180));
const view = beginView(`pan-${i + 1}-${move.kind}`, {
lat,
lon,
height: 450,
pitch: -35,
});
const before = new Set(
requests.filter((r) => r.category === 'ofmTiles').map((r) => r.target),
);
await fly(lat, lon, 450, 0, -35);
await settleTiles();
view.layers = await waitForSources();
const state = view.layers.find((r) => r.id === 'traffic');
const batch = requests.filter((r) => r.view === view.name);
const roadRequests = batch.filter((r) => r.category === 'ofmTiles');
if (move.kind === 'return')
assert.equal(
roadRequests.length,
0,
`${view.name}: cached return makes no OFM requests`,
);
for (const request of roadRequests)
assert.ok(
!before.has(request.target),
`${view.name}: no duplicate XYZ`,
);
if (move.kind === 'small' && roadRequests.length) {
const bounds = state.roadBounds;
assert.ok(bounds, 'admitted road bounds exposed');
const allowed = new Set(
[
...tilesForBounds(bounds, 12),
...tilesForBounds(trafficDetailBounds(bounds), 14),
].map((t) => `${t.z}/${t.x}/${t.y}.pbf`),
);
for (const request of roadRequests)
assert.ok(
[...allowed].some((x) => request.target.endsWith('/' + x)),
`${view.name}: newly exposed tile belongs to admitted viewport`,
);
}
assert.ok(
state.count > 0 && !state.error,
`${view.name}: healthy traffic`,
);
view.pan = {
move: i + 1,
kind: move.kind,
ofmRequests: roadRequests.length,
ofmBytes: roadRequests.reduce((n, r) => n + r.responseBytes, 0),
flowRequests: batch.filter((r) => r.flowTile).length,
apiCalls: batch.filter((r) => ['api', 'tomtom'].includes(r.category))
.length,
dots: state.count,
};
positions.push(move.p);
console.log(JSON.stringify(view.pan));
}
const ofm = requests.filter((r) => r.category === 'ofmTiles');
assert.equal(
new Set(ofm.map((r) => r.target)).size,
ofm.length,
'no identical OpenFreeMap XYZ requested twice in session',
);
const flow = requests.filter((r) => r.flowTile).length;
assert.ok(
flow <= sessionBudget,
`TomTom ${flow} requests <= ${sessionBudget} session budget`,
);
result.pan = {
sessionBudget,
defaultDailyBudget: 6000,
flowRequests: flow,
table: views.filter((v) => v.pan).map((v) => v.pan),
};
console.table(result.pan.table);
} else if (tour) {
// A continent jump can temporarily use the previous city's terrain height
// for collision avoidance (São Paulo -> Austin raises a 450 m target).
// Pin the prescribed camera fixtures so the revisit covers identical tiles.
await page.evaluate(() => {
window.__godsEyeView.viewer.scene.screenSpaceCameraController.enableCollisionDetection = false;
});
for (const city of [
['austin', 30.2672, -97.7431],
['london', 51.5074, -0.1278],
['dubai', 25.2048, 55.2708],
['san-diego', 32.7157, -117.1611],
['tokyo', 35.6762, 139.6503],
['sao-paulo', -23.5505, -46.6333, 1250],
])
await tourCity(...city);
await tourCity('austin-revisit', 30.2672, -97.7431);
} else {
beginView('austin', {
lat: 30.2672,
lon: -97.7431,
height: 2000,
heading: 0,
pitch: -75,
});
console.log('Checking Austin traffic and ALPR...');
await fly(30.2672, -97.7431);
await enableTrafficTimed('initialFirstDotsMs');
for (const id of ['military-installations', 'alpr-cameras']) {
console.log(`Enabling ${id}...`);
await page.evaluate(
(layerId) =>
Promise.race([
window.__godsEyeView.dataManager.setEnabled(layerId, true),
new Promise((_, reject) =>
setTimeout(
() => reject(new Error('Layer enable timed out')),
30_000,
),
),
]),
id,
);
}
await page.waitForFunction(
() => {
const layers = window.__godsEyeView.dataManager.layers;
return ['traffic', 'alpr-cameras'].every((id) => {
const s = layers.get(id).module.getStats();
return s.count > 0 && !s.loading && !s.error;
});
},
{ timeout: 120_000, polling: 500 },
);
const austin = await page.evaluate(() => {
const { viewer, dataManager } = window.__godsEyeView;
const traffic = dataManager.layers.get('traffic').module.getStats();
const alpr = dataManager.layers.get('alpr-cameras').module.getStats();
let cameraEntities = 0;
for (let i = 0; i < viewer.dataSources.length; i++)
for (const entity of viewer.dataSources.get(i).entities.values) {
if (
String(entity.id).startsWith('alpr:') &&
entity.billboard &&
entity.show
)
cameraEntities++;
}
return {
traffic,
alpr,
cameraEntities,
sourceLabel: traffic.loadingLabel,
cameraHeight: viewer.camera.positionCartographic.height,
};
});
result.traffic = austin.traffic;
result.loadTimes[
`${austin.traffic.mode === 'live' ? 'keyed' : 'keyless'}FirstDotsMs`
] = result.loadTimes.initialFirstDotsMs;
result.alpr = austin.alpr;
result.sourceLabel = austin.sourceLabel;
result.cameraHeight = austin.cameraHeight;
assert.ok(
austin.cameraHeight > 1500 && austin.cameraHeight < 2500,
'Austin assertions run at approximately 2 km',
);
assert.ok(austin.traffic.count > 0, 'road dots rendered');
assert.ok(austin.cameraEntities > 0, 'ALPR camera entities rendered');
assert.equal(
austin.traffic.roadSource,
austin.traffic.mode === 'live'
? 'TomTom + OpenStreetMap'
: 'OpenStreetMap',
);
assert.match(austin.sourceLabel, /Roads: (TomTom \+ )?OpenStreetMap/);
await shot('austin');
async function checkStreetSurface(name) {
await waitForSources();
beginView(name, {
lat: 30.2672,
lon: -97.7431,
height: 450,
heading: 0,
pitch: -35,
});
await page.evaluate(async () =>
window.__godsEyeView.dataManager.setEnabled('traffic', false),
);
await fly(30.2672, -97.7431, 450, 0, -35);
await settleTiles();
await enableTrafficTimed(`${name}WarmFirstDotsMs`);
await page.waitForFunction(
() => {
const s = window.__godsEyeView.dataManager.layers
.get('traffic')
.module.getStats();
return s.count > 0 && !s.loading && !s.error;
},
{ timeout: 180_000, polling: 500 },
);
await settleTiles();
const measured = await measureSurface();
result[name] = measured;
assert.equal(
measured.stats.roadSource,
measured.stats.mode === 'live'
? 'TomTom + OpenStreetMap'
: 'OpenStreetMap',
);
if (measured.stats.mode === 'live') {
assert.ok(
measured.stats.flowCoveragePct > 0,
'Austin has matched TomTom flow',
);
assert.match(
measured.stats.loadingLabel,
/LIVE · Roads: TomTom \+ OpenStreetMap · Flow/,
);
} else {
assert.equal(measured.stats.flowCoveragePct, 0);
assert.match(measured.stats.loadingLabel, /SIMULATED/);
}
const { free, slow, jam, sim } = measured.stats.flowBuckets;
assert.equal(
measured.stats.flowCoveragePct,
Math.round((100 * (free + slow + jam)) / (free + slow + jam + sim)),
);
console.log(
`${name}: ${measured.onMesh}/${measured.inView} projected dots on the surface; ${measured.stats.flowCoveragePct}% flow coverage`,
);
await shot(name);
await waitForSources();
// Preserve the earlier 350 m acceptance fixture and the 450 m view.
beginView(`${name}-350m`, {
lat: 30.2685,
lon: -97.7425,
height: 350,
heading: 10,
pitch: -30,
});
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('traffic', false),
);
await fly(30.2685, -97.7425, 350, 10, -30);
await settleTiles();
await enableTrafficTimed(`${name}-350m`);
await waitForSources();
const closeSurface = await measureSurface();
result[`${name}-350m`] = closeSurface;
assert.ok(
closeSurface.onMesh >= 150,
`${name}: preserved 350 m on-mesh gate (${closeSurface.onMesh})`,
);
await shot(`${name}-350m`);
beginView(`${name}-orbit`, {
lat: 30.2685,
lon: -97.7425,
height: 350,
heading: 65,
pitch: -40,
});
await fly(30.2685, -97.7425, 350, 65, -40);
await settleTiles();
await page.waitForFunction(
() => {
const s = window.__godsEyeView.dataManager.layers
.get('traffic')
.module.getStats();
return s.count > 0 && !s.loading && !s.error;
},
{ timeout: 180_000, polling: 500 },
);
await shot(`${name}-orbit`);
assert.ok(
measured.onMesh >= 150,
`${name}: at least 150 projected dots within -3/+25 m of the surface (got ${measured.onMesh})`,
);
}
console.log(
'Checking street-level traffic against the rendered surface...',
);
await checkStreetSurface('traffic-street');
if (result.traffic.mode === 'live') {
// A second isolated page models Google configured / TomTom absent. Geometry and mesh are real responses.
await waitForSources();
const keyedPage = page;
beginView('keyless-startup');
page = await browser.newPage();
await page.setViewport({ width: 1440, height: 1000 });
await page.evaluateOnNewDocument(() => {
sessionStorage.setItem('gev:first-run-mission-session:v1', 'dismissed');
const original = window.fetch;
window.fetch = (input, init) =>
new URL(typeof input === 'string' ? input : input.url, location.href)
.pathname === '/api/tomtom/status'
? Promise.resolve(
new Response(JSON.stringify({ hasKey: false }), {
headers: { 'content-type': 'application/json' },
}),
)
: original(input, init);
});
page.on('pageerror', (error) => result.errors.push(error.message));
await observeNetwork(page);
const fallbackUrl = new URL(navigationUrl);
fallbackUrl.searchParams.set('trafficRoads', 'tomtom');
await page.goto(fallbackUrl.href, {
waitUntil: 'domcontentloaded',
timeout: 60_000,
});
await page.waitForFunction(
() =>
window.__godsEyeView?.dataManager &&
document
.getElementById('loading-screen')
?.classList.contains('hidden'),
{ timeout: 90_000 },
);
await page.evaluate(
() => window.__godsEyeView.styleManager.initialRestorePromise,
);
await page.evaluate(() =>
window.__godsEyeView.styleManager.setDetection({ enabled: false }),
);
beginView('austin-keyless', {
lat: 30.2672,
lon: -97.7431,
height: 2000,
heading: 0,
pitch: -75,
});
console.log('Checking OpenFreeMap roads without TomTom flow...');
await fly(30.2672, -97.7431);
await enableTrafficTimed('keylessFirstDotsMs');
await checkStreetSurface('traffic-street-ofm');
assert.equal(
result['traffic-street-ofm'].stats.roadSource,
'OpenStreetMap',
);
assert.match(
result['traffic-street-ofm'].stats.loadingLabel,
/TomTom roads need a TomTom key/,
);
await waitForSources();
await page.close();
page = keyedPage;
}
await waitForSources();
beginView('camp-mabry', {
lat: 30.3125,
lon: -97.765,
height: 3500,
heading: 25,
pitch: -80,
});
console.log('Checking Camp Mabry...');
const before = await page.evaluate(
() =>
window.__godsEyeView.dataManager.layers
.get('military-installations')
.module.getStats().lastUpdate,
);
await fly(30.3125, -97.765, 3500, 25, -80);
await page.waitForFunction(
(prior) => {
const s = window.__godsEyeView.dataManager.layers
.get('military-installations')
.module.getStats();
return s.count > 0 && !s.loading && !s.error && s.lastUpdate !== prior;
},
{ timeout: 120_000, polling: 500 },
before,
);
result.military = await page.evaluate(() => {
const { viewer, dataManager } = window.__godsEyeView;
const center = viewer.scene.globe.ellipsoid.cartographicToCartesian({
latitude: (30.314 * Math.PI) / 180,
longitude: (-97.763 * Math.PI) / 180,
height: 0,
});
const records = dataManager.layers
.get('military-installations')
.module.getNearby(center, 4000);
const ids = new Set(records.map((r) => r.id));
let markers = 0,
outlines = 0;
for (let i = 0; i < viewer.dataSources.length; i++)
for (const entity of viewer.dataSources.get(i).entities.values) {
if (!ids.has(entity.installationId || entity.id) || !entity.show)
continue;
if (entity.billboard || entity.point) markers++;
if (entity.polyline) outlines++;
}
return {
markers,
outlines,
names: records.map((r) => r.name),
stats: dataManager.layers
.get('military-installations')
.module.getStats(),
};
});
assert.ok(result.military.markers > 0, 'military markers near Camp Mabry');
assert.ok(
result.military.outlines > 0,
'military polygon outlines near Camp Mabry',
);
await shot('camp-mabry');
await waitForSources();
beginView('camp-mabry-close', {
lat: 30.314,
lon: -97.763,
height: 650,
heading: 125,
pitch: -45,
});
await fly(30.314, -97.763, 650, 125, -45);
await shot('camp-mabry-close');
await waitForSources();
beginView('fort-cavazos', {
lat: 31.135,
lon: -97.78,
height: 30000,
heading: 0,
pitch: -90,
});
console.log('Checking Fort Cavazos...');
const priorFort = await page.evaluate(
() =>
window.__godsEyeView.dataManager.layers
.get('military-installations')
.module.getStats().lastUpdate,
);
await fly(31.135, -97.78, 30000, 0, -90);
await settleTiles();
await page.waitForFunction(
(prior) => {
const s = window.__godsEyeView.dataManager.layers
.get('military-installations')
.module.getStats();
return !s.loading && !s.error && s.count > 0 && s.lastUpdate !== prior;
},
{ timeout: 120_000, polling: 500 },
priorFort,
);
result.fortCavazos = await page.evaluate(() => {
const { viewer, dataManager } = window.__godsEyeView;
let markers = 0,
outlines = 0;
for (let i = 0; i < viewer.dataSources.length; i++)
for (const e of viewer.dataSources.get(i).entities.values) {
if (
!e.show ||
!/^(ofm:installation:|osm:military:)/.test(String(e.id))
)
continue;
if (
(e.point &&
e.point.show?.getValue(viewer.clock.currentTime) !== false) ||
e.billboard
)
markers++;
if (e.polyline) outlines++;
}
const stats = dataManager.layers
.get('military-installations')
.module.getStats();
return {
markers: markers + (stats.namedMarkers || 0),
outlines,
stats,
};
});
await shot('fort-cavazos');
assert.ok(
result.fortCavazos.markers > 0 && result.fortCavazos.markers <= 12,
`Fort Cavazos has 1–12 installation markers (got ${result.fortCavazos.markers})`,
);
await waitForSources();
beginView('austin-revisit', {
lat: 30.2672,
lon: -97.7431,
height: 2000,
heading: 0,
pitch: -75,
});
await fly(30.2672, -97.7431);
await waitForSources();
await shot('austin-revisit');
await waitForSources();
}
if (!pan && !profile && !compare && !tour && !coverage) {
// Area annotation outlines: without a configured Overpass instance the
// app learns that once and never sends the query (so no 503 is logged);
// the pin stays and bundled outlines still resolve.
beginView('annotation-area');
await page.evaluate(() =>
['traffic', 'military-installations', 'alpr-cameras'].forEach((id) =>
window.__godsEyeView.dataManager.setEnabled(id, false),
),
);
await fly(37.7989, -122.4662, 4000, 0, -45);
const errorsBefore = result.consoleErrors.length;
const postsBefore = result.overpassRequests.filter((r) =>
r.startsWith('POST'),
).length;
result.annotation = await page.evaluate(async () => {
const configured = await fetch('/api/overpass/status')
.then((response) => response.json())
.then((body) => body.configured);
const annotation = await window.__godsEyeView.annotations.annotate([
{ type: 'area', target: 'Presidio of San Francisco', footprint: true },
]);
return { configured, drawn: annotation.drawn };
});
// Let any deferred outline upgrade settle before counting.
await new Promise((resolve) => setTimeout(resolve, 8000));
result.annotation.consoleErrors = result.consoleErrors.slice(errorsBefore);
result.annotation.overpassPosts =
result.overpassRequests.filter((r) => r.startsWith('POST')).length -
postsBefore;
console.log('Area annotation:', result.annotation);
assert.ok(result.annotation.drawn >= 1, 'area annotation drawn');
assert.deepEqual(
result.annotation.consoleErrors,
[],
'area annotation logs no console errors',
);
if (result.annotation.configured === false)
assert.equal(
result.annotation.overpassPosts,
0,
'unconfigured Overpass is never queried',
);
await shot('annotation-area');
await page.evaluate(() => window.__godsEyeView.annotations.clear?.());
}
if (!pan && !profile && !compare) {
beginView('alpr-london');
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('traffic', false),
);
await fly(51.5074, -0.1278, 450, 0, -35);
await page.evaluate(() =>
window.__godsEyeView.dataManager.setEnabled('alpr-cameras', true),
);
await waitForSources();
result.alprLondon = await page.evaluate(() =>
window.__godsEyeView.dataManager.layers
.get('alpr-cameras')
.module.getStats(),
);
assert.equal(result.alprLondon.noCoverage, true);
assert.equal(result.alprLondon.countLabel, '');
assert.equal(
result.alprLondon.loadingLabel,
'No ALPR data for this area — US and Canada only',
);
await page.evaluate(() => {
if (document.querySelector('#data-panel.collapsed'))
document.querySelector('[data-collapse-target="data-panel"]').click();
document
.querySelector('[data-layer-id="alpr-cameras"]')
.scrollIntoView({ block: 'center' });
});
await page.waitForFunction(
() =>
document
.querySelector('[data-layer-id="alpr-cameras"] .data-toggle-meta')
?.textContent.includes('US and Canada only'),
{ timeout: 10000 },
);
const row = await page.$('[data-layer-id="alpr-cameras"]');
assert.doesNotMatch(
await row.evaluate((node) => node.innerText),
/0 nearby/,
);
await shot('alpr-london');
}
assert.deepEqual(result.consoleErrors, [], 'zero unexpected console errors');
assert.deepEqual(
result.forbiddenRequests,
[],
'zero Overpass/Nominatim browser requests',
);
assert.deepEqual(result.errors, [], 'no browser JavaScript errors');
result.passed = true;
console.log(`${result.mode} checks passed`);
} catch (error) {
result.passed = false;
result.failure = error.message;
if (page) {
await page
.screenshot({ path: path.join(shots, 'overpass-offload-failure.png') })
.catch(() => {});
result.diagnostic = await page
.evaluate(() => ({
text: document.body.innerText.slice(0, 3000),
layers: [...(window.__godsEyeView?.dataManager?.layers || [])]
.filter(([id]) =>
['traffic', 'military-installations', 'alpr-cameras'].includes(id),
)
.map(([id, entry]) => [id, entry.module.getStats()]),
}))
.catch(() => null);
}
console.error(error);
process.exitCode = 1;
} finally {
result.footprint = footprintSummary();
await fs.writeFile(
path.join(shots, `overpass-offload-${result.mode}-result.json`),
JSON.stringify(result, null, 2),
);
await fs.writeFile(
path.join(shots, 'overpass-offload-result.json'),
JSON.stringify(result, null, 2),
);
await browser.close();
}