Автоматизировать тестирование игр может быть сложно, если игровые приложения разработаны на основе разных UI-фреймворков. Тесты игрового цикла позволяют интегрировать собственные тесты с Test Lab и легко запускать их на выбранных устройствах. В этом руководстве описывается, как подготовить тест игрового цикла для запуска с помощью Firebase Test Lab .
О тестах игрового цикла
Что такое тест игрового цикла?
Тест игрового цикла имитирует действия реального игрока, чтобы быстро и масштабируемо проверить, насколько хорошо ваша игра работает для пользователей. Цикл — это полное или частичное выполнение теста в вашем игровом приложении. Вы можете запустить тест игрового цикла локально на симуляторе или на наборе устройств в Test Lab . Тесты игрового цикла можно использовать для:
- Проходите игру так, как это делает конечный пользователь. Вы можете либо запрограммировать действия пользователя, либо позволить ему бездействовать, либо заменить его искусственным интеллектом (например, если вы реализовали искусственный интеллект в гоночной игре, то можете поручить водителю-ИИ управлять действиями пользователя).
- Запустите игру с максимальными настройками качества, чтобы узнать, какие устройства ее поддерживают.
- Запустите технический тест, например, скомпилируйте несколько шейдеров, выполните их и проверьте, соответствует ли результат ожидаемому.
Шаг 1 : Зарегистрируйте пользовательскую схему URL-адресов Test Lab
- В Xcode выберите целевой проект. 
- Откройте вкладку «Информация» , затем добавьте новый тип URL . 
- В поле «Схемы URL» введите - firebase-game-loop. Вы также можете зарегистрировать пользовательскую схему URL, добавив её в файл конфигурации- Info.plistвашего проекта в любом месте тега- <dict>:- <key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLName</key> <string></string> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLSchemes</key> <array> <string>firebase-game-loop</string> </array> </dict> </array>
Теперь ваше приложение настроено для запуска теста с использованием Test Lab .
Шаг 2 : При необходимости настройте свое приложение
Выполнить несколько циклов
Если вы планируете запустить несколько циклов (т. н. сценариев) в своем тесте, вы должны указать, какие циклы вы хотите запустить в своем приложении во время запуска.
 В делегате приложения переопределите метод application(_:open:options:) : 
Быстрый
func application(_app: UIApplication,
                 open url: URL
                 options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let components = URLComponents(url: url, resolvingAgainstBaseURL: true)!
    if components.scheme == "firebase-game-loop" {
        // ...Enter Game Loop Test logic to override application(_:open:options:).
    }
    return true
}
Objective-C
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary <UIApplicationOpenURLOptionsKey, id> *)options {
  if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
      // ...Enter Game Loop Test logic to override application(_:open:options:).
  }
}
 При запуске нескольких циклов в тесте текущий цикл передаётся в качестве параметра URL-адресу, используемому для запуска приложения. Вы также можете получить номер текущего цикла, проанализировав объект URLComponents используемый для получения пользовательской схемы URL: 
Быстрый
if components.scheme == "firebase-game-loop" {
    // Iterate over all parameters and find the one with the key "scenario".
    let scenarioNum = Int(components.queryItems!.first(where: { $0.name == "scenario" })!.value!)!
    // ...Write logic specific to the current loop (scenarioNum).
}
Objective-C
if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
    // Launch the app as part of a game loop.
    NSURLComponents *components = [NSURLComponents componentsWithURL:url
                                             resolvingAgainstBaseURL:YES];
    for (NSURLQueryItem *item in [components queryItems]) {
        if ([item.name isEqualToString:@"scenario"]) {
            NSInteger scenarioNum = [item.value integerValue];
            // ...Write logic specific to the current loop (scenarioNum).
        }
    }
}
Завершить тест раньше времени
 По умолчанию тест игрового цикла продолжается до достижения тайм-аута в пять минут, даже после того, как все циклы выполнены. По истечении этого времени тест завершается, отменяя все ожидающие циклы. Вы можете ускорить тест или завершить его раньше, вызвав пользовательскую URL-схему Test Lab firebase-game-loop-complete в AppDelegate вашего приложения. Например: 
Быстрый
/// End the loop by calling our custom url scheme.
func finishLoop() {
    let url = URL(string: "firebase-game-loop-complete://")!
    UIApplication.shared.open(url)
}
Objective-C
- (void)finishLoop {
  UIApplication *app = [UIApplication sharedApplication];
  [app openURL:[NSURL URLWithString:@"firebase-game-loop-complete://"]
      options:@{}
completionHandler:^(BOOL success) {}];
}
Тест игрового цикла завершает текущий цикл и выполняет следующий. Когда циклов для выполнения больше нет, тест завершается.
Напишите пользовательские результаты теста
 Вы можете настроить тест Game Loop для записи результатов в файловую систему вашего устройства. Таким образом, при запуске теста Test Lab сохранит файлы результатов в каталоге GameLoopsResults на вашем тестовом устройстве (который вам необходимо создать самостоятельно). По завершении теста Test Lab переместит все файлы из каталога GameLoopResults в контейнер вашего проекта. При настройке теста учитывайте следующее:
- Все файлы результатов загружаются независимо от типа, размера или количества файлов. 
- Test Lab не обрабатывает результаты теста, пока все циклы в нём не завершатся. Поэтому, если ваш тест содержит несколько циклов, записывающих выходные данные, убедитесь, что вы добавляете их в отдельный файл результатов или создаете отдельный файл результатов для каждого цикла. Это позволит избежать перезаписи результатов предыдущего цикла. 
Чтобы настроить тест для записи пользовательских результатов теста:
- В каталоге - Documentsвашего приложения создайте каталог с именем- GameLoopResults.
- В любом месте кода вашего приложения (например, в делегате приложения) добавьте следующее: - Быстрый- /// Write to a results file. func writeResults() { let text = "Greetings from game loops!" let fileName = "results.txt" let fileManager = FileManager.default do { let docs = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let resultsDir = docs.appendingPathComponent("GameLoopResults") try fileManager.createDirectory( at: resultsDir, withIntermediateDirectories: true, attributes: nil) let fileURL = resultsDir.appendingPathComponent(fileName) try text.write(to: fileURL, atomically: false, encoding: .utf8) } catch { // ...Handle error writing to file. } }- Objective-C- /// Write to a results file. - (void)writeResults:(NSString *)message { // Locate and create the results directory (if it doesn't exist already). NSFileManager *manager = [NSFileManager defaultManager]; NSURL* url = [[manager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL* resultsDir = [url URLByAppendingPathComponent:@"GameLoopResults" isDirectory:YES]; [manager createDirectoryAtURL:resultsDir withIntermediateDirectories:NO attributes:nil error:nil]; // Write the result message to a text file. NSURL* resultFile = [resultsDir URLByAppendingPathComponent:@"result.txt"]; if ([manager fileExistsAtPath:[resultFile path]]) { // Append to the existing file NSFileHandle *handle = [NSFileHandle fileHandleForWritingToURL:resultFile error:nil]; [handle seekToEndOfFile]; [handle writeData:[message dataUsingEncoding:NSUTF8StringEncoding]]; [handle closeFile]; } else { // Create and write to the file. [message writeToURL:resultFile atomically:NO encoding:NSUTF8StringEncoding error:nil]; } }
Шаг 3 : Подпишите свое приложение
- Убедитесь, что все артефакты в приложении подписаны. Например, это можно сделать через Xcode, указав параметры подписи, такие как профиль подготовки и идентификатор. Подробнее см.: Apple Codesigning 
Шаг 4 : Упакуйте приложение для загрузки
Создайте файл IPA для своего приложения (вам понадобится найти его позже).
- В раскрывающемся меню выберите «Продукт» > «Архив» . Выберите последний архив и нажмите «Распространить приложение» . 
- В появившемся окне нажмите Разработка > Далее . 
- Нажмите «Экспорт» , затем введите каталог, в который вы хотите загрузить IPA-файл вашего приложения. 
Шаг 5 : Проверьте подпись приложения
-  Проверьте подпись приложения, распаковав файл .ipa и выполнив codesign --verify --deep --verbose /path/to/MyApp.app, где «MyApp» — имя приложения в распакованной папке (различается для каждого проекта). Ожидаемый результат:MyApp.app: valid on disk.
Шаг 6 : Запустите тест локально
Вы можете запустить тест локально, чтобы проверить его поведение перед запуском в Test Lab . Для локального тестирования загрузите игровое приложение в симулятор и выполните:
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://
- Узнать UDID вашего симулятора можно, выполнив команду - instruments -s devices.
- Если запущен только один симулятор, введите специальную строку - "booted"вместо SIMULATOR_UDID .
 Если ваш тест содержит несколько циклов, вы можете указать, какой цикл вы хотите запустить, передав номер цикла флагу scenario . Обратите внимание, что при локальном запуске теста можно запустить только один цикл за раз. Например, если вы хотите запустить циклы 1, 2 и 5, необходимо выполнить отдельную команду для каждого цикла: 
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=1xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=2xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=5
Следующие шаги
Запустите тест с помощью консоли Firebase или интерфейса командной строки gcloud .