При удалённой отладке клиента iOS сложнее всего воспроизвести не полный обрыв связи, а ситуацию, когда «соединение есть, но всё работает очень медленно». Растёт задержка при установлении соединения, падает скорость отправки данных, часть запросов теряется — в результате возникают повторные отправки, бесконечные попытки и длительное ожидание в интерфейсе. При проведении таких тестов на облачном Mac от HexVM в первую очередь нужно не ограничивать скорость, а отделить тестовый трафик приложения от управляющего трафика SSH, VNC и других служб. Иначе одно слишком широкое правило может перекрыть канал удалённого доступа.
Определите проверяемые сценарии плохой сети
Не используйте формулировку «плохая сеть» в качестве условия теста. Такой сценарий невозможно точно воспроизвести, а эффективность исправления — объективно оценить. Сначала задайте для каждого сценария фиксированные параметры и определите ожидаемое поведение клиента.
| Сценарий | Ограничение входящей и исходящей скорости | Дополнительная задержка | Потеря пакетов | Основные критерии приёмки |
|---|---|---|---|---|
| Базовый | Без ограничений | 0 ms | 0 | Время выполнения и доля успешных запросов |
| Высокая задержка | 8 Mbit/s | 120 ms | 0 | Состояние загрузки, отмена и тайм-ауты |
| Низкая пропускная способность | 1 Mbit/s | 80 ms | 0 | Ход отправки и переход в фоновый режим |
| Нестабильное соединение | 4 Mbit/s | 100 ms | 2% | Максимальное число повторов и идемпотентность |
Эти параметры являются входными данными теста, а не характеристикой качества сети узла. Команде следует корректировать значения с учётом тайм-аутов API, размеров файлов и сетевых условий пользователей, однако в рамках одного цикла регрессионного тестирования параметры должны оставаться неизменными.
Цель тестирования в плохой сети — не доказать, что запрос в конечном счёте завершится успешно. Необходимо убедиться, что при сбое клиент вовремя прекращает ожидание, показывает понятное состояние и не создаёт дублирующиеся данные.
Зафиксируйте базовые показатели без ограничений
Сначала запишите параметры DNS, маршрутизации и отклика конечной точки без каких-либо правил шейпинга. Тестовая конечная точка должна находиться под контролем команды и предоставлять диагностический путь, который не изменяет производственные данные.
export TEST_URL="https://test-endpoint.invalid/health"
scutil --dns | grep 'nameserver\[[0-9]*\]'
route -n get default
networkQuality
curl -sS -o /dev/null \
-w 'code=%{http_code} connect=%{time_connect} tls=%{time_appconnect} total=%{time_total}
' \
"$TEST_URL"
Домен из примера необходимо заменить реальным управляемым тестовым адресом. Зафиксируйте медиану как минимум трёх запросов, а не только лучший результат. Если уже на базовом этапе возникают ошибки DNS, проблемы с сертификатом или аномалии маршрутизации, сначала устраните их. Нельзя маскировать такие неполадки увеличением числа повторных попыток.
Также проверьте, обслуживается ли целевой адрес несколькими IP. Если ограничить только один IP, запрос может разрешиться в другой адрес, из-за чего результаты одного и того же теста будут заметно различаться. Надёжнее подготовить фиксированную точку входа для проверки плохой сети и сохранять результат разрешения имени перед запуском.
Применяйте шейпинг только к трафику целевой точки
В macOS утилита dnctl создаёт каналы с ограничением скорости и задержки, а pf направляет в них соответствующий трафик. Правило должно одновременно ограничивать целевой IP, протокол и порт. Нельзя использовать маску, охватывающую все исходящие соединения.
Следующий скрипт создаёт тестовый канал со скоростью 8 Mbit/s, задержкой 120 ms и потерей пакетов 2%. В TARGET_IP необходимо указать фиксированный адрес управляемой конечной точки.
set -euo pipefail
TARGET_IP="${TARGET_IP:?set TARGET_IP first}"
PIPE_ID=310
ANCHOR="com.apple/hexvm-nettest"
cleanup() {
sudo pfctl -a "$ANCHOR" -F all >/dev/null 2>&1 || true
sudo dnctl delete "$PIPE_ID" >/dev/null 2>&1 || true
}
trap cleanup EXIT INT TERM
sudo dnctl pipe "$PIPE_ID" config bw 8Mbit/s delay 120 plr 0.02
printf 'dummynet out quick proto tcp from any to %s port 443 pipe %s
' \
"$TARGET_IP" "$PIPE_ID" |
sudo pfctl -a "$ANCHOR" -f -
sudo pfctl -E >/dev/null
sudo pfctl -a "$ANCHOR" -sr -v
curl -sS -o /dev/null \
-w 'code=%{http_code} connect=%{time_connect} total=%{time_total}
' \
"$TEST_URL"
Сначала убедитесь с помощью проверки состояния только для чтения, что правило срабатывает, и лишь затем запускайте бизнес-тесты, записывающие данные. Не используйте pfctl -d для очистки: на машине могут действовать другие правила межсетевого экрана. Скрипт очищает только собственный anchor и удаляет только собственный pipe, не затрагивая параллельные задачи.
Защитите удалённый сеанс от случайной блокировки
Перед запуском оставьте открытым второй терминал управления и убедитесь, что правила не затрагивают управляющие порты, шлюз по умолчанию и произвольные целевые адреса. Если тестовая точка и управляющее соединение используют один IP, создайте отдельную точку входа для тестов, а не полагайтесь на порядок правил.
Превратите поведение клиента в автоматические проверки
При проверке плохой сети недостаточно знать, «вернулся ли ответ». Тестовый код должен фиксировать число попыток, итоговый тип ошибки, общее время ожидания и идентификатор запроса. Для операций записи сервер и клиент должны использовать стабильный ключ идемпотентности. После тайм-аута клиенту следует сначала запросить результат операции и лишь затем решать, нужна ли повторная попытка.
Сетевой слой можно обернуть в наблюдаемый компонент и проверить следующие граничные условия:
- Если отдельное соединение превышает заданный порог, его можно отменить без бесконечного ожидания.
- Число повторных попыток ограничено, интервалы включают отложенное повторение, а запросы не создают лавинообразную нагрузку.
- После явной отмены пользователем фоновая задача не отправляет запрос повторно.
- При прерывании отправки сохраняется однозначный статус, а ошибка не отображается как успешное завершение.
- После восстановления сети работу можно продолжить без повторного создания записей.
Тесты симулятора следует запускать по явному идентификатору устройства, не полагаясь на случайно существующее на машине имя устройства.
export SIMULATOR_UDID="replace-with-booted-simulator-udid"
xcodebuild test \
-scheme NetworkBehaviorTests \
-destination "platform=iOS Simulator,id=${SIMULATOR_UDID}" \
-resultBundlePath build/NetworkBehavior.xcresult
Сохраняйте пакет результатов отдельно для каждого сетевого сценария и записывайте его параметры в журнал теста. Тогда по сведениям о сбое можно будет определить, «при какой задержке и потере пакетов возникла ошибка», вместо того чтобы получить только невоспроизводимый красный статус.
Очистите правила и завершите регрессионную проверку
При штатном завершении скрипта или получении сигнала прерывания выполняется очистка. Однако после аварийного завершения удалённого сеанса состояние всё равно нужно проверить вручную. После повторного подключения выполните:
sudo pfctl -a com.apple/hexvm-nettest -sr
sudo dnctl list
curl -sS -o /dev/null \
-w 'code=%{http_code} connect=%{time_connect} total=%{time_total}
' \
"$TEST_URL"
Выделенный anchor должен быть пуст, а в списке не должно остаться pipe с номером 310. Затем снова выполните базовый запрос и убедитесь, что время соединения и общая продолжительность вернулись в нормальный диапазон, зафиксированный до теста.
В завершение проведите регрессионную проверку без шейпинга. Особое внимание уделите тому, не изменили ли исправления для плохой сети порядок запросов, попадание в кеш и скорость отклика интерфейса при нормальном соединении. Сохраните параметры сценария, версию клиента, версию целевой точки, путь к пакету результатов и итог очистки в одной записи запуска. Так тестирование плохой сети превратится из разовой ручной демонстрации в контролируемый, воспроизводимый и безопасно завершаемый инженерный процесс.
Часто задаваемые вопросы
Почему нельзя ограничивать весь исходящий трафик удалённого Mac?
Глобальное правило затронет SSH, VNC и загрузку зависимостей, поэтому административный сеанс может оборваться. Ограничение должно учитывать IP, протокол и порт тестовой системы.
Как убедиться, что временные сетевые правила удалены?
Очистите выделенный anchor pf, удалите связанную трубу dnctl, проверьте отсутствие правил и повторите исходный запрос без ограничений.
Какие реакции приложения нужно проверять при плохой сети?
Проверьте тайм-ауты, ограниченные повторы, отмену запроса, сообщение об отсутствии сети, защиту от дублей и корректное восстановление соединения.
Выберите выделенный облачный Mac для следующей сборки
Выбирайте модель Mac mini, срок аренды и регион под рабочую нагрузку. Каждый заказ соответствует отдельному физическому узлу, а фактическая доступность определяется в реальном времени через консоль.