Начните с тестов Game Loop

Автоматизировать тестирование игр может быть сложно, если игровые приложения разработаны на основе разных UI-фреймворков. Тесты игрового цикла позволяют интегрировать собственные тесты с Test Lab и легко запускать их на выбранных устройствах. Тест игрового цикла запускает тест через ваше игровое приложение, имитируя действия реального игрока. В этом руководстве показано, как запустить тест игрового цикла, а затем просматривать и управлять результатами в консоли Firebase .

В зависимости от вашего игрового движка вы можете реализовать тесты с одним или несколькими циклами. Цикл — это полное или частичное выполнение теста в вашем игровом приложении. Игровые циклы можно использовать для:

  • Проведите уровень вашей игры так, как это сделал бы конечный пользователь. Вы можете либо запрограммировать действия пользователя, либо позволить ему бездействовать, либо заменить его ИИ, если это имеет смысл для вашей игры (например, у вас есть гоночное приложение с уже реализованным ИИ. Вы можете легко поручить ИИ-водителю управлять действиями пользователя).
  • Запустите игру с максимальными настройками качества, чтобы проверить, поддерживают ли их устройства.
  • Проведите технический тест (скомпилируйте несколько шейдеров, выполните их, проверьте, соответствует ли вывод ожидаемому и т. д.).

Тест игрового цикла можно запустить на одном тестовом устройстве, наборе тестовых устройств или в Test Lab . Однако мы не рекомендуем запускать тесты игрового цикла на виртуальных устройствах, поскольку частота кадров на них ниже, чем на физических.

Прежде чем начать

Чтобы реализовать тест, необходимо сначала настроить приложение для тестов игрового цикла.

  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>

    Это позволяет Test Lab запускать вашу игру, активируя ее с определенным намерением.

  2. В свой код (мы рекомендуем внутри объявления метода onCreate ) добавьте следующее:

    Kotlin

    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
    }

    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
    }

    Это позволяет вашей активности проверять намерение, которое её запускает. Вы также можете добавить этот код позже, если захотите (например, после первоначальной загрузки игрового движка).

  3. Рекомендуется: в конце теста добавить:

    Kotlin

    yourActivity.finish()

    Java

    yourActivity.finish();

    Это закроет ваше приложение после завершения теста игрового цикла. Тест использует пользовательский интерфейс вашего приложения для запуска следующего цикла, и закрытие приложения означает, что тест завершён.

Создайте и запустите тест игрового цикла

После настройки приложения для тестирования игрового цикла вы можете сразу создать тест и запустить его в игровом приложении. Вы можете запустить тест в Test Lab , используя консоль Firebase или интерфейс командной строки (CLI) gcloud , либо на локальном устройстве с помощью Test Loop Manager .

Запустить на локальном устройстве

Test Loop Manager от Test Lab — это приложение с открытым исходным кодом, которое помогает интегрировать тесты игровых циклов и запускать их на локальных устройствах. Оно также позволяет вашей команде по контролю качества запускать те же игровые циклы на своих устройствах.

Чтобы запустить тест на локальном устройстве с помощью Test Loop Manager:

  1. Загрузите Test Loop Manager на телефон или планшет и установите его, выполнив:
    adb install testloopmanager.apk
  2. Откройте приложение Test Loop Apps на телефоне или планшете. Приложение отобразит список приложений на вашем устройстве, которые можно запустить с игровыми циклами. Если вы не видите здесь своего игрового приложения, убедитесь, что ваш фильтр намерений соответствует фильтру, описанному в первом шаге раздела «Перед началом работы» .
  3. Выберите игровое приложение, а затем укажите количество циклов, которые нужно запустить. Примечание: на этом этапе вы можете выбрать запуск подмножества циклов вместо одного. Подробнее об одновременном запуске нескольких циклов см. в разделе «Дополнительные функции» .
  4. Нажмите «Запустить тест» . Тест начнётся немедленно.

Запуск в Test Lab

Вы можете запустить тест игрового цикла в Test Lab используя консоль Firebase или интерфейс командной строки gcloud. Прежде чем начать, откройте консоль Firebase и создайте проект, если вы ещё этого не сделали.

Используйте консоль Firebase

  1. В консоли Firebase нажмите Test Lab на левой панели.
  2. Нажмите «Выполнить первый тест» (или «Выполнить тест», если в вашем проекте ранее уже выполнялось тестирование).
  3. Выберите «Игровой цикл» в качестве типа теста, затем нажмите «Продолжить» .
  4. Нажмите «Обзор» и выберите .apk файл вашего приложения. Примечание: на этом этапе вы можете выбрать запуск подмножества циклов вместо одного. Подробнее об одновременном запуске нескольких циклов см. в разделе «Дополнительные функции» .
  5. Нажмите «Продолжить» .
  6. Выберите физические устройства, которые вы будете использовать для тестирования своего приложения.
  7. Нажмите «Начать тесты» .

Дополнительную информацию о начале работы с консолью Firebase см. в разделе Начало тестирования с помощью консоли Firebase .

Используйте командную строку gcloud (CLI)

  1. Если вы еще этого не сделали, загрузите и установите Google Cloud SDK.

  2. Войдите в интерфейс командной строки gcloud, используя свою учетную запись Google:

    gcloud auth login

  3. Настройте свой проект Firebase в gcloud, где PROJECT_ID — это идентификатор вашего проекта Firebase:

    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 см. в разделе Начало тестирования из командной строки gcloud.

Дополнительные функции

Test Lab предлагает несколько дополнительных функций, позволяющих дополнительно настраивать тесты, включая возможность записи выходных данных, поддержку нескольких игровых циклов и метки для связанных циклов.

Записать выходные данные

Ваш тест игрового цикла может записывать выходные данные в файл, указанный в методе launchIntent.getData() . После запуска теста вы можете получить доступ к этим выходным данным в разделе Test Lab консоли Firebase (см. пример файла выходных данных теста игрового цикла ).

Test Lab следует рекомендациям по обмену файлами между приложениями, описанным в разделе «Общий доступ к файлам» . В методе onCreate() вашей активности, где находится ваше намерение, вы можете проверить выходной файл данных, выполнив следующий код:

Kotlin

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

Java

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

Если вы хотите записать данные в файл со стороны C++ вашего игрового приложения, вы можете передать дескриптор файла вместо пути к файлу:

Kotlin

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

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

С++

#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);
}

Пример выходного файла

Вы можете использовать файлы выходных данных (отформатированные, как в примере ниже) для отображения результатов тестирования игрового цикла в разделе Test Lab консоли 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-м уровне, вы можете напрямую запустить этот игровой цикл, чтобы воспроизвести сбой и протестировать исправление ошибки.

Чтобы ваше приложение могло запускать несколько циклов одновременно:

  • Если вы проводите тест с помощью Test Loop Manager:

    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, укажите список номеров циклов с помощью флага --scenario-numbers . Например, --scenario-numbers=1,3,5 запускает циклы 1, 3 и 5.

  • Если вы пишете на C++ и хотите изменить поведение цикла, передайте следующее дополнение в свой собственный код C++:

    Kotlin

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

    Java

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

    Теперь вы можете изменить поведение вашего цикла на основе полученного значения int .

Метка игровых циклов

Пометив игровые циклы одной или несколькими метками сценариев, вы и ваша команда по контролю качества сможете легко запустить набор связанных игровых циклов (например, «все игровые циклы совместимости») и протестировать их в единой матрице. Вы можете создать собственные метки или использовать готовые метки, предлагаемые Test Lab :

  • com.google.test.loops.player_experience : циклы, используемые для воспроизведения реального игрового опыта пользователя. Цель тестирования с помощью этих циклов — выявить проблемы, с которыми столкнулся бы реальный пользователь во время игры.
  • com.google.test.loops.gpu_compatibility : циклы, используемые для тестирования проблем, связанных с графическим процессором. Цель тестирования с помощью этих циклов — выполнить код графического процессора, который может работать некорректно в рабочей среде, чтобы выявить проблемы с оборудованием и драйверами.
  • com.google.test.loops.compatibility : циклы, используемые для тестирования широкого спектра проблем совместимости, включая проблемы ввода-вывода и проблемы OpenSSL.
  • com.google.test.loops.performance : циклы для тестирования производительности устройства. Например, игра может запускаться на самых высоких графических настройках, чтобы проверить поведение нового устройства.

Чтобы ваше приложение могло запускать циклы с одинаковой меткой:

  • Если вы проводите тест с помощью Test Loop Manager:

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

Поддержка лицензирования приложений

Test Lab поддерживает приложения, использующие службу лицензирования приложений , предлагаемую Google Play. Для успешной проверки лицензирования при тестировании приложения в Test Lab необходимо опубликовать приложение в производственном канале в Play Маркете. Чтобы протестировать приложение в альфа- или бета-канале с помощью Test Lab , удалите проверку лицензирования перед загрузкой приложения в Test Lab .

Известные проблемы

Тесты игрового цикла в Test Lab имеют следующие известные проблемы:

  • Некоторые сбои не поддерживают обратную трассировку. Например, некоторые сборки релиза могут подавлять вывод процесса debuggerd с помощью prctl(PR_SET_DUMPABLE, 0) . Подробнее см. в статье debuggerd .
  • Уровень API 19 в настоящее время не поддерживается из-за ошибок прав доступа к файлам.