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:
Shelley Vohr 2019-01-30 18:53:55 -08:00 committed by GitHub
parent cbb5164cc8
commit ba57e1d991
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 152 additions and 20 deletions

View file

@ -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(

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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 */ '')