Using Genymotion to Simulate a Moving Device

Testing mobile apps on an emulator requires simulating real-world conditions like device movement, but Genymotion's shell lacks built-in pause support. This post presents a bash script that reads a GPX file to feed time-accurate GPS coordinates—latitude, longitude, elevation, and heading—into a Genymotion emulator at the correct intervals.

Coveros Staff

November 10, 2015

Introduction

For my mobile testing, I’ve been using emulators more and more, as their capabilities improve, and my testing moves beyond the features that simulators can provide (see several of my old posts on ). I have spent a lot of time with Genymotion, as it provides a lot of excellent features, and most of them can be used with the free version. The biggest issues I have run across have been simulating more real world conditions. This post will focus on some scripting to simulate the emulator moving (updating GPS coordinates), while some future posts will focus on other simulations (incoming phone calls, switching networks, limiting bandwidth).

The Problem

I teach a mobile testing course, and one of the simpler points I make and emphasis the class to employ when testing, is that mobile devices move, and should be moved when testing. While that is all well and good, when testing with an emulator, how exactly does that work? After a lot of experimenting with multiple emulators, I determined that Genymotion could update values from a commandline interface. Genymotion comes with what is called the genymotion-shell, and allows multiple inputs to be manipulated programatically. The biggest problem with this input is that it can’t be easily scripted.

The Solution

Although the gneymotion-shell inputs must be entered in one at a time, there are two options that could allow us to enter commands interactively. I first experimented with the -f option, allowing us to populate a file with updating commands/values. Initially, this seemed like the best solution, until I found out that there was no way to implement any sort of pause in between any of the actions. This was not idea, as I wanted to simulate real movement, not movement at insane high velocities.
The second option I looked at was the -c option. This allowed me to pass in a parameter from the commandline, and return back to it once updated. This worked very well for what I wanted to do, as any scripting could be done in any script of my choice. Since I wanted to simulate movement, I looked at some GPS recordings I had, and thought that the gpx format would be idea for passing in movement to Genymotion. I decided to write a script to read in the gpx file, read through each point, and update Genymotion at the appropriate time.
I soon realized that updating each value to genymotion via the -c option was very slow, and I noticed timing issues. I decided to look back at the -f option, and instead of passing it all the values I desired with pauses in it, I would pass it a file with each value I wanted to update: latitude, longitude, elevation, and heading. I could then run this one command after waiting the proper amount of time based on the timestamps in the gpx file.
The basic workflow of the script is as below:

  • Read in our file, and for each line starting with trkpt process it
  • Extract out the latitude, longitude, elevation, and timestamp
  • Write the latitude, longitude, and elevation to a tmp file
  • If we have previous values, calculate the heading
  • Create a time differential from the timestamp to the current time
  • If enough time has passed, execute our update of gps command

The script is shown below, and can be downloaded from here
#!/bin/bash gps="gps.tmp" #clean up our file if [ -f $gps ]; then rm $gps fi #loop through each of our values in our gpx file while IFS='' read -r line || [[ -n "$line" ]]; do #if this is a line with latitude and longitude information on it if [[ $line == &lt;trkpt* ]]; then #extract each 'interesting' value from our trkpt element lat=$(echo "$line" | sed -r 's/[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)T([0-9.-:]+)./\1/g') lon=$(echo "$line" | sed -r 's/[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)T([0-9.-:]+)./\2/g') ele=$(echo "$line" | sed -r 's/[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)T([0-9.-:]+)./\3/g') day=$(echo "$line" | sed -r 's/[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)T([0-9.-:]+)./\4/g') tim=$(echo "$line" | sed -r 's/[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)[^0-9.-]([0-9.-]+)T([0-9.-:]+).*/\5/g') #set our values into one file that can be read into genymotion echo "gps setlatitude $lat" >> $gps echo "gps setlongitude $lon" >> $gps echo "gps setaltitude $ele" >> $gps #calculate our heading, but only do that if we have previous values if [ ! -z "$old_lat" ]; then pi=echo "4*a(1)" | bc -l #figure out the different of our longitude diff_lon=echo $lon - $old_lon | bc #calculate our y value for our heading diff_long_rads=echo "$diff_lon*($pi/180)" | bc -l lat_rads=echo "$lat*($pi/180)" | bc -l y=echo "s($diff_long_rads)*c($lat_rads)" | bc -l #calculate our x value for our heading old_lat_rads=echo "$old_lat*($pi/180)" | bc -l x=echo "c($old_lat_rads)*s($lat_rads) - s($old_lat_rads)*c($lat_rads)*c($diff_long_rads)" | bc -l #calculate our heading from our x and y values heading=echo "a($y/$x)" | bc -l heading=echo "($heading*180)/$pi" | bc -l #if our heading is below 0, add 360 is_neg=echo "$heading &lt; 0" | bc if [ $is_neg -eq 1 ]; then heading=echo $heading + 360 | bc fi #while our heading is above 360, subtract 360 too_big=echo "$heading &gt; 360" | bc while [ $too_big -eq 1 ]; do heading=echo $heading - 360 | bc too_big=echo "$heading &gt; 360" | bc done echo "gps setbearing $heading" >> $gps fi #determine the time difference between our script and local time sys_time=date +%s gps_time=date -d "${day}T${tim}" +%s #the first time we run through this, we want to calculate our timing difference if [ -z "$time_diff" ]; then time_diff=$((sys_time - gps_time)) fi #if it has not yet passed the desired time, then we need to wait while [ $sys_time -lt $((gps_time + time_diff)) ]; do echo "Waiting..." sleep 0.5 sys_time=date +%s done #when the timing is correct, push our updates into genymotion /opt/genymotion/genymotion-shell -f $gps rm $gps #save off our old values old_lat=$lat old_lon=$lon old_ele=$ele old_day=$day old_tim=$tim fi done < "$1"
This has worked quite well for me, but depending on the structure of your gpx file, you might have to change how you parse in your latitude, longitude, elevation, and datetime stamps.

Coveros Staff

Coveros Staff

This post represents the collective insights of the Coveros team. Our staff consists of software experts who bring deep experience in secure agile development, DevOps, testing, and software quality. Over the past 20 years, Coveros has trained more than 30,000 professionals and worked with half of the Fortune 100 companies on mission-critical software development challenges. We draw on this extensive experience to share practical insights, proven strategies, and real-world solutions that help organizations build better software faster and more securely.