Last active
January 1, 2024 07:05
-
-
Save azl397985856/5d5617ef617df23c8aea1fa1481e4d87 to your computer and use it in GitHub Desktop.
Automatically detect memory leaks with Puppeteer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// from : https://github.com/chrisguttandin/standardized-audio-context/blob/master/test/integration/memory.js | |
const MemoryFileSystem = require('memory-fs'); // eslint-disable-line no-undef | |
const puppeteer = require('puppeteer'); // eslint-disable-line no-undef | |
const webpack = require('webpack'); // eslint-disable-line no-undef | |
// eslint-disable-next-line padding-line-between-statements | |
const compileBundle = () => { | |
return new Promise((resolve, reject) => { | |
const memoryFileSystem = new MemoryFileSystem(); | |
const compiler = webpack({ | |
entry: { | |
bundle: './build/es2018/module.js' | |
}, | |
mode: 'development', | |
output: { | |
filename: '[name].js', | |
libraryTarget: 'umd', | |
path: '/' | |
} | |
}); | |
compiler.outputFileSystem = memoryFileSystem; | |
compiler.run((err, stats) => { | |
if (stats.hasErrors() || stats.hasWarnings()) { | |
reject(new Error(stats.toString({ errorDetails: true, warnings: true }))); | |
} else { | |
resolve(memoryFileSystem.readFileSync('/bundle.js', 'utf-8')); // eslint-disable-line no-sync | |
} | |
}); | |
}); | |
}; | |
const countObjects = async (page) => { | |
const prototypeHandle = await page.evaluateHandle(() => Object.prototype); | |
const objectsHandle = await page.queryObjects(prototypeHandle); | |
const numberOfObjects = await page.evaluate((instances) => instances.length, objectsHandle); | |
await Promise.all([ | |
prototypeHandle.dispose(), | |
objectsHandle.dispose() | |
]); | |
return numberOfObjects; | |
}; | |
describe('module', () => { | |
let browser; | |
let context; | |
let page; | |
after(() => browser.close()); | |
afterEach(() => context.close()); | |
before(async function () { | |
this.timeout(10000); | |
browser = await puppeteer.launch(); | |
}); | |
beforeEach(async function () { | |
this.timeout(10000); | |
context = await browser.createIncognitoBrowserContext(); | |
page = await context.newPage(); | |
await page.evaluate(await compileBundle()); | |
await page.evaluate(async () => { | |
audioContext = new AudioContext(); // eslint-disable-line no-undef | |
await new Promise((resolve) => setTimeout(resolve, 1000)); | |
}); | |
}); | |
describe('with a GainNode', () => { | |
it('should collect unconnected GainNodes', async function () { | |
this.timeout(10000); | |
const run = (numberOfIterations) => { | |
for (let i = 0; i < numberOfIterations; i += 1) { | |
new GainNode(audioContext); // eslint-disable-line no-undef | |
} | |
}; | |
const numberOfObjects = await countObjects(page); | |
await page.evaluate(run, 1000); | |
expect(await countObjects(page)).to.equal(numberOfObjects); | |
}); | |
it('should collect connected GainNodes', async function () { | |
this.timeout(10000); | |
const run = (numberOfIterations) => { | |
for (let i = 0; i < numberOfIterations; i += 1) { | |
const gainNode = new GainNode(audioContext); // eslint-disable-line no-undef | |
gainNode.connect(audioContext.destination); // eslint-disable-line no-undef | |
} | |
}; | |
const numberOfObjects = await countObjects(page); | |
await page.evaluate(run, 1000); | |
expect(await countObjects(page)).to.equal(numberOfObjects); | |
}); | |
it('should collect disconnected GainNodes', async function () { | |
this.timeout(10000); | |
const run = (numberOfIterations) => { | |
for (let i = 0; i < numberOfIterations; i += 1) { | |
const gainNode = new GainNode(audioContext); // eslint-disable-line no-undef | |
gainNode.connect(audioContext.destination); // eslint-disable-line no-undef | |
gainNode.disconnect(audioContext.destination); // eslint-disable-line no-undef | |
} | |
}; | |
const numberOfObjects = await countObjects(page); | |
await page.evaluate(run, 1000); | |
expect(await countObjects(page)).to.equal(numberOfObjects); | |
}); | |
}); | |
describe('with an AudioBufferSourceNode', () => { | |
it('should collect unconnected AudioBufferSourceNodes', async function () { | |
this.timeout(10000); | |
const run = (numberOfIterations) => { | |
for (let i = 0; i < numberOfIterations; i += 1) { | |
new AudioBufferSourceNode( | |
audioContext, // eslint-disable-line no-undef | |
{ buffer: new AudioBuffer({ length: 1, sampleRate: audioContext.sampleRate }) } // eslint-disable-line no-undef | |
); | |
} | |
}; | |
// Run the test once because the first run will trigger some memoizations. | |
await page.evaluate(run, 1); | |
const numberOfObjects = await countObjects(page); | |
await page.evaluate(run, 1000); | |
expect(await countObjects(page)).to.equal(numberOfObjects); | |
}); | |
it('should collect connected AudioBufferSourceNodes', async function () { | |
this.timeout(10000); | |
const run = (numberOfIterations) => { | |
for (let i = 0; i < numberOfIterations; i += 1) { | |
const audioBufferSourceNode = new AudioBufferSourceNode( | |
audioContext, // eslint-disable-line no-undef | |
{ buffer: new AudioBuffer({ length: 1, sampleRate: audioContext.sampleRate }) } // eslint-disable-line no-undef | |
); | |
audioBufferSourceNode.connect(audioContext.destination); // eslint-disable-line no-undef | |
} | |
}; | |
// Run the test once because the first run will trigger some memoizations. | |
await page.evaluate(run, 1); | |
const numberOfObjects = await countObjects(page); | |
await page.evaluate(run, 1000); | |
expect(await countObjects(page)).to.equal(numberOfObjects); | |
}); | |
// @todo Run a test with started AudioBufferSourceNodes. | |
it('should collect disconnected AudioBufferSourceNodes', async function () { | |
this.timeout(10000); | |
const run = (numberOfIterations) => { | |
for (let i = 0; i < numberOfIterations; i += 1) { | |
const audioBufferSourceNode = new AudioBufferSourceNode( | |
audioContext, // eslint-disable-line no-undef | |
{ buffer: new AudioBuffer({ length: 1, sampleRate: audioContext.sampleRate }) } // eslint-disable-line no-undef | |
); | |
audioBufferSourceNode.connect(audioContext.destination); // eslint-disable-line no-undef | |
audioBufferSourceNode.disconnect(audioContext.destination); // eslint-disable-line no-undef | |
} | |
}; | |
// Run the test once because the first run will trigger some memoizations. | |
await page.evaluate(run, 1); | |
const numberOfObjects = await countObjects(page); | |
await page.evaluate(run, 1000); | |
expect(await countObjects(page)).to.equal(numberOfObjects); | |
}); | |
}); | |
}); |
@mfdeveloper the code comes from here : https://github.com/chrisguttandin/standardized-audio-context/blob/v20.1.3/test/integration/memory.js
So I think you can test the code above by running it
@mfdeveloper the code comes from here : https://github.com/chrisguttandin/standardized-audio-context/blob/v20.1.3/test/integration/memory.js
So I think you can test the code above by running it
Ok! Thanks a lot for your reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello! Thank you so much for share this!
Unfortunately, I'm always got the error
Can't resolve './bundle/es2018/module.js
.Please, could you explain how do you generate this file? I'm trying an approach to test
AudioContext
with jest + puppeteer without success 😢I tried generate a
bundle.js
from my app and use it asentry
, but instead of the error above I got:Evaluation failed: ReferenceError: tslib_1 is not defined
error