mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-12 06:12:18 +00:00
Each script launches and closes Chrome through scripts/qa/browser.mjs, waits on the application through scripts/qa/ready.mjs instead of the loading screen and fixed pauses, and reads the contract instead of Cesium where the check is not about Cesium itself.
314 lines
10 KiB
JavaScript
314 lines
10 KiB
JavaScript
#!/usr/bin/env node
|
|
/** Reticle coverage on oblique arrivals: source requests, toggle oracle and zoom revisits. */
|
|
import { whenSettled } from './qa/ready.mjs';
|
|
import { launchBrowser, closeBrowser } from './qa/browser.mjs';
|
|
import fs from 'node:fs';
|
|
import {
|
|
moveCamera,
|
|
createScreencast,
|
|
installFrameProbe,
|
|
} from './qa-journey-recorder.mjs';
|
|
const args = process.argv.slice(2);
|
|
if (args.includes('--help')) {
|
|
console.log(
|
|
'Usage: node scripts/qa-traffic-oblique.mjs <url> [--baseline] [--mode=hybrid|osm|tomtom]',
|
|
);
|
|
process.exit(0);
|
|
}
|
|
const baseline = args.includes('--baseline');
|
|
const modeArg = args.find((a) => a.startsWith('--mode='))?.split('=')[1];
|
|
const modes = modeArg ? [modeArg] : ['tomtom', 'osm', 'hybrid'];
|
|
const out = `qa-shots/oblique-${baseline ? 'before' : 'after'}`;
|
|
fs.mkdirSync(out, { recursive: true });
|
|
const result = { views: [], errors: [], forbidden: [], failures: [] };
|
|
const browser = await launchBrowser({
|
|
headless: true,
|
|
protocolTimeout: 180000,
|
|
args: [
|
|
'--disable-background-timer-throttling',
|
|
'--disable-renderer-backgrounding',
|
|
],
|
|
});
|
|
try {
|
|
for (const mode of modes) {
|
|
const page = await browser.newPage();
|
|
await page.setViewport({ width: 1280, height: 800 });
|
|
const counts = { ofm: 0, tomtom: 0 };
|
|
page.on('request', (r) => {
|
|
const u = new URL(r.url());
|
|
if (
|
|
u.hostname === 'tiles.openfreemap.org' &&
|
|
/\/\d+\/\d+\/\d+(\.pbf)?$/.test(u.pathname)
|
|
)
|
|
counts.ofm++;
|
|
if (u.pathname.startsWith('/api/tomtom/flow/')) counts.tomtom++;
|
|
if (
|
|
/overpass/i.test(u.hostname) ||
|
|
u.hostname === 'nominatim.openstreetmap.org'
|
|
)
|
|
result.forbidden.push(u.hostname);
|
|
});
|
|
page.on('pageerror', (e) => result.errors.push(e.message));
|
|
page.on('console', (m) => {
|
|
if (m.type() === 'error') result.errors.push(m.text());
|
|
});
|
|
await page.evaluateOnNewDocument(() =>
|
|
sessionStorage.setItem('gev:first-run-mission-session:v1', 'dismissed'),
|
|
);
|
|
const url = new URL(args.find((a) => /^https?:/.test(a)));
|
|
url.searchParams.set('trafficRoads', mode);
|
|
await page.goto(url.href, { waitUntil: 'domcontentloaded' });
|
|
await page.waitForFunction(() => window.__godsEyeView?.dataManager, {
|
|
timeout: 120000,
|
|
});
|
|
await whenSettled(page);
|
|
await page.evaluate(
|
|
() => window.__godsEyeView.styleManager.initialRestorePromise,
|
|
);
|
|
await page.keyboard.press('Escape');
|
|
await page.evaluate(async () => {
|
|
const g = window.__godsEyeView;
|
|
for (const [id] of g.dataManager.layers)
|
|
await g.dataManager.setEnabled(id, false);
|
|
g.styleManager.setDetection({ enabled: false });
|
|
});
|
|
const snap = () =>
|
|
page.evaluate(() => {
|
|
const { dataManager } = window.__godsEyeView;
|
|
let upper = 0,
|
|
lower = 0;
|
|
dataManager.layers
|
|
.get('traffic')
|
|
.module.visitMotionDots((id, key, p) => {
|
|
if (!p.show) return;
|
|
const s = window.__godsEyeView.engine.projector.worldToWindow(
|
|
p.position,
|
|
);
|
|
if (s && s.x >= 240 && s.x <= 1040 && s.y >= 0 && s.y <= 800) {
|
|
if (s.y < 400) upper++;
|
|
else lower++;
|
|
}
|
|
});
|
|
return {
|
|
upper,
|
|
lower,
|
|
...dataManager.layers.get('traffic').module.getStats(),
|
|
};
|
|
});
|
|
const settle = async () => {
|
|
await whenSettled(page);
|
|
await page
|
|
.waitForFunction(
|
|
() => {
|
|
const s = window.__godsEyeView.dataManager.layers
|
|
.get('traffic')
|
|
.module.getStats();
|
|
return !s.loading;
|
|
},
|
|
{ timeout: 15000 },
|
|
)
|
|
.catch(() => {});
|
|
await whenSettled(page);
|
|
return snap();
|
|
};
|
|
for (const [name, lat, lon, height] of [
|
|
['dubai', 24.964, 55.037, 276],
|
|
['austin', 30.2672, -97.7431, 300],
|
|
['london', 51.507, -0.128, 300],
|
|
]) {
|
|
const before = { ...counts };
|
|
await moveCamera(page, { lat, lon, height: 3000, pitch: -45 }, 0);
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.dataManager.setEnabled('traffic', true),
|
|
);
|
|
await moveCamera(page, { lat, lon, height, pitch: -15 }, 1.4);
|
|
const settled = await settle();
|
|
const load = {
|
|
ofm: counts.ofm - before.ofm,
|
|
tomtom: counts.tomtom - before.tomtom,
|
|
};
|
|
await page.screenshot({ path: `${out}/${name}-${mode}.png` });
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.dataManager.setEnabled('traffic', false),
|
|
);
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.dataManager.setEnabled('traffic', true),
|
|
);
|
|
const toggled = await settle();
|
|
const oracle =
|
|
Math.abs(settled.upper - toggled.upper) / Math.max(1, toggled.upper);
|
|
const preZoom = { ...counts };
|
|
await moveCamera(page, { lat, lon, height: 3000, pitch: -15 }, 0.7);
|
|
await settle();
|
|
const atWide = { ...counts };
|
|
await moveCamera(page, { lat, lon, height, pitch: -15 }, 0.7);
|
|
await settle();
|
|
const revisit = {
|
|
ofm: counts.ofm - atWide.ofm,
|
|
tomtom: counts.tomtom - atWide.tomtom,
|
|
};
|
|
const row = {
|
|
mode,
|
|
name,
|
|
load,
|
|
settled,
|
|
toggled,
|
|
oracle,
|
|
revisit,
|
|
zoomOut: {
|
|
ofm: atWide.ofm - preZoom.ofm,
|
|
tomtom: atWide.tomtom - preZoom.tomtom,
|
|
},
|
|
};
|
|
result.views.push(row);
|
|
console.log(JSON.stringify(row));
|
|
if (oracle > 0.15)
|
|
result.failures.push(`${name}/${mode}: upper oracle ${oracle}`);
|
|
if (revisit.ofm || revisit.tomtom)
|
|
result.failures.push(`${name}/${mode}: revisit requests`);
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.dataManager.setEnabled('traffic', false),
|
|
);
|
|
}
|
|
const locationView = { lat: 24.973, lon: 55.037 };
|
|
await moveCamera(
|
|
page,
|
|
{ lat: 24.964, lon: 55.037, height: 3000, pitch: -45 },
|
|
0,
|
|
);
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.dataManager.setEnabled('traffic', true),
|
|
);
|
|
const dir = `${out}/dubai-${mode}-journey`;
|
|
const recording =
|
|
mode === 'hybrid' ? await createScreencast(page, dir, { fps: 24 }) : null;
|
|
await installFrameProbe(page);
|
|
await page.evaluate(() => {
|
|
const { dataManager } = window.__godsEyeView;
|
|
window.__obliqueFrames = [];
|
|
window.__obliqueMoving = true;
|
|
window.__godsEyeView.engine.frames.postRender.addEventListener(() => {
|
|
const s = dataManager.layers.get('traffic').module.getStats();
|
|
let upper = 0,
|
|
lower = 0,
|
|
near = 0;
|
|
const camera = window.__godsEyeView.engine.camera.pose().position;
|
|
dataManager.layers
|
|
.get('traffic')
|
|
.module.visitMotionDots((id, key, p) => {
|
|
if (!p.show) return;
|
|
const q = window.__godsEyeView.engine.projector.worldToWindow(
|
|
p.position,
|
|
);
|
|
if (q && q.x >= 240 && q.x <= 1040 && q.y >= 0 && q.y <= 800) {
|
|
if (q.y < 400) upper++;
|
|
else lower++;
|
|
const dx = p.position.x - camera.x,
|
|
dy = p.position.y - camera.y,
|
|
dz = p.position.z - camera.z;
|
|
if (dx * dx + dy * dy + dz * dz <= 1500 * 1500) near++;
|
|
}
|
|
});
|
|
window.__obliqueFrames.push({
|
|
t: performance.now(),
|
|
moving: window.__obliqueMoving,
|
|
count: s.count,
|
|
loading: s.loading,
|
|
upper,
|
|
lower,
|
|
near,
|
|
publishes: s.motion.publishes,
|
|
rebuilds: s.motion.rebuilds,
|
|
});
|
|
});
|
|
});
|
|
await recording?.start();
|
|
await page.evaluate(async (v) => {
|
|
const { flyToLandmark } = await import('/src/locations.js');
|
|
await new Promise((resolve) =>
|
|
flyToLandmark(window.__godsEyeView.engine, v.lat, v.lon, {
|
|
range: 1000,
|
|
pitch: -15,
|
|
heading: 0,
|
|
duration: 1.6,
|
|
buildingHeight: 0,
|
|
onComplete: resolve,
|
|
onCancel: resolve,
|
|
}),
|
|
);
|
|
window.__obliqueMoving = false;
|
|
}, locationView);
|
|
const arrived = await settle();
|
|
await page.evaluate(() => {
|
|
window.__obliqueMoving = true;
|
|
});
|
|
await moveCamera(
|
|
page,
|
|
{ lat: 24.964, lon: 55.042, height: 300, pitch: -15 },
|
|
1.2,
|
|
);
|
|
await page.evaluate(() => {
|
|
window.__obliqueMoving = false;
|
|
});
|
|
const panned = await settle();
|
|
await whenSettled(page);
|
|
const frames = await page.evaluate(() => window.__obliqueFrames);
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.dataManager.setEnabled('traffic', false),
|
|
);
|
|
await page.evaluate(() =>
|
|
window.__godsEyeView.dataManager.setEnabled('traffic', true),
|
|
);
|
|
const fresh = await settle();
|
|
const oracle =
|
|
Math.abs(panned.upper - fresh.upper) / Math.max(1, fresh.upper);
|
|
const stops = frames.flatMap((f, i) =>
|
|
i && !f.moving && frames[i - 1].moving ? [i] : [],
|
|
);
|
|
const nearSettles = stops.map((start, index) => {
|
|
let end = start + 1;
|
|
while (end < frames.length && !frames[end].moving) end++;
|
|
const phase = frames.slice(start, end),
|
|
target = phase.at(-1).near;
|
|
const reached = phase.find((f) => f.near >= target * 0.85);
|
|
const ms = reached ? Math.round(reached.t - phase[0].t) : Infinity;
|
|
if (target && ms > 1100)
|
|
result.failures.push(
|
|
`dubai-location-pan/${mode}/${index}: near settle ${ms} ms`,
|
|
);
|
|
return {
|
|
phase: index === 0 ? 'arrival' : 'pan',
|
|
ms,
|
|
target,
|
|
first: phase[0].near,
|
|
};
|
|
});
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
fs.writeFileSync(`${dir}/metrics.json`, JSON.stringify(frames));
|
|
const clip = await recording?.stop();
|
|
result.views.push({
|
|
mode,
|
|
name: 'dubai-location-pan',
|
|
nearSettles,
|
|
arrived,
|
|
settled: panned,
|
|
toggled: fresh,
|
|
oracle,
|
|
clip,
|
|
});
|
|
if (oracle > 0.15)
|
|
result.failures.push(
|
|
`dubai-location-pan/${mode}: upper oracle ${oracle}`,
|
|
);
|
|
await page.close();
|
|
}
|
|
} finally {
|
|
fs.writeFileSync(`${out}/result.json`, JSON.stringify(result, null, 2));
|
|
await closeBrowser(browser);
|
|
}
|
|
if (
|
|
!baseline &&
|
|
(result.failures.length || result.errors.length || result.forbidden.length)
|
|
)
|
|
process.exitCode = 1;
|