mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Check the photoreal paths in the browser on a synthetic 3D Tiles set
A generated tileset (a plate at ellipsoid level, 50 m and 200 m blocks and a pitched roof, 27 KB) replaces Google's tiles when a QA page passes ?qaTileset=/path; only a root-relative path on the page origin is accepted, and the Google credit is not shown for it. The strict harness serves the set from disk, so nothing reaches Google. qa-tiles-fixture checks on both engines that sampleHeight reads the block at 50 m, clampToHeight lands on the roof, tiles and terrain drapes draw where they should, tilesLoaded turns true, surface points land on the roof within their budget, and pick renders per pass stay within it.
This commit is contained in:
1 parent
a90407d547
commit
e9ba5e9859
17 files changed
+1855
-20
No files matched your search
@@ -0,0 +1,226 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { Cesium3DTileset } from 'cesium';
|
||||
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
||||
import { Raycaster, Vector3 } from 'three';
|
||||
import { buildTilesFixture } from './build-tiles3d-fixture.mjs';
|
||||
import { localTilesetPath, qaTilesetUrl } from '../src/maps/qaTileset.js';
|
||||
import { createDefaultMapSources } from '../src/maps/defaultSources.js';
|
||||
import { MapSourceController } from '../src/maps/controller.js';
|
||||
import { createFakeEngine } from '../src/globe/testing/fakeEngine.js';
|
||||
import { createNavaraMaps } from '../src/globe/navara/maps.js';
|
||||
import {
|
||||
assertKeylessPreview,
|
||||
TILES_FIXTURE_PATH,
|
||||
tilesFixtureRoutes,
|
||||
} from './qa-strict-harness.mjs';
|
||||
|
||||
function globals(t, values) {
|
||||
for (const [name, value] of Object.entries(values)) {
|
||||
const old = Object.getOwnPropertyDescriptor(globalThis, name);
|
||||
Object.defineProperty(globalThis, name, {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value,
|
||||
});
|
||||
t.after(() => {
|
||||
if (old) Object.defineProperty(globalThis, name, old);
|
||||
else delete globalThis[name];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
test('fixture is reproducible, self-contained and under 200 KB', () => {
|
||||
const files = buildTilesFixture();
|
||||
assert.equal(files.size, 5);
|
||||
assert.ok(
|
||||
[...files.values()].reduce((sum, bytes) => sum + bytes.length, 0) < 200_000,
|
||||
);
|
||||
for (const [name, bytes] of files)
|
||||
assert.deepEqual(
|
||||
readFileSync(new URL(`./fixtures/tiles3d/${name}`, import.meta.url)),
|
||||
bytes,
|
||||
);
|
||||
const tileset = JSON.parse(files.get('tileset.json'));
|
||||
for (const child of tileset.root.children)
|
||||
assert.ok(files.has(child.content.uri));
|
||||
assert.equal(tileset.extras.points.length, 40);
|
||||
});
|
||||
|
||||
test('independent glTF loader and triangle rays recover the authored heights', async () => {
|
||||
const files = buildTilesFixture();
|
||||
const probes = [
|
||||
['plate', 0, -170, 0],
|
||||
['block50', -160, 0, 50],
|
||||
['block200', 0, 170, 200],
|
||||
['roof', 180, 0, 84],
|
||||
['roof', 140, 30, 84],
|
||||
['roof', 160, 0, 100],
|
||||
];
|
||||
for (const [name, east, north, height] of probes) {
|
||||
const bytes = files.get(`${name}.glb`);
|
||||
const gltf = await new GLTFLoader().parseAsync(
|
||||
bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength),
|
||||
'',
|
||||
);
|
||||
gltf.scene.updateMatrixWorld(true);
|
||||
const ray = new Raycaster(
|
||||
new Vector3(east, 1000, -north),
|
||||
new Vector3(0, -1, 0),
|
||||
);
|
||||
const hits = ray.intersectObject(gltf.scene, true);
|
||||
assert.ok(hits.length, name);
|
||||
assert.ok(
|
||||
Math.abs(hits[0].point.y - height) < 1e-5,
|
||||
`${name}: ${hits[0].point.y}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('QA URL switch is inert by default and rejects nonlocal or malformed paths', (t) => {
|
||||
const location = new URL('http://localhost:4303/');
|
||||
globals(t, { location });
|
||||
assert.equal(qaTilesetUrl(), null);
|
||||
assert.equal(createDefaultMapSources().defaultId, 'esri-imagery');
|
||||
location.searchParams.set('qaTileset', TILES_FIXTURE_PATH);
|
||||
assert.equal(qaTilesetUrl(), TILES_FIXTURE_PATH);
|
||||
for (const value of [
|
||||
'https://example.com/tileset.json',
|
||||
'http://localhost:4303/tileset.json',
|
||||
'//example.com/x',
|
||||
'/\\example.com/x',
|
||||
'relative.json',
|
||||
'/x\n',
|
||||
'/x?key=test',
|
||||
'/x#fragment',
|
||||
]) {
|
||||
assert.throws(() => localTilesetPath(value), /page origin/);
|
||||
for (const engineKind of ['cesium', 'navara'])
|
||||
assert.throws(
|
||||
() => createDefaultMapSources({ engineKind, tilesetUrl: value }),
|
||||
/page origin/,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('controller passes local URL to Navara and owns the Cesium factory result', async (t) => {
|
||||
globals(t, { location: new URL('http://localhost:4303/') });
|
||||
const tileset = { url: TILES_FIXTURE_PATH, show: true, destroy: t.mock.fn() };
|
||||
const factory = t.mock.method(
|
||||
Cesium3DTileset,
|
||||
'fromUrl',
|
||||
async () => tileset,
|
||||
);
|
||||
for (const engineKind of ['navara', 'cesium']) {
|
||||
const registry = createDefaultMapSources({
|
||||
engineKind,
|
||||
tilesetUrl: TILES_FIXTURE_PATH,
|
||||
});
|
||||
const engine = createFakeEngine({ kind: engineKind });
|
||||
const controller = new MapSourceController({ registry, engine });
|
||||
await controller.setStack('photoreal');
|
||||
const source = engine.maps.current().photoreal;
|
||||
assert.equal(source.tilesetUrl, TILES_FIXTURE_PATH);
|
||||
assert.equal(source.tileset, engineKind === 'cesium' ? tileset : undefined);
|
||||
assert.equal(factory.mock.callCount(), engineKind === 'cesium' ? 1 : 0);
|
||||
controller.destroy();
|
||||
engine.destroy();
|
||||
}
|
||||
assert.equal(factory.mock.calls[0].arguments[0], TILES_FIXTURE_PATH);
|
||||
assert.equal(tileset.destroy.mock.callCount(), 1);
|
||||
});
|
||||
|
||||
test('Navara accepts the local 3D source without a key or Google credit, and rejects remote paths', async (t) => {
|
||||
globals(t, { location: new URL('http://localhost:4303/') });
|
||||
const sources = [],
|
||||
credits = [];
|
||||
const view = {
|
||||
addSource: (desc) => {
|
||||
sources.push(desc);
|
||||
return { delete() {} };
|
||||
},
|
||||
addLayer: () => ({ id: 'fixture', delete() {} }),
|
||||
forceUpdate() {},
|
||||
attribution: {
|
||||
add: (items) => credits.push(...items),
|
||||
remove() {},
|
||||
show: t.mock.fn(),
|
||||
},
|
||||
};
|
||||
const owner = createNavaraMaps(view, {
|
||||
setInterval: () => 1,
|
||||
clearInterval() {},
|
||||
});
|
||||
t.after(owner.destroy);
|
||||
await owner.maps.setPhotorealTiles({ tilesetUrl: TILES_FIXTURE_PATH });
|
||||
assert.deepEqual(sources, [
|
||||
{ type: '3d-tiles', url: `http://localhost:4303${TILES_FIXTURE_PATH}` },
|
||||
]);
|
||||
assert.equal(owner.maps.photorealActive(), true);
|
||||
assert.deepEqual(credits, []);
|
||||
assert.equal(view.attribution.show.mock.callCount(), 0);
|
||||
await assert.rejects(
|
||||
owner.maps.setPhotorealTiles({
|
||||
tilesetUrl: '//example.com/tileset.json',
|
||||
googleApiKey: 'unused',
|
||||
}),
|
||||
/page origin/,
|
||||
);
|
||||
assert.equal(sources.length, 1);
|
||||
await owner.maps.setPhotorealTiles(null);
|
||||
assert.equal(owner.maps.photorealActive(), false);
|
||||
});
|
||||
|
||||
test('strict keyless assertion permits only the opted-in fixture and its real tileset URL', async (t) => {
|
||||
const location = new URL(
|
||||
`http://localhost:4303/?qaTileset=${encodeURIComponent(TILES_FIXTURE_PATH)}`,
|
||||
);
|
||||
const source = {
|
||||
tilesetUrl: TILES_FIXTURE_PATH,
|
||||
tileset: { resource: { url: TILES_FIXTURE_PATH } },
|
||||
};
|
||||
const app = {
|
||||
engine: {
|
||||
maps: {
|
||||
current: () => ({ photoreal: source }),
|
||||
photorealActive: () => true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const window = { __godsEyeView: app };
|
||||
globals(t, { window, location });
|
||||
const page = { evaluate: async (fn, ...args) => fn(...args) };
|
||||
await assertKeylessPreview(page);
|
||||
source.tileset.resource.url = 'https://example.com/tileset.json';
|
||||
await assert.rejects(assertKeylessPreview(page), /Keyless/);
|
||||
source.tileset.resource.url = TILES_FIXTURE_PATH;
|
||||
location.search = '';
|
||||
await assert.rejects(assertKeylessPreview(page), /Keyless/);
|
||||
location.searchParams.set('qaTileset', TILES_FIXTURE_PATH);
|
||||
source.tilesetUrl = '/another/tileset.json';
|
||||
await assert.rejects(assertKeylessPreview(page), /Keyless/);
|
||||
source.tilesetUrl = TILES_FIXTURE_PATH;
|
||||
window.__GOOGLE_MAPS_API_KEY__ = 'key';
|
||||
await assert.rejects(assertKeylessPreview(page), /Keyless/);
|
||||
delete window.__GOOGLE_MAPS_API_KEY__;
|
||||
app.tileset = { url: '/another/tileset.json' };
|
||||
await assert.rejects(assertKeylessPreview(page), /Keyless/);
|
||||
});
|
||||
|
||||
test('harness exposes exactly the five binary-safe fixture routes on the preview origin', () => {
|
||||
const routes = tilesFixtureRoutes('http://localhost:4303');
|
||||
assert.equal(routes.length, 5);
|
||||
const files = buildTilesFixture();
|
||||
for (const route of routes) {
|
||||
const url = new URL(route.pattern);
|
||||
assert.equal(url.origin, 'http://localhost:4303');
|
||||
assert.ok(!route.pattern.includes('*'));
|
||||
const answer = route.respond();
|
||||
assert.deepEqual(answer.body, files.get(url.pathname.split('/').at(-1)));
|
||||
assert.equal(
|
||||
answer.contentType,
|
||||
url.pathname.endsWith('.glb') ? 'model/gltf-binary' : 'application/json',
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user