openvibers/openvibe.tools Commits

npm test no longer counts a skipped test as passed (roadmap WS-Q task 7, the rule of openvibe-shared/test-runner). test/run.js counted every app test that exited 0 as passed, so the three audio tests without ffmpeg and sdk-client without an OpenVibe.SDK checkout, which check nothing and exit 0, read as ok. A test now prints "<label>: skipped (<why>)" for what it could not run, and the runner lists it as skip with its reasons and does not count it as passed: the summary reads "52/55 test files passed, 3 skipped (…)", and only a run with nothing skipped says N/N; --strict (or OV_TEST_STRICT=1) makes a skip fail the run. The per-app runner stays (the syntax check, each test from its app directory, 180 s timeout); SKIP_RE and skipsIn are copied from the module because the root has no dependencies (each app pins openvibe-shared on its own, unchanged here). audio merge, audio jobs and audio hardening printed "SKIPPED (no ffmpeg on this machine)" and now print "skipped (…)"; sdk-client already printed the line. Without ffmpeg the three audio files are listed as skipped. npm test (Node 22, ffmpeg and the SDK checkout present): syntax 199/199, 55/55 test files passed.

OP OpenVibers committed 28 Sept 2026 · 9339e15001 · 4 files, +40 −7


Changes

ddiff --git a/apps/audio/test/hardening.test.js b/apps/audio/test/hardening.test.js
iindex 623ee46..cf46642 100644
---- a/apps/audio/test/hardening.test.js
++++ b/apps/audio/test/hardening.test.js
@@@ -13,7 +13,7 @@ const { execFileSync } = require('child_process');
const { startApp } = require('../../_shared/test/spawn');
try { execFileSync('ffmpeg', ['-version'], { stdio: 'ignore' }); } catch {
-- console.log('audio hardening: SKIPPED (no ffmpeg on this machine)');
++ console.log('audio hardening: skipped (no ffmpeg on this machine)');
process.exit(0);
}
ddiff --git a/apps/audio/test/jobs.test.js b/apps/audio/test/jobs.test.js
iindex 17bcf91..81c42fb 100644
---- a/apps/audio/test/jobs.test.js
++++ b/apps/audio/test/jobs.test.js
@@@ -12,7 +12,7 @@ const { startApp } = require('../../_shared/test/spawn');
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
try { execFileSync('ffmpeg', ['-version'], { stdio: 'ignore' }); } catch {
-- console.log('audio jobs: SKIPPED (no ffmpeg on this machine)');
++ console.log('audio jobs: skipped (no ffmpeg on this machine)');
process.exit(0);
}
ddiff --git a/apps/audio/test/merge.test.js b/apps/audio/test/merge.test.js
iindex 0e8498b..006ed94 100644
---- a/apps/audio/test/merge.test.js
++++ b/apps/audio/test/merge.test.js
@@@ -12,7 +12,7 @@ const { startApp } = require('../../_shared/test/spawn');
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
try { execFileSync('ffmpeg', ['-version'], { stdio: 'ignore' }); } catch {
-- console.log('audio merge: SKIPPED (no ffmpeg on this machine)');
++ console.log('audio merge: skipped (no ffmpeg on this machine)');
process.exit(0);
}
ddiff --git a/test/run.js b/test/run.js
iindex f687345..159ec11 100644
---- a/test/run.js
++++ b/test/run.js
@@@ -1,13 +1,37 @@
'use strict';
// npm test — every app's tests (apps/*/test/*.test.js, apps/_shared/test/*.test.js), each in its own
// process from its app directory, after a syntax check of every server file. Run on Node 22.
++//
++// npm test # everything
++// npm test -- audio/test # only files whose path contains one of the words
++// npm test -- --strict # a skipped test fails the run too (or OV_TEST_STRICT=1)
++//
++// A test that cannot run something here prints `<label>: skipped (<why>)` (no ffmpeg, no OpenVibe.SDK
++// checkout): that file is listed as `skip` with its reasons and not counted as passed, so the summary
++// reads `60/62 test files passed, 2 skipped (…)`; only a run with nothing skipped says `N/N test files
++// passed`. This is the rule of openvibe-shared/test-runner, copied here because this root runner has no
++// dependencies of its own (each app pins openvibe-shared separately).
const { spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const ROOT = path.join(__dirname, '..');
const APPS = path.join(ROOT, 'apps');
--const only = process.argv.slice(2);
++const strict = process.argv.includes('--strict') || process.env.OV_TEST_STRICT === '1';
++const only = process.argv.slice(2).filter(a => !a.startsWith('-'));
++
++/** `<label>: skipped (<why>)` on a line of its own (SKIP_RE from openvibe-shared/test-runner). */
++const SKIP_RE = /^[ \t]*[\w .,'()/+#-]{1,120}: skipped \((.+)\)[ \t]*$/gm;
++
++/** The skip lines in a test's output, in order, without repeats (skipsIn from openvibe-shared/test-runner). */
++function skipsIn(output) {
++ const out = [];
++ for (const m of String(output || '').matchAll(SKIP_RE)) {
++ const line = m[0].trim();
++ if (!out.includes(line)) out.push(line);
++ }
++ return out;
++}
function walk(dir, out = []) {
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
@@@ -37,17 +61,26 @@ const tests = apps.flatMap(a => {
}).filter(t => !only.length || only.some(o => t.file.includes(o)));
const failed = [];
++const skipped = [];
for (const t of tests) {
const name = path.relative(ROOT, t.file);
const t0 = Date.now();
const r = spawnSync(process.execPath, [t.file], { cwd: path.join(APPS, t.app), encoding: 'utf8', timeout: 180_000, env: { ...process.env, NODE_ENV: 'test' } });
const ms = Date.now() - t0;
-- if (r.status === 0) {
++ const skips = r.status === 0 ? skipsIn(`${r.stdout}${r.stderr}`) : [];
++ if (skips.length) {
++ skipped.push({ name, skips });
++ console.log(`skip ${name} (${ms} ms) ${skips.join('; ')}`);
++ } else if (r.status === 0) {
console.log(`ok ${name} (${ms} ms)${r.stdout.trim() ? ` — ${r.stdout.trim().split('\n').pop()}` : ''}`);
} else {
failed.push(name);
console.error(`FAIL ${name} (${ms} ms)\n${r.stdout}${r.stderr}${r.error ? r.error.message : ''}`);
}
}
--console.log(`\n${tests.length - failed.length}/${tests.length} test files passed${failed.length ? `; failed: ${failed.join(', ')}` : ''}`);
--process.exit(failed.length || bad ? 1 : 0);
++const passed = tests.length - failed.length - skipped.length;
++console.log(`\n${passed}/${tests.length} test files passed`
++ + (skipped.length ? `, ${skipped.length} skipped (${skipped.map(s => `${s.name}: ${s.skips.join('; ')}`).join(' | ')})` : '')
++ + (failed.length ? `; failed: ${failed.join(', ')}` : '')
++ + (strict && skipped.length ? ' — strict: skips fail the run' : ''));
++process.exit(failed.length || bad || (strict && skipped.length) ? 1 : 0);