ゲームアプリが異なる UI フレームワーク上に構築されている場合、ゲームのテストを自動化するのは簡単なことではありません。ゲームループ テストでは、ネイティブ テストを Test Lab と統合し、選択したデバイスで簡単に実行できます。このガイドでは、Firebase Test Lab でゲームループ テストを実行するための準備について説明します。
ゲームループ テストの概要
ゲームループ テストとは
ゲームループ テストでは、実際のプレーヤーの操作をシミュレートして、ゲームが高速かつスケーラブルに動作し、ユーザー向けに適切なパフォーマンスを発揮できるかどうかを検証します。ループは、ゲームアプリのテストの全体または一部を通しで実行します。ゲームループ テストは、ローカルのシミュレータで行うことも、Test Lab の一連のデバイスで行うこともできます。ゲームループ テストは次の目的で使用できます。
- エンドユーザーがプレイするのと同じ方法でゲームを実行する。ユーザーの入力はスクリプト化できます。また、ユーザーをアイドル状態にすることもできます。AI を実装している場合は、ユーザーを AI で置き換えることも可能です(たとえば、カーレース ゲームでユーザーの入力の代わりに AI のドライバを配置できます)。
- 最高品質の設定でゲームを実行し、デバイスの対応状況を確認する。
- 技術的なテストを行う。たとえば、複数のシェーダをコンパイルして実行し、予期した結果が出力されるかどうか確認します。
ステップ 1: Test Lab のカスタム URL スキームを登録する
Xcode でプロジェクト ターゲットを選択します。
[Info] タブをクリックし、新しい URL タイプを追加します。
[URL Schemes] フィールドに「
firebase-game-loop
」と入力します。また、<dict>
タグ内の任意の場所で、プロジェクトのInfo.plist
構成ファイルにカスタム URL スキームを追加することもできます。<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLName</key> <string></string> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLSchemes</key> <array> <string>firebase-game-loop</string> </array> </dict> </array>
これで、Test Lab を使用してテストを行うようにアプリが構成されました。
ステップ 2: アプリを構成する(省略可)
複数のループを実行する
テストで複数のループ(シナリオ)を実行する場合は、起動時にアプリで実行するループを指定する必要があります。
アプリのデリゲートで、application(_:open:options:)
メソッドをオーバーライドします。
Swift
func application(_app: UIApplication,
open url: URL
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
if components.scheme == "firebase-game-loop" {
// ...Enter Game Loop Test logic to override application(_:open:options:).
}
return true
}
Objective-C
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary <UIApplicationOpenURLOptionsKey, id> *)options {
if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
// ...Enter Game Loop Test logic to override application(_:open:options:).
}
}
テストで複数のループを実行すると、アプリの起動に使用される URL に現在のループがパラメータとして渡されます。カスタム URL スキームの取得に使用される URLComponents
オブジェクトを解析して、現在のループ番号を取得することもできます。
Swift
if components.scheme == "firebase-game-loop" {
// Iterate over all parameters and find the one with the key "scenario".
let scenarioNum = Int(components.queryItems!.first(where: { $0.name == "scenario" })!.value!)!
// ...Write logic specific to the current loop (scenarioNum).
}
Objective-C
if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
// Launch the app as part of a game loop.
NSURLComponents *components = [NSURLComponents componentsWithURL:url
resolvingAgainstBaseURL:YES];
for (NSURLQueryItem *item in [components queryItems]) {
if ([item.name isEqualToString:@"scenario"]) {
NSInteger scenarioNum = [item.value integerValue];
// ...Write logic specific to the current loop (scenarioNum).
}
}
}
早期にテストを終了する
デフォルトでは、すべてのループが実行された場合でも、ゲームループ テストは 5 分間のタイムアウトに達するまで継続されます。タイムアウトに達すると、テストは終了し、保留中のループはすべてキャンセルされます。テストを高速化または早期に終了するには、アプリの AppDelegate で Test Lab のカスタム URL スキーム firebase-game-loop-complete
を呼び出します。例:
Swift
/// End the loop by calling our custom url scheme.
func finishLoop() {
let url = URL(string: "firebase-game-loop-complete://")!
UIApplication.shared.open(url)
}
Objective-C
- (void)finishLoop {
UIApplication *app = [UIApplication sharedApplication];
[app openURL:[NSURL URLWithString:@"firebase-game-loop-complete://"]
options:@{}
completionHandler:^(BOOL success) {}];
}
ゲームループ テストで現在のループを終了し、次のループを実行します。実行するループがなくなると、テストが終了します。
カスタムテストの結果を書き込む
デバイスのファイル システムにカスタムテストの結果を書き込むように、ゲームループ テストを構成できます。この場合、テストが開始すると、Test Lab はテストデバイスの GameLoopsResults
ディレクトリに結果ファイルを保存します(ディレクトリは自分で作成する必要があります)。テストが終了すると、Test Lab はすべてのファイルを GameLoopResults
ディレクトリからプロジェクトのバケットに移動します。テストを設定する際は、次の点に注意してください。
ファイル形式、サイズ、数量に関係なく、すべての結果ファイルがアップロードされます。
Test Lab は、テストのすべてのループの実行が終了するまでテスト結果を処理しません。出力を書き込むループが複数ある場合は、各ループの出力が 1 つの結果ファイルに追加されるようにするか、ループごとに結果ファイルが作成されるようにする必要があります。これにより、前のループの結果が上書きされるのを防止できます。
カスタムテストの結果を書き込むようにテストを設定するには:
アプリの
Documents
ディレクトリに、GameLoopResults
という名前のディレクトリを作成します。アプリのコード内の任意の場所(アプリのデリゲートなど)から、次のものを追加します。
Swift
/// Write to a results file. func writeResults() { let text = "Greetings from game loops!" let fileName = "results.txt" let fileManager = FileManager.default do { let docs = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let resultsDir = docs.appendingPathComponent("GameLoopResults") try fileManager.createDirectory( at: resultsDir, withIntermediateDirectories: true, attributes: nil) let fileURL = resultsDir.appendingPathComponent(fileName) try text.write(to: fileURL, atomically: false, encoding: .utf8) } catch { // ...Handle error writing to file. } }
Objective-C
/// Write to a results file. - (void)writeResults:(NSString *)message { // Locate and create the results directory (if it doesn't exist already). NSFileManager *manager = [NSFileManager defaultManager]; NSURL* url = [[manager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL* resultsDir = [url URLByAppendingPathComponent:@"GameLoopResults" isDirectory:YES]; [manager createDirectoryAtURL:resultsDir withIntermediateDirectories:NO attributes:nil error:nil]; // Write the result message to a text file. NSURL* resultFile = [resultsDir URLByAppendingPathComponent:@"result.txt"]; if ([manager fileExistsAtPath:[resultFile path]]) { // Append to the existing file NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:resultFile error:nil]; [handle seekToEndOfFile]; [handle writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; [handle closeFile]; } else { // Create and write to the file. [message writeToURL:resultFile atomically:NO encoding:NSUTF8StringEncoding error:nil]; } }
ステップ 3: アプリに署名する
アプリ内のすべてのアーティファクトが署名されていることを確認します。たとえば、Xcode でプロファイルや ID のプロビジョニングなどの署名設定を指定して確認できます。詳細については、Apple コード署名をご覧ください
ステップ 4: アップロード用にアプリをパッケージ化する
アプリの IPA ファイルを生成します(このファイルは後で必要になります)。
表示されたプルダウン メニューで、[Product] > [Archive] の順にクリックします。最新のアーカイブを選択して [Distribute App] をクリックします。
表示されたウィンドウで、[Development] > [Next] の順にクリックします。
省略可: ビルドを高速化するには、[Rebuild from Bitcode] オプションをオフにして [Next] をクリックします。Test Lab では、テストを実行するためにアプリのスリム化や再ビルドを行う必要がないため、このオプションを無効にしても問題はありません。
[Export] をクリックして、アプリの IPA ファイルをダウンロードするディレクトリを入力します。
ステップ 5: アプリの署名を検証する
- .ipa ファイルを解凍し、
codesign --verify --deep --verbose /path/to/MyApp.app
を実行して、アプリの署名を検証します。この場合の「MyApp」は、解凍したフォルダ内にあるアプリの名前です(プロジェクトによって異なります)。想定される出力はMyApp.app: valid on disk
です。
ステップ 6: ローカルでテストを実行する
Test Lab でテストを実行する前に、ローカルでテストを実行して動作をチェックできます。ローカルでテストを実行するには、ゲームアプリをシミュレータに読み込み、次のコマンドを実行します。
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://
シミュレータの UDID を確認するには、
instruments -s devices
コマンドを実行します。実行中のシミュレータが 1 つだけの場合は、SIMULATOR_UDID の代わりに特殊な文字列
"booted"
を入力します。
テストに複数のループが含まれている場合は、ループ番号を scenario
フラグに渡し、実行するループを指定できます。テストをローカルで行う場合、一度に実行できるループは 1 つだけです。たとえば、ループ 1、2、5 を実行する場合は、ループごとに別々のコマンドを実行する必要があります。
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=1
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=2
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=5
次のステップ
Firebase コンソールまたは gcloud CLI を使用してテストを実行する。