Files
MapLibreParticleLab/metrics/integration/lib/query.js
2023-03-25 14:46:49 +01:00

154 lines
5.1 KiB
JavaScript

import path from 'path';
import fs from 'fs';
import * as diff from 'diff';
import {PNG} from 'pngjs';
import harness from './harness';
function deepEqual(a, b) {
if (typeof a !== typeof b)
return false;
if (typeof a === 'number')
return Math.abs(a - b) < 1e-10;
if (a === null || typeof a !== 'object')
return a === b;
const ka = Object.keys(a);
const kb = Object.keys(b);
if (ka.length !== kb.length)
return false;
ka.sort();
kb.sort();
for (let i = 0; i < ka.length; i++)
if (ka[i] !== kb[i] || !deepEqual(a[ka[i]], b[ka[i]]))
return false;
return true;
}
/**
* Run the query suite.
*
* @param {string} implementation - identify the implementation under test; used to
* deal with implementation-specific test exclusions and fudge-factors
* @param {Object} options
* @param {Array<string>} [options.tests] - array of test names to run; tests not in the
* array will be skipped
* @param {queryFn} query - a function that performs the query
* @returns {undefined} terminates the process when testing is complete
*/
export function run(implementation, options, query) {
const directory = path.join(__dirname, '../query-tests');
harness(directory, implementation, options, (style, params, done) => {
query(style, params, (err, data, results) => {
if (err) return done(err);
const dir = path.join(directory, params.id);
if (process.env.UPDATE) {
fs.writeFile(path.join(dir, 'expected.json'), JSON.stringify(results, null, 2), done);
return;
}
const expected = require(path.join(dir, 'expected.json'));
params.ok = deepEqual(results, expected);
if (!params.ok) {
const msg = diff.diffJson(expected, results)
.map((hunk) => {
if (hunk.added) {
return `+ ${hunk.value}`;
} else if (hunk.removed) {
return `- ${hunk.value}`;
} else {
return ` ${hunk.value}`;
}
})
.join('');
params.difference = msg;
console.log(msg);
}
const width = params.width * params.pixelRatio;
const height = params.height * params.pixelRatio;
const color = [255, 0, 0, 255];
function scaleByPixelRatio(x) {
return x * params.pixelRatio;
}
if (!Array.isArray(params.queryGeometry[0])) {
const p = params.queryGeometry.map(scaleByPixelRatio);
const d = 30;
drawAxisAlignedLine([p[0] - d, p[1]], [p[0] + d, p[1]], data, width, height, color);
drawAxisAlignedLine([p[0], p[1] - d], [p[0], p[1] + d], data, width, height, color);
} else {
const a = params.queryGeometry[0].map(scaleByPixelRatio);
const b = params.queryGeometry[1].map(scaleByPixelRatio);
drawAxisAlignedLine([a[0], a[1]], [a[0], b[1]], data, width, height, color);
drawAxisAlignedLine([a[0], b[1]], [b[0], b[1]], data, width, height, color);
drawAxisAlignedLine([b[0], b[1]], [b[0], a[1]], data, width, height, color);
drawAxisAlignedLine([b[0], a[1]], [a[0], a[1]], data, width, height, color);
}
const actualJSON = path.join(dir, 'actual.json');
fs.writeFile(actualJSON, JSON.stringify(results, null, 2), () => {});
const actualPNG = path.join(dir, 'actual.png');
const png = new PNG({
width: params.width * params.pixelRatio,
height: params.height * params.pixelRatio
});
png.data = data;
png.pack()
.pipe(fs.createWriteStream(actualPNG))
.on('finish', () => {
params.actual = fs.readFileSync(actualPNG).toString('base64');
done();
});
});
});
}
function drawAxisAlignedLine(a, b, pixels, width, height, color) {
const fromX = clamp(Math.min(a[0], b[0]), 0, width);
const toX = clamp(Math.max(a[0], b[0]), 0, width);
const fromY = clamp(Math.min(a[1], b[1]), 0, height);
const toY = clamp(Math.max(a[1], b[1]), 0, height);
let index;
if (fromX === toX) {
for (let y = fromY; y <= toY; y++) {
index = getIndex(fromX, y);
pixels[index + 0] = color[0];
pixels[index + 1] = color[1];
pixels[index + 2] = color[2];
pixels[index + 3] = color[3];
}
} else {
for (let x = fromX; x <= toX; x++) {
index = getIndex(x, fromY);
pixels[index + 0] = color[0];
pixels[index + 1] = color[1];
pixels[index + 2] = color[2];
pixels[index + 3] = color[3];
}
}
function getIndex(x, y) {
return (y * width + x) * 4;
}
}
function clamp(x, a, b) {
return Math.max(a, Math.min(b, x));
}