Skip to content

Commit

Permalink
Merge branch 'improve-player-testing-setup' into add-ads-player-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rolandkakonyi committed Dec 7, 2023
2 parents cc22758 + b97d304 commit eafa6ec
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 26 deletions.
10 changes: 2 additions & 8 deletions integration_test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,8 @@ yarn bootstrap
To run the tests, run the following command from the repository root:

```sh
yarn integration-test test:ios # Run tests on iOS
yarn integration-test test:android # Run tests on Android
```

Hint: You can provide a specific iOS simulator by name when using `--simulator` flag. `xcrun simctl list devices available` provides you with a list of available devices in your environment.

```sh
yarn example ios --simulator="iPhone 14 Pro"
yarn integration-test test:ios # Run tests on iOS simulator
yarn integration-test test:android # Run tests on Android emulator
```

To set the license key to be used for the tests, you can set the key `"licenseKey"` in `integration_test/app.json`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=appletvsimulator*]" = i386;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
INFOPLIST_FILE = IntegrationTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down Expand Up @@ -312,6 +314,8 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
"EXCLUDED_ARCHS[sdk=appletvsimulator*]" = i386;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
INFOPLIST_FILE = IntegrationTest/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down Expand Up @@ -367,7 +371,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=appletvsimulator*]" = i386;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
Expand Down Expand Up @@ -447,7 +451,7 @@
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=appletvsimulator*]" = i386;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
Expand Down
10 changes: 10 additions & 0 deletions integration_test/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ post_install do |installer|
)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
fix_deployment_target(installer)
fix_simulator_run(installer)
end

def fix_deployment_target(installer)
Expand All @@ -90,3 +91,12 @@ def fix_deployment_target(installer)
end
end
end

def fix_simulator_run(installer)
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
config.build_settings["EXCLUDED_ARCHS[sdk=appletvsimulator*]"] = "i386"
end
end
end
2 changes: 1 addition & 1 deletion integration_test/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,6 @@ SPEC CHECKSUMS:
Yoga: f67f5769ce78049c5fe798bc735f04535c7bc1ac
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

PODFILE CHECKSUM: aa4d941d9c016574cd5330873d8b6d6a8d7ea421
PODFILE CHECKSUM: a697b54562cb6063433ce9a20f9875a08a45b453

COCOAPODS: 1.14.2
8 changes: 6 additions & 2 deletions integration_test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"test:android": "yarn cavy run-android",
"test:ios": "yarn cavy run-ios",
"stop-test:android": "adb shell am force-stop com.bitmovin.player.reactnative.integrationtest",
"stop-test:ios": "xcrun simctl list devices available -e -j | jq --raw-output '.devices.[].[] | select( .state == \"Booted\" and (.deviceTypeIdentifier | contains(\"iPhone\"))).udid' | xargs -I UDID xcrun simctl terminate UDID com.bitmovin.player.reactnative.IntegrationTest 2> /dev/null; exit 0",
"start-test:android": "yarn cavy run-android",
"start-test:ios": "yarn cavy run-ios --simulator \"$(xcrun simctl list devices available -e -j | jq --raw-output -s '[.[].devices.[].[] | select( .deviceTypeIdentifier | contains(\"iPhone\") )|.name] | first')\"",
"test:android": "yarn stop-test:android && yarn start-test:android",
"test:ios": "yarn stop-test:ios && yarn start-test:ios",
"pods": "yarn pods-install || yarn pods-update",
"pods-install": "yarn pod-install",
"pods-update": "pod update --silent"
Expand Down
35 changes: 22 additions & 13 deletions integration_test/playertesting/PlayerTestWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default class PlayerTestWorld {
private player_: Player | undefined;
private isFinished_: boolean = false;
private eventListeners: { [key: string]: (event: Event) => void } = {};
private isPlayerInitialized: boolean = false;

static get shared(): PlayerTestWorld {
if (PlayerTestWorld.shared_ === undefined) {
Expand All @@ -42,6 +43,7 @@ export default class PlayerTestWorld {

private set player(player: Player | undefined) {
this.player_ = player;
this.isPlayerInitialized = false;
this.reRender();
}

Expand Down Expand Up @@ -72,10 +74,6 @@ export default class PlayerTestWorld {
player.initialize();
this.player = player;

// Trick to wait for the player to be initialized
// otherwise initial events might be missed
await player.isPlaying();

await fn().finally(() => {
player.destroy();
this.isFinished_ = true;
Expand All @@ -85,7 +83,7 @@ export default class PlayerTestWorld {
};

callPlayer = async <T>(fn: (player: Player) => Promise<T>): Promise<T> => {
return await fn(this.ensurePlayer());
return await fn(await this.ensurePlayer());
};

expectEvent = async <T extends Event>(
Expand All @@ -110,7 +108,7 @@ export default class PlayerTestWorld {
return await this.expectEventCalling<E>(
expectationConvertible,
timeoutSeconds,
() => fn(this.ensurePlayer())
async () => fn(await this.ensurePlayer())
);
};

Expand All @@ -122,7 +120,7 @@ export default class PlayerTestWorld {
return await this.expectEventsCalling(
expectationsConvertible,
timeoutSeconds,
() => fn(this.ensurePlayer())
async () => fn(await this.ensurePlayer())
);
};

Expand All @@ -143,7 +141,8 @@ export default class PlayerTestWorld {
time: number,
timeoutSeconds: number
): Promise<TimeChangedEvent> => {
const currentTime = await this.ensurePlayer().getCurrentTime();
const player = await this.ensurePlayer();
const currentTime = await player.getCurrentTime();
const targetTime = currentTime + time;
return await this.playUntil(targetTime, timeoutSeconds);
};
Expand All @@ -164,13 +163,14 @@ export default class PlayerTestWorld {
);
};

private ensurePlayer = (): Player => {
private ensurePlayer = async (): Promise<Player> => {
if (this.player !== undefined) {
if (this.player.isDestroyed) {
throw new Error(
'Player was destroyed. Did you forget to call "startPlayerTest" again?'
);
}
await this.ensurePlayerInitialized();
return this.player;
}
throw new Error("It seems you forgot to call 'startPlayerTest' first.");
Expand All @@ -186,7 +186,7 @@ export default class PlayerTestWorld {
private expectEventCalling = async <T extends Event>(
expectationConvertible: SingleEventExpectation | EventType,
timeoutSeconds: number,
afterListenerAttached: () => void = () => {}
afterListenerAttached: () => Promise<void> = async () => Promise.resolve()
): Promise<T> => {
let actualExpectation: SingleEventExpectation;
if (expectationConvertible instanceof SingleEventExpectation) {
Expand All @@ -211,7 +211,7 @@ export default class PlayerTestWorld {
reject(new Error(`Expectation was not met: ${actualExpectation}`));
}
}, timeoutSeconds * 1000);
afterListenerAttached();
await afterListenerAttached();
return future.then((event) => {
clearTimeout(timeoutHandle);
removeListener();
Expand All @@ -222,7 +222,7 @@ export default class PlayerTestWorld {
private expectEventsCalling = async (
expectationsConvertible: MultipleEventsExpectation | EventType[],
timeoutSeconds: number,
afterListenerAttached: () => void = () => {}
afterListenerAttached: () => Promise<void> = async () => Promise.resolve()
): Promise<Event[]> => {
let actualExpectation: MultipleEventsExpectation;
if (expectationsConvertible instanceof MultipleEventsExpectation) {
Expand Down Expand Up @@ -259,7 +259,7 @@ export default class PlayerTestWorld {
reject(new Error(`Expectation was not met: ${actualExpectation}`));
}
}, timeoutSeconds * 1000);
afterListenerAttached();
await afterListenerAttached();
return future.then((events) => {
clearTimeout(timeoutHandle);
removeListener();
Expand All @@ -276,4 +276,13 @@ export default class PlayerTestWorld {
delete this.eventListeners[key];
};
};

private ensurePlayerInitialized = async (): Promise<void> => {
if (this.isPlayerInitialized === false) {
// Trick to make sure the player is initialized,
// otherwise method calls might have no effect.
await new Promise((resolve) => setTimeout(resolve, 100));
this.isPlayerInitialized = true;
}
};
}

0 comments on commit eafa6ec

Please sign in to comment.