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 using browsers for simulation). 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 == \<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 < 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 > 360" | bc`
while [ $too_big -eq 1 ]; do
heading=`echo $heading - 360 | bc`
too_big=`echo "$heading > 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.
2 thoughts to “Using Genymotion to Simulate a Moving Device”
So, I noticed that in the latest version of Genymotion, there is a pause command that you can put into a file. I will be updating my script shortly to make use of this pause feature.
The git repo has a few more scripts added, including some sample gpx files. Additionally, the script was updated to read in one file, and then pause for the time paused in the gpx file