Fix issue where messages where not queued for non-selected apps

Summary: When determing whether to queue a message, the logic checked if the plugin is enabled on the currently selected app, rather than checking if it is enabled for the receiving app. This diff fixes that.

Reviewed By: passy

Differential Revision: D20000055

fbshipit-source-id: 665f0a650dcee8f7f46aa56f399a4f7d0d0aa1e0
This commit is contained in:
Michel Weststrate
2020-02-21 04:44:57 -08:00
committed by Facebook Github Bot
parent adce24d343
commit 4aec81b059
7 changed files with 167 additions and 70 deletions

View File

@@ -417,7 +417,8 @@ export default connect<StateFromProps, DispatchFromProps, OwnProps, Store>(
if (activePlugin && target) { if (activePlugin && target) {
pluginKey = getPluginKey(target.id, activePlugin.id); pluginKey = getPluginKey(target.id, activePlugin.id);
pluginIsEnabled = pluginIsStarred( pluginIsEnabled = pluginIsStarred(
{selectedApp, userStarredPlugins}, userStarredPlugins,
selectedApp,
activePlugin.id, activePlugin.id,
); );
} }

View File

@@ -5,10 +5,10 @@ Object {
"androidEmulators": Array [], "androidEmulators": Array [],
"clients": Array [ "clients": Array [
Object { Object {
"id": "TestApp#Android#unit_test#serial", "id": "TestApp#Android#MockAndroidDevice#serial",
"query": Object { "query": Object {
"app": "TestApp", "app": "TestApp",
"device": "unit_test", "device": "MockAndroidDevice",
"device_id": "serial", "device_id": "serial",
"os": "Android", "os": "Android",
}, },
@@ -25,7 +25,7 @@ Object {
}, },
], ],
"errors": Array [], "errors": Array [],
"selectedApp": "TestApp#Android#unit_test#serial", "selectedApp": "TestApp#Android#MockAndroidDevice#serial",
"selectedDevice": Object { "selectedDevice": Object {
"deviceType": "physical", "deviceType": "physical",
"logs": Array [], "logs": Array [],
@@ -36,7 +36,7 @@ Object {
"selectedPlugin": "TestPlugin", "selectedPlugin": "TestPlugin",
"staticView": null, "staticView": null,
"uninitializedClients": Array [], "uninitializedClients": Array [],
"userPreferredApp": "TestApp#Android#unit_test#serial", "userPreferredApp": "TestApp#Android#MockAndroidDevice#serial",
"userPreferredDevice": "MockAndroidDevice", "userPreferredDevice": "MockAndroidDevice",
"userPreferredPlugin": "TestPlugin", "userPreferredPlugin": "TestPlugin",
"userStarredPlugins": Object { "userStarredPlugins": Object {

View File

@@ -53,7 +53,7 @@ test('can create a Fake flipper', async () => {
sendMessage('inc', {}); sendMessage('inc', {});
expect(store.getState().pluginStates).toMatchInlineSnapshot(` expect(store.getState().pluginStates).toMatchInlineSnapshot(`
Object { Object {
"TestApp#Android#unit_test#serial#TestPlugin": Object { "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
"count": 1, "count": 1,
}, },
} }

View File

@@ -621,17 +621,14 @@ export function getSelectedPluginKey(state: State): string | undefined {
} }
export function pluginIsStarred( export function pluginIsStarred(
state: { userStarredPlugins: State['userStarredPlugins'],
selectedApp: string | null; app: string | null,
userStarredPlugins: State['userStarredPlugins'];
},
pluginId: string, pluginId: string,
): boolean { ): boolean {
const {selectedApp} = state; if (!app) {
if (!selectedApp) {
return false; return false;
} }
const appInfo = deconstructClientId(selectedApp); const appInfo = deconstructClientId(app);
const starred = state.userStarredPlugins[appInfo.app]; const starred = userStarredPlugins[appInfo.app];
return starred && starred.indexOf(pluginId) > -1; return starred && starred.indexOf(pluginId) > -1;
} }

View File

@@ -40,63 +40,72 @@ export async function createMockFlipperWithPlugin(
device: BaseDevice; device: BaseDevice;
store: Store; store: Store;
sendMessage(method: string, params: any): void; sendMessage(method: string, params: any): void;
createDevice(serial: string): BaseDevice;
createClient(device: BaseDevice, name: string): Client;
}) => Promise<void>, }) => Promise<void>,
) { ) {
const store = createStore(reducers); const store = createStore(reducers);
const device = new BaseDevice(
'serial',
'physical',
'MockAndroidDevice',
'Android',
);
const logger = createStubLogger(); const logger = createStubLogger();
store.dispatch(registerPlugins([pluginClazz])); store.dispatch(registerPlugins([pluginClazz]));
store.dispatch({ function createDevice(serial: string): BaseDevice {
type: 'REGISTER_DEVICE', const device = new BaseDevice(
payload: device, serial,
}); 'physical',
'MockAndroidDevice',
'Android',
);
store.dispatch({
type: 'REGISTER_DEVICE',
payload: device,
});
return device;
}
const query: ClientQuery = { function createClient(device: BaseDevice, name: string): Client {
app: 'TestApp', const query: ClientQuery = {
os: 'Android', app: name,
device: 'unit_test', os: 'Android',
device_id: device.serial, device: device.title,
}; device_id: device.serial,
const id = buildClientId({ };
app: query.app, const id = buildClientId({
os: query.os, app: query.app,
device: query.device, os: query.os,
device_id: query.device_id, device: query.device,
}); device_id: query.device_id,
});
const client = new Client( const client = new Client(
id, id,
query, query,
null, // create a stub connection to avoid this plugin to be archived? null, // create a stub connection to avoid this plugin to be archived?
logger, logger,
store, store,
[pluginClazz.id], [pluginClazz.id],
device, device,
); );
// yikes // yikes
client._deviceSet = device; client._deviceSet = device;
client.device = { client.device = {
then() { then() {
return device; return device;
}, },
} as any; } as any;
// As convenience, by default we select the new client, star the plugin, and select it // As convenience, by default we select the new client, star the plugin, and select it
store.dispatch({ store.dispatch({
type: 'NEW_CLIENT', type: 'NEW_CLIENT',
payload: client, payload: client,
}); });
return client;
}
const device = createDevice('serial');
const client = createClient(device, 'TestApp');
store.dispatch(selectDevice(device)); store.dispatch(selectDevice(device));
store.dispatch(selectClient(client.id)); store.dispatch(selectClient(client.id));
store.dispatch( store.dispatch(
@@ -131,5 +140,7 @@ export async function createMockFlipperWithPlugin(
}), }),
); );
}, },
createDevice,
createClient,
}); });
} }

View File

@@ -9,8 +9,13 @@
import {FlipperPlugin} from '../../plugin'; import {FlipperPlugin} from '../../plugin';
import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin'; import {createMockFlipperWithPlugin} from '../../test-utils/createMockFlipperWithPlugin';
import {GK, Store, Client} from 'flipper'; import {GK, Store, Client} from '../../';
import {selectPlugin, starPlugin} from '../../reducers/connections'; import {
selectPlugin,
starPlugin,
selectClient,
selectDevice,
} from '../../reducers/connections';
import {processMessageQueue} from '../messageQueue'; import {processMessageQueue} from '../messageQueue';
import {getPluginKey} from '../pluginUtils'; import {getPluginKey} from '../pluginUtils';
import {TestIdler} from '../Idler'; import {TestIdler} from '../Idler';
@@ -87,7 +92,7 @@ test('will process event with GK disabled', async () => {
sendMessage('inc', {}); sendMessage('inc', {});
expect(store.getState().pluginStates).toMatchInlineSnapshot(` expect(store.getState().pluginStates).toMatchInlineSnapshot(`
Object { Object {
"TestApp#Android#unit_test#serial#TestPlugin": Object { "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
"count": 1, "count": 1,
}, },
} }
@@ -104,12 +109,12 @@ test('queue - events are processed immediately if plugin is selected', async ()
expect(store.getState().connections.selectedPlugin).toBe('TestPlugin'); expect(store.getState().connections.selectedPlugin).toBe('TestPlugin');
sendMessage('inc', {}); sendMessage('inc', {});
expect(store.getState().pluginStates).toMatchInlineSnapshot(` expect(store.getState().pluginStates).toMatchInlineSnapshot(`
Object { Object {
"TestApp#Android#unit_test#serial#TestPlugin": Object { "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Object {
"count": 1, "count": 1,
}, },
} }
`); `);
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot( expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(
`Object {}`, `Object {}`,
); );
@@ -135,7 +140,7 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu
); );
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(` expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(`
Object { Object {
"TestApp#Android#unit_test#serial#TestPlugin": Array [ "TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
Object { Object {
"method": "inc", "method": "inc",
"params": Object {}, "params": Object {},
@@ -193,6 +198,84 @@ test('queue - events are NOT processed immediately if plugin is NOT selected (bu
); );
}); });
test('queue - events are queued for plugins that are favorite when app is not selected', async () => {
await createMockFlipperWithPlugin(
TestPlugin,
async ({device, store, sendMessage, createClient}) => {
await GK.withWhitelistedGK('flipper_event_queue', async () => {
selectDeviceLogs(store);
expect(store.getState().connections.selectedPlugin).not.toBe(
'TestPlugin',
);
const client2 = createClient(device, 'TestApp2');
store.dispatch(selectClient(client2.id));
// Now we send a message to the second client, it should arrive,
// as the plugin was enabled already on the first client as well
sendMessage('inc', {delta: 2});
expect(store.getState().pluginStates).toMatchInlineSnapshot(
`Object {}`,
);
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(
`
Object {
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
Object {
"method": "inc",
"params": Object {
"delta": 2,
},
},
],
}
`,
);
});
},
);
});
test('queue - events are queued for plugins that are favorite when app is selected on different device', async () => {
await createMockFlipperWithPlugin(
TestPlugin,
async ({client, store, sendMessage, createDevice, createClient}) => {
await GK.withWhitelistedGK('flipper_event_queue', async () => {
selectDeviceLogs(store);
expect(store.getState().connections.selectedPlugin).not.toBe(
'TestPlugin',
);
const device2 = createDevice('serial2');
const client2 = createClient(device2, client.query.app); // same app id
store.dispatch(selectDevice(device2));
store.dispatch(selectClient(client2.id));
// Now we send a message to the second client, it should arrive,
// as the plugin was enabled already on the first client as well
sendMessage('inc', {delta: 2});
expect(store.getState().pluginStates).toMatchInlineSnapshot(
`Object {}`,
);
expect(store.getState().pluginMessageQueue).toMatchInlineSnapshot(
`
Object {
"TestApp#Android#MockAndroidDevice#serial#TestPlugin": Array [
Object {
"method": "inc",
"params": Object {
"delta": 2,
},
},
],
}
`,
);
});
},
);
});
test('queue - events processing will be paused', async () => { test('queue - events processing will be paused', async () => {
await createMockFlipperWithPlugin( await createMockFlipperWithPlugin(
TestPlugin, TestPlugin,

View File

@@ -18,6 +18,7 @@ import {
} from '../reducers/pluginMessageQueue'; } from '../reducers/pluginMessageQueue';
import {Idler, BaseIdler} from './Idler'; import {Idler, BaseIdler} from './Idler';
import {pluginIsStarred, getSelectedPluginKey} from '../reducers/connections'; import {pluginIsStarred, getSelectedPluginKey} from '../reducers/connections';
import {deconstructPluginKey} from './clientUtils';
const MAX_BACKGROUND_TASK_TIME = 25; const MAX_BACKGROUND_TASK_TIME = 25;
@@ -180,7 +181,11 @@ export function processMessageLater(
break; break;
case isSelected: case isSelected:
case plugin instanceof FlipperDevicePlugin: case plugin instanceof FlipperDevicePlugin:
case pluginIsStarred(store.getState().connections, plugin.id): case pluginIsStarred(
store.getState().connections.userStarredPlugins,
deconstructPluginKey(pluginKey).client,
plugin.id,
):
store.dispatch( store.dispatch(
queueMessage( queueMessage(
pluginKey, pluginKey,