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

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

Что такое тест игрового цикла?

Поскольку игровые приложения работают с различными фреймворками и движками пользовательского интерфейса, такими как Unity, Unreal или код C++ OpenGL/Vulkan, стандартные инструменты автоматизации не могут проверять или взаимодействовать с элементами пользовательского интерфейса. Тест игрового цикла позволяет интегрировать нативные тесты движка с Test Lab , запуская игру с намерением, содержащим номер конкретного сценария. Этот номер сценария заставляет ваше приложение запускать предварительно запрограммированную логику, симуляции ИИ или проверки производительности непосредственно в игровом движке.

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

Чем игровые циклы отличаются от инструментальных тестов?

  • Инструментальные тесты (такие как Espresso или UI Automator) используют внешние фреймворки для управления взаимодействием с пользовательским интерфейсом, нажатиями кнопок и проверкой состояния приложения в иерархии представлений Android извне вашего приложения.
  • Тесты игрового цикла делегируют логику тестирования вашему приложению. Ваше приложение запускает собственный внутренний цикл тестирования или симуляцию без внешнего взаимодействия с пользовательским интерфейсом, в то время как Test Lab запускает ваш сценарий и собирает результаты тестирования.

Примеры

Предположим, вы хотите убедиться, что ваша игра поддерживает частоту кадров не менее 60 кадров в секунду на различных физических устройствах. Вы можете реализовать сценарий 1 в коде своей игры, чтобы загрузить самый сложный с визуальной точки зрения уровень и позволить игроку с искусственным интеллектом или автоматизированному скрипту пройти его в течение двух минут.

Когда Test Lab запускает сценарий 1 , ваша игра запускает прохождение под управлением ИИ, отслеживает время отрисовки кадров и использование памяти, передает эти метрики в пользовательский файл журнала ( logFile ) и вызывает finish() по завершении. Затем Test Lab захватывает файл журнала и делает его доступным для вас в Cloud Storage и консоли 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();

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

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

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

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

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

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

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

Запуск в Test Lab

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

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

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

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

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

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

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

Когда Test Lab запускает вашу игру, он указывает URL-адрес, ведущий к локальному файлу результатов на тестовом устройстве, доступному с помощью launchIntent.getData() . Ваша игра может записывать результаты тестов, метрики производительности и логи ( logFile ) непосредственно в это место во время выполнения теста.

В logFile можно записывать любое произвольное содержимое , например, обычный текст, данные CSV, время отрисовки кадров или пользовательские диагностические отчеты. Нет никаких ограничений или требований к форматированию записываемого содержимого; файл сохраняется после завершения теста и загружается в ваше хранилище Cloud Storage вместе с другими тестовыми артефактами.

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

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

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

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

Хотя Test Lab не требует определенного формата для вашего logFile , структурирование выходных данных в формате JSON, соответствующем приведенной ниже схеме, позволяет консоли Firebase ( https://console.firebase.google.com/project/_/testlab ) анализировать и отображать сводные метрики для вашего теста игрового цикла.

Основные сведения об этом примере:

  • Кто предоставляет данные: Вы (разработчик тестов) несете ответственность за расчет и запись всех метрик в этот файл из кода вашей игры. Test Lab не собирает и не генерирует эти данные автоматически.
  • Добавление пользовательских полей: Области, отмеченные как /.../ указывают, где можно добавить произвольные пользовательские пары ключ-значение (например, "rendered_vertices": 12000 или "ai_error_count": 0 ). При записи файла из приложения вы можете добавить любое допустимое свойство JSON в выходные данные, если имена полей не конфликтуют с полями схемы, такими как name или start_timestamp .
  • Где найти выходной файл: После завершения теста исходный выходной файл сохраняется и доступен для загрузки в хранилище результатов вашего проекта Cloud Storage ( GCS ) в каталоге артефактов тестового запуска. Вы также можете просмотреть и проверить обработанные выходные данные на странице Test Lab в консоли Firebase .

Ниже приведён пример структурированного выходного файла в формате JSON:

{
  "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. В приложении «Менеджер тестовых циклов» появляется экран выбора, позволяющий выбрать, какие циклы вы хотите запустить. Если вы выберете несколько циклов, каждый цикл будет запущен последовательно после завершения предыдущего.

  • Если вы запускаете тест с помощью консоли Firebase , введите список или диапазон номеров циклов в поле «Сценарии» .

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

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

  • Если вы запускаете тест с помощью менеджера тестовых циклов:

    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. В приложении «Менеджер тестовых циклов» введите одну или несколько меток в поле «Метки» .

  • Если вы запускаете тест с помощью консоли Firebase , введите одну или несколько меток в поле «Метки» .

  • Если вы запускаете тест с помощью CLI gcloud, укажите одну или несколько меток сценария, используя флаг --scenario-labels (например, --scenario-labels=performance,gpu ).

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

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

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

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

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