|
1 | 1 | import { execFile } from 'child_process'; |
2 | 2 | import fs from 'fs'; |
3 | 3 | import jsesc from 'jsesc'; |
| 4 | +import circularSafeStringify from 'json-stringify-safe'; |
4 | 5 | import { EOL } from 'os'; |
5 | 6 | import path from 'path'; |
6 | 7 | import { preview } from 'preview'; |
7 | 8 | import { shape, Shape } from 'shape'; |
8 | 9 | import { file as makeTmpFile } from 'tmp-promise'; |
9 | | -import * as uuid from 'uuid'; |
10 | 10 | import { |
11 | 11 | Cancelled, |
12 | 12 | EVAL_ERRORS, |
13 | 13 | InvalidDependentPanelError, |
| 14 | + NoResultError, |
14 | 15 | } from '../../shared/errors'; |
15 | 16 | import log from '../../shared/log'; |
| 17 | +import { newId } from '../../shared/object'; |
16 | 18 | import { PanelBody } from '../../shared/rpc'; |
17 | 19 | import { |
18 | 20 | ConnectorInfo, |
@@ -131,9 +133,7 @@ export async function evalInSubprocess( |
131 | 133 | ensureFile(path.join(CODE_ROOT, 'coverage', 'fake.cov')); |
132 | 134 | args.unshift('-test.run'); |
133 | 135 | args.unshift('^TestRunMain$'); |
134 | | - args.unshift( |
135 | | - '-test.coverprofile=coverage/gorunner.' + uuid.v4() + '.cov' |
136 | | - ); |
| 136 | + args.unshift('-test.coverprofile=coverage/gorunner.' + newId() + '.cov'); |
137 | 137 | } |
138 | 138 |
|
139 | 139 | log.info(`Launching "${base} ${args.join(' ')}"`); |
@@ -189,20 +189,31 @@ export async function evalInSubprocess( |
189 | 189 | } |
190 | 190 | }); |
191 | 191 |
|
192 | | - const resultMeta = fs.readFileSync(tmp.path).toString(); |
193 | | - let parsePartial = !resultMeta; |
| 192 | + const resultMeta = JSON.parse(fs.readFileSync(tmp.path).toString()); |
| 193 | + let parsePartial = typeof resultMeta.preview === 'undefined'; |
194 | 194 | if (!parsePartial) { |
195 | | - const rm: Partial<PanelResult> = JSON.parse(resultMeta); |
| 195 | + const rm: Partial<PanelResult> = resultMeta; |
196 | 196 | // Case of existing Node.js runner |
197 | 197 | return [rm, stderr]; |
198 | 198 | } |
199 | 199 |
|
200 | 200 | // Case of new Go runner |
201 | 201 | const projectResultsFile = getProjectResultsFile(projectName); |
202 | | - const rm: Partial<PanelResult> = parsePartialJSONFile( |
203 | | - projectResultsFile + panel.id |
204 | | - ); |
205 | | - return [rm, stderr]; |
| 202 | + let rm: Partial<PanelResult>; |
| 203 | + try { |
| 204 | + rm = parsePartialJSONFile(projectResultsFile + panel.id); |
| 205 | + } catch (e) { |
| 206 | + if (!e.stdout) { |
| 207 | + e.stdout = resultMeta.stdout; |
| 208 | + } |
| 209 | + |
| 210 | + if (e instanceof NoResultError && resultMeta.exception) { |
| 211 | + // do nothing. The underlying exception is more interesting |
| 212 | + } else { |
| 213 | + throw e; |
| 214 | + } |
| 215 | + } |
| 216 | + return [{ ...rm, ...resultMeta }, stderr]; |
206 | 217 | } finally { |
207 | 218 | try { |
208 | 219 | if (pid) { |
@@ -352,20 +363,36 @@ export const makeEvalHandler = (subprocessEval?: { |
352 | 363 | dispatch, |
353 | 364 | subprocessEval |
354 | 365 | ); |
355 | | - |
356 | | - // This is to handle "exceptions" within the runner. |
357 | | - if (res.exception) { |
358 | | - throw res.exception; |
359 | | - } |
360 | | - // The outer try-catch is to handle exceptions within this Node code |
361 | 366 | } catch (e) { |
362 | 367 | log.error(e); |
363 | 368 | res.exception = e; |
364 | | - if (!EVAL_ERRORS.find((ee) => ee.name === e.name) && stderr) { |
365 | | - // Just a generic exception, we already caught all info in `stderr`, so just throw that. |
366 | | - res.exception = stderr; |
| 369 | + if (res.exception.stdout) { |
| 370 | + res.stdout = res.exception.stdout; |
| 371 | + delete res.exception.stdout; |
367 | 372 | } |
368 | 373 | } |
| 374 | + |
| 375 | + if ( |
| 376 | + res.exception && |
| 377 | + !EVAL_ERRORS.find((ee) => ee.name === res.exception.name) && |
| 378 | + stderr |
| 379 | + ) { |
| 380 | + // Just a generic exception, we already caught all info in `stderr`, so just store that. |
| 381 | + res.exception = new Error(res.stdout || stderr); |
| 382 | + } |
| 383 | + |
| 384 | + // I'm not really sure why this is necessary but sometimes the |
| 385 | + // exception comes out in an object that can't be stringified. And |
| 386 | + // yet I don't believe there's any throwing of errors from the Go |
| 387 | + // code. |
| 388 | + if (res.exception && circularSafeStringify(res.exception) === '{}') { |
| 389 | + res.exception = { |
| 390 | + name: res.exception.name, |
| 391 | + message: res.exception.message, |
| 392 | + ...res.exception, |
| 393 | + }; |
| 394 | + } |
| 395 | + |
369 | 396 | res.lastRun = start; |
370 | 397 | res.loading = false; |
371 | 398 | res.elapsed = new Date().valueOf() - start.valueOf(); |
|
0 commit comments