mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-02 04:50:06 -05:00
catch child process killed with a signal (#5810)
* WIP: catch child process killed with a signal * unit test getError * we don't need custom exit code * Update cli/lib/exec/spawn.js Co-Authored-By: Zach Bloomquist <github@chary.us> * Update cli/lib/errors.js Co-Authored-By: Zach Bloomquist <github@chary.us> * update snapshots with wording
This commit is contained in:
committed by
Zach Bloomquist
parent
0311c580c8
commit
64f5bf0870
@@ -29,6 +29,7 @@ Cypress Version: 1.2.3
|
||||
exports['errors individual has the following errors 1'] = [
|
||||
"CYPRESS_RUN_BINARY",
|
||||
"binaryNotExecutable",
|
||||
"childProcessKilled",
|
||||
"failedDownload",
|
||||
"failedUnzip",
|
||||
"invalidCacheDirectory",
|
||||
@@ -71,3 +72,27 @@ If you are using Docker, we provide containers with all required dependencies in
|
||||
Platform: test platform (Foo-OsVersion)
|
||||
Cypress Version: 1.2.3
|
||||
`
|
||||
|
||||
exports['child kill error object'] = {
|
||||
"description": "The Test Runner unexpectedly exited via a [36mexit[39m event with signal [36mSIGKILL[39m",
|
||||
"solution": "Please search Cypress documentation for possible solutions:\n\n [34mhttps://on.cypress.io[39m\n\nCheck if there is a GitHub issue describing this crash:\n\n [34mhttps://github.com/cypress-io/cypress/issues[39m\n\nConsider opening a new issue."
|
||||
}
|
||||
|
||||
exports['Error message'] = `
|
||||
The Test Runner unexpectedly exited via a [36mexit[39m event with signal [36mSIGKILL[39m
|
||||
|
||||
Please search Cypress documentation for possible solutions:
|
||||
|
||||
[34mhttps://on.cypress.io[39m
|
||||
|
||||
Check if there is a GitHub issue describing this crash:
|
||||
|
||||
[34mhttps://github.com/cypress-io/cypress/issues[39m
|
||||
|
||||
Consider opening a new issue.
|
||||
|
||||
----------
|
||||
|
||||
Platform: test platform (Foo-OsVersion)
|
||||
Cypress Version: 1.2.3
|
||||
`
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
exports['lib/exec/spawn .start detects kill signal exits with error on SIGKILL 1'] = `
|
||||
The Test Runner unexpectedly exited via a [36mexit[39m event with signal [36mSIGKILL[39m
|
||||
|
||||
Please search Cypress documentation for possible solutions:
|
||||
|
||||
[34mhttps://on.cypress.io[39m
|
||||
|
||||
Check if there is a GitHub issue describing this crash:
|
||||
|
||||
[34mhttps://github.com/cypress-io/cypress/issues[39m
|
||||
|
||||
Consider opening a new issue.
|
||||
|
||||
----------
|
||||
|
||||
Platform: darwin (Foo-OsVersion)
|
||||
Cypress Version: 0.0.0
|
||||
`
|
||||
+50
-11
@@ -168,20 +168,21 @@ const versionMismatch = {
|
||||
solution: 'Install Cypress and verify app again',
|
||||
}
|
||||
|
||||
const solutionUnknown = stripIndent`
|
||||
Please search Cypress documentation for possible solutions:
|
||||
|
||||
${chalk.blue(docsUrl)}
|
||||
|
||||
Check if there is a GitHub issue describing this crash:
|
||||
|
||||
${chalk.blue(util.issuesUrl)}
|
||||
|
||||
Consider opening a new issue.
|
||||
`
|
||||
const unexpected = {
|
||||
description:
|
||||
'An unexpected error occurred while verifying the Cypress executable.',
|
||||
solution: stripIndent`
|
||||
Please search Cypress documentation for possible solutions:
|
||||
|
||||
${chalk.blue(docsUrl)}
|
||||
|
||||
Check if there is a GitHub issue describing this crash:
|
||||
|
||||
${chalk.blue(util.issuesUrl)}
|
||||
|
||||
Consider opening a new issue.
|
||||
`,
|
||||
solution: solutionUnknown,
|
||||
}
|
||||
|
||||
const invalidCypressEnv = {
|
||||
@@ -191,6 +192,20 @@ const invalidCypressEnv = {
|
||||
exitCode: 11,
|
||||
}
|
||||
|
||||
/**
|
||||
* This error happens when CLI detects that the child Test Runner process
|
||||
* was killed with a signal, like SIGBUS
|
||||
* @see https://github.com/cypress-io/cypress/issues/5808
|
||||
* @param {'close'|'event'} eventName Child close event name
|
||||
* @param {string} signal Signal that closed the child process, like "SIGBUS"
|
||||
*/
|
||||
const childProcessKilled = (eventName, signal) => {
|
||||
return {
|
||||
description: `The Test Runner unexpectedly exited via a ${chalk.cyan(eventName)} event with signal ${chalk.cyan(signal)}`,
|
||||
solution: solutionUnknown,
|
||||
}
|
||||
}
|
||||
|
||||
const removed = {
|
||||
CYPRESS_BINARY_VERSION: {
|
||||
description: stripIndent`
|
||||
@@ -240,6 +255,28 @@ function addPlatformInformation (info) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an error object (see the errors above), forms error message text with details,
|
||||
* then resolves with Error instance you can throw or reject with.
|
||||
* @param {object} errorObject
|
||||
* @returns {Promise<Error>} resolves with an Error
|
||||
* @example
|
||||
```js
|
||||
// inside a Promise with "resolve" and "reject"
|
||||
const errorObject = childProcessKilled('exit', 'SIGKILL')
|
||||
return getError(errorObject).then(reject)
|
||||
```
|
||||
*/
|
||||
function getError (errorObject) {
|
||||
return formErrorText(errorObject).then((errorMessage) => {
|
||||
const err = new Error(errorMessage)
|
||||
|
||||
err.known = true
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms nice error message with error and platform information,
|
||||
* and if possible a way to solve it. Resolves with a string.
|
||||
@@ -355,6 +392,7 @@ module.exports = {
|
||||
// formError,
|
||||
formErrorText,
|
||||
throwFormErrorText,
|
||||
getError,
|
||||
hr,
|
||||
errors: {
|
||||
nonZeroExitCodeXvfb,
|
||||
@@ -373,5 +411,6 @@ module.exports = {
|
||||
removed,
|
||||
CYPRESS_RUN_BINARY,
|
||||
smokeTestFailure,
|
||||
childProcessKilled,
|
||||
},
|
||||
}
|
||||
|
||||
+11
-2
@@ -10,7 +10,7 @@ const util = require('../util')
|
||||
const state = require('../tasks/state')
|
||||
const xvfb = require('./xvfb')
|
||||
const verify = require('../tasks/verify')
|
||||
const { throwFormErrorText, errors } = require('../errors')
|
||||
const errors = require('../errors')
|
||||
|
||||
const isXlibOrLibudevRe = /^(?:Xlib|libudev)/
|
||||
const isHighSierraWarningRe = /\*\*\* WARNING/
|
||||
@@ -140,6 +140,13 @@ module.exports = {
|
||||
function resolveOn (event) {
|
||||
return function (code, signal) {
|
||||
debug('child event fired %o', { event, code, signal })
|
||||
|
||||
if (code === null) {
|
||||
const errorObject = errors.errors.childProcessKilled(event, signal)
|
||||
|
||||
return errors.getError(errorObject).then(reject)
|
||||
}
|
||||
|
||||
resolve(code)
|
||||
}
|
||||
}
|
||||
@@ -251,7 +258,9 @@ module.exports = {
|
||||
|
||||
return code
|
||||
})
|
||||
.catch(throwFormErrorText(errors.unexpected))
|
||||
// we can format and handle an error message from the code above
|
||||
// prevent wrapping error again by using "known: undefined" filter
|
||||
.catch({ known: undefined }, errors.throwFormErrorText(errors.errors.unexpected))
|
||||
}
|
||||
|
||||
if (needsXvfb) {
|
||||
|
||||
@@ -2,7 +2,7 @@ require('../spec_helper')
|
||||
|
||||
const os = require('os')
|
||||
const snapshot = require('../support/snapshot')
|
||||
const { errors, formErrorText } = require(`${lib}/errors`)
|
||||
const { errors, getError, formErrorText } = require(`${lib}/errors`)
|
||||
const util = require(`${lib}/util`)
|
||||
|
||||
describe('errors', function () {
|
||||
@@ -19,6 +19,20 @@ describe('errors', function () {
|
||||
})
|
||||
})
|
||||
|
||||
context('getError', () => {
|
||||
it('forms full message and creates Error object', () => {
|
||||
const errObject = errors.childProcessKilled('exit', 'SIGKILL')
|
||||
|
||||
snapshot('child kill error object', errObject)
|
||||
|
||||
return getError(errObject).then((e) => {
|
||||
expect(e).to.be.an('Error')
|
||||
expect(e).to.have.property('known', true)
|
||||
snapshot('Error message', e.message)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context('.errors.formErrorText', function () {
|
||||
it('returns fully formed text message', () => {
|
||||
expect(missingXvfb).to.be.an('object')
|
||||
|
||||
@@ -7,6 +7,7 @@ const tty = require('tty')
|
||||
const path = require('path')
|
||||
const EE = require('events')
|
||||
const mockedEnv = require('mocked-env')
|
||||
const debug = require('debug')('test')
|
||||
|
||||
const state = require(`${lib}/tasks/state`)
|
||||
const xvfb = require(`${lib}/exec/xvfb`)
|
||||
@@ -14,6 +15,7 @@ const spawn = require(`${lib}/exec/spawn`)
|
||||
const verify = require(`${lib}/tasks/verify`)
|
||||
const util = require(`${lib}/util.js`)
|
||||
const expect = require('chai').expect
|
||||
const snapshot = require('../../support/snapshot')
|
||||
|
||||
const cwd = process.cwd()
|
||||
|
||||
@@ -171,6 +173,20 @@ describe('lib/exec/spawn', function () {
|
||||
})
|
||||
})
|
||||
|
||||
context('detects kill signal', function () {
|
||||
it('exits with error on SIGKILL', function () {
|
||||
this.spawnedProcess.on.withArgs('exit').yieldsAsync(null, 'SIGKILL')
|
||||
|
||||
return spawn.start('--foo')
|
||||
.then(() => {
|
||||
throw new Error('should have hit error handler but did not')
|
||||
}, (e) => {
|
||||
debug('error message', e.message)
|
||||
snapshot(e.message)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('does not start xvfb when its not needed', function () {
|
||||
this.spawnedProcess.on.withArgs('close').yieldsAsync(0)
|
||||
|
||||
@@ -246,6 +262,7 @@ describe('lib/exec/spawn', function () {
|
||||
.then(() => {
|
||||
throw new Error('should have hit error handler but did not')
|
||||
}, (e) => {
|
||||
debug('error message', e.message)
|
||||
expect(e.message).to.include(msg)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user