获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

开始使用游戏循环测试

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

当游戏应用程序构建在不同的 UI 框架上时,自动化游戏测试可能会很困难。游戏循环测试允许您将本机测试与测试实验室集成,并在您选择的设备上轻松运行它们。游戏循环测试通过您的游戏应用程序运行您的测试,同时模拟真实玩家的动作。本指南向您展示如何运行 Game Loop 测试,然后在 Firebase 控制台中查看和管理您的测试结果。

根据您的游戏引擎,您可以使用单个或多个循环实现测试。循环是您对游戏应用程序的测试的全部或部分运行。游戏循环可用于:

  • 以最终用户玩游戏的方式运行游戏关卡。您可以编写用户的输入脚本,让用户处于空闲状态,或者如果它在您的游戏中有意义(例如,假设您有一个赛车游戏应用程序并且已经实现了 AI。您可以很容易让 AI 驱动程序负责用户的输入)。
  • 以最高质量设置运行您的游戏,看看设备是否支持它。
  • 运行技术测试(编译多个着色器,执行它们,检查输出是否符合预期等)。

您可以在单个测试设备、一组测试设备或测试实验室上运行游戏循环测试。但是,我们不建议在虚拟设备上运行 Game Loop 测试,因为它们的图形帧速率低于物理设备。

在你开始之前

要实施测试,您必须首先配置您的应用程序以进行游戏循环测试。

  1. 在您的应用清单中,为您的活动添加一个新的意图过滤器:

    <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>
    

    这允许测试实验室通过以特定意图触发游戏来启动您的游戏。

  2. 在您的代码中(我们建议在onCreate方法声明中),添加以下内容:

    Java

    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+KTX

    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
    }

    这允许您的活动检查启动它的意图。如果您愿意,您也可以稍后添加此代码(例如,在最初加载您的游戏引擎之后)。

  3. 推荐:在测试结束时,添加:

    Java

    yourActivity.finish();

    Kotlin+KTX

    yourActivity.finish()

    当游戏循环测试完成时,这会关闭您的应用程序。测试依赖于您的应用程序的 UI 框架来启动下一个循环,并关闭您的应用程序告诉它测试已完成。

创建并运行游戏循环测试

为游戏循环测试配置您的应用程序后,您可以立即创建一个测试并在您的游戏应用程序中运行它。您可以选择使用Firebase 控制台gcloud 命令行界面 (CLI)在测试实验室中运行测试,或者使用测试循环管理器在本地设备上运行测试。

在本地设备上运行

Test Lab 的Test Loop Manager是一款开源应用程序,可帮助您集成 Game Loop 测试并在本地设备上运行它们。它还允许您的质量保证团队在他们的设备上运行相同的游戏循环。

要使用测试循环管理器在本地设备上运行测试:

  1. 在手机或平板电脑上下载Test Loop Manager并运行以下命令进行安装:
    adb install testloopmanager.apk
  2. 在您的设备上,打开手机或平板电脑上的Test Loop Apps应用程序。该应用程序会显示您设备上可以通过游戏循环运行的应用程序列表。如果您在此处未看到您的游戏应用,请确保您的意图过滤器与开始之前部分的第一步中描述的过滤器相匹配。
  3. 选择您的游戏应用程序,然后选择您要运行的循环数。注意:在此步骤中,您可以选择运行循环子集而不是仅运行一个循环。有关一次运行多个循环的更多信息,请参阅可选功能。
  4. 单击运行测试。您的测试立即开始运行。

在测试实验室运行

您可以使用Firebase 控制台gcloud CLI 在测试实验室中运行游戏循环测试。在开始之前,如果您还没有,请打开Firebase 控制台并创建一个项目。

使用 Firebase 控制台

  1. 在 Firebase 控制台中,单击左侧面板中的测试实验室
  2. 单击运行您的第一个测试(如果您的项目之前运行过测试,则单击运行测试)。
  3. 选择Game Loop作为测试类型,然后单击Continue
  4. 单击Browse ,然后浏览到您的应用的.apk文件。注意:在此步骤中,您可以选择运行循环子集而不是仅运行一个循环。有关一次运行多个循环的更多信息,请参阅可选功能。
  5. 单击继续
  6. 选择用于测试您的应用的物理设备。
  7. 单击开始测试

如需详细了解如何开始使用 Firebase 控制台,请参阅使用 Firebase 控制台开始测试。

使用 gcloud 命令行 (CLI)

  1. 如果您还没有,请下载并安装Google Cloud SDK。

  2. 使用您的 Google 帐户登录 gcloud CLI:

    gcloud auth login

  3. 在 gcloud 中设置您的 Firebase 项目,其中PROJECT_ID是您的 Firebase 项目的 ID:

    gcloud config set project PROJECT_ID
    
  4. 运行你的第一个测试:

    gcloud firebase test android run \
     --type=game-loop --app=<var>path-to-apk</var> \
     --device model=herolte,version=23
    

有关开始使用 gcloud CLI 的更多信息,请参阅从 gcloud 命令行开始测试。

可选功能

测试实验室提供了几个可选功能,可让您进一步自定义测试,包括编写输出数据的能力、支持多个游戏循环以及相关循环的标签。

写入输出数据

您的游戏循环测试可以将输出写入launchIntent.getData()方法中指定的文件。运行测试后,您可以在 Firebase 控制台的测试实验室部分访问此输出数据(请参阅游戏循环测试输出文件示例)。

测试实验室遵循共享文件中描述的在应用程序之间共享文件的最佳实践。在您的意图所在的活动的onCreate()方法中,您可以通过运行以下代码来检查您的数据输出文件:

Java

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

Kotlin+KTX

val launchIntent = intent
val logFile = launchIntent.data
logFile?.let {
    Log.i(TAG, "Log file ${it.encodedPath}")
    // ...
}

如果要从游戏应用程序的 C++ 端写入文件,可以传入文件描述符而不是文件路径:

Java

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+KTX

val launchIntent = intent
val logFile = launchIntent.data
var fd = -1
logFile?.let {
    Log.i(TAG, "Log file ${it.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);
}

输出文件示例

您可以使用输出数据文件(格式如下例)在 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
}

多个游戏循环

您可能会发现在您的应用程序中运行多个游戏循环很有用。循环是游戏应用程序从头到尾的完整运行。例如,如果您的游戏中有多个关卡,您可能希望有一个游戏循环来启动每个关卡,而不是让一个循环遍历所有关卡。这样,如果您的应用程序在 32 级崩溃,您可以直接启动该游戏循环以重现崩溃并测试错误修复。

要使您的应用程序一次运行多个循环:

  • 如果您正在使用测试循环管理器运行测试:

    1. 将以下行添加到应用程序清单的<application>元素内:

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

      此启动意图包含目标循环作为整数参数。在android:value字段中,您可以指定一个从 1 到 1024 的整数(单个测试允许的最大循环数)。请注意,循环的索引从 1 开始,而不是 0。

    2. 在 Test Loop Manager 应用程序中,会出现一个选择屏幕,允许您选择要运行的循环。如果选择多个循环,则每个循环在前一个循环完成后依次启动。

  • 如果您使用 Firebase 控制台运行测试,请在“场景”字段中输入一个列表或循环编号范围。

  • 如果您使用 gcloud CLI 运行测试,请使用--scenario-numbers标志指定循环编号列表。例如, --scenario-numbers=1,3,5运行循环 1、3 和 5。

  • 如果您正在编写 C++ 并希望更改循环的行为,请将以下额外内容传递给您的本机 C++ 代码:

    Java

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

    Kotlin+KTX

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

    您现在可以根据生成的int值更改循环的行为。

标记游戏循环

当您使用一个或多个场景标签标记您的游戏循环时,您和您的 QA 团队可以轻松启动一组相关的游戏循环(例如,“所有兼容性游戏循环”)并在单个矩阵中测试它们。您可以创建自己的标签或使用测试实验室提供的预定义标签:

  • 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 :用于测试设备性能的循环。例如,游戏可能会在最复杂的图形设置下运行,以查看新设备的行为方式。

要使您的应用能够运行具有相同标签的循环:

  • 如果您正在使用测试循环管理器运行测试:

    1. 在您应用的清单中,添加以下元数据行并将LABEL_NAME替换为您选择的标签:

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

      android:value字段中,您可以指定一个范围或一组从 1 到 1024(单个测试允许的最大循环数)的整数来表示您要标记的循环。请注意,循环的索引从 1 开始,而不是 0。例如, android:value="1,3-5"LABEL_NAME应用于循环 1、3、4 和 5。

    2. 在 Test Loop Manager 应用程序的标签字段中输入一个或多个标签。

  • 如果您使用 Firebase 控制台运行测试,请在标签字段中输入一个或多个标签。

  • 如果您使用 gcloud CLI 运行测试,请使用--scenario-labels标志(例如--scenario-labels=performance,gpu )指定一个或多个场景标签。

应用许可支持

测试实验室支持使用 Google Play 提供的应用许可服务的应用。要在使用测试实验室测试您的应用时成功检查许可,您必须将应用发布到 Play 商店的生产渠道。要使用测试实验室在 Alpha 或 Beta 通道中测试您的应用,请在将您的应用上传到测试实验室之前删除许可检查。

已知的问题

测试实验室中的游戏循环测试存在以下已知问题:

  • 一些崩溃不支持回溯。例如,某些发布版本可能会使用prctl(PR_SET_DUMPABLE, 0)抑制debuggerd进程的输出。要了解更多信息,请参阅debuggerd
  • 由于文件权限错误,目前不支持 API 级别 19。