Skip to content

Commit

Permalink
Improve errors
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Oct 30, 2022
1 parent caf0927 commit cb957d7
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 25 deletions.
5 changes: 3 additions & 2 deletions src/combination/tasks/find.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const findTasks = async function ({
noDimensions,
},
)
validateDuplicateTaskIds(ids)
validateDuplicateTaskIds(ids, runner)
return ids.map((id) => ({ id, taskPath, runner }))
} catch (cause) {
throw new AnyError(`Tasks file: "${taskPath}"`, { cause })
Expand All @@ -37,12 +37,13 @@ export const findTasks = async function ({
// Using the same task id is allowed through in different:
// - Runners: to compare the same task across runners
// - Task files: to override shared configuration's tasks
const validateDuplicateTaskIds = function (ids) {
const validateDuplicateTaskIds = function (ids, { bugs }) {
const duplicateId = ids.find(isDuplicateId)

if (duplicateId !== undefined) {
throw new PluginError(
`Task "${duplicateId}" must not be defined several times.`,
{ bugs },
)
}
}
Expand Down
9 changes: 5 additions & 4 deletions src/combination/tasks/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,33 @@ import { computeRunnerVersions } from '../../top/system/versions/compute.js'
// Select the runners and retrieve their related spawn options using
// `runner.launch()`
export const loadRunner = async function (
{ id, config, launch },
{ id, config, launch, bugs },
cwd,
commonVersions,
) {
const {
spawn,
spawnOptions = {},
versions,
} = await launchRunner({ id, config, launch })
} = await launchRunner({ id, config, launch, bugs })
const versionsA = await computeRunnerVersions({
commonVersions,
versions,
id,
spawnOptions,
cwd,
bugs,
})
return { id, spawn, spawnOptions, versions: versionsA, config }
}

// Fire `runner.launch()`.
// Errors are always considered plugin errors.
// User errors should be captured in `plugin.config` instead.
const launchRunner = async function ({ id, config, launch }) {
const launchRunner = async function ({ id, config, launch, bugs }) {
try {
return await launch(config)
} catch (cause) {
throw new PluginError(`Could not launch runner "${id}"`, { cause })
throw new PluginError(`Could not launch runner "${id}"`, { cause, bugs })
}
}
3 changes: 2 additions & 1 deletion src/report/contents/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const callReportFunc = async function ({
startData,
config: reporterConfig,
config: { format, output, colors },
bugs,
},
}) {
const reporterSpecificConfig = excludeKeys(
Expand All @@ -40,7 +41,7 @@ const callReportFunc = async function ({
])
return { content, result, format, footerString, output, colors }
} catch (cause) {
throw new PluginError(`Could not call reporter "${id}".`, { cause })
throw new PluginError(`Could not call reporter "${id}".`, { cause, bugs })
}
}

Expand Down
14 changes: 7 additions & 7 deletions src/report/start_end.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ export const startReporters = async function (config) {
}

const startReporter = async function (reporter) {
if (reporter.start === undefined) {
const { start, id, bugs } = reporter

if (start === undefined) {
return reporter
}

try {
const startData = await reporter.start()
const startData = await start()
return { ...reporter, startData }
} catch (cause) {
throw new PluginError(`Could not start reporter "${reporter.id}".`, {
cause,
})
throw new PluginError(`Could not start reporter "${id}".`, { cause, bugs })
}
}

Expand All @@ -26,14 +26,14 @@ export const endReporters = async function ({ reporters }) {
await Promise.all(reporters.map(endReporter))
}

const endReporter = async function ({ end, startData, id }) {
const endReporter = async function ({ end, startData, id, bugs }) {
if (end === undefined) {
return
}

try {
await end(startData)
} catch (cause) {
throw new PluginError(`Could not end reporter "${id}".`, { cause })
throw new PluginError(`Could not end reporter "${id}".`, { cause, bugs })
}
}
4 changes: 2 additions & 2 deletions src/run/process/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { PluginError, UserError } from '../../error/main.js'
// Throws when process exit as it spawns.
// We distinguish it from process exits after spawning, since this is a
// plugin error, not a user error.
export const throwOnSpawnExit = async function (childProcess) {
export const throwOnSpawnExit = async function (childProcess, bugs) {
const { message = DEFAULT_SPAWN_ERROR } = await childProcess
throw new PluginError(message)
throw new PluginError(message, { bugs })
}

const DEFAULT_SPAWN_ERROR = 'The runner exited while starting.'
Expand Down
7 changes: 4 additions & 3 deletions src/run/process/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const spawnRunnerProcess = async function ({
id,
spawn: [file, ...args],
spawnOptions,
bugs,
},
},
},
Expand All @@ -58,7 +59,7 @@ export const spawnRunnerProcess = async function ({
},
cwd,
)
await waitForIpcSetup(childProcess, server)
await waitForIpcSetup(childProcess, server, bugs)
const onTaskExit = noUnhandledRejection(throwOnTaskExit(childProcess))
return { childProcess, onTaskExit }
} catch (cause) {
Expand All @@ -79,9 +80,9 @@ const getStdio = function (logsStream) {
}

// Wait for IPC to be initialized. Throw if process exits before that.
const waitForIpcSetup = async function (childProcess, server) {
const waitForIpcSetup = async function (childProcess, server, bugs) {
await Promise.race([
throwOnSpawnExit(childProcess),
throwOnSpawnExit(childProcess, bugs),
receiveReturnValue(server),
])
}
Expand Down
27 changes: 21 additions & 6 deletions src/top/system/versions/compute.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ export const computeRunnerVersions = async function ({
id,
spawnOptions,
cwd,
bugs,
}) {
const dedupedVersions = mapValues(versions, keepOriginalName)
const dedupedVersionsA = mapKeys(dedupedVersions, dedupeVersion)
const dedupedVersionsB = await pProps(dedupedVersionsA, ({ name, version }) =>
computeRunnerVersion({ name, version, id, spawnOptions, cwd }),
computeRunnerVersion({ name, version, id, spawnOptions, cwd, bugs }),
)
const versionsA = mapValues(
versions,
Expand Down Expand Up @@ -61,13 +62,26 @@ const computeRunnerVersion = async function ({
id,
spawnOptions,
cwd,
bugs,
}) {
const versionA = await getRunnerVersion({ version, id, spawnOptions, cwd })
validateVersion(versionA, name, id)
const versionA = await getRunnerVersion({
version,
id,
spawnOptions,
cwd,
bugs,
})
validateVersion({ version: versionA, name, id, bugs })
return versionA
}

const getRunnerVersion = async function ({ version, id, spawnOptions, cwd }) {
const getRunnerVersion = async function ({
version,
id,
spawnOptions,
cwd,
bugs,
}) {
if (typeof version === 'string') {
return version
}
Expand All @@ -83,19 +97,20 @@ const getRunnerVersion = async function ({ version, id, spawnOptions, cwd }) {
throw new PluginError(
`Could not start runner "${id}".
Retrieving runner versions failed: ${version.join(' ')}`,
{ cause },
{ cause, bugs },
)
}
}

// When merging runners and results with different values of the same version
// property, we concatenate them. This becomes ambiguous if the version value
// contains the same separator.
const validateVersion = function (version, name, id) {
const validateVersion = function ({ version, name, id, bugs }) {
if (version.includes(VERSIONS_VALUE_SEPARATOR)) {
throw new PluginError(
`Could not start runner "${id}".
Computing runner's "${name}" version failed because it cannot contain "${VERSIONS_VALUE_SEPARATOR}": "${version}"`,
{ bugs },
)
}
}

0 comments on commit cb957d7

Please sign in to comment.