Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Froyo compatibility #10

Open
wants to merge 7 commits into from

2 participants

@marcosdiez

I assumed that you liked my other PRs with exception of the missing camelCase.

So here I fixed all the camelCase issues. Also, I added Froyo compatibility. (at least for findWithText() and touch() )

It can be separated better, but I believe it is nevertheless a step forward.

@dtmilano

I think this should be LAYOUT_HEIGHT_PROPERTY. Ideally we should get rid of the GET in properties' names because it's not always there and it would be more consistent.

@dtmilano

I would name it LAYOUT_LEFT_PROPERTY

@dtmilano

I would name it LAYOUT_TOP_PROPERTY

@dtmilano

See the constan I added and the check for

self.build[VERSION_SDK_PROPERTY] > 0

to avoid selecting it if -1. Also see that it's already an int.

@dtmilano

I would do something more generic. I think of a map or array containing the different property names by version.

@dtmilano

Good. Now we test only what's relevant for the OS.

@dtmilano

Don't forget the comments even if they are private. It's the only documentation that exist.

@marcosdiez

I merged the code, renamed all the variables as you requested and commented the functions I created.

@dtmilano
Owner

Thanks. I will take a look at it when I have some time.

@marcosdiez
@dtmilano
Owner

Hi Marcos,
I understand your frustration.
Some of the changes you proposed are now in the master branch (mText for API 10 and ADB autodetection).
To ease my job merging please provide the remaining changes as smaller separate patches and don't forget to include tests for every change. Otherwise I have to create the test myself, as I did for the aforementioned changes, and this slows down the process even more.

Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 22, 2012
  1. @marcosdiez
  2. @marcosdiez

    try_hard_to_find_adb

    marcosdiez authored
Commits on Oct 23, 2012
  1. @marcosdiez
  2. @marcosdiez
Commits on Oct 24, 2012
  1. @marcosdiez
  2. @marcosdiez
Commits on Oct 28, 2012
  1. @marcosdiez

    merge

    marcosdiez authored
This page is out of date. Refresh to see the latest.
Showing with 255 additions and 170 deletions.
  1. +255 −170 AndroidViewClient/src/com/dtmilano/android/viewclient.py
View
425 AndroidViewClient/src/com/dtmilano/android/viewclient.py
@@ -42,18 +42,9 @@
WARNINGS = False
-ANDROID_HOME = os.environ['ANDROID_HOME'] if os.environ.has_key('ANDROID_HOME') else '/opt/android-sdk'
-''' This environment variable is used to locate the I{Android SDK} components needed.
- Set C{ANDROID_HOME} in the process environment to point to the I{Android SDK} installation. '''
VIEW_SERVER_HOST = 'localhost'
VIEW_SERVER_PORT = 4939
-# this is probably the only reliable way of determining the OS in monkeyrunner
-os_name = java.lang.System.getProperty('os.name')
-if os_name.startswith('Windows'):
- ADB = 'adb.exe'
-else:
- ADB = 'adb'
OFFSET = 25
''' This assumes the smallest touchable view on the screen is approximately 50px x 50px
@@ -71,9 +62,14 @@
# some constants for the attributes
TEXT_PROPERTY = 'text:mText'
+LAYOUT_LEFT_PROPERTY = 'layout:mLeft'
+LAYOUT_TOP_PROPERTY = 'layout:mTop'
+LAYOUT_HEIGHT_PROPERTY = 'layout:getHeight()'
+LAYOUT_WIDTH_PROPERTY = 'layout:getWidth()'
+
+
WS = "\xfe" # the whitespace replacement char for TEXT_PROPERTY
-GET_VISIBILITY_PROPERTY = 'getVisibility()'
-LAYOUT_TOP_MARGIN_PROPERTY = 'layout:layout_topMargin'
+LAYOUT_VISIBILITY_PROPERTY = 'getVisibility()'
def __nd(name):
'''
@@ -90,12 +86,12 @@ def __nh(name):
def __ns(name, greedy=False):
'''
NOTICE: this is using a non-greedy (or minimal) regex
-
+
@type name: str
@param name: the name used to tag the expression
@type greedy: bool
@param greedy: Whether the regex is greedy or not
-
+
@return: Returns a named string (only non-whitespace characters allowed)
'''
return '(?P<%s>\S+%s)' % (name, '' if greedy else '?')
@@ -109,7 +105,7 @@ class Window:
def __init__(self, num, winId, activity, wvx, wvy, wvw, wvh, px, py, visibility):
'''
Constructor
-
+
@type num: int
@param num: Ordering number in Window Manager
@type winId: str
@@ -163,7 +159,7 @@ def factory(attrs, device):
'''
View factory
'''
-
+
if attrs.has_key('class'):
clazz = attrs['class']
if clazz == 'android.widget.TextView':
@@ -174,17 +170,17 @@ def factory(attrs, device):
return View(attrs, device)
else:
return View(attrs, device)
-
+
def __init__(self, map, device):
'''
Constructor
-
+
@type map: map
@param map: the map containing the (attribute, value) pairs
@type device: MonkeyDevice
@param device: the device containing this View
'''
-
+
self.map = map
self.device = device
self.children = []
@@ -196,17 +192,17 @@ def __init__(self, map, device):
self.build[VERSION_SDK_PROPERTY] = int(device.getProperty('build.version.sdk'))
except:
self.build[VERSION_SDK_PROPERTY] = -1
-
+
def __getitem__(self, key):
return self.map[key]
-
+
def __getattr__(self, name):
if DEBUG_GETATTR:
print >>sys.stderr, "__getattr__(%s)" % (name)
-
+
# I should try to see if 'name' is a defined method
# but it seems that if I call locals() here an infinite loop is entered
-
+
if self.map.has_key(name):
r = self.map[name]
elif self.map.has_key(name + '()'):
@@ -223,37 +219,37 @@ def __getattr__(self, name):
else:
# Default behavior
raise AttributeError, name
-
+
# if the method name starts with 'is' let's assume its return value is boolean
if name[:2] == 'is':
r = True if r == 'true' else False
-
+
# this should not cached in some way
def innerMethod():
if DEBUG:
print >>sys.stderr, "innerMethod: %s returning %s" % (innerMethod.__name__, r)
return r
-
+
innerMethod.__name__ = name
-
- # this should work, but then there's problems with the arguments of innerMethod
+
+ # this should work, but then there's problems with the arguments of innerMethod
# even if innerMethod(self) is added
#setattr(View, innerMethod.__name__, innerMethod)
#setattr(self, innerMethod.__name__, innerMethod)
-
+
return innerMethod
-
+
def __call__(self, *args, **kwargs):
if DEBUG:
print >>sys.stderr, "__call__(%s)" % (args if args else None)
-
+
def getClass(self):
'''
Gets the View class
-
+
@return: the View class or None if not defined
'''
-
+
try:
return self.map['class']
except:
@@ -262,11 +258,11 @@ def getClass(self):
def getId(self):
'''
Gets the View Id
-
+
@return: the View Id or None if not defined
@see: L{getUniqueId()}
'''
-
+
try:
return self.map['mID']
except:
@@ -274,14 +270,14 @@ def getId(self):
def getParent(self):
return self.parent
-
+
def getText(self):
'''
Gets the text attribute
-
+
@return: the text attribute or None if not defined
'''
-
+
try:
return self.map[TEXT_PROPERTY]
except Exception:
@@ -289,23 +285,23 @@ def getText(self):
def getHeight(self):
try:
- return int(self.map['layout:getHeight()'])
+ return int(self.map[LAYOUT_HEIGHT_PROPERTY])
except:
return 0
def getWidth(self):
try:
- return int(self.map['layout:getWidth()'])
+ return int(self.map[LAYOUT_WIDTH_PROPERTY])
except:
return 0
def getUniqueId(self):
'''
Gets the unique Id of this View.
-
+
@see: L{ViewClient.__splitAttrs()} for a discussion on B{Unique Ids}
'''
-
+
try:
return self.map['uniqueId']
except:
@@ -315,13 +311,13 @@ def getVisibility(self):
'''
Gets the View visibility
'''
-
+
try:
- if self.map[GET_VISIBILITY_PROPERTY] == 'VISIBLE':
+ if self.map[LAYOUT_VISIBILITY_PROPERTY] == 'VISIBLE':
return 0x0
- elif self.map[GET_VISIBILITY_PROPERTY] == 'INVISIBLE':
+ elif self.map[LAYOUT_VISIBILITY_PROPERTY] == 'INVISIBLE':
return 0x4
- elif self.map[GET_VISIBILITY_PROPERTY] == 'GONE':
+ elif self.map[LAYOUT_VISIBILITY_PROPERTY] == 'GONE':
return 0x8
else:
return -2
@@ -332,45 +328,45 @@ def getX(self):
'''
Gets the View X coordinate
'''
-
+
if DEBUG_COORDS:
print >>sys.stderr, "getX(%s %s ## %s)" % (self.getClass(), self.getId(), self.getUniqueId())
x = 0
try:
- if GET_VISIBILITY_PROPERTY in self.map and self.map[GET_VISIBILITY_PROPERTY] == 'VISIBLE':
- if DEBUG_COORDS: print >>sys.stderr, " getX: VISIBLE adding %d" % int(self.map['layout:mLeft'])
- x += int(self.map['layout:mLeft'])
+ if LAYOUT_VISIBILITY_PROPERTY in self.map and self.map[LAYOUT_VISIBILITY_PROPERTY] == 'VISIBLE':
+ if DEBUG_COORDS: print >>sys.stderr, " getX: VISIBLE adding %d" % int(self.map[LAYOUT_LEFT_PROPERTY])
+ x += int(self.map[LAYOUT_LEFT_PROPERTY])
except:
- warnings.warn("View %s has no 'layout:mLeft' property" % self.getId())
-
+ warnings.warn("View %s has no %s property" % ( self.getId() , LAYOUT_LEFT_PROPERTY ))
+
if DEBUG_COORDS: print >>sys.stderr, " getX: returning %d" % (x)
return x
-
+
def getY(self):
'''
Gets the View Y coordinate
'''
-
+
if DEBUG_COORDS:
print >>sys.stderr, "getY(%s %s ## %s)" % (self.getClass(), self.getId(), self.getUniqueId())
y = 0
try:
- if GET_VISIBILITY_PROPERTY in self.map and self.map[GET_VISIBILITY_PROPERTY] == 'VISIBLE':
- if DEBUG_COORDS: print >>sys.stderr, " getY: VISIBLE adding %d" % int(self.map['layout:mTop'])
- y += int(self.map['layout:mTop'])
+ if LAYOUT_VISIBILITY_PROPERTY in self.map and self.map[LAYOUT_VISIBILITY_PROPERTY] == 'VISIBLE':
+ if DEBUG_COORDS: print >>sys.stderr, " getY: VISIBLE adding %d" % int(self.map[LAYOUT_TOP_PROPERTY])
+ y += int(self.map[LAYOUT_TOP_PROPERTY])
except:
- warnings.warn("View %s has no 'layout:mTop' property" % self.getId())
+ warnings.warn("View %s has no %s property" % (self.getId(), LAYOUT_TOP_PROPERTY))
if DEBUG_COORDS: print >>sys.stderr, " getY: returning %d" % (y)
return y
-
+
def getXY(self):
'''
Returns the I{screen} coordinates of this C{View}.
-
+
@return: The I{screen} coordinates of this C{View}
'''
-
+
if DEBUG_COORDS:
print >> sys.stderr, "getXY(%s %s ## %s)" % (self.getClass(), self.getId(), self.getUniqueId())
@@ -409,21 +405,21 @@ def getXY(self):
statusBarOffset = 0
pwx = 0
pwy = 0
-
+
if fw:
if fw.wvy <= sbh: # it's very unlikely that fw.wvy < sbh, that is a window over the statusbar
if DEBUG_STATUSBAR: print >>sys.stderr, "yes, considering offset=", sbh
statusBarOffset = sbh
else:
if DEBUG_STATUSBAR: print >>sys.stderr, "no, ignoring statusbar offset fw.wvy=", fw.wvy, ">", sbh
-
+
if fw.py == fw.wvy:
if DEBUG_STATUSBAR: print >>sys.stderr, "but wait, fw.py == fw.wvy so we are adjusting by ", (fw.px, fw.py)
pwx = fw.px
pwy = fw.py
else:
if DEBUG_STATUSBAR: print >>sys.stderr, "fw.py=%d <= fw.wvy=%d, no adjustment" % (fw.py, fw.wvy)
-
+
if DEBUG_COORDS or DEBUG_STATUSBAR:
print >>sys.stderr, " getXY: returning (%d, %d) ***" % (x+hx+wvx+pwx, y+hy+wvy-statusBarOffset+pwy)
return (x+hx+wvx+pwx, y+hy+wvy-statusBarOffset+pwy)
@@ -432,7 +428,7 @@ def getCoords(self):
'''
Gets the coords of the View
'''
-
+
if DEBUG_COORDS:
print >>sys.stderr, "getCoords(%s %s ## %s)" % (self.getClass(), self.getId(), self.getUniqueId())
@@ -445,20 +441,20 @@ def getPositionAndSize(self):
'''
Gets the position and size (X,Y, W, H)
'''
-
+
(x, y) = self.getXY();
w = self.getWidth()
h = self.getHeight()
return (x, y, w, h)
-
+
def getCenter(self):
'''
Gets the center coords of the View
-
+
@author: U{Dean Morin <https://github.com/deanmorin>}
'''
-
+
(left, top), (right, bottom) = self.getCoords()
x = left + (right - left) / 2
y = top + (bottom - top) / 2
@@ -494,7 +490,7 @@ def __obtainPxPy(self, m):
px = int(m.group('px'))
py = int(m.group('py'))
return (px, py)
-
+
def __dumpWindowsInformation(self):
self.windows = {}
self.currentFocus = None
@@ -513,10 +509,10 @@ def __dumpWindowsInformation(self):
# This is for 4.1 API-16
framesRE = re.compile('^ *Frames: containing=\[%s,%s\]\[%s,%s\] parent=\[%s,%s\]\[%s,%s\]' %
(__nd('cx'), __nd('cy'), __nd('cw'), __nd('ch'), __nd('px'), __nd('py'), __nd('pw'), __nd('ph')))
- contentRE = re.compile('^ *content=\[%s,%s\]\[%s,%s\] visible=\[%s,%s\]\[%s,%s\]' %
+ contentRE = re.compile('^ *content=\[%s,%s\]\[%s,%s\] visible=\[%s,%s\]\[%s,%s\]' %
(__nd('x'), __nd('y'), __nd('w'), __nd('h'), __nd('vx'), __nd('vy'), __nd('vx1'), __nd('vy1')))
policyVisibilityRE = re.compile('mPolicyVisibility=%s ' % __ns('policyVisibility', greedy=True))
-
+
for l in range(len(lines)):
m = widRE.search(lines[l])
if m:
@@ -531,7 +527,7 @@ def __dumpWindowsInformation(self):
py = 0
visibility = -1
policyVisibility = 0x0
-
+
for l2 in range(l+1, len(lines)):
m = widRE.search(lines[l2])
if m:
@@ -541,7 +537,7 @@ def __dumpWindowsInformation(self):
if m:
visibility = int(m.group('visibility'))
if DEBUG_COORDS: print >> sys.stderr, "__dumpWindowsInformation: visibility=", visibility
- if self.build[VERSION_SDK_PROPERTY] == 16:
+ if self.build[VERSION_SDK_PROPERTY] == 16: #jelly bean
m = framesRE.search(lines[l2])
if m:
px, py = self.__obtainPxPy(m)
@@ -549,7 +545,7 @@ def __dumpWindowsInformation(self):
if m:
wvx, wvy = self.__obtainVxVy(m)
wvw, wvh = self.__obtainVwVh(m)
- elif self.build[VERSION_SDK_PROPERTY] == 15:
+ elif self.build[VERSION_SDK_PROPERTY] == 15: #ics
m = containingFrameRE.search(lines[l2])
if m:
px, py = self.__obtainPxPy(m)
@@ -559,18 +555,18 @@ def __dumpWindowsInformation(self):
wvw, wvh = self.__obtainVwVh(m)
else:
warnings.warn("Unsupported Android version %d" % self.build[VERSION_SDK_PROPERTY])
-
+
#print >> sys.stderr, "Searching policyVisibility in", lines[l2]
m = policyVisibilityRE.search(lines[l2])
if m:
policyVisibility = 0x0 if m.group('policyVisibility') == 'true' else 0x8
-
+
self.windows[winId] = Window(num, winId, activity, wvx, wvy, wvw, wvh, px, py, visibility + policyVisibility)
else:
m = currentFocusRE.search(lines[l])
if m:
self.currentFocus = m.group('winId')
-
+
if self.currentFocus in self.windows and self.windows[self.currentFocus].visibility == 0:
if DEBUG_COORDS:
print >> sys.stderr, "__dumpWindowsInformation: focus=", self.currentFocus
@@ -580,12 +576,12 @@ def __dumpWindowsInformation(self):
else:
if DEBUG_COORDS: print >> sys.stderr, "__dumpWindowsInformation: (0,0)"
return (0,0)
-
+
def touch(self, type=MonkeyDevice.DOWN_AND_UP):
'''
Touches the center of this C{View}
'''
-
+
(x, y) = self.getCenter()
if DEBUG_TOUCH:
print >>sys.stderr, "should touch @ (%d, %d)" % (x, y)
@@ -597,7 +593,7 @@ def touch(self, type=MonkeyDevice.DOWN_AND_UP):
self.device.touch(x+10, y+10, MonkeyDevice.UP)
else:
self.device.touch(x, y, type)
-
+
def allPossibleNamesWithColon(self, name):
l = []
for i in range(name.count("_")):
@@ -607,17 +603,17 @@ def allPossibleNamesWithColon(self, name):
def intersection(self, l1, l2):
return list(set(l1) & set(l2))
-
+
def add(self, child):
'''
Adds a child
-
+
@type child: View
@param child: The child to add
'''
child.parent = self
self.children.append(child)
-
+
def __smallStr__(self):
__str = "View["
if "class" in self.map:
@@ -630,7 +626,7 @@ def __smallStr__(self):
__str += "None"
return __str
-
+
def __tinyStr__(self):
__str = "View["
if "class" in self.map:
@@ -639,7 +635,7 @@ def __tinyStr__(self):
__str += " ]"
return __str
-
+
def __str__(self):
__str = "View["
if "class" in self.map:
@@ -661,14 +657,14 @@ class TextView(View):
'''
TextView class.
'''
-
+
pass
class EditText(TextView):
'''
EditText class.
'''
-
+
def type(self, text):
self.touch()
MonkeyRunner.sleep(1)
@@ -678,15 +674,40 @@ def type(self, text):
class ViewClient:
'''
ViewClient is a I{ViewServer} client.
-
+
If not running the ViewServer is started on the target device or emulator and then the port
mapping is created.
'''
- def __init__(self, device, serialno='emulator-5554', adb=os.path.join(ANDROID_HOME, 'platform-tools', ADB), autodump=True):
+
+ @staticmethod
+ def _getSerialNumber(serialno=None):
+ """
+ Returns the serial number of the attached android device/emulator. Tries to autodetect
+ if no serialno is given.
+
+ @type serialno: str
+ @param serialno: "The suggested serial number."
+
+ """
+ if serialno != None:
+ return serialno
+
+ import subprocess
+ cmd = "adb devices |grep -v attached |grep device |head -n 1 | cut -f1"
+ p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ serialno = p.stdout.readline()
+ if serialno != None and len(serialno) > 0:
+ if serialno != None and serialno.find("\n") >= 0:
+ serialno = serialno.split("\n")[0]
+ return serialno
+ return "emulator-5554"
+
+ def __init__(self, device, serialno=None, adb=None, autodump=True):
'''
Constructor
-
+
@type device: MonkeyDevice
@param device: The device running the C{View server} to which this client will connect
@type serialno: str
@@ -696,11 +717,13 @@ def __init__(self, device, serialno='emulator-5554', adb=os.path.join(ANDROID_HO
@type autodump: boolean
@param autodump: whether an automatic dump is performed at the end of this constructor
'''
-
+
+ adb = self._getAdbPath(adb)
+ serialno = ViewClient._getSerialNumber(serialno)
+
if not device:
raise Exception('Device is not connected')
- if not os.access(adb, os.X_OK):
- raise Exception('adb="%s" is not executable. Did you forget to set ANDROID_HOME in the environment?' % adb)
+
if not self.serviceResponse(device.shell('service call window 3')):
try:
self.assertServiceResponse(device.shell('service call window 1 i32 %d' %
@@ -739,43 +762,106 @@ def __init__(self, device, serialno='emulator-5554', adb=os.path.join(ANDROID_HO
except:
self.build[prop] = -1
- if self.build[VERSION_SDK_PROPERTY] > 0 and self.build[VERSION_SDK_PROPERTY] <= 10: # gingerbread 2.3.3
- global TEXT_PROPERTY
- TEXT_PROPERTY = "mText"
+ sdk_version = int(self.build[VERSION_SDK_PROPERTY])
+ if sdk_version > 0:
+ if sdk_version == 9 or sdk_version == 10: # gingerbread
+ global TEXT_PROPERTY
+ TEXT_PROPERTY = "mText"
+ elif sdk_version == 8: # froyo
+ global TEXT_PROPERTY
+ TEXT_PROPERTY = "mText"
+
+ global LAYOUT_LEFT_PROPERTY
+ LAYOUT_LEFT_PROPERTY = "mLeft"
+
+ global LAYOUT_TOP_PROPERTY
+ LAYOUT_TOP_PROPERTY = "mTop"
+
+ global LAYOUT_HEIGHT_PROPERTY
+ LAYOUT_HEIGHT_PROPERTY = 'getHeight()'
+
+ global LAYOUT_WIDTH_PROPERTY
+ LAYOUT_WIDTH_PROPERTY = 'getWidth()'
if autodump:
self.dump()
-
+
+ def _getAdbPath(self, suggested_path):
+ """
+ Searches for adb/adb.exe Tries to use suggested_path. If it can not be found there, looks elsewhere.
+
+ @type suggested_path: str
+ @param suggested_path: "The suggested path where adb/adb.exe is located"
+ """
+ osName = java.lang.System.getProperty('os.name')
+ if osName.startswith('Windows'):
+ adb = 'adb.exe'
+ else:
+ adb = 'adb'
+
+ ANDROID_HOME = os.environ['ANDROID_HOME'] if os.environ.has_key('ANDROID_HOME') else '/opt/android-sdk'
+
+ possibleChoices= [ suggested_path,
+ os.path.join(ANDROID_HOME, 'platform-tools', adb),
+ os.path.join(os.environ['HOME'], "android", 'platform-tools', adb),
+ os.path.join(os.environ['HOME'], "android-sdk", 'platform-tools', adb),
+ adb,
+ ]
+
+ if osName.startswith('Windows'):
+ possibleChoices.append(os.path.join("""C:\Program Files\Android\android-sdk\platform-tools""", adb)),
+ possibleChoices.append(os.path.join("""C:\Program Files (x86)\Android\android-sdk\platform-tools""", adb)),
+ elif osName.startswith('Linux'):
+ possibleChoices.append(os.path.join(os.environ['HOME'], "android-sdk-linux", 'platform-tools', adb)),
+ else:
+ possibleChoices.append(os.path.join(os.environ['HOME'], "android-sdk-mac", 'platform-tools', adb)),
+ possibleChoices.append(os.path.join(os.environ['HOME'], "android-sdk-mac_x86", 'platform-tools', adb)),
+
+ for exeFile in possibleChoices:
+ if exeFile != None and os.access(exeFile, os.X_OK):
+ return exeFile
+
+ for path in os.environ["PATH"].split(os.pathsep):
+ exeFile = os.path.join(path, adb)
+ if exeFile != None and os.access(exeFile, os.X_OK):
+ return exeFile
+
+ raise Exception('adb="%s" is not executable. Did you forget to set ANDROID_HOME in the environment?' % adb)
+
@staticmethod
def __mapSerialNo(serialno):
+ "formats the serial number in a way MonkeyRunner understands it"
+ ipRE = re.compile('\d+\.\d+.\d+.\d+:\d+')
+ if ipRE.match(serialno):
+ return serialno
ipRE = re.compile('\d+\.\d+.\d+.\d+')
if ipRE.match(serialno):
serialno += ':5555'
return serialno
-
+
@staticmethod
def connectToDeviceOrExit(timeout=60, verbose=False, ignoresecuredevice=False):
'''
Connects to a device which serial number is obtained from the script arguments if available
or using the default C{emulator-5554}.
-
+
If the connection is not successful the script exits.
L{MonkeyRunner.waitForConnection()} returns a L{MonkeyDevice} even if the connection failed.
Then, to detect this situation, C{device.wake()} is attempted and if it fails then it is
assumed the previous connection failed.
-
+
@type timeout: int
@param timeout: timeout for the connection
@type verbose: bool
@param verbose: Verbose output
@type ignoresecuredevice: bool
@param ignoresecuredevice: Ignores the check for a secure device
-
+
@return: the device and serialno used for the connection
'''
-
+
progname = os.path.basename(sys.argv[0])
- serialno = sys.argv[1] if len(sys.argv) > 1 else 'emulator-5554'
+ serialno = sys.argv[1] if len(sys.argv) > 1 else ViewClient._getSerialNumber()
if verbose:
print 'Connecting to a device with serialno=%s with a timeout of %d secs...' % (serialno, timeout)
# Sometimes MonkeyRunner doesn't even timeout (i.e. two connections from same process), so let's
@@ -796,36 +882,36 @@ def connectToDeviceOrExit(timeout=60, verbose=False, ignoresecuredevice=False):
print >> sys.stderr, "%s: ERROR: Device is secure, AndroidViewClient won't work." % progname
sys.exit(1)
return device, serialno
-
+
@staticmethod
def traverseShowClassIdAndText(view, extraInfo=None):
'''
Shows the View class, id and text if available.
This function can be used as a transform function to L{ViewClient.traverse()}
-
+
@type view: I{View}
@param view: the View
@type extraInfo: method
- @param extraInfo: the View method to add extra info
- @return: the string containing class, id, and text if available
+ @param extraInfo: the View method to add extra info
+ @return: the string containing class, id, and text if available
'''
-
+
try:
return "%s %s %s%s" % (view.getClass(), view.getId(), view.getText(), " " + extraInfo(view).__str__() if extraInfo != None else '')
except Exception, e:
return "Exception in view=%s: %s" % (view.__smallStr__(), e)
-
+
@staticmethod
def traverseShowClassIdTextAndCenter(view):
'''
Shows the View class, id and text if available.
This function can be used as a transform function to L{ViewClient.traverse()}
-
+
@type view: I{View}
@param view: the View
- @return: the string containing class, id, and text if available
+ @return: the string containing class, id, and text if available
'''
-
+
return ViewClient.traverseShowClassIdAndText(view, View.getCenter)
@staticmethod
@@ -833,14 +919,14 @@ def traverseShowClassIdTextPositionAndSize(view):
'''
Shows the View class, id and text if available.
This function can be used as a transform function to L{ViewClient.traverse()}
-
+
@type view: I{View}
@param view: the View
- @return: the string containing class, id, and text if available
+ @return: the string containing class, id, and text if available
'''
-
+
return ViewClient.traverseShowClassIdAndText(view, View.getPositionAndSize)
-
+
# methods that can be used to transform ViewClient.traverse output
TRAVERSE_CIT = traverseShowClassIdAndText
''' An alias for L{traverseShowClassIdAndText(view)} '''
@@ -848,26 +934,26 @@ def traverseShowClassIdTextPositionAndSize(view):
''' An alias for L{traverseShowClassIdTextAndCenter(view)} '''
TRAVERSE_CITPS = traverseShowClassIdTextPositionAndSize
''' An alias for L{traverseShowClassIdTextPositionAndSize(view)} '''
-
+
def assertServiceResponse(self, response):
'''
Checks whether the response received from the server is correct or raises and Exception.
-
+
@type response: str
@param response: Response received from the server
@raise Exception: If the response received from the server is invalid
'''
-
+
if not self.serviceResponse(response):
raise Exception('Invalid response received from service.')
def serviceResponse(self, response):
'''
Checks the response received from the I{ViewServer}.
-
+
@return: C{True} if the response received matches L{PARCEL_TRUE}, C{False} otherwise
'''
-
+
PARCEL_TRUE = "Result: Parcel(00000000 00000001 '........')\r\n"
if DEBUG:
print >>sys.stderr, "serviceResponse: comparing '%s' vs Parcel(%s)" % (response, PARCEL_TRUE)
@@ -876,11 +962,11 @@ def serviceResponse(self, response):
def setViews(self, received):
'''
Sets L{self.views} to the received value splitting it into lines.
-
+
@type received: str
@param received: the string received from the I{View server}
'''
-
+
if not received or received == "":
raise ValueError("received is empty")
self.views = []
@@ -892,25 +978,25 @@ def setViews(self, received):
def __splitAttrs(self, strArgs, addViewToViewsById=False):
'''
Splits the C{View} attributes in C{strArgs} and optionally adds the view id to the C{viewsById} list.
-
+
Unique Ids
==========
- It is very common to find C{View}s having B{NO_ID} as the Id. This turns very difficult to
+ It is very common to find C{View}s having B{NO_ID} as the Id. This turns very difficult to
use L{self.findViewById()}. To help in this situation this method assigns B{unique Ids} if
C{addViewToViewsById} is C{True}.
-
+
The B{unique Ids} are generated using the pattern C{id/no_id/<number>} with C{<number>} starting
at 1.
-
+
@type strArgs: str
@param strArgs: the string containing the raw list of attributes and values
@type addViewToViewsById: boolean
@param addViewToViewsById: whether to add the parsed list of attributes and values to the
map C{self.viewsById}
-
+
@return: Returns the attributes map.
'''
-
+
# replace the spaces in text:mText to preserve them in later split
# they are translated back after the attribute matches
textRE = re.compile('%s=%s,' % (TEXT_PROPERTY, __nd('len')))
@@ -926,7 +1012,7 @@ def __splitAttrs(self, strArgs, addViewToViewsById=False):
idRE = re.compile("(?P<viewId>id/\S+)")
attrRE = re.compile('%s(?P<parens>\(\))?=%s,(?P<val>[^ ]*)' % (__ns('attr'), __nd('len')), flags=re.DOTALL)
hashRE = re.compile('%s@%s' % (__ns('class'), __nh('oid')))
-
+
attrs = {}
viewId = None
m = idRE.search(strArgs)
@@ -956,7 +1042,7 @@ def __splitAttrs(self, strArgs, addViewToViewsById=False):
else:
if DEBUG:
print >>sys.stderr, attr, "doesn't match"
-
+
if addViewToViewsById:
if not viewId:
# If the view has NO_ID we are assigning a default id here (id/no_id) which is
@@ -978,14 +1064,14 @@ def __splitAttrs(self, strArgs, addViewToViewsById=False):
# been NO_ID repeated multiple times
attrs['uniqueId'] = viewId
self.viewsById[viewId] = attrs
-
+
return attrs
-
+
def __parseTree(self, receivedLines):
'''
Parses the View tree contained in L{self.views}. The tree is created and the root node assigned to L{self.root}.
'''
-
+
self.root = None
self.viewsById = {}
parent = None
@@ -1033,12 +1119,11 @@ def __parseTree(self, receivedLines):
parent.add(child)
treeLevel = newLevel
lastView = child
-
def getRoot(self):
'''
Gets the root node of the C{View} tree
-
+
@return: the root node of the C{View} tree
'''
return self.root
@@ -1046,16 +1131,16 @@ def getRoot(self):
def traverse(self, root="ROOT", indent="", transform=View.__str__, stream=sys.stdout):
'''
Traverses the C{View} tree and prints its nodes.
-
+
The nodes are printed converting them to string but other transformations can be specified
by providing a method name as the C{transform} parameter.
-
+
@type root: L{View}
@param root: the root node from where the traverse starts
@type indent: str
@param indent: the indentation string to use to print the nodes
@type transform: method
- @param transform: a method to use to transform the node before is printed
+ @param transform: a method to use to transform the node before is printed
'''
if not root:
return
@@ -1066,25 +1151,25 @@ def traverse(self, root="ROOT", indent="", transform=View.__str__, stream=sys.st
s = transform(root)
if s:
print >>stream, "%s%s" % (indent, s)
-
+
for ch in root.children:
self.traverse(ch, indent=indent+" ", transform=transform, stream=stream)
def dump(self, windowId=-1, sleep=1):
'''
Dumps the window content.
-
+
Sleep is useful to wait some time before obtaining the new content when something in the
window has changed.
-
+
@type windowId: int
@param windowId: the window id of the window to dump or -1 to dump all windows
@type sleep: int
@param sleep: sleep in seconds before proceeding to dump the content
-
+
@return: the list of Views as C{str} received from the server after being split into lines
'''
-
+
if sleep > 0:
MonkeyRunner.sleep(sleep)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
@@ -1099,7 +1184,7 @@ def dump(self, windowId=-1, sleep=1):
s.close()
if DEBUG:
- self.received = received
+ self.received = received
if DEBUG_RECEIVED:
print >>sys.stderr, "received %d chars" % len(received)
print >>sys.stderr
@@ -1109,7 +1194,7 @@ def dump(self, windowId=-1, sleep=1):
if DEBUG_TREE:
self.traverse(self.root)
-
+
return self.views
def findViewById(self, viewId, root="ROOT"):
@@ -1129,7 +1214,7 @@ def findViewById(self, viewId, root="ROOT"):
if re.match('^id/no_id', viewId):
if root.getUniqueId() == viewId:
return root;
-
+
for ch in root.children:
foundView = self.findViewById(viewId, ch)
if foundView:
@@ -1138,35 +1223,35 @@ def findViewById(self, viewId, root="ROOT"):
def findViewByIdOrRaise(self, viewId, root="ROOT"):
'''
Finds the View or raise a ViewNotFoundException.
-
+
@return: the View found
@raise ViewNotFoundException: raise the exception if View not found
'''
-
+
view = self.findViewById(viewId, root)
if view:
return view
else:
raise ViewNotFoundException("Couldn't find view with ID=%s in tree with root=%s" % (viewId, root))
-
+
def findViewByTag(self, tag, root="ROOT"):
'''
Finds the View with the specified tag
'''
-
+
return self.findViewWithAttribute('getTag()', tag, root)
-
+
def findViewByTagOrRaise(self, tag, root="ROOT"):
'''
Finds the View with the specified tag or raise a ViewNotFoundException
'''
-
+
view = self.findViewWithAttribute('getTag()', tag, root)
if view:
return view
else:
raise ViewNotFoundException("Couldn't find view with tag=%s in tree with root=%s" % (tag, root))
-
+
def __findViewWithAttributeInTree(self, attr, val, root):
if not self.root:
print >>sys.stderr, "ERROR: no root, did you forget to call dump()?"
@@ -1176,7 +1261,7 @@ def __findViewWithAttributeInTree(self, attr, val, root):
root = self.root
if DEBUG: print >>sys.stderr, "__findViewWithAttributeInTree: checking if root=%s has attr=%s == %s" % (root.__smallStr__(), attr, val)
-
+
if root and attr in root.map and root.map[attr] == val:
if DEBUG: print >>sys.stderr, "__findViewWithAttributeInTree: FOUND: %s" % root.__smallStr__()
return root
@@ -1185,9 +1270,9 @@ def __findViewWithAttributeInTree(self, attr, val, root):
v = self.__findViewWithAttributeInTree(attr, val, ch)
if v:
return v
-
+
return None
-
+
def __findViewWithAttributeInTreeThatMatches(self, attr, regex, root, rlist=[]):
if not self.root:
print >>sys.stderr, "ERROR: no root, did you forget to call dump()?"
@@ -1197,7 +1282,7 @@ def __findViewWithAttributeInTreeThatMatches(self, attr, regex, root, rlist=[]):
root = self.root
if DEBUG: print >>sys.stderr, "__findViewWithAttributeInTreeThatMatches: checking if root=%s attr=%s matches %s" % (root.__smallStr__(), attr, regex)
-
+
if root and attr in root.map and regex.match(root.map[attr]):
if DEBUG: print >>sys.stderr, "__findViewWithAttributeInTreeThatMatches: FOUND: %s" % root.__smallStr__()
return root
@@ -1210,7 +1295,7 @@ def __findViewWithAttributeInTreeThatMatches(self, attr, regex, root, rlist=[]):
return v
#print >>sys.stderr, "appending v=%s to rlist=%s" % (v.__smallStr__(), rlist)
#return rlist.append(v)
-
+
return None
#return rlist
@@ -1218,31 +1303,31 @@ def findViewWithAttribute(self, attr, val, root="ROOT"):
'''
Finds the View with the specified attribute and value
'''
-
+
return self.__findViewWithAttributeInTree(attr, val, root)
-
+
def findViewWithAttributeOrRaise(self, attr, val, root="ROOT"):
'''
Finds the View or raise a ViewNotFoundException.
-
+
@return: the View found
@raise ViewNotFoundException: raise the exception if View not found
'''
-
+
view = self.findViewWithAttribute(attr, val, root)
if view:
return view
else:
raise ViewNotFoundException("Couldn't find View with %s='%s' in tree with root=%s" % (attr, val, root))
-
+
def findViewWithAttributeThatMatches(self, attr, regex, root="ROOT"):
'''
Finds the list of Views with the specified attribute matching
regex
'''
-
+
return self.__findViewWithAttributeInTreeThatMatches(attr, regex, root)
-
+
def findViewWithText(self, text, root="ROOT"):
if type(text).__name__ == 'PatternObject':
return self.findViewWithAttributeThatMatches(TEXT_PROPERTY, text, root)
@@ -1261,24 +1346,24 @@ def findViewWithText(self, text, root="ROOT"):
def findViewWithTextOrRaise(self, text, root="ROOT"):
'''
Finds the View or raise a ViewNotFoundException.
-
+
@return: the View found
@raise ViewNotFoundException: raise the exception if View not found
'''
-
+
view = self.findViewWithText(text, root)
if view:
return view
else:
raise ViewNotFoundException("Coulnd't find View with text='%s' in tree with root=%s" % (text, root))
-
+
def getViewIds(self):
'''
Returns the Views map.
'''
return self.viewsById
-
+
def __getFocusedWindowPosition(self):
return self.__getFocusedWindowId()
Something went wrong with that request. Please try again.