Added profiling chart data tests

This commit is contained in:
Brian Vaughn
2019-05-06 13:37:06 -07:00
parent d86bc1020e
commit b25d996fc4
4 changed files with 641 additions and 7 deletions
@@ -0,0 +1,333 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`profiling charts flamegraph chart should contain valid data: 0: CommitTree 1`] = `
Object {
"nodes": Map {
1 => Object {
"children": Array [
2,
],
"displayName": null,
"id": 1,
"key": null,
"parentID": 0,
"treeBaseDuration": 14,
},
2 => Object {
"children": Array [
3,
4,
],
"displayName": "Parent",
"id": 2,
"key": null,
"parentID": 1,
"treeBaseDuration": 14,
},
3 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 3,
"key": "first",
"parentID": 2,
"treeBaseDuration": 2,
},
4 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 4,
"key": "second",
"parentID": 2,
"treeBaseDuration": 2,
},
},
"rootID": 1,
}
`;
exports[`profiling charts flamegraph chart should contain valid data: 0: FlamegraphChartData 1`] = `
Object {
"baseDuration": 14,
"depth": 2,
"idToDepthMap": Map {
2 => 1,
4 => 2,
3 => 2,
},
"maxSelfDuration": 10,
"rows": Array [
Array [
Object {
"actualDuration": 14,
"didRender": true,
"id": 2,
"label": "Parent (10.0ms of 14.0ms)",
"name": "Parent",
"offset": 0,
"selfDuration": 10,
"treeBaseDuration": 14,
},
],
Array [
Object {
"actualDuration": 2,
"didRender": true,
"id": 4,
"label": "Memo(Child) key=\\"second\\" (2.0ms of 2.0ms)",
"name": "Memo(Child)",
"offset": 12,
"selfDuration": 2,
"treeBaseDuration": 2,
},
Object {
"actualDuration": 2,
"didRender": true,
"id": 3,
"label": "Memo(Child) key=\\"first\\" (2.0ms of 2.0ms)",
"name": "Memo(Child)",
"offset": 10,
"selfDuration": 2,
"treeBaseDuration": 2,
},
],
],
}
`;
exports[`profiling charts flamegraph chart should contain valid data: 1: CommitTree 1`] = `
Object {
"nodes": Map {
1 => Object {
"children": Array [
2,
],
"displayName": null,
"id": 1,
"key": null,
"parentID": 0,
"treeBaseDuration": 14,
},
2 => Object {
"children": Array [
3,
4,
],
"displayName": "Parent",
"id": 2,
"key": null,
"parentID": 1,
"treeBaseDuration": 14,
},
3 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 3,
"key": "first",
"parentID": 2,
"treeBaseDuration": 2,
},
4 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 4,
"key": "second",
"parentID": 2,
"treeBaseDuration": 2,
},
},
"rootID": 1,
}
`;
exports[`profiling charts flamegraph chart should contain valid data: 1: FlamegraphChartData 1`] = `
Object {
"baseDuration": 14,
"depth": 2,
"idToDepthMap": Map {
2 => 1,
4 => 2,
3 => 2,
},
"maxSelfDuration": 10,
"rows": Array [
Array [
Object {
"actualDuration": 10,
"didRender": true,
"id": 2,
"label": "Parent (10.0ms of 10.0ms)",
"name": "Parent",
"offset": 0,
"selfDuration": 10,
"treeBaseDuration": 14,
},
],
Array [
Object {
"actualDuration": 0,
"didRender": false,
"id": 4,
"label": "Memo(Child) key=\\"second\\"",
"name": "Memo(Child)",
"offset": 12,
"selfDuration": 0,
"treeBaseDuration": 2,
},
Object {
"actualDuration": 0,
"didRender": false,
"id": 3,
"label": "Memo(Child) key=\\"first\\"",
"name": "Memo(Child)",
"offset": 10,
"selfDuration": 0,
"treeBaseDuration": 2,
},
],
],
}
`;
exports[`profiling charts interactions should contain valid data: Interactions 1`] = `
Object {
"lastInteractionTime": 24,
"maxCommitDuration": 14,
}
`;
exports[`profiling charts interactions should contain valid data: Interactions 2`] = `
Object {
"lastInteractionTime": 24,
"maxCommitDuration": 14,
}
`;
exports[`profiling charts ranked chart should contain valid data: 0: CommitTree 1`] = `
Object {
"nodes": Map {
1 => Object {
"children": Array [
2,
],
"displayName": null,
"id": 1,
"key": null,
"parentID": 0,
"treeBaseDuration": 14,
},
2 => Object {
"children": Array [
3,
4,
],
"displayName": "Parent",
"id": 2,
"key": null,
"parentID": 1,
"treeBaseDuration": 14,
},
3 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 3,
"key": "first",
"parentID": 2,
"treeBaseDuration": 2,
},
4 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 4,
"key": "second",
"parentID": 2,
"treeBaseDuration": 2,
},
},
"rootID": 1,
}
`;
exports[`profiling charts ranked chart should contain valid data: 0: RankedChartData 1`] = `
Object {
"maxValue": 10,
"nodes": Array [
Object {
"id": 2,
"label": "Parent (10.0ms)",
"name": "Parent",
"value": 10,
},
Object {
"id": 3,
"label": "Memo(Child) key=\\"first\\" (2.0ms)",
"name": "Memo(Child)",
"value": 2,
},
Object {
"id": 4,
"label": "Memo(Child) key=\\"second\\" (2.0ms)",
"name": "Memo(Child)",
"value": 2,
},
],
}
`;
exports[`profiling charts ranked chart should contain valid data: 1: CommitTree 1`] = `
Object {
"nodes": Map {
1 => Object {
"children": Array [
2,
],
"displayName": null,
"id": 1,
"key": null,
"parentID": 0,
"treeBaseDuration": 14,
},
2 => Object {
"children": Array [
3,
4,
],
"displayName": "Parent",
"id": 2,
"key": null,
"parentID": 1,
"treeBaseDuration": 14,
},
3 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 3,
"key": "first",
"parentID": 2,
"treeBaseDuration": 2,
},
4 => Object {
"children": Array [],
"displayName": "Memo(Child)",
"id": 4,
"key": "second",
"parentID": 2,
"treeBaseDuration": 2,
},
},
"rootID": 1,
}
`;
exports[`profiling charts ranked chart should contain valid data: 1: RankedChartData 1`] = `
Object {
"maxValue": 10,
"nodes": Array [
Object {
"id": 2,
"label": "Parent (10.0ms)",
"name": "Parent",
"value": 10,
},
],
}
`;
+2 -4
View File
@@ -1,13 +1,11 @@
// @flow
import type React from 'react';
import type ReactDOM from 'react-dom';
import typeof ReactTestRenderer from 'react-test-renderer';
import type Store from 'src/devtools/store';
describe('profiling', () => {
let React: React;
let ReactDOM: ReactDOM;
let React;
let ReactDOM;
let Scheduler;
let SchedulerTracing;
let TestRenderer: ReactTestRenderer;
+292
View File
@@ -0,0 +1,292 @@
// @flow
import typeof TestRendererType from 'react-test-renderer';
import type Store from 'src/devtools/store';
describe('profiling charts', () => {
let React;
let ReactDOM;
let Scheduler;
let SchedulerTracing;
let TestRenderer: TestRendererType;
let store: Store;
let utils;
beforeEach(() => {
utils = require('./utils');
utils.beforeEachProfiling();
store = global.store;
store.collapseNodesByDefault = false;
React = require('react');
ReactDOM = require('react-dom');
Scheduler = require('scheduler');
SchedulerTracing = require('scheduler/tracing');
TestRenderer = utils.requireTestRenderer();
});
describe('flamegraph chart', () => {
it('should contain valid data', async done => {
const Parent = ({ count }) => {
Scheduler.advanceTime(10);
return (
<React.Fragment>
<Child key="first" />
<Child key="second" />
</React.Fragment>
);
};
// Memoize children to verify that chart doesn't include in the update.
const Child = React.memo(function Child() {
Scheduler.advanceTime(2);
return null;
});
const container = document.createElement('div');
utils.act(() => store.startProfiling());
utils.act(() =>
SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
ReactDOM.render(<Parent />, container)
)
);
utils.act(() =>
SchedulerTracing.unstable_trace(
'update',
Scheduler.unstable_now(),
() => ReactDOM.render(<Parent />, container)
)
);
utils.act(() => store.stopProfiling());
let suspenseResolved = false;
function Suspender({ commitIndex, rendererID, rootID }) {
const profilingSummary = store.profilingCache.ProfilingSummary.read({
rendererID,
rootID,
});
const commitDetails = store.profilingCache.CommitDetails.read({
commitIndex,
rendererID,
rootID,
});
suspenseResolved = true;
const commitTree = store.profilingCache.getCommitTree({
commitIndex,
profilingSummary,
});
const chartData = store.profilingCache.getFlamegraphChartData({
commitDetails,
commitIndex,
commitTree,
});
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
expect(chartData).toMatchSnapshot(
`${commitIndex}: FlamegraphChartData`
);
return null;
}
const rendererID = utils.getRendererID();
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
suspenseResolved = false;
await utils.actSuspense(
() =>
TestRenderer.create(
<React.Suspense fallback={null}>
<Suspender
commitIndex={commitIndex}
rendererID={rendererID}
rootID={rootID}
/>
</React.Suspense>
),
3
);
expect(suspenseResolved).toBe(true);
}
expect(suspenseResolved).toBe(true);
done();
});
});
describe('ranked chart', () => {
it('should contain valid data', async done => {
const Parent = ({ count }) => {
Scheduler.advanceTime(10);
return (
<React.Fragment>
<Child key="first" />
<Child key="second" />
</React.Fragment>
);
};
// Memoize children to verify that chart doesn't include in the update.
const Child = React.memo(function Child() {
Scheduler.advanceTime(2);
return null;
});
const container = document.createElement('div');
utils.act(() => store.startProfiling());
utils.act(() =>
SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
ReactDOM.render(<Parent />, container)
)
);
utils.act(() =>
SchedulerTracing.unstable_trace(
'update',
Scheduler.unstable_now(),
() => ReactDOM.render(<Parent />, container)
)
);
utils.act(() => store.stopProfiling());
let suspenseResolved = false;
function Suspender({ commitIndex, rendererID, rootID }) {
const profilingSummary = store.profilingCache.ProfilingSummary.read({
rendererID,
rootID,
});
const commitDetails = store.profilingCache.CommitDetails.read({
commitIndex,
rendererID,
rootID,
});
suspenseResolved = true;
const commitTree = store.profilingCache.getCommitTree({
commitIndex,
profilingSummary,
});
const chartData = store.profilingCache.getRankedChartData({
commitDetails,
commitIndex,
commitTree,
});
expect(commitTree).toMatchSnapshot(`${commitIndex}: CommitTree`);
expect(chartData).toMatchSnapshot(`${commitIndex}: RankedChartData`);
return null;
}
const rendererID = utils.getRendererID();
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
suspenseResolved = false;
await utils.actSuspense(
() =>
TestRenderer.create(
<React.Suspense fallback={null}>
<Suspender
commitIndex={commitIndex}
rendererID={rendererID}
rootID={rootID}
/>
</React.Suspense>
),
3
);
expect(suspenseResolved).toBe(true);
}
done();
});
});
describe('interactions', () => {
it('should contain valid data', async done => {
const Parent = ({ count }) => {
Scheduler.advanceTime(10);
return (
<React.Fragment>
<Child key="first" />
<Child key="second" />
</React.Fragment>
);
};
// Memoize children to verify that chart doesn't include in the update.
const Child = React.memo(function Child() {
Scheduler.advanceTime(2);
return null;
});
const container = document.createElement('div');
utils.act(() => store.startProfiling());
utils.act(() =>
SchedulerTracing.unstable_trace('mount', Scheduler.unstable_now(), () =>
ReactDOM.render(<Parent />, container)
)
);
utils.act(() =>
SchedulerTracing.unstable_trace(
'update',
Scheduler.unstable_now(),
() => ReactDOM.render(<Parent />, container)
)
);
utils.act(() => store.stopProfiling());
let suspenseResolved = false;
function Suspender({ commitIndex, rendererID, rootID }) {
const profilingSummary = store.profilingCache.ProfilingSummary.read({
rendererID,
rootID,
});
const interactions = store.profilingCache.Interactions.read({
rendererID,
rootID,
});
suspenseResolved = true;
const chartData = store.profilingCache.getInteractionsChartData({
interactions,
profilingSummary,
});
expect(chartData).toMatchSnapshot('Interactions');
return null;
}
const rendererID = utils.getRendererID();
const rootID = store.roots[0];
for (let commitIndex = 0; commitIndex < 2; commitIndex++) {
suspenseResolved = false;
await utils.actSuspense(
() =>
TestRenderer.create(
<React.Suspense fallback={null}>
<Suspender
commitIndex={commitIndex}
rendererID={rendererID}
rootID={rootID}
/>
</React.Suspense>
),
3
);
expect(suspenseResolved).toBe(true);
}
done();
});
});
});
+14 -3
View File
@@ -12,7 +12,10 @@ export function act(callback: Function): void {
jest.runAllTimers();
}
export async function actSuspense(callback: Function): Promise<void> {
export async function actSuspense(
callback: Function,
numTimesToFlush: number = 1
): Promise<void> {
const TestUtils = require('react-dom/test-utils');
const Scheduler = require('scheduler');
@@ -24,8 +27,16 @@ export async function actSuspense(callback: Function): Promise<void> {
jest.runAllTimers();
});
// Re-render after resolved promises
Scheduler.flushAll();
// Run cascading microtasks and flush scheduled React work.
// Components that suspend multiple times will need to do this once per suspend operation.
// HACK Ideally the mock scheduler would provide an API to ask if there was outstanding work.
while (--numTimesToFlush >= 0) {
// $FlowFixMe Flow doens't know about "await act()" yet
await TestUtils.act(async () => {
jest.runAllTimers();
Scheduler.flushAll();
});
}
}
export function beforeEachProfiling(): void {