droidmate-bandits
Strategies for DroidMate-2: A Platform for Android Test Generation.
ISSTA'19 paper: Learning User Interface Element Interactions
Directory Convention
The tool can be downloaded to any directory.
In this document we name the directory where the tool is located as <DM>
By default input apks should be located in <DM>/apks and the output will be store in <DM>/out
Building
DM-2-Bandits is built on top of DroidMate-2 and automatically downloads all dependencies from JitPack and Maven Central.
Environment configuration
It requires both JDK and Android SDK to be installed and their respective environment variables JAVA_HOME and ANDROID_HOME to be configured.
Here is a link with detailed about how to install and the JDK and Android SDK.
The tool is also integrated into the Travis-CI, its configuration file contains all commands used to configure and execute it.
In case of trouble compiling the tool, have a look at the DM-2 wiki
Android Devices
The tool requires a device or emulator to be executed and the device has to be recognized by the adb devices command.
The emulator or device must be connected before executing the tool.
We don't ship a device emulator with the tool to facilitate its integration on different setups, such as physical devices, preferred emulator or external device farms.
Emulator Support
The tool can be used with an emulator.
The Android Virtual Device (AVD) emulator is part of the SDK.
Google provides the following tutorial on how to create a virtual device using Android Studio and this tutorial to start it from the command line.
We mainly use Nexus 5X API 25 or Google Pixel XL API 26.
Physical Devices
Check DM-2 list of supported device models.
Compiling
on <DM> folder execute /gradlew clean build (Linux/OSX) or gradlew.bat clean build (Windows)
The Gradle wrapper will automatically download all necessary dependencies
Testing your installation
To confirm that everything worked fine execute:
./gradlew run --args="--help"
It will list all available configuration parameters.
Obtaining apks
Apks for testing can be downloaded from F-Droid or alternative app stores, such as ApkPure.
Running the experiments
The tool is executed in 2 steps: instrumentation and exploration.
Instrumentation recompile the APK with log statements so that we can monitor the coverage during testing. Exploration executes the app and interacts with it.
By default both steps search for apks in the <DM>/apks directory.
Also, the instrumentation step produces its output in the <DM>/apks directory, while the exploration step stores its results in the <DM>/out/droidMate directory.
Please note that the output folder is cleaned after each exploration, if you sequentially execute multiple experiments your results will be overwritten.
Step 1: Instrumenting apps for code coverage
For the tool to obtain coverage, the apps should first be instrumented.
on <DM> folder execute the command below to instrument an app (it will take the first app from the <DM>/apks folder).
./gradlew run --args="--ExecutionMode-explore=false --ExecutionMode-coverage=true"
Once the instrumentation is complete it produces 2 files:
- Instrumented APK
- List of instrumented statements
Before executing an exploration, remove the original apk file from the input folder.
Step 2: Exploring an app
Executing an exploration with FPS (Fitness Proportionate Selection) -- Only Static Model
To run the experiment with the baseline strategy for 100 clicks, on <DM> folder execute:
./gradlew run --args="-fps true --StatementCoverage-enableCoverage=true --Selectors-randomSeed=0 --Selectors-actionLimit=100"
Executing an exploration with FPS-H (Fitness Proportionate Selection Hybrid) -- Static Model + RL
To run the experiment with the baseline strategy for 100 clicks, on <DM> folder execute:
./gradlew run --args="-fpsh true --StatementCoverage-enableCoverage=true --Selectors-randomSeed=0 --Selectors-actionLimit=100"
Executing an exploration with Epsilon-Greedy -- Only RL
To run the experiment with the baseline strategy for 100 clicks, on <DM> folder execute:
./gradlew run --args="-e true --StatementCoverage-enableCoverage=true --Selectors-randomSeed=0 --Selectors-actionLimit=100"
Executing an exploration with Epsilon-Greedy Hybrid -- Static Model + RL
To run the experiment with the baseline strategy for 100 clicks, on <DM> folder execute:
./gradlew run --args="-eh true --StatementCoverage-enableCoverage=true --Selectors-randomSeed=0 --Selectors-actionLimit=100"
Executing an exploration with Thompson Sampling -- Only RL
To run the experiment with the baseline strategy for 100 clicks, on <DM> folder execute:
./gradlew run --args="-t true --StatementCoverage-enableCoverage=true --Selectors-randomSeed=0 --Selectors-actionLimit=100"
Executing an exploration with Thompson Sampling Hybrid -- Static Model + RL
To run the experiment with the baseline strategy for 100 clicks, on <DM> folder execute:
./gradlew run --args="-th true --StatementCoverage-enableCoverage=true --Selectors-randomSeed=0 --Selectors-actionLimit=100"
Note: The instrumentation keeps the original (non-instrumented) app in the same location, to run only the version with coverage move the original apk to another folder.
Reading the results
The results are by default stored in the <DM>/out/droidMate directory.
The most significant elements of the output folder are:
coverage: contains the statements reached in each exploration action. This data can be compared against the original set of instrumented statements to obtain coverage.model: contains the DM-2 model of the app, with all performed actions (trace*.csv) as well as all observedstatesandwidgets.log: Copy of the device logcat during exploration.
For the paper experiments we used only the reached statements from the <DM>/out/droidMate/coverage.
If the coverage folder is empty you are executing an apk which has not been instrumented. Please check Step 1
Misc comments
List all available arguments:
To list all available parameters, run with --help
./gradlew run --args="--help"
Importing on another project
To import Dm-2 bandits on another project built on top of the DroidMate-2 platform follow the Jitpack instructions
Troubleshooting
Window could not be extracted
If you see the following error message:
14:49:43 WARN [main @coroutine#1] o.droidmate.command.ExploreCommand initial fetch (warnIfNotHomeScreen) failed
java.lang.IllegalStateException: Error: Displayed Windows could not be extracted [DisplayedWindow(w=AppWindow(windowId=969, pkgName=com.android.systemui, hasInputFocus=false, hasFocus=false, boundaries=0:0:1440:84), initialArea=[Rect(0, 0 - 1440, 84)], rootNode=null, isKeyboard=false, layer=1, bounds=Rect(0, 0 - 1440, 84), windowType=3, isLauncher=false)]
at org.droidmate.uiautomator2daemon.uiautomatorExtensions.UiAutomationEnvironment.getDisplayedWindows(UiAutomationEnvironment.kt:191)
at org.droidmate.uiautomator2daemon.uiautomatorExtensions.UiAutomationEnvironment$getDisplayedWindows$1.invokeSuspend(Unknown Source)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)
at kotlinx.coroutines.DispatchedKt.resume(Dispatched.kt:272)
at kotlinx.coroutines.DispatchedKt.dispatch(Dispatched.kt:261)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:218)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:227)
at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:299)
at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.kt:298)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:76)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:53)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:35)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at org.droidmate.uiautomator2daemon.UiAutomator2DaemonDriver.executeCommand(UiAutomator2DaemonDriver.kt:48)
at org.droidmate.uiautomator2daemon.UiAutomator2DaemonServer.onServerRequest(UiAutomator2DaemonServer.kt:42)
at org.droidmate.uiautomator2daemon.UiAutomator2DaemonServer.onServerRequest(UiAutomator2DaemonServer.kt:33)
at org.droidmate.uiautomator2daemon.Uiautomator2DaemonTcpServerBase$ServerRunnable.run(Uiautomator2DaemonTcpServerBase.java:145)
at java.lang.Thread.run(Thread.java:764)
It means that DM-2 could not connect to the accessibility service on the device.
Please restart the device and try the exploration again.
No Android Devices Found exception
If you see a NoAndroidDevicesFoundException please check your device connection with the command adb devices.
A device should be identified by ADB to be used in DM.
Missing Qt/lib
If you are experiencing issues installing the emulator, such as:
[4601566656]:ERROR:android/android-emu/android/qt/qt_setup.cpp:28:Qt library not found at ../emulator/lib64/qt/lib
Could not launch '/usr/local/Caskroom/android-sdk/4333796/tools/../emulator/qemu/darwin-x86_64/qemu-system-i386': No such file or directory
The Android emulator require special host characteristics, such as qemu (e.g. when running on Docker) and a graphics card (to use hardware accelerated rendering). If you are using a virtualized environment (such as Docker), try it in your host OS. Otherwise, have a look at this stack overflow thread.
Other exceptions
Exception in thread "main" org.droidmate.device.android_sdk.AdbWrapperException: Executing 'adb install' failed. Oh my.
at org.droidmate.device.android_sdk.AdbWrapper.installApk(AdbWrapper.kt:149)
at org.droidmate.device.AndroidDevice.installApk(AndroidDevice.kt:361)
at org.droidmate.device.AndroidDevice.reinstallUiAutomatorDaemon(AndroidDevice.kt:372)
at org.droidmate.device.deviceInterface.RobustDevice$reinstallUiAutomatorDaemon2.invokeSuspend(RobustDevice.kt:645)
at org.droidmate.device.deviceInterface.RobustDevice2.invokeSuspend(RobustDevice.kt:645)
at org.droidmate.device.deviceInterface.RobustDevice2.invokeSuspend(RobustDevice.kt:645)
at org.droidmate.device.deviceInterface.RobustDevicereinstallUiAutomatorDaemon2.invoke(RobustDevice.kt)
at org.droidmate.misc.Utils2.invoke(RobustDevice.kt)
at org.droidmate.misc.Utils2.invoke(RobustDevice.kt)
at org.droidmate.misc.UtilsCompanion.retryOnException(Utils.kt:57)
at org.droidmate.misc.UtilsCompanionCompanionCompanionretryOnException1.invokeSuspend(Utils.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
(...)
A problem occured while starting the on-device TCP server used to interact with the device. Please restart the device or emulator.
More
More customization instructions can be found on the DroidMate-2 project.
