UNPKG

@nearform/heap-profiler

Version:

Heap dump, sample profiler and allocation timeline generator for Node.

185 lines (134 loc) 5.07 kB
'use strict' const t = require('tap') const { readFileSync } = require('fs') const { file: tmpFile } = require('tmp-promise') const AbortController = require('abort-controller') const EventEmitter = require('events') const generateHeapSamplingProfile = require('../src/profile') const allocateMemoryFor = require('./allocator') const validate = require('./fixtures/profileSchema') t.test('it correctly generates a profile using promises', async t => { const { path: destination, cleanup } = await tmpFile() const samplingPromise = generateHeapSamplingProfile({ destination, duration: 100 }) await allocateMemoryFor(100) await samplingPromise const generated = JSON.parse(readFileSync(destination, 'utf-8')) t.true(validate(generated)) t.strictSame(validate.errors, null) cleanup() }) t.test('it correctly generates a profile using callbacks', async t => { const { path: destination, cleanup } = await tmpFile() const test = new Promise((resolve, reject) => { generateHeapSamplingProfile({ destination, duration: 100 }, () => { try { const generated = JSON.parse(readFileSync(destination, 'utf-8')) cleanup() t.true(validate(generated)) t.strictSame(validate.errors, null) resolve() } catch (e) { reject(e) } }) }) await allocateMemoryFor(100) return test }) t.test('it handles file saving errors using promises', async t => { await t.rejects(() => generateHeapSamplingProfile({ duration: 10, destination: '/this/doesnt/exists' }), { message: "ENOENT: no such file or directory, open '/this/doesnt/exists'" }) }) t.test('it handles file saving errors using callbacks', t => { generateHeapSamplingProfile({ duration: 10, destination: '/this/doesnt/exists' }, err => { t.equal(err.message, "ENOENT: no such file or directory, open '/this/doesnt/exists'") t.end() }) }) t.test('it can use an AbortController', async t => { const { path: destination, cleanup } = await tmpFile() const controller = new AbortController() const start = Date.now() setTimeout(function() { controller.abort() }, 100) const samplingPromise = generateHeapSamplingProfile({ destination, signal: controller.signal }) await allocateMemoryFor(100) await samplingPromise const end = Date.now() const generated = JSON.parse(readFileSync(destination, 'utf-8')) t.is(end - start < 1000, true) t.is(end - start >= 100, true) t.true(validate(generated)) cleanup() }) t.test('it can use an EventEmitter as AbortController', async t => { const { path: destination, cleanup } = await tmpFile() const controller = new EventEmitter() const start = Date.now() setTimeout(function() { controller.emit('abort') }, 100) const samplingPromise = generateHeapSamplingProfile({ destination, signal: controller }) await allocateMemoryFor(100) await samplingPromise const end = Date.now() const generated = JSON.parse(readFileSync(destination, 'utf-8')) t.is(end - start < 1000, true) t.is(end - start >= 100, true) t.true(validate(generated)) cleanup() }) t.test('it ignores duration when using an AbortController', async t => { const { path: destination, cleanup } = await tmpFile() const controller = new AbortController() const start = Date.now() setTimeout(function() { controller.abort() }, 200) const samplingPromise = generateHeapSamplingProfile({ destination, signal: controller.signal, duration: 100 }) await allocateMemoryFor(100) await samplingPromise const end = Date.now() const generated = JSON.parse(readFileSync(destination, 'utf-8')) t.is(end - start < 1000, true) t.is(end - start >= 200, true) t.true(validate(generated)) cleanup() }) t.test('it validates the destination option', t => { t.plan(2) generateHeapSamplingProfile({ destination: '' }, err => { t.is(err.message, 'The destination option must be a non empty string') }) generateHeapSamplingProfile({ destination: -1 }, err => { t.is(err.message, 'The destination option must be a non empty string') }) }) t.test('it validates the duration option', t => { t.plan(2) generateHeapSamplingProfile({ duration: '' }, err => { t.is(err.message, 'The duration option must be a number greater than 0') }) generateHeapSamplingProfile({ duration: -1 }, err => { t.is(err.message, 'The duration option must be a number greater than 0') }) }) t.test('it validates the interval option', t => { t.plan(2) generateHeapSamplingProfile({ interval: '' }, err => { t.is(err.message, 'The interval option must be a number greater than 0') }) generateHeapSamplingProfile({ interval: -1 }, err => { t.is(err.message, 'The interval option must be a number greater than 0') }) }) t.test('it validates the signal option', t => { const controller = new AbortController() controller.abort() generateHeapSamplingProfile({ signal: controller.signal }, err => { t.is(err.message, 'The AbortController has already been aborted') t.end() }) })