Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Automating Your AVD Test Run - Talk Ver.

F1sherKK
December 01, 2017

Automating Your AVD Test Run - Talk Ver.

Short version (to make in 45min) of presentation about using SDK to create script for automating AVD launch and test run.

F1sherKK

December 01, 2017
Tweet

More Decks by F1sherKK

Other Decks in Technology

Transcript

  1. Goal of this presentation: - Introduce you to ADB executables

    and commands which might be helpful in automating AVD run. - Introduce you to problems that you will need to face in order to create such automation on you own. - Present how to solve those problems. - Give you brief idea how you interact with command line from programming language.
  2. Using command line command from Python code import subprocess def

    execute_shell(cmd, display_cmd=True, display_output=True): if display_cmd: print("- Executing shell command: {}".format(cmd)) with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) as p: output = "" for line in p.stdout: if display_output: print(line.strip()) output += line if p.returncode != 0: raise Exception(p.returncode, p.args) return output
  3. Using command line command from Python code import subprocess def

    execute_shell(cmd, display_cmd=True, display_output=True): if display_cmd: print("- Executing shell command: {}".format(cmd)) with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) as p: output = "" for line in p.stdout: if display_output: print(line.strip()) output += line if p.returncode != 0: raise Exception(p.returncode, p.args) return output Blocking
  4. Restarting ADB Purpose: - Resetting currently undergoing ADB processes in

    order to avoid potential ADB errors: error: protocol fault (couldn't read status): Connection reset by peer Kill ADB Server Start ADB Server Diagram: Any operation that is in grey box, is using execute_shell function
  5. Killing ADB Server Binary file location: Hint: <SDK_location>/platform-tools/adb <SDK_location> can

    be saved in ANDROID_HOME environment variable or ANDROID_SDK_ROOT (ANDROID_HOME got deprecated in Android Studio lately) Command: $ adb kill-server
  6. Killing ADB Server Command line: $ adb kill-server * server

    not running * - ADB server not running case $ adb kill-server - ADB server running case
  7. Starting ADB Server Command line: $ adb start-server * daemon

    not running. starting it now at tcp:5037 * * daemon started successfully * - ADB server not running case $ adb start-server - ADB server running case
  8. Killing all currently launched AVD Purpose: - PROPERLY turn off

    currently running AVD instances. - Free memory allocated by HAXM to allow starting new AVD instances. Get Visible Devices Diagram: List of (device name, status) Loop Next item starts with “emulator” Kill AVD Instance continue No Yes
  9. Getting visible AVD devices Command line: $ adb devices List

    of devices attached emulator-5554 offline - Emulator instance init case $ adb devices List of devices attached emulator-5554 device - Emulator system booting case $ adb devices List of devices emulator-5554 unauthorized - Device unauthorized case
  10. Getting visible AVD devices $ adb devices List of devices

    attached - No devices case Command line:
  11. Killing AVD instance Binary file location: <SDK_location>/platform-tools/adb Command: $ adb

    -s AVD_NAME_IN_ADB emu kill Where: -s - specific device on which command should be executed AVD_NAME_IN_ADB - serial name of AVD which is visible after in ADB e.g after calling `adb devices` command Native AVD are always named in the same pattern: ‘emulator-` + four digit number which is a port to which AVD was assigned (in example emulator-5556)
  12. Waiting for AVD to turn off Purpose: - AVD are

    no longer forcefully killed. There is a save state process and we need to synchronise with that before we make any other AVD related operations. Diagram: Loop Get Visible Devices “emulator” in list No Yes List of (device name, status) sleep(5) break Timeout start time current time - start time >= timeout Yes No
  13. AVD Preparation Part -Creating fresh AVD -Downloading system images -AVD

    configuration -Re-creating AVD -Get available ports -Launching AVD instances -Waiting for AVD instances to properly launch Actions:
  14. Number of AVD instances - Number of instances is limited

    not only to machine RAM capacity but also to RAM limit set in Intel® Hardware Accelerated Execution Manager (Intel® HAXM).
  15. Number of AVD instances - Number of instances is limited

    not only to machine RAM capacity but also to RAM limit set in Intel® Hardware Accelerated Execution Manager (Intel® HAXM). - In the past you had to download HAXM from Intel official website. Now you can do it from Android Studio. RAM limit is set during installation.
  16. Number of AVD instances - Number of instances is limited

    not only to machine RAM capacity but also to RAM limit set in Intel® Hardware Accelerated Execution Manager (Intel® HAXM). - In the past you had to download HAXM from Intel official website. Now you can do it from Android Studio. RAM limit is set during installation. - First statement is not really true. If you start all AVD in the same moment RAM limit won’t kick in. e.g. you can start even 10 AVD at the same time with 2GB RAM limit. Don’t do it, your machine will simply restart from OOM.
  17. Number of AVD instances - Number of instances is limited

    not only to machine RAM capacity but also to RAM limit set in Intel® Hardware Accelerated Execution Manager (Intel® HAXM). - In the past you had to download HAXM from Intel official website. Now you can do it from Android Studio. RAM limit is set during installation. - First statement is not really true. If you start all AVD in the same moment RAM limit won’t kick in. e.g. you can start even 10 AVD at the same time with 2GB RAM limit. Don’t do it, your machine will simply restart from OOM. - Pick number of AVD according to your machine power. If you have enough RAM but not enough computing power AVD will lag and that will affect your tests.
  18. Creating fresh AVD Purpose: - Automatically configure each machine on

    which you would like to run tests. Diagram: List with AVD data Create AVD Next item Loop
  19. Creating fresh AVD Binary file location: <SDK_location>/tools/bin/avdmanager Command: $ avdmanager

    create avd —-name AVD_NAME —-abi AVD_ABI —-package AVD_PACKAGE Required parameters: AVD_NAME - unique alias under which all settings and properties of AVD will be connected to. Can take any value you like. AVD_ABI - application binary interface e.g. x86, x86_64, google_apis/x86_64 (https://developer.android.com/ndk/guides/abis.html) AVD_PACKAGE - system image to be launched. You can pick Android SDK version to be used and if it should support Google Apis or Google Play Store. Example: • system-images;android-23;google_apis;x86_64
  20. Creating fresh AVD Binary file location: <SDK_location>/tools/bin/avdmanager Command: Useful optional

    parameters (for now): AVD_SD_CARD - SD card size in Mb e.g. 512M --force - if AVD exists it will be forcefully overwritten $ avdmanager create avd —-name AVD_NAME —-abi AVD_ABI —-package AVD_PACKAGE —-sdcard AVD_SD_CARD —-force
  21. How to list packages Binary file location: <SDK_location>/tools/bin/sdkmanager Command: $

    sdkmanager —-list —-verbose Where: --list - lists all available packages --verbose - took me a while before I have found this parameter, it unwraps all package names
  22. How to list packages system-images;a...ult;armeabi-v7a | 4 | ARM EABI

    v7a System Image system-images;a...-10;default;x86 | 4 | Intel x86 Atom System Image system-images;a...pis;armeabi-v7a | 5 | Google APIs ARM EABI v7a Syste... system-images;a...google_apis;x86 | 5 | Google APIs Intel x86 Atom Sys… $ sdkmanager —-list VS system-images;android-10;default;armeabi-v7a Description: ARM EABI v7a System Image Version: 4 system-images;android-10;default;x86 Description: Intel x86 Atom System Image Version: 4 system-images;android-10;google_apis;armeabi-v7a Description: Google APIs ARM EABI v7a System Image Version: 5 system-images;android-10;google_apis;x86 Description: Google APIs Intel x86 Atom System Image Version: 5 $ sdkmanager —-list —-verbose
  23. Downloading packages Purpose: We want to be able to provide

    any config we like and make sure that script will fetch all dependencies and files that we are lacking. Diagram: List system- image packages Download Package Next item Loop
  24. Downloading packages Binary file location: <SDK_location>/tools/bin/sdkmanager Command: $ sdkmanager PACKAGE

    Where: PACKAGE - correct package string to be fetched from Google repo
  25. Uninstalling packages Binary file location: <SDK_location>/tools/bin/sdkmanager Command: $ sdkmanager -uninstall

    PACKAGE Where: PACKAGE - correct package string to be fetched from Google repo
  26. Downloading packages Pick package e.g: system-images;android-10;google_apis;x86 And use it: $

    sdkmanager "system-images;android-26;google_apis;x86" [=======================================] 100% Unzipping... x86/data/benchmarkte $ sdkmanager --uninstall "system-images;android-26;google_apis;x86" [=======================================] 100% Fetch remote repository...
  27. Let’s try creating AVD: $ avdmanager create avd --name "testAVD"

    --package "system-images;android-26;google_apis;x86" --abi "google_apis/x86" Do you wish to create a custom hardware profile? [no]
  28. How to handle AVD config? Let’s try creating AVD: If

    you answer `yes` then you will be asked 52 low level questions about where to store cache, where to allocate memory during AVD initial boot, networking, CPU properties and so on… $ avdmanager create avd --name "testAVD" --package "system-images;android-26;google_apis;x86" --abi "google_apis/x86" Do you wish to create a custom hardware profile? [no]
  29. You can automate that… $ echo "answer1 answer2 … answer52"

    | avdmanager create avd --name "testAVD" --package "system-images;android-26;google_apis;x86" --abi "google_apis/x86" But if you are scripting, then you can make it simpler in my opinion. First of all you don’t need to bother with those answers. How to handle AVD config? Let’s try creating AVD: If you answer `yes` then you will be asked 52 low level questions about where to store cache, where to allocate memory during AVD initial boot, networking, CPU properties and so on… $ avdmanager create avd --name "testAVD" --package "system-images;android-26;google_apis;x86" --abi "google_apis/x86" Do you wish to create a custom hardware profile? [no]
  30. How to handle AVD config - method for scripting Create

    AVD and refuse to provide custom hardware config profile: $ echo "no" | avdmanager create avd --name "AVDfromTerminal" --package "system-images;android-23;google_apis;x86" --abi "google_apis/x86" Do you wish to create a custom hardware profile? [no] no
  31. Where AVD is located after creation? Default: ~/.android/avd/ All device

    settings are stored there Current memory state of AVD (Try compressing it and check what happens with size)
  32. Where AVD is located after creation? How to change it?

    1. Set new environment variable named ANDROID_SDK_HOME and put path to which `~/.android/avd/` file structure will be written. 2. Set new environment variable name ANDROID_AVD_HOME to specify directory only for AVD files. 3. Add parameter --path AVD_PATH to your `avdmanager create avd` command. Default: ~/.android/avd/
  33. How to handle AVD config - method for scripting Create

    AVD with AVD Manager via Android Studio without modifying anything - just click through it.
  34. avd.ini.encoding=UTF-8 AvdId=Nexus_5X_API_26 PlayStore.enabled=false abi.type=x86 avd.ini.displayname=Nexus 5X API 26 disk.dataPartition.size=800M hw.accelerometer=yes

    hw.audioInput=yes hw.battery=yes hw.camera.back=emulated hw.camera.front=emulated hw.cpu.arch=x86 hw.cpu.ncore=4 hw.dPad=no hw.device.hash2=MD5:1be89bc42ec9644d4b77968b23474980 hw.device.manufacturer=Google hw.device.name=Nexus 5X hw.gps=yes hw.gpu.enabled=yes hw.gpu.mode=auto hw.initialOrientation=Portrait hw.keyboard=yes hw.lcd.density=420 hw.mainKeys=no hw.ramSize=1536 hw.sdCard=yes hw.sensors.orientation=yes hw.sensors.proximity=yes hw.trackBall=no image.sysdir.1=system-images/android-26/google_apis/x86/ runtime.network.latency=none runtime.network.speed=full sdcard.size=100M showDeviceFrame=yes skin.dynamic=yes skin.name=nexus_5x skin.path=/Users/F1sherKK/Library/Android/sdk/skins/nexus_5x tag.display=Google APIs tag.id=google_apis vm.heapSize=256 Copy content of config.ini file of cerated emulator. Important: Do not use ‘Copy to Clipboard and Close’ function as copied content is different than config.ini file content. (It’s parsed for better readability)
  35. """ avd.ini.encoding=UTF-8 AvdId={} PlayStore.enabled=false abi.type={} avd.ini.displayname={} disk.dataPartition.size={} hw.accelerometer=yes hw.audioInput=yes hw.battery=yes

    hw.camera.back=emulated hw.camera.front=emulated hw.cpu.arch={} hw.cpu.ncore=4 hw.dPad=no hw.device.hash2={} hw.device.manufacturer=Google hw.device.name={} hw.gps=yes hw.gpu.enabled=yes hw.gpu.mode=auto hw.initialOrientation=Portrait hw.keyboard=yes hw.lcd.density={} hw.mainKeys=no hw.ramSize=1536 hw.sdCard=yes hw.sensors.orientation=yes hw.sensors.proximity=yes hw.trackBall=no image.sysdir.1={} runtime.network.latency=none runtime.network.speed=full sdcard.size={} showDeviceFrame=yes skin.dynamic=yes skin.name={} skin.path={} tag.display={} tag.id={} vm.heapSize=256 """ 1. Make string template from it. 2. Every time you create new AVD simply overwrite it’s config.ini. based on AVD_NAME based on AVD_NAME based on AVD_NAME your choice your choice your choice based on AVD_PACKAGE delete it based on AVD_ABI your choice based on AVD_ABI based on AVD_ABI based on AVD_SD_CARD based on AVD_ABI
  36. Get available ports Purpose: - AVD are listed in ADB

    with ‘emulator-port’ name format. - When you create new AVD you want to be able to interact with it and monitor it’s behaviour. If you know port of AVD you have started then you know how to interact with it. Port is a reference to your AVD in ADB. - Google recommends to start AVD on port range 5554 - 5586 so you want to see if those ports are free.
  37. Get available ports Diagram: List of ports to check Loop

    is available store Check Port Availability No Yes Next item Number of needed ports continue List of available ports len of ‘List of available ports’ > ‘Number of needed ports’ exit() No Yes
  38. Check port availability Binary file location: Command: $ lsof -i:PORT

    Where: PORT - is four digit number (e.g. 5554) Build in your system: /usr/sbin/lsof
  39. Check port availability Command line: $ lsof -i:5554 - AVD

    not running case on port 5554 $ lsof -i:5554 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME qemu-syst 46043 F1sherKK 45u IPv4 0x78c5e65ae41cd8ff 0t0 TCP localhost:sgi-esphttp (LISTEN) - AVD launched on port 5554 case Seems like no output - but in reality it returns code 1 which means error! Error because it couldn’t find any resource on port 5554.
  40. Check port availability Command line: $ lsof -i:5555 COMMAND PID

    USER FD TYPE DEVICE SIZE/OFF NODE NAME qemu-syst 46043 F1sherKK 43u IPv4 0x78c5e65ae9a40e17 0t0 TCP localhost:personal-agent (LISTEN) qemu-syst 46043 F1sherKK 44u IPv6 0x78c5e65ae4824a5f 0t0 TCP localhost:personal-agent (LISTEN) qemu-syst 46043 F1sherKK 62u IPv4 0x78c5e65ae5543aef 0t0 TCP localhost:personal-agent-> localhost:57472 (ESTABLISHED) adb 46046 F1sherKK 9u IPv4 0x78c5e65af299f1f7 0t0 TCP localhost:57472-> localhost:personal-agent (ESTABLISHED) - Hint Every time you reserve port for AVD - additional one is taken by ADB. AVD has to be started only on ports with even number! Uneven ports are reserved for ADB interaction with AVD.
  41. Starting AVD Binary file location: <SDK_location>/emulator/emulator Command: $ emulator -avd

    AVD_NAME Required parameters: AVD_NAME - unique alias under which all settings and properties of AVD will be connected to.
  42. Starting AVD Binary file location: <SDK_location>/emulator/emulator Command: $ emulator -avd

    AVD_NAME -port AVD_PORT Optional but very helpful parameters: AVD_PORT - port on which AVD will run. Only way to have reference to created AVD from code. Takes 4 digit int value e.g. 5554.
  43. Starting AVD Binary file location: <SDK_location>/emulator/emulator Command: $ emulator -avd

    AVD_NAME -port AVD_PORT -no-boot-anim Optional but very helpful parameters: AVD_PORT - port on which AVD will run. Only way to have reference to created AVD from code. Takes 4 digit int value e.g. 5554 -no-boot-anim - disables boot animation during emulator startup for faster booting
  44. Starting AVD Binary file location: <SDK_location>/emulator/emulator Command: $ emulator -avd

    AVD_NAME -port AVD_PORT -no-boot-anim -gpu on Optional but very helpful parameters: AVD_PORT - port on which AVD will run. Only way to have reference to created AVD from code. Takes 4 digit int value e.g. 5554 -no-boot-anim - disables boot animation during emulator startup for faster booting -gpu on - makes use of machine GPU to render graphics - speed ups AVD performance
  45. Starting AVD Binary file location: <SDK_location>/emulator/emulator Command: $ emulator -avd

    AVD_NAME -port AVD_PORT -no-boot-anim -gpu on -memory AVD_MEMORY Optional but very helpful parameters: AVD_PORT - port on which AVD will run. Only way to have reference to created AVD from code. Takes 4 digit int value e.g. 5554 -no-boot-anim - disables boot animation during emulator startup for faster booting -gpu on - makes use of machine GPU to render graphics - speed ups AVD performance AVD_MEMORY - possibility to extend or decrease memory usage by AVD e.g. -memory 2048
  46. Starting AVD Binary file location: <SDK_location>/emulator/emulator Command: $ emulator -avd

    AVD_NAME -port AVD_PORT -no-boot-anim -gpu on -memory AVD_MEMORY -wipe-data Optional but very helpful parameters: -wipe-data - resets emulator memory -> it resets userdata-qemu.img (current memory state) file to userdata.img (initial memory state) state
  47. Starting AVD Binary file location: <SDK_location>/emulator/emulator Command: $ emulator -avd

    AVD_NAME -port AVD_PORT -no-boot-anim -gpu on -memory AVD_MEMORY -wipe-data -initdata AVD_MEMORY_IMAGE Optional but very helpful parameters: -wipe-data - resets emulator memory -> it resets userdata-qemu.img (current memory state) file to userdata.img (initial memory state) state -initdata - Allows to specify new path to memory image. File under path becomes new userdata.img. But userdata.img is used only on first AVD boot or after using -wipe-data. Therefore -initdata should be always used with -wipe-data. AVD_MEMORY_IMAGE - absolute path to AVD memory image
  48. Edit AVD state -> save it -> load it 1.

    You can start AVD, change any option you like e.g language, set VPN, disable animations, setup pin or fingerprint (if you need to test that) etc. 2. Turn off AVD and copy userdata-qemu to create reusable AVD memory state.
  49. Starting AVD Command line: $ emulator -avd Nexus_5X_API_26 Problems: 1.

    Process which starts take over command line until AVD turns off. 2. No output.
  50. Starting AVD Command line: $ emulator -avd Nexus_5X_API_26 & [1]

    22914 Problems: 1. Process which starts take over command line until AVD turns off. 2. No output. Add “&” to delegate process to different thread. We want delegate process to different thread because it will end when device is turned off. We need to avoid blocking our script main thread after starting AVD.
  51. Starting AVD $ emulator -avd Nexus_5X_API_26 -verbose & [1] 22914

    emulator:Android emulator version 26.0.3.0 (build_id 3965150) emulator:Found AVD name 'Nexus_5X_API_26' emulator:Found AVD target architecture: x86 emulator:argv[0]: 'emulator'; program directory: '/Users/F1sherKK/Library/Android/sdk/ tools' emulator: Found directory: /Users/F1sherKK/Library/Android/sdk/system-images/ android-26/google_apis/x86/ emulator:Probing for /Users/F1sherKK/Library/Android/sdk/system-images/android-26/ google_apis/x86//kernel-ranchu: file exists emulator:Auto-config: -engine qemu2 (based on configuration) emulator: Found directory: /Users/F1sherKK/Library/Android/sdk/system-images/ android-26/google_apis/x86/ emulator:try dir /Users/F1sherKK/Library/Android/sdk/tools emulator:try dir . (…) Problems: 1. Process which starts take over command line until AVD turns off. 2. No output. 3. Too much output. Command line: Add “-verbose” to receive output from AVD.
  52. Starting AVD $ emulator -avd Nexus_5X_API_26 -verbose &> /Users/F1sherKK/log.txt &

    [1] 24475 Problems: 1. Process which starts take over command line until AVD turns off. 2. No output. 3. Too much output. Command line: Add “&> AVD_LOG_PATH” to dump AVD output to file.
  53. Important: - We want to make sure that Start AVD

    is called after AVD has finished launching to prevent machine going OOM. Diagram: Loop Yes sleep(5) exit() Timeout per AVD current time - start time >= timeout No List with AVD data Start AVD Get AVD boot status launched Next item No continue Yes Starting AVD sequentially start time
  54. Getting AVD boot status Binary file location: Command: $ adb

    devices 1. Print currently visible devices and their statuses in ADB. <SDK_location>/platform-tools/adb 2. Get specific property from specific device $ adb -s ADB_DEVICE shell getprop DEVICE_PROPERTY
  55. AVD boot stages $ adb devices List of devices attached

    emulator-5554 device Status ‘device’ but animation is still running…
  56. AVD boot stages $ adb -s emulator-5554 shell getprop |

    grep -e "dev.bootcomplete" -e "sys.boot_completed" -e "init.svc.bootanim" [init.svc.bootanim]: [running] Parameters we can wait for: - dev.bootcomplete - sys.boot_completed - init.svc.bootanim
  57. AVD boot stages $ adb -s emulator-5554 shell getprop |

    grep -e "dev.bootcomplete" -e "sys.boot_completed" -e "init.svc.bootanim" [dev.bootcomplete]: [1] [init.svc.bootanim]: [stopped] [sys.boot_completed]: [1]
  58. .apk preparation Part -Building app and test .apk files -Picking

    .apk files for test -Inspecting your .apk data -Retrieving instrumentation runner and app package from .apk -Checking if app is installed on device -Installing and uninstalling .apk on/from device Actions:
  59. Build app .apk and test .apk files Purpose: - Making

    sure you always have complete of both, up to date .apk files and with right versions. Diagram: Gradle task names Build .apk files Clean
  60. Binary file location: <android_project_dir>/gradlew Command: $ gradlew clean GRADLE_BUILD_TASK GRADLE_BUILD_TASK

    Gradle tasks: clean - removes whole buildDir GRADLE_BUILD_TASK - task name depends on project, it builds specific type/flavour of android application Build app .apk and test .apk files
  61. Binary file location: <android_project_dir>/gradlew Command: $ gradlew clean GRADLE_BUILD_TASK GRADLE_BUILD_TASK

    Gradle tasks: clean - removes whole buildDir GRADLE_BUILD_TASK - task name depends on project, it builds specific type/flavour of android application Build app .apk and test .apk files If you have specific flavour name, build tasks will be camel cased result of: assemble + BUILD_TYPE_NAME + AndroidTest assembleStaging assembleStagingAndroidTest Example:
  62. Picking .apk files for tests Purpose: - If script won’t

    be able to pick .apk file by itself then it won’t be able to run without programmers help. Diagram: .apk name part Pick .apk dir where .apk is stored List with paths to app and test .apk
  63. Picking .apk files for tests …/app-debug-401.apk …/app-debug-402.apk …/app-debug-403.apk …/app-staging-402-androidTest.apk …/app-staging-403-androidTest.apk

    …/app-staging-401.apk …/app-staging-402.apk …/app-staging-403.apk staging Use name of your build_type to filter .apk files:
  64. Picking .apk files for tests …/app-debug-401.apk …/app-debug-402.apk …/app-debug-403.apk …/app-staging-402-androidTest.apk …/app-staging-403-androidTest.apk

    …/app-staging-401.apk …/app-staging-402.apk …/app-staging-403.apk And then inspect .apk files and find pair of two newest ones based on version code. 400002 400003 400001 400002 400003 Names might not contain versioning.
  65. Inspecting .apk file Purpose: - Get version code for .apk

    comparison. - Get instrumentation runner package for further test running. - Get app package and test package for checking if it’s currently on device. Diagram: .apk file path Dump Badging .apk package .apk file path Instrumentation runner package List contents of .zip archive Parse output version code Parse output
  66. Binary file location: <SDK_location>/<build-tools_folder>/aapt Inspecting .apk file aapt - Android

    Asset Packaging Tool Explanation: First obstacle: Script will need to handle build-tools version sorting
  67. Binary file location: <SDK_location>/<build-tools_folder>/aapt Inspecting .apk file aapt - Android

    Asset Packaging Tool Explanation: Command: $ aapt dump badging PATH_TO_APK Where: dump badging - prints the label and icon for the app declared in .apk PATH_TO_APK - path to .apk file, absolute or relative to your current directory
  68. Command line: Kamils-MacBook-Pro-2 F1sherKK$ aapt dump badging /Users/F1sherKK/MyProject/ app/build/outputs/apk/app-staging-403.apk package:

    name=‘com.myapp.debug1’ versionCode='400003' versionName='4.0.3' platformBuildVersionName='7.1.1' sdkVersion:'18' targetSdkVersion:'25' uses-permission: name='android.permission.SET_ANIMATION_SCALE' uses-permission: name='android.permission.DISABLE_KEYGUARD' uses-permission: name='android.permission.WRITE_CONTACTS' uses-permission: name='android.permission.READ_EXTERNAL_STORAGE' uses-permission: name='android.permission.VIBRATE' uses-permission: name='android.permission.INTERNET' uses-permission: name='android.permission.ACCESS_NETWORK_STATE' uses-permission: name='android.permission.WRITE_SYNC_SETTINGS' uses-permission: name='android.permission.READ_SYNC_SETTINGS' uses-permission: name='android.permission.READ_SYNC_STATS' uses-permission: name='android.permission.WAKE_LOCK' uses-permission: name='com.google.android.c2dm.permission.RECEIVE' uses-permission: name='android.permission.MANAGE_ACCOUNTS' uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE' uses-permission: name='android.permission.READ_CONTACTS' uses-permission: name='android.permission.CAMERA' uses-permission: name='android.permission.GET_ACCOUNTS' (…) Inspecting .apk file
  69. Command line: Kamils-MacBook-Pro-2 F1sherKK$ aapt dump badging /Users/F1sherKK/MyProject/ app/build/outputs/apk/app-staging-403.apk package:

    name=‘com.myapp.debug1’ versionCode='400003' versionName='4.0.3' platformBuildVersionName='7.1.1' sdkVersion:'18' targetSdkVersion:'25' uses-permission: name='android.permission.SET_ANIMATION_SCALE' uses-permission: name='android.permission.DISABLE_KEYGUARD' uses-permission: name='android.permission.WRITE_CONTACTS' uses-permission: name='android.permission.READ_EXTERNAL_STORAGE' uses-permission: name='android.permission.VIBRATE' uses-permission: name='android.permission.INTERNET' uses-permission: name='android.permission.ACCESS_NETWORK_STATE' uses-permission: name='android.permission.WRITE_SYNC_SETTINGS' uses-permission: name='android.permission.READ_SYNC_SETTINGS' uses-permission: name='android.permission.READ_SYNC_STATS' uses-permission: name='android.permission.WAKE_LOCK' uses-permission: name='com.google.android.c2dm.permission.RECEIVE' uses-permission: name='android.permission.MANAGE_ACCOUNTS' uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE' uses-permission: name='android.permission.READ_CONTACTS' uses-permission: name='android.permission.CAMERA' uses-permission: name='android.permission.GET_ACCOUNTS' (…) Inspecting .apk file Package and version code to be found in first line.
  70. Binary file location: <SDK_location>/<build-tools_folder>/aapt Inspecting .apk file aapt - Android

    Asset Packaging Tool Explanation: Command: $ aapt l -a PATH_TO_APK Where: l -a - list contents of Zip-compatible archive PATH_TO_APK - path to .apk file, absolute or relative to your current directory
  71. Command line: $ aapt l -a /Users/F1sherKK/MyProject/app/build/outputs/apk/androidTest/staging/app- staging-403-androidTest.apk (…) Android

    manifest: N: android=http://schemas.android.com/apk/res/android E: manifest (line=2) A: package=“com.myapp.debug1.test" (Raw: "com.myapp.debug1.test") A: platformBuildVersionCode=(type 0x10)0x19 (Raw: "25") A: platformBuildVersionName="7.1.1" (Raw: "7.1.1") E: uses-sdk (line=5) A: android:minSdkVersion(0x0101020c)=(type 0x10)0x15 A: android:targetSdkVersion(0x01010270)=(type 0x10)0x19 E: instrumentation (line=9) A: android:label(0x01010001)="Tests for com.myapp.debug1" (Raw: "Tests for com.myapp.debug1”) A: android:name(0x01010003)="com.myapp.instrumentation.config.MyAppTestRunner" (Raw: "com.myapp.instrumentation.config.MyAppTestRunner") A: android:targetPackage(0x01010021)="com.myapp.debug1" (Raw: "com.myapp.debug1") A: android:handleProfiling(0x01010022)=(type 0x12)0x0 A: android:functionalTest(0x01010023)=(type 0x12)0x0 E: application (line=16) A: android:label(0x01010001)="Intents" (Raw: "Intents") A: android:debuggable(0x0101000f)=(type 0x12)0xffffffff E: uses-library (line=19) A: android:name(0x01010003)="android.test.runner" (Raw: "android.test.runner") Inspecting .apk file
  72. Command line: $ aapt l -a /Users/F1sherKK/MyProject/app/build/outputs/apk/androidTest/staging/app- staging-403-androidTest.apk (…) Android

    manifest: N: android=http://schemas.android.com/apk/res/android E: manifest (line=2) A: package=“com.myapp.debug1.test" (Raw: "com.myapp.debug1.test") A: platformBuildVersionCode=(type 0x10)0x19 (Raw: "25") A: platformBuildVersionName="7.1.1" (Raw: "7.1.1") E: uses-sdk (line=5) A: android:minSdkVersion(0x0101020c)=(type 0x10)0x15 A: android:targetSdkVersion(0x01010270)=(type 0x10)0x19 E: instrumentation (line=9) A: android:label(0x01010001)="Tests for com.myapp.debug1" (Raw: "Tests for com.myapp.debug1”) A: android:name(0x01010003)="com.myapp.instrumentation.config.MyAppTestRunner" (Raw: "com.myapp.instrumentation.config.MyAppTestRunner") A: android:targetPackage(0x01010021)="com.myapp.debug1" (Raw: "com.myapp.debug1") A: android:handleProfiling(0x01010022)=(type 0x12)0x0 A: android:functionalTest(0x01010023)=(type 0x12)0x0 E: application (line=16) A: android:label(0x01010001)="Intents" (Raw: "Intents") A: android:debuggable(0x0101000f)=(type 0x12)0xffffffff E: uses-library (line=19) A: android:name(0x01010003)="android.test.runner" (Raw: "android.test.runner") Inspecting .apk file in project that I have checked, there was 15000+ lines We can extract Instrumentation Runner package.
  73. Checking if app is installed on device Purpose: - If

    app package is currently on device then we can remove it before next install attempt to avoid: Diagram: Installation error: INSTALL_FAILED_VERSION_DOWNGRADE Get Packages Installed On Device Next item Loop app and test packages is in list of device packages continue Device Packages List No Yes Uninstall Package
  74. Checking if app is installed on device Binary file location:

    <SDK_location>/platform-tools/adb Command: $ adb -s AVD_NAME_IN_ADB shell pm list packages Where: AVD_NAME_IN_ADB - serial name of AVD which is visible after in ADB e.g after calling `adb devices` command shell - interaction with shell module of adb pm - interaction with adb shell module named package manager list packages - listing all installed packages on device
  75. Command line: $ adb -s emulator-5554 shell pm list packages

    (…) package:com.android.smoketest package:com.android.cts.priv.ctsshim package:com.google.android.youtube package:com.google.android.ext.services package:com.example.android.livecubes package:com.myapp.debug1 package:com.android.providers.telephony package:com.google.android.googlequicksearchbox package:com.android.providers.calendar package:com.android.providers.media package:com.android.protips package:com.android.documentsui package:com.android.externalstorage package:com.android.htmlviewer package:com.android.companiondevicemanager package:com.android.mms.service package:com.android.providers.downloads package:com.google.android.apps.messaging package:com.google.android.configupdater package:com.android.defcontainer package:com.myapp.debug1.test package:com.android.providers.downloads.ui package:com.android.vending (…) Inspecting .apk file
  76. Command line: $ adb -s emulator-5554 shell pm list packages

    (…) package:com.android.smoketest package:com.android.cts.priv.ctsshim package:com.google.android.youtube package:com.google.android.ext.services package:com.example.android.livecubes package:com.myapp.debug1 package:com.android.providers.telephony package:com.google.android.googlequicksearchbox package:com.android.providers.calendar package:com.android.providers.media package:com.android.protips package:com.android.documentsui package:com.android.externalstorage package:com.android.htmlviewer package:com.android.companiondevicemanager package:com.android.mms.service package:com.android.providers.downloads package:com.google.android.apps.messaging package:com.google.android.configupdater package:com.android.defcontainer package:com.myapp.debug1.test package:com.android.providers.downloads.ui package:com.android.vending (…) Inspecting .apk file
  77. Uninstalling package Binary file location: <SDK_location>/platform-tools/adb Command: $ adb -s

    AVD_NAME_IN_ADB shell pm uninstall APP_PACKAGE Where: AVD_NAME_IN_ADB - serial name of AVD which is visible after in ADB e.g after calling `adb devices` command shell - interaction with shell module of adb pm - interaction with adb shell module named package manager APP_PACKAGE - package of Android application or Android application under test
  78. Uninstalling package Command line: $ adb -s emulator-5554 shell pm

    uninstall com.myapp.debug1 Success Uninstalling package happens almost instantly.
  79. Installing .apk Binary file location: <SDK_location>/platform-tools/adb Command: $ adb -s

    AVD_NAME_IN_ADB install APK_FILE_PATH Where: AVD_NAME_IN_ADB - serial name of AVD which is visible after in ADB e.g after calling `adb devices` command APK_FILE_PATH - path to .apk file that should be installed
  80. Installing .apk Command line: $ adb -s emulator-5554 install /Users/F1sherKK/MyApp/build/outputs/

    apk/staging/app-staging-403.apk Success Takes more time - depending on .apk size even up to 20 seconds. You will need to install two .apk files - app and test. Command line: $ adb -s emulator-5554 install /Users/F1sherKK/MyApp/build/outputs/ apk/staging/app-staging-403.apk | adb -s emulator-5554 install /Users/ F1sherKK/MyApp/build/outputs/apk/androidTest/staging/app-staging-403- androidTest.apk Success Two .apk installs single process.
  81. Diagram: test/app .apk filepaths Installing .apk 1st Device Thread 2nd

    Device Thread Install .apk n-th Device Thread join Install .apk Install .apk avd ports
  82. Multithreading vs ADB 1st Device Thread 2nd Device Thread ADB

    call n-th Device Thread ADB call ADB call ADB Server
  83. Multithreading vs ADB 1st Device Thread 2nd Device Thread ADB

    call n-th Device Thread ADB call ADB call ADB Server error: protocol fault (couldn't read status): Connection reset by peer
  84. Multithreading vs ADB 1st Device Thread 2nd Device Thread ADB

    call n-th Device Thread ADB call ADB call ADB Server ADB call ADB call ADB call Call queue 2.5 - 5 sec interval
  85. Running Tests Binary file location: <SDK_location>/platform-tools/adb Command: $ adb -s

    AVD_NAME_IN_ADB shell am instrument -e package JAVA_PACKAGE TEST_APPLICATION_PACKAGE/INSTRUMENTATION_RUNNER_PACKAGE Where: AVD_NAME_IN_ADB - serial name of AVD which is visible after in ADB e.g after calling `adb devices` command shell - interaction with shell module of adb am - interaction with adb shell module named activity manager instrument - interaction with adb shell am module responsible for instrumentation process in which tests run Running tests by java package will allow you to test parts of your app rather than whole flavour. If you have long tests it will allow you to avoid memory problems.
  86. Running Tests JAVA_PACKAGE - package in which .java files containing

    code annotated with @Test are stored package com.example.labs.azimo.note.tests.function.login; import … public class Login_Function_Tests { @Rule public ActivityTestRule<LoginActivity> activityRule = new ActivityTestRule<>(LoginActivity.class, true, false); @Before public void before() {…} @After public void after() {…} @Test public void testLogin_insertWrongEmail_shouldDisplayError() {…} @Test public void testLogin_whenEmailFieldIsEmpty_shouldDisplayError() {…} @Test public void testLogin_whenPasswordFieldIsEmpty_shouldDisplayError() {…} @Test public void testLogin_whenSwitchingFromLoginToRegister_shouldKeepData() {…} all login tests tests below belongs to this package
  87. Running Tests Binary file location: <SDK_location>/platform-tools/adb Command: $ adb -s

    AVD_NAME_IN_ADB shell am instrument -e package JAVA_PACKAGE TEST_APPLICATION_PACKAGE/INSTRUMENTATION_RUNNER_PACKAGE Where: TEST_APPLICATION_PACKAGE - package of your application with ‘.test’ added at the end INSTRUMENTATION_RUNNER_PACKAGE - java package to Instrumentation Runner class which should be used during test session. Running tests by java package will allow you to test parts of your app rather than whole flavour. If you have long tests it will allow you to avoid memory problems. Remember that both values can be automatically scrapped with usage of aapt binary. It will decrease complexity of your input config to script.
  88. Command line: $ adb -s emulator-5554 shell am instrument -e

    package com.example.labs.azimo.note.tests.function.login com.example.labs.azimo.note.test/ com.example.labs.azimo.note.config.AzimoTestRunner Running Tests Problems: 1. Script cannot tell when tests has finished. 2. No info about which tests have passed and which have failed.
  89. Running Tests Binary file location: <SDK_location>/platform-tools/adb Command: $ adb -s

    AVD_NAME_IN_ADB shell am instrument -w -r -e package JAVA_PACKAGE TEST_APPLICATION_PACKAGE/INSTRUMENTATION_RUNNER_PACKAGE Where: -w - instead of starting process in different thread, current thread will be blocked and you will receive basic output -r - turns basic output into raw output so there is more information about tests and their status Running tests by java package will allow you to test parts of your app rather than whole flavour. If you have long tests it will allow you to avoid memory problems.
  90. Command line: $ adb —s emulator-5554 shell am instrument -w

    -r -e package com.example.labs.azimo.note.tests.function.login com.example.labs.azimo.note.test/ com.example.labs.azimo.note.config.AzimoTestRunner INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= com.example.labs.azimo.note.tests.function.login.Login_Function_Tests: INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenEmailFieldIsEmpty_shouldDisplayError (…) Running Tests Problems: 1. Script cannot tell when tests has finished. 2. No info about which tests have passed and which have failed. Using command line tests start instantly!
  91. Command line: $ adb —s emulator-5554 shell am instrument -w

    -r -e package com.example.labs.azimo.note.tests.function.login com.example.labs.azimo.note.test/ com.example.labs.azimo.note.config.AzimoTestRunner Running Tests Test Case Output Test Case Output Test Case Output … Session Result Code + Time
  92. What we can get from passed test case output Test

    case output INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 Displays overall number of tests in package: INSTRUMENTATION_STATUS: numtests=4
  93. What we can get from passed test case output Test

    case output INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 Displays test case name: INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError
  94. What we can get from passed test case output INSTRUMENTATION_STATUS:

    numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 Displays whole test package: INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests Test case output
  95. What we can get from passed test case output INSTRUMENTATION_STATUS:

    numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 Index of current test (can be used to determine how much tests are left): INSTRUMENTATION_STATUS: current=1 Test case output
  96. What we can get from passed test case output INSTRUMENTATION_STATUS:

    numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 STATUS CODE with value 1 means that test has started: INSTRUMENTATION_STATUS_CODE: 1 Test case output
  97. What we can get from passed test case output INSTRUMENTATION_STATUS:

    numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 Repeats itself once: INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 Test case output
  98. What we can get from passed test case output INSTRUMENTATION_STATUS:

    numtests=4 INSTRUMENTATION_STATUS: stream= INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream=. INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 0 STATUS CODE with value 0 means that test has PASSED: INSTRUMENTATION_STATUS_CODE: 0 Test case output
  99. What we can get from failed test case output INSTRUMENTATION_STATUS:

    numtests=4 INSTRUMENTATION_STATUS: stream= com.example.labs.azimo.note.tests.function.login.Login_Function_Tests: INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= Error in testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(com.example.labs.azimo.note.tests.function.login.Login_Function_Tests): org.mockito.exceptions.misusing.InvalidUseOfMatchersException: No matchers found for additional matcher Not(?) -> at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at java.lang.reflect.Method.invoke(Native Method) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at android.support.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80) at android.support.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61) at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:433) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at org.junit.runner.JUnitCore.run(JUnitCore.java:115) at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58) at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:369) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074) INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: stack= org.mockito.exceptions.misusing.InvalidUseOfMatchersException: No matchers found for additional matcher Not(?) -> at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at java.lang.reflect.Method.invoke(Native Method) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at android.support.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80) at android.support.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61) at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:433) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at org.junit.runner.JUnitCore.run(JUnitCore.java:115) at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58) at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:369) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074) INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: -2 Full error stack trace Full error stack trace Test case output
  100. What we can get from failed test case output INSTRUMENTATION_STATUS:

    numtests=4 INSTRUMENTATION_STATUS: stream= com.example.labs.azimo.note.tests.function.login.Login_Function_Tests: INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: 1 INSTRUMENTATION_STATUS: numtests=4 INSTRUMENTATION_STATUS: stream= Error in testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(com.example.labs.azimo.note.tests.function.login.Login_Function_Tests): org.mockito.exceptions.misusing.InvalidUseOfMatchersException: No matchers found for additional matcher Not(?) -> at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at java.lang.reflect.Method.invoke(Native Method) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at android.support.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80) at android.support.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61) at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:433) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at org.junit.runner.JUnitCore.run(JUnitCore.java:115) at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58) at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:369) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074) INSTRUMENTATION_STATUS: id=AndroidJUnitRunner INSTRUMENTATION_STATUS: test=testLogin_whenPasswordFieldIsEmpty_shouldDisplayError INSTRUMENTATION_STATUS: class=com.example.labs.azimo.note.tests.function.login.Login_Function_Tests INSTRUMENTATION_STATUS: stack= org.mockito.exceptions.misusing.InvalidUseOfMatchersException: No matchers found for additional matcher Not(?) -> at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at com.example.labs.azimo.note.tests.function.login.Login_Function_Tests.testLogin_whenPasswordFieldIsEmpty_shouldDisplayError(Login_Function_Tests.java:96) at java.lang.reflect.Method.invoke(Native Method) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at android.support.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80) at android.support.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61) at android.support.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:433) at org.junit.rules.RunRules.evaluate(RunRules.java:20) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runners.Suite.runChild(Suite.java:128) at org.junit.runners.Suite.runChild(Suite.java:27) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at org.junit.runner.JUnitCore.run(JUnitCore.java:115) at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:58) at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:369) at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2074) INSTRUMENTATION_STATUS: current=1 INSTRUMENTATION_STATUS_CODE: -2 status code -2 means test has failed Test case output
  101. Gather test packages in groups as you wish: Running Tests

    com.example.labs.azimo.note.tests.function.login com.example.labs.azimo.note.tests.function.register com.example.labs.azimo.note.tests.function.welcome All_Function_Tests com.example.labs.azimo.note.tests.endtoend.dispatcher com.example.labs.azimo.note.tests.endtoend.login com.example.labs.azimo.note.tests.endtoend.logout com.example.labs.azimo.note.tests.endtoend.notes com.example.labs.azimo.note.tests.endtoend.welcome All_EndToEnd_Tests All_Login_Tests com.example.labs.azimo.note.tests.function.login com.example.labs.azimo.note.tests.endtoend.login
  102. Package … num of device working threads = num of

    devices No n-th Device Thread Run Test Package Test Results Loop Next Line Parse Line n-th Logging Thread Write to .json Yes Thread References List Start Test Thread for next package Next item Check Test Threads Statues Loop is there idle thread with unsaved logs No Yes Start Test Logging Thread join Packages are distributed between all devices. Devices List Package Package
  103. Test Split Problem - when each device takes different package

    All_Login_Tests com.example.labs.azimo.note.tests.function.login Test Case - 10 seconds Test Case - 10 seconds Test Case - 10 seconds com.example.labs.azimo.note.tests.endtoend.login Test Case - 1 minute Test Case - 2 minute Test Case - 1 minute Test Case - 2 minute Test Case - 1 minute Test Case - 2 minute Device 1 - received package function.login Device 2 - received package endtoend.login Execution time took 30 seconds. Execution time took 9 minutes. Device 1 had to wait for Device 2 - 8 minutes 30 seconds while it could take half of tests.
  104. Google - Test Sharding com.example.labs.azimo.note.tests.endtoend.login Test Case - 1 minute

    Test Case - 2 minute Test Case - 1 minute Test Case - 2 minute Test Case - 1 minute Test Case - 2 minute No shards: Shards Number - 3 What do you expect to happen: Test Case - 1 minute Test Case - 2 minute Device 1: endtoend.login shard 1 of 3 Test Case - 1 minute Test Case - 2 minute Device 2: endtoend.login shard 2 of 3 Test Case - 1 minute Test Case - 2 minute Device 3: endtoend.login shard 3 of 3 Execution time 3 min. Execution time 3 min. Execution time 3 min.
  105. Shards Number - 3 What really happens (or can happen

    in different variation): Device 1: endtoend.login shard 1 of 3 Test Case - 1 minute Test Case - 2 minute Device 2: endtoend.login shard 2 of 3 Test Case - 1 minute Test Case - 1 minute Test Case - 2 minute Device 3: endtoend.login shard 3 of 3 Test Case - 2 minute Execution time 0 min. Execution time 3 min. Execution time 6 min. 6 min wasted. 3 min wasted. Google - Test Sharding com.example.labs.azimo.note.tests.endtoend.login Test Case - 1 minute Test Case - 2 minute Test Case - 1 minute Test Case - 2 minute Test Case - 1 minute Test Case - 2 minute No shards:
  106. Running Tests With Shards Binary file location: <SDK_location>/platform-tools/adb Command: $

    adb -s AVD_NAME_IN_ADB shell am instrument -w -r -e package JAVA_PACKAGE -e numShards NUM_SHARDS -e shardIndex SHARD_INDEX TEST_APPLICATION_PACKAGE/INSTRUMENTATION_RUNNER_PACKAGE Where: NUM_SHARDS - number of parts to which inserted package should be divided SHARD_INDEX - index of package part
  107. Instead of using list of packages - prepare test run

    command from each java package: Running Tests with com.example.labs.azimo.note.tests.function.login adb —s emulator-{} shell am instrument -w -r -e package com.example.labs.azimo.note.tests.function.login -e numShards 3 -e shardIndex 0 com.example.labs.azimo.note.test/com.example.labs.azimo.note.config.AzimoTestRunner Emulator port will be added in test thread that will use command adb —s emulator-{} shell am instrument -w -r -e package com.example.labs.azimo.note.tests.function.login -e numShards 3 -e shardIndex 1 com.example.labs.azimo.note.test/com.example.labs.azimo.note.config.AzimoTestRunner adb —s emulator-{} shell am instrument -w -r -e package com.example.labs.azimo.note.tests.function.login -e numShards 3 -e shardIndex 2 com.example.labs.azimo.note.test/com.example.labs.azimo.note.config.AzimoTestRunner
  108. … num of device working threads = num of devices

    No n-th Device Thread Run Test Package Test Results Loop Next Line Parse Line n-th Logging Thread Write to .json Yes Thread References List Start Test Thread for next package Next item Check Test Threads Statues Loop is there idle thread with unsaved logs No Yes Start Test Logging Thread join With this structure the more shards you use the less idle devices you have. Each device receives new launch command instantly after process created with previous one has ended. Devices List Package Launch Cmd … Package Launch Cmd Package Launch Cmd
  109. Introducing AutomationTestSupervisor https://github.com/AzimoLabs/AutomationTestSupervisor Our own Python tool for launching automation

    tests with ability to: • maintaining AVD instances according to provided .json or working with currently connected devices • building and installing .apk files • specifying test sets according to your liking • speeding up test run (when using packages) by making each device independent • generating HTML dashboard with output, device LogCats and more https://github.com/AzimoLabs/AzimoNote Example project: Tool: