Как начать работу с тестами игровых циклов для iOS

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

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

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

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

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

  • Запустите игру с самыми высокими настройками качества, чтобы узнать, какие устройства ее поддерживают.

  • Выполните технический тест, например скомпилируйте несколько шейдеров, запустите их и проверьте, соответствуют ли результаты ожиданиям.

Шаг 1. Зарегистрируйте специальную схему URL для приложения "Test Lab"

Сначала зарегистрируйте в приложении специальную схему URL для Firebase Test Lab:

  1. В Xcode выберите цель проекта.

  2. Перейдите на вкладку Информация и добавьте новый тип URL.

  3. В поле URL Schemes (Схемы 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 (необязательно). Настройте приложение для запуска нескольких циклов

Если в приложении зарегистрировано несколько специальных схем URL и вы планируете запустить несколько циклов (сценариев) в тесте, укажите, какие циклы должны выполняться в приложении при запуске.

В делегате приложения переопределите метод application(_:open:options:):

Swift

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 &lt;UIApplicationOpenURLOptionsKey, id&gt; *)options {
  if ([url.scheme isEqualToString:(@"firebase-game-loop")]) {
      // ...Enter Game Loop Test logic to override application(_:open:options:).
  }
}

Если в тесте выполняется несколько циклов, текущий цикл передается в качестве параметра в URL, используемый для запуска приложения. Вы также можете получить номер текущего цикла, проанализировав объект URLComponents, используемый для получения специальной схемы URL:

Swift

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

Шаг 3. Создайте и запустите тест

После регистрации специальной схемы URL для приложения "Test Lab" вы можете запустить тест в консоли Firebase или с помощью бета-версии gcloud CLI. Если вы ещё этого не сделали, создайте IPA-файл для своего приложения (он понадобится вам позже).

Как запустить тест в консоли Firebase

  1. В консоли Firebase выберите DevOps & Engagement (DevOps и вовлечение) > Test Lab (Лаборатория тестирования).

  2. Нажмите Run Your First Test (Запустить первый тест) > Run an iOS Game Loop (Запустить игровой цикл iOS).

  3. В разделе Загрузка приложения нажмите Обзор и выберите IPA-файл приложения. Если вы ещё не создали его, узнайте, как это сделать.

  4. Необязательно. Если вы хотите запустить несколько циклов(сценариев) одновременно или выбрать определенные циклы, введите их номера в поле Scenarios (Сценарии).

    Например, если вы введете 1-3, 5, Test Lab выполнит циклы 1, 2, 3 и 5. По умолчанию (если вы ничего не введете в поле Scenarios (Сценарии)) Test Lab выполняет только цикл 1.

  5. В разделе Устройства выберите одно или несколько физических устройств, на которых хотите протестировать приложение, и нажмите Начать тестирование.

Как запустить тест с помощью бета-версии gcloud CLI

  1. Если вы ещё не настроили локальную среду gcloud SDK, сделайте это, а затем установите бета-компонент gcloud.

  2. Выполните команду gcloud beta firebase test ios run и используйте следующие флаги, чтобы настроить запуск:

Пометки для тестов с игровым циклом
--type

Обязательный параметр. Указывает тип тестирования iOS, которое вы хотите запустить. Вы можете указать типы тестирования xctest (по умолчанию) или game-loop.

--app

Обязательный параметр. Абсолютный путь (Google Cloud Storage или файловая система) к IPA-файлу приложения. Этот флаг действителен только при запуске тестов игрового цикла.

--scenario-numbers

Циклы (сценарии), которые вы хотите запустить в приложении. Можно указать один цикл, список циклов или диапазон циклов. По умолчанию используется значение 1.

Например, --scenario-numbers=1-3,5 выполняет циклы 1, 2, 3 и 5.

--device-model

Физическое устройство, на котором вы хотите запустить тест. Ознакомьтесь со списком доступных устройств.

--timeout

Максимальная продолжительность тестирования. Вы можете указать целое число, обозначающее продолжительность в секундах, или целое число и перечисление, обозначающее продолжительность в более крупных единицах времени.

Пример:

  • --timeout=200 принудительно завершает тест, если он выполняется дольше 200 секунд.
  • --timeout=1h принудительно завершает тест, если он выполняется дольше часа.

Например, следующая команда запускает тест игрового цикла, который выполняет циклы 1, 4, 6, 7 и 8 на iPhone 8 Plus:

gcloud beta firebase test ios run
 --type game-loop --app path/to/my/App.ipa --scenario-numbers 1,4,6-8
 --device-model=iphone8plus

Дополнительную информацию о gcloud CLI можно найти в справочной документации.

Как запустить локальное тестирование

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

xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://
  • Чтобы узнать UDID симулятора, выполните команду instruments -s devices.

  • Если запущен только один симулятор, вместо SIMULATOR_UDID введите специальную строку "booted".

Если в тесте несколько циклов, вы можете указать, какой из них нужно запустить, передав номер цикла флагу scenario. Обратите внимание, что при локальном тестировании можно запустить только один цикл. Например, если вы хотите запустить циклы 1, 2 и 5, вам нужно выполнить отдельную команду для каждого цикла:

xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=1
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=2
xcrun simctl openurl SIMULATOR_UDID firebase-game-loop://?scenario=5

Как досрочно завершить тестирование

По умолчанию тестирование игрового цикла продолжается до истечения пятиминутного тайм-аута, даже если все циклы были выполнены. По истечении времени ожидания тест завершается, а все ожидающие циклы отменяются. Вы можете ускорить или досрочно завершить тестирование, вызвав специальную схему URL Test Lab (firebase-game-loop-complete) в AppDelegate приложения. Пример:

Swift

/// 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) {}];
}

Тест с игровым циклом завершает текущий цикл и выполняет следующий. Когда циклы закончатся, тестирование завершится.

Как записать результаты собственного теста

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

  • Все файлы результатов загружаются независимо от типа, размера или количества.

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

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

  1. В каталоге Documents приложения создайте каталог с названием GameLoopResults.

  2. Добавьте в код приложения (например, в делегат) следующий фрагмент:

    Swift

    /// 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];
        }
    }