Added test cases for flipper import and export

Summary: Adds tests for flipper import and export. Also fixed few edge cases which was discovered through tests. The edge case was that it didn't export device plugin states.

Reviewed By: danielbuechele

Differential Revision: D13828357

fbshipit-source-id: ddc4352b729ca7f05c5875f2f3fbdd2c7f4e2186
This commit is contained in:
Pritesh Nandgaonkar
2019-01-29 08:49:41 -08:00
committed by Facebook Github Bot
parent 5ef970e5b5
commit cbd70f573c
4 changed files with 475 additions and 46 deletions

View File

@@ -4,6 +4,15 @@
* LICENSE file in the root directory of this source tree.
* @format
*/
import type {Store} from '../reducers';
import type {DeviceExport} from '../devices/BaseDevice';
import type {State as PluginStates} from '../reducers/pluginStates';
import type {PluginNotification} from '../reducers/notifications.js';
import type {ClientExport} from '../Client.js';
import type {State as PluginStatesState} from '../reducers/pluginStates';
import {FlipperDevicePlugin} from '../plugin.js';
import {default as BaseDevice} from '../devices/BaseDevice';
import fs from 'fs';
import os from 'os';
import path from 'path';
@@ -14,54 +23,117 @@ const exportFilePath = path.join(
'FlipperExport.json',
);
export const exportStoreToFile = (data: Store): Promise<void> => {
const state = data.getState();
const json = {
fileVersion: '1.0.0',
device: {},
clients: [],
store: {
pluginStates: {},
activeNotifications: [],
},
};
export type ExportType = {|
fileVersion: '1.0.0',
clients: Array<ClientExport>,
device: ?DeviceExport,
store: {
pluginStates: PluginStates,
activeNotifications: Array<PluginNotification>,
},
|};
const device = state.connections.selectedDevice;
export function processClients(
clients: Array<ClientExport>,
serial: string,
): Array<ClientExport> {
return clients.filter(client => client.query.device_id === serial);
}
export function processPluginStates(
clients: Array<ClientExport>,
serial: string,
allPluginStates: PluginStatesState,
devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
): PluginStatesState {
let pluginStates = {};
for (let key in allPluginStates) {
let keyArray = key.split('#');
const pluginName = keyArray.pop();
const filteredClients = clients.filter(client => {
// Remove the last entry related to plugin
return client.id.includes(keyArray.join('#'));
});
if (
filteredClients.length > 0 ||
(devicePlugins.has(pluginName) && serial === keyArray[0])
) {
// There need not be any client for device Plugins
pluginStates = {...pluginStates, [key]: allPluginStates[key]};
}
}
return pluginStates;
}
export function processNotificationStates(
clients: Array<ClientExport>,
serial: string,
allActiveNotifications: Array<PluginNotification>,
devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
): Array<PluginNotification> {
let activeNotifications = allActiveNotifications.filter(notif => {
const filteredClients = clients.filter(
client => (notif.client ? client.id.includes(notif.client) : false),
);
return (
filteredClients.length > 0 ||
(devicePlugins.has(notif.pluginId) && serial === notif.client)
); // There need not be any client for device Plugins
});
return activeNotifications;
}
export const processStore = (
activeNotifications: Array<PluginNotification>,
device: ?BaseDevice,
pluginStates: PluginStatesState,
clients: Array<ClientExport>,
devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
): ?ExportType => {
if (device) {
const {serial} = device;
json.device = device.toJSON();
const clients = state.connections.clients
.filter(client => {
return client.query.device_id === serial;
})
.map(client => {
return client.toJSON();
});
const processedClients = processClients(clients, serial);
let processedPluginStates = processPluginStates(
processedClients,
serial,
pluginStates,
devicePlugins,
);
const processedActiveNotifications = processNotificationStates(
processedClients,
serial,
activeNotifications,
devicePlugins,
);
return {
fileVersion: '1.0.0',
clients: processedClients,
device: device.toJSON(),
store: {
pluginStates: processedPluginStates,
activeNotifications: processedActiveNotifications,
},
};
}
return null;
};
json.clients = clients;
const allPluginStates = state.pluginStates;
let pluginStates = {};
for (let key in allPluginStates) {
const filteredClients = clients.filter(client => {
let keyArray = key.split('#');
keyArray.pop(); // Remove the last entry related to plugin
return client.id.includes(keyArray.join('#'));
});
if (filteredClients.length > 0) {
pluginStates = {...pluginStates, [key]: allPluginStates[key]};
json.store.pluginStates = pluginStates;
}
}
const allActiveNotifications = state.notifications.activeNotifications;
let activeNotifications = allActiveNotifications.filter(notif => {
const filteredClients = clients.filter(client =>
client.id.includes(notif.client),
);
return filteredClients.length > 0;
});
json.store.activeNotifications = activeNotifications;
export const exportStoreToFile = (data: Store): Promise<void> => {
const state = data.getState();
const {activeNotifications} = state.notifications;
const {selectedDevice, clients} = state.connections;
const {pluginStates} = state;
const {devicePlugins} = state.plugins;
// TODO: T39612653 Make Client mockable. Currently rsocket logic is tightly coupled.
// Not passing the entire state as currently Client is not mockable.
const json = processStore(
activeNotifications,
selectedDevice,
pluginStates,
clients.map(client => client.toJSON()),
devicePlugins,
);
if (json) {
return new Promise((resolve, reject) => {
fs.writeFile(exportFilePath, JSON.stringify(json), err => {
if (err) {