Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exract async code to separate classes #6258

Merged
merged 4 commits into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { View, Button, StyleSheet, Text } from 'react-native';
import type { ReactNode } from 'react';
import React, { useEffect, useState } from 'react';
import { runTests, configure } from './RuntimeTestsApi';
import type { LockObject } from './types';
import { RenderLock } from './TestRunner';

let renderLock: LockObject = { lock: false };
let renderLock: RenderLock = new RenderLock();
export class ErrorBoundary extends React.Component<
{ children: React.JSX.Element | Array<React.JSX.Element> },
{ hasError: boolean }
Expand Down Expand Up @@ -32,7 +32,7 @@ export default function RuntimeTestsRunner() {
const [component, setComponent] = useState<ReactNode | null>(null);
useEffect(() => {
if (renderLock) {
renderLock.lock = false;
renderLock.unlockRender();
}
}, [component]);
return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { runOnJS, runOnUI } from 'react-native-reanimated';
import type { LockObject } from './types';

export class SyncUIRunner {
private _threadLock: LockObject = {
lock: false,
};

private waitForThreadUnlock(maxWaitTime?: number) {
return new Promise(resolve => {
const startTime = performance.now();
const interval = setInterval(() => {
const currentTime = performance.now();
const waitTimeExceeded = maxWaitTime && maxWaitTime < currentTime - startTime;
if (this._threadLock.lock !== true || waitTimeExceeded) {
clearInterval(interval);
resolve(this._threadLock.lock);
}
}, 10);
});
}

public async runOnUIBlocking(worklet: () => void, maxWaitTime?: number) {
const unlock = () => (this._threadLock.lock = false);
this._threadLock.lock = true;
runOnUI(() => {
'worklet';
worklet();
runOnJS(unlock)();
})();
await this.waitForThreadUnlock(maxWaitTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import type { Component, MutableRefObject, ReactElement } from 'react';
import { useRef } from 'react';
import type {
BuildFunction,
NullableTestValue,
LockObject,
NullableTestValue,
Operation,
SharedValueSnapshot,
TestCase,
Expand All @@ -17,10 +17,11 @@ import { DescribeDecorator, TestDecorator } from './types';
import { TestComponent } from './TestComponent';
import { EMPTY_LOG_PLACEHOLDER, applyMarkdown, color, formatString, indentNestingLevel } from './stringFormatUtils';
import type { SharedValue } from 'react-native-reanimated';
import { makeMutable, runOnUI, runOnJS } from 'react-native-reanimated';
import { makeMutable, runOnJS } from 'react-native-reanimated';
import { Matchers, nullableMatch } from './matchers/Matchers';
import { assertMockedAnimationTimestamp, assertTestCase, assertTestSuite } from './Asserts';
import { createUpdatesContainer } from './UpdatesContainer';
import { SyncUIRunner } from './SyncUIRunner';

let callTrackerRegistryJS: Record<string, number> = {};
const callTrackerRegistryUI = makeMutable<Record<string, number>>({});
Expand All @@ -36,15 +37,42 @@ function notifyJS(name: string) {
notificationRegistry[name] = true;
}

export class RenderLock {
private _renderLock: LockObject = { lock: false };

public lockRender() {
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
this._renderLock = { lock: true };
}

public unlockRender() {
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
this._renderLock = { lock: false };
}

public waitForUnlock(maxWaitTime?: number) {
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
return new Promise(resolve => {
const startTime = performance.now();
const interval = setInterval(() => {
const currentTime = performance.now();
const waitTimeExceeded = maxWaitTime && maxWaitTime < currentTime - startTime;
if (this._renderLock.lock !== true || waitTimeExceeded) {
clearInterval(interval);
resolve(this._renderLock.lock);
}
}, 10);
});
}
}

export class TestRunner {
private _testSuites: TestSuite[] = [];
private _currentTestSuite: TestSuite | null = null;
private _currentTestCase: TestCase | null = null;
private _renderHook: (component: ReactElement<Component> | null) => void = () => {};
private _renderLock: LockObject = { lock: false };
private _valueRegistry: Record<string, SharedValue> = {};
private _wasRenderedNull: boolean = false;
private _includesOnly: boolean = false;
private _syncUIRunner: SyncUIRunner = new SyncUIRunner();
private _renderLock: RenderLock = new RenderLock();
private _summary: TestSummary = {
passed: 0,
failed: 0,
Expand All @@ -54,10 +82,6 @@ export class TestRunner {
endTime: 0,
};

private _threadLock: LockObject = {
lock: false,
};

public notify(name: string) {
'worklet';
if (_WORKLET) {
Expand All @@ -83,18 +107,19 @@ export class TestRunner {
return this._renderLock;
}

public async render(component: ReactElement<Component> | null) {
public render(component: ReactElement<Component> | null) {
piaskowyk marked this conversation as resolved.
Show resolved Hide resolved
if (!component && this._wasRenderedNull) {
return;
}
this._wasRenderedNull = !component;
this._renderLock.lock = true;
this._renderLock.lockRender();

try {
this._renderHook(component);
} catch (e) {
console.log(e);
}
return this.waitForPropertyValueChange(this._renderLock, 'lock');
return this._renderLock.waitForUnlock();
}

public async clearRenderOutput() {
Expand Down Expand Up @@ -207,7 +232,7 @@ export class TestRunner {
const jsValue = this._valueRegistry[name].value;
const sharedValue = this._valueRegistry[name];
const valueContainer = makeMutable<unknown>(null);
await this.runOnUIBlocking(() => {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
valueContainer.value = sharedValue.value;
}, 1000);
Expand Down Expand Up @@ -379,42 +404,12 @@ export class TestRunner {
this._currentTestSuite.afterEach = job;
}

private waitForPropertyValueChange(
targetObject: LockObject,
targetProperty: 'lock',
initialValue = true,
maxWaitTime?: number,
) {
return new Promise(resolve => {
const startTime = performance.now();
const interval = setInterval(() => {
const currentTime = performance.now();
const waitTimeExceeded = maxWaitTime && maxWaitTime < currentTime - startTime;
if (targetObject[targetProperty] !== initialValue || waitTimeExceeded) {
clearInterval(interval);
resolve(targetObject[targetProperty]);
}
}, 10);
});
}

public async runOnUIBlocking(worklet: () => void, maxWaitTime?: number) {
const unlock = () => (this._threadLock.lock = false);
this._threadLock.lock = true;
runOnUI(() => {
'worklet';
worklet();
runOnJS(unlock)();
})();
await this.waitForPropertyValueChange(this._threadLock, 'lock', true, maxWaitTime);
}

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

await this.runOnUIBlocking(() => {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
const originalUpdateProps = global._IS_FABRIC ? global._updatePropsFabric : global._updatePropsPaper;
global.originalUpdateProps = originalUpdateProps;
Expand All @@ -441,7 +436,7 @@ export class TestRunner {
}

public async stopRecordingAnimationUpdates() {
await this.runOnUIBlocking(() => {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
if (global.originalUpdateProps) {
if (global._IS_FABRIC) {
Expand All @@ -459,7 +454,7 @@ export class TestRunner {
}

public async mockAnimationTimer() {
await this.runOnUIBlocking(() => {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
global.mockedAnimationTimestamp = 0;
global.originalGetAnimationTimestamp = global._getAnimationTimestamp;
Expand Down Expand Up @@ -489,31 +484,31 @@ export class TestRunner {
}

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

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

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

public async unmockAnimationTimer() {
await this.runOnUIBlocking(() => {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
if (global.originalGetAnimationTimestamp) {
global._getAnimationTimestamp = global.originalGetAnimationTimestamp;
Expand Down Expand Up @@ -583,7 +578,7 @@ export class TestRunner {
};
console.error = mockedConsoleFunction;
console.warn = mockedConsoleFunction;
await this.runOnUIBlocking(() => {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
console.error = mockedConsoleFunction;
console.warn = mockedConsoleFunction;
Expand All @@ -592,7 +587,7 @@ export class TestRunner {
const restoreConsole = async () => {
console.error = originalError;
console.warn = originalWarning;
await this.runOnUIBlocking(() => {
await this._syncUIRunner.runOnUIBlocking(() => {
'worklet';
console.error = originalError;
console.warn = originalWarning;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { makeMutable } from 'react-native-reanimated';
import type { Operation, OperationUpdate } from './types';
import { isValidPropName } from './types';
import type { TestRunner } from './TestRunner';
import type { MultiViewSnapshot, SingleViewSnapshot } from './matchers/snapshotMatchers';
import { convertDecimalColor } from './util';
import type { TestComponent } from './TestComponent';
import { SyncUIRunner } from './SyncUIRunner';

type JsUpdate = {
tag: number;
Expand All @@ -18,7 +18,7 @@ type NativeUpdate = {
jsUpdateIndex: number;
};

export function createUpdatesContainer(testRunner: TestRunner) {
export function createUpdatesContainer() {
Latropos marked this conversation as resolved.
Show resolved Hide resolved
const jsUpdates = makeMutable<Array<JsUpdate>>([]);
const nativeSnapshots = makeMutable<Array<NativeUpdate>>([]);

Expand Down Expand Up @@ -151,7 +151,7 @@ export function createUpdatesContainer(testRunner: TestRunner) {
const nativeSnapshotsCount = nativeSnapshots.value.length;
const jsUpdatesCount = jsUpdates.value.length;
if (jsUpdatesCount === nativeSnapshotsCount) {
await testRunner.runOnUIBlocking(() => {
await new SyncUIRunner().runOnUIBlocking(() => {
'worklet';
const lastSnapshot = nativeSnapshots.value[nativeSnapshotsCount - 1];
if (lastSnapshot) {
Expand Down
Loading