openvibers/openvibe.live Commits
npm test no longer counts a skipped test as passed (roadmap WS-Q task 7; Shared v1.25.0 openvibe-shared/test-runner). test/run.js is now the shared runner (same files, parallelism, 120 s timeout and [DB] filter) with the skip rule: a test prints "<label>: skipped (<why>)" for what it could not run, and that file is listed with ○ and its reasons, not counted as passed. The four tests that skipped part of themselves in free-form words now print that line: audio-events-bad-segment (OK B without a local model), restream-manager-lifecycle (the ACK test without ffmpeg), ai-remote-only (media analysis with speech without ffmpeg) and lineage-resolver (contract validation on an older openvibe-contracts); rollback-newer-writes and transcript-ai already did. Here the run reads 149/150 test files passed, 1 skipped (no local audio model), where it said 150/150. openvibe-shared is pinned at v1.25.0 (from v1.23.1; 1.24.0 adds the readiness skip, which Live's checks do not use).
Changes
@@@ -27,7 +27,7 @@"onnxruntime-node": "^1.20.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.71.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.23.1",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0","sanitize-html": "^2.17.7","sdp-transform": "^3.0.0","sharp": "^0.33.0",@@@ -2839,9 +2839,9 @@}},"node_modules/openvibe-shared": {-- "version": "1.23.1",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.23.1",-- "integrity": "sha512-b7zhjQi74jiFi/nVmRZ0sY0w99eAS+Yrk7Zw/iskcdQBd5ki5f8RUnGNFyMFnUTZrvzyN+7CX14yXLTGr98a3A==",++ "version": "1.25.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0",++ "integrity": "sha512-oqZ+PvljrWi8IEbtg6bDYai17ZV2sALFx/ezMptniELSr10TgFUiGUQt+b1IWwiHv0T2Y1U2iSrkirJxMNgjSw==","license": "MIT","dependencies": {"jsonwebtoken": "^9.0.0"@@@ -46,7 +46,7 @@"onnxruntime-node": "^1.20.0","openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.71.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",-- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.23.1",++ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0","sanitize-html": "^2.17.7","sdp-transform": "^3.0.0","sharp": "^0.33.0",@@@ -163,7 +163,7 @@ async function check(name, fn) {await check('media analysis: a recording with speech gets no overview while AI is off, and nothing goes out', async () => {const wav = path.join(tmp, 'speech.wav');const made = spawnSync('ffmpeg', ['-v', 'error', '-y', '-f', 'lavfi', '-i', 'anullsrc=r=16000:cl=mono', '-t', '1', wav], { stdio: 'ignore' });-- if (made.status !== 0 || !fs.existsSync(wav)) { quiet(' (ffmpeg unavailable: skipped)'); return; }++ if (made.status !== 0 || !fs.existsSync(wav)) { quiet('media analysis with speech: skipped (ffmpeg unavailable)'); return; }const transcribe = require('../server/ai/transcribe');transcribe.available = () => true;transcribe.transcribeMediaDetailed = async () => ({ text: 'welcome back everyone', segments: [{ start: 0, end: 1, text: 'welcome back everyone' }], ok: true });@@@ -74,7 +74,7 @@ headerOnly.write('WAVE', 8, 'ascii');'a bad segment must not disable detection for every segment that follows');console.log('OK B: detection still available after a bad segment');} else {-- console.log('OK B: skipped — no local model to check availability against');++ console.log('OK B: skipped (no local model to check availability against)');}// ── C: only a model load failure is allowed to be permanent ───────────────────────@@@ -319,6 +319,7 @@ function checkContract(out, label) {}console.log = log;-- console.log(`lineage resolver: all checks passed (${contractKnown ? `${validated} answers validated against ${RESOLUTION}` : `installed openvibe-contracts ${require('openvibe-contracts/package.json').version} predates ${RESOLUTION}; contract validation skipped`})`);++ if (!contractKnown) console.log(`lineage contract validation: skipped (installed openvibe-contracts ${require('openvibe-contracts/package.json').version} predates ${RESOLUTION})`);++ console.log(`lineage resolver: all checks passed${contractKnown ? ` (${validated} answers validated against ${RESOLUTION})` : ''}`);process.exit(0);})().catch((err) => { console.error(err); process.exit(1); });@@@ -85,7 +85,7 @@ manager.sessions.delete('1:9');// ── Progress-based live ACK against a real ffmpeg (skipped when none is installed) ──(async () => {const have = spawnSync('ffmpeg', ['-version']).status === 0;-- if (!have) { console.log = quiet; console.log('restream-manager lifecycle: checks passed (ffmpeg not installed — ACK test skipped)'); process.exit(0); }++ if (!have) { console.log = quiet; console.log('restream-manager ACK: skipped (ffmpeg not installed)'); console.log('restream-manager lifecycle: checks passed'); process.exit(0); }const events = [];const session = {@@@ -1,64 +1,17 @@#!/usr/bin/env node/*** Runs every deterministic test in test/ — the files named *.test.js — each in its own process,-- * a few at a time, and fails if any of them fails.++ * a few at a time, and fails if any of them fails (openvibe-shared/test-runner).** npm test # everything* npm test -- powerchat whip # only files whose name contains one of the words++ * npm test -- --strict # a skipped test fails the run too** These tests use temp SQLite files and in-process servers only; none of them needs the network,* OpenVibe.Network, OpenVibe.Media, a camera or a running site. Tests that do need those live* elsewhere (test/browser/ needs a running server and Chrome — see `npm run test:browser`).++ * A test that cannot run part of itself here (no ffmpeg, no local model, no git history) prints++ * `<label>: skipped (<why>)`: that file is listed with ○ and not counted as passed.*/'use strict';--const { spawn } = require('child_process');--const fs = require('fs');--const os = require('os');--const path = require('path');----const filters = process.argv.slice(2).filter((a) => !a.startsWith('-'));--const files = fs.readdirSync(__dirname)-- .filter((f) => f.endsWith('.test.js'))-- .filter((f) => !filters.length || filters.some((w) => f.includes(w)))-- .sort();--const PARALLEL = Math.max(1, Math.min(4, os.cpus().length - 1));--const TIMEOUT_MS = 120000;----function runOne(file) {-- return new Promise((resolve) => {-- const started = Date.now();-- const child = spawn(process.execPath, [path.join(__dirname, file)], {-- cwd: path.join(__dirname, '..'),-- env: { ...process.env, NODE_ENV: 'test' },-- stdio: ['ignore', 'pipe', 'pipe'],-- });-- let output = '';-- child.stdout.on('data', (c) => { output += c; });-- child.stderr.on('data', (c) => { output += c; });-- const timer = setTimeout(() => { output += `\n[run] timed out after ${TIMEOUT_MS}ms`; child.kill('SIGKILL'); }, TIMEOUT_MS);-- child.on('close', (code, signal) => {-- clearTimeout(timer);-- resolve({ file, ok: code === 0, code: code ?? signal, ms: Date.now() - started, output });-- });-- });--}----(async () => {-- if (!files.length) { console.error('no test files matched'); process.exit(1); }-- const queue = [...files];-- const results = [];-- await Promise.all(Array.from({ length: PARALLEL }, async () => {-- while (queue.length) {-- const r = await runOne(queue.shift());-- results.push(r);-- console.log(`${r.ok ? '✓' : '✗'} ${r.file.padEnd(44)} ${String(r.ms).padStart(6)}ms`);-- }-- }));-- const failed = results.filter((r) => !r.ok);-- for (const r of failed) {-- console.log(`\n── ${r.file} (exit ${r.code}) ──`);-- console.log(r.output.split('\n').filter((l) => !/^\[DB\] /.test(l)).slice(-40).join('\n'));-- }-- console.log(`\n${results.length - failed.length}/${results.length} test files passed`);-- process.exit(failed.length ? 1 : 0);--})();++require('openvibe-shared/test-runner').main({ dir: __dirname, timeoutMs: 120000, pad: 44, hide: /^\[DB\] / });
@@ -27,7 +27,7 @@
@@ -27,7 +27,7 @@
"onnxruntime-node": "^1.20.0",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.71.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.71.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.23.1",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0",
"sanitize-html": "^2.17.7",
"sanitize-html": "^2.17.7",
"sdp-transform": "^3.0.0",
"sdp-transform": "^3.0.0",
"sharp": "^0.33.0",
"sharp": "^0.33.0",
@@ -2839,9 +2839,9 @@
@@ -2839,9 +2839,9 @@
}
}
},
},
"node_modules/openvibe-shared": {
"node_modules/openvibe-shared": {
- "version": "1.23.1",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.23.1",
- "integrity": "sha512-b7zhjQi74jiFi/nVmRZ0sY0w99eAS+Yrk7Zw/iskcdQBd5ki5f8RUnGNFyMFnUTZrvzyN+7CX14yXLTGr98a3A==",
+ "version": "1.25.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0",
+ "integrity": "sha512-oqZ+PvljrWi8IEbtg6bDYai17ZV2sALFx/ezMptniELSr10TgFUiGUQt+b1IWwiHv0T2Y1U2iSrkirJxMNgjSw==",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -46,7 +46,7 @@
@@ -46,7 +46,7 @@
"onnxruntime-node": "^1.20.0",
"onnxruntime-node": "^1.20.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.71.0",
"openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.71.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.12.0",
- "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.23.1",
+ "openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v1.25.0",
"sanitize-html": "^2.17.7",
"sanitize-html": "^2.17.7",
"sdp-transform": "^3.0.0",
"sdp-transform": "^3.0.0",
"sharp": "^0.33.0",
"sharp": "^0.33.0",
@@ -163,7 +163,7 @@ async function check(name, fn) {
@@ -163,7 +163,7 @@ async function check(name, fn) {
await check('media analysis: a recording with speech gets no overview while AI is off, and nothing goes out', async () => {
await check('media analysis: a recording with speech gets no overview while AI is off, and nothing goes out', async () => {
const wav = path.join(tmp, 'speech.wav');
const wav = path.join(tmp, 'speech.wav');
const made = spawnSync('ffmpeg', ['-v', 'error', '-y', '-f', 'lavfi', '-i', 'anullsrc=r=16000:cl=mono', '-t', '1', wav], { stdio: 'ignore' });
const made = spawnSync('ffmpeg', ['-v', 'error', '-y', '-f', 'lavfi', '-i', 'anullsrc=r=16000:cl=mono', '-t', '1', wav], { stdio: 'ignore' });
- if (made.status !== 0 || !fs.existsSync(wav)) { quiet(' (ffmpeg unavailable: skipped)'); return; }
+ if (made.status !== 0 || !fs.existsSync(wav)) { quiet('media analysis with speech: skipped (ffmpeg unavailable)'); return; }
const transcribe = require('../server/ai/transcribe');
const transcribe = require('../server/ai/transcribe');
transcribe.available = () => true;
transcribe.available = () => true;
transcribe.transcribeMediaDetailed = async () => ({ text: 'welcome back everyone', segments: [{ start: 0, end: 1, text: 'welcome back everyone' }], ok: true });
transcribe.transcribeMediaDetailed = async () => ({ text: 'welcome back everyone', segments: [{ start: 0, end: 1, text: 'welcome back everyone' }], ok: true });
@@ -74,7 +74,7 @@ headerOnly.write('WAVE', 8, 'ascii');
@@ -74,7 +74,7 @@ headerOnly.write('WAVE', 8, 'ascii');
'a bad segment must not disable detection for every segment that follows');
'a bad segment must not disable detection for every segment that follows');
console.log('OK B: detection still available after a bad segment');
console.log('OK B: detection still available after a bad segment');
} else {
} else {
- console.log('OK B: skipped — no local model to check availability against');
+ console.log('OK B: skipped (no local model to check availability against)');
}
}
// ── C: only a model load failure is allowed to be permanent ───────────────────────
// ── C: only a model load failure is allowed to be permanent ───────────────────────
@@ -319,6 +319,7 @@ function checkContract(out, label) {
@@ -319,6 +319,7 @@ function checkContract(out, label) {
}
}
console.log = log;
console.log = log;
- console.log(`lineage resolver: all checks passed (${contractKnown ? `${validated} answers validated against ${RESOLUTION}` : `installed openvibe-contracts ${require('openvibe-contracts/package.json').version} predates ${RESOLUTION}; contract validation skipped`})`);
+ if (!contractKnown) console.log(`lineage contract validation: skipped (installed openvibe-contracts ${require('openvibe-contracts/package.json').version} predates ${RESOLUTION})`);
+ console.log(`lineage resolver: all checks passed${contractKnown ? ` (${validated} answers validated against ${RESOLUTION})` : ''}`);
process.exit(0);
process.exit(0);
})().catch((err) => { console.error(err); process.exit(1); });
})().catch((err) => { console.error(err); process.exit(1); });
@@ -85,7 +85,7 @@ manager.sessions.delete('1:9');
@@ -85,7 +85,7 @@ manager.sessions.delete('1:9');
// ── Progress-based live ACK against a real ffmpeg (skipped when none is installed) ──
// ── Progress-based live ACK against a real ffmpeg (skipped when none is installed) ──
(async () => {
(async () => {
const have = spawnSync('ffmpeg', ['-version']).status === 0;
const have = spawnSync('ffmpeg', ['-version']).status === 0;
- if (!have) { console.log = quiet; console.log('restream-manager lifecycle: checks passed (ffmpeg not installed — ACK test skipped)'); process.exit(0); }
+ if (!have) { console.log = quiet; console.log('restream-manager ACK: skipped (ffmpeg not installed)'); console.log('restream-manager lifecycle: checks passed'); process.exit(0); }
const events = [];
const events = [];
const session = {
const session = {
@@ -1,64 +1,17 @@
@@ -1,64 +1,17 @@
#!/usr/bin/env node
#!/usr/bin/env node
/**
/**
* Runs every deterministic test in test/ — the files named *.test.js — each in its own process,
* Runs every deterministic test in test/ — the files named *.test.js — each in its own process,
- * a few at a time, and fails if any of them fails.
+ * a few at a time, and fails if any of them fails (openvibe-shared/test-runner).
*
*
* npm test # everything
* npm test # everything
* npm test -- powerchat whip # only files whose name contains one of the words
* npm test -- powerchat whip # only files whose name contains one of the words
+ * npm test -- --strict # a skipped test fails the run too
*
*
* These tests use temp SQLite files and in-process servers only; none of them needs the network,
* These tests use temp SQLite files and in-process servers only; none of them needs the network,
* OpenVibe.Network, OpenVibe.Media, a camera or a running site. Tests that do need those live
* OpenVibe.Network, OpenVibe.Media, a camera or a running site. Tests that do need those live
* elsewhere (test/browser/ needs a running server and Chrome — see `npm run test:browser`).
* elsewhere (test/browser/ needs a running server and Chrome — see `npm run test:browser`).
+ * A test that cannot run part of itself here (no ffmpeg, no local model, no git history) prints
+ * `<label>: skipped (<why>)`: that file is listed with ○ and not counted as passed.
*/
*/
'use strict';
'use strict';
-const { spawn } = require('child_process');
-const fs = require('fs');
-const os = require('os');
-const path = require('path');
-
-const filters = process.argv.slice(2).filter((a) => !a.startsWith('-'));
-const files = fs.readdirSync(__dirname)
- .filter((f) => f.endsWith('.test.js'))
- .filter((f) => !filters.length || filters.some((w) => f.includes(w)))
- .sort();
-const PARALLEL = Math.max(1, Math.min(4, os.cpus().length - 1));
-const TIMEOUT_MS = 120000;
-
-function runOne(file) {
- return new Promise((resolve) => {
- const started = Date.now();
- const child = spawn(process.execPath, [path.join(__dirname, file)], {
- cwd: path.join(__dirname, '..'),
- env: { ...process.env, NODE_ENV: 'test' },
- stdio: ['ignore', 'pipe', 'pipe'],
- });
- let output = '';
- child.stdout.on('data', (c) => { output += c; });
- child.stderr.on('data', (c) => { output += c; });
- const timer = setTimeout(() => { output += `\n[run] timed out after ${TIMEOUT_MS}ms`; child.kill('SIGKILL'); }, TIMEOUT_MS);
- child.on('close', (code, signal) => {
- clearTimeout(timer);
- resolve({ file, ok: code === 0, code: code ?? signal, ms: Date.now() - started, output });
- });
- });
-}
-
-(async () => {
- if (!files.length) { console.error('no test files matched'); process.exit(1); }
- const queue = [...files];
- const results = [];
- await Promise.all(Array.from({ length: PARALLEL }, async () => {
- while (queue.length) {
- const r = await runOne(queue.shift());
- results.push(r);
- console.log(`${r.ok ? '✓' : '✗'} ${r.file.padEnd(44)} ${String(r.ms).padStart(6)}ms`);
- }
- }));
- const failed = results.filter((r) => !r.ok);
- for (const r of failed) {
- console.log(`\n── ${r.file} (exit ${r.code}) ──`);
- console.log(r.output.split('\n').filter((l) => !/^\[DB\] /.test(l)).slice(-40).join('\n'));
- }
- console.log(`\n${results.length - failed.length}/${results.length} test files passed`);
- process.exit(failed.length ? 1 : 0);
-})();
+require('openvibe-shared/test-runner').main({ dir: __dirname, timeoutMs: 120000, pad: 44, hide: /^\[DB\] / });