Skip to content

Commit

Permalink
breaking: remove log:memory:pressure and firefox:force:gc APIs (#30331)
Browse files Browse the repository at this point in the history
* remove foxdriver and force:gc/log:memory:pressure APIs

* Add changelog entry

* remove patched foxdriver file

* define types/spec FoundSpec without ref to Cypress namespace

* args

* chore: update unit test to still throw firefox cannot connect error if CDP fails to connect instead of the rtrying socket, which is no longer used when connecting to firefox

* Remove firefox-memory system test

---------

Co-authored-by: Cacie Prins <[email protected]>
Co-authored-by: AtofStryker <[email protected]>
  • Loading branch information
3 people authored Oct 3, 2024
1 parent 45ac4e6 commit 93e5383
Show file tree
Hide file tree
Showing 14 changed files with 22 additions and 681 deletions.
1 change: 1 addition & 0 deletions cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ _Released 12/3/2024 (PENDING)_

- Removed support for Node.js 16 and Node.js 21. Addresses [#29930](https://github.com/cypress-io/cypress/issues/29930).
- Prebuilt binaries for Linux are no longer compatible with Linux distributions based on glibc <2.28, for example: Ubuntu 14-18, RHEL 7, CentOS 7, Amazon Linux 2. Addresses [#29601](https://github.com/cypress-io/cypress/issues/29601).
- The undocumented methods `Cypress.backend('firefox:force:gc')` and `Cypress.backend('log:memory:pressure')` were removed. Addresses [#30222](https://github.com/cypress-io/cypress/issues/30222).

## 13.15.1

Expand Down
7 changes: 0 additions & 7 deletions cli/types/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,6 @@ declare namespace Cypress {
}

interface Backend {
/**
* Firefox only: Force Cypress to run garbage collection routines.
* No-op if not running in Firefox.
*
* @see https://on.cypress.io/firefox-gc-issue
*/
(task: 'firefox:force:gc'): Promise<void>
(task: 'net', eventName: string, frame: any): Promise<void>
}

Expand Down
233 changes: 5 additions & 228 deletions packages/server/lib/browsers/firefox-util.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import Bluebird from 'bluebird'
import Debug from 'debug'
import _ from 'lodash'
import util from 'util'
import Foxdriver from '@benmalka/foxdriver'
import * as protocol from './protocol'
import { CdpAutomation } from './cdp_automation'
import { BrowserCriClient } from './browser-cri-client'
import type { Automation } from '../automation'
Expand All @@ -12,92 +7,8 @@ import type { WebDriverClassic } from './webdriver-classic'

const debug = Debug('cypress:server:browsers:firefox-util')

let forceGcCc: () => Promise<void>

let timings = {
gc: [] as any[],
cc: [] as any[],
collections: [] as any[],
}

let webDriverClassic: WebDriverClassic

const getTabId = (tab) => {
return _.get(tab, 'browsingContextID')
}

const getDelayMsForRetry = (i) => {
let maxRetries = Number.parseInt(process.env.CYPRESS_CONNECT_RETRY_THRESHOLD ? process.env.CYPRESS_CONNECT_RETRY_THRESHOLD : '62')

if (i < 10) {
return 100
}

if (i < 18) {
return 500
}

if (i <= maxRetries) {
return 1000
}

return
}

const getPrimaryTab = Bluebird.method((browser) => {
const setPrimaryTab = () => {
return browser.listTabs()
.then((tabs) => {
browser.tabs = tabs

return browser.primaryTab = _.first(tabs)
})
}

// on first connection
if (!browser.primaryTab) {
return setPrimaryTab()
}

// `listTabs` will set some internal state, including marking attached tabs
// as detached. so use the raw `request` here:
return browser.request('listTabs')
.then(({ tabs }) => {
const firstTab = _.first(tabs)

// primaryTab has changed, get all tabs and rediscover first tab
if (getTabId(browser.primaryTab.data) !== getTabId(firstTab)) {
return setPrimaryTab()
}

return browser.primaryTab
})
})

const attachToTabMemory = Bluebird.method((tab) => {
// TODO: figure out why tab.memory is sometimes undefined
if (!tab.memory) return

if (tab.memory.isAttached) {
return
}

return tab.memory.getState()
.then((state) => {
if (state === 'attached') {
return
}

tab.memory.on('garbage-collection', ({ data }) => {
data.num = timings.collections.length + 1
timings.collections.push(data)
debug('received garbage-collection event %o', data)
})

return tab.memory.attach()
})
})

async function connectToNewTabClassic () {
// Firefox keeps a blank tab open in versions of Firefox 123 and lower when the last tab is closed.
// For versions 124 and above, a new tab is not created, so @packages/extension creates one for us.
Expand Down Expand Up @@ -143,91 +54,23 @@ async function navigateToUrlClassic (url: string) {
await webDriverClassic.navigate(url)
}

const logGcDetails = () => {
const reducedTimings = {
...timings,
collections: _.map(timings.collections, (event) => {
return _
.chain(event)
.extend({
duration: _.sumBy(event.collections, (collection: any) => {
return collection.endTimestamp - collection.startTimestamp
}),
spread: _.chain(event.collections).thru((collection) => {
const first = _.first(collection)
const last = _.last(collection)

return last.endTimestamp - first.startTimestamp
}).value(),
})
.pick('num', 'nonincrementalReason', 'reason', 'gcCycleNumber', 'duration', 'spread')
.value()
}),
}

debug('forced GC timings %o', util.inspect(reducedTimings, {
breakLength: Infinity,
maxArrayLength: Infinity,
}))

debug('forced GC times %o', {
gc: reducedTimings.gc.length,
cc: reducedTimings.cc.length,
collections: reducedTimings.collections.length,
})

debug('forced GC averages %o', {
gc: _.chain(reducedTimings.gc).sum().divide(reducedTimings.gc.length).value(),
cc: _.chain(reducedTimings.cc).sum().divide(reducedTimings.cc.length).value(),
collections: _.chain(reducedTimings.collections).sumBy('duration').divide(reducedTimings.collections.length).value(),
spread: _.chain(reducedTimings.collections).sumBy('spread').divide(reducedTimings.collections.length).value(),
})

debug('forced GC totals %o', {
gc: _.sum(reducedTimings.gc),
cc: _.sum(reducedTimings.cc),
collections: _.sumBy(reducedTimings.collections, 'duration'),
spread: _.sumBy(reducedTimings.collections, 'spread'),
})

// reset all the timings
timings = {
gc: [],
cc: [],
collections: [],
}
}

export default {
log () {
logGcDetails()
},

collectGarbage () {
return forceGcCc()
},

async setup ({
automation,
onError,
url,
foxdriverPort,
remotePort,
webDriverClassic: wdcInstance,
remotePort,
onError,
}: {
automation: Automation
onError?: (err: Error) => void
url: string
foxdriverPort: number
remotePort: number
webDriverClassic: WebDriverClassic
remotePort: number
onError?: (err: Error) => void
}): Promise<BrowserCriClient> {
// set the WebDriver classic instance instantiated from geckodriver
webDriverClassic = wdcInstance
const [, browserCriClient] = await Promise.all([
this.setupFoxdriver(foxdriverPort),
setupCDP(remotePort, automation, onError),
])
const browserCriClient = await setupCDP(remotePort, automation, onError)

await navigateToUrlClassic(url)

Expand All @@ -239,70 +82,4 @@ export default {
navigateToUrlClassic,

setupCDP,

// NOTE: this is going to be removed in Cypress 14. @see https://github.com/cypress-io/cypress/issues/30222
async setupFoxdriver (port) {
await protocol._connectAsync({
host: '127.0.0.1',
port,
getDelayMsForRetry,
})

const foxdriver = await Foxdriver.attach('127.0.0.1', port)

const { browser } = foxdriver

browser.on('error', (err) => {
debug('received error from foxdriver connection, ignoring %o', err)
})

forceGcCc = () => {
let gcDuration; let ccDuration

const gc = (tab) => {
return () => {
// TODO: figure out why tab.memory is sometimes undefined
if (!tab.memory) return

const start = Date.now()

return tab.memory.forceGarbageCollection()
.then(() => {
gcDuration = Date.now() - start
timings.gc.push(gcDuration)
})
}
}

const cc = (tab) => {
return () => {
// TODO: figure out why tab.memory is sometimes undefined
if (!tab.memory) return

const start = Date.now()

return tab.memory.forceCycleCollection()
.then(() => {
ccDuration = Date.now() - start
timings.cc.push(ccDuration)
})
}
}

debug('forcing GC and CC...')

return getPrimaryTab(browser)
.then((tab) => {
return attachToTabMemory(tab)
.then(gc(tab))
.then(cc(tab))
})
.then(() => {
debug('forced GC and CC completed %o', { ccDuration, gcDuration })
})
.tapCatch((err) => {
debug('firefox RDP error while forcing GC and CC %o', err)
})
}
},
}
8 changes: 2 additions & 6 deletions packages/server/lib/browsers/firefox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,6 @@ const defaultPreferences = {

// necessary for adding extensions
'devtools.debugger.remote-enabled': true,
// bind foxdriver to 127.0.0.1
'devtools.debugger.remote-host': '127.0.0.1',
// devtools.debugger.remote-port is set per-launch

'devtools.debugger.prompt-connection': false,
Expand Down Expand Up @@ -441,18 +439,16 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
}

const [
foxdriverPort,
marionettePort,
geckoDriverPort,
webDriverBiDiPort,
] = await Promise.all([getPort(), getPort(), getPort(), getPort()])

defaultLaunchOptions.preferences['devtools.debugger.remote-port'] = foxdriverPort
defaultLaunchOptions.preferences['marionette.port'] = marionettePort

// NOTE: we get the BiDi port and set it inside of geckodriver, but BiDi is not currently enabled (see remote.active-protocols above).
// this is so the BiDi websocket port does not get set to 0, which is the default for the geckodriver package.
debug('available ports: %o', { foxdriverPort, marionettePort, geckoDriverPort, webDriverBiDiPort })
debug('available ports: %o', { marionettePort, geckoDriverPort, webDriverBiDiPort })

const [
cacheDir,
Expand Down Expand Up @@ -605,7 +601,7 @@ export async function open (browser: Browser, url: string, options: BrowserLaunc
}))

debug('setting up firefox utils')
browserCriClient = await firefoxUtil.setup({ automation, url, foxdriverPort, webDriverClassic: wdcInstance, remotePort: cdpPort, onError: options.onError })
browserCriClient = await firefoxUtil.setup({ automation, url, webDriverClassic: wdcInstance, remotePort: cdpPort, onError: options.onError })

// monkey-patch the .kill method to that the CDP connection is closed
const originalGeckoDriverKill = geckoDriverInstance.kill
Expand Down
5 changes: 0 additions & 5 deletions packages/server/lib/socket-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { onNetStubbingEvent } from '@packages/net-stubbing'
import * as socketIo from '@packages/socket'
import { CDPSocketServer } from '@packages/socket/lib/cdp-socket'

import firefoxUtil from './browsers/firefox-util'
import * as errors from './errors'
import fixture from './fixture'
import { ensureProp } from './util/class-helpers'
Expand Down Expand Up @@ -417,10 +416,6 @@ export class SocketBase {
return options.onRequest(userAgent, automationRequest, args[0])
case 'reset:server:state':
return options.onResetServerState()
case 'log:memory:pressure':
return firefoxUtil.log()
case 'firefox:force:gc':
return firefoxUtil.collectGarbage()
case 'get:fixture':
return getFixture(args[0], args[1])
case 'net':
Expand Down
2 changes: 0 additions & 2 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
},
"dependencies": {
"@babel/parser": "7.25.3",
"@benmalka/foxdriver": "0.4.1",
"@cypress/commit-info": "2.2.0",
"@cypress/get-windows-proxy": "1.6.2",
"@cypress/request": "^3.0.4",
Expand Down Expand Up @@ -215,7 +214,6 @@
"productName": "Cypress",
"workspaces": {
"nohoist": [
"@benmalka/foxdriver",
"devtools-protocol",
"geckodriver",
"http-proxy",
Expand Down
Loading

1 comment on commit 93e5383

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 93e5383 Oct 3, 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/14.0.0/darwin-arm64/release/14.0.0-93e53837a424d9bbb17443470ba9c656ad70271b/cypress.tgz

Please sign in to comment.