2012-08-31 4 views
6

Quando si utilizza MonkeyRunner, ogni tanto ho un errore del tipo:Come posso catturare SocketExceptions in MonkeyRunner?

120830 18:39:32.755:S [MainThread] [com.android.chimpchat.adb.AdbChimpDevice] Unable to get variable: display.density 
120830 18:39:32.755:S [MainThread] [com.android.chimpchat.adb.AdbChimpDevice]java.net.SocketException: Connection reset 

Da quello che ho letto, a volte la connessione adb va male, ed è necessario riconnettersi. L'unico problema è che non riesco a catturare lo SocketException. Inserirò il mio codice in questo modo:

try: 
    density = self.device.getProperty('display.density') 
except: 
    print 'This will never print.' 

Ma l'eccezione non è apparentemente sollevata fino al chiamante. Ho verificato che MonkeyRunner/Jython può intercettare le eccezioni Java il modo in cui mi aspetto:

>>> from java.io import FileInputStream 
>>> def test_java_exceptions(): 
...  try: 
...   FileInputStream('bad mojo') 
...  except: 
...   print 'Caught it!' 
... 
>>> test_java_exceptions() 
Caught it! 

Come posso trattare con queste eccezioni presa?

risposta

2

Di seguito è la soluzione che ho finito per utilizzare. Qualsiasi funzione che può soffrire di guasti ADB ha solo bisogno di utilizzare il seguente decoratore:

from subprocess import call, PIPE, Popen 
from time import sleep 

def check_connection(f): 
    """ 
    adb is unstable and cannot be trusted. When there's a problem, a 
    SocketException will be thrown, but caught internally by MonkeyRunner 
    and simply logged. As a hacky solution, this checks if the stderr log 
    grows after f is called (a false positive isn't going to cause any harm). 
    If so, the connection will be repaired and the decorated function/method 
    will be called again. 

    Make sure that stderr is redirected at the command line to the file 
    specified by config.STDERR. Also, this decorator will only work for 
    functions/methods that take a Device object as the first argument. 
    """ 
    def wrapper(*args, **kwargs): 
     while True: 
      cmd = "wc -l %s | awk '{print $1}'" % config.STDERR 
      p = Popen(cmd, shell=True, stdout=PIPE) 
      (line_count_pre, stderr) = p.communicate() 
      line_count_pre = line_count_pre.strip() 

      f(*args, **kwargs) 

      p = Popen(cmd, shell=True, stdout=PIPE) 
      (line_count_post, stderr) = p.communicate() 
      line_count_post = line_count_post.strip() 

      if line_count_pre == line_count_post: 
       # the connection was fine 
       break 
      print 'Connection error. Restarting adb...' 
      sleep(1) 
      call('adb kill-server', shell=True) 
      call('adb start-server', shell=True) 
      args[0].connection = MonkeyRunner.waitForConnection() 

    return wrapper 

perché questo può creare una nuova connessione, è necessario avvolgere la connessione corrente in un oggetto periferica in modo che possa essere modificato. Ecco il mio classi di periferiche (la maggior parte della classe è per convenienza, l'unica cosa che è necessario è il membro connection:

class Device: 
    def __init__(self): 
     self.connection = MonkeyRunner.waitForConnection() 
     self.width = int(self.connection.getProperty('display.width')) 
     self.height = int(self.connection.getProperty('display.height')) 
     self.model = self.connection.getProperty('build.model') 

    def touch(self, x, y, press=MonkeyDevice.DOWN_AND_UP): 
     self.connection.touch(x, y, press) 

Un esempio su come utilizzare il decoratore:

@check_connection 
def screenshot(device, filename): 
    screen = device.connection.takeSnapshot() 
    screen.writeToFile(filename + '.png', 'png') 
+0

Beh, devo dire che non è dovuta a instabilità 'adb' ma MonkeyRuner/Chimpchat. [AndroidViewclient/culebra] (https://github.com/dtmilano/AndroidViewClient) utilizza un 'adbclient', un modulo python che incapsula tutte le finestre di dialogo con' adb' ed è piuttosto stabile. –

6

Otterrete che errore ogni volta che si avvia MonkeyRunner perché il comando monkey --port 12345 sul dispositivo non viene arrestato quando si interrompe lo script Si tratta di un bug nella scimmia

Un modo più bello per risolvere questo problema è uccidere scimmia quando SIGINT viene inviato al tuo script (quando hai ctrl+c). In altre parole: $ killall com.android.commands.monkey.

modo rapido per farlo:

from sys, signal 
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice 

device = None 

def execute(): 
    device = MonkeyRunner.waitForConnection() 
    # your code 

def exitGracefully(self, signum, frame): 
    signal.signal(signal.SIGINT, signal.getsignal(signal.SIGINT)) 
    device.shell('killall com.android.commands.monkey') 
    sys.exit(1) 

if __name__ == '__main__' 
    signal.signal(signal.SIGINT, exitGracefully) 
    execute() 

Edit: come un addendum, ho trovato anche un modo per notare gli errori di Java: Monkey Runner throwing socket exception broken pipe on touuch