Проверка экспорта PDF из iOS на облачном Mac

Проверка экспорта PDF из iOS на облачном Mac

В iOS-приложении нажали «Экспорт» для отчёта, файл появился в меню «Поделиться», но получатель обнаружил, что в PDF не хватает страницы. В такой ситуации недостаточно проверить, что функция экспорта вернула успешный результат. Тестовое задание должно получить итоговый PDF-файл, проверить то, что можно определить автоматически, а остальное оставить для визуальной проверки. Этот процесс подходит для тестов в симуляторе на облачном Mac, но его можно выполнить и локально.

Сначала определите проверяемый результат

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

Пусть тестовая сборка приложения записывает PDF в свой Documents/export.pdf. Это упрощает проверку и не означает, что готовый продукт должен использовать тот же путь для выдачи файла. Перед каждым запуском удаляйте старый файл, а после завершения экспорта проверяйте, что новый файл существует и не пуст. Иначе результат предыдущего запуска может привести к ложному успеху теста.

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

Извлеките результат текущего запуска из симулятора

Сначала запустите приложение и операцию экспорта в симуляторе с идентификатором, указанным в тестовом задании. По окончании задания найдите контейнер приложения на устройстве с тем же идентификатором. Не определяйте нужное устройство по booted: при одновременно запущенных симуляторах так особенно легко выбрать не то. Идентификатор и имя пакета приложения в примере должны поступать из конфигурации теста:

UDID="替换为测试使用的模拟器标识"
BUNDLE_ID="替换为测试应用的包名"
CONTAINER="$(xcrun simctl get_app_container "$UDID" "$BUNDLE_ID" data)" || exit 1
mkdir -p artifacts
test -s "$CONTAINER/Documents/export.pdf" || exit 1
cp "$CONTAINER/Documents/export.pdf" artifacts/export.pdf
file artifacts/export.pdf

Для работы get_app_container приложение должно быть установлено в указанном симуляторе. Если запрос не удался, сначала проверьте идентификатор устройства, фактически использованного тестом, имя пакета и результат установки приложения, а не переходите к поиску по всему диску. После копирования проверки выполняются над файлом в каталоге задания; для дальнейшего разбора и архивирования используйте именно эту копию, чтобы повторная запись со стороны приложения не изменила проверяемый файл. Вывод file помогает при диагностике, но не заменяет разбор PDF.

Проверьте структуру и текст с помощью PDFKit

Сохраните приведённый ниже код Swift в scripts/check-pdf.swift. Он принимает путь к файлу и ожидаемую фразу, проверяет границы каждой страницы и подтверждает, что фраза встречается хотя бы на одной из них. Ненулевой код завершения позволяет заданию сборки считать автоматическую проверку не пройденной.

import Foundation
import PDFKit

guard CommandLine.arguments.count == 3,
      let pdf = PDFDocument(url: URL(fileURLWithPath: CommandLine.arguments[1])),
      pdf.pageCount > 0 else {
    fputs("PDF is missing or cannot be parsed\n", stderr)
    exit(2)
}

let expected = CommandLine.arguments[2]
var extracted = ""
for index in 0..<pdf.pageCount {
    guard let page = pdf.page(at: index) else {
        fputs("PDF page is missing\n", stderr)
        exit(2)
    }
    let media = page.bounds(for: .mediaBox)
    let crop = page.bounds(for: .cropBox)
    guard media.width > 0, media.height > 0,
          crop.width > 0, crop.height > 0,
          media.contains(crop) else {
        fputs("PDF page bounds are invalid\n", stderr)
        exit(2)
    }
    extracted += page.string ?? ""
}
guard extracted.contains(expected) else {
    fputs("Expected text was not extracted\n", stderr)
    exit(2)
}
print("Checked \(pdf.pageCount) PDF page(s)")

Выполните swift scripts/check-pdf.swift artifacts/export.pdf "Quarterly Report". Замените фразу из примера текстом, который точно присутствует в фиксированных тестовых данных. Если по требованиям страниц должно быть ровно две, добавьте проверку pdf.pageCount == 2: условие «больше нуля» не обнаружит изменение разбивки на страницы. Размеры страниц тоже можно проверять по требованиям продукта, но не стоит без предварительной договорённости считать ошибкой все другие допустимые форматы бумаги.

Извлечение текста не заменяет визуальную проверку

page.string проверяет извлекаемый текст, но не гарантирует, что символы не обрезаны, а диаграммы, изображения и цвета отображаются верно. И наоборот: из сканов и текста, отрисованного как изображение, содержимое может не извлекаться, хотя визуально страницы выглядят нормально. Если проверка текста не прошла, сохраните исходный PDF и выясните, отсутствует ли текст в файле или способ извлечения не подходит для этой страницы. Не удаляйте проверку лишь ради успешного завершения задания.

Определите этап, на котором произошёл сбой

Рекомендуется отдельно записывать для каждого задания результат операции экспорта, результат копирования файла и код завершения PDFKit. Тогда при сбое можно последовательно выяснить: выполнило ли приложение экспорт; появился ли в контейнере файл текущего запуска; разбирается ли копия; соответствуют ли ожиданиям число страниц, их границы и текст. Не ограничивайтесь сообщением «Тест PDF не пройден», иначе для диагностики придётся повторить весь процесс.

Признак Что проверить в первую очередь
Контейнер не найден Идентификатор симулятора, имя пакета, результат установки
Файл пуст Удалён ли старый файл, завершилась ли запись
PDF не разбирается Процесс копирования, формат итогового файла
Текст не совпадает Фиксированные входные данные, содержимое страниц, ограничения извлечения текста

При сбое архивируйте artifacts/export.pdf и журналы теста, но сначала убедитесь, что отчёт не содержит реальных данных пользователей. Если файл может содержать конфиденциальную информацию, используйте синтетический образец, ограничьте доступ к артефактам и удаляйте их по правилам команды. Не загружайте рабочие отчёты в общедоступные артефакты конвейера.

Завершите процесс запуском, результаты которого можно перепроверить

Прежде чем утверждать процедуру приёмки, выполните её дважды подряд с одними и теми же фиксированными входными данными. Оба раза экспорт должен выполняться заново, целевой файл — перезаписываться, а проверки числа страниц и текста — давать одинаковый результат. Затем намеренно уберите ожидаемую фразу и убедитесь, что скрипт завершается с ошибкой. После этого заставьте тестовое приложение не создавать файл и убедитесь, что этап копирования завершается с ошибкой. Эти две негативные проверки исключают ситуацию, когда проверки фактически не выполнялись.

Даже после успешных автоматических проверок откройте PDF с репрезентативными данными и осмотрите переходы между страницами, переносы длинного текста и края диаграмм. Машина проверяет устойчивые требования к структуре и тексту, человек — фактическое отображение. Записывайте эти выводы отдельно, чтобы при следующем сбое экспорта было с чем сравнивать.

Часто задаваемые вопросы

Если PDFKit не извлёк текст, экспорт сломан?

Не обязательно. Текст может быть нарисован как изображение или закодирован шрифтом, который мешает извлечению. Сохраните файл и проверьте отображение.

Как передать PDF из симулятора в задачу проверки?

Приложение записывает его в Documents; затем simctl get_app_container находит каталог данных, откуда файл копируется в артефакты задачи.

Доверьте следующую сборку выделенному облачному Mac.

Сравните модели, регионы и сроки аренды ViewMini, а затем выберите физический узел для своего рабочего процесса.

Настроить облачный Mac

Проверьте параметры устройства и срок аренды.

Три модели доступны с оплатой за день, неделю, месяц или квартал. Перед заказом ознакомьтесь со всеми тарифами.

Модели и цены Настроить облачный Mac