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

211 lines
6.8 KiB
JavaScript

/* eslint-disable no-process-exit */
import path from 'path';
import fs from 'fs';
import glob from 'glob';
import {shuffle} from 'shuffle-seed';
import {queue} from 'd3';
import colors from 'chalk';
import template from 'lodash.template';
import createServer from './server';
export default function (directory, implementation, options, run) {
const q = queue(1);
const server = createServer();
const tests = options.tests || [];
const ignores = options.ignores || {};
let sequence = glob.sync(`**/${options.fixtureFilename || 'style.json'}`, {cwd: directory})
.map(fixture => {
const id = path.dirname(fixture);
const style = require(path.join(directory, fixture));
server.localizeURLs(style);
style.metadata = style.metadata || {};
style.metadata.test = Object.assign({
id,
ignored: ignores[`${path.basename(directory)}/${id}`],
width: 512,
height: 512,
pixelRatio: 1,
recycleMap: options.recycleMap || false,
allowed: 0.00015
}, style.metadata.test);
return style;
})
.filter(style => {
const test = style.metadata.test;
if (tests.length !== 0 && !tests.some(t => test.id.indexOf(t) !== -1)) {
return false;
}
if (implementation === 'native' && process.env.BUILDTYPE !== 'Debug' && test.id.match(/^debug\//)) {
console.log(colors.gray(`* skipped ${test.id}`));
return false;
}
if (/^skip/.test(test.ignored)) {
console.log(colors.gray(`* skipped ${test.id} (${test.ignored})`));
return false;
}
return true;
});
if (options.shuffle) {
console.log(colors.white(`* shuffle seed: `) + colors.bold(`${options.seed}`));
sequence = shuffle(sequence, options.seed);
}
q.defer(server.listen);
sequence.forEach(style => {
q.defer((callback) => {
const test = style.metadata.test;
try {
run(style, test, handleResult);
} catch (error) {
handleResult(error);
}
function handleResult (error) {
if (error) {
test.error = error;
}
if (test.ignored && !test.ok) {
test.color = '#9E9E9E';
test.status = 'ignored failed';
console.log(colors.white(`* ignore ${test.id} (${test.ignored})`));
} else if (test.ignored) {
test.color = '#E8A408';
test.status = 'ignored passed';
console.log(colors.yellow(`* ignore ${test.id} (${test.ignored})`));
} else if (test.error) {
test.color = 'red';
test.status = 'errored';
console.log(colors.red(`* errored ${test.id}`));
} else if (!test.ok) {
test.color = 'red';
test.status = 'failed';
console.log(colors.red(`* failed ${test.id}`));
} else {
test.color = 'green';
test.status = 'passed';
console.log(colors.green(`* passed ${test.id}`));
}
callback(null, test);
}
});
});
q.defer(server.close);
q.awaitAll((err, results) => {
if (err) {
console.error(err);
setTimeout(() => { process.exit(-1); }, 0);
return;
}
const tests = results.slice(1, -1);
if (process.env.UPDATE) {
console.log(`Updated ${tests.length} tests.`);
process.exit(0);
}
let passedCount = 0,
ignoreCount = 0,
ignorePassCount = 0,
failedCount = 0,
erroredCount = 0;
tests.forEach((test) => {
if (test.ignored && !test.ok) {
ignoreCount++;
} else if (test.ignored) {
ignorePassCount++;
} else if (test.error) {
erroredCount++;
} else if (!test.ok) {
failedCount++;
} else {
passedCount++;
}
});
const totalCount = passedCount + ignorePassCount + ignoreCount + failedCount + erroredCount;
if (passedCount > 0) {
console.log(colors.green('%d passed (%s%)'),
passedCount, (100 * passedCount / totalCount).toFixed(1));
}
if (ignorePassCount > 0) {
console.log(colors.yellow('%d passed but were ignored (%s%)'),
ignorePassCount, (100 * ignorePassCount / totalCount).toFixed(1));
}
if (ignoreCount > 0) {
console.log(colors.white('%d ignored (%s%)'),
ignoreCount, (100 * ignoreCount / totalCount).toFixed(1));
}
if (failedCount > 0) {
console.log(colors.red('%d failed (%s%)'),
failedCount, (100 * failedCount / totalCount).toFixed(1));
}
if (erroredCount > 0) {
console.log(colors.red('%d errored (%s%)'),
erroredCount, (100 * erroredCount / totalCount).toFixed(1));
}
const resultsTemplate = template(fs.readFileSync(path.join(__dirname, '..', 'results.html.tmpl'), 'utf8'));
const itemTemplate = template(fs.readFileSync(path.join(directory, 'result_item.html.tmpl'), 'utf8'));
const stats = {};
for (const test of tests) {
stats[test.status] = (stats[test.status] || 0) + 1;
}
const unsuccessful = tests.filter(test =>
test.status === 'failed' || test.status === 'errored');
const resultsShell = resultsTemplate({unsuccessful, tests, stats, shuffle: options.shuffle, seed: options.seed})
.split('<!-- results go here -->');
const p = path.join(directory, options.recycleMap ? 'index-recycle-map.html' : 'index.html');
const out = fs.createWriteStream(p);
const q = queue(1);
q.defer(write, out, resultsShell[0]);
for (const test of tests) {
q.defer(write, out, itemTemplate({r: test, hasFailedTests: unsuccessful.length > 0}));
}
q.defer(write, out, resultsShell[1]);
q.await(() => {
out.end();
out.on('close', () => {
console.log(`Results at: ${p}`);
process.exit((failedCount + erroredCount) === 0 ? 0 : 1);
});
});
});
}
function write(stream, data, cb) {
if (!stream.write(data)) {
stream.once('drain', cb);
} else {
process.nextTick(cb);
}
}