Магистратура/Аспирантура по AI с полной стипендией — дедлайн 27 февраля
Support us

Темная сторона SQA Automation.

18 комментариев
Темная сторона SQA Automation.

Всем доброго времени суток!

О чем пойдет речь: практическое решение задачи — как заработать кучу виртуальных денег в популярной Android игре — CSR Racing, приложив минимум усилий (< 2х часов свободного времени). Фактически, как использовать обратную сторону медали автоматизированного тестирования и наглядный пример того, что же такое на самом деле ночная автоматизация и какую выгоду можно от нее получить.

Статья ориентирована на начинающих QA специалистов, геймеров и любителей виртуального драг рейсинга, критика приветствуется :)

Итак, как же автору удалось одновременно обзавестись крутой тачкой в игре и найти критический баг.

1. Небольшое лирическое предисловие о том, как вообще родилась эта идея. Обзор объекта тестирования.

Началось все с того, что Barnes & Noble включили в свою платформу Google Play и как результат, все пользователи Nook-в, включая меня, получили возможность устанавливать любые приложения из Google Play. Затем у меня сломалась машина и, чтобы не скучать в маршрутке/метро/автобусе, я начал брать с собой рабочий Nook HD+. Быстро была найдена интересная игра автомобильной тематики — CSR Racing (в категории от 5 до 10 млн установок). Она бесплатная и обладает превосходной 3D графикой и геймплеем.

Коварство разработчиков крылось в том, что если вы не делаете in-app-purchases, то у вас имеется только 10 единиц топлива (1 единица на заезд) и для получения следующей единицы топлива необходимо подождать 7 минут. Экономика игры построена на двух принципах: вы можете получать деньги от заездов или покупать деньги за реальные доллары, причем расценки такие:

 — Игровые 56 000$ — это совсем небольшая сумма. Нитро 4-го уровня стоит 72, 591$ на Ford Mustang Boss 302, т. е. потратив 10$ реальных денег, мы не сможем даже нитро купить 4-го уровня. Так не пойдет! :)

Получать деньги за победные заезды здорово — но это (на 3-м уровне) ~ 5.700 каждые 7 минут, при стоимости того же нитро в 72, 591. Капля в море.

2. Стратегия действий, для доминирования в игре.

  • Изучить объект тестирования;
  • Найти дефекты или логические просчеты, которые могут открыть дверь к халявному обогащению;
  • Если найдутся дефекты и их можно будет эффективно автоматизировать — сделать это. Если нет — рассмотреть вариант использования найденных дефектов в «ручном» режиме;
  • За счет найденных дефектов заполучить машину превосходящего класса;
  • За счет крутой тачки свести проблемы обогащения (и соответственно развития на текущем уровне) к минимуму.

3. Доступные стратегии обогащения через автоматизацию, без детального изучения объекта тестирования. 

Примечание: Я все это проделывал на Tier 3, где за проигрыш начисляют 425$. На Tier 1 эта сумма всего 60$ :) (Уточнил: можно найти рейс на Tier 1 и с 325$ призовых. Битва с 2-3 crew, что делает разумным автоматизацию проигрыша, даже на первом уровне).

1) Любая победа приносит хорошие деньги, т. к. взамен мы тратим всего лишь 1 единицу топлива. В игре все упирается в топливо. Если его нет, вам не испытать новенькую турбину в состязании. 3 раза в 24 часа можно отправить email другу «в никуда» и получить 3 ед топлива. На общем фоне не сильно меняет картину.

2) Топливо можно получать за золотые фишки, которые переодически даются за определенные ачивки, но это расточительно Нитро за 72, 591 стоит 8 золотых фишек, а заправить 10 ед топлива — 2 фишки. Жуть!. Гипотетически, используя возможности получения доптоплива, можно удлиннить победную серию.

4. Дополнительно найденная стратегия при детальном изучении объекта тестирования.

3) В результате хорошего ad-hoc тестирования пограничных значений топливного бака было обнаружено, что на последней единице топлива можно переигрывать проигранный рейс бесконечно долго. Если вы проигрываете его, единица добавленного топлива через 7 минут будет потрачена, но дальше можно будет продолжать переигрывать рейс снова и снова на «сухом баке». Что примечательно, за проигрыш вы тоже получаете деньги (бонус за удачный старт, бонус за удачное переключение, бонус за просто участие в рейсе). В рейсе с призовыми в 3000, максимум получилось выжать на проигрыше — 1000 (за счет череды удачных переключений с первой на вторую, со второй на первую и так бесконечно до финиша). Выиграть 1000 на проигрыше достаточно тяжело и требует предельной концентрации. Выиграть 500 — не проблема вообще, достаточно одной удачно переключенной передачи. Время рейса — 12 секунд. Итого получаем (с паузами на загрузку и обработку диалогов) 45 секунд на одну итерацию. 7 минут = 420 секунд, а это 9 полных итераций и, в свою очередь, это 4500 100% заработанных игровых денег, что  на 1500 больше, если просто выиграть заезд и подождать 7 минут до следующей единицы топлива. Неплохо :)

Вот тут-то ладошки и вспотели, сами потянулись написать какой-нить автотест. Только вот на чем его писать?

5. Выбор инструмента реализации.

  1. Хотел было сразу взяться за Google UiAutomator (статья в нашем блоге и документация Google), но у него ограничение по минимальной версии платформы — Android 4.1. Сразу отпадает.
  2. Старый добрый adb shell input swipe/tap. Тоже не работает на Android 4.0.3. Более того, проблема возникнет, если необходимо продолжительное нажатие (6-8 секунд удержание педали газа в начале заезда).
  3. Robotium Solo, методом черного ящикас последующим Robotium тестом. В принципе, допустимо, но придется потратиться на получения apk, создание проекта, написания теста, переподписывание apk, его повторную установку. Ну и плюс все сложности, при работе с Robotium.
  4. Monkey Runner? Блин, как я не пытался — не быть нам с Python вместе. Также проблема очень медленного старта теста.
  5. Старый добрый event-driven подход, который выручал нас с тех пор, когда у эмулятора Андроид была красненькая шкурка.

Предпочтение было отдано #5, так, как это самый простой подход не требующий даже блокнонта, можно прямо в терминале написать весь тест: D

6. Выявление маршрута автоматизации необходимого процесса.

Чтобы что-то успешно автоматизировать на продолжительном отрезке времени, необходимо найти циклическую итерацию и запихнуть ее в while (true). В нашем случае этот маршрут состоит из следующих взаимодействий с экраном:

Предусловие: запущена гонка.

  1. Нажать и держать педаль газа 6 секунд — создаем файл acceleration.sh;
  2. Переключить передачу минимум 2 раза (расчитываем время и дотягиваем его тестами), чтобы получить «Perfect shift» хотя бы раз. Это даст нам 500$ за гонку. Создаем файл shift_up.sh;
  3. Нажать на кнопку «Переиграть рейс». Создаем файл restart_race.sh;
  4. Подавить негативный диалог с вопросом о походе к механику или о необходимости попытаться на более простом уровне. Добавим этот клик в файл restart_race.sh.
  5. Вернуться к шагу 1. Создаем файл scenario.sh с бесконечным циклом.

Хм. не самый сложный алгоритм: D

7. Реализация

Для упрощения всех кликов внутри выше описанных файлов, создадим еще один файл — click.sh — и запишем в него сигнатуру клика для нашего устройства. Сигнатура служебного Nook HD+ доподлинно известна (и скорее всего сработает на большинстве 4.0.3 девайсов):

1. Touch down:                                               

sendevent /dev/input/event2 1 330 1
sendevent /dev/input/event2 3 48 14
sendevent /dev/input/event2 3 53 $2
sendevent /dev/input/event2 3 54 $3
sendevent /dev/input/event2 0 0 0

2. Touch up:

sendevent /dev/input/event2 1 330 0
sendevent /dev/input/event2 3 48 0
sendevent /dev/input/event2 3 53 $2
sendevent /dev/input/event2 3 54 $3
sendevent /dev/input/event2 0 0 0

3. Параметр $1 зарезирвируем для вида действия — нажатие экрана (Touch down) или его отпускание (Touch up).

4. Конечный click.sh (не забудте записать этот файл на девайс) принимает 3 параметра — вид нажатия, X, Y и выглядит следующим образом:

case $1 in
click_down)
  sendevent /dev/input/event2 1 330 1
  sendevent /dev/input/event2 3 48 14
  sendevent /dev/input/event2 3 53 $2
  sendevent /dev/input/event2 3 54 $3
  sendevent /dev/input/event2 0 0 0
;;
click_up)
sendevent /dev/input/event2 1 330 0
sendevent /dev/input/event2 3 48 0
sendevent /dev/input/event2 3 53 $2
sendevent /dev/input/event2 3 54 $3
sendevent /dev/input/event2 0 0 0
;;

*)
   echo Wrong parameter: $1;;
esac

Вызов нажатия в 100 200:

adb shell /data/click.sh click_down 100 200
adb shell /data/click.sh click_up 100 200

Обязательно сделать этому скрипту:

adb shell chmod 777 /data/click.sh

А также интерфейсу, который отвечает за сенсорный экран (в нашем случае event2):

adb shell chmod 777 /dev/event2

Иначе клики работать не будут.

С помощью getevent можно получить координаты всех нужных кнопок и запрограммировать наши файлы-функции (далее координаты приведены для Nook HD+ в обратно-ландшафтной ориентации):

#Файл acceleration.sh

echo accelerating!
adb shell /data/./click.sh click_down 784 82
sleep 5 # имитация продолжительного удержания педали газа
adb shell /data/./click.sh click_up 784 82
# Файл shift_up.sh

echo "shift up!"
adb shell /data/./click.sh click_down 1017 473
adb shell /data/./click.sh click_up 1017 473
# Файл restart_race.sh

echo click restart

adb shell /data/./click.sh click_down 1122 1716
sleep .5
adb shell /data/./click.sh click_up 1122 1716

sleep 4 # ожидаем появление возможного негативного диалога

echo suppress possible negative dialog

adb shell /data/./click.sh click_down 913 1064
sleep .5
adb shell /data/./click.sh click_up 913 1064
# Сам сценарий авто теста scenario.sh

while true; do
  echo "-------------------------------------------------"
  echo "Iteration started at: " `date +%d-%m-%y-%H:%M:%S`
 ./acceleration.sh
  sleep 1
  ./shift_up.sh
  sleep 1.4
  ./shift_up.sh
  sleep 1.4
  ./shift_up.sh
  
  sleep 25
  
  echo "Long race sleep is over"
  
  ./restart_race.sh
  sleep 8
 done

Для Windows можно заменить все файлы, кроме того, который записывается на устройство (click.sh), на bat-файлы.

8. Анализ результатов.

1) Самое печальное, что даже под таким простым автоматическим нагрузочным тестированием был найден критический дефект в приложении, который приводит к сбою работы и неожиданному выходу из приложения (это я пытался на русском написать «краш приложения»). Более того, если продолжать игнорировать подобный дефект, через двое суток (в моем случае) это приведет к потере всего вашего накопления, достижений, машин. В общем игра загрузится так, как будто вы первый раз ее установили. Пойманный текст ошибки (вероятно не в нем причина вайпа аккаунта, но этот приводил к регулярным вылетам из игры):

W/Unity   (18032): Player race name requested but nothing or user100000 was returned, defaulting to "You"
W/Unity   (18032):  
W/Unity   (18032): (Filename: ./Runtime/ExportGenerated/AndroidManaged/UnityEngineDebug.cpp Line: 43)
W/Unity   (18032): 
W/Unity   (18032): Player race name requested but nothing or user100000 was returned, defaulting to "You"
W/Unity   (18032):  
W/Unity   (18032): (Filename: ./Runtime/ExportGenerated/AndroidManaged/UnityEngineDebug.cpp Line: 43)
W/Unity   (18032): 
F/Looper  (18032): Could not create wake pipe.  errno=24
F/libc    (18032): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1)
F/Looper  (18032): Could not create wake pipe.  errno=24
F/libc    (18032): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1)
D/DeviceManagerBroadcastReceiver(  384): Updated Battery Level: 73
D/DeviceManagerBroadcastReceiver(  384): Action: android.intent.action.BATTERY_CHANGED
D/DeviceManagerBroadcastReceiver(  384): Set Alarm: false
D/BatteryService(  230): PROCESSVALUES : update plugged:false was:false stat:3 zone:16 was:16 send low:false
I/ActivityManager(  230): Process com.naturalmotion.csrracing (pid 18032) has died.
W/ActivityManager(  230): Scheduling restart of crashed service com.naturalmotion.csrracing/com.bossalien.racer01.CSRNotificationService in 5000ms
W/ActivityManager(  230): Scheduling restart of crashed service com.naturalmotion.csrracing/com.bossalien.racer01.CSRNotificationManager$NotificationIntent in 15000ms
W/ActivityManager(  230): Force removing ActivityRecord{413efa08 com.naturalmotion.csrracing/com.bossalien.racer01.CSRPlayerActivity}: app died, no saved state
I/WindowManager(  230): WIN DEATH: Window{4188c6a0 com.naturalmotion.csrracing/com.bossalien.racer01.CSRPlayerActivity paused=false}
W/WindowManager(  230): Force-removing child win Window{4188caa0 SurfaceView paused=false} from container Window{4188c6a0 com.naturalmotion.csrracing/com.bossalien.racer01.CSRPlayerActivity paused=false}
W/WindowManager(  230): Failed looking up window
W/WindowManager(  230): java.lang.IllegalArgumentException: Requested window android.os.BinderProxy@41ef62f0 does not exist
W/WindowManager(  230):         at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7189)
W/WindowManager(  230):         at com.android.server.wm.WindowManagerService.windowForClientLocked(WindowManagerService.java:7180)
W/WindowManager(  230):         at com.android.server.wm.WindowState$DeathRecipient.binderDied(WindowState.java:1551)
W/WindowManager(  230):         at android.os.BinderProxy.sendDeathNotice(Binder.java:417)
W/WindowManager(  230):         at dalvik.system.NativeStart.run(Native Method)
I/WindowManager(  230): WIN DEATH: null

2) Мне таки удалось заработать около 500 000$, что почти 100 реальных долларов в эквиваленте :) Данный подход был использован на Tier 3, так как на предыдущих в нем не было необходимости и стоимость запчастей была приемлемой. На Tier 3 мне удалось купить McLaren SLR за 260 000$ (для следующего Tier 4) и Ford Mustang Boss 302 за 120 000$ + большинство апгрейдов к нему. 

Видео результат (прошу прощения за низкое качество)

3) Интересный побочный вывод. Даже простейшая нагрузочная ночная автоматизация может вскрывать серьезные проблемы, а чтобы это было еще более эффективно, разумно делать так (в Ubuntu):

Открываем терминал с тремя вкладками. В первой запускаем сам сценарий. Во второй: 

adb logcat -v time > logOutput.txt (сохраняем в файл журнал ошибок с подопытного устройства)

В случае с Nook HD+ (так как я наверняка знаю, какое сообщение говорит о состоянии батареи):

D/DeviceManagerBroadcastReceiver(  384): Updated Battery Level: 73

Можно еще добавить следующую запись:

adb logcat -v time | grep "Updated Battery"

Что даст нам следующий вывод:

05-13 03:27:28.176 D/DeviceManagerBroadcastReceiver( 5126): Updated Battery Level: 56
05-13 03:27:33.285 D/DeviceManagerBroadcastReceiver( 5126): Updated Battery Level: 56
05-13 03:27:43.449 D/DeviceManagerBroadcastReceiver( 5126): Updated Battery Level: 56
05-13 03:27:48.535 D/DeviceManagerBroadcastReceiver( 5126): Updated Battery Level: 56
05-13 03:27:58.684 D/DeviceManagerBroadcastReceiver( 5126): Updated Battery Level: 56
05-13 03:28:08.848 D/DeviceManagerBroadcastReceiver( 5126): Updated Battery Level: 56

Фактически такой подход поможет нам измерять расход батареи под постоянной нагрузкой на продолжительном промежутке времени.

Самые внимательные сразу скажут:  как это можно доверять таким значениям, если устройство подключено по USB проводу к компьютеру и постоянно заряжается! И они будут правы. Чтобы обеспечить подобный замер, необходимо перевести ваш ADB в режим работы по WiFi.

При желании можно еще добавить забор скриншотов с помощью утилиты screencap (идет сейчас в поставке). Простой shell script для взятия скриншотов каждые 3 секунды (если убрать паузу, можно получить видео с 2-3 fps после сборки всех скриншотов в gif или avi):

while true; do
  adb shell /system/bin/screencap -p /sdcard/img.png #перезапись не тратит свободное место
  adb pull /sdcard/img.png "./`date`.png"
  sleep 3
done

Если вы захотите повторить это на вашем устройстве и у вас что-то не получится — пишите, разберемся. Если вам нравится подобный род деятельности и вы бы этим хотели заниматься по-серьезному — тоже пишите, разберемся =)

9. Можно ли было сделать этот тест еще круче?

Да, можно было бы еще добавить несколько простых, но классных штук:

  • С помощью любого планировщика задач стартовать тест каждые 10 минут, если текущая Activity! = CSR Racing;
  • Предусмотреть маршрут входа в нужный цикл зарабатывания виртуальных денег из нулевого состояния приложения (когда мы его загружаем с домашнего экрана);
  • Оптимизировать сценарий по принципу: выиграй 9 гонок, на 10-й включай стратегию заработка на проигрыше в течении N минут (желательно меньше часа, чтобы не происходил краш приложения), затем подожди часок (чтобы заправился бензобак) и начинай снова. Такой сценарий приносил бы больше денег, так как был бы полностью автоматическим и более стабильным;
  • Ваш вариант =)

ЗЫ: за 25 лайков готов написать статью, как записать и воспроизвести пользовательское взаимодействие с Android устройством на shell event-ах (Windows & Ubuntu). Да, да, такой я алчный негодяй ^__^

Читайте также
FullStack наступает. Разбираемся, какие изменения ждут тестирование в 2026 году
FullStack наступает. Разбираемся, какие изменения ждут тестирование в 2026 году
FullStack наступает. Разбираемся, какие изменения ждут тестирование в 2026 году
Вымрут ли автоматические тестировщики? Или, скорее, под угрозой исчезновения, наоборот, ручные? Всех ли заменят FullStack QA? Я в тестировании с 2008 года и поделюсь своими рассуждениями, как изменится профессия тестировщика и в какую стезю стоит идти начинающим QA.
3 комментария
Из TikTok новую профессию не выучишь. Автор курсов по QA рассказывает, почему нам всё сложнее учиться
Из TikTok новую профессию не выучишь. Автор курсов по QA рассказывает, почему нам всё сложнее учиться
Из TikTok новую профессию не выучишь. Автор курсов по QA рассказывает, почему нам всё сложнее учиться
Кажется, что в мире, где любую информацию можно получить в пару кликов, очень легко выучить что-то новое. Но я наблюдаю обратное — людям становиться всё сложнее усваивать новую информацию.  На мой взгляд в мире стало больше труднообучаемых людей. Поделюсь своими наблюдениями, почему так происходит. 
1 комментарий
Готовьтесь писать код. Изучаем требования к QA в 2025 году
Готовьтесь писать код. Изучаем требования к QA в 2025 году
Готовьтесь писать код. Изучаем требования к QA в 2025 году
Так ли важно знать мануальным QA основы языков программирования? Без каких навыков сейчас джунам не найти работу?  Я повторил своё прошлогоднее исследование и проанализировал ключевые тенденции рынка труда, чтобы вы могли объективно оценить свои шансы найти работу в разных регионах.
6 комментариев
Из мясокомбината в ИТ. Карьерная консультантка разбирает резюме начинающего PM
Из мясокомбината в ИТ. Карьерная консультантка разбирает резюме начинающего PM
Из мясокомбината в ИТ. Карьерная консультантка разбирает резюме начинающего PM
Найти работу в ИТ после… мясокомбината. Такую цель поставила перед собой наша читательница Анастасия. Она 4 года работала на Салтовском мясокомбинате, но позже закончила курсы QA, сейчас изучает проджект-менеджмент и хочет «войти в ИТ». Карьерная консультантка и HR-generalist Алина Бондаренко подробно разбирает её резюме и советует, как его улучшить — и для рекрутера, и для ATS.
7 комментариев

Хотите сообщить важную новость? Пишите в Telegram-бот

Главные события и полезные ссылки в нашем Telegram-канале

Обсуждение
Комментируйте без ограничений

Релоцировались? Теперь вы можете комментировать без верификации аккаунта.

Комментариев пока нет.