Merge branch 'develop' into v4.0-release

This commit is contained in:
Zach Bloomquist
2020-01-31 13:31:36 -05:00
committed by GitHub
16 changed files with 659 additions and 29 deletions
@@ -0,0 +1,82 @@
exports['lib/util/process_profiler ._aggregateGroups aggregates groups as expected 1'] = [
{
"group": "electron-shared",
"processCount": 4,
"pids": "99991, 99990, 99992, 99993",
"cpuPercent": 80,
"memRssMb": 40,
"meanCpuPercent": 80,
"meanMemRssMb": 40,
"maxMemRssMb": 40
},
{
"group": "other",
"processCount": 2,
"pids": "66666, 88888",
"cpuPercent": 40,
"memRssMb": 20,
"meanCpuPercent": 40,
"meanMemRssMb": 20,
"maxMemRssMb": 20
},
{
"group": "plugin",
"processCount": 2,
"pids": "22222, 22223",
"cpuPercent": 40,
"memRssMb": 20,
"meanCpuPercent": 40,
"meanMemRssMb": 20,
"maxMemRssMb": 20
},
{
"group": "browser",
"processCount": 2,
"pids": "11111, 11112",
"cpuPercent": 40,
"memRssMb": 20,
"meanCpuPercent": 40,
"meanMemRssMb": 20,
"maxMemRssMb": 20
},
{
"group": "ffmpeg",
"processCount": 1,
"pids": "33333",
"cpuPercent": 20,
"memRssMb": 10,
"meanCpuPercent": 20,
"meanMemRssMb": 10,
"maxMemRssMb": 10
},
{
"group": "desktop-gui",
"processCount": 1,
"pids": "77777",
"cpuPercent": 20,
"memRssMb": 10,
"meanCpuPercent": 20,
"meanMemRssMb": 10,
"maxMemRssMb": 10
},
{
"group": "cypress",
"processCount": 1,
"pids": "111111111",
"cpuPercent": 20,
"memRssMb": 10,
"meanCpuPercent": 20,
"meanMemRssMb": 10,
"maxMemRssMb": 10
},
{
"group": "TOTAL",
"processCount": 13,
"pids": "-",
"cpuPercent": 260,
"memRssMb": 130,
"meanCpuPercent": 260,
"meanMemRssMb": 130,
"maxMemRssMb": 130
}
]
+11 -1
View File
@@ -1,13 +1,19 @@
// override tty if we're being forced to
require('./lib/util/tty').override()
if (process.env.CY_NET_PROFILE && process.env.CYPRESS_ENV) {
const electronApp = require('./lib/util/electron-app')
// are we in the main node process or the electron process?
const isRunningElectron = electronApp.isRunning()
if (process.env.CY_NET_PROFILE && isRunningElectron) {
const netProfiler = require('./lib/util/net_profiler')()
process.stdout.write(`Network profiler writing to ${netProfiler.logPath}\n`)
}
process.env.UV_THREADPOOL_SIZE = 128
require('graceful-fs').gracefulify(require('fs'))
// if running in production mode (CYPRESS_ENV)
// all transpile should have been done already
@@ -15,6 +21,10 @@ require('graceful-fs').gracefulify(require('fs'))
require('@packages/ts/register')
require('@packages/coffee/register')
if (isRunningElectron) {
require('./lib/util/process_profiler').start()
}
require && require.extensions && delete require.extensions['.litcoffee']
require && require.extensions && delete require.extensions['.coffee.md']
+10 -1
View File
@@ -21,6 +21,8 @@ ELECTRON_DEBUG_EVENTS = [
'unresponsive'
]
instance = null
tryToCall = (win, method) ->
try
if not win.isDestroyed()
@@ -72,6 +74,10 @@ module.exports = {
_win.on "close", ->
if not child.isDestroyed()
child.destroy()
## add this pid to list of pids
tryToCall child, ->
instance?.pid?.push(child.webContents.getOSProcessId())
}
_.defaultsDeep({}, options, defaults)
@@ -253,9 +259,12 @@ module.exports = {
events.emit("exit")
return _.extend events, {
instance = _.extend events, {
pid: [tryToCall(win, -> win.webContents.getOSProcessId())]
browserWindow: win
kill: -> tryToCall(win, "destroy")
removeAllListeners: -> tryToCall(win, "removeAllListeners")
}
return instance
}
+13
View File
@@ -157,6 +157,17 @@ module.exports = {
close: kill,
_setInstance (_instance) {
// for testing
instance = _instance
},
// note: does not guarantee that `browser` is still running
// note: electron will return a list of pids for each webContent
getBrowserInstance () {
return instance
},
getAllBrowsersWith (nameOrPath) {
debug('getAllBrowsersWith %o', { nameOrPath })
if (nameOrPath) {
@@ -192,6 +203,8 @@ module.exports = {
// TODO: bind to process.exit here
// or move this functionality into cypress-core-launder
i.browser = browser
instance = i
// TODO: normalizing opening and closing / exiting
+2 -2
View File
@@ -45,7 +45,7 @@ const exitErr = (err) => {
module.exports = {
isCurrentlyRunningElectron () {
return !!(process.versions && process.versions.electron)
return require('./util/electron-app').isRunning()
},
runElectron (mode, options) {
@@ -128,7 +128,7 @@ module.exports = {
// to force retina screens to not
// upsample their images when offscreen
// rendering
require('./util/electron_app').scale()
require('./util/electron-app').scale()
}
// make sure we have the appData folder
+3 -2
View File
@@ -26,7 +26,7 @@ const newlines = require('../util/newlines')
const terminal = require('../util/terminal')
const specsUtil = require('../util/specs')
const humanTime = require('../util/human_time')
const electronApp = require('../util/electron_app')
const electronApp = require('../util/electron-app')
const settings = require('../util/settings')
const chromePolicyCheck = require('../util/chrome_policy_check')
@@ -660,6 +660,7 @@ const getVideoRecordingDelay = function (startedVideoCapture) {
const maybeStartVideoRecording = Promise.method(function (options = {}) {
const { spec, browser, video, videosFolder } = options
debug(`video recording has been ${video ? 'enabled' : 'disabled'}. video: %s`, video)
// bail if we've been told not to capture
// a video recording
if (!video) {
@@ -1403,7 +1404,7 @@ module.exports = {
run (options) {
return electronApp
.ready()
.waitForReady()
.then(() => {
return this.ready(options)
})
+8
View File
@@ -22,6 +22,14 @@ register = (event, callback) ->
registeredEvents[event] = callback
module.exports = {
## for testing
_setPluginsProcess: (_pluginsProcess) ->
pluginsProcess = _pluginsProcess
getPluginPid: () ->
if pluginsProcess
return pluginsProcess.pid
registerHandler: (handler) ->
handlers.push(handler)
-14
View File
@@ -55,7 +55,6 @@ class Project extends EE {
this.spec = null
this.browser = null
this.server = null
this.memoryCheck = null
this.automation = null
this.getConfig = this.getConfig.bind(this)
@@ -78,15 +77,6 @@ class Project extends EE {
debug('project options %o', options)
this.options = options
if (process.env.CYPRESS_MEMORY) {
const logMemory = () => {
// eslint-disable-next-line no-console
return console.log('memory info', process.memoryUsage())
}
this.memoryCheck = setInterval(logMemory, 1000)
}
this.onWarning = options.onWarning
return this.getConfig(options)
@@ -205,10 +195,6 @@ class Project extends EE {
close () {
debug('closing project instance %s', this.projectRoot)
if (this.memoryCheck) {
clearInterval(this.memoryCheck)
}
this.cfg = null
this.spec = null
this.browser = null
@@ -1,5 +1,3 @@
const debug = require('debug')('cypress:server:electron_app')
const scale = () => {
try {
const { app } = require('electron')
@@ -10,7 +8,9 @@ const scale = () => {
}
}
const ready = () => {
const waitForReady = () => {
const debug = require('debug')('cypress:server:electron-app')
const Promise = require('bluebird')
const { app } = require('electron')
@@ -21,20 +21,27 @@ const ready = () => {
debug('all BrowserWindows closed, not exiting')
})
const waitForReady = () => {
const onReadyEvent = () => {
return new Promise((resolve) => {
app.on('ready', resolve)
})
}
return Promise.any([
waitForReady(),
onReadyEvent(),
Promise.delay(500),
])
}
const isRunning = () => {
// are we in the electron or the node process?
return Boolean(process.versions && process.versions.electron)
}
module.exports = {
scale,
ready,
waitForReady,
isRunning,
}
@@ -0,0 +1,263 @@
import Debug from 'debug'
import la from 'lazy-ass'
import _ from 'lodash'
import si from 'systeminformation'
import { concatStream } from '@packages/network'
const browsers = require('../browsers')
const plugins = require('../plugins')
type Group = 'browser' | 'cypress' | 'plugin' | 'desktop-gui' | 'ffmpeg' | 'electron-shared' | 'other'
type Process = si.Systeminformation.ProcessesProcessData & {
group?: Group
}
const debug = Debug('cypress:server:util:process_profiler')
const debugVerbose = Debug('cypress-verbose:server:util:process_profiler')
const interval = Number(process.env.CYPRESS_PROCESS_PROFILER_INTERVAL) || 10000
let started = false
let groupsOverTime = {}
export const _reset = () => {
groupsOverTime = {}
}
const formatPidDisplay = (groupedProcesses) => {
const pids = _.map(groupedProcesses, 'pid')
const maxArrayLength = 6
let display = pids.slice(0, maxArrayLength).join(', ')
if (pids.length > maxArrayLength) {
display += ` ... ${pids.length - maxArrayLength} more items`
}
return display
}
export const _groupCyProcesses = ({ list }: si.Systeminformation.ProcessesData) => {
const cyProcesses: Process[] = []
const thisProcess: Process = _.find(list, { pid: process.pid })!
la(thisProcess, 'expected to find current pid in process list', process.pid)
const isParentProcessInGroup = (proc: Process, group: Group) => {
return _.chain(cyProcesses).filter({ group }).map('pid').includes(proc.parentPid).value()
}
// is this a browser process launched to run Cypress tests?
const isBrowserProcess = (proc: Process): boolean => {
const instance = browsers.getBrowserInstance()
// electron will return a list of pids, since it's not a hierarchy
const pid: number | number[] = instance && instance.pid
return (Array.isArray(pid) ? (pid as number[]).includes(proc.pid) : proc.pid === pid)
|| isParentProcessInGroup(proc, 'browser')
}
const isPluginProcess = (proc: Process): boolean => {
return proc.pid === plugins.getPluginPid()
|| isParentProcessInGroup(proc, 'plugin')
}
// is this the renderer for the desktop-gui?
const isDesktopGuiProcess = (proc: Process): boolean => {
return proc.params.includes('--type=renderer')
&& !isBrowserProcess(proc)
}
// these processes may be shared between the AUT and desktop-gui
// rather than treat them as part of the `browser` in `run` mode and have
// their usage in `open` mode be ambiguous, just put them in their own group
const isElectronSharedProcess = (proc: Process): boolean => {
const isType = (type) => {
return proc.params.includes(`--type=${type}`)
}
return isType('broker')
|| isType('gpu-process')
|| isType('utility')
|| isType('zygote')
}
const isFfmpegProcess = (proc: Process): boolean => {
return proc.parentPid === thisProcess.pid
&& /ffmpeg/i.test(proc.name)
}
const getProcessGroup = (proc: Process): Group => {
if (proc === thisProcess) {
return 'cypress'
}
if (isBrowserProcess(proc)) {
return 'browser'
}
if (isPluginProcess(proc)) {
return 'plugin'
}
if (isDesktopGuiProcess(proc)) {
return 'desktop-gui'
}
if (isFfmpegProcess(proc)) {
return 'ffmpeg'
}
if (isElectronSharedProcess(proc)) {
return 'electron-shared'
}
return 'other'
}
const classifyProcess = (proc: Process) => {
const classify = (group: Group) => {
proc.group = group
cyProcesses.push(proc)
// queue all children
_.chain(list)
.filter({ parentPid: proc.pid })
.map(classifyProcess)
.value()
}
classify(getProcessGroup(proc))
}
classifyProcess(thisProcess)
return cyProcesses
}
export const _renameBrowserGroup = (processes: Process[]) => {
const instance = browsers.getBrowserInstance()
const displayName = _.get(instance, 'browser.displayName')
processes.forEach((proc) => {
if (!displayName) {
return
}
if (proc.group === 'browser') {
proc.group = displayName
}
})
return processes
}
export const _aggregateGroups = (processes: Process[]) => {
debugVerbose('all Cypress-launched processes: %s', require('util').inspect(processes))
const groupTotals = _.chain(processes)
.groupBy('group')
.mapValues((groupedProcesses, group) => {
return {
group,
processCount: groupedProcesses.length,
pids: formatPidDisplay(groupedProcesses),
cpuPercent: _.sumBy(groupedProcesses, 'pcpu'),
memRssMb: _.sumBy(groupedProcesses, 'mem_rss') / 1024,
}
})
.values()
.sortBy('memRssMb')
.reverse()
.value()
groupTotals.push(_.reduce(groupTotals, (acc, val) => {
acc.processCount += val.processCount
acc.cpuPercent += val.cpuPercent
acc.memRssMb += val.memRssMb
return acc
}, { group: 'TOTAL', processCount: 0, pids: '-', cpuPercent: 0, memRssMb: 0 }))
groupTotals.forEach((total) => {
if (!groupsOverTime[total.group]) {
groupsOverTime[total.group] = []
}
const measurements = groupsOverTime[total.group]
measurements.push(total)
_.merge(total, {
meanCpuPercent: _.meanBy(measurements, 'cpuPercent'),
meanMemRssMb: _.meanBy(measurements, 'memRssMb'),
maxMemRssMb: _.max(_.map(measurements, _.property('memRssMb'))),
})
_.forEach(total, (v, k) => {
// round all numbers to 100ths precision
if (_.isNumber(v)) {
total[k] = _.round(v, 2)
}
})
})
return groupTotals
}
export const _printGroupedProcesses = (groupTotals) => {
const consoleBuffer = concatStream((buf) => {
// get rid of trailing newline
debug(String(buf).trim())
})
// eslint-disable-next-line no-console
const buffedConsole = new console.Console(consoleBuffer)
buffedConsole.log('current & mean memory and CPU usage by process group:')
buffedConsole.table(groupTotals, [
'group',
'processCount',
'pids',
'cpuPercent',
'meanCpuPercent',
'memRssMb',
'meanMemRssMb',
'maxMemRssMb',
])
consoleBuffer.end()
}
function _checkProcesses () {
return si.processes()
.then(_groupCyProcesses)
.then(_renameBrowserGroup)
.then(_aggregateGroups)
.then(_printGroupedProcesses)
.then(_scheduleProcessCheck)
.catch((err) => {
debug('error running process profiler: %o', err)
})
}
function _scheduleProcessCheck () {
// not setinterval, since checkProcesses is asynchronous
setTimeout(_checkProcesses, interval)
}
export function start () {
if (!debug.enabled && !debugVerbose.enabled) {
debug('process profiler not enabled')
return
}
if (started) {
return
}
_checkProcesses()
started = true
}
+1
View File
@@ -120,6 +120,7 @@
"squirrelly": "7.7.0",
"strip-ansi": "3.0.1",
"syntax-error": "1.4.0",
"systeminformation": "4.19.1",
"term-size": "2.1.0",
"tough-cookie": "3.0.1",
"trash": "5.2.0",
@@ -44,6 +44,7 @@ const env = require(`${root}lib/util/env`)
const v = require(`${root}lib/util/validation`)
const system = require(`${root}lib/util/system`)
const appData = require(`${root}lib/util/app_data`)
const electronApp = require('../../lib/util/electron-app')
const { formStatePath } = require(`${root}lib/util/saved_state`)
const TYPICAL_BROWSERS = [
@@ -121,7 +122,7 @@ describe('lib/cypress', () => {
// spawning a separate process
sinon.stub(videoCapture, 'start').resolves({})
sinon.stub(plugins, 'init').resolves(undefined)
sinon.stub(cypress, 'isCurrentlyRunningElectron').returns(true)
sinon.stub(electronApp, 'isRunning').returns(true)
sinon.stub(extension, 'setHostAndPath').resolves()
sinon.stub(launcher, 'detect').resolves(TYPICAL_BROWSERS)
sinon.stub(process, 'exit')
@@ -6,6 +6,16 @@ browsers = require("#{root}../lib/browsers")
utils = require("#{root}../lib/browsers/utils")
describe "lib/browsers/index", ->
context ".getBrowserInstance", ->
it "returns instance", ->
instance = { pid: 1234 }
browsers._setInstance(instance)
expect(browsers.getBrowserInstance()).to.eq(instance)
it "returns undefined if no instance", ->
browsers._setInstance()
expect(browsers.getBrowserInstance()).to.be.undefined
context ".isBrowserFamily", ->
it "allows only known browsers", ->
expect(browsers.isBrowserFamily("chromium")).to.be.true
@@ -12,6 +12,8 @@ electron = require("#{root}../lib/browsers/electron")
savedState = require("#{root}../lib/saved_state")
Automation = require("#{root}../lib/automation")
ELECTRON_PID = 10001
describe "lib/browsers/electron", ->
beforeEach ->
@url = "https://foo.com"
@@ -34,6 +36,7 @@ describe "lib/browsers/electron", ->
remove: sinon.stub()
}
}
getOSProcessId: sinon.stub().returns(ELECTRON_PID)
"debugger": {
attach: sinon.stub().returns()
sendCommand: sinon.stub().resolves()
@@ -42,8 +45,7 @@ describe "lib/browsers/electron", ->
}
})
context ".open", ->
beforeEach ->
@stubForOpen = ->
sinon.stub(electron, "_render").resolves(@win)
sinon.stub(plugins, "has")
sinon.stub(plugins, "execute")
@@ -53,6 +55,10 @@ describe "lib/browsers/electron", ->
la(check.fn(state.get), "state is missing .get to stub", state)
sinon.stub(state, "get").resolves(@state)
context ".open", ->
beforeEach ->
@stubForOpen()
it "calls render with url, state, and options", ->
electron.open("electron", @url, @options, @automation)
.then =>
@@ -76,6 +82,9 @@ describe "lib/browsers/electron", ->
expect(obj.kill).to.be.a("function")
expect(obj.removeAllListeners).to.be.a("function")
expect(@win.webContents.getOSProcessId).to.be.calledOnce
expect(obj.pid).to.deep.eq([ELECTRON_PID])
it "is noop when before:browser:launch yields null", ->
plugins.has.returns(true)
plugins.execute.resolves(null)
@@ -239,6 +248,24 @@ describe "lib/browsers/electron", ->
event, @url, parentWindow, @options.projectRoot, @state, @options
)
it "adds pid of new BrowserWindow to pid list", ->
opts = electron._defaultOptions(@options.projectRoot, @state, @options)
NEW_WINDOW_PID = ELECTRON_PID * 2
child = _.cloneDeep(@win)
child.webContents.getOSProcessId = sinon.stub().returns(NEW_WINDOW_PID)
electron._launchChild.resolves(child)
@stubForOpen()
.then =>
electron.open("electron", @url, opts, @automation)
.then (instance) =>
opts.onNewWindow.call(@win, {}, @url)
.then ->
expect(instance.pid).to.deep.eq([ELECTRON_PID, NEW_WINDOW_PID])
## TODO: these all need to be updated
context.skip "._launchChild", ->
beforeEach ->
@@ -5,6 +5,8 @@ cp = require("child_process")
util = require("#{root}../lib/plugins/util")
plugins = require("#{root}../lib/plugins")
PLUGIN_PID = 77777
describe "lib/plugins/index", ->
beforeEach ->
plugins._reset()
@@ -13,6 +15,7 @@ describe "lib/plugins/index", ->
send: sinon.spy()
on: sinon.stub()
kill: sinon.spy()
pid: PLUGIN_PID
}
sinon.stub(cp, "fork").returns(@pluginsProcess)
@@ -200,3 +203,16 @@ describe "lib/plugins/index", ->
plugins.register("foo", foo)
plugins.execute("foo", "arg1", "arg2")
expect(foo).to.be.calledWith("arg1", "arg2")
context "#getPluginPid", ->
beforeEach ->
plugins._setPluginsProcess(null)
it "returns the pid if there is a plugins process", ->
@ipc.on.withArgs("loaded").yields([])
plugins.init({ pluginsFile: "cypress-plugin" })
.then ->
expect(plugins.getPluginPid()).to.eq(PLUGIN_PID)
it "returns undefined if there is no plugins process", ->
expect(plugins.getPluginPid()).to.be.undefined
@@ -0,0 +1,196 @@
import '../../spec_helper'
import _ from 'lodash'
import si from 'systeminformation'
import { expect } from 'chai'
import {
_groupCyProcesses,
_renameBrowserGroup,
_aggregateGroups,
_reset,
} from '../../../lib/util/process_profiler'
import sinon from 'sinon'
import snapshot from 'snap-shot-it'
const browsers = require('../../../lib/browsers')
const plugins = require('../../../lib/plugins')
const BROWSER_PID = 11111
const SUB_BROWSER_PID = 11112
const GUI_PID = 77777
const PLUGIN_PID = 22222
const SUB_PLUGIN_PID = 22223
const FFMPEG_PID = 33333
const MAIN_PID = process.pid
const OTHER_PID = 66666
const ANOTHER_PID = 88888
const LAUNCHER_PID = 55555
const SHARED_BROKER_PID = 99990
const SHARED_GPU_PID = 99991
const SHARED_UTILITY_PID = 99992
const SHARED_ZYGOTE_PID = 99993
const PROCESSES: Partial<si.Systeminformation.ProcessesProcessData>[] = [
{
pid: MAIN_PID,
parentPid: LAUNCHER_PID,
params: '',
name: 'Cypress',
},
{
pid: BROWSER_PID,
parentPid: MAIN_PID,
params: '',
name: 'firefox',
},
{
pid: SUB_BROWSER_PID,
parentPid: BROWSER_PID,
params: '',
name: 'firefox-bin',
},
{
pid: GUI_PID,
parentPid: MAIN_PID,
params: '--type=renderer',
name: 'Cypress',
},
{
pid: PLUGIN_PID,
parentPid: MAIN_PID,
params: 'plugin.js',
name: 'node',
},
{
pid: SUB_PLUGIN_PID,
parentPid: PLUGIN_PID,
params: '',
name: 'msword.exe',
},
{
pid: FFMPEG_PID,
parentPid: MAIN_PID,
params: '',
name: 'ffmpeg',
},
{
pid: OTHER_PID,
parentPid: MAIN_PID,
params: '',
name: 'foo',
},
{
pid: ANOTHER_PID,
parentPid: MAIN_PID,
params: '',
name: 'bar',
},
{
pid: SHARED_GPU_PID,
parentPid: MAIN_PID,
params: '--type=gpu-process',
name: 'Cypress',
},
{
pid: SHARED_BROKER_PID,
parentPid: MAIN_PID,
params: '--type=broker',
name: 'Cypress',
},
{
pid: SHARED_UTILITY_PID,
parentPid: MAIN_PID,
params: '--type=utility',
name: 'Cypress',
},
{
pid: SHARED_ZYGOTE_PID,
parentPid: MAIN_PID,
params: '--type=zygote',
name: 'Cypress',
},
]
describe('lib/util/process_profiler', function () {
beforeEach(() => {
_reset()
})
context('._groupCyProcesses', () => {
it('groups correctly', () => {
sinon.stub(browsers, 'getBrowserInstance').returns({ pid: BROWSER_PID })
sinon.stub(plugins, 'getPluginPid').returns(PLUGIN_PID)
// @ts-ignore
const groupedProcesses = _groupCyProcesses({ list: PROCESSES })
const checkGroup = (pid, group) => {
expect(_.find(groupedProcesses, { pid }))
.to.have.property('group')
.eq(group)
}
checkGroup(BROWSER_PID, 'browser')
checkGroup(SUB_BROWSER_PID, 'browser')
checkGroup(GUI_PID, 'desktop-gui')
checkGroup(PLUGIN_PID, 'plugin')
checkGroup(SUB_PLUGIN_PID, 'plugin')
checkGroup(FFMPEG_PID, 'ffmpeg')
checkGroup(MAIN_PID, 'cypress')
checkGroup(OTHER_PID, 'other')
checkGroup(ANOTHER_PID, 'other')
checkGroup(SHARED_GPU_PID, 'electron-shared')
checkGroup(SHARED_BROKER_PID, 'electron-shared')
checkGroup(SHARED_UTILITY_PID, 'electron-shared')
checkGroup(SHARED_ZYGOTE_PID, 'electron-shared')
})
})
context('._renameBrowserGroup', () => {
it('renames browser-grouped processes to correct name', () => {
sinon.stub(browsers, 'getBrowserInstance').returns({ browser: { displayName: 'FooBrowser' } })
const processes = [
{ group: 'foo' },
{ group: 'bar' },
{ group: 'browser', pid: 1 },
{ group: 'browser', pid: 2 },
]
const expected = [
{ group: 'foo' },
{ group: 'bar' },
{ group: 'FooBrowser', pid: 1 },
{ group: 'FooBrowser', pid: 2 },
]
// @ts-ignore
expect(_renameBrowserGroup(processes)).to.deep.eq(expected)
})
})
context('._aggregateGroups', () => {
it('aggregates groups as expected', () => {
sinon.stub(browsers, 'getBrowserInstance').returns({ pid: BROWSER_PID })
sinon.stub(plugins, 'getPluginPid').returns(PLUGIN_PID)
const processes = _.cloneDeep(PROCESSES)
.map((proc) => {
// add some dummy measurements so there is data to aggregate
proc.mem_rss = 10 * 1024 // 10mb
proc.pcpu = 20
return proc
})
// @ts-ignore
const result = _aggregateGroups(_groupCyProcesses({ list: processes }))
// main process will have variable pid, replace it w constant for snapshotting
_.find(result, { pids: String(MAIN_PID) }).pids = '111111111'
// @ts-ignore
snapshot(result)
})
})
})