Skip to content

Commit

Permalink
add timeout of monkeyrunner connection
Browse files Browse the repository at this point in the history
  • Loading branch information
yuanchun-li committed Jun 16, 2015
1 parent 98a7b6b commit e31147c
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 52 deletions.
74 changes: 68 additions & 6 deletions droidbot/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
__author__ = 'liyc'
import subprocess
import logging
import threading
import time
import signal


class ADBException(Exception):
Expand All @@ -18,12 +21,37 @@ class TelnetException(Exception):
pass


class MonkeyException(Exception):
class MonkeyRunnerException(Exception):
"""
Exception in monkeyrunner connection
"""
pass


class TimeoutException(Exception):
"""
Exception if connection has been waiting too long
"""
pass


class Timeout:
def __init__(self, seconds=0, error_message='Timeout'):
self.seconds = seconds
self.error_message = error_message

def handle_timeout(self, signum, frame):
raise TimeoutException()

def __enter__(self):
if self.seconds != 0:
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.seconds)

def __exit__(self, type, value, traceback):
signal.alarm(0)


class ADB(object):
"""
interface of ADB
Expand Down Expand Up @@ -98,6 +126,12 @@ def check_connectivity(self):
r = self.run_cmd("get-state")
return r.startswith("device")

def disconnect(self):
"""
disconnect adb
"""
self.logger.info("ADB disconnected")


class TelnetConsole(object):
"""
Expand Down Expand Up @@ -161,6 +195,12 @@ def check_connectivity(self):
return False
return True

def disconnect(self):
"""
disconnect telnet
"""
self.console.close()


class MonkeyRunner(object):
"""
Expand All @@ -176,12 +216,24 @@ def __init__(self, device):
self.logger = logging.getLogger('MonkeyRunner')
self.console = subprocess.Popen('monkeyrunner', stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.running = True
self.run_cmd('from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice')
self.run_cmd('device=MonkeyRunner.waitForConnection(5,%s)' % device.serial)
self.run_cmd('device=MonkeyRunner.waitForConnection(5,\'%s\')' % device.serial)
if self.check_connectivity():
self.logger.info("monkeyrunner successfully initiated, the device is %s" % device.serial)
else:
raise MonkeyException()
raise MonkeyRunnerException()

def read_until(self, token, timeout=0):
lines = []
with Timeout(timeout):
while True:
line = self.console.stdout.readline()
lines.append(line)
if line.startswith(token):
break
return lines


def run_cmd(self, args):
"""
Expand All @@ -197,7 +249,9 @@ def run_cmd(self, args):
self.logger.debug(cmd_line)
cmd_line += '\n'
self.console.stdin.write(cmd_line)
r=self.console.stdout.readline()
self.console.stdin.flush()
time.sleep(2)
r=self.read_until('>>>')
self.logger.debug('return:')
self.logger.debug(r)
return r
Expand All @@ -208,7 +262,7 @@ def check_connectivity(self):
:return: True for connected
"""
try:
self.run_cmd("r=device.getProperty(clock.millis)")
self.run_cmd("r=device.getProperty(\'clock.millis\')")
(out, err) = self.run_cmd("print r")
if err != None:
return False
Expand All @@ -219,4 +273,12 @@ def check_connectivity(self):
return False
except:
return False
return True
return True

def disconnect(self):
"""
disconnect monkeyrunner
:return:
"""
self.running = False
self.console.terminate()
45 changes: 45 additions & 0 deletions droidbot/droidbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# This file contains the main class of droidbot
# It can be used after AVD was started, app was installed, and adb had been set up properly
# By configuring and creating a droidbot instance,
# droidbot will start interacting with Android in AVD like a human

__author__ = 'liyc'
import logging
from types import App, Device
from app_env import AppEnvManager
from app_event import AppEventManager
from connection import TelnetException, ADBException, MonkeyRunnerException


class DroidBot(object):
"""
The main class of droidbot
A robot which interact with Android automatically
"""

def __init__(self, options):
"""
initiate droidbot with configurations
:param options: the options which contain configurations of droidbot
:return:
"""
self.logger = logging.getLogger('DroidBot')
self.options = options

def start(self):
"""
start interacting
:return:
"""
try:
device = Device(self.options.device_serial)
app = App(self.options.package_name, self.options.app_path)
except TelnetException as te:
# allow telnet not connected
self.logger.exception(te)

env_manager = AppEnvManager(device, app, self.options.env_policy)
event_manager = AppEventManager(device, app, self.options.event_policy, self.options.event_count)

env_manager.deploy()
event_manager.start()
51 changes: 5 additions & 46 deletions start.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,11 @@
# This file contains the main class of droidbot
# It can be used after AVD was started, app was installed, and adb had been set up properly
# By configuring and creating a droidbot instance,
# droidbot will start interacting with Android in AVD like a human
# helper file of droidbot
# it parses command arguments and send the options to droidbot

__author__ = 'liyc'
import argparse
import logging
from argparse import RawTextHelpFormatter

from droidbot.types import App, Device
from droidbot.app_env import AppEnvManager
from droidbot.app_event import AppEventManager


class droidbot(object):
"""
The main class of droidbot
A robot which interact with Android automatically
"""

def __init__(self, device, package_name, env_policy, event_policy):
"""
initiate droidbot with configurations
:param device: name of device droidbot is going to interact with
:param package_name: package name of app droidbot is going to interact with
:param env_policy: the policy used to set up device environment
:param event_policy: the policy used to generate events at runtime
:return:
"""
self.device = device
self.package_name = package_name
self.env_policy = env_policy
self.event_policy = event_policy

def start(self):
"""
start interacting
:return:
"""
# TODO implement this method
pass

from droidbot.droidbot import DroidBot

def parse_args():
"""
Expand Down Expand Up @@ -84,15 +49,9 @@ def main():
"""
logging.basicConfig(level=logging.DEBUG)
opts = parse_args()
device = Device(opts.device_serial)
device.get_adb()
device.get_telnet()
app = App(opts.package_name, opts.app_path)
env_manager = AppEnvManager(device, app, opts.env_policy)
event_manager = AppEventManager(device, app, opts.event_policy, opts.event_count)

env_manager.deploy()
event_manager.start()
droidbot = DroidBot(opts)
droidbot.start()
return


Expand Down

0 comments on commit e31147c

Please sign in to comment.