MacroSIP
EnglishРусский中文

Тесты

MacroSIP проверяют три уровня: наборы Swift Testing для MacroSIPCore, сквозной звонок по loopback с pjsua и демонстрационные запуски приложения с имитацией движка. Все три выполняются в CI при каждом push и pull request. Веха считается закрытой, только когда swift build проходит чисто, тесты зелёные, scripts/selftest-call.sh проходит и проверен настоящий звонок (ADR 0006, docs/DEVELOPMENT.md).

Модульные тесты и тесты движка: scripts/test.sh

scripts/test.sh                                  # all suites
scripts/test.sh --filter DialStringTests         # extra arguments go to swift test

Никогда не запускайте голый swift test: Command Line Tools поставляют фреймворк Testing, но SwiftPM сам не находит его плагин макросов. test.sh передаёт путь к плагину явно (-Xswiftc -plugin-path -Xswiftc .../usr/lib/swift/host/plugins/testing). XCTest без Xcode недоступен, поэтому каждый набор использует import Testing.

Наборы в Tests/MacroSIPCoreTests содержат около 210 тестов:

Файл Что проверяет
DialStringTests таблицу разбора (sip:, tel:, простые номера, user@host)
JSONStoreTests, StoreTests, SettingsCompatTests атомарную запись, восстановление после повреждённого файла, круговые обходы хранилищ, декодирование старых settings.json и журнала звонков со значениями по умолчанию, звонки/«Не беспокоить» на время/записи
KeychainTests обёртку с тестовым именем службы
ContactTests нормализацию номеров, сопоставление, сортировку, фото, устаревшие поля
AutoAnswerPolicyTests порядок правил («Не беспокоить», другой звонок, answer-after, режим)
QuickKeyTests модель клавиши, URI перехвата, реестр статусов
RecordingConverterTests WAV в M4A, пары стереоразделения, починку заголовка
EngineTests запуск/остановку, аккаунты, loopback-звонки (движок звонит сам себе), удержание, выключение звука, DTMF, перевод, слушателей TLS
EngineMilestone5Tests конференцию, запись в моно и стерео, отображение статусов на заглушке PIDF, доверие к answer-after
EngineAudioTests таблицу MOS, громкость звонка и мелодии, Opus FEC/DTX, политику обработки голоса, подавление дребезга событий устройств
EngineCallOptionsTests режим одного звонка, сигнал ожидания вызова, способы DTMF, «Не беспокоить»
EngineNetworkTests повтор регистрации с нарастающей паузой, сетевые раунды, исходы re-INVITE, сторожа медиа, сон и пробуждение, с NetworkStub (UDP-регистратор/вызываемый)
Engine262Tests окно портов RTP, rport, DNS SRV с DNS-заглушкой в процессе, правила отклонения, дополнительные кодеки
DirectoryParsingTests, DirectoryServiceTests, DirectoryLDAPTests ленты CSV/JSON и сопоставление номеров, сервис с провайдером-заглушкой, LDAP с сервером NWListener в процессе

Тесты движка работают с null-аудио, привязаны к loopback и используют Engine(monitorNetwork: false), чтобы не зависеть от сети хоста; makeEngine() останавливает движок, оставшийся от упавшего теста, так что один сбой не тянет за собой остальные. TestSupport.swift предоставляет временные каталоги.

Сквозной звонок: scripts/selftest-call.sh

scripts/selftest-call.sh                          # native
SELFTEST_ARCH=x86_64 scripts/selftest-call.sh     # the x86_64 slice under Rosetta 2
SELFTEST_LOG=/tmp/selftest.log SELFTEST_LOG_LEVEL=5 scripts/selftest-call.sh

Скрипт запускает два телефона pjsua из third_party/pjsip/bin/pjsua на портах 5080 и 5081 (null-аудио, автоответ, зацикленный тон 600 Гц, telnet-консоли на 5088 и 5089), собирает macrosip-cli и выполняет macrosip-cli --selftest. Самопроверка (Sources/macrosip-cli/SelfTest*.swift) проверяет на независимой реализации PJSIP: двусторонний RTP, удержание и возобновление (в том числе подряд), DTMF, запись в моно (WAV со слышимым тоном, затем M4A), стереозапись с разделением и заглушённой средней секундой, статусы, управляемые через консоль pjsua (доступен, отошёл, занят), конференцию из двух веток с объединением и разделением и записью, переходящей между ветками, входящие звонки (ответ, отклонение с 486), перевод сразу и с консультацией между двумя телефонами, DTMF через SIP INFO и зарегистрированный аккаунт на SIPStub (UDP-регистратор с digest-вызовом внутри macrosip-cli; каждый авторизованный INVITE должен быть больше 1300 байт без ошибки смены транспорта), имитацию смены сети во время звонка и с зарегистрированным аккаунтом, качество звонка (должно быть good), режим одного звонка, «Не беспокоить», правила отклонения и звонок только с GSM. При SELFTEST_ARCH=x86_64 CLI собирается универсальным, а CLI и оба телефона запускаются через arch -x86_64; pjsua под Rosetta поднимается на несколько секунд дольше, поэтому скрипт опрашивает его консоль до ~15 с. SELFTEST_CLI=<path> использует уже собранный CLI.

Демонстрационные запуски и имитация движка

Debug-приложение (scripts/make-app.sh debug) принимает параметры запуска из Sources/MacroSIPApp/Model/LaunchOptions.swift; release-приложение их игнорирует.

Параметр Эффект
--mock-engine MockEngine вместо PJSIP: регистрация проходит через 1 с (пароль wrong или домен, содержащий fail, дают 403), исходящий звонок соединяется через 2 с
--demo-state <name> заполняет образцы аккаунтов, контактов и записей журнала, подразумевает --mock-engine и временный каталог данных; связка ключей не затрагивается
--data-dir <path> JSON-хранилища в <path> вместо Application Support
--snapshot <prefix> пишет <prefix>-<window>.png каждого окна через --snapshot-after секунд (по умолчанию 6), затем завершается; работает без дисплея
--appearance light|dark принудительно задаёт тему
-AppleLanguages "(ru)" стандартный аргумент macOS; запускает приложение на русском
--log-level 0-6 подробность PJSIP (все сборки)

Демонстрационные состояния по областям (полный список в заголовке Model/DemoState.swift):

Демонстрационная самопроверка. --demo-state selftest управляет моделью так же, как это делают представления, через имитацию движка, и записывает строку PASS или FAIL на каждую проверку: состояния регистрации (один аккаунт зарегистрирован, один с ошибкой 403), состояния вызова и разговора, имя звонящего, определённое по контакту, выключение звука, удержание и возобновление, два звонка с переключением удержания, перевод с консультацией, воспроизведение записи, проверки аккаунтов, сводка состояния и другое. Результат идёт в stdout или, с --snapshot, в <prefix>-selftest.txt, и приложение завершается. --demo-state directory-selftest выполняет только часть, относящуюся к справочнику: подсказки в поле номера, определение звонящего, импорт и слияние.

timeout 60 dist/MacroSIP.app/Contents/MacOS/MacroSIP --demo-state selftest
timeout 60 dist/MacroSIP.app/Contents/MacOS/MacroSIP --demo-state directory-selftest

Строки: scripts/check-strings.sh

Проверяет, что каждый ключ, используемый в Model/Strings.swift, есть и в en.lproj, и в ru.lproj (Localizable.strings или Localizable.stringsdict), что ни в одном файле нет ключей, которые код не использует, что ни один ключ не лежит в обоих файлах сразу, что категории множественного числа полны (one/other для английского, one/few/many/other для русского) и что в русском значении нет подстановок, которых нет в английском. Запускайте после каждого изменения строк.

Снимки экрана

Изображения в docs/screenshots/{light,dark} и docs/screenshots/ru/{light,dark} получены из демонстрационных запусков с --snapshot; отдельного скрипта захвата нет. Типичный захват русского тёмного варианта:

timeout 30 dist/MacroSIP.app/Contents/MacOS/MacroSIP --demo-state contacts \
  --appearance dark -AppleLanguages "(ru)" --snapshot /tmp/shots/contacts --snapshot-after 3

Состояния со звонящим вызовом или открытым листом никогда не завершаются сами, отсюда timeout. Русский текст примерно на 25 % длиннее, поэтому после добавления строк проверяйте строку состояния, кнопки панели звонка и меню выбора аккаунта. Контакты Google в имитации отвечают через 4 с; используйте --snapshot-after 3, чтобы поймать строку «Поиск: …».

CI

.github/workflows/ci.yml выполняется при push и pull request в main, по одному активному запуску на ref.

Задание Раннер Шаги
build macos-15 (arm64) восстанавливает кэш third_party/pjsip и third_party/deps (ключ: хеш build-pjsip.sh и config_site.h), build-pjsip.sh, swift build, test.sh, selftest-call.sh нативно и с SELFTEST_ARCH=x86_64 под Rosetta 2 (устанавливается при необходимости), make-app.sh release, lipo -info на бинарнике; выгружает приложение (14 дней) и универсальную сборку PJSIP (3 дня) как артефакты, а при сбое — журналы сборки
intel macos-15-intel (x86_64), после build скачивает универсальные библиотеки и приложение (хост Intel не может выполнить build-pjsip.sh), проверяет подпись, запускает приложение на 15 с с --data-dir и MACROSIP_LOG_DIR, затем swift build, test.sh и selftest-call.sh нативно

release.yml повторяет шаги arm64 перед сборкой DMG (см. Релизы).

См. также Сборка и Участие.

Другие страницы: Главная · Home (English)