77 lines
2.6 KiB
JavaScript
77 lines
2.6 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
/**
|
||
|
|
* Runs `npm test` in every submodule under `nodes/` and prints a
|
||
|
|
* summary table. Exits non-zero if any submodule's test run failed.
|
||
|
|
*
|
||
|
|
* Usage: node scripts/test-platform.js
|
||
|
|
* (or `npm run test:platform` from the EVOLV root)
|
||
|
|
*/
|
||
|
|
'use strict';
|
||
|
|
|
||
|
|
const { spawnSync } = require('child_process');
|
||
|
|
const fs = require('fs');
|
||
|
|
const path = require('path');
|
||
|
|
|
||
|
|
const ROOT = path.resolve(__dirname, '..');
|
||
|
|
const NODES_DIR = path.join(ROOT, 'nodes');
|
||
|
|
const TIMEOUT_MS = 300_000; // 5 minutes per node (mathjs-heavy ones can take ~13s/file)
|
||
|
|
|
||
|
|
function listSubmodules() {
|
||
|
|
return fs.readdirSync(NODES_DIR)
|
||
|
|
.filter((d) => fs.existsSync(path.join(NODES_DIR, d, 'package.json')))
|
||
|
|
.sort();
|
||
|
|
}
|
||
|
|
|
||
|
|
function pad(s, w) { s = String(s); return s + ' '.repeat(Math.max(0, w - s.length)); }
|
||
|
|
|
||
|
|
function runOne(name) {
|
||
|
|
const dir = path.join(NODES_DIR, name);
|
||
|
|
const t0 = Date.now();
|
||
|
|
const r = spawnSync('npm', ['test', '--silent'], {
|
||
|
|
cwd: dir, encoding: 'utf8', timeout: TIMEOUT_MS, env: { ...process.env, FORCE_COLOR: '0' },
|
||
|
|
});
|
||
|
|
const out = (r.stdout || '') + (r.stderr || '');
|
||
|
|
const pass = Number((out.match(/^# pass (\d+)/m) || [])[1] || 0);
|
||
|
|
const fail = Number((out.match(/^# fail (\d+)/m) || [])[1] || 0);
|
||
|
|
const tests = Number((out.match(/^# tests (\d+)/m) || [])[1] || 0);
|
||
|
|
const durSec = ((Date.now() - t0) / 1000).toFixed(1);
|
||
|
|
const ok = r.status === 0 && fail === 0;
|
||
|
|
return { name, pass, fail, tests, durSec, ok, output: out };
|
||
|
|
}
|
||
|
|
|
||
|
|
function main() {
|
||
|
|
const nodes = listSubmodules();
|
||
|
|
console.log(`Running tests across ${nodes.length} submodules...\n`);
|
||
|
|
|
||
|
|
const results = [];
|
||
|
|
for (const node of nodes) {
|
||
|
|
process.stdout.write(` ${pad(node, 22)}`);
|
||
|
|
const r = runOne(node);
|
||
|
|
results.push(r);
|
||
|
|
const mark = r.ok ? '✓' : '✗';
|
||
|
|
process.stdout.write(`${mark} ${pad(`${r.pass} pass / ${r.fail} fail`, 22)} ${r.durSec}s\n`);
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('\n--- Summary ---');
|
||
|
|
const totalPass = results.reduce((s, r) => s + r.pass, 0);
|
||
|
|
const totalFail = results.reduce((s, r) => s + r.fail, 0);
|
||
|
|
const totalDur = results.reduce((s, r) => s + Number(r.durSec), 0).toFixed(1);
|
||
|
|
const allOk = results.every((r) => r.ok);
|
||
|
|
console.log(` ${totalPass} pass / ${totalFail} fail across ${results.length} submodules in ${totalDur}s`);
|
||
|
|
|
||
|
|
if (!allOk) {
|
||
|
|
console.log('\n--- Failures ---');
|
||
|
|
for (const r of results.filter((r) => !r.ok)) {
|
||
|
|
console.log(`\n[${r.name}]`);
|
||
|
|
const failingLines = r.output.split('\n').filter((l) =>
|
||
|
|
/^not ok|AssertionError|Error:/.test(l)
|
||
|
|
).slice(0, 10);
|
||
|
|
failingLines.forEach((l) => console.log(` ${l}`));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
process.exit(allOk ? 0 : 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
main();
|