Kill metrics reducer
Reviewed By: nikoant Differential Revision: D24332440 fbshipit-source-id: 0a48b25f98d93b181b622e8477a74c7ef0094816
This commit is contained in:
committed by
Facebook GitHub Bot
parent
707b8a922e
commit
41a1af33cb
@@ -33,7 +33,6 @@ export {
|
|||||||
} from './plugin';
|
} from './plugin';
|
||||||
export {PluginClient, Props} from './plugin';
|
export {PluginClient, Props} from './plugin';
|
||||||
export {default as Client} from './Client';
|
export {default as Client} from './Client';
|
||||||
export {MetricType} from './utils/exportMetrics';
|
|
||||||
export {reportUsage} from './utils/metrics';
|
export {reportUsage} from './utils/metrics';
|
||||||
export {default as promiseTimeout} from './utils/promiseTimeout';
|
export {default as promiseTimeout} from './utils/promiseTimeout';
|
||||||
export {clipboard, remote, OpenDialogOptions} from 'electron';
|
export {clipboard, remote, OpenDialogOptions} from 'electron';
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {KeyboardActions} from './MenuBar';
|
|||||||
import {Logger} from './fb-interfaces/Logger';
|
import {Logger} from './fb-interfaces/Logger';
|
||||||
import Client from './Client';
|
import Client from './Client';
|
||||||
import {Store} from './reducers/index';
|
import {Store} from './reducers/index';
|
||||||
import {MetricType} from './utils/exportMetrics';
|
|
||||||
import {ReactNode, Component} from 'react';
|
import {ReactNode, Component} from 'react';
|
||||||
import BaseDevice from './devices/BaseDevice';
|
import BaseDevice from './devices/BaseDevice';
|
||||||
import {serialize, deserialize} from './utils/serialization';
|
import {serialize, deserialize} from './utils/serialization';
|
||||||
@@ -131,9 +130,6 @@ export abstract class FlipperBasePlugin<
|
|||||||
static defaultPersistedState: any;
|
static defaultPersistedState: any;
|
||||||
static persistedStateReducer: PersistedStateReducer | null;
|
static persistedStateReducer: PersistedStateReducer | null;
|
||||||
static maxQueueSize: number = DEFAULT_MAX_QUEUE_SIZE;
|
static maxQueueSize: number = DEFAULT_MAX_QUEUE_SIZE;
|
||||||
static metricsReducer:
|
|
||||||
| ((persistedState: StaticPersistedState) => Promise<MetricType>)
|
|
||||||
| undefined;
|
|
||||||
static exportPersistedState:
|
static exportPersistedState:
|
||||||
| ((
|
| ((
|
||||||
callClient: (method: string, params?: any) => Promise<any>,
|
callClient: (method: string, params?: any) => Promise<any>,
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the MIT license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
* @format
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {serialize} from './serialization';
|
|
||||||
import {State as PluginStatesState} from '../reducers/pluginStates';
|
|
||||||
import {Store} from '../reducers';
|
|
||||||
import fs from 'fs';
|
|
||||||
import {
|
|
||||||
ExportType,
|
|
||||||
fetchMetadata,
|
|
||||||
determinePluginsToProcess,
|
|
||||||
} from './exportData';
|
|
||||||
import {deserializeObject} from './serialization';
|
|
||||||
import {deconstructPluginKey} from './clientUtils';
|
|
||||||
import {pluginsClassMap} from './pluginUtils';
|
|
||||||
import {PluginDefinition, isSandyPlugin} from '../plugin';
|
|
||||||
|
|
||||||
export type MetricType = {[metricName: string]: number};
|
|
||||||
type MetricPluginType = {[pluginID: string]: MetricType};
|
|
||||||
export type ExportMetricType = {[clientID: string]: MetricPluginType};
|
|
||||||
|
|
||||||
async function exportMetrics(
|
|
||||||
pluginStates: PluginStatesState,
|
|
||||||
pluginsMap: Map<string, PluginDefinition>,
|
|
||||||
selectedPlugins: Array<string>,
|
|
||||||
): Promise<string> {
|
|
||||||
const metrics: ExportMetricType = {};
|
|
||||||
for (const key in pluginStates) {
|
|
||||||
const pluginStateData = pluginStates[key];
|
|
||||||
|
|
||||||
const plugin = deconstructPluginKey(key);
|
|
||||||
const pluginName = plugin.pluginName;
|
|
||||||
if (
|
|
||||||
pluginName === undefined ||
|
|
||||||
(selectedPlugins.length > 0 && !selectedPlugins.includes(pluginName))
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const client = plugin.client;
|
|
||||||
const pluginClass = pluginsMap.get(pluginName);
|
|
||||||
const metricsReducer:
|
|
||||||
| (<U>(persistedState: U) => Promise<MetricType>)
|
|
||||||
| undefined =
|
|
||||||
pluginClass && !isSandyPlugin(pluginClass) // This feature doesn't seem to be used at all, so let's add it when needed for Sandy
|
|
||||||
? pluginClass.metricsReducer
|
|
||||||
: undefined;
|
|
||||||
if (pluginsMap.has(pluginName) && metricsReducer) {
|
|
||||||
const metricsObject = await metricsReducer(pluginStateData);
|
|
||||||
const pluginObject: MetricPluginType = {};
|
|
||||||
pluginObject[pluginName] = metricsObject;
|
|
||||||
if (!metrics[client]) {
|
|
||||||
metrics[client] = pluginObject;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const mergedMetrics = {...metrics[client], ...pluginObject};
|
|
||||||
metrics[client] = mergedMetrics;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.resolve(await serialize(metrics));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function exportMetricsWithoutTrace(
|
|
||||||
store: Store,
|
|
||||||
pluginStates: PluginStatesState,
|
|
||||||
): Promise<string | null> {
|
|
||||||
const pluginsMap = pluginsClassMap(store.getState().plugins);
|
|
||||||
const {clients, selectedDevice} = store.getState().connections;
|
|
||||||
const pluginsToProcess = determinePluginsToProcess(
|
|
||||||
clients,
|
|
||||||
selectedDevice,
|
|
||||||
store.getState().plugins,
|
|
||||||
);
|
|
||||||
const metadata = await fetchMetadata(
|
|
||||||
pluginsToProcess,
|
|
||||||
pluginStates,
|
|
||||||
store.getState(),
|
|
||||||
);
|
|
||||||
const newPluginStates = metadata.pluginStates;
|
|
||||||
const {errors} = metadata;
|
|
||||||
if (errors) {
|
|
||||||
console.error(errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
const metrics = await exportMetrics(
|
|
||||||
newPluginStates,
|
|
||||||
pluginsMap,
|
|
||||||
store.getState().plugins.selectedPlugins,
|
|
||||||
);
|
|
||||||
return metrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseJSON(str: string): any {
|
|
||||||
try {
|
|
||||||
return JSON.parse(str);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function exportMetricsFromTrace(
|
|
||||||
trace: string,
|
|
||||||
pluginsMap: Map<string, PluginDefinition>,
|
|
||||||
selectedPlugins: Array<string>,
|
|
||||||
): Promise<string> {
|
|
||||||
const data = fs.readFileSync(trace, 'utf8');
|
|
||||||
const parsedJSONData = parseJSON(data);
|
|
||||||
if (!parsedJSONData) {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error('Please pass the file which has a valid JSON'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const importedData: ExportType = deserializeObject(parsedJSONData);
|
|
||||||
const importedStore = importedData.store;
|
|
||||||
if (!importedStore) {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error(
|
|
||||||
'No store in the imported file, thus exiting without exporting metrics.',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const {pluginStates} = importedStore;
|
|
||||||
|
|
||||||
if (!pluginStates) {
|
|
||||||
return Promise.reject(
|
|
||||||
new Error(
|
|
||||||
'No pluginStates in the imported file, thus exiting without exporting metrics.',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return await exportMetrics(pluginStates, pluginsMap, selectedPlugins);
|
|
||||||
}
|
|
||||||
@@ -17,10 +17,6 @@ import dispatcher from '../app/src/dispatcher/index';
|
|||||||
import reducers, {Actions, State} from '../app/src/reducers/index';
|
import reducers, {Actions, State} from '../app/src/reducers/index';
|
||||||
import {init as initLogger} from '../app/src/fb-stubs/Logger';
|
import {init as initLogger} from '../app/src/fb-stubs/Logger';
|
||||||
import {exportStore} from '../app/src/utils/exportData';
|
import {exportStore} from '../app/src/utils/exportData';
|
||||||
import {
|
|
||||||
exportMetricsWithoutTrace,
|
|
||||||
exportMetricsFromTrace,
|
|
||||||
} from '../app/src/utils/exportMetrics';
|
|
||||||
import {listDevices} from '../app/src/utils/listDevices';
|
import {listDevices} from '../app/src/utils/listDevices';
|
||||||
import setup from '../static/setup';
|
import setup from '../static/setup';
|
||||||
import {
|
import {
|
||||||
@@ -42,7 +38,7 @@ type UserArguments = {
|
|||||||
dev: boolean;
|
dev: boolean;
|
||||||
exit: 'sigint' | 'disconnect';
|
exit: 'sigint' | 'disconnect';
|
||||||
verbose: boolean;
|
verbose: boolean;
|
||||||
metrics: string;
|
metrics: boolean;
|
||||||
listDevices: boolean;
|
listDevices: boolean;
|
||||||
device: string;
|
device: string;
|
||||||
listPlugins: boolean;
|
listPlugins: boolean;
|
||||||
@@ -116,13 +112,6 @@ type UserArguments = {
|
|||||||
.version(global.__VERSION__)
|
.version(global.__VERSION__)
|
||||||
.help().argv; // http://yargs.js.org/docs/#api-argv
|
.help().argv; // http://yargs.js.org/docs/#api-argv
|
||||||
|
|
||||||
function shouldExportMetric(metrics: string): boolean {
|
|
||||||
if (!metrics) {
|
|
||||||
return process.argv.includes('--metrics');
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function outputAndExit(output: string | null | undefined): void {
|
function outputAndExit(output: string | null | undefined): void {
|
||||||
output = output || '';
|
output = output || '';
|
||||||
console.log(`Finished. Outputting ${output.length} characters.`);
|
console.log(`Finished. Outputting ${output.length} characters.`);
|
||||||
@@ -161,7 +150,7 @@ async function exitActions(
|
|||||||
userArguments: UserArguments,
|
userArguments: UserArguments,
|
||||||
store: Store,
|
store: Store,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const {metrics, exit} = userArguments;
|
const {exit} = userArguments;
|
||||||
for (const exitAction of exitClosures) {
|
for (const exitAction of exitClosures) {
|
||||||
try {
|
try {
|
||||||
const action = await exitAction(userArguments, store);
|
const action = await exitAction(userArguments, store);
|
||||||
@@ -176,20 +165,11 @@ async function exitActions(
|
|||||||
if (exit == 'sigint') {
|
if (exit == 'sigint') {
|
||||||
process.on('SIGINT', async () => {
|
process.on('SIGINT', async () => {
|
||||||
try {
|
try {
|
||||||
if (shouldExportMetric(metrics) && !metrics) {
|
const {serializedString, fetchMetaDataErrors} = await exportStore(
|
||||||
const state = store.getState();
|
store,
|
||||||
const payload = await exportMetricsWithoutTrace(
|
);
|
||||||
store,
|
console.error('Error while fetching metadata', fetchMetaDataErrors);
|
||||||
state.pluginStates,
|
outputAndExit(serializedString);
|
||||||
);
|
|
||||||
outputAndExit(payload);
|
|
||||||
} else {
|
|
||||||
const {serializedString, fetchMetaDataErrors} = await exportStore(
|
|
||||||
store,
|
|
||||||
);
|
|
||||||
console.error('Error while fetching metadata', fetchMetaDataErrors);
|
|
||||||
outputAndExit(serializedString);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorAndExit(e);
|
errorAndExit(e);
|
||||||
}
|
}
|
||||||
@@ -217,7 +197,7 @@ async function storeModifyingActions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function startFlipper(userArguments: UserArguments) {
|
async function startFlipper(userArguments: UserArguments) {
|
||||||
const {verbose, metrics, exit, insecurePort, securePort} = userArguments;
|
const {verbose, exit, insecurePort, securePort, metrics} = userArguments;
|
||||||
console.error(`
|
console.error(`
|
||||||
_____ _ _
|
_____ _ _
|
||||||
| __| |_|___ ___ ___ ___
|
| __| |_|___ ___ ___ ___
|
||||||
@@ -225,6 +205,11 @@ async function startFlipper(userArguments: UserArguments) {
|
|||||||
|__| |_|_| _| _|___|_| v${global.__VERSION__}
|
|__| |_|_| _| _|___|_| v${global.__VERSION__}
|
||||||
|_| |_|
|
|_| |_|
|
||||||
`);
|
`);
|
||||||
|
if (metrics) {
|
||||||
|
throw new Error(
|
||||||
|
'--metrics is no longer supported, see D24332440 for details.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// redirect all logging to stderr
|
// redirect all logging to stderr
|
||||||
const overriddenMethods = ['debug', 'info', 'log', 'warn', 'error'];
|
const overriddenMethods = ['debug', 'info', 'log', 'warn', 'error'];
|
||||||
@@ -253,24 +238,13 @@ async function startFlipper(userArguments: UserArguments) {
|
|||||||
// TODO(T42325892): Investigate why the export stalls without exiting the
|
// TODO(T42325892): Investigate why the export stalls without exiting the
|
||||||
// current eventloop task here.
|
// current eventloop task here.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (shouldExportMetric(metrics) && !metrics) {
|
exportStore(store)
|
||||||
const state = store.getState();
|
.then(({serializedString}) => {
|
||||||
exportMetricsWithoutTrace(store as Store, state.pluginStates)
|
outputAndExit(serializedString);
|
||||||
.then((payload: string | null) => {
|
})
|
||||||
outputAndExit(payload || '');
|
.catch((e: Error) => {
|
||||||
})
|
errorAndExit(e);
|
||||||
.catch((e: Error) => {
|
});
|
||||||
errorAndExit(e);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
exportStore(store)
|
|
||||||
.then(({serializedString}) => {
|
|
||||||
outputAndExit(serializedString);
|
|
||||||
})
|
|
||||||
.catch((e: Error) => {
|
|
||||||
errorAndExit(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 10);
|
}, 10);
|
||||||
}
|
}
|
||||||
return next(action);
|
return next(action);
|
||||||
@@ -378,19 +352,6 @@ async function startFlipper(userArguments: UserArguments) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
async (userArguments: UserArguments, store: Store) => {
|
async (userArguments: UserArguments, store: Store) => {
|
||||||
const {metrics} = userArguments;
|
|
||||||
if (shouldExportMetric(metrics) && metrics && metrics.length > 0) {
|
|
||||||
try {
|
|
||||||
const payload = await exportMetricsFromTrace(
|
|
||||||
metrics,
|
|
||||||
pluginsClassMap(store.getState().plugins),
|
|
||||||
store.getState().plugins.selectedPlugins,
|
|
||||||
);
|
|
||||||
return {exit: true, result: payload ? payload.toString() : ''};
|
|
||||||
} catch (error) {
|
|
||||||
return {exit: true, result: error};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Promise.resolve({exit: false});
|
return Promise.resolve({exit: false});
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
import FrescoPlugin from '../index';
|
import FrescoPlugin from '../index';
|
||||||
import {PersistedState, ImageEventWithId} from '../index';
|
import {PersistedState, ImageEventWithId} from '../index';
|
||||||
import {AndroidCloseableReferenceLeakEvent} from '../api';
|
import {AndroidCloseableReferenceLeakEvent} from '../api';
|
||||||
import {MetricType, Notification} from 'flipper';
|
import {Notification} from 'flipper';
|
||||||
import {ImagesMap} from '../ImagePool';
|
import {ImagesMap} from '../ImagePool';
|
||||||
|
|
||||||
type ScanDisplayTime = {[scan_number: number]: number};
|
type ScanDisplayTime = {[scan_number: number]: number};
|
||||||
@@ -63,227 +63,6 @@ function mockPersistedState(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test('the metric reducer for the input having regression', () => {
|
|
||||||
const persistedState = mockPersistedState(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
expect(FrescoPlugin.metricsReducer).toBeDefined();
|
|
||||||
const metrics = FrescoPlugin.metricsReducer(persistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({
|
|
||||||
WASTED_BYTES: 37500,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the metric reducer for the input having no regression', () => {
|
|
||||||
const persistedState = mockPersistedState(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const metricsReducer = FrescoPlugin.metricsReducer;
|
|
||||||
expect(metricsReducer).toBeDefined();
|
|
||||||
const metrics = metricsReducer(persistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({
|
|
||||||
WASTED_BYTES: 0,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the metric reducer for the default persisted state', () => {
|
|
||||||
const metricsReducer = FrescoPlugin.metricsReducer;
|
|
||||||
expect(metricsReducer).toBeDefined();
|
|
||||||
const metrics = metricsReducer(FrescoPlugin.defaultPersistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 0});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the metric reducer with the events data but with no imageData in imagesMap ', () => {
|
|
||||||
const persistedState = mockPersistedState(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
persistedState.imagesMap = {};
|
|
||||||
const metricsReducer = FrescoPlugin.metricsReducer;
|
|
||||||
expect(metricsReducer).toBeDefined();
|
|
||||||
const metrics = metricsReducer(persistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 0});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('the metric reducer with the no viewPort data in events', () => {
|
|
||||||
const persistedState = mockPersistedState(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 50,
|
|
||||||
height: 50,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
delete persistedState.events[0].viewport;
|
|
||||||
const metricsReducer = FrescoPlugin.metricsReducer;
|
|
||||||
expect(metricsReducer).toBeDefined();
|
|
||||||
const metrics = metricsReducer(persistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 0});
|
|
||||||
});
|
|
||||||
test('the metric reducer with the multiple events', () => {
|
|
||||||
const scanDisplayTime: ScanDisplayTime = {};
|
|
||||||
scanDisplayTime[1] = 3;
|
|
||||||
const events: Array<ImageEventWithId> = [
|
|
||||||
{
|
|
||||||
imageIds: ['0', '1'],
|
|
||||||
eventId: 0,
|
|
||||||
attribution: [],
|
|
||||||
startTime: 1,
|
|
||||||
endTime: 2,
|
|
||||||
source: 'source',
|
|
||||||
coldStart: true,
|
|
||||||
viewport: {width: 100, height: 100, scanDisplayTime},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
imageIds: ['2', '3'],
|
|
||||||
eventId: 1,
|
|
||||||
attribution: [],
|
|
||||||
startTime: 1,
|
|
||||||
endTime: 2,
|
|
||||||
source: 'source',
|
|
||||||
coldStart: true,
|
|
||||||
viewport: {width: 50, height: 50, scanDisplayTime},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const imageSizes = [
|
|
||||||
{
|
|
||||||
width: 150,
|
|
||||||
height: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 100,
|
|
||||||
height: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 250,
|
|
||||||
height: 250,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
width: 300,
|
|
||||||
height: 300,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const imagesMap = imageSizes.reduce((acc, val, index) => {
|
|
||||||
acc[index] = {
|
|
||||||
imageId: String(index),
|
|
||||||
width: val.width,
|
|
||||||
height: val.height,
|
|
||||||
sizeBytes: 10,
|
|
||||||
data: 'undefined',
|
|
||||||
};
|
|
||||||
return acc;
|
|
||||||
}, {} as ImagesMap);
|
|
||||||
const persistedState = {
|
|
||||||
surfaceList: new Set<string>(),
|
|
||||||
images: [],
|
|
||||||
nextEventId: 0,
|
|
||||||
events,
|
|
||||||
imagesMap,
|
|
||||||
closeableReferenceLeaks: [],
|
|
||||||
isLeakTrackingEnabled: true,
|
|
||||||
showDiskImages: false,
|
|
||||||
};
|
|
||||||
const metricsReducer = FrescoPlugin.metricsReducer;
|
|
||||||
expect(metricsReducer).toBeDefined();
|
|
||||||
const metrics = metricsReducer(persistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({WASTED_BYTES: 160000});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('closeable reference metrics on empty state', () => {
|
|
||||||
const metricsReducer: (
|
|
||||||
persistedState: PersistedState,
|
|
||||||
) => Promise<MetricType> = FrescoPlugin.metricsReducer;
|
|
||||||
const persistedState = mockPersistedState();
|
|
||||||
const metrics = metricsReducer(persistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({CLOSEABLE_REFERENCE_LEAKS: 0});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('closeable reference metrics on input', () => {
|
|
||||||
const metricsReducer: (
|
|
||||||
persistedState: PersistedState,
|
|
||||||
) => Promise<MetricType> = FrescoPlugin.metricsReducer;
|
|
||||||
const closeableReferenceLeaks: Array<AndroidCloseableReferenceLeakEvent> = [
|
|
||||||
{
|
|
||||||
identityHashCode: 'deadbeef',
|
|
||||||
className: 'com.facebook.imagepipeline.memory.NativeMemoryChunk',
|
|
||||||
stacktrace: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
identityHashCode: 'f4c3b00c',
|
|
||||||
className: 'com.facebook.flipper.SomeMemoryAbstraction',
|
|
||||||
stacktrace: null,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const persistedState = {
|
|
||||||
...mockPersistedState(),
|
|
||||||
closeableReferenceLeaks,
|
|
||||||
};
|
|
||||||
const metrics = metricsReducer(persistedState);
|
|
||||||
return expect(metrics).resolves.toMatchObject({CLOSEABLE_REFERENCE_LEAKS: 2});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('notifications for leaks', () => {
|
test('notifications for leaks', () => {
|
||||||
const notificationReducer: (
|
const notificationReducer: (
|
||||||
persistedState: PersistedState,
|
persistedState: PersistedState,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
} from './api';
|
} from './api';
|
||||||
import {Fragment} from 'react';
|
import {Fragment} from 'react';
|
||||||
import {ImagesMap} from './ImagePool';
|
import {ImagesMap} from './ImagePool';
|
||||||
import {MetricType, ReduxState} from 'flipper';
|
import {ReduxState} from 'flipper';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ImagesCacheOverview from './ImagesCacheOverview';
|
import ImagesCacheOverview from './ImagesCacheOverview';
|
||||||
import {
|
import {
|
||||||
@@ -207,37 +207,6 @@ export default class FlipperImagesPlugin extends FlipperPlugin<
|
|||||||
return persistedState;
|
return persistedState;
|
||||||
};
|
};
|
||||||
|
|
||||||
static metricsReducer = (
|
|
||||||
persistedState: PersistedState,
|
|
||||||
): Promise<MetricType> => {
|
|
||||||
const {events, imagesMap, closeableReferenceLeaks} = persistedState;
|
|
||||||
|
|
||||||
const wastedBytes = (events || []).reduce((acc, event) => {
|
|
||||||
const {viewport, imageIds} = event;
|
|
||||||
if (!viewport) {
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
return imageIds.reduce((innerAcc, imageID) => {
|
|
||||||
const imageData: ImageData = imagesMap[imageID];
|
|
||||||
if (!imageData) {
|
|
||||||
return innerAcc;
|
|
||||||
}
|
|
||||||
const imageWidth: number = imageData.width;
|
|
||||||
const imageHeight: number = imageData.height;
|
|
||||||
const viewPortWidth: number = viewport.width;
|
|
||||||
const viewPortHeight: number = viewport.height;
|
|
||||||
const viewPortArea = viewPortWidth * viewPortHeight;
|
|
||||||
const imageArea = imageWidth * imageHeight;
|
|
||||||
return innerAcc + Math.max(0, imageArea - viewPortArea);
|
|
||||||
}, acc);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
return Promise.resolve({
|
|
||||||
WASTED_BYTES: wastedBytes,
|
|
||||||
CLOSEABLE_REFERENCE_LEAKS: (closeableReferenceLeaks || []).length,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
static getActiveNotifications = ({
|
static getActiveNotifications = ({
|
||||||
closeableReferenceLeaks = [],
|
closeableReferenceLeaks = [],
|
||||||
isLeakTrackingEnabled = false,
|
isLeakTrackingEnabled = false,
|
||||||
|
|||||||
Reference in New Issue
Block a user