Skip to content

Commit

Permalink
Extract AnimationUpdatesRecorder from TestRunner (#6342)
Browse files Browse the repository at this point in the history
## Summary
Logic related with managing time when recording AnimationUpdates was
moved to a separate file.
Also I delete the following functions:
* `public async setAnimationTimestamp`
* `public async advanceAnimationByTime`
* `public async advanceAnimationByFrames`

## Test plan
  • Loading branch information
Latropos authored Jul 31, 2024
1 parent f306de0 commit 4c11d2e
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DescribeDecorator, TestDecorator } from './types';
export { Presets } from './Presets';

const testRunner = new TestRunner();
const animationRecorder = testRunner.getAnimationUpdatesRecorder();
const valueRegistry = testRunner.getValueRegistry();

type DescribeFunction = (name: string, buildSuite: BuildFunction) => void;
Expand Down Expand Up @@ -170,11 +171,11 @@ export function configure(config: TestConfiguration) {
}

export async function mockAnimationTimer() {
await testRunner.mockAnimationTimer();
await animationRecorder.mockAnimationTimer();
}

export async function unmockAnimationTimer() {
await testRunner.unmockAnimationTimer();
await animationRecorder.unmockAnimationTimer();
}

export async function mockWindowDimensions() {
Expand All @@ -185,22 +186,10 @@ export async function unmockWindowDimensions() {
await testRunner.unmockWindowDimensions();
}

export async function setAnimationTimestamp(timestamp: number) {
await testRunner.setAnimationTimestamp(timestamp);
}

export async function advanceAnimationByTime(time: number) {
await testRunner.advanceAnimationByTime(time);
}

export async function advanceAnimationByFrames(frameCount: number) {
await testRunner.advanceAnimationByFrames(frameCount);
}

export async function recordAnimationUpdates() {
return testRunner.recordAnimationUpdates();
return animationRecorder.recordAnimationUpdates();
}

export async function stopRecordingAnimationUpdates() {
await testRunner.stopRecordingAnimationUpdates();
await animationRecorder.stopRecordingAnimationUpdates();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import type { Operation } from '../types';
import { SyncUIRunner } from '../utils/SyncUIRunner';
import { createUpdatesContainer } from './UpdatesContainer';

export class AnimationUpdatesRecorder {
private _syncUIRunner: SyncUIRunner = new SyncUIRunner();

public async recordAnimationUpdates() {
const updatesContainer = createUpdatesContainer();
const recordAnimationUpdates = updatesContainer.pushAnimationUpdates;
const recordLayoutAnimationUpdates = updatesContainer.pushLayoutAnimationUpdates;

await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
const originalUpdateProps = global._IS_FABRIC ? global._updatePropsFabric : global._updatePropsPaper;
global.originalUpdateProps = originalUpdateProps;

const mockedUpdateProps = (operations: Operation[]) => {
recordAnimationUpdates(operations);
originalUpdateProps(operations);
};

if (global._IS_FABRIC) {
global._updatePropsFabric = mockedUpdateProps;
} else {
global._updatePropsPaper = mockedUpdateProps;
}

const originalNotifyAboutProgress = global._notifyAboutProgress;
global.originalNotifyAboutProgress = originalNotifyAboutProgress;
global._notifyAboutProgress = (tag: number, value: Record<string, unknown>, isSharedTransition: boolean) => {
recordLayoutAnimationUpdates(tag, value);
originalNotifyAboutProgress(tag, value, isSharedTransition);
};
});
return updatesContainer;
}

public async stopRecordingAnimationUpdates() {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
if (global.originalUpdateProps) {
if (global._IS_FABRIC) {
global._updatePropsFabric = global.originalUpdateProps;
} else {
global._updatePropsPaper = global.originalUpdateProps;
}
global.originalUpdateProps = undefined;
}
if (global.originalNotifyAboutProgress) {
global._notifyAboutProgress = global.originalNotifyAboutProgress;
global.originalNotifyAboutProgress = undefined;
}
});
}

public async mockAnimationTimer() {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
global.mockedAnimationTimestamp = 0;
global.originalGetAnimationTimestamp = global._getAnimationTimestamp;
global._getAnimationTimestamp = () => {
if (global.mockedAnimationTimestamp === undefined) {
throw new Error("Animation timestamp wasn't initialized");
}
return global.mockedAnimationTimestamp;
};
global.framesCount = 0;

const originalRequestAnimationFrame = global.requestAnimationFrame;
global.originalRequestAnimationFrame = originalRequestAnimationFrame;
global.requestAnimationFrame = (callback: FrameRequestCallback) => {
originalRequestAnimationFrame(() => {
callback(global._getAnimationTimestamp());
});
return 0;
};

global.originalFlushAnimationFrame = global.__flushAnimationFrame;
global.__flushAnimationFrame = (_frameTimestamp: number) => {
global.mockedAnimationTimestamp! += 16;
global.__frameTimestamp = global.mockedAnimationTimestamp;
global.originalFlushAnimationFrame!(global.mockedAnimationTimestamp!);
global.framesCount!++;
};
});
}

public async unmockAnimationTimer() {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
if (global.originalGetAnimationTimestamp) {
global._getAnimationTimestamp = global.originalGetAnimationTimestamp;
global.originalGetAnimationTimestamp = undefined;
}
if (global.originalRequestAnimationFrame) {
(global.requestAnimationFrame as any) = global.originalRequestAnimationFrame;
global.originalRequestAnimationFrame = undefined;
}
if (global.originalFlushAnimationFrame) {
global.__flushAnimationFrame = global.originalFlushAnimationFrame;
global.originalFlushAnimationFrame = undefined;
}
if (global.mockedAnimationTimestamp) {
global.mockedAnimationTimestamp = undefined;
}
if (global.framesCount) {
global.framesCount = undefined;
}
});
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import type { Component, MutableRefObject, ReactElement } from 'react';
import { useRef } from 'react';
import type {
BuildFunction,
Operation,
TestCase,
TestConfiguration,
TestSuite,
TestValue,
TrackerCallCount,
} from '../types';
import type { BuildFunction, TestCase, TestConfiguration, TestSuite, TestValue, TrackerCallCount } from '../types';
import { DescribeDecorator, TestDecorator } from '../types';
import { TestComponent } from '../TestComponent';
import { applyMarkdown, formatTestName } from '../utils/stringFormatUtils';
Expand All @@ -20,11 +12,11 @@ import type {
} from 'react-native-reanimated';
import { Matchers } from '../matchers/Matchers';
import { assertMockedAnimationTimestamp, assertTestCase, assertTestSuite } from './Asserts';
import { createUpdatesContainer } from './UpdatesContainer';
import { makeMutable, runOnJS } from 'react-native-reanimated';
import { RenderLock, SyncUIRunner } from '../utils/SyncUIRunner';
import { ValueRegistry } from './ValueRegistry';
import { TestSummaryLogger } from './TestSummaryLogger';
import { AnimationUpdatesRecorder } from './AnimationUpdatesRecorder';
export { Presets } from '../Presets';

let callTrackerRegistryJS: Record<string, number> = {};
Expand All @@ -49,8 +41,13 @@ export class TestRunner {
private _includesOnly: boolean = false;
private _syncUIRunner: SyncUIRunner = new SyncUIRunner();
private _renderLock: RenderLock = new RenderLock();
private _testSummary: TestSummaryLogger = new TestSummaryLogger();
private _animationRecorder = new AnimationUpdatesRecorder();
private _valueRegistry = new ValueRegistry();
private _testSummary = new TestSummaryLogger();

public getAnimationUpdatesRecorder() {
return this._animationRecorder;
}

public getValueRegistry() {
return this._valueRegistry;
Expand Down Expand Up @@ -298,8 +295,8 @@ export class TestRunner {

this._currentTestCase = null;
await this.render(null);
await this.unmockAnimationTimer();
await this.stopRecordingAnimationUpdates();
await this._animationRecorder.unmockAnimationTimer();
await this._animationRecorder.stopRecordingAnimationUpdates();
}

public expect(currentValue: TestValue): Matchers {
Expand Down Expand Up @@ -327,135 +324,6 @@ export class TestRunner {
this._currentTestSuite.afterEach = job;
}

public async recordAnimationUpdates() {
const updatesContainer = createUpdatesContainer();
const recordAnimationUpdates = updatesContainer.pushAnimationUpdates;
const recordLayoutAnimationUpdates = updatesContainer.pushLayoutAnimationUpdates;

await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
const originalUpdateProps = global._IS_FABRIC ? global._updatePropsFabric : global._updatePropsPaper;
global.originalUpdateProps = originalUpdateProps;

const mockedUpdateProps = (operations: Operation[]) => {
recordAnimationUpdates(operations);
originalUpdateProps(operations);
};

if (global._IS_FABRIC) {
global._updatePropsFabric = mockedUpdateProps;
} else {
global._updatePropsPaper = mockedUpdateProps;
}

const originalNotifyAboutProgress = global._notifyAboutProgress;
global.originalNotifyAboutProgress = originalNotifyAboutProgress;
global._notifyAboutProgress = (tag: number, value: Record<string, unknown>, isSharedTransition: boolean) => {
recordLayoutAnimationUpdates(tag, value);
originalNotifyAboutProgress(tag, value, isSharedTransition);
};
});
return updatesContainer;
}

public async stopRecordingAnimationUpdates() {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
if (global.originalUpdateProps) {
if (global._IS_FABRIC) {
global._updatePropsFabric = global.originalUpdateProps;
} else {
global._updatePropsPaper = global.originalUpdateProps;
}
global.originalUpdateProps = undefined;
}
if (global.originalNotifyAboutProgress) {
global._notifyAboutProgress = global.originalNotifyAboutProgress;
global.originalNotifyAboutProgress = undefined;
}
});
}

public async mockAnimationTimer() {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
global.mockedAnimationTimestamp = 0;
global.originalGetAnimationTimestamp = global._getAnimationTimestamp;
global._getAnimationTimestamp = () => {
if (global.mockedAnimationTimestamp === undefined) {
throw new Error("Animation timestamp wasn't initialized");
}
return global.mockedAnimationTimestamp;
};
global.framesCount = 0;

const originalRequestAnimationFrame = global.requestAnimationFrame;
global.originalRequestAnimationFrame = originalRequestAnimationFrame;
global.requestAnimationFrame = (callback: FrameRequestCallback) => {
originalRequestAnimationFrame(() => {
callback(global._getAnimationTimestamp());
});
return 0;
};

global.originalFlushAnimationFrame = global.__flushAnimationFrame;
global.__flushAnimationFrame = (_frameTimestamp: number) => {
global.mockedAnimationTimestamp! += 16;
global.__frameTimestamp = global.mockedAnimationTimestamp;
global.originalFlushAnimationFrame!(global.mockedAnimationTimestamp!);
global.framesCount!++;
};
});
}

public async setAnimationTimestamp(timestamp: number) {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
assertMockedAnimationTimestamp(global.mockedAnimationTimestamp);
global.mockedAnimationTimestamp = timestamp;
});
}

public async advanceAnimationByTime(time: number) {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
assertMockedAnimationTimestamp(global.mockedAnimationTimestamp);
global.mockedAnimationTimestamp += time;
});
}

public async advanceAnimationByFrames(frameCount: number) {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
assertMockedAnimationTimestamp(global.mockedAnimationTimestamp);
global.mockedAnimationTimestamp += frameCount * 16;
});
}

public async unmockAnimationTimer() {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
if (global.originalGetAnimationTimestamp) {
global._getAnimationTimestamp = global.originalGetAnimationTimestamp;
global.originalGetAnimationTimestamp = undefined;
}
if (global.originalRequestAnimationFrame) {
(global.requestAnimationFrame as any) = global.originalRequestAnimationFrame;
global.originalRequestAnimationFrame = undefined;
}
if (global.originalFlushAnimationFrame) {
global.__flushAnimationFrame = global.originalFlushAnimationFrame;
global.originalFlushAnimationFrame = undefined;
}
if (global.mockedAnimationTimestamp) {
global.mockedAnimationTimestamp = undefined;
}
if (global.framesCount) {
global.framesCount = undefined;
}
});
}

public async unmockWindowDimensions() {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
Expand Down

0 comments on commit 4c11d2e

Please sign in to comment.