Салют, хабр! В интернете уже довольно много материалов о том, как другие команды строили свои решения для тестирования мобильных приложений (и не только), например: Яндекс Колхоз, Ферма мобильных устройств Selectel, Ферма девайсов VK, Ферма мобильных устройств в Сбере, Мобильная ферма 101 (доклад Антона Малинского).
Я же хочу рассказать про Ферму, которую мы строим в SberDevices.
Меня зовут Александр Телешов, и я руковожу разработкой Фермы и Чембера (но про это расскажу в другой раз). Под катом вы найдете небольшую хронологию событий развития Фермы, собранные грабли, интересные факты и забавные наблюдения.
2020 год. Начало
На КДПВ изображена одна из комнат Фермы сейчас, а начинали мы в далеком уже 2020 году. Год ознаменовал начало удаленки. Все разъехались, и каждый тестировщик, естественно, хотел взять с собой на удаленку как можно больше разных устройств, желательно, чтобы это был телевизор с большой диагональю. Помимо этого, задачу с автотестами на большом зоопарке устройств тоже никто не отменял. И мы принялись строить свою Ферму.
В тот момент к нашей Ферме планировалось подключать только Android-устройства, и наиболее подходящим «коробочным» решением для задач удалённого управления на то время казался проект OpenSTF (впоследствии переименованный в Device Farmer) с открытым исходным кодом.
Разработка началось с разработки Hardware-решений, которые должны были обеспечить подключение ПК к периферийным устройствам по схеме один ко многим. На 2020 год в SberDevices существовало два типа умных устройств: SberPortal и SberBox, которые использовали USB Type-C и USB Type-A интерфейсы для питания и обмена данными с ПК соответственно. Поэтому было принято решение о создании универсального хаба, совместимого со всеми существующими типами умных устройств. Изначально хаб был рассчитан на параллельное подключение лишь 3-х устройств с отдельным блоком питания ≈220 В (см. картинку ниже).
После того как был изготовлен первый образец универсального хаба, и устройства стали доступны через adb на стороне хост ПК, пришло время предоставить к ним удалённый доступ по сети. Развёртывание проекта и подключение к нему первых устройств (SberPortal) через универсальный хаб не было сложной проблемой, если следовать документации на сайте проекта. OpenSTF предоставлял доступ к устройствам через adb, обеспечивал выполнение команд через Shell-терминал по сети и транслировал экран устройств в веб-интерфейсе для ручных манипуляций пользователем.
Почему мы не взяли готовые USB хабы, а стали изобретать свой велосипед? Причин несколько: во-первых, мы понимали, что подключаемые устройства при длительной работе или некачественной прошивке будут зависать, и нам хотелось управлять питанием девайсов программно. Во-вторых, устройства, которые стоят на нашей Ферме, в большинстве своем релизные, как из магазина, а при помощи нашей «коробочки» удалось получить доступ до отладочных интерфейсов девайса.
Экран на безэкранном устройстве
Проблемы совместимости OpenSTF с нашими девайсами начались уже со вторым устройством от SberDevices — SberBox, который отличался тем, что у него отсутствует собственный тач экран в сравнении с обычным мобильным телефоном или даже SberPortal. Для корректной работы OpenSTF требовалась загрузка на устройство нескольких утилит, одна из которых — Minitouch, отвечающая за мультитач-события и жесты. Однако эти события не могли происходить на SberBox ввиду отсутствия тач-экрана, поэтому утилита Minitouch падала с ошибкой. Так как OpenSTF — проект с открытым кодом, то можно было внести исправление в исходный код (мы еще не подозревали, к чему это приведет) и пересобрать утилиту так, чтобы обойти ограничение на использование безэкранных устройств. В итоге появилась кастомная рабочая версия OpenSTF, которая поддерживала устройства с тач-экраном и без него см. картинку ниже.
А как получить UART?
Изначально ферма Android-устройств собиралась из девайсов с установленной отладочной (debug) прошивкой. Данная прошивка держит открытыми все отладочные интерфейсы, включая adb-доступ в периферийном интерфейсе USB, который требуется для работы проекта OpenSTF. Но возникла потребность в тестировании не только дебаг-устройств, но и устройств с релизной прошивкой, для которых adb не доступен по умолчанию. Из-за особенностей девайса для получения доступа к adb на релизном SberBox пришлось делать реворк на PCB-плате (никому не советуем так делать). К сожалению, SberBox потерял свой внешний вид из-за отверстия в корпусе для дополнительного кабеля.
Но его функциональность не пострадала, а даже возросла, что позволило подключать релизные девайсы к универсальному хабу и использовать их в связке с OpenSTF.
OpenSTF и 33 девайса
Ферма увеличивалась в размерах, добавлялись новые устройства, но однажды мы столкнулись с тем, что очередное новое устройство не всегда отражается доступным в OpenSTF. Adb-сервер показывал, что с устройством все хорошо, с ним можно работать, однако OpenSTF не всегда его инициализировал. После долгой отладки и проверки множества гипотез, было обнаружено, что OpenSTF захватывает только 33 девайса, хотя adb-сервер видит больше 33. Если отключить физически уже захваченные девайсы, тогда OpenSTF захватит незахваченные до этого (но, в сумме число захваченных девайсов не превышало 33).
Опять отладка, опять лезем в исходники OpenSTF. Ошибка была в древней версии adb (1.0.31), которая идет вместе с Ubuntu 16, на которой основан контейнер с OpenSTF (мы используем контейнер от разработчиков OpenSTF).
Ошибка была в том, что adb-сервер на запрос host:track-devices
(https://gist.github.com/hfutxqd/a5b2969c485dabd512e543768a35a046#file-services-txt-L23) возвращал максимум 33 устройства. А запрос host:devices-l
(который использует официальный клиент adb)(https://gist.github.com/hfutxqd/a5b2969c485dabd512e543768a35a046#file-services-txt-L16) возвращал все устройства как и положено.
OpenSTF использует свою реализацию adb-клиента и в своей реализации adb-клиента использует запрос host:track-devices
, а официальный клиент adb использует запрос host:devices-l
.
Отделались малой кровью — решением проблемы было подключение репы из Ubuntu 21 и установка adb из этой репы. В репе находится версия adb 1.0.41, которая является последней официальной версией из пакета SDK Platform-Tools 31.0.3 (August 2021).
Добавляем Linux девайсы на Ферму
На Ферму принесли новое устройство с линуксом на борту, при этом на нем был запущен adb-демон, и устройством можно было управлять через обычные adb-команды. Экрана на устройстве не было (мы это уже проходили), но самое главное — на устройстве не было утилиты getprop, вывод которой использует OpenSTF, чтобы инициализировать устройство. По сути, нам нужно было предоставить пользователям только доступ до adb без расширенных функций по управлению девайсом, которые предлагает OpenSTF. Однако OpenSTF запускал и следил за работой утилит minicap и minitouch, но для нашей платформы эти утилиты не были собраны и адаптированы…
Начинаем разбираться в исходниках OpenSTF (в очередной раз), смотрим как происходит инициализации и какие ключи getprop используются для этого. Разобравшись, пишем небольшой скрипт на Bash, который называется getprop (😊):
#!/bin/sh
CONFIG_FILE=/SOME_PATH/getprop_config.txt
if [ $# -eq 0 ];
then
cat $CONFIG_FILE
else
# grep -Po "(?<=(\[$1]: \[))(\w+)" $CONFIG_FILE
grep "$1" $CONFIG_FILE | awk -F"[" '{print $NF}' | awk -F"]" '{print $1}'
fi
Скрипт возвращает нужные значения из заранее созданного конфига. Кладем его на девайс, делаем исполняемым (попутно узнаем, что grep на девайсе не поддерживает regexp и нас выручил awk) и теперь наша линукс машина умеет притворяться андроид устройством с точки зрения OpenSTF. Но остается проблема с minicap и minitouch. Теперь начинаем править OpenSTF, чтобы он не загружал на нашу линукс машину эти утилиты и не следил за их работой. Неделя не самой приятной работы и теперь через OpenSTF можно работать с линукс устройствами. Найдите, кстати, это устройство).
Нам уже не нравилось это решение, т.к. оно плохо масштабируется, а кастомизированная версия (в очередной раз) OpenSTF только добавляет проблем с поддержкой.
А потом пришел OOM killer
Шло время, пользователей на Ферме стало больше, и начали появляться жалобы, что телевизоры очень часто в процессе работы «отваливаются». Они отключались от OpenSTF на короткое время и снова становились доступны. Пользователям приходилось заново «захватывать» девайс через API OpenSTF, получать новый порт для подключения и заново перезапускать тесты. Однако ситуация могла повториться, и тест завершить так и не получалось.
Ситуация была плавающая. Очень часто тестировщики просто перезапускали тесты, и они проходили, однако с увеличением количества телевизоров на Ферме таких отвалов становилось больше.
Логи работы OpenSTF показывали следующую ситуацию:
11:37:20 - пользователь занял девайс
11:37:20 - пользователь получил параметры для подключения к девайсу (порт 7545)
11:37:36 - через 16 секунд пользователь снова получил параметры для подключения к девайсу (порт 7545)
11:38:13 - был открыт shell девайса, что дальше происходило с девайсом - неизвестно, у пользователя полный доступ до ТВ
11:40:52 - OpenSTF переинициализировал устройство
11:40:53 - пользователь получил новые параметры для подключения к девайсу (порт 7685)
Стало очевидно: с телевизором что-то произошло, и подключение к нему кратковременно пропало. Подозрение упало уже на девайс. Пришлось поменять работу тестов и добавить логирование dmesg на устройстве. И в следующий раз, когда поймали такую же ситуацию, перед нашим взором в dmesg предстала следующая картина:
Андроидовский low memory killer daemon (lmkd) убил процесс OpenSTF. Тесты на ТВ стали потреблять много памяти, lmkd начал убивать процессы с последней группы процессов (подробнее тут) и дошел до процесса OpenSTF. После того, как память освободилась, OpenSTF заново переинициализировал девайс.
У OpenSTF на девайсе есть три процесса:
У всех этих процессов разный приоритет:
И lmkd как раз убивает процесс jp.co.cyberagent.stf с приоритетом 3.
Казалось бы, нужно просто поменять приоритет процессов OpenSTF через renice или например:
Но нужны рутовые права, а с рутом есть проблема:
В сборках, которые устанавливаются для тестирования на телевизоры нельзя перезапустить демон adb от рута…
Начинаем все сначала, больше никаких apk
После описанного выше случая стало понятно, что есть два варианта развития событий: или мы продолжаем погружаться в недра OpenSTF и кастомизируем его под свои нужны все сильнее, или пишем свое решение. Разбираться в OpenSTF для нас было тяжело, для нашей команды основной стек — C++/Python, поэтому мы выбрали второй вариант и как оказалось, не зря.
Основная идея новой реализации – Ферма не должна ничего загружать и запускать на устройстве.
Погрузившись в документацию на adb, стало понятно, что наш софт должен притворяться adb-демоном для внешнего пользователя Фермы и adb-сервером для adb-демона на устройстве.
Плюс надо не забывать про авторизацию по ключу, которая появилась в свежих (относительно) андроидах. На гитхабе уже есть реализации на Python протокола между сервером и демоном, но это решение казалось слишком сложным. По сути, мы повторяем OpenSTF (который как раз и используют самописную реализацию adb-клиента и свою реализацию протокола между сервером и демоном), но на Python и снова завязываемся на реализацию стороннего человека узкоспециализированной фичи. Если гугл поменяет протокол, то наше решение превратится в тыкву.
И тут появилась идея, а что если вообще никак не вмешиваться в обмен между adb-сервером клиента Фермы и adb-демоном на девайсе, а просто выступать как proxy и по-сути, просто перекладывать трафик. За субботу-воскресенье был реализован proof of concept. Наш сервер Фермы отдавал пользователю порт, пользователь у себя делал adb connect, а дальше трафик, который приходит на этот порт, мы перекладываем в порт, который слушает adb-демон на устройстве.
Одним из плюсов OpenSTF была возможность видеть экран устройства в браузере. Эта возможность как раз и обеспечивалась приложением minicap, однако мы же решили ничего больше не загружать на девайс со стороны Фермы. И тут на помощь пришел активно развивающийся проект scrcpy (https://github.com/Genymobile/scrcpy). Теперь пользователи Фермы при работе с конкретным девайсом сами решают, нужен им доступ к экрану или нет, а если нужен — запускают утилиту scrcpy. А нам, как разработчикам Фермы, больше не нужно поддерживать большой зоопарк андроид устройств. Утилита развивается, и у всех только позитивные отзывы о ее работе.
Наше самописное решение повысило стабильность работы с девайсами. Scrcpy всем понравился, и начиная с этого момента мы стали переносить все девайсы из OpenSTF на свою реализацию. Спустя время реализовали уже все основные фичи OpenSt как группы пользователей и группы девайсов, принудительное отключение по таймауту и тд.
После этого принялись за расширение функционала и, например, теперь сервер Фермы возвращает дополнительные поля с информацией об устройстве:
{
"device_id": "SBB01Y08AB068929",
"name": "SberBox",
"model": "SberBox",
"is_available": true,
"is_busy": false,
"user_addr": null,
"user_email": null,
"note": "heartbeats 170+ms ",
"device_group_name": "default_device_group",
"additional_ports": [],
"additional_device_info": {
"is_debug": false
},
"info": {
"staros_version": "1.90.21",
"revision": "sberbox",
"mem_available": "874 292 kB (42.88%)",
"loadavg": "5.22 4.96 3.91 3/1671 10235",
"getting_time": "2024-01-22T15:19:30.820603",
"has_antenna": null
},
"status_msg": "success",
"response_time": "2024-01-22 12:19:54.789245+03:00"
}
Ферма и телевизоры
У нас на Ферме стоит сейчас около 30 телевизоров разных диагоналей. Сначала мы ставили телевизоры просто на стойку, но быстро поняли, что они занимают много места и от матрицы исходит много тепла.
Оказалось, что для нашей работы матрица и вовсе не нужна. Можно взять платы от телевизоров, «закатать» их в акрил и в таком виде размещать на полках. Так они занимают гораздо меньше места, меньше греются и меньше потребляют.
А если все-таки нужно получить доступ к экрану, всегда можно воспользоваться вот таким мониторчиком:
Wi-Fi и офис Сбера
Очень долго мы не могли понять, почему у нас на Ферме плохо работает Wi-Fi, пинги даже около роутера могут достигать нескольких секунд, а иногда пакеты вообще могут перестать ходить. Да, на Ферме много устройств, они стоят в металлических стойках, которые могут экранировать сигнал – все это действительно может влиять. Естественно, точки доступа всегда настраивались на свободный канал. Стали разбираться. Для начала замерили мощность сигнала в разных местах комнаты. Оказалось, что до некоторых участков комнаты сигнал добивает хуже.
Параллельно с этим решили поднять новую тестовую отдельную Wi-Fi сеть и подключить к ней ноутбук. Пинги были очень большими, даже вплотную к роутеру. Стало очевидно, что нам кто-то мешает. Связались с администраторами здания, а оттуда с безопасниками.
Решение оказалось простым, безопасники нам предоставили требования к Wi-Fi сети, чтобы они нас не глушили (а они это делали). Договорились, чтобы отключили корпоративные точки доступа около помещения Фермы — это сделало эфир гораздо чище, а мы оптимизировали положение точек доступа у нас на Ферме.
Подключаем Умный Дом
Летом 2023 к нам приходят коллеги из подразделения, которое занимается Умным Домом, у нас состоялся такой диалог:
- Мы хотим заехать к вам на Ферму. Нам нужен удаленный доступ, объединение девайсов в группы, группы пользователей, бронирование — вот это вот все.
- Да, отлично. У вас adb-демон на устройстве поддерживает работу по tcp/ip или только по usb?
- У нас вообще нет adb...
Тут стало очевидно, что нам нужно снова пересмотреть концепцию Фермы. Если раньше нашей задачей было дать доступ до adb-девайса, то сейчас надо подходить к этой задаче в более общем виде, а именно, надо давать доступ до какого-то порта на девайсе, который случает клиентское приложение. Например, в случае adb-устройств — adb-демон на девайсе. Если устройство никакой порт не слушает и работает только по usb, тогда пишем прокси микросервер, который будет слушать порт и уже перекладывать данные в usb или куда-то еще.
В итоге получается следующая картина:
-
Если adb-девайс поддерживает adb tcpip — даем доступ до порта, который слушает adb демон (по умолчанию 5555).
-
Если adb-девайс работает только по usb — используем/пишем свой прокси микросервер, который будет слушать порт и перекладывать данные в usb. Благо умные люди за нас все написали, и мы стали использовать для этих целей утилиту adbkit из DeviceFarm.
-
Если девайс сразу поддерживает работу по сети — даем доступ до нужного порта, например до SSH-порта.
Потратив некоторое время на кодинг, мы получили решение, которое позволяет подключать к Ферме практически любое устройство. Если устройство слушает порт — хорошо, если работает по какому-то интерфейсу — пишем свой прокси микросервер, который перекладывает данные с порта в этот интерфейс.
В процессе разработки выяснилось, что многие устройства слушают несколько портов, и было бы неплохо, когда пользователь занимает девайс, давать доступ сразу до всех нужных портов. Эта доработка была уже не такой сложной. Благодаря этой доработке мы смогли давать доступ через Ферму до стендов, к примеру, с WirenBoard.
Или собирать комплексные стенды из нескольких девайсов и какой-нибудь периферии, например стенд, где происходит пейринг SberTop к нашим умным лампочкам.
Как мне нажать кнопку?
Осенью 2023 к нам приходит запрос: «Хочу нажимать на SberBoom физические кнопки! Эмуляция нажатия через adb не подходит». Стали думать, как подходить к решению этой задачи. Можно поставить на Ферме, например, «малинку», на ней подключить по usb девайс, а также собрать какое-то исполнительное устройство на базе сервоприводов, управляемое по UART. А через Ферму как раз давать доступ к «малинке» по SSH. Для автотестеров — пользователей Фермы, получается некрасиво. Им для некоторых Бумов придется писать отдельную логику: логин по SSH, какой-то воркер для работы по UART. Но ведь мы же можем отдавать пользователю несколько ip:port с одного тестового девайса, давайте наш «кнопконажиматель» просто сделаем небольшим сервером, у которого будет несколько методов по нажатию кнопок?
При разработке «кнопконажимателя» важным условием было «неинвазивное» подключение, то есть тестируемый девайс не нужно разбирать, паять и как-либо дорабатывать для проведения тестов.
Во-первых, тестируемый девайс, как система механических компонентов, остается неизмененным. Это важно, например, для акустических тестов, где каждый элемент корпуса влияет на звучание девайса в целом и отсутствие резонансов.
Во-вторых, это сильно экономит время, так как девайс достаточно сложен в разборке, и чтобы подобраться к контактным площадкам кнопок, нужно потратить достаточно много усилий и времени.
В-третьих, тестируемый девайс сохраняет свой товарный вид (что важно, если мы берем девайс из прода и планируем вернуть его обратно).
Но самое главное его достоинство — единообразное нажатие на кнопку. Дело в том, что человек может нажимать на кнопки с разной силой, попадать или не попадать пальцем в центр кнопки, иногда делать двойные и тройные нажатия. В конце концов, человек может нажимать кнопку с той продолжительностью, которую лично он считает правильной.
И всё это, конечно, может отражаться на результатах теста. Вот здесь непредвзятый «кнопконажиматель» оказывается более предпочтительным по сравнению с человеком.
Наш «кнопконажиматель» планировалось использовать в помещении, где уже находится много тестируемых устройств, и эфир Wi-Fi там сильно «зашумлён». Поэтому при разработке мы не смотрели в сторону беспроводных подключений, а сразу заложили сетевой интерфейс Ethernet. А так как наш прибор получает IP-адрес от DHCP-сервера, то мы добавили небольшой OLED экран для отображения полученного IP-адреса. Это повысило удобство использования прибора.
Во время разработки конструкции в приоритете была скорость и простота. Нам хотелось попробовать саму идею «кнопконажимателя» с минимальными затратами. Несущие элементы конструкции напечатаны на 3D принтере, электроника по максимуму собрана из готовых модулей, которые можно купить в локальных магазинах без сроков изготовления и поставки. В качестве механических толкателей использованы авиамодельные сервомоторы с самодельными направляющими.
И хотя текущий экземпляр «кнопконажимателя» разрабатывался для работы с колонкой SberBoom, у нас в конструкции заложена возможность работы с различными девайсами. Для этого платформа с толкателями сделана сменной и регулируемой по высоте. Можно опустить платформу, ослабив винты, и работать со SberBoom mini. Или же вовсе изменить паттерн расположения кнопок под другой тестируемый девайс, просто напечатав новую платформу на 3D принтере.
А где же телефоны?
Удивительно, но наша Ферма никогда не рассматривалась как решение для тестирования мобильных приложений. Мы всегда были ориентированы на тестирование наших девайсов, поэтому только в декабре 2023 года к нам пришел запрос на размещение на Ферме телефона для проверок механизма пейринга. Это был самый легкий в плане добавления девайс за все время. Просто открыли adb - никакого экшена.
Вместо заключения
Мы начинали Ферму, как и все, с OpenSTF, дальше было несколько итераций, в которых мы допиливали OpenSTF, но в итоге из-за сложности поддержки и проблем пришли к своему решению, благодаря чему теперь можем давать удаленный доступ не только до Android-устройств, но практически до любых.
Ферма очень сильно ускоряет процесс тестирования. Например, время прохождения смоука автотестов сокращено в 6 раз, а время прохождения регресса автотестов сокращено в 7 раз.
Ферма требует поддержки: оборудование должно быть заземлено, все, что возможно, должно быть подключено проводом, кабели должны быть качественные, а влажность не должна быть низкой. Это всё прописные истины, тем не менее, оборудование зависает, платы сгорают, на контактах появляются окислы и без выделенного человека поддерживать оборудование тяжело.
Задач на Ферме много, и они очень разнообразные. Без помощи многих команд нам бы не удалось все это сделать. Команда HW RnD помогала нам с железными проблемами, DevOps — с нашей сетевой инфраструктурой, коллеги из команды автотестеров постоянно делились с нами обратной связью, говорили что мы можем улучшить, а коллеги из Оснащения и Сопровождения помогали с оборудованием, решали вопросы с помещением. За что мы говорим коллегам большое спасибо!