From dc7941d7327bc605ca92c16f8d8d484407912a96 Mon Sep 17 00:00:00 2001 From: Ruslan Shestopalyuk Date: Tue, 14 Mar 2023 04:48:02 -0700 Subject: [PATCH] Shim implementation for NativePerformance on JS side Summary: Changelog: [Internal] Implements a shim for the NativePerformance TurboModule, which can be used as a mock to unit test the JS side of WebPerformance functionality (in particular, the higher level API logic of `Performance` and `PerformanceObserver` implementations in JS) without need to have the native NativePerformance implementation available (which would otherwise require running some heavy weight e2e tests). Reviewed By: rubennorte Differential Revision: D43985454 fbshipit-source-id: da10387c1d70cb0300c077972d3925bc2fad4f5e --- .../__mocks__/NativePerformance.js | 65 +++++++++++++++ .../__tests__/NativePerformanceMock-test.js | 82 +++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 Libraries/WebPerformance/__mocks__/NativePerformance.js create mode 100644 Libraries/WebPerformance/__tests__/NativePerformanceMock-test.js diff --git a/Libraries/WebPerformance/__mocks__/NativePerformance.js b/Libraries/WebPerformance/__mocks__/NativePerformance.js new file mode 100644 index 00000000000..6b11a27a20a --- /dev/null +++ b/Libraries/WebPerformance/__mocks__/NativePerformance.js @@ -0,0 +1,65 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict + * @format + */ + +import type { + NativeMemoryInfo, + ReactNativeStartupTiming, + Spec as NativePerformance, +} from '../NativePerformance'; + +import NativePerformanceObserver from '../NativePerformanceObserver'; +import {RawPerformanceEntryTypeValues} from '../RawPerformanceEntry'; + +const marks: Map = new Map(); + +const NativePerformanceMock: NativePerformance = { + mark: (name: string, startTime: number, duration: number): void => { + NativePerformanceObserver?.logRawEntry({ + name, + entryType: RawPerformanceEntryTypeValues.MARK, + startTime, + duration, + }); + marks.set(name, startTime); + }, + + measure: ( + name: string, + startTime: number, + endTime: number, + duration?: number, + startMark?: string, + endMark?: string, + ): void => { + const start = startMark != null ? marks.get(startMark) ?? 0 : startTime; + const end = endMark != null ? marks.get(endMark) ?? 0 : endTime; + NativePerformanceObserver?.logRawEntry({ + name, + entryType: RawPerformanceEntryTypeValues.MEASURE, + startTime: start, + duration: duration ?? (end ? end - start : 0), + }); + }, + + getSimpleMemoryInfo: (): NativeMemoryInfo => { + return {}; + }, + + getReactNativeStartupTiming: (): ReactNativeStartupTiming => { + return { + startTime: 0, + endTime: 0, + executeJavaScriptBundleEntryPointStart: 0, + executeJavaScriptBundleEntryPointEnd: 0, + }; + }, +}; + +export default NativePerformanceMock; diff --git a/Libraries/WebPerformance/__tests__/NativePerformanceMock-test.js b/Libraries/WebPerformance/__tests__/NativePerformanceMock-test.js new file mode 100644 index 00000000000..679dad39dfc --- /dev/null +++ b/Libraries/WebPerformance/__tests__/NativePerformanceMock-test.js @@ -0,0 +1,82 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall react_native + */ + +const NativePerformanceMock = require('../__mocks__/NativePerformance').default; +const PerformanceObserver = require('../PerformanceObserver').default; + +describe('NativePerformanceMock', () => { + jest.mock( + '../NativePerformanceObserver', + () => require('../__mocks__/NativePerformanceObserver').default, + ); + + it('marks get reported', async () => { + let entries = []; + const observer = new PerformanceObserver((list, _observer) => { + entries = [...entries, ...list.getEntries()]; + }); + + observer.observe({type: 'mark'}); + + NativePerformanceMock.mark('mark1', 0, 10); + NativePerformanceMock.mark('mark2', 5, 10); + NativePerformanceMock.mark('mark3', 10, 20); + + await jest.runAllTicks(); + expect(entries).toHaveLength(3); + expect(entries.map(e => e.name)).toStrictEqual(['mark1', 'mark2', 'mark3']); + expect(entries.map(e => e.startTime)).toStrictEqual([0, 5, 10]); + }); + + it('measures get reported', async () => { + let entries = []; + const observer = new PerformanceObserver((list, _observer) => { + entries = [...entries, ...list.getEntries()]; + }); + + observer.observe({entryTypes: ['measure']}); + + NativePerformanceMock.mark('mark0', 0.0, 1.0); + NativePerformanceMock.mark('mark1', 1.0, 3.0); + NativePerformanceMock.mark('mark2', 2.0, 4.0); + + NativePerformanceMock.measure('measure0', 0, 2); + NativePerformanceMock.measure('measure1', 0, 2, 4); + NativePerformanceMock.measure( + 'measure2', + 0, + 0, + undefined, + 'mark1', + 'mark2', + ); + NativePerformanceMock.measure('measure3', 0, 0, 5, 'mark1'); + NativePerformanceMock.measure( + 'measure4', + 1.5, + 0, + undefined, + undefined, + 'mark2', + ); + + await jest.runAllTicks(); + expect(entries).toHaveLength(5); + expect(entries.map(e => e.name)).toStrictEqual([ + 'measure0', + 'measure1', + 'measure2', + 'measure3', + 'measure4', + ]); + expect(entries.map(e => e.startTime)).toStrictEqual([0, 0, 1, 1, 1.5]); + expect(entries.map(e => e.duration)).toStrictEqual([2, 4, 1, 5, 0.5]); + }); +});