Wednesday, March 21, 2012

Selecting the adb device

How many times have your received an error message like this


error: more than one device and emulator


when you run the adb command ?


I guess many, if like me, you normally use several devices an emulators. When this happens, you should obtain the serial number using


$ adb devices


then cut & paste the desired serial number into the command line and run the desired command again. Imagine how much time is wasted if this occurs tens or even hundreds of times during your day.


Hopefully, Linux and Mac OSX (or perhaps Cygwin if you are using Windows) give you the power to change what you don't like, so the following scripts will transparently allow you to select a device from the list when there's more than one a it wasn't specified in the command line.


android-select-device
This script, which is called android-select-device, is the responsibly of prompting the user for the selection of the device.


#! /bin/bash
# selects an android device

PROGNAME=$(basename $0)
UNAME=$(uname)
DEVICE_OPT=
for opt in "$@"
do
   case "$opt" in
      -d|-e|-s)
         DEVICE_OPT=$opt
         ;;
   esac
done
[ -n "$DEVICE_OPT" ] && exit 0
DEV=$(adb devices 2>&1 | tail -n +2 | sed '/^$/d')
if [ -z "$DEV" ]
then
   echo "$PROGNAME: ERROR: There's no connected devices." >&2
   exit 1
fi
N=$(echo "$DEV" | wc -l | sed 's/ //g')

case $N in
1)
   # only one device detected
   D=$DEV
   ;;

*)
   # more than one device detected
   OLDIFS=$IFS
   IFS="
"
   PS3="Select the device to use, <Q> to quit: "
   select D in $DEV
   do
      [ "$REPLY" = 'q' -o "$REPLY" = 'Q' ] && exit 2
      [ -n "$D" ] && break
   done

   IFS=$OLDIFS
   ;;
esac

if [ -z "$D" ]
then
   echo "$PROGNAME: ERROR: target device coulnd't be determined" >&2
   exit 1
fi

# this didn't work on Darwin
# echo "-s ${D%% *}"
echo "-s $(echo ${D} | sed 's/ .*$//')"

my-adb
This is the other component of our solution. This script, which we are calling my-adb will be the adb replacement which ultimately invokes the real adb.

#! /bin/bash
# This command can be used as an alias for adb and it will prompt for the
# device selection if needed
#   alias adb=my-adb

set +x
PROGNAME=$(basename $0)
ADB=$(which adb)
if [ -z "$ADB" ]
then
   echo "$PROGNAME: ERROR: cannot found adb"
   exit 1
fi

set -e
if [ $# == 0 ]
then
   # no arguments
   exec $ADB
elif [ "$1" == 'devices' ]
then
   # adb devices should not accept -s, -e or -d
   exec $ADB devices
else
   # because of the set -e, if selecting the device fails it exits
   S=$(android-select-device "$@")
   exec $ADB $S "$@"
fi


final step
The final step is to put this solution in place. To achieve this we need a way of replacing a normal adb command with the modified version. The alias shell's internal command is the best way of getting this done (you can add it to ~/.bash_aliases):


$ alias adb=my-adb


providing that the scripts are in your PATH and execute permission was granted.
So now, every time you type adb without specifying the target device, android-select-device will prompt you for the selection:



$ adb shell
1) 02783201431feeee device           3) emulator-5554
2) 3832380FA5F30000 device           4) emulator-5556
Select the device to use, <Q> to quit: 1
$

Hope this saves you some time.

Friday, March 16, 2012

Eclipse: working monkeyrunner configuration

This post is intended to help you if you have problems running monkeyrunner from Eclipse.
There are tons of messages floating around describing a variety of problems. It seems that the most problematic platform in this respect is Microsoft Windows, and Linux or Mac OSX are both much less tricky.


Using Android monkeyrunner from Eclipse is one of the all-time most popular post in this blog. Clearly, this indicates that the setup is not as straightforward as it should be, so I decided to post a detailed configuration that has been tested and is the one I mostly use to develop tools like AndroidViewClient, which has been described in latests posts like monkeyrunner: interacting with the Views.


After this brief introduction we are ready to start, firstly my Eclipse Helios configuration:

  •   Android DDMS 16.0.1.v201112150204-238534
  •   Android Development Tools 16.0.1.v201112150204-238534
  •   Android Hierarchy Viewer 16.0.1.v201112150204-238534
  •   Android Traceview 16.0.1.v201112150204-238534
  •   AspectJ Development Tools 2.1.3.e36x-20110622-1300
  •   Cross References tool (XRef) 2.1.3.e36x-20110622-1300
  •   EclEmma Java Code Coverage 2.0.1.201112281951
  •   Eclipse EGit 1.2.0.201112221803-r
  •   Eclipse IDE for Java Developers 1.3.2.20110301-1807
  •   Eclipse JGit 1.2.0.201112221803-r
  •   Eclipse Weaving Service Feature 2.1.3.e36x-20110622-1300
  •   Equinox Weaving SDK 1.0.0.v20100421-79--EVVFNFFsFc
  •   m2e - Maven Integration for Eclipse 1.0.100.20110804-1717
  •   m2e - slf4j over logback logging (Optional) 1.0.100.20110804-1717
  •   PyDev for Eclipse 2.2.0.2011062419
  •   Pydev Mylyn Integration 0.3.0


Following this configuration we will be using one of the AndroidViewClient's example: browser-open-url.py. This is showing its run configuration.

main

arguments

interpreter

refresh

environment

common





Monday, March 12, 2012

monkeyrunner: running unit tests

First of all, I hope you like the new look of the blog. Personally I think the darker background turns the posts more relevant and less distracting.


Let me know what you think.


Now, to the point. We have presented an analyzed here many monkeyrunner scripts and techniques but we haven't explicitly created some unit tests, and this is precisely what we are going to do.


As our Application Under Test (AUT) we are using the well-known Temperature Converter, which was used many times to demonstrate other concepts as well.
The source code is as usual available through github.


Additionally, we are also using the help of AndroidViewClient which is available at github too and was introduced in monkeyrunner: interacting with the Views. The central idea is to use AndroidViewClient facilities to create a unit test. Let's name this script temperature-converter-view-client.mr.

#! /usr/bin/env monkeyrunner

'''
Created on 2012-03-08

@author: diego
'''

import sys
import unittest
import subprocess
import socket
import re
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
import viewclient

class TemperatureConverterActivityTests(unittest.TestCase):

   def setUp(self):
      # connect to the device
      self.device = MonkeyRunner.waitForConnection(60)
      self.assertNotEqual(None, self.device)
      self.device.wake()

      # start TemperatureConverter
      self.device.startActivity(component="com.example.i2at.tc/.TemperatureConverterActivity")

      # clear the field
      for n in range(10):
         self.device.press('DEL', MonkeyDevice.DOWN_AND_UP)

      # create the client
      self.viewclient = viewclient.ViewClient(self.device)


   def tearDown(self):
      pass

   def testConversion(self):
      C = '123'
      F = '253.4'

      MonkeyRunner.sleep(1)
      self.device.type(C)
      MonkeyRunner.sleep(1)

      self.viewclient.dump()

      celsius = self.viewclient.findViewById('id/celsius')['mText']
      fahrenheit = self.viewclient.findViewById('id/fahrenheit')['mText']

      self.assertEqual(celsius, C)
      self.assertEqual(fahrenheit, F)


if __name__ == '__main__':
   unittest.main()

It is noticeable how the use of AndroidViewClient simplifies this test.


So if everything goes well, providing that your emulator or device is running and reachable, you will receive this output for the test run:

$ ./temperature-converter-view-client.mr 
.
----------------------------------------------------------------------
Ran 1 test in 8.801s

OK

Hope this helps.