From fd68b6ac1620d3ac9b6e98eeee7a507c3ccb3d79 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Sat, 9 Nov 2024 19:50:31 -0500 Subject: [PATCH] test(cosmic-swingset): Include the last three lines of stderr when a `make` test fails (#10438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Extracted from #10165 to independently resolve a preƫxisting TODO. ### Security Considerations n/a ### Scaling Considerations n/a ### Documentation Considerations n/a ### Testing Considerations Makes it easier to debug failures in `make`-dependent cosmic-swingset tests. ### Upgrade Considerations n/a --- packages/cosmic-swingset/test/scenario2.js | 36 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/packages/cosmic-swingset/test/scenario2.js b/packages/cosmic-swingset/test/scenario2.js index a489195be51..fa1a861302b 100644 --- a/packages/cosmic-swingset/test/scenario2.js +++ b/packages/cosmic-swingset/test/scenario2.js @@ -1,4 +1,6 @@ // @ts-check +import { text } from 'node:stream/consumers'; + const { freeze, entries } = Object; const onlyStderr = ['ignore', 'ignore', 'inherit']; @@ -10,13 +12,35 @@ export const pspawn = (bin, { spawn, cwd }) => { /** @type {import('child_process').ChildProcess} */ let child; const exit = new Promise((resolve, reject) => { - // console.debug('spawn', bin, args, { cwd: makefileDir, ...opts }); - child = spawn(bin, args, { cwd, ...opts }); - child.addListener('exit', code => { + // When stderr is otherwise ignored, spy on it for inclusion in error messages. + /** @type {Promise | undefined} */ + let stderrP; + // https://nodejs.org/docs/latest/api/child_process.html#optionsstdio + let { stdio = 'pipe' } = opts; + if (stdio === 'ignore' || stdio[2] === 'ignore') { + stdio = typeof stdio === 'string' ? [stdio, stdio, stdio] : [...stdio]; + stdio[2] = 'pipe'; + } else { + // The caller is not ignoring stderr, so we pretend it is empty here. + stderrP = Promise.resolve(''); + } + const resolvedOpts = { cwd, ...opts, stdio }; + // console.debug('spawn', bin, args, resolvedOpts); + child = spawn(bin, args, resolvedOpts); + if (!stderrP) { + assert(child.stderr); + stderrP = text(child.stderr); + child.stderr = null; + } + child.addListener('exit', async code => { if (code !== 0) { - // TODO: Include ~3 lines from child.stderr or child.stdout if present. - // see https://nodejs.org/api/child_process.html#child_processspawncommand-args-options - reject(Error(`exit ${code} from command: ${bin} ${args}`)); + let msg = `exit ${code} from command: ${bin} ${args}`; + const errStr = await stderrP; + if (errStr) { + const errTail = errStr.split('\n').slice(-3); + msg = `${msg}\n${errTail.map(line => ` ${line}`).join('\n')}`; + } + reject(Error(msg)); return; } resolve(0);