From 61f848819fa4bd98bb90808f075a83d5442ef8b9 Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Thu, 26 Oct 2023 13:57:21 +0200 Subject: [PATCH 1/4] chore: add perf test for getLastVisibleAction with 10k reportActions --- .../perf-test/ReportActionsUtils.perf-test.js | 62 +++++++++++++++++++ tests/utils/LHNTestUtils.js | 50 +++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/perf-test/ReportActionsUtils.perf-test.js diff --git a/tests/perf-test/ReportActionsUtils.perf-test.js b/tests/perf-test/ReportActionsUtils.perf-test.js new file mode 100644 index 0000000000000..656e863fc3e1a --- /dev/null +++ b/tests/perf-test/ReportActionsUtils.perf-test.js @@ -0,0 +1,62 @@ +import {measureFunction} from 'reassure'; +import Onyx from 'react-native-onyx'; +import _ from 'underscore'; +import ONYXKEYS from '../../src/ONYXKEYS'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; +import * as ReportActionsUtils from '../../src/libs/ReportActionsUtils'; +import * as LHNTestUtils from '../utils/LHNTestUtils'; + +jest.setTimeout(60000); + +beforeAll(() => + Onyx.init({ + keys: ONYXKEYS, + safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], + registerStorageEventListener: () => {}, + }), +); + +// Clear out Onyx after each test so that each test starts with a clean slate +afterEach(() => { + Onyx.clear(); +}); + +const getMockedReportActionsMap = (reportsLength = 10, actionsPerReportLength = 100) => { + const mockReportActions = Array.from({length: actionsPerReportLength}, (__, i) => { + const reportActionKey = i + 1; + const email = `actor+${reportActionKey}@mail.com`; + const reportAction = LHNTestUtils.getFakeReportAction(email); + + return {[reportActionKey]: reportAction}; + }); + + const reportKeysMap = Array.from({length: reportsLength}, (__, i) => { + const key = i + 1; + + return {[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${key}`]: _.assign({}, ...mockReportActions)}; + }); + + return _.assign({}, ...reportKeysMap); +}; + +const mockedReportActionsMap = getMockedReportActionsMap(2, 10000); + +/** + * This function will be executed 20 times and the average time will be used on the comparison. + * It will fail based on the CI configuration around Reassure: + * @see /.github/workflows/reassurePerformanceTests.yml + * + * Max deviation on the duration is set to 20% at the time of writing. + * + * More on the measureFunction API: + * @see https://callstack.github.io/reassure/docs/api#measurefunction-function + */ +test('getLastVisibleAction with 10k reportActions stored per report', async () => { + const reportId = '1'; + + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getLastVisibleAction(reportId), {runs: 20}); +}); diff --git a/tests/utils/LHNTestUtils.js b/tests/utils/LHNTestUtils.js index ce4edc75b4448..83f85fdfe663d 100644 --- a/tests/utils/LHNTestUtils.js +++ b/tests/utils/LHNTestUtils.js @@ -127,6 +127,7 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = actor, actorAccountID: 1, reportActionID: `${++lastFakeReportActionID}`, + actionName: CONST.REPORT.ACTIONS.TYPE.CREATED, shouldShow: true, timestamp, reportActionTimestamp: timestamp, @@ -139,6 +140,55 @@ function getFakeReportAction(actor = 'email1@test.com', millisecondsInThePast = ], whisperedToAccountIDs: [], automatic: false, + message: [ + { + type: 'COMMENT', + html: 'hey', + text: 'hey', + isEdited: false, + whisperedTo: [], + isDeletedParentAction: false, + reactions: [ + { + emoji: 'heart', + users: [ + { + accountID: 1, + skinTone: -1, + }, + ], + }, + ], + }, + ], + originalMessage: { + childReportID: `${++lastFakeReportActionID}`, + emojiReactions: { + heart: { + createdAt: '2023-08-28 15:27:52', + users: { + 1: { + skinTones: { + '-1': '2023-08-28 15:27:52', + }, + }, + }, + }, + }, + html: 'hey', + lastModified: '2023-08-28 15:28:12.432', + reactions: [ + { + emoji: 'heart', + users: [ + { + accountID: 1, + skinTone: -1, + }, + ], + }, + ], + }, }; } From 91e1f288768a6e160ee42f1904013072472f5a8d Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Thu, 26 Oct 2023 17:32:25 +0200 Subject: [PATCH 2/4] chore: add perf test for getLastVisibleAction with actionsToMerge --- .../perf-test/ReportActionsUtils.perf-test.js | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/perf-test/ReportActionsUtils.perf-test.js b/tests/perf-test/ReportActionsUtils.perf-test.js index 656e863fc3e1a..417f2c61be4ab 100644 --- a/tests/perf-test/ReportActionsUtils.perf-test.js +++ b/tests/perf-test/ReportActionsUtils.perf-test.js @@ -1,6 +1,7 @@ import {measureFunction} from 'reassure'; import Onyx from 'react-native-onyx'; import _ from 'underscore'; +import CONST from '../../src/CONST'; import ONYXKEYS from '../../src/ONYXKEYS'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import * as ReportActionsUtils from '../../src/libs/ReportActionsUtils'; @@ -51,7 +52,7 @@ const mockedReportActionsMap = getMockedReportActionsMap(2, 10000); * More on the measureFunction API: * @see https://callstack.github.io/reassure/docs/api#measurefunction-function */ -test('getLastVisibleAction with 10k reportActions stored per report', async () => { +test('getLastVisibleAction on 10k reportActions', async () => { const reportId = '1'; await Onyx.multiSet({ @@ -60,3 +61,33 @@ test('getLastVisibleAction with 10k reportActions stored per report', async () = await waitForBatchedUpdates(); await measureFunction(() => ReportActionsUtils.getLastVisibleAction(reportId), {runs: 20}); }); + +test('getLastVisibleAction on 10k reportActions with actionsToMerge', async () => { + const reportId = '1'; + const parentReportActionId = '1'; + const fakeParentAction = mockedReportActionsMap[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportId}`][parentReportActionId]; + const actionsToMerge = { + [parentReportActionId]: { + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + previousMessage: fakeParentAction.message, + message: [ + { + translationKey: '', + type: 'COMMENT', + html: '', + text: '', + isEdited: true, + isDeletedParentAction: true, + }, + ], + errors: null, + linkMetaData: [], + }, + }; + + await Onyx.multiSet({ + ...mockedReportActionsMap, + }); + await waitForBatchedUpdates(); + await measureFunction(() => ReportActionsUtils.getLastVisibleAction(reportId, actionsToMerge), {runs: 20}); +}); From 1fb0e5b7bd0ff96c6b8a42c23e10ad53025a850e Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Mon, 30 Oct 2023 12:36:35 +0100 Subject: [PATCH 3/4] chore: satisfy linter, remove redundnat double underscore placeholder --- tests/perf-test/ReportActionsUtils.perf-test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/perf-test/ReportActionsUtils.perf-test.js b/tests/perf-test/ReportActionsUtils.perf-test.js index 417f2c61be4ab..c28d6ce434ebb 100644 --- a/tests/perf-test/ReportActionsUtils.perf-test.js +++ b/tests/perf-test/ReportActionsUtils.perf-test.js @@ -1,11 +1,11 @@ -import {measureFunction} from 'reassure'; import Onyx from 'react-native-onyx'; +import {measureFunction} from 'reassure'; import _ from 'underscore'; import CONST from '../../src/CONST'; -import ONYXKEYS from '../../src/ONYXKEYS'; -import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import * as ReportActionsUtils from '../../src/libs/ReportActionsUtils'; +import ONYXKEYS from '../../src/ONYXKEYS'; import * as LHNTestUtils from '../utils/LHNTestUtils'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; jest.setTimeout(60000); @@ -23,7 +23,7 @@ afterEach(() => { }); const getMockedReportActionsMap = (reportsLength = 10, actionsPerReportLength = 100) => { - const mockReportActions = Array.from({length: actionsPerReportLength}, (__, i) => { + const mockReportActions = Array.from({length: actionsPerReportLength}, (_, i) => { const reportActionKey = i + 1; const email = `actor+${reportActionKey}@mail.com`; const reportAction = LHNTestUtils.getFakeReportAction(email); From f8b184a8fd7c3f8c7b95aa930648061add84029a Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Mon, 30 Oct 2023 16:16:54 +0100 Subject: [PATCH 4/4] chore: apply code review updates for naming unused parameters --- tests/perf-test/ReportActionsUtils.perf-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/perf-test/ReportActionsUtils.perf-test.js b/tests/perf-test/ReportActionsUtils.perf-test.js index c28d6ce434ebb..f643206c9f498 100644 --- a/tests/perf-test/ReportActionsUtils.perf-test.js +++ b/tests/perf-test/ReportActionsUtils.perf-test.js @@ -23,7 +23,7 @@ afterEach(() => { }); const getMockedReportActionsMap = (reportsLength = 10, actionsPerReportLength = 100) => { - const mockReportActions = Array.from({length: actionsPerReportLength}, (_, i) => { + const mockReportActions = Array.from({length: actionsPerReportLength}, (_reportAction, i) => { const reportActionKey = i + 1; const email = `actor+${reportActionKey}@mail.com`; const reportAction = LHNTestUtils.getFakeReportAction(email); @@ -31,7 +31,7 @@ const getMockedReportActionsMap = (reportsLength = 10, actionsPerReportLength = return {[reportActionKey]: reportAction}; }); - const reportKeysMap = Array.from({length: reportsLength}, (__, i) => { + const reportKeysMap = Array.from({length: reportsLength}, (_report, i) => { const key = i + 1; return {[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${key}`]: _.assign({}, ...mockReportActions)};