Skip to content

Commit

Permalink
fix: preserve orphaned process electron (#29515)
Browse files Browse the repository at this point in the history
  • Loading branch information
AtofStryker committed May 20, 2024
1 parent 3a00948 commit e39e748
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 9 deletions.
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ _Released 5/21/2024 (PENDING)_

**Bugfixes:**

- Fixed an issue where orphaned Electron processes were inadvertently terminating the browser's CRI client. Fixes [#28397](https://github.com/cypress-io/cypress/issues/28397). Fixed in [#29515](https://github.com/cypress-io/cypress/pull/29515).
- Fixed an issue where Cypress was unable to search in the Specs list for files or folders containing numbers. Fixes [#29034](https://github.com/cypress-io/cypress/issues/29034).
- Fixed an issue where Cypress would use the wrong URL to upload Test Replay recordings when it wasn't able to determine the upload URL. It now displays an error when the upload URL cannot be determined, rather than a "Request Entity Too Large" error. Addressed in [#29512](https://github.com/cypress-io/cypress/pull/29512).

Expand Down
17 changes: 12 additions & 5 deletions packages/server/lib/browsers/electron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const _getAutomation = async function (win, options: BrowserLaunchOpts, parent)
const port = getRemoteDebuggingPort()

if (!browserCriClient) {
debug(`browser CRI is not set. Creating...`)
browserCriClient = await BrowserCriClient.create({
hosts: ['127.0.0.1'],
port,
Expand All @@ -68,6 +69,7 @@ const _getAutomation = async function (win, options: BrowserLaunchOpts, parent)
const pageCriClient = await browserCriClient.attachToTargetUrl('about:blank')

const sendClose = async () => {
debug(`sendClose called, browserCriClient is set? ${!!browserCriClient}`)
if (browserCriClient) {
const gracefulShutdown = true

Expand Down Expand Up @@ -443,10 +445,15 @@ export = {
* Clear instance state for the electron instance, this is normally called on kill or on exit, for electron there isn't any state to clear.
*/
clearInstanceState (options: GracefulShutdownOptions = {}) {
debug('closing remote interface client', { options })
// Do nothing on failure here since we're shutting down anyway
browserCriClient?.close(options.gracefulShutdown).catch(() => {})
browserCriClient = null
// in the case of orphaned browser clients, we should preserve the CRI client as it is connected
// to the same host regardless of window
debug('clearInstanceState called with options', { options })
if (!options.shouldPreserveCriClient) {
debug('closing remote interface client')
// Do nothing on failure here since we're shutting down anyway
browserCriClient?.close(options.gracefulShutdown).catch(() => {})
browserCriClient = null
}
},

connectToNewSpec (browser: Browser, options: ElectronOpts, automation: Automation) {
Expand Down Expand Up @@ -534,7 +541,7 @@ export = {
allPids: [mainPid],
browserWindow: win,
kill (this: BrowserInstance) {
clearInstanceState({ gracefulShutdown: true })
clearInstanceState({ gracefulShutdown: true, shouldPreserveCriClient: this.isOrphanedBrowserProcess })

if (this.isProcessExit) {
// if the process is exiting, all BrowserWindows will be destroyed anyways
Expand Down
13 changes: 9 additions & 4 deletions packages/server/lib/browsers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ interface KillOptions {
isProcessExit?: boolean
nullOut?: boolean
unbind?: boolean
isOrphanedBrowserProcess?: boolean
}

const kill = (options: KillOptions = {}) => {
options = _.defaults({}, options, {
instance,
isProcessExit: false,
isOrphanedBrowserProcess: false,
unbind: true,
nullOut: true,
})
Expand Down Expand Up @@ -60,7 +62,7 @@ const kill = (options: KillOptions = {}) => {
debug('killing browser process')

instanceToKill.isProcessExit = options.isProcessExit

instanceToKill.isOrphanedBrowserProcess = options.isOrphanedBrowserProcess
instanceToKill.kill()
})
}
Expand Down Expand Up @@ -180,7 +182,7 @@ export = {

const _instance = await browserLauncher.open(browser, options.url, options, automation, ctx.coreData.servers.cdpSocketServer)

debug('browser opened')
debug(`browser opened for launch ${thisLaunchAttempt}`)

// in most cases, we'll kill any running browser instance before launching
// a new one when we call `await kill()` early in this function.
Expand All @@ -204,8 +206,11 @@ export = {
// this browser, it means it has been orphaned and should be terminated.
//
// https://github.com/cypress-io/cypress/issues/24377
if (thisLaunchAttempt !== launchAttempt) {
await kill({ instance: _instance, nullOut: false })
const isOrphanedBrowserProcess = thisLaunchAttempt !== launchAttempt

if (isOrphanedBrowserProcess) {
debug(`killing process because launch attempt: ${thisLaunchAttempt} does not match current launch attempt: ${launchAttempt}`)
await kill({ instance: _instance, isOrphanedBrowserProcess, nullOut: false })

return null
}
Expand Down
2 changes: 2 additions & 0 deletions packages/server/lib/browsers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type BrowserInstance = EventEmitter & {
* TODO: remove need for this
*/
isProcessExit?: boolean
isOrphanedBrowserProcess?: boolean
}

export type BrowserLauncher = {
Expand All @@ -52,4 +53,5 @@ export type BrowserLauncher = {

export type GracefulShutdownOptions = {
gracefulShutdown?: boolean
shouldPreserveCriClient?: boolean
}
2 changes: 2 additions & 0 deletions packages/server/test/unit/browsers/browsers_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ describe('lib/browsers/index', () => {
browsers._setInstance(null)

expect(browserInstance1.kill).to.be.calledOnce
expect(browserInstance1.isOrphanedBrowserProcess).to.be.true
expect(currentInstance).to.equal(browserInstance2)
})

Expand Down Expand Up @@ -262,6 +263,7 @@ describe('lib/browsers/index', () => {
browsers._setInstance(null)

expect(browserInstance1.kill).to.be.calledOnce
expect(browserInstance1.isOrphanedBrowserProcess).to.be.true
expect(currentInstance).to.equal(browserInstance2)
})
})
Expand Down
27 changes: 27 additions & 0 deletions packages/server/test/unit/browsers/electron_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,33 @@ describe('lib/browsers/electron', () => {
})
})

context('.kill', () => {
beforeEach(async function () {
await electron._getAutomation({}, { onError: () => {} }, {})

await this.stubForOpen()

sinon.stub(electron, '_getBrowserCriClient').returns(this.browserCriClient)
})

it('does not terminate the browserCriClient if the instance is an orphaned process', async function () {
const instance = await electron.open('electron', this.url, this.options, this.automation)

instance.isOrphanedBrowserProcess = true
instance.kill()

expect(this.browserCriClient.close).not.to.be.called
})

it('terminates the browserCriClient otherwise', async function () {
const instance = await electron.open('electron', this.url, this.options, this.automation)

instance.kill()

expect(this.browserCriClient.close).to.be.called
})
})

context('._launch', () => {
beforeEach(() => {
sinon.stub(menu, 'set')
Expand Down

5 comments on commit e39e748

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e39e748 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.9.1/linux-x64/develop-e39e74866ffce2e9168017dc97186ce05dc33bd3/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e39e748 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.9.1/linux-arm64/develop-e39e74866ffce2e9168017dc97186ce05dc33bd3/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e39e748 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.9.1/darwin-arm64/develop-e39e74866ffce2e9168017dc97186ce05dc33bd3/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e39e748 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.9.1/win32-x64/develop-e39e74866ffce2e9168017dc97186ce05dc33bd3/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on e39e748 May 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/13.9.1/darwin-x64/develop-e39e74866ffce2e9168017dc97186ce05dc33bd3/cypress.tgz

Please sign in to comment.