feat: promisify contentTracing recording APIs (#16584)
* feat: promisify contentTracing.startRecording() * feat: promisify contentTracing.stopRecording() * test: convert specs for new promisified apis * chore: deprecate and ensure legacy tests work
This commit is contained in:
parent
cbb5164cc8
commit
ba57e1d991
5 changed files with 152 additions and 20 deletions
|
@ -66,10 +66,19 @@ scoped_refptr<TracingController::TraceDataEndpoint> GetTraceDataEndpoint(
|
||||||
result_file_path, base::Bind(callback, result_file_path));
|
result_file_path, base::Bind(callback, result_file_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopRecording(const base::FilePath& path,
|
void OnRecordingStopped(scoped_refptr<atom::util::Promise> promise,
|
||||||
const CompletionCallback& callback) {
|
const base::FilePath& path) {
|
||||||
|
promise->Resolve(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Promise> StopRecording(v8::Isolate* isolate,
|
||||||
|
const base::FilePath& path) {
|
||||||
|
scoped_refptr<atom::util::Promise> promise = new atom::util::Promise(isolate);
|
||||||
|
|
||||||
TracingController::GetInstance()->StopTracing(
|
TracingController::GetInstance()->StopTracing(
|
||||||
GetTraceDataEndpoint(path, callback));
|
GetTraceDataEndpoint(path, base::Bind(&OnRecordingStopped, promise)));
|
||||||
|
|
||||||
|
return promise->GetHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnCategoriesAvailable(scoped_refptr<atom::util::Promise> promise,
|
void OnCategoriesAvailable(scoped_refptr<atom::util::Promise> promise,
|
||||||
|
@ -88,10 +97,22 @@ v8::Local<v8::Promise> GetCategories(v8::Isolate* isolate) {
|
||||||
return promise->GetHandle();
|
return promise->GetHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StartTracing(const base::trace_event::TraceConfig& trace_config,
|
void OnTracingStarted(scoped_refptr<atom::util::Promise> promise) {
|
||||||
const base::RepeatingCallback<void()>& callback) {
|
promise->Resolve();
|
||||||
return TracingController::GetInstance()->StartTracing(
|
}
|
||||||
trace_config, base::BindOnce(callback));
|
|
||||||
|
v8::Local<v8::Promise> StartTracing(
|
||||||
|
v8::Isolate* isolate,
|
||||||
|
const base::trace_event::TraceConfig& trace_config) {
|
||||||
|
scoped_refptr<atom::util::Promise> promise = new atom::util::Promise(isolate);
|
||||||
|
|
||||||
|
bool success = TracingController::GetInstance()->StartTracing(
|
||||||
|
trace_config, base::BindOnce(&OnTracingStarted, promise));
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
promise->RejectWithErrorMessage("Could not start tracing");
|
||||||
|
|
||||||
|
return promise->GetHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetTraceBufferUsage(
|
bool GetTraceBufferUsage(
|
||||||
|
|
|
@ -12,7 +12,6 @@ result.
|
||||||
**Note:** You should not use this module until the `ready` event of the app
|
**Note:** You should not use this module until the `ready` event of the app
|
||||||
module is emitted.
|
module is emitted.
|
||||||
|
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const { app, contentTracing } = require('electron')
|
const { app, contentTracing } = require('electron')
|
||||||
|
|
||||||
|
@ -67,6 +66,19 @@ Recording begins immediately locally and asynchronously on child processes
|
||||||
as soon as they receive the EnableRecording request. The `callback` will be
|
as soon as they receive the EnableRecording request. The `callback` will be
|
||||||
called once all child processes have acknowledged the `startRecording` request.
|
called once all child processes have acknowledged the `startRecording` request.
|
||||||
|
|
||||||
|
**[Deprecated Soon](promisification.md)**
|
||||||
|
|
||||||
|
### `contentTracing.startRecording(options)`
|
||||||
|
|
||||||
|
* `options` ([TraceCategoriesAndOptions](structures/trace-categories-and-options.md) | [TraceConfig](structures/trace-config.md))
|
||||||
|
|
||||||
|
Returns `Promise<void>` - resolved once all child processes have acknowledged the `startRecording` request.
|
||||||
|
|
||||||
|
Start recording on all processes.
|
||||||
|
|
||||||
|
Recording begins immediately locally and asynchronously on child processes
|
||||||
|
as soon as they receive the EnableRecording request.
|
||||||
|
|
||||||
### `contentTracing.stopRecording(resultFilePath, callback)`
|
### `contentTracing.stopRecording(resultFilePath, callback)`
|
||||||
|
|
||||||
* `resultFilePath` String
|
* `resultFilePath` String
|
||||||
|
@ -88,6 +100,25 @@ Trace data will be written into `resultFilePath` if it is not empty or into a
|
||||||
temporary file. The actual file path will be passed to `callback` if it's not
|
temporary file. The actual file path will be passed to `callback` if it's not
|
||||||
`null`.
|
`null`.
|
||||||
|
|
||||||
|
**[Deprecated Soon](promisification.md)**
|
||||||
|
|
||||||
|
### `contentTracing.stopRecording(resultFilePath)`
|
||||||
|
|
||||||
|
* `resultFilePath` String
|
||||||
|
|
||||||
|
Returns `Promise<String>` - resolves with a file that contains the traced data once all child processes have acknowledged the `stopRecording` request
|
||||||
|
|
||||||
|
Stop recording on all processes.
|
||||||
|
|
||||||
|
Child processes typically cache trace data and only rarely flush and send
|
||||||
|
trace data back to the main process. This helps to minimize the runtime overhead
|
||||||
|
of tracing since sending trace data over IPC can be an expensive operation. So,
|
||||||
|
to end tracing, we must asynchronously ask all child processes to flush any
|
||||||
|
pending trace data.
|
||||||
|
|
||||||
|
Trace data will be written into `resultFilePath` if it is not empty or into a
|
||||||
|
temporary file.
|
||||||
|
|
||||||
### `contentTracing.getTraceBufferUsage(callback)`
|
### `contentTracing.getTraceBufferUsage(callback)`
|
||||||
|
|
||||||
* `callback` Function
|
* `callback` Function
|
||||||
|
|
|
@ -11,8 +11,6 @@ When a majority of affected functions are migrated, this flag will be enabled by
|
||||||
- [app.importCertificate(options, callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#importCertificate)
|
- [app.importCertificate(options, callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#importCertificate)
|
||||||
- [request.write(chunk[, encoding][, callback])](https://github.com/electron/electron/blob/master/docs/api/client-request.md#write)
|
- [request.write(chunk[, encoding][, callback])](https://github.com/electron/electron/blob/master/docs/api/client-request.md#write)
|
||||||
- [request.end([chunk][, encoding][, callback])](https://github.com/electron/electron/blob/master/docs/api/client-request.md#end)
|
- [request.end([chunk][, encoding][, callback])](https://github.com/electron/electron/blob/master/docs/api/client-request.md#end)
|
||||||
- [contentTracing.startRecording(options, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#startRecording)
|
|
||||||
- [contentTracing.stopRecording(resultFilePath, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#stopRecording)
|
|
||||||
- [contentTracing.getTraceBufferUsage(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getTraceBufferUsage)
|
- [contentTracing.getTraceBufferUsage(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getTraceBufferUsage)
|
||||||
- [debugger.sendCommand(method[, commandParams, callback])](https://github.com/electron/electron/blob/master/docs/api/debugger.md#sendCommand)
|
- [debugger.sendCommand(method[, commandParams, callback])](https://github.com/electron/electron/blob/master/docs/api/debugger.md#sendCommand)
|
||||||
- [dialog.showOpenDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showOpenDialog)
|
- [dialog.showOpenDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showOpenDialog)
|
||||||
|
@ -45,6 +43,8 @@ When a majority of affected functions are migrated, this flag will be enabled by
|
||||||
- [app.getFileIcon(path[, options], callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#getFileIcon)
|
- [app.getFileIcon(path[, options], callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#getFileIcon)
|
||||||
- [contents.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#capturePage)
|
- [contents.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#capturePage)
|
||||||
- [contentTracing.getCategories(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getCategories)
|
- [contentTracing.getCategories(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getCategories)
|
||||||
|
- [contentTracing.startRecording(options, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#startRecording)
|
||||||
|
- [contentTracing.stopRecording(resultFilePath, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#stopRecording)
|
||||||
- [cookies.flushStore(callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#flushStore)
|
- [cookies.flushStore(callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#flushStore)
|
||||||
- [cookies.get(filter, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#get)
|
- [cookies.get(filter, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#get)
|
||||||
- [cookies.remove(url, name, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#remove)
|
- [cookies.remove(url, name, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#remove)
|
||||||
|
|
|
@ -3,5 +3,7 @@ const { deprecate } = require('electron')
|
||||||
const contentTracing = process.atomBinding('content_tracing')
|
const contentTracing = process.atomBinding('content_tracing')
|
||||||
|
|
||||||
contentTracing.getCategories = deprecate.promisify(contentTracing.getCategories)
|
contentTracing.getCategories = deprecate.promisify(contentTracing.getCategories)
|
||||||
|
contentTracing.startRecording = deprecate.promisify(contentTracing.startRecording)
|
||||||
|
contentTracing.stopRecording = deprecate.promisify(contentTracing.stopRecording)
|
||||||
|
|
||||||
module.exports = contentTracing
|
module.exports = contentTracing
|
||||||
|
|
|
@ -28,6 +28,28 @@ describe('contentTracing', () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const record = async (options, outputFilePath, recordTimeInMilliseconds = 1e3) => {
|
||||||
|
await app.whenReady()
|
||||||
|
|
||||||
|
await contentTracing.startRecording(options)
|
||||||
|
await timeout(recordTimeInMilliseconds)
|
||||||
|
const resultFilePath = await contentTracing.stopRecording(outputFilePath)
|
||||||
|
|
||||||
|
return resultFilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(codebytere): remove when promisification is complete
|
||||||
|
const recordCallback = async (options, outputFilePath, recordTimeInMilliseconds = 1e3) => {
|
||||||
|
await app.whenReady()
|
||||||
|
|
||||||
|
await startRecording(options)
|
||||||
|
await timeout(recordTimeInMilliseconds)
|
||||||
|
const resultFilePath = await stopRecording(outputFilePath)
|
||||||
|
|
||||||
|
return resultFilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(codebytere): remove when promisification is complete
|
||||||
const startRecording = async (options) => {
|
const startRecording = async (options) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
contentTracing.startRecording(options, () => {
|
contentTracing.startRecording(options, () => {
|
||||||
|
@ -36,6 +58,7 @@ describe('contentTracing', () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(codebytere): remove when promisification is complete
|
||||||
const stopRecording = async (filePath) => {
|
const stopRecording = async (filePath) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
contentTracing.stopRecording(filePath, (resultFilePath) => {
|
contentTracing.stopRecording(filePath, (resultFilePath) => {
|
||||||
|
@ -44,16 +67,6 @@ describe('contentTracing', () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = async (options, outputFilePath, recordTimeInMilliseconds = 1e3) => {
|
|
||||||
await app.whenReady()
|
|
||||||
|
|
||||||
await startRecording(options)
|
|
||||||
await timeout(recordTimeInMilliseconds)
|
|
||||||
const resultFilePath = await stopRecording(outputFilePath)
|
|
||||||
|
|
||||||
return resultFilePath
|
|
||||||
}
|
|
||||||
|
|
||||||
const outputFilePath = getPathInATempFolder('trace.json')
|
const outputFilePath = getPathInATempFolder('trace.json')
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
if (fs.existsSync(outputFilePath)) {
|
if (fs.existsSync(outputFilePath)) {
|
||||||
|
@ -82,6 +95,18 @@ describe('contentTracing', () => {
|
||||||
`the trace output file is empty, check "${outputFilePath}"`)
|
`the trace output file is empty, check "${outputFilePath}"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO(codebytere): remove when promisification is complete
|
||||||
|
it('accepts an empty config (callback)', async () => {
|
||||||
|
const config = {}
|
||||||
|
await recordCallback(config, outputFilePath)
|
||||||
|
|
||||||
|
expect(fs.existsSync(outputFilePath)).to.be.true()
|
||||||
|
|
||||||
|
const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath)
|
||||||
|
expect(fileSizeInKiloBytes).to.be.above(0,
|
||||||
|
`the trace output file is empty, check "${outputFilePath}"`)
|
||||||
|
})
|
||||||
|
|
||||||
it('accepts a trace config', async () => {
|
it('accepts a trace config', async () => {
|
||||||
// (alexeykuzmin): All categories are excluded on purpose,
|
// (alexeykuzmin): All categories are excluded on purpose,
|
||||||
// so only metadata gets into the output file.
|
// so only metadata gets into the output file.
|
||||||
|
@ -104,6 +129,29 @@ describe('contentTracing', () => {
|
||||||
check "${outputFilePath}"`)
|
check "${outputFilePath}"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO(codebytere): remove when promisification is complete
|
||||||
|
it('accepts a trace config (callback)', async () => {
|
||||||
|
// (alexeykuzmin): All categories are excluded on purpose,
|
||||||
|
// so only metadata gets into the output file.
|
||||||
|
const config = {
|
||||||
|
excluded_categories: ['*']
|
||||||
|
}
|
||||||
|
await recordCallback(config, outputFilePath)
|
||||||
|
|
||||||
|
expect(fs.existsSync(outputFilePath)).to.be.true()
|
||||||
|
|
||||||
|
// If the `excluded_categories` param above is not respected
|
||||||
|
// the file size will be above 50KB.
|
||||||
|
const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath)
|
||||||
|
const expectedMaximumFileSize = 10 // Depends on a platform.
|
||||||
|
|
||||||
|
expect(fileSizeInKiloBytes).to.be.above(0,
|
||||||
|
`the trace output file is empty, check "${outputFilePath}"`)
|
||||||
|
expect(fileSizeInKiloBytes).to.be.below(expectedMaximumFileSize,
|
||||||
|
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
|
||||||
|
check "${outputFilePath}"`)
|
||||||
|
})
|
||||||
|
|
||||||
it('accepts "categoryFilter" and "traceOptions" as a config', async () => {
|
it('accepts "categoryFilter" and "traceOptions" as a config', async () => {
|
||||||
// (alexeykuzmin): All categories are excluded on purpose,
|
// (alexeykuzmin): All categories are excluded on purpose,
|
||||||
// so only metadata gets into the output file.
|
// so only metadata gets into the output file.
|
||||||
|
@ -126,6 +174,30 @@ describe('contentTracing', () => {
|
||||||
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
|
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
|
||||||
check "${outputFilePath}"`)
|
check "${outputFilePath}"`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO(codebytere): remove when promisification is complete
|
||||||
|
it('accepts "categoryFilter" and "traceOptions" as a config (callback)', async () => {
|
||||||
|
// (alexeykuzmin): All categories are excluded on purpose,
|
||||||
|
// so only metadata gets into the output file.
|
||||||
|
const config = {
|
||||||
|
categoryFilter: '__ThisIsANonexistentCategory__',
|
||||||
|
traceOptions: ''
|
||||||
|
}
|
||||||
|
await recordCallback(config, outputFilePath)
|
||||||
|
|
||||||
|
expect(fs.existsSync(outputFilePath)).to.be.true()
|
||||||
|
|
||||||
|
// If the `categoryFilter` param above is not respected
|
||||||
|
// the file size will be above 50KB.
|
||||||
|
const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath)
|
||||||
|
const expectedMaximumFileSize = 10 // Depends on a platform.
|
||||||
|
|
||||||
|
expect(fileSizeInKiloBytes).to.be.above(0,
|
||||||
|
`the trace output file is empty, check "${outputFilePath}"`)
|
||||||
|
expect(fileSizeInKiloBytes).to.be.below(expectedMaximumFileSize,
|
||||||
|
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
|
||||||
|
check "${outputFilePath}"`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('stopRecording', function () {
|
describe('stopRecording', function () {
|
||||||
|
@ -136,6 +208,12 @@ describe('contentTracing', () => {
|
||||||
expect(resultFilePath).to.be.a('string').and.be.equal(outputFilePath)
|
expect(resultFilePath).to.be.a('string').and.be.equal(outputFilePath)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// TODO(codebytere): remove when promisification is complete
|
||||||
|
it('calls its callback with a result file path (callback)', async () => {
|
||||||
|
const resultFilePath = await recordCallback(/* options */ {}, outputFilePath)
|
||||||
|
expect(resultFilePath).to.be.a('string').and.be.equal(outputFilePath)
|
||||||
|
})
|
||||||
|
|
||||||
// FIXME(alexeykuzmin): https://github.com/electron/electron/issues/16019
|
// FIXME(alexeykuzmin): https://github.com/electron/electron/issues/16019
|
||||||
xit('creates a temporary file when an empty string is passed', async function () {
|
xit('creates a temporary file when an empty string is passed', async function () {
|
||||||
const resultFilePath = await record(/* options */ {}, /* outputFilePath */ '')
|
const resultFilePath = await record(/* options */ {}, /* outputFilePath */ '')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue