Как начать тестирование с игровым циклом

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

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

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

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

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

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

Примеры

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

Когда Test Lab запускает сценарий 1, игра начинает прохождение с ИИ, отслеживает время отрисовки кадров и использование памяти, передает эти показатели в пользовательский выходной файл журнала (logFile) и по завершении вызывает finish(). Test Lab сохраняет файл журнала и делает его доступным для вас в Cloud Storage и консоли Firebase.

Примеры использования

В зависимости от игрового движка вы можете реализовать тесты с одним или несколькими циклами. Цикл – это полное или частичное тестирование мобильной игры. Игровые циклы можно использовать, чтобы:

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

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

Подготовка

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

  1. Добавьте в манифест приложения новый фильтр интентов для activity:

    <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 или интерфейса командной строки gcloud, а также на локальном устройстве с помощью Менеджера тестирования.

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

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

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

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

Бег в течение Test Lab

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

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

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

Подробнее о том, Firebase как начать работу с консолью Firebase…

Как использовать интерфейс командной строки gcloud

  1. Если вы ещё этого не сделали, скачайте и установите Google Cloud SDK.

  2. Войдите в gcloud CLI, используя аккаунт 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 CLI…

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

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

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

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

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

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

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

      Этот объект Intent содержит целевой цикл в виде целочисленного параметра. В поле android:value можно указать целое число от 1 до 1024 (максимальное количество циклов, разрешенное для одного теста). Обратите внимание, что циклы индексируются начиная с 1, а не с 0.

    2. В приложении "Менеджер тестирования" появится экран, на котором можно выбрать нужные циклы. Если вы выберете несколько циклов, они будут запускаться по очереди.

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

  • Если вы запускаете тест с помощью gcloud CLI, укажите список номеров циклов, используя флаг --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: циклы 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. В приложении Test Loop Manager введите один или несколько ярлыков в поле Labels (Ярлыки).

  • Если вы проводите тестирование в консоли Firebase, введите один или несколько ярлыков в поле Ярлыки.

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

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

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

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

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

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