170 lines
7.8 KiB
JavaScript
170 lines
7.8 KiB
JavaScript
#!/usr/bin/env node
|
|
"use strict";
|
|
|
|
const assert = require("assert");
|
|
const fs = require("fs");
|
|
const os = require("os");
|
|
const path = require("path");
|
|
const { normalizeAreaConfig } = require("./lib/area-config");
|
|
const { stageManifestStatus } = require("./lib/area-diagnostics");
|
|
const { digestGltf } = require("./glb-digest");
|
|
const { evaluateGlbBudget, BUDGETS, fileRecord, writeStageManifest } = require("./lib/stage-manifest");
|
|
|
|
const qgisBuildSource = fs.readFileSync(path.join(__dirname, "build-osm2streets-qgis.js"), "utf8");
|
|
const areaBuildSource = fs.readFileSync(path.join(__dirname, "build-area.js"), "utf8");
|
|
assert.match(qgisBuildSource, /QgsFieldConstraints\.Constraint\.ConstraintNotNull/);
|
|
assert.match(qgisBuildSource, /QgsFieldConstraints\.ConstraintNotNull/);
|
|
assert.doesNotMatch(qgisBuildSource, /setFieldConstraint\(index, 1\)/);
|
|
assert.match(areaBuildSource, /exportCesium\(area, roadProvider\)/);
|
|
assert.match(areaBuildSource, /"--dynamic-glb", area\.outputs\.trafficSignalsDynamicGlb/);
|
|
assert.match(areaBuildSource, /trafficSignalsCountdown0Glb: fileRecord/);
|
|
assert.match(areaBuildSource, /if \(roadProvider === "osm2streets"\) \{[\s\S]*?buildPreviewVehicleRoute/);
|
|
assert.match(areaBuildSource, /Object\.assign\(previewInputs, nativeRoadRecords\(area\)\)/);
|
|
assert.match(areaBuildSource, /vehicleRoute: optionalFileRecord\(area\.outputs\.vehicleRoute\)/);
|
|
|
|
const gltf = {
|
|
nodes: [
|
|
{ name: "Building_1", mesh: 0 },
|
|
{ name: "Shapespark_tree-01", mesh: 1 },
|
|
],
|
|
meshes: [
|
|
{ name: "Building", primitives: [{ indices: 0, attributes: { POSITION: 1 } }] },
|
|
{ name: "Tree", primitives: [{ indices: 2, attributes: { POSITION: 3 } }] },
|
|
],
|
|
accessors: [{ count: 12 }, { count: 4 }, { count: 9 }, { count: 3 }],
|
|
images: [{ name: "large", bufferView: 0 }, { name: "small", bufferView: 1 }],
|
|
bufferViews: [{ byteLength: 800 }, { byteLength: 200 }],
|
|
};
|
|
const digest = digestGltf(gltf, { file: "fixture.glb", fileBytes: 1500 });
|
|
assert.equal(digest.counts.triangles, 7);
|
|
assert.equal(digest.counts.renderTriangles, 7);
|
|
assert.equal(digest.embeddedImageBytes, 1000);
|
|
assert.deepEqual(digest.sourceSummary.map((entry) => [entry.source, entry.triangles]), [["buildings", 4], ["vegetation", 3]]);
|
|
assert.deepEqual(digest.images.map((image) => image.bytes), [800, 200]);
|
|
|
|
const budget = { ...BUDGETS, glbBytes: 1000 };
|
|
assert.equal(evaluateGlbBudget(digest, budget).violations[0].key, "glbBytes");
|
|
|
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "asset-budget-"));
|
|
const input = path.join(tempDir, "input.osm");
|
|
fs.writeFileSync(input, "<osm/>");
|
|
const base = { id: "test-area", input, outputRoot: tempDir };
|
|
assert.equal(
|
|
normalizeAreaConfig(base).outputs.trafficSignals,
|
|
path.join(tempDir, "test-area", "osm2streets_web_out", "traffic_signals.json"),
|
|
);
|
|
assert.equal(
|
|
normalizeAreaConfig(base).outputs.trafficSignalAssemblies,
|
|
path.join(tempDir, "test-area", "osm2streets_web_out", "traffic_signal_assemblies.geojson"),
|
|
);
|
|
assert.equal(
|
|
normalizeAreaConfig(base).outputs.trafficSimulation,
|
|
path.join(tempDir, "test-area", "_preview", "test-area-traffic-simulation.json"),
|
|
);
|
|
assert.equal(normalizeAreaConfig({ ...base, budget: { nodes: 800 } }).budget.glbNodes, 800);
|
|
assert.equal(normalizeAreaConfig(base).stages.intermediates, false);
|
|
assert.equal(normalizeAreaConfig(base).blender.roadProvider, "native");
|
|
assert.deepEqual(normalizeAreaConfig(base).v2xPreview, {
|
|
enabled: false,
|
|
apiBaseUrl: "/api",
|
|
wsBaseUrl: "/websocket",
|
|
crossCode: "",
|
|
});
|
|
assert.deepEqual(normalizeAreaConfig({ ...base, v2xPreview: { enabled: false, apiBaseUrl: "/v2x-api/", wsBaseUrl: "/v2x-ws", crossCode: "420100023333" } }).v2xPreview, {
|
|
enabled: false,
|
|
apiBaseUrl: "/v2x-api/",
|
|
wsBaseUrl: "/v2x-ws",
|
|
crossCode: "420100023333",
|
|
});
|
|
assert.throws(
|
|
() => normalizeAreaConfig({ ...base, v2xPreview: "enabled" }),
|
|
/v2xPreview must be an object/,
|
|
);
|
|
assert.equal(normalizeAreaConfig({ ...base, blender: { roadProvider: "native" } }).blender.roadProvider, "native");
|
|
assert.throws(
|
|
() => normalizeAreaConfig({ ...base, blender: { roadProvider: "other" } }),
|
|
/blender\.roadProvider/,
|
|
);
|
|
assert.throws(
|
|
() => normalizeAreaConfig({ ...base, budget: { nodes: 1200 } }),
|
|
/budget.reason is required/,
|
|
);
|
|
assert.throws(
|
|
() => normalizeAreaConfig({ ...base, budget: { triangles: 0 } }),
|
|
/budget.triangles must be a positive integer/,
|
|
);
|
|
assert.throws(
|
|
() => normalizeAreaConfig({ ...base, budget: "large" }),
|
|
/budget must be an object/,
|
|
);
|
|
assert.equal(
|
|
normalizeAreaConfig({ ...base, budget: { nodes: 1200, reason: "Dense campus vegetation" } }).budget.glbNodes,
|
|
1200,
|
|
);
|
|
|
|
const configPath = path.join(tempDir, "area.json");
|
|
const legacyBase = { ...base, blender: { roadProvider: "osm2streets" } };
|
|
fs.writeFileSync(configPath, `${JSON.stringify(legacyBase)}\n`);
|
|
const area = normalizeAreaConfig(legacyBase);
|
|
assert.equal(area.stages.compress, true);
|
|
assert.equal("compressedGlb" in area.outputs, false);
|
|
assert.equal("compressedMetadata" in area.outputs, false);
|
|
assert.equal("compressedCesiumPreview" in area.outputs, false);
|
|
fs.mkdirSync(area.outputs.geojsonDir, { recursive: true });
|
|
for (const file of [area.outputs.glb, area.outputs.metadata, area.outputs.cesiumPreview, area.outputs.vehicleRoute, area.outputs.vehicleModel]) {
|
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
fs.writeFileSync(file, "fixture\n");
|
|
}
|
|
const lanePolygons = path.join(area.outputs.geojsonDir, "lane_polygons.geojson");
|
|
const emptyFeatureCollection = '{"type":"FeatureCollection","features":[]}\n';
|
|
const emptyNetwork = '{"roads":[],"intersections":[],"gps_bounds":{}}\n';
|
|
fs.writeFileSync(lanePolygons, emptyFeatureCollection);
|
|
const network = path.join(area.outputs.geojsonDir, "network.json");
|
|
fs.writeFileSync(network, emptyNetwork);
|
|
const intersectionSurface = path.join(area.outputs.geojsonDir, "intersection_surface.geojson");
|
|
fs.writeFileSync(intersectionSurface, emptyFeatureCollection);
|
|
const previewCss = path.join(__dirname, "lib", "cesium-preview.css");
|
|
const previewJs = path.join(__dirname, "lib", "cesium-preview.js");
|
|
writeStageManifest(area, {
|
|
stage: "preview",
|
|
status: "ok",
|
|
config: configPath,
|
|
inputs: {
|
|
config: fileRecord(configPath),
|
|
osm: fileRecord(input),
|
|
glb: fileRecord(area.outputs.glb),
|
|
metadata: fileRecord(area.outputs.metadata),
|
|
lanePolygons: fileRecord(lanePolygons),
|
|
network: fileRecord(network),
|
|
intersectionSurface: fileRecord(intersectionSurface),
|
|
previewCss: fileRecord(previewCss),
|
|
previewJs: fileRecord(previewJs),
|
|
},
|
|
outputs: {
|
|
cesiumPreview: fileRecord(area.outputs.cesiumPreview),
|
|
vehicleRoute: fileRecord(area.outputs.vehicleRoute),
|
|
vehicleModel: fileRecord(area.outputs.vehicleModel),
|
|
},
|
|
summary: {},
|
|
warnings: [],
|
|
});
|
|
let previewManifest = stageManifestStatus(area, configPath).find((manifest) => manifest.stage === "preview");
|
|
assert.equal(previewManifest.fresh, true);
|
|
fs.appendFileSync(lanePolygons, " \n");
|
|
previewManifest = stageManifestStatus(area, configPath).find((manifest) => manifest.stage === "preview");
|
|
assert.equal(previewManifest.fresh, false);
|
|
assert.ok(previewManifest.issues.some((issue) => issue.includes("lanePolygons")));
|
|
fs.writeFileSync(lanePolygons, emptyFeatureCollection);
|
|
fs.appendFileSync(network, " \n");
|
|
previewManifest = stageManifestStatus(area, configPath).find((manifest) => manifest.stage === "preview");
|
|
assert.equal(previewManifest.fresh, false);
|
|
assert.ok(previewManifest.issues.some((issue) => issue.includes("network")));
|
|
fs.writeFileSync(network, emptyNetwork);
|
|
fs.appendFileSync(intersectionSurface, " \n");
|
|
previewManifest = stageManifestStatus(area, configPath).find((manifest) => manifest.stage === "preview");
|
|
assert.equal(previewManifest.fresh, false);
|
|
assert.ok(previewManifest.issues.some((issue) => issue.includes("intersectionSurface")));
|
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
|
|
console.log("Asset budget tests passed.");
|