diff --git a/packages/reassure-measure/src/__tests__/measure-function.test.tsx b/packages/reassure-measure/src/__tests__/measure-function.test.tsx index ade058e3..47e48046 100644 --- a/packages/reassure-measure/src/__tests__/measure-function.test.tsx +++ b/packages/reassure-measure/src/__tests__/measure-function.test.tsx @@ -41,3 +41,15 @@ test('measureFunctionInternal applies dropsWorst option', () => { expect(results.meanCount).toBe(1); expect(results.stdevCount).toBe(0); }); + +test('measureFunctionInternal supports "runs: quick-3" option', () => { + const fn = jest.fn(() => fib(5)); + const results = measureFunctionInternal(fn, { runs: 'quick-3', warmupRuns: 1 }); + + expect(fn).toHaveBeenCalledTimes(4); + expect(results.runs).toBe(6); + expect(results.durations).toHaveLength(6); + expect(results.counts).toHaveLength(6); + expect(results.meanCount).toBe(1); + expect(results.stdevCount).toBe(0); +}); diff --git a/packages/reassure-measure/src/__tests__/measure-helpers.test.ts b/packages/reassure-measure/src/__tests__/measure-helpers.test.ts index 714e5ac5..63cd8f8b 100644 --- a/packages/reassure-measure/src/__tests__/measure-helpers.test.ts +++ b/packages/reassure-measure/src/__tests__/measure-helpers.test.ts @@ -7,7 +7,7 @@ test('processRunResults calculates correct means and stdevs', () => { { duration: 14, count: 2 }, ]; - expect(processRunResults(input, 0)).toEqual({ + expect(processRunResults(input, 3, 0)).toEqual({ runs: 3, meanDuration: 12, stdevDuration: 2, @@ -20,13 +20,13 @@ test('processRunResults calculates correct means and stdevs', () => { test('processRunResults applies warmupRuns option', () => { const input = [ - { duration: 23, count: 1 }, + { duration: 23, count: 1 }, // warmup run { duration: 20, count: 5 }, { duration: 24, count: 5 }, { duration: 22, count: 5 }, ]; - expect(processRunResults(input, 1)).toEqual({ + expect(processRunResults(input, 4, 1)).toEqual({ runs: 3, meanDuration: 22, stdevDuration: 2, @@ -36,3 +36,22 @@ test('processRunResults applies warmupRuns option', () => { counts: [5, 5, 5], }); }); + +test('processRunResults supports "runs: quick-3" option', () => { + const input = [ + { duration: 100, count: 1 }, // warmup run + { duration: 1, count: 5 }, + { duration: 6, count: 5 }, + { duration: 29, count: 5 }, + ]; + + expect(processRunResults(input, 'quick-3', 1)).toEqual({ + runs: 6, + meanDuration: 9, + stdevDuration: 10, + durations: [29, 6, 6, 6, 6, 1], + meanCount: 5, + stdevCount: 0, + counts: [5, 5, 5, 5, 5, 5], + }); +}); diff --git a/packages/reassure-measure/src/__tests__/measure-render.test.tsx b/packages/reassure-measure/src/__tests__/measure-render.test.tsx index a51c68b3..69a3ff9b 100644 --- a/packages/reassure-measure/src/__tests__/measure-render.test.tsx +++ b/packages/reassure-measure/src/__tests__/measure-render.test.tsx @@ -89,3 +89,27 @@ test('buildUiToRender does not wrap when no wrapper is passed', () => { `); }); + +test('measureRender applies dropsWorst option', async () => { + const scenario = jest.fn(() => Promise.resolve(null)); + const results = await measureRender(, { runs: 10, warmupRuns: 1, scenario }); + + expect(scenario).toHaveBeenCalledTimes(11); + expect(results.runs).toBe(10); + expect(results.durations).toHaveLength(10); + expect(results.counts).toHaveLength(10); + expect(results.meanCount).toBe(1); + expect(results.stdevCount).toBe(0); +}); + +test('measureRender supports "runs: quick-3" option', async () => { + const scenario = jest.fn(() => Promise.resolve(null)); + const results = await measureRender(, { runs: 'quick-3', warmupRuns: 1, scenario }); + + expect(scenario).toHaveBeenCalledTimes(4); + expect(results.runs).toBe(6); + expect(results.durations).toHaveLength(6); + expect(results.counts).toHaveLength(6); + expect(results.meanCount).toBe(1); + expect(results.stdevCount).toBe(0); +}); diff --git a/packages/reassure-measure/src/config.ts b/packages/reassure-measure/src/config.ts index 88f91890..ceb030ca 100644 --- a/packages/reassure-measure/src/config.ts +++ b/packages/reassure-measure/src/config.ts @@ -4,7 +4,7 @@ export type Render = (component: React.ReactElement) => any; export type Cleanup = () => void; type Config = { - runs: number; + runs: number | 'quick-3'; warmupRuns: number; outputFile: string; testingLibrary?: TestingLibrary; diff --git a/packages/reassure-measure/src/measure-function.tsx b/packages/reassure-measure/src/measure-function.tsx index 15a59b7a..7f4f89c2 100644 --- a/packages/reassure-measure/src/measure-function.tsx +++ b/packages/reassure-measure/src/measure-function.tsx @@ -5,7 +5,7 @@ import { type RunResult, processRunResults } from './measure-helpers'; import { showFlagsOuputIfNeeded, writeTestStats } from './output'; interface MeasureFunctionOptions { - runs?: number; + runs?: number | 'quick-3'; warmupRuns?: number; } @@ -18,12 +18,13 @@ export async function measureFunction(fn: () => void, options?: MeasureFunctionO export function measureFunctionInternal(fn: () => void, options?: MeasureFunctionOptions): MeasureResults { const runs = options?.runs ?? config.runs; + const runCount = runs === 'quick-3' ? 3 : runs; const warmupRuns = options?.warmupRuns ?? config.warmupRuns; showFlagsOuputIfNeeded(); const runResults: RunResult[] = []; - for (let i = 0; i < runs + warmupRuns; i += 1) { + for (let i = 0; i < runCount + warmupRuns; i += 1) { const timeStart = getCurrentTime(); fn(); const timeEnd = getCurrentTime(); @@ -32,7 +33,7 @@ export function measureFunctionInternal(fn: () => void, options?: MeasureFunctio runResults.push({ duration, count: 1 }); } - return processRunResults(runResults, warmupRuns); + return processRunResults(runResults, runs, warmupRuns); } function getCurrentTime() { diff --git a/packages/reassure-measure/src/measure-helpers.tsx b/packages/reassure-measure/src/measure-helpers.tsx index 8713cf60..97c12367 100644 --- a/packages/reassure-measure/src/measure-helpers.tsx +++ b/packages/reassure-measure/src/measure-helpers.tsx @@ -6,10 +6,15 @@ export interface RunResult { count: number; } -export function processRunResults(results: RunResult[], warmupRuns: number): MeasureResults { +export function processRunResults(results: RunResult[], runs: number | 'quick-3', warmupRuns: number): MeasureResults { results = results.slice(warmupRuns); + results.sort((first, second) => second.duration - first.duration); // duration DESC + if (runs === 'quick-3') { + results = [results[0], results[1], results[1], results[1], results[1], results[2]]; + } + const durations = results.map((result) => result.duration); const meanDuration = math.mean(durations) as number; const stdevDuration = math.std(...durations); diff --git a/packages/reassure-measure/src/measure-render.tsx b/packages/reassure-measure/src/measure-render.tsx index e9b56c47..b25afe9f 100644 --- a/packages/reassure-measure/src/measure-render.tsx +++ b/packages/reassure-measure/src/measure-render.tsx @@ -12,7 +12,7 @@ logger.configure({ }); export interface MeasureOptions { - runs?: number; + runs?: number | 'quick-3'; warmupRuns?: number; wrapper?: React.ComponentType<{ children: React.ReactElement }>; scenario?: (screen: any) => Promise; @@ -27,6 +27,8 @@ export async function measurePerformance(ui: React.ReactElement, options?: Measu export async function measureRender(ui: React.ReactElement, options?: MeasureOptions): Promise { const runs = options?.runs ?? config.runs; + const runCount = runs === 'quick-3' ? 3 : runs; + const scenario = options?.scenario; const warmupRuns = options?.warmupRuns ?? config.warmupRuns; @@ -36,7 +38,7 @@ export async function measureRender(ui: React.ReactElement, options?: MeasureOpt const runResults: RunResult[] = []; let hasTooLateRender = false; - for (let i = 0; i < runs + warmupRuns; i += 1) { + for (let i = 0; i < runCount + warmupRuns; i += 1) { let duration = 0; let count = 0; let isFinished = false; @@ -72,7 +74,7 @@ export async function measureRender(ui: React.ReactElement, options?: MeasureOpt ); } - return processRunResults(runResults, warmupRuns); + return processRunResults(runResults, runs, warmupRuns); } export function buildUiToRender( diff --git a/test-apps/native/src/OtherTest.perf-test.tsx b/test-apps/native/src/OtherTest.perf-test.tsx index bf3b09fe..d3729f8a 100644 --- a/test-apps/native/src/OtherTest.perf-test.tsx +++ b/test-apps/native/src/OtherTest.perf-test.tsx @@ -1,7 +1,9 @@ import React from 'react'; import { View, Text, Pressable } from 'react-native'; import { fireEvent, RenderAPI, screen } from '@testing-library/react-native'; -import { measurePerformance } from 'reassure'; +import { measurePerformance, configure } from 'reassure'; + +configure({ runs: 'quick-3' }); import { SlowList } from './SlowList'; diff --git a/test-apps/native/src/SlowList.perf-test.tsx b/test-apps/native/src/SlowList.perf-test.tsx index ddb4d15a..0986a15d 100644 --- a/test-apps/native/src/SlowList.perf-test.tsx +++ b/test-apps/native/src/SlowList.perf-test.tsx @@ -1,9 +1,11 @@ import * as React from 'react'; import { View, Text, Pressable } from 'react-native'; import { fireEvent, screen } from '@testing-library/react-native'; -import { measurePerformance } from 'reassure'; +import { measurePerformance, configure } from 'reassure'; import { SlowList } from './SlowList'; +configure({ runs: 'quick-3' }); + const AsyncComponent = () => { const [count, setCount] = React.useState(0); diff --git a/test-apps/native/src/fib.perf-test.tsx b/test-apps/native/src/fib.perf-test.tsx index 42480e0c..61023524 100644 --- a/test-apps/native/src/fib.perf-test.tsx +++ b/test-apps/native/src/fib.perf-test.tsx @@ -1,4 +1,6 @@ -import { measureFunction } from '@callstack/reassure-measure'; +import { configure, measureFunction } from '@callstack/reassure-measure'; + +configure({ runs: 'quick-3' }); function fib(n: number): number { if (n <= 1) {