Added a second Profiling test (for CommitDetails) and fixed some module reset prolems

This commit is contained in:
Brian Vaughn
2019-05-04 13:11:56 -07:00
parent dd96b3314c
commit da1e5776b1
7 changed files with 188 additions and 63 deletions
-1
View File
@@ -23,7 +23,6 @@
"<rootDir>/src/__tests__/setupTests"
],
"snapshotSerializers": [
"<rootDir>/src/__tests__/profilingSummarySerializer",
"<rootDir>/src/__tests__/storeSerializer"
],
"testMatch": [
@@ -1,5 +1,74 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`profiling commitDetails should be collected for each commit: 1: mount 1`] = `
[root]
▾ <Parent key="A">
<Child key="0">
<Child key="1">
`;
exports[`profiling commitDetails should be collected for each commit: 2: add child 1`] = `
[root]
▾ <Parent key="A">
<Child key="0">
<Child key="1">
<Child key="2">
`;
exports[`profiling commitDetails should be collected for each commit: 3: remove children 1`] = `
[root]
▾ <Parent key="A">
<Child key="0">
`;
exports[`profiling commitDetails should be collected for each commit: 4: profiling stopped 1`] = `
[root]
▾ <Parent key="A">
<Child key="0">
`;
exports[`profiling commitDetails should be collected for each commit: 5: CommitDetails: mount 1`] = `
Object {
"actualDurations": Map {
1 => 14,
2 => 14,
3 => 2,
4 => 2,
},
"commitIndex": 0,
"interactions": Array [],
"rootID": 1,
}
`;
exports[`profiling commitDetails should be collected for each commit: 6: CommitDetails: add child 1`] = `
Object {
"actualDurations": Map {
3 => 2,
4 => 2,
5 => 2,
2 => 16,
1 => 16,
},
"commitIndex": 1,
"interactions": Array [],
"rootID": 1,
}
`;
exports[`profiling commitDetails should be collected for each commit: 7: CommitDetails: remove children 1`] = `
Object {
"actualDurations": Map {
3 => 2,
2 => 12,
1 => 12,
},
"commitIndex": 2,
"interactions": Array [],
"rootID": 1,
}
`;
exports[`profiling profilingSummary should be collected for each commit: 1: mount 1`] = `
[root]
▾ <Parent key="A">
@@ -28,34 +97,22 @@ exports[`profiling profilingSummary should be collected for each commit: 4: prof
`;
exports[`profiling profilingSummary should be collected for each commit: ProfilingSummary 1`] = `
{
Object {
"commitDurations": Array [
16,
12,
],
"commitTimes": Array [
16,
28,
],
"initialTreeBaseDurations": Map {
1 => 14,
2 => 14,
3 => 2,
4 => 2,
},
"interactionCount": 0,
"rootID": 1,
"commitDurations": [
16,
12
],
"commitTimes": [
16,
28
],
"initialTreeBaseDurations": [
[
1,
14
],
[
2,
14
],
[
3,
2
],
[
4,
2
]
],
"interactionCount": 0
}
`;
+87
View File
@@ -76,6 +76,93 @@ describe('profiling', () => {
});
});
describe('commitDetails', () => {
it('should be collected for each commit', async done => {
const Parent = ({ count }) => {
Scheduler.advanceTime(10);
return new Array(count)
.fill(true)
.map((_, index) => <Child key={index} />);
};
const Child = () => {
Scheduler.advanceTime(2);
return null;
};
const container = document.createElement('div');
utils.act(() => store.startProfiling());
utils.act(() => ReactDOM.render(<Parent key="A" count={2} />, container));
expect(store).toMatchSnapshot('1: mount');
utils.act(() => ReactDOM.render(<Parent key="A" count={3} />, container));
expect(store).toMatchSnapshot('2: add child');
utils.act(() => ReactDOM.render(<Parent key="A" count={1} />, container));
expect(store).toMatchSnapshot('3: remove children');
utils.act(() => store.stopProfiling());
expect(store).toMatchSnapshot('4: profiling stopped');
let commitDetails;
function Suspender({ commitIndex, rendererID, rootID }) {
commitDetails = store.profilingCache.CommitDetails.read({
commitIndex,
rendererID,
rootID,
});
return null;
}
const rendererID = utils.getRendererID();
const rootID = store.roots[0];
await utils.actSuspense(() =>
TestRenderer.create(
<React.Suspense fallback={null}>
<Suspender
commitIndex={0}
rendererID={rendererID}
rootID={rootID}
/>
</React.Suspense>
)
);
expect(commitDetails).toMatchSnapshot('5: CommitDetails: mount');
await utils.actSuspense(() =>
TestRenderer.create(
<React.Suspense fallback={null}>
<Suspender
commitIndex={1}
rendererID={rendererID}
rootID={rootID}
/>
</React.Suspense>
)
);
expect(commitDetails).toMatchSnapshot('6: CommitDetails: add child');
await utils.actSuspense(() =>
TestRenderer.create(
<React.Suspense fallback={null}>
<Suspender
commitIndex={2}
rendererID={rendererID}
rootID={rootID}
/>
</React.Suspense>
)
);
expect(commitDetails).toMatchSnapshot(
'7: CommitDetails: remove children'
);
done();
});
});
it('should remove profiling data when roots are unmounted', async () => {
const Parent = ({ count }) =>
new Array(count).fill(true).map((_, index) => <Child key={index} />);
@@ -1,25 +0,0 @@
// test() is part of Jest's serializer API
export function test(maybeProfilingSummary) {
return (
typeof maybeProfilingSummary === 'object' &&
maybeProfilingSummary !== null &&
typeof maybeProfilingSummary.rootID === 'number' &&
Array.isArray(maybeProfilingSummary.commitDurations) &&
Array.isArray(maybeProfilingSummary.commitTimes) &&
typeof maybeProfilingSummary.initialTreeBaseDurations === 'object' &&
maybeProfilingSummary.initialTreeBaseDurations !== null &&
typeof maybeProfilingSummary.interactionCount === 'number'
);
}
// print() is part of Jest's serializer API
export function print(profilingSummary, serialize, indent) {
return JSON.stringify(
{
...profilingSummary,
initialTreeBaseDurations: [...profilingSummary.initialTreeBaseDurations],
},
null,
2
);
}
+10 -6
View File
@@ -1,13 +1,17 @@
// @flow
import Agent from 'src/backend/agent';
import { initBackend } from 'src/backend';
import Bridge from 'src/bridge';
import Store from 'src/devtools/store';
import { installHook } from 'src/hook';
const env = jasmine.getEnv();
env.beforeEach(() => {
// These files should be required (and re-reuired) before each test,
// rather than imported at the head of the module.
// That's because we reset modules between tests,
// which disconnects the DevTool's cache from the current dispatcher ref.
const Agent = require('src/backend/agent').default;
const { initBackend } = require('src/backend');
const Bridge = require('src/bridge').default;
const Store = require('src/devtools/store').default;
const { installHook } = require('src/hook');
// Fake timers let us flush Bridge operations between setup and assertions.
jest.useFakeTimers();
+4 -3
View File
@@ -1,8 +1,9 @@
import Store from 'src/devtools/store';
// test() is part of Jest's serializer API
export function test(maybeStore) {
return maybeStore instanceof Store;
// It's important to lazy-require the Store rather than imported at the head of the module.
// Because we reset modules between tests, different Store implementations will be used for each test.
// Unfortunately Jest does not reset its own serializer modules.
return maybeStore instanceof require('src/devtools/store').default;
}
// print() is part of Jest's serializer API
+2
View File
@@ -49,6 +49,8 @@ const Pending = 0;
const Resolved = 1;
const Rejected = 2;
// TODO This file isn't being re-imported between tests it seems, so it's getting disconnected
const ReactCurrentDispatcher = (React: any)
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher;