From 32bde8cace09345e339dcfa4f1d5367315d52f0b Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Mon, 1 Feb 2021 11:40:20 -0800 Subject: [PATCH] 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 --- .../createMockFlipperWithPlugin.node.tsx.snap | 2 + desktop/app/src/devices/BaseDevice.tsx | 22 ++++- .../src/utils/__tests__/exportData.node.tsx | 95 +++++++++++++++++++ desktop/app/src/utils/exportData.tsx | 5 +- 4 files changed, 119 insertions(+), 5 deletions(-) diff --git a/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap b/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap index 8c290e6d2..98840bc61 100644 --- a/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap +++ b/desktop/app/src/__tests__/__snapshots__/createMockFlipperWithPlugin.node.tsx.snap @@ -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", }, diff --git a/desktop/app/src/devices/BaseDevice.tsx b/desktop/app/src/devices/BaseDevice.tsx index 6ef409d17..e76bee6b8 100644 --- a/desktop/app/src/devices/BaseDevice.tsx +++ b/desktop/app/src/devices/BaseDevice.tsx @@ -33,6 +33,7 @@ export type DeviceExport = { deviceType: DeviceType; serial: string; logs: Array; + pluginStates: Record; }; export default class BaseDevice { @@ -91,12 +92,21 @@ export default class BaseDevice { } toJSON(): DeviceExport { + const pluginStates: Record = {}; + + 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, + ) { 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)) { diff --git a/desktop/app/src/utils/__tests__/exportData.node.tsx b/desktop/app/src/utils/__tests__/exportData.node.tsx index f829cd108..c1199e0ca 100644 --- a/desktop/app/src/utils/__tests__/exportData.node.tsx +++ b/desktop/app/src/utils/__tests__/exportData.node.tsx @@ -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", + } + `); +}); diff --git a/desktop/app/src/utils/exportData.tsx b/desktop/app/src/utils/exportData.tsx index 95f592e45..a35b34c2e 100644 --- a/desktop/app/src/utils/exportData.tsx +++ b/desktop/app/src/utils/exportData.tsx @@ -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,