Firebase Test Lab ゲームループ テスト

ゲームのテストを自動化することは簡単ではありません。ゲーム開発に使用される UI フレームワークはさまざまで、エンジンに依存するものもあります。また、ゲームの UI ナビゲーションを自動化することも簡単ではありません。ゲームアプリのテストをサポートするために、Test Lab に「デモモード」のサポートが追加されました。このモードを使用すると、プレーヤーの動作をシミュレートしながらゲームアプリを実行できます。このモードでは複数のループ(シナリオ)を実行できます。また、関連するループを一緒に実行できるように、ラベルで論理的にまとめることもできます。

このドキュメントでは、開発中にゲームを簡単にテストできるようにゲームループを実装する場合のガイドラインを説明します。このガイドラインは、テストが 1 台のテスト端末、テスト端末のファーム、Test Lab のいずれで実施されるかにかかわらず適用されます。

使ってみる

Test Lab でゲームループ テストを使用するには、ゲームを次のように変更する必要があります。

  1. ループを起動する
  2. ループを実行する
  3. (省略可)ゲームアプリを閉じる

ループを起動する

ゲームループ テストを起動すると、ゲームが特定のインテントで開始します。このため、次のサンプルコードのようにマニフェストを変更し、アクティビティに新しいインテント フィルタを追加する必要があります。

<activity android:name=".MyActivity">
   <intent-filter>
       <action android:name="com.google.intent.action.TEST_LOOP"/>
       <category android:name="android.intent.category.DEFAULT"/>
       <data android:mimeType="application/javascript"/>
   </intent-filter>
   <intent-filter>
      ... (other intent filters here)
   </intent-filter>
</activity>

ループを実行する

起動すると、次のサンプルコードのように、アクティビティは起動したインテントを確認します。

Java
Android

Intent launchIntent = getIntent();
if(launchIntent.getAction().equals("com.google.intent.action.TEST_LOOP")) {
    int scenario = launchIntent.getIntExtra("scenario", 0);
    // Code to handle your game loop here
}

Kotlin
Android

val launchIntent = intent
if (launchIntent.action == "com.google.intent.action.TEST_LOOP") {
    val scenario = launchIntent.getIntExtra("scenario", 0)
    // Code to handle your game loop here
}

このコードは onCreate メソッドで実行することをおすすめしますが、後で実行してもかまいません(たとえば、ゲームエンジンを最初に読み込んだ後に実行できます)。次に、ゲームエンジンに応じてゲームループを実装します(複数のゲームループで説明されているように、複数のループを実行することもできます)。ゲームループの使い方には次のようなものがあります。

  • エンドユーザーがプレイするのと同じ方法でゲームのレベルを実行する。ユーザーの入力をスクリプト化したり、ユーザーをアイドル状態にしたりできます。必要であれば、ユーザーを AI で置き換えることもできます。たとえば、カーレース ゲームのように AI が実装されている場合、ユーザーの入力の代わりに AI のドライバを簡単に配置できます。
  • 最高品質の設定でゲームを実行し、端末がそれに対応しているかどうか確認する。
  • 技術的なテストを実行する。たとえば、複数のシェーダをコンパイルして実行し、予期した結果が出力されるかどうか確認します。

Test Loop Manager

ゲームループを統合してローカルの端末で実行できるように、Test Loop Manager というオープンソースのアプリが用意されています。このアプリは、品質保証チームが自身の端末でゲームループを実行する場合にも利用できます。

Test Loop Manager を使用するには:

  1. Test Loop Manager をダウンロードします。
  2. 次のコマンドを使用して、端末に Test Loop Manager をインストールします。
    adb install testloopmanager.apk
  3. スマートフォンやタブレットで、Test Loop Apps というアプリを開きます。ゲームループを含むアプリのリストが端末に表示されます。ここにアプリが表示されない場合は、インテント フィルタがループを実行するで説明されているフィルタと一致しているかどうか確認してください。
  4. テストするゲームアプリを選択します。
    1. リストでゲームアプリの名前をクリックして、アプリが実装するシナリオ(ゲームループ)を選択します。アプリが複数のループを実装している場合には、複数のゲームループをご覧ください。
    2. [Run test] をクリックして、実行されたシナリオを確認します。

ゲームアプリを閉じる

テストが終了したら、次の方法でアプリを閉じます。

Java
Android

yourActivity.finish();

Kotlin
Android

yourActivity.finish()

通常、ゲームループ テストでは、UI フレームワークから次のループを開始できます。 アプリを終了しないと、ループを実行している UI フレームワークがテストの完了を認識できず、しばらくしてからアプリを終了する場合があります。

Test Lab でゲームループ テストを実行する

Firebase コンソールに移動します。プロジェクトをまだ作成していない場合には、ここで作成します。Spark 料金プランを使用すると、Test Lab を無料で利用できますが、一日の使用量に上限があります。使用制限なしで Test Lab を使用するには、Firebase の料金プランをご覧ください。

Firebase コンソールを使用する

次の手順に沿って、Firebase コンソールでゲームループ テストを実行します。

  1. Firebase コンソールの左側にあるナビゲーション パネルで [Test Lab] をクリックします。
  2. [最初のテストを実行する] をクリックします(プロジェクトですでにテストを実行している場合は [テストを実行] をクリックします)。
  3. テストの種類に [ゲームループ] を選択し、[続行] をクリックします。
  4. [参照] をクリックして、アプリの .apk ファイルを選択します。
  5. (省略可)使用可能なシナリオ(ゲームループ)のサブセットを選択するには、次のいずれかの操作を行います。

    • 特定のシナリオを選択するには、[シナリオ] フィールドにシナリオ番号のリストまたは範囲を入力します。
    • 特定のラベルが適用されているシナリオをすべて選択するには、[ラベル] フィールドにシナリオラベルを入力します。
  6. [続行] をクリックします。

  7. アプリのテストに使用する物理端末を選択します。

  8. [テストを開始] をクリックします。

Firebase コンソールの詳細については、Firebase コンソールから Firebase Test Lab を使用するをご覧ください。

gcloud コマンドライン環境を使用する

gcloud コマンドライン環境(CLI)を使用してゲームループ テストを実行するには、次の操作を行います。

  1. Firebase プロジェクトをまだ作成していない場合は、Firebase コンソールに移動して [プロジェクトを追加] をクリックし、プロジェクトを作成します。
  2. gcloud CLI を含む Google Cloud SDK をインストールします。
  3. Google アカウントでログインします。

    gcloud auth login

  4. 次のコマンドを使用して、Firebase プロジェクトを設定します。PROJECT_ID は、手順 1 で作成した Firebase プロジェクトです。

    gcloud config set project PROJECT_ID
    
  5. 次のように、最初のテストを実行します。

    gcloud firebase test android run \
    --type=game-loop --app=<path-to-apk> \
    --device model=herolte,version=23
  6. (省略可)使用可能なシナリオ(ゲームループ)のサブセットを選択するには、ゲームループ テストを実行するときに、次のいずれかの操作を行います。

    • 特定のシナリオを選択するには、--scenario-numbers フラグを使用してシナリオ番号のリストを指定します(例: --scenario-numbers=1,3,5)。
    • 特定のラベルが適用されているシナリオをすべて選択するには、--scenario-labels フラグを使用してシナリオラベルを指定します(例: --scenario-labels=performance,gpu)。

Test Lab で gcloud CLI を使用する方法については、gcloud コマンドラインから Firebase Test Lab for Android を使用するをご覧ください。

オプション機能

このセクションでは、オプション機能の使い方を説明します。オプション機能を使用すると、出力ファイルへのデータの書き込み、複数のゲームループのサポート、関連ループへのラベルの適用などの操作を行うことができます。1 つのテスト マトリックスで関連するループを簡単にテストできます。

出力データを書き込む

launchIntent.getData() メソッドを使用すると、指定したファイルにゲームループの出力を書き込むことができます。この出力データは、Test Lab のテスト結果ページで確認できます。データ出力ファイルのサンプルについては、ゲームループ テスト出力ファイルの例をご覧ください。

Test Lab では、アプリ間でファイルを共有する場合のベスト プラクティスを実装しています(詳細はファイルの共有をご覧ください)。インテントを取得しているアクティビティの onCreate() メソッドで次のコードを使用すると、このファイルを確認できます。

Java
Android

Intent launchIntent = getIntent();
Uri logFile = launchIntent.getData();
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.getEncodedPath());
    // ...
}

Kotlin
Android

val launchIntent = intent
val logFile = launchIntent.data
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.encodedPath!!)
    // ...
}

ゲームアプリの C++ 側からファイルに書き込む場合には、次の例のように、ファイルパスではなくファイル記述子を渡します。

Java
Android

Intent launchIntent = getIntent();
Uri logFile = launchIntent.getData();
int fd = -1;
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.getEncodedPath());
    try {
        fd = getContentResolver()
                .openAssetFileDescriptor(logFile, "w")
                .getParcelFileDescriptor()
                .getFd();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        fd = -1;
    } catch (NullPointerException e) {
        e.printStackTrace();
        fd = -1;
    }
}

// C++ code invoked here.
// native_function(fd);

Kotlin
Android

val launchIntent = intent
val logFile = launchIntent.data
var fd = -1
if (logFile != null) {
    Log.i(TAG, "Log file " + logFile.encodedPath!!)
    fd = try {
        contentResolver
                .openAssetFileDescriptor(logFile, "w")!!
                .parcelFileDescriptor
                .fd
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
        -1
    } catch (e: NullPointerException) {
        e.printStackTrace()
        -1
    }
}

// C++ code invoked here.
// native_function(fd);

C++

#include <unistd.h>
JNIEXPORT void JNICALL
Java_my_package_name_MyActivity_native_function(JNIEnv *env, jclass type, jint log_file_descriptor) {
// The file descriptor needs to be duplicated.
int my_file_descriptor = dup(log_file_descriptor);
}

複数のゲームループ

ゲームアプリで複数のゲームループをサポートすることもできます。ゲームに複数のレベルがある場合、1 つのループですべてを繰り返し処理するのではなく、レベルごとに 1 つのゲームループで起動することができます。

これにより、アプリがレベル 32 でクラッシュした場合、そのゲームループを直接起動してクラッシュを再現し、バグの修正をテストすることができます。

アプリに 5 つのゲームループがある場合、アプリのマニフェスト ファイルの <application> 要素に 1 行だけ追加します。

<meta-data
  android:name="com.google.test.loops"
  android:value="5" />

Test Loop Manager を起動したときに、起動するループを選択画面から選択できます。複数のループを起動するよう選択すると、前のループの完了後に次のループが順番に起動します。Test Lab では、起動するループを選択します。

起動インテントには、対象のループが整数パラメータとして含まれます。値の範囲は 1 からサポートされるループの最大数までです。

次のサンプルコードのように、Java のインテントから読み込むこともできます。

Java
Android

Intent launchIntent = getIntent();
int scenario = launchIntent.getIntExtra("scenario", 0);

Kotlin
Android

val launchIntent = intent
val scenario = launchIntent.getIntExtra("scenario", 0)

これをネイティブの C++ コードに渡し、int 値に基づいてループの動作を変更できます。

ゲームループにラベルを適用する

ゲームループに 1 つ以上のシナリオラベルを適用すると、QA チームが一連の関連ゲームループ(たとえば、互換性のあるすべてのゲームループ)を簡単に開始できるようになります。この操作を行うには、次のように meta-data 行をアプリのマニフェスト ファイルに追加する必要があります。

<meta-data
  android:name="com.google.test.loops.LABEL_NAME"
  android:value="1,3-5" />

独自のラベルを作成することもできますが、次の 4 つの事前定義のラベルも使用できます。

  • com.google.test.loops.player_experience: ゲームでのユーザーの体験を再現するループに使用します。このループでテストする目的は、実際のユーザーがゲームをプレイしているときに発生する問題を確認することです。
  • com.google.test.loops.gpu_compatibility: GPU 関連の問題をテストするループに使用します。このループで実行するテストの目的は、本番環境で正しく動作しないおそれのある GPU コードを実行し、ハードウェアやドライバの問題を明らかにすることです。
  • com.google.test.loops.compatibility: I/O の問題や OpenSSL の問題など、さまざまな互換性の問題をテストするループに使用します。
  • com.google.test.loops.performance: 端末のパフォーマンスをテストするループに使用します。たとえば、最も複雑なグラフィック設定でゲームを実行して、新しい端末での動作を確認することができます。

アプリのライセンス サポート

Test Lab は、Google Play が提供するアプリ ライセンス サービスを使用するアプリをサポートしています。Test Lab でアプリをテストする際にライセンスを確認するには、アプリを Play ストアの製品版チャネルに公開する必要があります。Test Lab を使用してアルファまたはベータチャネルでアプリをテストするには、アプリを Test Lab にアップロードする前にライセンス チェックを削除します。

既知の問題

Test Lab のゲームループ テストでは次の問題が確認されています。

  • Khronos Vulkan API のサポートが制限されています。Test Lab で使用可能な端末で、Vulkan API をサポートしているのは Samsung Galaxy S7(API レベル 24)と Google Pixel(API レベル 25)のみです。
  • 一部のクラッシュはバックトレースに対応していません。たとえば、リリースビルドによっては、prctl(PR_SET_DUMPABLE, 0) により debuggerd プロセスの出力が抑止される場合があります。詳しくは debuggerd をご覧ください。
  • ファイル権限エラーのため、API レベル 19 はサポートされていません。

ゲームループ テストの出力ファイルの例

次のような形式の出力データファイルを使用して、Firebase コンソールにゲームループ テストの結果を表示できます。/.../ の部分には、このファイル内の他のフィールド名と競合しない限り、任意のカスタム フィールドを入れることができます。

{
  "name": "test name",
  "start_timestamp": 0, // Timestamp of the test start (in us).
                           Can be absolute or relative
  "driver_info": "...",
  "frame_stats": [
    {
      "timestamp": 1200000, // Timestamp at which this section was written
                               It contains value regarding the period
                               start_timestamp(0) -> this timestamp (1200000 us)
      "avg_frame_time": 15320, // Average time to render a frame in ns
      "nb_swap": 52, // Number of frame rendered
      "threads": [
        {
          "name": "physics",
          "Avg_time": 8030 // Average time spent in this thread per frame in us
        },
        {
          "name": "AI",
          "Avg_time": 2030 // Average time spent in this thread per frame in us
        }
      ],
      /.../ // Any custom field you want (vertices display on the screen, nb units …)
    },
    {
      // Next frame data here, same format as above
    }
  ],
  "loading_stats": [
    {
      "name": "assets_level_1",
      "total_time": 7850, // in us
      /.../
    },
    {
      "name": "victory_screen",
      "total_time": 554, // in us
      /.../
    }

  ],
  /.../, // You can add custom fields here
}

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。