eyeling 1.28.6 → 1.28.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.28.6",
3
+ "version": "1.28.7",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
package/test/api.test.js CHANGED
@@ -66,29 +66,12 @@ function reasonQuiet(opt, input) {
66
66
  throw err;
67
67
  }
68
68
 
69
- const TTY = process.stdout.isTTY;
70
- const C = TTY
71
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
72
- : { g: '', r: '', y: '', dim: '', n: '' };
73
-
74
- function ok(msg) {
75
- console.log(`${C.g}OK ${C.n} ${msg}`);
76
- }
77
- function info(msg) {
78
- console.log(`${C.y}==${C.n} ${msg}`);
79
- }
80
- function fail(msg) {
81
- console.error(`${C.r}FAIL${C.n} ${msg}`);
82
- }
69
+ const { detail, failResult, info, pass } = require('./report');
83
70
 
84
71
  function unnumberedName(name) {
85
72
  return String(name).replace(/^\d+[a-z]*\s+/i, '');
86
73
  }
87
74
 
88
- function numberedName(index, name) {
89
- return `${String(index + 1).padStart(3, '0')} ${unnumberedName(name)}`;
90
- }
91
-
92
75
  function msNow() {
93
76
  return Date.now();
94
77
  }
@@ -3172,7 +3155,8 @@ let failed = 0;
3172
3155
  info(`Running ${cases.length} API tests (independent of examples/)`);
3173
3156
 
3174
3157
  for (const [index, tc] of cases.entries()) {
3175
- const testName = numberedName(index, tc.name);
3158
+ const testNr = index + 1;
3159
+ const testName = unnumberedName(tc.name);
3176
3160
  const start = msNow();
3177
3161
  try {
3178
3162
  const out = typeof tc.run === 'function' ? await tc.run() : reasonQuiet(tc.opt, tc.input);
@@ -3187,19 +3171,19 @@ let failed = 0;
3187
3171
  if (typeof tc.check === 'function') tc.check(out, tc);
3188
3172
 
3189
3173
  const dur = msNow() - start;
3190
- ok(`${testName} ${C.dim}(${dur} ms)${C.n}`);
3174
+ pass(testNr, testName, dur);
3191
3175
  passed++;
3192
3176
  } catch (e) {
3193
3177
  const dur = msNow() - start;
3194
3178
 
3195
3179
  if (tc.expectErrorCode != null) {
3196
3180
  if (e && typeof e === 'object' && 'code' in e && e.code === tc.expectErrorCode) {
3197
- ok(`${testName} ${C.dim}(expected exit ${tc.expectErrorCode}, ${dur} ms)${C.n}`);
3181
+ pass(testNr, `${testName} (expected exit ${tc.expectErrorCode})`, dur);
3198
3182
  passed++;
3199
3183
  continue;
3200
3184
  }
3201
- fail(`${testName} ${C.dim}(${dur} ms)${C.n}`);
3202
- fail(
3185
+ failResult(testNr, testName, dur);
3186
+ detail(
3203
3187
  `Expected exit code ${tc.expectErrorCode}, got: ${e && e.code != null ? e.code : 'unknown'}\n${
3204
3188
  e && e.stderr ? e.stderr : e && e.stack ? e.stack : String(e)
3205
3189
  }`,
@@ -3209,26 +3193,26 @@ let failed = 0;
3209
3193
  }
3210
3194
 
3211
3195
  if (tc.expectError) {
3212
- ok(`${testName} ${C.dim}(expected error, ${dur} ms)${C.n}`);
3196
+ pass(testNr, `${testName} (expected error)`, dur);
3213
3197
  passed++;
3214
3198
  continue;
3215
3199
  }
3216
3200
 
3217
- fail(`${testName} ${C.dim}(${dur} ms)${C.n}`);
3218
- fail(e && e.stack ? e.stack : String(e));
3201
+ failResult(testNr, testName, dur);
3202
+ detail(e && e.stack ? e.stack : String(e));
3219
3203
  failed++;
3220
3204
  }
3221
3205
  }
3222
3206
 
3223
3207
  console.log('');
3224
3208
  const suiteMs = Date.now() - suiteStart;
3225
- console.log(`${C.y}==${C.n} Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
3209
+ info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
3226
3210
 
3227
3211
  if (failed === 0) {
3228
- ok(`All API tests passed (${passed}/${cases.length})`);
3212
+ info(`All API tests passed (${passed}/${cases.length})`);
3229
3213
  process.exit(0);
3230
3214
  } else {
3231
- fail(`Some API tests failed (${passed}/${cases.length})`);
3215
+ info(`Some API tests failed (${passed}/${cases.length})`);
3232
3216
  process.exit(1);
3233
3217
  }
3234
3218
  })();
@@ -2,20 +2,7 @@
2
2
 
3
3
  const assert = require('node:assert/strict');
4
4
 
5
- const TTY = process.stdout.isTTY;
6
- const C = TTY
7
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
8
- : { g: '', r: '', y: '', dim: '', n: '' };
9
-
10
- function ok(msg) {
11
- console.log(`${C.g}OK ${C.n} ${msg}`);
12
- }
13
- function info(msg) {
14
- console.log(`${C.y}==${C.n} ${msg}`);
15
- }
16
- function fail(msg) {
17
- console.error(`${C.r}FAIL${C.n} ${msg}`);
18
- }
5
+ const { detail, failResult, info, pass } = require('./report');
19
6
 
20
7
  const builtins = require('../lib/builtins');
21
8
  require('../lib/engine');
@@ -226,25 +213,25 @@ let failed = 0;
226
213
  const suiteStart = Date.now();
227
214
  info(`Running ${cases.length} builtin contract tests`);
228
215
 
229
- for (const tc of cases) {
216
+ for (const [index, tc] of cases.entries()) {
230
217
  const start = Date.now();
231
218
  try {
232
219
  tc.run();
233
- ok(`${tc.name} ${C.dim}(${Date.now() - start} ms)${C.n}`);
220
+ pass(index + 1, tc.name, Date.now() - start);
234
221
  passed++;
235
222
  } catch (e) {
236
- fail(`${tc.name} ${C.dim}(${Date.now() - start} ms)${C.n}`);
237
- fail(e && e.stack ? e.stack : String(e));
223
+ failResult(index + 1, tc.name, Date.now() - start);
224
+ detail(e && e.stack ? e.stack : String(e));
238
225
  failed++;
239
226
  }
240
227
  }
241
228
 
242
229
  console.log('');
243
- console.log(`${C.y}==${C.n} Total elapsed: ${Date.now() - suiteStart} ms`);
230
+ info(`Total elapsed: ${Date.now() - suiteStart} ms`);
244
231
  if (failed === 0) {
245
- ok(`All builtin contract tests passed (${passed}/${cases.length})`);
232
+ info(`All builtin contract tests passed (${passed}/${cases.length})`);
246
233
  process.exit(0);
247
234
  }
248
- fail(`Some builtin contract tests failed (${passed}/${cases.length})`);
235
+ info(`Some builtin contract tests failed (${passed}/${cases.length})`);
249
236
  process.exit(1);
250
237
  })();
@@ -6,21 +6,7 @@ const os = require('node:os');
6
6
  const path = require('node:path');
7
7
  const cp = require('node:child_process');
8
8
 
9
- const TTY = process.stdout.isTTY;
10
- const C = TTY
11
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
12
- : { g: '', r: '', y: '', dim: '', n: '' };
13
- const msTag = (ms) => `${C.dim}(${ms} ms)${C.n}`;
14
-
15
- function ok(msg) {
16
- console.log(`${C.g}OK${C.n} ${msg}`);
17
- }
18
- function fail(msg) {
19
- console.error(`${C.r}FAIL${C.n} ${msg}`);
20
- }
21
- function info(msg) {
22
- console.log(`${C.y}==${C.n} ${msg}`);
23
- }
9
+ const { C, detail, failResult, info, pass } = require('./report');
24
10
 
25
11
  function run(cmd, args, opts = {}) {
26
12
  return cp.spawnSync(cmd, args, {
@@ -224,11 +210,11 @@ function main() {
224
210
  const nodePath = process.execPath;
225
211
 
226
212
  if (!fs.existsSync(examplesDir)) {
227
- fail(`Cannot find examples directory: ${examplesDir}`);
213
+ failResult(1, `Cannot find examples directory: ${examplesDir}`, 0);
228
214
  process.exit(1);
229
215
  }
230
216
  if (!fs.existsSync(eyelingJsPath)) {
231
- fail(`Cannot find eyeling.js: ${eyelingJsPath}`);
217
+ failResult(1, `Cannot find eyeling.js: ${eyelingJsPath}`, 0);
232
218
  process.exit(1);
233
219
  }
234
220
 
@@ -252,21 +238,19 @@ function main() {
252
238
  console.log(`${C.dim}${getEyelingVersion(nodePath, eyelingJsPath, root)}; node ${process.version}${C.n}`);
253
239
 
254
240
  if (totalTests === 0) {
255
- ok(proofOnly ? 'No proof goldens found in examples/proof/' : 'No .n3 files found in examples/');
241
+ info(proofOnly ? 'No proof goldens found in examples/proof/' : 'No .n3 files found in examples/');
256
242
  process.exit(0);
257
243
  }
258
244
 
259
245
  let passed = 0;
260
246
  let failed = 0;
261
247
 
262
- // Pretty, stable numbering (e.g., 001..100 when running 100 tests)
263
- const idxWidth = String(files.length).length;
264
- const proofIdxWidth = String(Math.max(proofFiles.length, 1)).length;
265
- const proofLabel = proofOnly ? '' : 'proof ';
248
+ let sequence = 0;
266
249
 
267
250
  for (let i = 0; i < files.length; i++) {
268
- const idx = String(i + 1).padStart(idxWidth, '0');
251
+ const testNr = ++sequence;
269
252
  const file = files[i];
253
+ const testName = file;
270
254
 
271
255
  const start = Date.now();
272
256
 
@@ -278,8 +262,8 @@ function main() {
278
262
  n3Text = fs.readFileSync(filePath, 'utf8');
279
263
  } catch (e) {
280
264
  const ms = Date.now() - start;
281
- fail(`${idx} ${file} ${msTag(ms)}`);
282
- fail(`Cannot read input: ${e.message}`);
265
+ failResult(testNr, testName, ms);
266
+ detail(`Cannot read input: ${e.message}`);
283
267
  failed++;
284
268
  continue;
285
269
  }
@@ -291,8 +275,8 @@ function main() {
291
275
  // comparable across environments.
292
276
  if (!fs.existsSync(expectedPath)) {
293
277
  const ms = Date.now() - start;
294
- fail(`${idx} ${file} ${msTag(ms)}`);
295
- fail(`Missing expected output for ${path.basename(file, path.extname(file))}.*`);
278
+ failResult(testNr, testName, ms);
279
+ detail(`Missing expected output for ${path.basename(file, path.extname(file))}.*`);
296
280
  failed++;
297
281
  continue;
298
282
  }
@@ -324,18 +308,18 @@ function main() {
324
308
 
325
309
  if (diffOk && rcOk) {
326
310
  if (expectedRc === 0) {
327
- ok(`${idx} ${file} ${msTag(ms)}`);
311
+ pass(testNr, testName, ms);
328
312
  } else {
329
- ok(`${idx} ${file} (expected exit ${expectedRc}) ${msTag(ms)}`);
313
+ pass(testNr, `${testName} (expected exit ${expectedRc})`, ms);
330
314
  }
331
315
  passed++;
332
316
  } else {
333
- fail(`${idx} ${file} ${msTag(ms)}`);
317
+ failResult(testNr, testName, ms);
334
318
  if (!rcOk) {
335
- fail(`Exit code ${rc}, expected ${expectedRc}`);
319
+ detail(`Exit code ${rc}, expected ${expectedRc}`);
336
320
  }
337
321
  if (!diffOk) {
338
- fail('Output differs');
322
+ detail('Output differs');
339
323
  }
340
324
 
341
325
  // Show diffs, because this is a test runner
@@ -353,8 +337,9 @@ function main() {
353
337
 
354
338
 
355
339
  for (let i = 0; i < proofFiles.length; i++) {
356
- const idx = String(i + 1).padStart(proofIdxWidth, '0');
340
+ const testNr = ++sequence;
357
341
  const file = proofFiles[i];
342
+ const testName = proofOnly ? file : `proof ${file}`;
358
343
  const start = Date.now();
359
344
  const filePath = path.join(examplesDir, file);
360
345
  const expectedPath = path.join(proofDir, file);
@@ -364,8 +349,8 @@ function main() {
364
349
  n3Text = fs.readFileSync(filePath, 'utf8');
365
350
  } catch (e) {
366
351
  const ms = Date.now() - start;
367
- fail(`${proofLabel}${idx} ${file} ${msTag(ms)}`);
368
- fail(`Cannot read proof input: ${e.message}`);
352
+ failResult(testNr, testName, ms);
353
+ detail(`Cannot read proof input: ${e.message}`);
369
354
  failed++;
370
355
  continue;
371
356
  }
@@ -388,18 +373,18 @@ function main() {
388
373
 
389
374
  if (diffOk && rcOk) {
390
375
  if (expectedRc === 0) {
391
- ok(`${proofLabel}${idx} ${file} ${msTag(ms)}`);
376
+ pass(testNr, testName, ms);
392
377
  } else {
393
- ok(`${proofLabel}${idx} ${file} (expected exit ${expectedRc}) ${msTag(ms)}`);
378
+ pass(testNr, `${testName} (expected exit ${expectedRc})`, ms);
394
379
  }
395
380
  passed++;
396
381
  } else {
397
- fail(`${proofLabel}${idx} ${file} ${msTag(ms)}`);
382
+ failResult(testNr, testName, ms);
398
383
  if (!rcOk) {
399
- fail(`Exit code ${rc}, expected ${expectedRc}`);
384
+ detail(`Exit code ${rc}, expected ${expectedRc}`);
400
385
  }
401
386
  if (!diffOk) {
402
- fail('Proof output differs');
387
+ detail('Proof output differs');
403
388
  }
404
389
  showDiff({
405
390
  examplesDir,
@@ -417,12 +402,12 @@ function main() {
417
402
  info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
418
403
 
419
404
  if (failed === 0) {
420
- ok(proofOnly
405
+ info(proofOnly
421
406
  ? `All proof golden tests passed (${passed}/${totalTests})`
422
407
  : `All examples tests passed (${passed}/${totalTests})`);
423
408
  process.exit(0);
424
409
  } else {
425
- fail(proofOnly
410
+ info(proofOnly
426
411
  ? `Some proof golden tests failed (${passed}/${totalTests})`
427
412
  : `Some examples tests failed (${passed}/${totalTests})`);
428
413
  // keep exit code 2 (matches historical behavior of examples/test)
@@ -5,21 +5,7 @@ const fs = require('node:fs');
5
5
  const path = require('node:path');
6
6
  const cp = require('node:child_process');
7
7
 
8
- const TTY = process.stdout.isTTY;
9
- const C = TTY
10
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
11
- : { g: '', r: '', y: '', dim: '', n: '' };
12
- const msTag = (ms) => `${C.dim}(${ms} ms)${C.n}`;
13
-
14
- function ok(msg) {
15
- console.log(`${C.g}OK${C.n} ${msg}`);
16
- }
17
- function fail(msg) {
18
- console.error(`${C.r}FAIL${C.n} ${msg}`);
19
- }
20
- function info(msg) {
21
- console.log(`${C.y}==${C.n} ${msg}`);
22
- }
8
+ const { C, detail, failResult, info, pass } = require('./report');
23
9
 
24
10
  function main() {
25
11
  const suiteStart = Date.now();
@@ -29,7 +15,7 @@ function main() {
29
15
  const nodePath = process.execPath;
30
16
 
31
17
  if (!fs.existsSync(extraDir)) {
32
- fail(`Cannot find examples/extra directory: ${extraDir}`);
18
+ failResult(1, `Cannot find examples/extra directory: ${extraDir}`, 0);
33
19
  process.exit(1);
34
20
  }
35
21
 
@@ -44,16 +30,14 @@ function main() {
44
30
  console.log(`${C.dim}node ${process.version}${C.n}`);
45
31
 
46
32
  if (files.length === 0) {
47
- ok('No .js files found in examples/extra/');
33
+ info('No .js files found in examples/extra/');
48
34
  process.exit(0);
49
35
  }
50
36
 
51
37
  let passed = 0;
52
38
  let failed = 0;
53
- const idxWidth = String(files.length).length;
54
-
55
39
  for (let i = 0; i < files.length; i += 1) {
56
- const idx = String(i + 1).padStart(idxWidth, '0');
40
+ const testNr = i + 1;
57
41
  const file = files[i];
58
42
  const start = Date.now();
59
43
 
@@ -74,11 +58,11 @@ function main() {
74
58
  const ms = Date.now() - start;
75
59
 
76
60
  if (rc === 0) {
77
- ok(`${idx} ${file} -> output/${path.basename(outputPath)} ${msTag(ms)}`);
61
+ pass(testNr, `${file} -> output/${path.basename(outputPath)}`, ms);
78
62
  passed += 1;
79
63
  } else {
80
- fail(`${idx} ${file} ${msTag(ms)}`);
81
- fail(`Exit code ${rc}`);
64
+ failResult(testNr, file, ms);
65
+ detail(`Exit code ${rc}`);
82
66
  if (r.stderr) process.stderr.write(r.stderr);
83
67
  failed += 1;
84
68
  }
@@ -89,11 +73,11 @@ function main() {
89
73
  info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
90
74
 
91
75
  if (failed === 0) {
92
- ok(`All extra examples passed (${passed}/${files.length})`);
76
+ info(`All extra examples passed (${passed}/${files.length})`);
93
77
  process.exit(0);
94
78
  }
95
79
 
96
- fail(`Some extra examples failed (${passed}/${files.length})`);
80
+ info(`Some extra examples failed (${passed}/${files.length})`);
97
81
  process.exit(2);
98
82
  }
99
83
 
@@ -25,21 +25,7 @@ const cp = require('node:child_process');
25
25
 
26
26
  const ROOT = path.resolve(__dirname, '..');
27
27
 
28
- const TTY = process.stdout.isTTY;
29
- const C = TTY
30
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
31
- : { g: '', r: '', y: '', dim: '', n: '' };
32
-
33
- function ok(msg) {
34
- console.log(`${C.g}OK${C.n} ${msg}`);
35
- }
36
- function warn(msg) {
37
- console.log(`${C.y}SKIP${C.n} ${msg}`);
38
- }
39
- function fail(msg) {
40
- console.error(`${C.r}FAIL${C.n} ${msg}`);
41
- process.exitCode = 1;
42
- }
28
+ const { C, failResult, pass, warn } = require('./report');
43
29
 
44
30
  function run(cmd, args, opts = {}) {
45
31
  const t0 = Date.now();
@@ -70,17 +56,20 @@ function rmrf(p) {
70
56
  }
71
57
 
72
58
  (async function main() {
59
+ let sequence = 0;
73
60
  if (process.env.CI && process.env.EYELING_RUN_NOTATION3TESTS !== '1') {
74
61
  warn('CI detected; set EYELING_RUN_NOTATION3TESTS=1 to run Notation3 tests');
75
62
  return;
76
63
  }
77
64
 
78
65
  if (!has('git')) {
79
- fail('git not found in PATH');
66
+ failResult(++sequence, 'git not found in PATH', 0);
67
+ process.exitCode = 1;
80
68
  return;
81
69
  }
82
70
  if (!has('npm')) {
83
- fail('npm not found in PATH');
71
+ failResult(++sequence, 'npm not found in PATH', 0);
72
+ process.exitCode = 1;
84
73
  return;
85
74
  }
86
75
 
@@ -94,11 +83,12 @@ function rmrf(p) {
94
83
  // 1) Clone suite
95
84
  let r = run('git', ['clone', '--depth', '1', 'https://codeberg.org/phochste/notation3tests', suiteDir]);
96
85
  if (r.status !== 0) {
97
- fail(`git clone failed (exit ${r.status})`);
86
+ failResult(++sequence, `git clone failed (exit ${r.status})`, r.ms);
87
+ process.exitCode = 1;
98
88
  rmrf(workDir);
99
89
  return;
100
90
  }
101
- ok(`cloned notation3tests ${C.dim}(${r.ms} ms)${C.n}`);
91
+ pass(++sequence, 'cloned notation3tests', r.ms);
102
92
 
103
93
  // 2) Install suite dependencies
104
94
  // Notation3tests can carry vulnerabilities in its transient dependency tree.
@@ -106,11 +96,12 @@ function rmrf(p) {
106
96
  // Disable audit/funding output for this integration test install.
107
97
  r = run('npm', ['ci', '--audit=false', '--fund=false'], { cwd: suiteDir });
108
98
  if (r.status !== 0) {
109
- fail(`npm ci failed (exit ${r.status})`);
99
+ failResult(++sequence, `npm ci failed (exit ${r.status})`, r.ms);
100
+ process.exitCode = 1;
110
101
  rmrf(workDir);
111
102
  return;
112
103
  }
113
- ok(`npm ci ${C.dim}(${r.ms} ms)${C.n}`);
104
+ pass(++sequence, 'npm ci', r.ms);
114
105
 
115
106
  // 3) Pack local Eyeling
116
107
  const pack = runCapture('npm', ['pack', '--silent'], {
@@ -120,29 +111,32 @@ function rmrf(p) {
120
111
  });
121
112
  if (pack.status !== 0) {
122
113
  console.error(pack.stderr || '');
123
- fail(`npm pack failed (exit ${pack.status})`);
114
+ failResult(++sequence, `npm pack failed (exit ${pack.status})`, pack.ms);
115
+ process.exitCode = 1;
124
116
  rmrf(workDir);
125
117
  return;
126
118
  }
127
119
  const tgzName = String(pack.stdout).trim().split(/\r?\n/).pop();
128
120
  const tgzPath = path.join(ROOT, tgzName);
129
121
  if (!fs.existsSync(tgzPath)) {
130
- fail(`npm pack did not produce expected tarball: ${tgzPath}`);
122
+ failResult(++sequence, `npm pack did not produce expected tarball: ${tgzPath}`, pack.ms);
123
+ process.exitCode = 1;
131
124
  rmrf(workDir);
132
125
  return;
133
126
  }
134
- ok(`packed ${tgzName} ${C.dim}(${pack.ms} ms)${C.n}`);
127
+ pass(++sequence, `packed ${tgzName}`, pack.ms);
135
128
 
136
129
  // 4) Install local tarball into suite
137
130
  // Keep the install output focused on the actual test run.
138
131
  r = run('npm', ['install', '--no-save', '--audit=false', '--fund=false', tgzPath], { cwd: suiteDir });
139
132
  if (r.status !== 0) {
140
- fail(`npm install eyeling tarball failed (exit ${r.status})`);
133
+ failResult(++sequence, `npm install eyeling tarball failed (exit ${r.status})`, r.ms);
134
+ process.exitCode = 1;
141
135
  rmrf(tgzPath);
142
136
  rmrf(workDir);
143
137
  return;
144
138
  }
145
- ok(`installed local eyeling ${C.dim}(${r.ms} ms)${C.n}`);
139
+ pass(++sequence, 'installed local eyeling', r.ms);
146
140
 
147
141
  // 5) Run suite test target
148
142
  const t0 = Date.now();
@@ -153,7 +147,7 @@ function rmrf(p) {
153
147
  rmrf(tgzPath);
154
148
 
155
149
  if (r.status === 0) {
156
- ok(`notation3tests:eyeling passed ${C.dim}(${totalMs} ms)${C.n}`);
150
+ pass(++sequence, 'notation3tests:eyeling passed', totalMs);
157
151
  if (process.env.EYELING_KEEP_NOTATION3TESTS === '1') {
158
152
  console.log(`${C.dim}Keeping workdir (EYELING_KEEP_NOTATION3TESTS=1):${C.n} ${workDir}`);
159
153
  } else {
@@ -162,7 +156,8 @@ function rmrf(p) {
162
156
  return;
163
157
  }
164
158
 
165
- fail(`notation3tests:eyeling failed (exit ${r.status}) ${C.dim}(${totalMs} ms)${C.n}`);
159
+ failResult(++sequence, `notation3tests:eyeling failed (exit ${r.status})`, totalMs);
160
+ process.exitCode = 1;
166
161
  if (process.env.EYELING_KEEP_NOTATION3TESTS === '1') {
167
162
  console.log(`${C.dim}Keeping workdir (EYELING_KEEP_NOTATION3TESTS=1):${C.n} ${workDir}`);
168
163
  } else {
@@ -6,20 +6,7 @@ const os = require('node:os');
6
6
  const path = require('node:path');
7
7
  const cp = require('node:child_process');
8
8
 
9
- const TTY = process.stdout.isTTY;
10
- const C = TTY
11
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
12
- : { g: '', r: '', y: '', dim: '', n: '' };
13
-
14
- function info(msg) {
15
- console.log(`${C.y}==${C.n} ${msg}`);
16
- }
17
- function ok(msg) {
18
- console.log(`${C.g}OK${C.n} ${msg}`);
19
- }
20
- function fail(msg) {
21
- console.error(`${C.r}FAIL${C.n} ${msg}`);
22
- }
9
+ const { C, detail, failResult, info, pass } = require('./report');
23
10
 
24
11
  function isWin() {
25
12
  return process.platform === 'win32';
@@ -158,6 +145,7 @@ function packTarball(root) {
158
145
 
159
146
  function main() {
160
147
  const suiteStart = Date.now();
148
+ let sequence = 0;
161
149
  const root = path.resolve(__dirname, '..');
162
150
 
163
151
  const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-smoke-'));
@@ -167,15 +155,19 @@ function main() {
167
155
 
168
156
  try {
169
157
  info('Building tarball (npm pack)');
158
+ let stepStart = Date.now();
170
159
  tgzInRoot = packTarball(root);
171
160
  const srcTgz = path.join(root, tgzInRoot);
172
161
  const dstTgz = path.join(tmp, tgzInRoot);
173
162
 
174
163
  fs.renameSync(srcTgz, dstTgz);
164
+ pass(++sequence, `packed ${tgzInRoot}`, Date.now() - stepStart);
175
165
 
176
166
  info('Creating temp project + installing tarball');
167
+ stepStart = Date.now();
177
168
  runChecked(npmCmd(), ['init', '-y'], { cwd: tmp, stdio: 'ignore' });
178
169
  runChecked(npmCmd(), ['install', `./${tgzInRoot}`, '--no-audit', '--no-fund'], { cwd: tmp, stdio: 'inherit' });
170
+ pass(++sequence, 'created temp project and installed tarball', Date.now() - stepStart);
179
171
 
180
172
  info('API smoke test');
181
173
  // Run a tiny API check via node -e
@@ -193,17 +185,18 @@ function main() {
193
185
  console.error('Unexpected output:\\n' + out);
194
186
  process.exit(1);
195
187
  }
196
- console.log('OK: API works');
197
188
  `;
189
+ stepStart = Date.now();
198
190
  runChecked(process.execPath, ['-e', apiCode], { cwd: tmp, stdio: 'inherit' });
199
- ok('API works');
191
+ pass(++sequence, 'API works', Date.now() - stepStart);
200
192
 
201
193
  info('CLI smoke test');
202
194
  const bin = isWin()
203
195
  ? path.join(tmp, 'node_modules', '.bin', 'eyeling.cmd')
204
196
  : path.join(tmp, 'node_modules', '.bin', 'eyeling');
197
+ stepStart = Date.now();
205
198
  runChecked(bin, ['-v'], { cwd: tmp, stdio: 'inherit' });
206
- ok('CLI works');
199
+ pass(++sequence, 'CLI works', Date.now() - stepStart);
207
200
 
208
201
  info('Examples smoke test (installed package)');
209
202
  const pkgRoot = path.join(tmp, 'node_modules', 'eyeling');
@@ -220,11 +213,10 @@ function main() {
220
213
  const SMOKE_EXAMPLES = ['age.n3', 'basic-monadic.n3', 'family-cousins.n3', 'backward.n3', 'context-association.n3'];
221
214
 
222
215
  const tmpExamplesOut = fs.mkdtempSync(path.join(os.tmpdir(), 'eyeling-pkg-examples-'));
223
- let smokeIdx = 1;
224
- const smokePad2 = (n) => String(n).padStart(2, '0');
225
- const smokeOk = (n, msg) => console.log(`${C.g}OK${C.n} ${smokePad2(n)} ${msg}`);
216
+ stepStart = Date.now();
226
217
  try {
227
218
  for (const file of SMOKE_EXAMPLES) {
219
+ const smokeStart = Date.now();
228
220
  const inputPath = path.join(examplesDir, file);
229
221
  const expectedPath = resolveExpectedPath(outputDir, file);
230
222
 
@@ -275,20 +267,21 @@ Output differs for ${file}:`);
275
267
  throw new Error(`Example ${file}: output differs from golden`);
276
268
  }
277
269
 
278
- smokeOk(smokeIdx++, `Example smoke: ${file}`);
270
+ pass(++sequence, `Example smoke: ${file}`, Date.now() - smokeStart);
279
271
  }
280
272
  } finally {
281
273
  rmrf(tmpExamplesOut);
282
274
  }
283
- smokeOk(smokeIdx++, 'Installed examples smoke test passed');
275
+ pass(++sequence, 'Installed examples smoke test passed', Date.now() - stepStart);
284
276
 
285
277
  const suiteMs = Date.now() - suiteStart;
286
278
  console.log('');
287
- ok(`Packaged install smoke test passed ${C.dim}(${suiteMs} ms, ${(suiteMs / 1000).toFixed(2)} s)${C.n}`);
279
+ info(`Packaged install smoke test passed (${suiteMs} ms, ${(suiteMs / 1000).toFixed(2)} s)`);
288
280
  process.exit(0);
289
281
  } catch (e) {
290
282
  console.log('');
291
- fail(e && e.stack ? e.stack : String(e));
283
+ failResult(++sequence, 'packaged install smoke test failed', Date.now() - suiteStart);
284
+ detail(e && e.stack ? e.stack : String(e));
292
285
  process.exit(1);
293
286
  } finally {
294
287
  // If rename failed and the tarball still exists in root, try to delete it
@@ -4,19 +4,9 @@ const assert = require('node:assert/strict');
4
4
  const cp = require('node:child_process');
5
5
  const fs = require('node:fs');
6
6
 
7
- const TTY = process.stdout.isTTY;
8
- const C = TTY ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', n: '\x1b[0m' } : { g: '', r: '', y: '', n: '' };
9
-
10
- function ok(msg) {
11
- console.log(`${C.g}OK ${C.n} ${msg}`);
12
- }
13
- function info(msg) {
14
- console.log(`${C.y}${msg}${C.n}`);
15
- }
16
- function fail(msg) {
17
- console.error(`${C.r}FAIL${C.n} ${msg}`);
18
- }
7
+ const { detail, failResult, info, pass } = require('./report');
19
8
 
9
+ const start = Date.now();
20
10
  try {
21
11
  info('Checking packlist + metadata…');
22
12
 
@@ -65,8 +55,9 @@ try {
65
55
  'missing from npm pack: examples/output/*',
66
56
  );
67
57
 
68
- ok('packlist + metadata sanity checks passed');
58
+ pass(1, 'packlist + metadata sanity checks passed', Date.now() - start);
69
59
  } catch (e) {
70
- fail(e && e.stack ? e.stack : String(e));
60
+ failResult(1, 'packlist + metadata sanity checks failed', Date.now() - start);
61
+ detail(e && e.stack ? e.stack : String(e));
71
62
  process.exit(1);
72
63
  }
@@ -18,45 +18,29 @@ const { setTimeout: sleep } = require('node:timers/promises');
18
18
 
19
19
  const ROOT = path.resolve(__dirname, '..');
20
20
 
21
- const TTY = process.stdout.isTTY;
22
- const C = TTY
23
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
24
- : { g: '', r: '', y: '', dim: '', n: '' };
25
- const msTag = (ms) => `${C.dim}(${ms} ms)${C.n}`;
21
+ const { detail, failResult, info, pass } = require('./report');
26
22
 
27
23
  const TOTAL_TESTS = (fs.readFileSync(__filename, 'utf8').match(/^\s*beginTest\(/gm) || []).length;
28
- const idxWidth = String(Math.max(1, TOTAL_TESTS)).length;
29
24
  let passed = 0;
30
25
  let failed = 0;
31
26
  let currentTest = null;
32
27
  let nonTestFailure = false;
33
28
  const suiteStart = Date.now();
34
29
 
35
- function ok(msg) {
36
- console.log(`${C.g}OK${C.n} ${msg}`);
37
- }
38
- function info(msg) {
39
- console.log(`${C.y}==${C.n} ${msg}`);
40
- }
41
- function fail(msg) {
42
- console.error(`${C.r}FAIL${C.n} ${msg}`);
43
- }
44
30
  function beginTest(msg) {
45
31
  currentTest = { msg, start: Date.now() };
46
32
  }
47
33
  function endTest() {
48
34
  const tc = currentTest;
49
35
  if (!tc) return;
50
- const idx = String(passed + failed + 1).padStart(idxWidth, '0');
51
- ok(`${idx} ${tc.msg} ${msTag(Date.now() - tc.start)}`);
36
+ pass(passed + failed + 1, tc.msg, Date.now() - tc.start);
52
37
  passed += 1;
53
38
  currentTest = null;
54
39
  }
55
40
  function recordCurrentFailure() {
56
41
  const tc = currentTest;
57
42
  if (!tc) return false;
58
- const idx = String(passed + failed + 1).padStart(idxWidth, '0');
59
- fail(`${idx} ${tc.msg} ${msTag(Date.now() - tc.start)}`);
43
+ failResult(passed + failed + 1, tc.msg, Date.now() - tc.start);
60
44
  failed += 1;
61
45
  currentTest = null;
62
46
  return true;
@@ -66,9 +50,9 @@ function printSummary() {
66
50
  const suiteMs = Date.now() - suiteStart;
67
51
  info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
68
52
  if (failed === 0 && !nonTestFailure) {
69
- ok(`All playground tests passed (${passed}/${TOTAL_TESTS})`);
53
+ info(`All playground tests passed (${passed}/${TOTAL_TESTS})`);
70
54
  } else {
71
- fail(`Some playground tests failed (${passed}/${TOTAL_TESTS})`);
55
+ info(`Some playground tests failed (${passed}/${TOTAL_TESTS})`);
72
56
  }
73
57
  }
74
58
 
@@ -1269,6 +1253,6 @@ ${JSON.stringify(last, null, 2)}`);
1269
1253
  main().catch((e) => {
1270
1254
  if (!recordCurrentFailure()) nonTestFailure = true;
1271
1255
  printSummary();
1272
- fail(e && e.stack ? e.stack : String(e));
1256
+ detail(e && e.stack ? e.stack : String(e));
1273
1257
  process.exit(1);
1274
1258
  });
@@ -3,44 +3,7 @@
3
3
 
4
4
  const cp = require('node:child_process');
5
5
 
6
- const USE_COLOR = !process.env.NO_COLOR && (
7
- process.stdout.isTTY ||
8
- (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0')
9
- );
10
- const C = USE_COLOR
11
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', gray: '\x1b[90m', dim: '\x1b[2m', n: '\x1b[0m' }
12
- : { g: '', r: '', y: '', gray: '', dim: '', n: '' };
13
-
14
- function formatDuration(ms) {
15
- if (!Number.isFinite(ms) || ms < 0) return '0 ms';
16
- if (ms < 1000) return `${Math.round(ms)} ms`;
17
- if (ms < 10000) return `${(ms / 1000).toFixed(2)} s`;
18
- if (ms < 60000) return `${(ms / 1000).toFixed(1)} s`;
19
-
20
- const minutes = Math.floor(ms / 60000);
21
- const seconds = ((ms % 60000) / 1000).toFixed(1).padStart(4, '0');
22
- return `${minutes}m ${seconds}s`;
23
- }
24
-
25
- function durationSuffix(ms) {
26
- return ms == null ? '' : ` ${C.gray}${formatDuration(ms)}${C.n}`;
27
- }
28
-
29
- function ok(msg, ms) {
30
- console.log(`${C.g}OK${C.n} ${msg}${durationSuffix(ms)}`);
31
- }
32
-
33
- function fail(msg, ms) {
34
- console.error(`${C.r}FAIL${C.n} ${msg}${durationSuffix(ms)}`);
35
- }
36
-
37
- function info(msg, ms) {
38
- console.log(`${C.y}==${C.n} ${msg}${durationSuffix(ms)}`);
39
- }
40
-
41
- function detail(msg) {
42
- console.log(`${C.dim}${msg}${C.n}`);
43
- }
6
+ const { C, detail, failResult, formatDuration, info, pass } = require('./report');
44
7
 
45
8
  function nowMs() {
46
9
  return Number(process.hrtime.bigint()) / 1e6;
@@ -143,9 +106,6 @@ function resolveSuiteKeys(args) {
143
106
  return keys;
144
107
  }
145
108
 
146
- function sequenceLabel(n) {
147
- return String(n).padStart(3, '0');
148
- }
149
109
 
150
110
  function createLineReader(onLine) {
151
111
  let buffer = '';
@@ -177,12 +137,12 @@ function printEvent(suiteResult, event, totals) {
177
137
  const caseElapsedMs = now - suiteResult.lastCaseAt;
178
138
  suiteResult.lastCaseAt = now;
179
139
 
180
- const message = `${sequenceLabel(totals.sequence)} ${suiteResult.suite.label}: ${event.message}`;
140
+ const message = `${suiteResult.suite.label}: ${event.message}`;
181
141
  if (event.kind === 'ok') {
182
- ok(message, caseElapsedMs);
142
+ pass(totals.sequence, message, caseElapsedMs);
183
143
  totals.passed++;
184
144
  } else {
185
- fail(message, caseElapsedMs);
145
+ failResult(totals.sequence, message, caseElapsedMs);
186
146
  totals.failed++;
187
147
  suiteResult.failedCount++;
188
148
  }
@@ -236,11 +196,11 @@ function runSuite(key, totals) {
236
196
  suiteResult.error = e;
237
197
  if (!sawCase) {
238
198
  totals.sequence++;
239
- fail(`${sequenceLabel(totals.sequence)} ${suite.label}: rdf-test-suite could not start: ${e.message || String(e)}`, elapsedMs);
199
+ failResult(totals.sequence, `${suite.label}: rdf-test-suite could not start: ${e.message || String(e)}`, elapsedMs);
240
200
  totals.failed++;
241
201
  suiteResult.caseCount++;
242
202
  }
243
- console.log(`${C.gray}Suite elapsed ${formatDuration(elapsedMs)}${C.n}`);
203
+ console.log(`${C.dim}Suite elapsed ${formatDuration(elapsedMs)}${C.n}`);
244
204
  console.log('');
245
205
  resolve({ ...suiteResult, status: 1, elapsedMs });
246
206
  return;
@@ -273,21 +233,21 @@ function runSuite(key, totals) {
273
233
  : `${suite.label}: rdf-test-suite exited with ${suiteResult.status === 0 ? 'success' : exitText}`;
274
234
 
275
235
  if (suiteResult.status === 0) {
276
- ok(`${sequenceLabel(totals.sequence)} ${message}`, elapsedMs);
236
+ pass(totals.sequence, message, elapsedMs);
277
237
  totals.passed++;
278
238
  } else {
279
- fail(`${sequenceLabel(totals.sequence)} ${message}`, elapsedMs);
239
+ failResult(totals.sequence, message, elapsedMs);
280
240
  totals.failed++;
281
241
  }
282
242
  suiteResult.caseCount++;
283
243
  } else if (suiteResult.status !== 0 && suiteResult.failedCount === 0) {
284
244
  totals.sequence++;
285
- fail(`${sequenceLabel(totals.sequence)} ${suite.label}: rdf-test-suite exited with status ${suiteResult.status}`, elapsedMs);
245
+ failResult(totals.sequence, `${suite.label}: rdf-test-suite exited with status ${suiteResult.status}`, elapsedMs);
286
246
  totals.failed++;
287
247
  suiteResult.caseCount++;
288
248
  }
289
249
 
290
- console.log(`${C.gray}Suite elapsed ${formatDuration(elapsedMs)}${C.n}`);
250
+ console.log(`${C.dim}Suite elapsed ${formatDuration(elapsedMs)}${C.n}`);
291
251
  console.log('');
292
252
  resolve(suiteResult);
293
253
  });
@@ -299,7 +259,7 @@ async function main() {
299
259
  try {
300
260
  keys = resolveSuiteKeys(process.argv.slice(2));
301
261
  } catch (e) {
302
- fail(e && e.message ? e.message : String(e));
262
+ failResult(1, e && e.message ? e.message : String(e), 0);
303
263
  process.exit(1);
304
264
  return;
305
265
  }
@@ -322,16 +282,17 @@ async function main() {
322
282
  info(`Grand total: ${totals.passed} OK, ${totals.failed} FAIL, ${totals.sequence} RDF 1.2 tests`, grandElapsedMs);
323
283
 
324
284
  if (totals.failed === 0 && results.every((suiteResult) => suiteResult.status === 0)) {
325
- ok(`All RDF 1.2 syntax tests passed (${totals.passed}/${totals.sequence})`);
285
+ info(`All RDF 1.2 syntax tests passed (${totals.passed}/${totals.sequence})`);
326
286
  process.exit(0);
327
287
  return;
328
288
  }
329
289
 
330
- fail(`Some RDF 1.2 syntax tests failed (${totals.passed}/${totals.sequence})`);
290
+ info(`Some RDF 1.2 syntax tests failed (${totals.passed}/${totals.sequence})`);
331
291
  process.exit(1);
332
292
  }
333
293
 
334
294
  main().catch((e) => {
335
- fail(e && e.stack ? e.stack : String(e));
295
+ failResult(1, 'RDF 1.2 syntax test runner failed', 0);
296
+ detail(e && e.stack ? e.stack : String(e));
336
297
  process.exit(1);
337
298
  });
package/test/report.js ADDED
@@ -0,0 +1,67 @@
1
+ 'use strict';
2
+
3
+ const USE_COLOR = !process.env.NO_COLOR && (
4
+ process.stdout.isTTY ||
5
+ (process.env.FORCE_COLOR && process.env.FORCE_COLOR !== '0')
6
+ );
7
+
8
+ const C = USE_COLOR
9
+ ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
10
+ : { g: '', r: '', y: '', dim: '', n: '' };
11
+
12
+ function formatDuration(ms) {
13
+ if (!Number.isFinite(ms) || ms < 0) return '0 ms';
14
+ if (ms < 1000) return `${Math.round(ms)} ms`;
15
+ if (ms < 10000) return `${(ms / 1000).toFixed(2)} s`;
16
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)} s`;
17
+
18
+ const minutes = Math.floor(ms / 60000);
19
+ const seconds = ((ms % 60000) / 1000).toFixed(1).padStart(4, '0');
20
+ return `${minutes}m ${seconds}s`;
21
+ }
22
+
23
+ function durationSuffix(ms) {
24
+ return ms == null ? '' : ` ${C.dim}(${formatDuration(ms)})${C.n}`;
25
+ }
26
+
27
+ function sequenceLabel(n) {
28
+ return String(n).padStart(3, '0');
29
+ }
30
+
31
+ function result(status, n, description, ms) {
32
+ const color = status === 'OK' ? C.g : C.r;
33
+ const stream = status === 'OK' ? console.log : console.error;
34
+ stream(`${color}${status}${C.n} ${sequenceLabel(n)} ${description}${durationSuffix(ms)}`);
35
+ }
36
+
37
+ function pass(n, description, ms) {
38
+ result('OK', n, description, ms);
39
+ }
40
+
41
+ function failResult(n, description, ms) {
42
+ result('FAIL', n, description, ms);
43
+ }
44
+
45
+ function info(msg, ms) {
46
+ console.log(`${C.y}==${C.n} ${msg}${durationSuffix(ms)}`);
47
+ }
48
+
49
+ function warn(msg) {
50
+ console.log(`${C.y}SKIP${C.n} ${msg}`);
51
+ }
52
+
53
+ function detail(msg) {
54
+ console.log(`${C.dim}${msg}${C.n}`);
55
+ }
56
+
57
+ module.exports = {
58
+ C,
59
+ detail,
60
+ durationSuffix,
61
+ failResult,
62
+ formatDuration,
63
+ info,
64
+ pass,
65
+ sequenceLabel,
66
+ warn,
67
+ };
@@ -9,23 +9,7 @@ const path = require('node:path');
9
9
  const root = path.resolve(__dirname, '..');
10
10
  const eyelingJsPath = path.join(root, 'eyeling.js');
11
11
 
12
- const TTY = process.stdout.isTTY;
13
- const C = TTY
14
- ? { g: '\x1b[32m', r: '\x1b[31m', y: '\x1b[33m', dim: '\x1b[2m', n: '\x1b[0m' }
15
- : { g: '', r: '', y: '', dim: '', n: '' };
16
-
17
- function ok(msg) {
18
- console.log(`${C.g}OK ${C.n} ${msg}`);
19
- }
20
- function info(msg) {
21
- console.log(`${C.y}==${C.n} ${msg}`);
22
- }
23
- function fail(msg) {
24
- console.error(`${C.r}FAIL${C.n} ${msg}`);
25
- }
26
- function numberedName(index, name) {
27
- return `${String(index + 1).padStart(3, '0')} ${name}`;
28
- }
12
+ const { detail, failResult, info, pass } = require('./report');
29
13
  function msNow() {
30
14
  return Date.now();
31
15
  }
@@ -354,15 +338,16 @@ const cases = [
354
338
  let failed = 0;
355
339
  for (const [index, tc] of cases.entries()) {
356
340
  const tmp = mkTmpDir();
357
- const testName = numberedName(index, tc.name);
341
+ const testNr = index + 1;
342
+ const testName = tc.name;
358
343
  const start = msNow();
359
344
  try {
360
345
  await tc.run(tmp);
361
- ok(`${testName} ${C.dim}(${msNow() - start} ms)${C.n}`);
346
+ pass(testNr, testName, msNow() - start);
362
347
  passed++;
363
348
  } catch (e) {
364
- fail(`${testName} ${C.dim}(${msNow() - start} ms)${C.n}`);
365
- fail(e && e.stack ? e.stack : String(e));
349
+ failResult(testNr, testName, msNow() - start);
350
+ detail(e && e.stack ? e.stack : String(e));
366
351
  failed++;
367
352
  } finally {
368
353
  rmrf(tmp);
@@ -370,14 +355,15 @@ const cases = [
370
355
  }
371
356
  console.log('');
372
357
  const suiteMs = msNow() - suiteStart;
373
- console.log(`${C.y}==${C.n} Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
358
+ info(`Total elapsed: ${suiteMs} ms (${(suiteMs / 1000).toFixed(2)} s)`);
374
359
  if (failed === 0) {
375
- ok(`All stream-message tests passed (${passed}/${cases.length})`);
360
+ info(`All stream-message tests passed (${passed}/${cases.length})`);
376
361
  process.exit(0);
377
362
  }
378
- fail(`Some stream-message tests failed (${passed}/${cases.length})`);
363
+ info(`Some stream-message tests failed (${passed}/${cases.length})`);
379
364
  process.exit(1);
380
365
  })().catch((e) => {
381
- fail(e && e.stack ? e.stack : String(e));
366
+ failResult(1, 'stream-message test runner failed', 0);
367
+ detail(e && e.stack ? e.stack : String(e));
382
368
  process.exit(1);
383
369
  });