#!C:\tools\Python27\python.exe # -*- coding: utf-8 -*- ''' Copyright (C) 2013-2015 Diego Torres Milano Created on Mar 28, 2013 Culebra helps you create AndroidViewClient scripts generating a working template that can be modified to suit more specific needs. __ __ __ __ / \ / \ / \ / \ ____________________/ __\/ __\/ __\/ __\_____________________________ ___________________/ /__/ /__/ /__/ /________________________________ | / \ / \ / \ / \ \___ |/ \_/ \_/ \_/ \ o \ \_____/--< @author: Diego Torres Milano @author: Jennifer E. Swofford (ascii art snake) ''' __version__ = '10.3.0' import re import sys import os import getopt import warnings import subprocess import codecs import calendar from datetime import date try: sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src')) except: pass from com.dtmilano.android.viewclient import ViewClient, ViewClientOptions, View, CulebraOptions from com.dtmilano.android.culebron import Culebron, Operation, Unit DEBUG = False USAGE = 'usage: %s [OPTION]... [serialno]' TAG = 'CULEBRA' class Descriptor: CONTENT_DESCRIPTION = 'content-description' TEXT = 'text' ID = 'id' @staticmethod def findBestDescriptor(view): ''' Finds the best possible descriptor for the View ''' cd = view.getContentDescription() if cd and options[CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]: return Descriptor.CONTENT_DESCRIPTION else: t = view.getText() if t and options[CulebraOptions.FIND_VIEWS_WITH_TEXT]: return Descriptor.TEXT return Descriptor.ID def fillAutoRegexpsRes(): are = {} are['clock'] = re.compile('[012]?\d:[0-5]\d') d = "(" for i in range(7): d += calendar.day_abbr[i] if i != 6: d += '|' d += '), (' for i in range(1, 13): d += calendar.month_name[i] if i != 12: d += '|' d += ') [0123]\d' are['date'] = re.compile(d, re.IGNORECASE) are['battery'] = re.compile('Charging, \d\d%') return are CulebraOptions.AUTO_REGEXPS_RES = fillAutoRegexpsRes() SB_NO_JAR = 'no-jar' SB_JAR = 'jar' SB_JAR_LINUX = 'jar-linux' SHEBANG = { SB_NO_JAR: '#! /usr/bin/env python', SB_JAR: '#! /usr/bin/env shebang monkeyrunner -plugin $ANDROID_VIEW_CLIENT_HOME/bin/androidviewclient-$ANDROID_VIEW_CLIENT_VERSION.jar @!', SB_JAR_LINUX: '#! /usr/local/bin/shebang monkeyrunner -plugin $AVC_HOME/bin/androidviewclient-$AVC_VERSION.jar @!' } indent = '' prefix = '' def shortAndLongOptions(): ''' @return: the list of corresponding (short-option, long-option) tuples ''' short_opts = CulebraOptions.SHORT_OPTS.replace(':', '') if len(short_opts) != len(CulebraOptions.LONG_OPTS): raise Exception('There is a mismatch between short and long options') t = tuple(short_opts) + tuple(CulebraOptions.LONG_OPTS) l2 = len(t) / 2 sl = [] for i in range(l2): sl.append((t[i], t[i + l2])) return sl def usage(exitVal=1): print >> sys.stderr, USAGE % progname print >> sys.stderr, "Try '%s --help' for more information." % progname sys.exit(exitVal) def help(): print >> sys.stderr, USAGE % progname print >> sys.stderr print >> sys.stderr, "Options:" for so, lo in shortAndLongOptions(): o = ' -%c, --%s' % (so, lo) if lo[-1] == '=': o += CulebraOptions.LONG_OPTS_ARG[lo[:-1]] try: o = '%-34s %-45s' % (o, CulebraOptions.OPTS_HELP[so]) except: pass print >> sys.stderr, o sys.exit(0) def version(): print progname, __version__ sys.exit(0) def autoRegexpsHelp(): print >> sys.stderr, "Available %s options:" % CulebraOptions.AUTO_REGEXPS print >> sys.stderr, "\thelp: prints this help" print >> sys.stderr, "\tall: includes all the available regexps" for r in CulebraOptions.AUTO_REGEXPS_RES: print >> sys.stderr, "\t%s: %s" % (r, CulebraOptions.AUTO_REGEXPS_RES[r].pattern) print >> sys.stderr sys.exit(0) def error(msg, fatal=False): print >> sys.stderr, "%s: ERROR: %s" % (progname, msg) if fatal: sys.exit(1) def notNull(val, default): if val: return val return default def printVerboseComments(view): ''' Prints the verbose comments for view. ''' print '\n%s# class=%s' % (indent, view.getClass()), try: text = view.getText() if text: u = 'u' if isinstance(text, unicode) else '' if '\n' in text: text = re.sub(r'\n(.)', r'\n#\1', text) print " text=%c'%s'" % (u, text), except: pass try: contentDescription = view.getContentDescription() if contentDescription: print u" cd='%s'" % contentDescription, except: pass try: tag = view.getTag() if tag and tag != 'null': print ' tag=%s' % tag, except: pass print def variableNameFromIdOrKey(view): ''' Returns a suitable variable name from the id. @type view: L{View} @param id: the View from where the I{uniqueId} is obtained @return: the variable name from the id ''' var = view.variableNameFromId() if options[CulebraOptions.USE_DICTIONARY]: return '%sviews[\'%s\']' % (prefix, notNull(dictionaryKeyFrom(options[CulebraOptions.DICTIONARY_KEYS_FROM], view), var)) else: return var def dictionaryKeyFrom(key, view): if key == 'id': return view.getUniqueId() elif key == 'text': return view.getText() elif key == 'content-description': return view.getContentDescription() else: raise Exception('Not a valid dictionary key: %s' % key) def escapeRegexpSpecialChars(text): return re.escape(text) def printFindViewWithText(view, useregexp, op=Operation.ASSIGN, arg=None): ''' Prints the corresponding statement. @type view: L{View} @param view: the View ''' text = view.getText() isUnicode = isinstance(text, unicode) if isUnicode and sys.stdout.encoding is None: warnings.warn('''\ You are trying to print unicode characters to an unencoded stdout, it will probably fail. You have to set PYTHONIOENCODING environment variable. For example: export PYTHONIOENCODING=utf-8 ''') u = 'u' if isUnicode else '' if text: var = variableNameFromIdOrKey(view) if text.find(u"\n") > 0 or text.find(u"'") > 0: # 2 quotes + 1 quote = 3 quotes text = "''%s''" % text if useregexp: # if there are special chars already in the text escape them text = escapeRegexpSpecialChars(text) if options[CulebraOptions.AUTO_REGEXPS]: for r in options[CulebraOptions.AUTO_REGEXPS]: autoRegexp = CulebraOptions.AUTO_REGEXPS_RES[r] if autoRegexp.match(text): text = autoRegexp.pattern break text = "re.compile(%s'%s')" % (u, text) else: text = "%s'%s'" % (u, text) if op == Operation.ASSIGN: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print u'%s%s = %svc.findViewWithTextOrRaise(%s)' % (indent, var, prefix, text) elif op == Operation.TOUCH_VIEW: root = ', root=%svc.findViewByIdOrRaise(\'%s\')' % (prefix, arg.getUniqueId()) if arg else '' if options[CulebraOptions.MULTI_DEVICE]: print u'%s[_vc.findViewWithTextOrRaise(%s%s).touch() for _vc in %sallVcs()]' % (indent, text, prefix, root) else: logAction(u'touching view with text=%s' % text) print u'%s%svc.findViewWithTextOrRaise(%s%s).touch()' % (indent, prefix, text, root) elif op == Operation.TYPE: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%svc.findViewWithTextOrRaise("%s").type(u"%s")' % (indent, prefix, text, arg) elif op == Operation.TEST: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%sassertIsNotNone(%svc.findViewWithText(%s))' % (indent, prefix, prefix, text) elif kwargs1[CulebraOptions.VERBOSE]: warnings.warn('View with id=%s has no text' % view.getUniqueId()) def printFindViewWithContentDescription(view, useregexp, op=Operation.ASSIGN, arg=None): ''' Prints the corresponding statement. @type view: L{View} @param view: the View ''' contentDescription = view.getContentDescription() if contentDescription: var = variableNameFromIdOrKey(view) if useregexp: if options[CulebraOptions.AUTO_REGEXPS]: for r in options[CulebraOptions.AUTO_REGEXPS]: autoRegexp = CulebraOptions.AUTO_REGEXPS_RES[r] if autoRegexp.match(contentDescription): contentDescription = autoRegexp.pattern break contentDescription = "re.compile(u'''%s''')" % contentDescription else: contentDescription = "u'''%s'''" % contentDescription if op == Operation.ASSIGN: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%s = %svc.findViewWithContentDescriptionOrRaise(%s)' % (indent, var, prefix, contentDescription) elif op == Operation.TOUCH_VIEW: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction(u'touching view with content-description=%s' % contentDescription) print '%s%svc.findViewWithContentDescriptionOrRaise(%s).touch()' % (indent, prefix, contentDescription) elif op == Operation.TYPE: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%svc.findViewWithContentDescriptionOrRaise(%s).type(u"%s")' % (indent, prefix, contentDescription, arg) elif op == Operation.TEST: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%sassertEquals(%svc.findViewWithContentDescriptionOrRaise(%s).getText(), u\'\'\'%s\'\'\')' % (indent, prefix, prefix, contentDescription, arg) else: error("Invalid operation in %s: %s" % (sys._getframe().f_code.co_name), op) elif kwargs1[CulebraOptions.VERBOSE]: warnings.warn('View with id=%s has no content-description' % view.getUniqueId()) def printFindViewById(view, op=Operation.ASSIGN, arg=None): ''' Prints the corresponding statement. @type view: L{View} @param view: the View ''' var = variableNameFromIdOrKey(view) _id = view.getId() if view.getId() else view.getUniqueId() if op == Operation.ASSIGN: if options[CulebraOptions.MULTI_DEVICE]: logAction('finding view with id=%s on ${serialno}' % _id) print '%s%s = [_vc.findViewByIdOrRaise("%s") for _vc in %sallVcs()]' % (indent, var, _id, prefix) else: logAction('finding view with id=%s' % _id) print '%s%s = %svc.findViewByIdOrRaise("%s")' % (indent, var, prefix, _id) elif op == Operation.TOUCH_VIEW: if options[CulebraOptions.MULTI_DEVICE]: logAction(u'touching view with id=%s on ${serialno}' % _id) print '%s[_vc.findViewByIdOrRaise("%s").touch() for _vc in %sallVcs()]' % (indent, _id, prefix) else: logAction(u'touching view with id=%s' % _id) print '%s%svc.findViewByIdOrRaise("%s").touch()' % (indent, prefix, _id) elif op == Operation.TYPE: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction('typing "%s" on view with id=' % (arg, _id)) print '%s%svc.findViewByIdOrRaise("%s").type(u"%s")' % (indent, prefix, _id, arg) elif op == Operation.TEST: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%sassertEquals(%svc.findViewByIdOrRaise("%s").getText(), u\'\'\'%s\'\'\')' % (indent, prefix, prefix, _id, arg) else: error("Invalid operation in %s: %s" % (sys._getframe().f_code.co_name, op)) def printTraverse(dump=None): ''' Prints the result of traversing the tree. A previously obtained dump can be passed as a parameter and in such case that tree is used. @param dump: Dump of Views previously obtained via L{ViewClient.dump()} @type dump: list ''' print if dump: for view in dump: transform(view) else: vc.traverse(transform=transform) print def printDump(window, dump=None): ''' Prints a dump. @param window: The window id to use to print the dump @type window: int or str @param dump: Dump of Views previously obtained via L{ViewClient.dump()} @type dump: list ''' if options[CulebraOptions.MULTI_DEVICE]: logAction('dumping content of window=%s on ${serialno}' % window) print '%s[_vc.dump(window=%s) for _vc in %sallVcs()]' % (indent, window, prefix) else: logAction('dumping content of window=%s' % window) print '%s%svc.dump(window=%s)' % (indent, prefix, window) if not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]: printTraverse(dump) def printSleep(secs): ''' Prints a sleep. This method relies on shortcut variables being set (i.e. _s) ''' if options[CulebraOptions.MULTI_DEVICE]: print '%s[_vc.sleep(%s) for _vc in %sallVcs()]' % (indent, secs if secs != Operation.DEFAULT else '_s', prefix) else: print '%s%svc.sleep(%s)' % (indent, prefix, secs if secs != Operation.DEFAULT else '_s') def printPress(keycode): ''' Prints a key press ''' if options[CulebraOptions.MULTI_DEVICE]: logAction(u'pressing key=%s on ${serialno}' % keycode) print '%s[_d.press(\'%s\') for _d in %sallDevices()]' % (indent, keycode, prefix) else: logAction(u'pressing key=%s' % keycode) print '%s%sdevice.press(\'%s\')' % (indent, prefix, keycode) def printDrag(start, end, duration, steps, unit, orientation): ''' Prints a drag ''' if unit == Unit.PX: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%sdevice.drag(%s, %s, %d, %d, %d)' % (indent, prefix, start, end, duration, steps, orientation) elif unit == Unit.DIP: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: print '%s%sdevice.dragDip(%s, %s, %d, %d, %d)' % (indent, prefix, start, end, duration, steps, orientation) else: raise RuntimeError('Invalid unit: %s' % unit) def printTouch(x, y, unit, orientation): ''' Prints a touch ''' if unit == Unit.PX: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction(u'touching point by PX @ (%s, %s) orientation=%s' % (x, y, orientation)) print '%s%sdevice.touch(%s, %s, %s)' % (indent, prefix, x, y, orientation) elif unit == Unit.DIP: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction(u'touching point by DIP @ (%s, %s) orientation=%s' % (x, y, orientation)) print '%s%sdevice.touchDip(%s, %s, %s)' % (indent, prefix, x, y, orientation) else: raise RuntimeError('Invalid unit: %s' % unit) def printLongTouch(x, y, duration, unit, orientation): ''' Prints a long touch ''' if unit == Unit.PX: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction(u'long touching point by PX @ (%s, %s) duration=%s orientation=%s' % (x, y, duration, orientation)) print '%s%sdevice.longTouch(%s, %s, %s, %s)' % (indent, prefix, x, y, duration, orientation) elif unit == Unit.DIP: if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction(u'long touching point by DIP @ (%s, %s) duration=%s orientation=%s' % (x, y, duration, orientation)) print '%s%sdevice.longTouch(%s, %s, %s, %s)' % (indent, prefix, x, y, duration, orientation) else: raise RuntimeError('Invalid unit: %s' % unit) def printSaveViewScreenshot(view, filename, _format): ''' Prints the writeImageToFile for the specified L{View}. @type view: L{View} @param view: the View @type filename: str @param filename: the filename to store the image @type _format: str @param _format: The image format (i.e. PNG) ''' if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: # FIXME: if -u was passed in the command line then we are not saving the variables and thus # next line will generate an error in the script as the variable is 'undefined' print '%s%s.writeImageToFile(\'%s\', \'%s\')' % (indent, view.variableNameFromId(), filename, _format) def printFlingBackward(view): if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: # FIXME: if -u was passed in the command line then we are not saving the variables and thus # next line will generate an error in the script as the variable is 'undefined' logAction('flinging backward view with id=%s' % view.getId()) print '%s%s.uiScrollable.flingBackward()' % (indent, view.variableNameFromId()) def printFlingForward(view): if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: # FIXME: if -u was passed in the command line then we are not saving the variables and thus # next line will generate an error in the script as the variable is 'undefined' logAction('flinging forward view with id=%s' % view.getId()) print '%s%s.uiScrollable.flingForward()' % (indent, view.variableNameFromId()) def printFlingToBeginning(view): if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: # FIXME: if -u was passed in the command line then we are not saving the variables and thus # next line will generate an error in the script as the variable is 'undefined' logAction('flinging to beginning view with id=%s' % view.getId()) print '%s%s.uiScrollable.flingToBeginning()' % (indent, view.variableNameFromId()) def printFlingToEnd(view): if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: # FIXME: if -u was passed in the command line then we are not saving the variables and thus # next line will generate an error in the script as the variable is 'undefined' logAction('flinging to end view with id=%s' % view.getId()) print '%s%s.uiScrollable.flingToEnd()' % (indent, view.variableNameFromId()) def printOpenNotification(): if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction('opening Notification') print '%s%svc.uiDevice.openNotification()' % (indent, prefix) def printOpenQuickSettings(): if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction('opening Quick Settings') print '%s%svc.uiDevice.openQuickSettings()' % (indent, prefix) def printChangeLanguage(code): if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction('Changing language to %s' % code) print '%s%svc.uiDevice.changeLanguage("%s")' % (indent, prefix, code) def printTakeSnapshot(filename, _format, deviceart, dropshadow, screenglare): ''' Prints the corresponding writeImageToFile() to take a snapshot ''' if options[CulebraOptions.MULTI_DEVICE]: warnings.warn('Multi-device not implemented yet for this case') else: logAction(u'taking snapshot @ %s format=%s %s %s %s' % (filename, _format, deviceart, dropshadow, screenglare)) print '%s%svc.writeImageToFile(\'%s\', \'%s\', \'%s\', %s, %s)' % (indent, prefix, filename, _format, deviceart, dropshadow, screenglare) def traverseAndPrint(view): ''' Traverses the View tree and prints the corresponding statement. @type view: L{View} @param view: the View ''' if DEBUG: print >> sys.stderr, "traverseAndPrint(view=%s)" % view.getId() if options[CulebraOptions.VERBOSE_COMMENTS]: printVerboseComments(view) if options[CulebraOptions.FIND_VIEWS_BY_ID]: printFindViewById(view) if options[CulebraOptions.FIND_VIEWS_WITH_TEXT]: printFindViewWithText(view, options[CulebraOptions.USE_REGEXPS]) if options[CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]: printFindViewWithContentDescription(view, options[CulebraOptions.USE_REGEXPS]) if options[CulebraOptions.SAVE_VIEW_SCREENSHOTS]: _format = 'PNG' filename = options[CulebraOptions.SAVE_VIEW_SCREENSHOTS] + os.sep + view.variableNameFromId() + '.' + _format.lower() printSaveViewScreenshot(view, filename, _format) def printOperation(view, op, *args): if len(args) == 0: # We use tuple values in the rest of this method, so if an empty tuple was passed # replace it by one containing None args = ( None, None ) elif len(args) < 2: # We use tuple values in the rest of this method, so if an empty tuple was passed # replace it by one containing None args = ( args[0], None ) if DEBUG: print >> sys.stderr, "printOperation(", print >> sys.stderr, view.__str__(), print >> sys.stderr, ",", op, ",", for a in args: if isinstance(a, unicode): print >> sys.stderr, a.encode(encoding='ascii', errors='replace'), ", ", else: print >> sys.stderr, a, print >> sys.stderr, ")" if op == Operation.SLEEP: printSleep(secs=args[0]) return elif op == Operation.PRESS: printPress(keycode=args[0]) return elif op == Operation.DUMP: printDump(window=args[0], dump=args[1]) return elif op == Operation.DRAG: printDrag(start=args[0], end=args[1], duration=args[2], steps=args[3], unit=args[4], orientation=args[5]) return elif op == Operation.TOUCH_POINT: printTouch(x=args[0], y=args[1], unit=args[2], orientation=args[3]) return elif op == Operation.LONG_TOUCH_POINT: printLongTouch(x=args[0], y=args[1], duration=args[2], unit=args[3], orientation=args[4]) return elif op == Operation.TRAVERSE: printTraverse() return elif op == Operation.SNAPSHOT: printTakeSnapshot(filename=args[0], _format=args[1], deviceart=args[2], dropshadow=args[3], screenglare=args[4]) return elif op == Operation.VIEW_SNAPSHOT: printSaveViewScreenshot(view, filename=args[0], _format=args[1]) return elif op == Operation.FLING_BACKWARD: printFlingBackward(view) return elif op == Operation.FLING_FORWARD: printFlingForward(view) return elif op == Operation.FLING_TO_BEGINNING: printFlingToBeginning(view) return elif op == Operation.FLING_TO_END: printFlingToEnd(view) return elif op == Operation.OPEN_NOTIFICATION: printOpenNotification() return elif op == Operation.OPEN_QUICK_SETTINGS: printOpenQuickSettings() return elif op == Operation.CHANGE_LANGUAGE: printChangeLanguage(code=args[0]) return if view is None: warnings.warn('view is None. Perhaps you forgot to add some "op" in the previous if.') bd = Descriptor.findBestDescriptor(view) if bd == Descriptor.CONTENT_DESCRIPTION: printFindViewWithContentDescription(view, options[CulebraOptions.USE_REGEXPS], op, args[0]) elif bd == Descriptor.TEXT: printFindViewWithText(view, options[CulebraOptions.USE_REGEXPS], op, args[0]) else: printFindViewById(view, op, args[0]) def str2bool(v): return v.lower() in ("yes", "true", "t", "1", "on") def value2dictionaryKey(v): v = v.lower() k = ['id', 'text', 'content-description'] sk = ['i', 't', 'd'] if v in k: return v if v in sk: return k[sk.index(v)] error("Invalid dictionary key: %s" % v) usage() def getShebangJar(): if options[CulebraOptions.USE_JAR]: import java osName = java.lang.System.getProperty('os.name') if osName == 'Linux': return SHEBANG[SB_JAR_LINUX] else: return SHEBANG[SB_JAR] else: return SHEBANG[SB_NO_JAR] def getWindowOption(): return options[CulebraOptions.WINDOW] if isinstance(options[CulebraOptions.WINDOW], str) and options[CulebraOptions.WINDOW][0] in '-0123456789' else "'%s'" % options[CulebraOptions.WINDOW] def printScriptHeader(): print '''%s # -*- coding: utf-8 -*- \'\'\' Copyright (C) 2013-2014 Diego Torres Milano Created on %s by Culebra v%s __ __ __ __ / \ / \ / \ / \ ____________________/ __\/ __\/ __\/ __\_____________________________ ___________________/ /__/ /__/ /__/ /________________________________ | / \ / \ / \ / \ \___ |/ \_/ \_/ \_/ \ o \ \_____/--< @author: Diego Torres Milano @author: Jennifer E. Swofford (ascii art snake) \'\'\' import re import sys import os ''' % (getShebangJar(), date.today(), __version__) def printUnittestImport(): print ''' import unittest ''' def printAppendToSysPath(): print ''' try: sys.path.append(os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src')) except: pass ''' def printPrependToSysPath(): print ''' try: sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src')) except: pass ''' def printLogAction(action, priority='D'): # FIXME: This works only for CulebraTestCases # This relies on shortcut variables already set (i.e. _v) if options[CulebraOptions.MULTI_DEVICE]: print u'%s[_d.Log.%s(TAG, "%s", _v) for _d in %sallDevices()]' % (indent, priority.lower(), action, prefix) else: print '%s%sdevice.Log.%s(TAG, "%s", _v)' % (indent, prefix, priority.lower(), action) def logAction(action): if options[CulebraOptions.LOG_ACTIONS]: printLogAction(action) def runCulebron(): Culebron.checkSupportedSdkVersion(device.getSdkVersion()) Culebron.checkDependencies() culebron = Culebron(vc, printOperation, options[CulebraOptions.SCALE]) if options[CulebraOptions.DEVICE_ART]: culebron.deviceArt = options[CulebraOptions.DEVICE_ART] culebron.dropShadow = options[CulebraOptions.DROP_SHADOW] culebron.screenGlare = options[CulebraOptions.SCREEN_GLARE] culebron.takeScreenshotAndShowItOnWindow() culebron.mainloop() def printShortcutVariables(): if options[CulebraOptions.UNIT_TEST_CLASS] or options[CulebraOptions.UNIT_TEST_METHOD]: print '''\ _s = CulebraTests.sleep _v = CulebraTests.verbose ''' else: print '''\ _s = 5 _v = '--verbose' in sys.argv ''' ################ # __main__ ################ progname = os.path.basename(sys.argv[0]) try: optlist, args = getopt.getopt(sys.argv[1:], CulebraOptions.SHORT_OPTS, CulebraOptions.LONG_OPTS) sys.argv[1:] = args except getopt.GetoptError, e: error(str(e)) usage() kwargs1 = {CulebraOptions.VERBOSE: False, 'ignoresecuredevice': False, 'ignoreversioncheck': False} serialno = None kwargs2 = {ViewClientOptions.FORCE_VIEW_SERVER_USE: False, ViewClientOptions.START_VIEW_SERVER: True, ViewClientOptions.AUTO_DUMP: False, ViewClientOptions.IGNORE_UIAUTOMATOR_KILLED: True, ViewClientOptions.COMPRESSED_DUMP: True} options = {CulebraOptions.FIND_VIEWS_BY_ID: True, CulebraOptions.FIND_VIEWS_WITH_TEXT: True, CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION: True, CulebraOptions.USE_REGEXPS: False, CulebraOptions.VERBOSE_COMMENTS: False, CulebraOptions.UNIT_TEST_CLASS: False, CulebraOptions.UNIT_TEST_METHOD: None, CulebraOptions.USE_JAR: False, CulebraOptions.USE_DICTIONARY: False, CulebraOptions.DICTIONARY_KEYS_FROM: 'id', CulebraOptions.AUTO_REGEXPS: None, CulebraOptions.START_ACTIVITY: None, CulebraOptions.OUTPUT: None, CulebraOptions.INTERACTIVE: False, CulebraOptions.WINDOW:-1, CulebraOptions.PREPEND_TO_SYS_PATH: False, CulebraOptions.SAVE_SCREENSHOT: None, CulebraOptions.SAVE_VIEW_SCREENSHOTS: None, CulebraOptions.GUI: False, CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP: False, CulebraOptions.SCALE: 1, CulebraOptions.ORIENTATION_LOCKED: None, CulebraOptions.MULTI_DEVICE: False, CulebraOptions.LOG_ACTIONS: False, CulebraOptions.DEVICE_ART: None, CulebraOptions.DROP_SHADOW: False, CulebraOptions.SCREEN_GLARE: False, } transform = traverseAndPrint for o, a in optlist: o = o.strip('-') if o in ['H', CulebraOptions.HELP]: help() elif o in ['V', CulebraOptions.VERBOSE]: kwargs1[CulebraOptions.VERBOSE] = True elif o in ['v', CulebraOptions.VERSION]: version() elif o in ['I', CulebraOptions.IGNORE_SECURE_DEVICE]: kwargs1['ignoresecuredevice'] = True elif o in ['E', CulebraOptions.IGNORE_VERSION_CHECK]: kwargs1['ignoreversioncheck'] = True elif o in ['F', CulebraOptions.FORCE_VIEW_SERVER_USE]: kwargs2['forceviewserveruse'] = True elif o in ['S', CulebraOptions.DO_NOT_START_VIEW_SERVER]: kwargs2['startviewserver'] = False elif o in ['k', CulebraOptions.DO_NOT_IGNORE_UIAUTOMATOR_KILLED]: kwargs2['ignoreuiautomatorkilled'] = False elif o in ['w', CulebraOptions.WINDOW]: options[CulebraOptions.WINDOW] = a elif o in ['i', CulebraOptions.FIND_VIEWS_BY_ID]: options[CulebraOptions.FIND_VIEWS_BY_ID] = str2bool(a) elif o in ['t', CulebraOptions.FIND_VIEWS_WITH_TEXT]: options[CulebraOptions.FIND_VIEWS_WITH_TEXT] = str2bool(a) elif o in ['d', CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]: options[CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION] = str2bool(a) elif o in ['r', CulebraOptions.USE_REGEXPS]: options[CulebraOptions.USE_REGEXPS] = True elif o in ['C', CulebraOptions.VERBOSE_COMMENTS]: options[CulebraOptions.VERBOSE_COMMENTS] = True elif o in ['U', CulebraOptions.UNIT_TEST_CLASS]: options[CulebraOptions.UNIT_TEST_CLASS] = True elif o in ['M', CulebraOptions.UNIT_TEST_METHOD]: if not a.startswith('test'): warnings.warn('Method name should start with "test"') options[CulebraOptions.UNIT_TEST_METHOD] = a elif o in ['j', CulebraOptions.USE_JAR]: options[CulebraOptions.USE_JAR] = str2bool(a) elif o in ['D', CulebraOptions.USE_DICTIONARY]: options[CulebraOptions.USE_DICTIONARY] = str2bool(a) elif o in ['K', CulebraOptions.DICTIONARY_KEYS_FROM]: options[CulebraOptions.DICTIONARY_KEYS_FROM] = value2dictionaryKey(a) elif o in ['R', CulebraOptions.AUTO_REGEXPS]: options[CulebraOptions.AUTO_REGEXPS] = a.split(',') for r in options[CulebraOptions.AUTO_REGEXPS]: if r == 'help': autoRegexpsHelp() if r == 'all': options[CulebraOptions.AUTO_REGEXPS] = CulebraOptions.AUTO_REGEXPS_RES.keys() break if r not in CulebraOptions.AUTO_REGEXPS_RES: error("invalid auto regexp: %s\n" % (r)) usage() # CulebraOptions.AUTO_REGEPXS implies CulebraOptions.USE_REGEXPS options[CulebraOptions.USE_REGEXPS] = True elif o in ['a', CulebraOptions.START_ACTIVITY]: options[CulebraOptions.START_ACTIVITY] = a elif o in ['o', CulebraOptions.OUTPUT]: options[CulebraOptions.OUTPUT] = a elif o in ['p', CulebraOptions.PREPEND_TO_SYS_PATH]: options[CulebraOptions.PREPEND_TO_SYS_PATH] = True elif o in ['f', CulebraOptions.SAVE_SCREENSHOT]: options[CulebraOptions.SAVE_SCREENSHOT] = a elif o in ['W', CulebraOptions.SAVE_VIEW_SCREENSHOTS]: options[CulebraOptions.SAVE_VIEW_SCREENSHOTS] = a elif o in ['G', CulebraOptions.GUI]: options[CulebraOptions.GUI] = True elif o in ['u', CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]: options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP] = True elif o in ['P', CulebraOptions.SCALE]: options[CulebraOptions.SCALE] = float(a) elif o in ['O', CulebraOptions.ORIENTATION_LOCKED]: options[CulebraOptions.ORIENTATION_LOCKED] = 'PENDING' elif o in ['s', CulebraOptions.SERIALNO]: __devices = a.split() if len(__devices) > 1: warnings.warn('List of devices not supported yet. Using first device instead.') serialno = __devices[0] elif o in ['m', CulebraOptions.MULTI_DEVICE]: options[CulebraOptions.MULTI_DEVICE] = True elif o in ['L', CulebraOptions.LOG_ACTIONS]: options[CulebraOptions.LOG_ACTIONS] = True elif o in ['A', CulebraOptions.DEVICE_ART]: options[CulebraOptions.DEVICE_ART] = a elif o in ['Z', CulebraOptions.DROP_SHADOW]: options[CulebraOptions.DROP_SHADOW] = True elif o in ['B', CulebraOptions.SCREEN_GLARE]: options[CulebraOptions.SCREEN_GLARE] = True if not (options[CulebraOptions.FIND_VIEWS_BY_ID] or options[CulebraOptions.FIND_VIEWS_WITH_TEXT] or options[CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]): if not options[CulebraOptions.VERBOSE_COMMENTS]: warnings.warn('All printing options disabled. Output will be empty.') else: warnings.warn('Only verbose comments will be printed') device, serialno = ViewClient.connectToDeviceOrExit(serialno=serialno, **kwargs1) if options[CulebraOptions.ORIENTATION_LOCKED] == 'PENDING': options[CulebraOptions.ORIENTATION_LOCKED] = device.display['orientation'] if options[CulebraOptions.START_ACTIVITY]: device.startActivity(component=options[CulebraOptions.START_ACTIVITY]) vc = ViewClient(device, serialno, **kwargs2) if options[CulebraOptions.OUTPUT]: sys.stdout = codecs.open(options[CulebraOptions.OUTPUT], mode='w', encoding='utf-8', errors='replace') import stat st = os.stat(options[CulebraOptions.OUTPUT]) os.chmod(options[CulebraOptions.OUTPUT], st.st_mode | stat.S_IEXEC) if options[CulebraOptions.UNIT_TEST_CLASS] or (not options[CulebraOptions.UNIT_TEST_METHOD]): printScriptHeader() if options[CulebraOptions.UNIT_TEST_CLASS]: printUnittestImport() if options[CulebraOptions.PREPEND_TO_SYS_PATH]: printPrependToSysPath() if options[CulebraOptions.UNIT_TEST_CLASS] or (not options[CulebraOptions.UNIT_TEST_METHOD]): print '''\ from com.dtmilano.android.viewclient import ViewClient%s TAG = '%s' ''' % (', CulebraTestCase' if options[CulebraOptions.UNIT_TEST_CLASS] else '', TAG) if options[CulebraOptions.UNIT_TEST_CLASS]: print ''' class CulebraTests(CulebraTestCase): @classmethod def setUpClass(cls): cls.kwargs1 = %s cls.kwargs2 = %s cls.options = %s cls.sleep = 5 ''' % (kwargs1, kwargs2, options), print '''\ def setUp(self): super(CulebraTests, self).setUp() ''' print '''\ def tearDown(self): super(CulebraTests, self).tearDown() ''' print '''\ def preconditions(self): if not super(CulebraTests, self).preconditions(): return False return True def %s(self): if not self.preconditions(): self.fail('Preconditions failed') ''' % (options[CulebraOptions.UNIT_TEST_METHOD] if options[CulebraOptions.UNIT_TEST_METHOD] else 'testSomething') printShortcutVariables() if options[CulebraOptions.SAVE_SCREENSHOT]: print '''\ self.vc.writeImageToFile('%s') ''' % options[CulebraOptions.SAVE_SCREENSHOT] if options[CulebraOptions.USE_DICTIONARY]: print '''\ self.views = dict()''' vc.dump(window=options[CulebraOptions.WINDOW]) indent = ' ' * 8 prefix = 'self.' # if not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]: # print '''\ # self.vc.dump(%s) # ''' % getWindowOption() # vc.traverse(transform=transform) # print if options[CulebraOptions.GUI]: runCulebron() elif not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]: printDump(getWindowOption()) print ''' if __name__ == '__main__': CulebraTests.main() ''' else: # Not a unittest class, still could be a unittest method as we allow to generate methods separately from their classes if not options[CulebraOptions.UNIT_TEST_METHOD]: printShortcutVariables() print ''' kwargs1 = %s device, serialno = ViewClient.connectToDeviceOrExit(**kwargs1) ''' % kwargs1, if options[CulebraOptions.START_ACTIVITY] and not options[CulebraOptions.UNIT_TEST_METHOD]: print '''\ device.startActivity(component='%s')''' % options[CulebraOptions.START_ACTIVITY] if not options[CulebraOptions.UNIT_TEST_METHOD]: print '''\ kwargs2 = %s vc = ViewClient(device, serialno, **kwargs2) #vc.dump(window=%s) # FIXME: seems not needed ''' % (kwargs2, getWindowOption()) if options[CulebraOptions.USE_DICTIONARY]: print '''views = dict()''' if options[CulebraOptions.SAVE_SCREENSHOT]: print '''\ vc.writeImageToFile('%s') ''' % options[CulebraOptions.SAVE_SCREENSHOT] vc.dump(window=options[CulebraOptions.WINDOW]) if options[CulebraOptions.UNIT_TEST_METHOD]: print ''' def %s(self): \'\'\' Test method. \'\'\' if not self.preconditions(): self.fail('Preconditions failed') ''' % (options[CulebraOptions.UNIT_TEST_METHOD]) indent = ' ' * 8 prefix = 'self.' printShortcutVariables() if options[CulebraOptions.GUI]: runCulebron() elif not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]: printDump(getWindowOption()) if options[CulebraOptions.INTERACTIVE]: import socket HOST = 'localhost' PORT = 8900 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) s.sendall("RECORD EVENTS START\n") fin = open("/dev/tty") while True: print >> sys.stderr, "Reading events..." data = s.recv(1024) code = ViewClient.excerpt(data) exec code resp = raw_input("Continue recording events? [Y/n]: ") if resp in ['N', 'n']: break s.sendall("RECORD EVENTS STOP\n") s.close()