Постановка задач
В предыдущей статье наш коллега писал про Selenoid с Android-эмуляторами. Однако это решение было пробой пера и проверкой работоспособности Selenoid. Применение данного решения выявило несколько проблем:
-
Эмулятор — это не реальное устройство, возможен пропуск различных дефектов в нашем приложении. Есть много Android-устройств с различными версиями ОС, экранами, процессорами и другими характеристиками. Тестирование на реальных устройствах помогает убедиться, что приложение работает корректно на максимально возможном количестве конфигураций.
-
Загруженность процессора хоста значительно выше из-за необходимости эмуляции аппаратных и программных компонентов устройства. Это может привести к снижению общей производительности системы, особенно при запуске большого количества эмуляторов.
-
Не учитываются все особенности работы устройства, включая производительность, энергопотребление и особенности сетевого соединения.
Если кратко, лучше реальность, чем симуляция.
Однако не всё так гладко. У развёртывания Selenoid на реальном железе, есть некоторые проблемы:
-
Сейчас нет универсального и готового к использованию решения для развёртывания сервера, которое бы автоматизировано обновляло конфигурацию Selenoid хаба в зависимости от подключения/отключения устройств Android в USB-порт. В большинстве случаев инженеры по тестированию вынуждены вручную настраивать сервер и конфигурации для каждого нового устройства, что требует значительных временных и трудовых затрат.
-
После подключения/отключения новых устройств конфигурационные файлы необходимо обновлять вручную, что увеличивает риск ошибок и требует дополнительных ресурсов. Для автоматизации процесса подключения и конфигурирования новых устройств требуются кастомные скрипты, которые необходимо разрабатывать и поддерживать. Внедрение таких решений может значительно улучшить качество тестирования и сократить затраты на его проведение.
Решение трудностей — развернуть Selenoid без Докера на сервере для работы с реальными устройствами, а также обеспечить полную автоматизацию при создании конфигураций для подключаемых Android девайсов через USB-порт.
Об этой и других задачах развёртывания инфраструктуры для запуска тестов на реальных Android-устройствах на Linux и Mac расскажу в этой статье. А также продемонстрирую реализованные нами кастомные скрипты для генерации конфигурационных файлов для подключенных устройств.
Начнём с виртуализации.
Шаг 1. Виртуализация на сервере
В первую очередь необходимо включить виртуализацию на нашем Linux-сервере.
№1. Перезагружаем компьютер. При включении постоянно нажимаем кнопку подсказки, чтобы попасть в BIOS. Если подсказки нет, жмём «F9» или «F10».
№2. Выбираем «Security» — «System Security».
№3. Находим «Intel Virtual Technology». Выбираем «Enabled» с помощью клавиш «→», а затем жмём «F10».
№4. Вернёмся в меню «File» и выберем «Save Changes and Exit».
Шаг 2. Настройка окружения сервера
На следующем этапе перейдем к установке требуемого ПО для настройки сервера перед запуском автотестов.
№1. Создаём папку на сервере, где будут находится Selenoid и Selenoid UI, а также скрипты для автоматизированного создания конфигурационных файлов для Selenoid и Appium.
Пример:
mkdir selenoid
№2. Переходим в директорию, которая была создана на предыдущем шаге.
cd selenoid
№3. Переходим в репозиторий Selenoid. Копируем ссылку на бинарник и скачиваем бинарник на сервер (может отличаться от того, что указано в примере).
wget -o selenoid бинарник
№4. Переходим по ссылке в репозиторий Selenoid UI, копируем ссылку на бинарник. Также скачиваем бинарник на сервер.
wget -o selenoid-ui бинарник
№5. Устанавливаем nvm.
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash export NVM_DIR="NVM_DIR/nvm.sh" ] && . "NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion" # This loadsnvm bash_completion
№6. Устанавливаем Node.js.
sudo apt-get install -y nodejs
№7. Устанавливаем npm.
sudo apt install -y npm
№8. Устанавливаем Appium.
npm install -g appium
№9. Устанавливаем драйвер uiautomator.
appium driver install uiautomator2
№10. Устанавливаем необходимую версию Java Azul. Подробные инструкции об установке.
№11. Установим Android SDK.
wget https://dl.google.com/android/repository/tools_r25.2.3-linux.zip unzip tools_r25.2.3-linux.zip -d sdk cd /sdk/tools ./android update sdk --no-ui
№12. Создаём файл с переменными средами в домашней директории пользователя.
touch $HOME/.bash_profile
№13. В текстовом редакторе nano прописываем следующие переменные:
№14. Экспортируем настройки переменных энвайронмента из файла.
source .bash_profile
№15. Устанавливаем adb.
sudo apt-get install adb
№16. Инициализируем локальный репозиторий и связываем его с центральным.
git init git remote add origin <Ссылка на ваш репозиторий> git pull origin master
Шаг 3. Листинг bash скриптов
После реализации и отладки скриптов в нашем репозитории получилась следующая структура:
healthCheck.sh
Этот скрипт предназначен для создания новой сессии Appium с использованием команды curl
. Служит для проверки развернутого Selenoid.
#!/bin/bash HOST=$1 PORT=$2 PLATFORM_NAME=$3 DEVICE_NAME=$4 if [ "$PLATFORM_NAME" = "android" ]; then APP_PATH="" else APP_PATH="" fi REQUEST_BODY=$(echo '{ "capabilities": { "alwaysMatch": { "browserVersion": "deviceNameToReplace", "selenoid:options": { "name": "Session started using curl command...", "sessionTimeout": "1m" }, "appium:deviceName": "platformNameToReplace", "appium:app": "appPathToReplace" } } }') REQUEST_BODY=${REQUEST_BODY/deviceNameToReplace/$DEVICE_NAME} REQUEST_BODY=${REQUEST_BODY/appPathToReplace/$APP_PATH} REQUEST_BODY=${REQUEST_BODY/platformNameToReplace/$PLATFORM_NAME} curl -H'Content-Type: application/json' http://$HOST:$PORT/wd/hub/session -d"$REQUEST_BODY"
Этот скрипт выполняет следующие действия:
-
Определяет уникальные идентификаторы для поиска скриптов.
-
Экспортирует переменные окружения, необходимые для работы Selenoid и Appium.
-
Ищет и определяет абсолютные пути к скриптам и конфигурациям.
-
Экспортирует дополнительные переменные окружения.
-
Выводит значения переменных в режиме отладки, если скрипт запускается с аргументом
--debug
.
Таким образом, скрипт настраивает окружение для работы с Selenoid и Appium, обеспечивая возможность поиска и использования необходимых скриптов и конфигураций.
Листинг:
#!/bin/bash get_realpath_from_egrep() { search_pattern=$1 search_dir=$2 DIRS=$(egrep -r --include=*.sh --exclude-dir=$HOME/Library --exclude-dir=$HOME/.Trash "$search_pattern" "$search_dir") echo $DIRS | sed "s/:/\\n/" | head -n1 | xargs realpath } get_last_segment_from_pattern() { search_pattern=$1 echo "$search_pattern" | sed "s/\//\\n/g" | tail -n1 } FILE_NAME=".zshrc" if [ -f "$HOME/$FILE_NAME" ]; then #for mac os source $HOME/$FILE_NAME else FILE_NAME=".bash_profile" #for linux if [ -f "$HOME/$FILE_NAME" ]; then source $HOME/$FILE_NAME fi fi APPIUM_SCRIPT_FIND_BY="5cc2bd0d-96c5-4567-b82a-a896695af033" DEVICES_SCRIPT_FIND_BY="94843c3a-f128-4bb6-8819-4644156699d9" SELENOID_SCRIPT_FIND_BY="1b6f0b38-04e2-421c-b122-a54ab8a68bbd" SELENOID_CONFIG_SCRIPT_FIND_BY="660368f2-f0cc-49d8-bf2e-4a7d0f2c84d8" export SELENOID_PORT=4444 export CRON_SETTINGS="*/15 * * * *" export SELENOID_UI_PORT=8080 export SELENOID_CONFIG_NAME=devices.json SELENOID_LIMITS="-disable-docker -limit 20 -retry-count 1000" SELENOID_TIMEOUTS="-max-timeout 20m -session-attempt-timeout 15m -timeout 10m -service-startup-timeout 10m" SELENOID_PATH=$(get_realpath_from_egrep "$SELENOID_SCRIPT_FIND_BY" "$HOME") SELENOID_HOME=$(echo $SELENOID_PATH | xargs dirname) APPIUM_CONFIG_PATH=$(get_realpath_from_egrep "$APPIUM_SCRIPT_FIND_BY" "$SELENOID_HOME") SELENOID_CONFIG_PATH=$(get_realpath_from_egrep "$SELENOID_CONFIG_SCRIPT_FIND_BY" "$SELENOID_HOME") export SELENOID_HOME=$SELENOID_HOME export SELENOID_LOGS_DIR=$SELENOID_HOME/logs export COMMON_CONFIG_DIR=$(echo $SELENOID_CONFIG_PATH | xargs dirname) export APPIUM_CONFIG_DIR=$(echo $APPIUM_CONFIG_PATH | xargs dirname) export DEVICES_WATCHER_PATH=$(get_realpath_from_egrep "$DEVICES_SCRIPT_FIND_BY" "$SELENOID_HOME") export SELENOID_SCRIPT_NAME=$(get_last_segment_from_pattern "$SELENOID_PATH") export APPIUM_CONFIG_CREATER=$(get_last_segment_from_pattern "$APPIUM_CONFIG_PATH") export SELENOID_CONFIG_CREATER=$(get_last_segment_from_pattern "$SELENOID_CONFIG_PATH") CONFIG=$COMMON_CONFIG_DIR/$SELENOID_CONFIG_NAME export SELENOID_ARGS="$SELENOID_LIMITS -listen :$SELENOID_PORT -conf $CONFIG $SELENOID_TIMEOUTS -log-output-dir $SELENOID_LOGS_DIR" export SELENOID_UI_ARGS="-listen :$SELENOID_UI_PORT --selenoid-uri=http://localhost:$SELENOID_PORT" if [ "$1" = "--debug" ]; then echo $SELENOID_LOGS_DIR echo $COMMON_CONFIG_DIR echo $APPIUM_CONFIG_DIR echo $DEVICES_WATCHER_PATH echo $SELENOID_CONFIG_NAME echo $SELENOID_SCRIPT_NAME echo $APPIUM_CONFIG_CREATER echo $SELENOID_CONFIG_CREATER fi
Этот скрипт автоматизирует процесс мониторинга подключенных Android-устройств и перенастраивает Selenoid в случае изменений в списке устройств. Он загружает переменные окружения, проверяет текущий список устройств, сравнивает его с предыдущим состоянием и, при необходимости, перезапускает Selenoid, обновляя конфигурацию.
#!/bin/bash #94843c3a-f128-4bb6-8819-4644156699d9 - don't delete DIR_TO_SCRIPT=$(realpath "$0" | xargs dirname) if [ "$#" != "1" ]; then echo "Необходимо передать путь до .env" exit 120 fi if [ -f $1 ]; then cd $(dirname $1) source $1 else exit 120 fi DEVICES_FILE="devices" DEVICES_REGISTRY_PREV="" cd $DIR_TO_SCRIPT if [ -f $DEVICES_FILE ]; then DEVICES_REGISTRY_PREV=$(cat $DEVICES_FILE) else touch $DEVICES_FILE fi DEVICES=$(adb devices -l| grep -Eo "[a-zA-Z0-9-]{4,}\s{2,}" | xargs -n1 echo) DEVICES_COUNT=$(echo $DEVICES | wc -w) for ((DEVICE_INDEX=1; DEVICE_INDEX <= $((DEVICES_COUNT)); DEVICE_INDEX++)) do DEVICE=$(echo $DEVICES | cut -d' ' -f$DEVICE_INDEX) DEVICES_TO_LINE=$DEVICE,$DEVICES_TO_LINE done DEVICES_TO_LINE=${DEVICES_TO_LINE:0:$((${#DEVICES_TO_LINE}-1))} IS_RECONFIGURE_SELENOID="false" if [ -z "$DEVICES_REGISTRY_PREV" ]; then IS_RECONFIGURE_SELENOID="true" else IFS=',' read -r -a DEVICE_UDIDS <<< "$DEVICES_REGISTRY_PREV" #previously registry device was disconnected for UDID in "${DEVICE_UDIDS[@]}" do DEVICE_LINE=$(echo $DEVICES | grep $UDID) if [ -z "$DEVICE_LINE" ]; then IS_RECONFIGURE_SELENOID="true" fi done #registry new device was connected for ((DEVICE_INDEX=1; DEVICE_INDEX <= $((DEVICES_COUNT)); DEVICE_INDEX++)) do DEVICE=$(echo $DEVICES | cut -d' ' -f$DEVICE_INDEX) DEVICE_LINE=$(echo $DEVICES_REGISTRY_PREV | grep $DEVICE) if [ -z "$DEVICE_LINE" ]; then IS_RECONFIGURE_SELENOID="true" fi done fi if [ "$IS_RECONFIGURE_SELENOID" = "true" ]; then echo "Reconfigure selenoid" $SELENOID_HOME/"$SELENOID_SCRIPT_NAME" reconfigure & DEVICES_TO_LINE="" for ((DEVICE_INDEX=1; DEVICE_INDEX <= $((DEVICES_COUNT)); DEVICE_INDEX++)) do DEVICE=$(echo $DEVICES | cut -d' ' -f$DEVICE_INDEX) APPIUM_CONFIG=$(cat $APPIUM_CONFIG_DIR/$DEVICE.json 2> /dev/null | grep $DEVICE) SELENOID_CONFIG=$(cat $COMMON_CONFIG_DIR/$SELENOID_CONFIG_NAME 2> /dev/null | grep $DEVICE) if [ -z "$APPIUM_CONFIG" ]; then continue fi if [ -z "$SELENOID_CONFIG" ]; then continue fi DEVICES_TO_LINE=$DEVICE,$DEVICES_TO_LINE done DEVICES_TO_LINE=${DEVICES_TO_LINE:0:$((${#DEVICES_TO_LINE}-1))} echo -n "$DEVICES_TO_LINE" > $DEVICES_FILE fi exit 0
Отслеживание новой версии Selenoid и Selenoid UI. Загрузка и обновление бинарников в случае появления новой версии.
#!/bin/bash function getLatestVersion() { echo $(curl -s $1 | grep "/aerokube/$2/releases/tag" | grep -Eo "[0-9]{1,}[.][0-9]{1,}[.][0-9]{1,}" | head -n1) } function getCurrentVersion() { echo $($SELENOID_HOME/$1 --version 2> /dev/null | grep -Eo "[0-9]{1,}[.][0-9]{1,}[.][0-9]{1,}" || echo $BAD_CODE) } function downloadBinary() { echo "Download $3" DOWNLOAD_URL=$1 DOWNLOAD_URL=${DOWNLOAD_URL/"{os}"/$OS} DOWNLOAD_URL=${DOWNLOAD_URL/"{osPlatform}"/$OS_PLATFORM} DOWNLOAD_URL=${DOWNLOAD_URL/"{latest_version}"/$2} cd "$SELENOID_HOME" || exit $BAD_CODE rm -f $3 curl -s -L -o $SELENOID_HOME/$3 $DOWNLOAD_URL || exit $BAD_CODE chmod 766 $3 cd "$CURRENT_DIR_PATH" || exit $BAD_CODE } if [ "$#" != "1" ]; then echo "Необходимо передать путь до .env" exit 120 fi CURRENT_DIR_PATH=$(realpath "$0" | xargs dirname) PATH_TO_ENV=$(realpath "$1") if [ -f $PATH_TO_ENV ]; then DIR=$(dirname PATH_TO_ENV) cd $DIR || exit $BAD_CODE source $PATH_TO_ENV else echo "Неверно передан путь до .env" exit 120 fi OS=$(uname | tr A-Z a-z) OS_PLATFORM=$(uname -m) BAD_CODE="126" SELENOID_URL="https://github.com/aerokube/selenoid/releases" SELENOID_UI_URL="https://github.com/aerokube/selenoid-ui/releases" SELENOID_DOWNLOAD_URL="https://github.com/aerokube/selenoid/releases/download/{latest_version}/selenoid_{os}_{osPlatform}" SELENOID_UI_DOWNLOAD_URL="https://github.com/aerokube/selenoid-ui/releases/download/{latest_version}/selenoid-ui_{os}_{osPlatform}" SELENOID_LATEST_VERSION=$(getLatestVersion $SELENOID_URL "selenoid") SELENOID_UI_LATEST_VERSION=$(getLatestVersion $SELENOID_UI_URL "selenoid-ui") CURRENT_SELENOID_VERSION=$(getCurrentVersion "selenoid") CURRENT_SELENOID_UI_VERSION=$(getCurrentVersion "selenoid-ui") if [ "$CURRENT_SELENOID_VERSION" = "$BAD_CODE" ]; then CURRENT_SELENOID_VERSION="0.0.0" fi if [ "$CURRENT_SELENOID_UI_VERSION" = "$BAD_CODE" ]; then CURRENT_SELENOID_UI_VERSION="0.0.0" fi OS_PLATFORM=${OS_PLATFORM/"x86_64"/"amd64"} IS_START_SELENOID="false" if [[ "$SELENOID_LATEST_VERSION" != *"$CURRENT_SELENOID_VERSION"* ]]; then downloadBinary $SELENOID_DOWNLOAD_URL $SELENOID_LATEST_VERSION "selenoid" IS_START_SELENOID="true" else echo "Selenoid version latest: $SELENOID_LATEST_VERSION" fi if [[ "$SELENOID_UI_LATEST_VERSION" != *"$CURRENT_SELENOID_UI_VERSION"* ]]; then downloadBinary $SELENOID_UI_DOWNLOAD_URL $SELENOID_UI_LATEST_VERSION "selenoid-ui" IS_START_SELENOID="true" else echo "Selenoid UI version latest: $SELENOID_UI_LATEST_VERSION" fi if [ "$IS_START_SELENOID" = "true" ]; then echo "Start selenoid and selenoid ui" eval "$SELENOID_HOME/$SELENOID_SCRIPT_NAME restart" fi exit 0
Этот скрипт автоматически генерирует конфигурационные файлы для Appium на основе подключённых Android-устройств. Он извлекает список UDID устройств с помощью adb, создаёт для каждого устройства конфигурационный файл с соответствующими параметрами и сохраняет его в формате JSON.
#!/bin/bash #5cc2bd0d-96c5-4567-b82a-a896695af033 - don't delete IFS=" " IDS=$(adb devices -l | grep -v 'List of devices attached' | grep -Eo '[0-9a-zA-Z-]{8,}\s') IDS=$(echo "${IDS}" | tr -d "\r\n") echo "Удаляем конфигурационные файлы:" ls *.json rm -f *.json read -ra UDIDS <<< "$IDS" for UDID in "${UDIDS[@]}" do FILE_NAME=$UDID".json" FILE_CONTENT=$(echo '{ "server": { "address": "127.0.0.1", "allow-cors": true, "allow-insecure": [ "get_server_logs", "adb_shell" ], "base-path": "/wd/hub", "debug-log-spacing": true, "default-capabilities": { "platformName": "android", "appium:androidNaturalOrientation": true, "appium:deviceName": "android", "appium:udid": "udidToReplace", "appium:automationName": "UiAutomator2", "appium:enforceAppInstall": true, "appium:newCommandTimeout": 90, "appium:autoGrantPermissions": false, "appium:noReset": noResetToReplace, "appium:ignoreHiddenApiPolicyError": true, "appium:appActivity": "ru.alfabank.mobile.android.splash.presentation.activity.SplashActivity", "appium:appPackage": "ru.alfabank.mobile.android.feature" }, "log-level": "debug", "log-no-colors": true } }') XIAOMI_LINE=$(adb -s $UDID shell getprop ro.vendor.build.fingerprint | sed 's/\//\n/g' | head -n1 | grep "Xiaomi") if [ -z "$XIAOMI_LINE" ]; then NO_RESET_ENABLE="false"; else NO_RESET_ENABLE="true"; fi FILE_CONTENT=${FILE_CONTENT/udidToReplace/$UDID} FILE_CONTENT=${FILE_CONTENT/noResetToReplace/$NO_RESET_ENABLE} echo "создаем конфигурационный файл "${FILE_NAME} touch $FILE_NAME echo "настройки конфигурационного файла:" FILE_CONTENT_PRETTY=$(echo $FILE_CONTENT | json_reformat 2> /dev/null) if [ "$?" = 0 ]; then echo $FILE_CONTENT_PRETTY > $FILE_NAME else echo $FILE_CONTENT > $FILE_NAME fi cat $FILE_NAME done exit 0 createSelenoidConfig.sh
Этот скрипт на языке Bash предназначен для создания конфигурационного файла Selenoid на основе подключенных Android-устройств.
Листинг:
#!/bin/bash #660368f2-f0cc-49d8-bf2e-4a7d0f2c84d8 - don't delete if [ "$#" != "1" ]; then echo "Необходимо передать путь до .env" exit 120 fi if [ -f $1 ]; then cd $(dirname $1) source $1 cd $COMMON_CONFIG_DIR else exit 120 fi FILE_CONTENT=$(echo '{ "android": { "default": "defaultToReplace", "versions": { versionsToReplace } } }') VERSION=$(echo ' "deviceNameToReplace": { "image": ["pathToAppium", "--config", "configNameToReplace"] }') VERSIONS="" DEVICES_COUNT=$(adb devices -l | wc -l) DEVICES_DEFAULT="" APPIUM_PATH=$(echo $APPIUM_HOME)appium for ((DEVICE_INDEX=2; DEVICE_INDEX <= $((DEVICES_COUNT-1)); DEVICE_INDEX++)) do ADB_LINE=$(adb devices -l | sed $DEVICE_INDEX'!D') USB=$(echo $ADB_LINE | grep -Eo "usb:[0-9-]{1,}" | grep -Eo "[0-9-]{1,}" ) UDID=$(echo $ADB_LINE | grep -Eo "[0-9A-Za-z-]+\s" | head -n1 | xargs -n1 echo | head -n1) if [ -z "$USB" ]; then #for emulators MODEL=$(adb -s $UDID shell getprop ro.boot.qemu.avd_name | sed 's/.*/&/') DEVICE=$(adb -s $UDID shell getprop ro.product.vendor.manufacturer | sed 's/.*/&/') else #for real devices MODEL=$(adb -s $UDID shell getprop ro.product.model | sed 's/.*/\u&/') DEVICE=$(adb -s $UDID shell getprop ro.vendor.build.fingerprint | sed 's/\//\n/g' | head -n1 | sed 's/.*/\u&/') fi DEVICES_DEFAULT="${DEVICE//_/ } ${MODEL//_/ }" PATH_TO_APPIUM_CONFIG_FILE="$APPIUM_CONFIG_DIR/$UDID.json" VER=$(echo $VERSION) VER=${VER/pathToAppium/$APPIUM_PATH} VER=${VER/deviceNameToReplace/$DEVICES_DEFAULT} VER=${VER/configNameToReplace/$PATH_TO_APPIUM_CONFIG_FILE} VERSIONS=$(echo -e "$VER,$VERSIONS") done LENGTH=$(echo ${#VERSIONS}) LENGTH=$((LENGTH-1)) if [ "$LENGTH" != "-1" ]; then echo "Удаляем файл конфигурации: "$SELENOID_CONFIG_NAME rm -f $SELENOID_CONFIG_NAME VERSIONS=${VERSIONS:0:$LENGTH} FILE_CONTENT=${FILE_CONTENT/versionsToReplace/$VERSIONS} FILE_CONTENT=${FILE_CONTENT/defaultToReplace/$DEVICES_DEFAULT} FILE_CONTENT_PRETTY=$(echo $FILE_CONTENT | json_reformat 2> /dev/null) EXIT_CODE=$? echo "Создаем файл конфигурации: "$SELENOID_CONFIG_NAME if [ "$EXIT_CODE" = 0 ]; then echo $FILE_CONTENT_PRETTY > $SELENOID_CONFIG_NAME else echo $FILE_CONTENT > $SELENOID_CONFIG_NAME fi echo "Настройки конфигурационного файла:" cat $SELENOID_CONFIG_NAME fi exit 0 selenoid.sh
Скрипт верхней оркестрации, управляющий скриптами, описанными выше. Позволяет запуска, останавливать, перенастраивать Selenoid с генерацией необходимых конфигурационных файлов.
#!/bin/bash #1b6f0b38-04e2-421c-b122-a54ab8a68bbd function get_pids(){ PIDS=$(ps -ax | grep "$1" | grep -v grep | grep -v ggr | grep -Eo "^\s{0,6}[0-9]+\s" | grep -Eo "[0-9]+" | grep -v "$$") } function kill_procs(){ if [ -n "$1" ]; then echo "$1" | xargs kill $2 > /dev/null 2> /dev/null fi } function kill_appium_procs(){ get_pids "appium" kill_procs "$PIDS" -9 } function kill_selenoid_procs(){ get_pids "./selenoid $SELENOID_ARGS" kill_procs "$PIDS" -9 } function kill_selenoid_ui_procs(){ get_pids "./selenoid-ui $SELENOID_UI_ARGS" kill_procs "$PIDS" -9 } function terminate() { kill_appium_procs kill_procs "$SELENOID_PID" -TERM kill_procs "$SELENOID_UI_PID" -TERM } function start_selenoid(){ cd $SELENOID_HOME chmod 766 selenoid if [ "$VERBOSE" = "true" ]; then clear_crontab echo "Запускаем selenoid:" trap terminate SIGINT SIGTERM ./selenoid $SELENOID_ARGS SELENOID_PID=$! wait else SELENOID_LOG="$SELENOID_LOGS_DIR/selenoid-output-$DATE_TIME.log" touch $SELENOID_LOG ./selenoid $SELENOID_ARGS > $SELENOID_LOG 2> $SELENOID_LOG & fi } function start_selenoid_ui(){ cd $SELENOID_HOME SELENOID_UI_LOG="$SELENOID_LOGS_DIR/selenoid-ui-output-$DATE_TIME.log" touch $SELENOID_UI_LOG chmod 766 selenoid-ui $(sleep 5; ./selenoid-ui $SELENOID_UI_ARGS > $SELENOID_UI_LOG 2> $SELENOID_UI_LOG)& } function createConfigs() { cd $APPIUM_CONFIG_DIR chmod 766 $APPIUM_CONFIG_CREATER ./$APPIUM_CONFIG_CREATER cd $COMMON_CONFIG_DIR chmod 766 $SELENOID_CONFIG_CREATER ./$SELENOID_CONFIG_CREATER $SELENOID_HOME/.env } function start() { createConfigs start_selenoid_ui start_selenoid } function full_kill_procs(){ kill_appium_procs kill_selenoid_ui_procs kill_selenoid_procs } function help() { echo "Ожидается два аргумента:" echo "1) Обязательный команды: help|stop|start|restart" echo "2) Необязательный флаг: --debug" } function clear_crontab(){ echo "Удаляем запись из crontab:" crontab -l crontab -r 2> /dev/null } function add_to_crontab(){ CRONTAB_CMD="$CRON_SETTINGS $DEVICES_WATCHER_PATH" CRONTAB_LINE=$(crontab -l 2> /dev/null) if [[ "$CRONTAB_LINE" == *"$CRONTAB_CMD"* ]]; then echo "Уже был настроен crontab:" crontab -l exit 0 fi if [[ "$CRONTAB_LINE" == *"$DEVICES_WATCHER_PATH"* ]]; then clear_crontab fi chmod 766 $DEVICES_WATCHER_PATH echo "Добавляем запись в crontab:" TEMP_FILE="usbd" echo "$CRON_SETTINGS $DEVICES_WATCHER_PATH $SELENOID_HOME/.env > $SELENOID_LOGS_DIR/cron.log" | tee $TEMP_FILE crontab $TEMP_FILE rm -f $TEMP_FILE } cd $(realpath "$0" | xargs dirname) source .env DATE_TIME=$(date +%Y_%m_%d_%H_%M_%S) HELP_COMMAND=$(echo $@ | grep help) if [ -z "$HELP_COMMAND" ]; then IS_ARG_USE=false DEBUG_MODE=$(echo $@ | grep debug) if [ -z "$DEBUG_MODE" ]; then export VERBOSE=false else export VERBOSE=true fi mkdir -p $SELENOID_LOGS_DIR for flag in "$@" do case "${flag}" in stop) full_kill_procs rm -f $APPIUM_CONFIG_DIR/*.json rm -f $COMMON_CONFIG_DIR/$SELENOID_CONFIG_NAME clear_crontab exit 0 ;; start) start IS_ARG_USE=true ;; restart) full_kill_procs start IS_ARG_USE=true ;; reconfigure) createConfigs get_pids "./selenoid $SELENOID_ARGS" kill -HUP $PIDS exit 0 ;; esac done if [ "$VERBOSE" = "false" ]; then if [ "$IS_ARG_USE" = "false" ]; then help exit 120; fi sleep 6 IS_SELENOID_STARTED=$(ps -x | grep " :$SELENOID_PORT") if [ -z "$IS_SELENOID_STARTED" ]; then echo "Selenoid не был запущен:" cat $SELENOID_LOG exit 120 fi IS_SELENOID_UI_STARTED=$(ps -x | grep " :$SELENOID_UI_PORT") if [ -z "$IS_SELENOID_UI_STARTED" ]; then echo "Selenoid UI не был запущен:" cat $SELENOID_UI_LOG exit 120 fi add_to_crontab fi else help fi exit 0 Шаг 4. Запуск
Наконец, мы приблизились непосредственно к запуску Selenoid и тестированию жизнеспособности нашего окружения.
№1. Запускаем скрипт выполняющий загрузку и обновление бинарников в случае появления новой версии ./updateSelenoidVersion.sh
.
./selenoid.sh start --debug
№2. В другом окне терминала запускаем наш health_check ./check/health_check.sh 127.0.0.1 4444 <имя устройства из команды adb devices>
.
Наблюдаем следующие логи:
2024/06/24 22:21:34 [-] [INIT] [Loading configuration files...] 2024/06/24 22:21:34 [-] [INIT] [Loaded configuration from config/browsers.json] 2024/06/24 22:21:34 [-] [INIT] [Logs Dir: /home/am_user2/selenoid/logs] 2024/06/24 22:21:34 [-] [INIT] [Timezone: Local] 2024/06/24 22:21:34 [-] [INIT] [Listening on :4444] 2024/06/24 22:22:18 [-] [NEW_REQUEST] [unknown] [127.0.0.1] 2024/06/24 22:22:18 [-] [NEW_REQUEST_ACCEPTED] [unknown] [127.0.0.1] 2024/06/24 22:22:18 [7] [LOCATING_SERVICE] [android] [Google Pixel 5] 2024/06/24 22:22:18 [7] [USING_DRIVER] [android] [Google Pixel 5] 2024/06/24 22:22:18 [7] [ALLOCATING_PORT] 2024/06/24 22:22:18 [7] [ALLOCATED_PORT] [35659] 2024/06/24 22:22:18 [7] [STARTING_PROCESS] [[appium --config /home/am_user2/selenoid/config/appium/08221FDD4006R1.json --port=35659]] 2024/06/24 22:22:20 [7] [PROCESS_STARTED] [258659] [1.69s] 2024/06/24 22:22:20 [7] [PROXY_TO] [http://127.0.0.1:35659] 2024/06/24 22:22:20 [7] [SESSION_ATTEMPTED] [http://127.0.0.1:35659] [1] 2024/06/24 22:22:20 [7] [SESSION_ATTEMPTED] [http://127.0.0.1:35659/wd/hub] [2] 2024/06/24 22:22:31 [7] [SESSION_CREATED] [8bf1b30e-1b37-423b-9271-5423c652ba18] [2] [12.41s]
Строка говорит о том, что сессия создана успешно:
2024/06/24 22:22:31 [7] [SESSION_CREATED] [8bf1b30e-1b37-423b-9271-5423c652ba18] [2] [12.41s]
Нюансы
Во время подготовки окружения столкнулись со следующими особенностями — с ошибками при попытке запуске на Xiaomi Mi Mix. Для китайских девайсов могут встречаться проблемы с доступом к управлению приложениями через Appium.
04-19 21:09:59.581 927 927 E libc : Access denied finding property "ro.hardware.fp.fod" 04-19 21:09:59.581 927 927 E libc : Access denied finding property "ro.hardware.fp.sideCap" 04-19 21:09:59.573 927 927 W surfaceflinger: type=1400 audit(0.0:3966956): avc: denied { read } for name="u:object_r:vendor_fp_prop:s0" dev="tmpfs" ino=22608 scontext=u:r:surfaceflinger:s0 tcontext=u:object_r:vendor_fp_prop:s0 tclass=file permissive=0 04-19 21:09:59.588 1716 11879 I Timeline: Timeline: App_transition_ready time:704191047 04-19 21:09:59.588 1716 11879 I Timeline: Timeline: App_transition_stopped time:704191048 04-19 21:09:59.588 5313 5313 D EventBus: [5313, u0] send(AppTransitionFinishedEvent) 04-19 21:09:59.588 5313 5313 D EventBus: [5313, u0] -> ForcedResizableInfoActivityController [0x17a1178, P1] onBusEvent(AppTransitionFinishedEvent) 04-19 21:09:59.588 5313 5313 D EventBus: [5313, u0] onBusEvent(AppTransitionFinishedEvent) duration: 8 microseconds, avg: 20 04-19 21:09:59.591 24287 24287 D Launcher.Lifecycle: onResume:UserHandle{0},67eca74,true 04-19 21:09:59.591 24287 24287 D ScreenElementRoot: resume 04-19 21:09:59.591 1716 2230 E Pm : install msg : Failure [INSTALL_CANCELED_BY_USER
Для решения проблемы в настройках Xiaomi необходимо включить режим разработчика и разрешить отладку по USB. Однако даже после включения этих настроек, устройства могут периодически отключать отладку или требовать повторного подтверждения.
Оптимизация USB: MIUI может включать функции оптимизации USB, которые могут прерывать соединение ADB или ограничивать доступ к устройству.
Также необходимо в конфигах Appium прописать следующие capabilities:
autoGrantPermission=false, noReset=true
Выводы
Эмуляторы подходят для масштабируемого тестирования, когда требуется запускать большое количество тестов параллельно, но при этом увеличивается нагрузка на процессор хоста и может снижаться точность тестов.
А реальные устройства подходят для тестирования с минимальной нагрузкой на процессор хоста и для получения более точных результатов, соответствующих реальным условиям использования.
Выбор между реальными устройствами и эмуляторами зависит от конкретных целей тестирования, доступных ресурсов и необходимой точности результатов. В идеале, лучше использовать комбинированный подход, чтобы воспользоваться преимуществами обоих методов.
Статья написана:
ДАРРП, клиентский путь «Платежи и переводы»:
@wanro,@ILeonteva — подготовка и отладка окружения для тестирования на Android устройствах, написание скриптов и отладка Selenoid.
ДАРРП, клиентский путь «Самозанятые:
@pbezpal— установка и настройка операционной системы.
ссылка на оригинал статьи https://habr.com/ru/articles/833136/
Добавить комментарий