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

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

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

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

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

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

Чтобы реализовать тест, сначала необходимо настроить приложение для тестов 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>

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

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

    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
    }

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

    yourActivity.finish()

    Java

    yourActivity.finish();

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

Создайте и запустите тест Game Loop.

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

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

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

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

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

Запустить в Test Lab

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

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

  1. В консоли Firebase нажмите Test Lab на левой панели.
  2. Нажмите «Запустить первый тест» (или «Запустить тест» , если в вашем проекте ранее выполнялся тест).
  3. Выберите Game Loop в качестве типа теста и нажмите «Продолжить» .
  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 предлагает несколько дополнительных функций, которые позволяют дополнительно настраивать тесты, включая возможность записи выходных данных, поддержку нескольких игровых циклов и метки для связанных циклов.

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

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

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

Kotlin+KTX

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

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

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

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

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