add support for device plugin import / export

Summary: Sandy device plugins weren't exported till now (the only stateful plugin so far was Logs, but logs were stored hardcoded on the device rather than using the plugin export mechanisms). This diff makes sure that SandyDevicePlugins will be exported as well if they are persistable.

Reviewed By: nikoant

Differential Revision: D22724822

fbshipit-source-id: a10354a9c7e02f3e696d0cdda0f2c6be6f5ac61e
This commit is contained in:
Michel Weststrate
2021-02-01 11:40:20 -08:00
committed by Facebook GitHub Bot
parent e1daa449ba
commit 32bde8cace
4 changed files with 119 additions and 5 deletions

View File

@@ -21,6 +21,7 @@ Object {
"deviceType": "physical",
"logs": Array [],
"os": "Android",
"pluginStates": Object {},
"serial": "serial",
"title": "MockAndroidDevice",
},
@@ -30,6 +31,7 @@ Object {
"deviceType": "physical",
"logs": Array [],
"os": "Android",
"pluginStates": Object {},
"serial": "serial",
"title": "MockAndroidDevice",
},

View File

@@ -33,6 +33,7 @@ export type DeviceExport = {
deviceType: DeviceType;
serial: string;
logs: Array<DeviceLogEntry>;
pluginStates: Record<string, any>;
};
export default class BaseDevice {
@@ -91,12 +92,21 @@ export default class BaseDevice {
}
toJSON(): DeviceExport {
const pluginStates: Record<string, any> = {};
for (const instance of this.sandyPluginStates.values()) {
if (instance.isPersistable()) {
pluginStates[instance.definition.id] = instance.exportState();
}
}
return {
os: this.os,
title: this.title,
deviceType: this.deviceType,
serial: this.serial,
logs: this.getLogs(),
pluginStates,
};
}
@@ -175,17 +185,20 @@ export default class BaseDevice {
return null;
}
loadDevicePlugins(devicePlugins?: DevicePluginMap) {
loadDevicePlugins(
devicePlugins?: DevicePluginMap,
pluginStates?: Record<string, any>,
) {
if (!devicePlugins) {
return;
}
const plugins = Array.from(devicePlugins.values());
for (const plugin of plugins) {
this.loadDevicePlugin(plugin);
this.loadDevicePlugin(plugin, pluginStates?.[plugin.id]);
}
}
loadDevicePlugin(plugin: DevicePluginDefinition) {
loadDevicePlugin(plugin: DevicePluginDefinition, initialState?: any) {
if (plugin instanceof _SandyPluginDefinition) {
if (plugin.asDevicePluginModule().supportsDevice(this as any)) {
this.devicePlugins.push(plugin.id);
@@ -195,8 +208,9 @@ export default class BaseDevice {
getFlipperLibImplementation(),
plugin,
this,
initialState,
),
); // TODO T70582933: pass initial state if applicable
);
}
} else {
if (plugin.supportsDevice(this)) {

View File

@@ -27,6 +27,7 @@ import {
_SandyPluginDefinition,
createState,
PluginClient,
DevicePluginClient,
} from 'flipper-plugin';
import {selectPlugin} from '../../reducers/connections';
@@ -1208,3 +1209,97 @@ test('Sandy plugins are imported properly', async () => {
}
`);
});
const sandyDeviceTestPlugin = new _SandyPluginDefinition(
TestUtils.createMockPluginDetails(),
{
supportsDevice: () => true,
devicePlugin(client: DevicePluginClient) {
const counter = createState(0, {persist: 'counter'});
const _somethingElse = createState(0);
const anotherState = createState({testCount: 0}, {persist: 'otherState'});
client.device.onLogEntry(() => {
counter.set(counter.get() + 1);
anotherState.update((draft) => {
draft.testCount -= 1;
});
});
return {
counter,
};
},
Component() {
return null;
},
},
);
test('Sandy device plugins are exported / imported properly', async () => {
const data = {
clients: [],
device: {
deviceType: 'archivedPhysical',
logs: [],
os: 'Android',
serial: '2e52cea6-94b0-4ea1-b9a8-c9135ede14ca-serial',
title: 'MockAndroidDevice',
pluginStates: {
[sandyDeviceTestPlugin.id]: {
otherState: {
testCount: -3,
},
counter: 3,
},
},
},
deviceScreenshot: null,
fileVersion: '0.9.99',
flipperReleaseRevision: undefined,
pluginStates2: {},
store: {
activeNotifications: [],
pluginStates: {},
},
};
const {device, store} = await renderMockFlipperWithPlugin(
sandyDeviceTestPlugin,
);
await importDataToStore('unittest.json', JSON.stringify(data), store);
const device2 = store.getState().connections.devices[1];
expect(device2).not.toBeFalsy();
expect(device2).not.toBe(device);
expect(device2.devicePlugins).toEqual([TestPlugin.id]);
const {counter} = device2.sandyPluginStates.get(TestPlugin.id)?.instanceApi;
counter.set(counter.get() + 1);
expect(device.toJSON().pluginStates[TestPlugin.id]).toMatchInlineSnapshot(`
Object {
"counter": 0,
"otherState": Object {
"testCount": 0,
},
}
`);
expect(device2.toJSON()).toMatchInlineSnapshot(`
Object {
"deviceType": "archivedPhysical",
"logs": Array [],
"os": "Android",
"pluginStates": Object {
"TestPlugin": Object {
"counter": 4,
"otherState": Object {
"testCount": -3,
},
},
},
"serial": "2e52cea6-94b0-4ea1-b9a8-c9135ede14ca-serial",
"title": "MockAndroidDevice",
}
`);
});

View File

@@ -791,7 +791,10 @@ export function importDataToStore(source: string, data: string, store: Store) {
});
return;
}
archivedDevice.loadDevicePlugins(store.getState().plugins.devicePlugins);
archivedDevice.loadDevicePlugins(
store.getState().plugins.devicePlugins,
device.pluginStates,
);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: archivedDevice,