from __future__ import absolute_import from contextlib import contextmanager from multiprocessing import TimeoutError import signal import datetime import os import subprocess import time import urllib import zipfile import shutil import pytest from .adb import ADB from ..logger import Logger def get_center(bounds): """ Returns given element center coords:: from magneto.utils import get_center element = self.magneto(text='Foo') (x, y) = get_center(element.info['bounds']) :param dict bounds: Element position coordinates (top, right, bottom, left) :return: x and y coordinates of element center """ x = bounds['right'] - ((bounds['right'] - bounds['left']) / 2) y = bounds['bottom'] - ((bounds['bottom'] - bounds['top']) / 2) return x, y def get_config(attr, default=None): """ Allows access to config parameters:: from magneto.utils import get_config package = get_config('--app-package') :param str attr: Command line argument :return: Requested config value """ # must have this check to avoid sphinx-autodoc exception if getattr(pytest, 'config', None) != None: return pytest.config.getoption(attr) or default else: return default @contextmanager def timewarp(timedelta_): now = datetime.datetime.now() future = now + timedelta_ ADB.set_datetime(future) try: yield finally: now = datetime.datetime.now() ADB.set_datetime(now) class Timeout(): """ Allows polling a function till success or timeout:: import time from magneto.utils import Timeout result = False with Timeout(seconds=5): while not result: result = some_function() time.sleep(0.5) :param integer seconds: Timeout value in seconds. Defaults to 1. :param str error_message: Error message to display when timeout occurs. Defaults to 'Timeout'. """ def __init__(self, seconds=1, error_message='Timeout'): self.seconds = seconds or 1 self.error_message = error_message def handle_timeout(self, signum, frame): Logger.debug('Timeout reached {} seconds limit'.format(self.seconds)) raise TimeoutError(self.error_message) def __enter__(self): Logger.debug('Timeout started for {} seconds'.format(self.seconds)) signal.signal(signal.SIGALRM, self.handle_timeout) signal.alarm(self.seconds) def __exit__(self, type, value, traceback): Logger.debug('Timeout stopped.') signal.alarm(0) def unlock_device(): """ Powers on device and unlocks it. """ # read device screen state p = ADB.exec_cmd("shell 'if [ -z $(dumpsys power | grep mScreenOn=true) ]; then echo off; else echo on;fi'", stdout=subprocess.PIPE) device_screen = p.stdout.readline().strip('\r\n') if device_screen == 'off': # power on device ADB.exec_cmd('shell input keyevent 26').wait() # unlock device ADB.exec_cmd('shell input keyevent 82').wait() def wait_for_device(): """ Wait for device to boot. 1 minute timeout. """ wait_for_device_cmd = 'wait-for-device shell getprop sys.boot_completed' p = ADB.exec_cmd(wait_for_device_cmd, stdout=subprocess.PIPE) boot_completed = p.stdout.readline().strip('\r\n') try: with Timeout(seconds=60): while boot_completed != '1': time.sleep(1) p = ADB.exec_cmd(wait_for_device_cmd, stdout=subprocess.PIPE) boot_completed = p.stdout.readline().strip('\r\n') Logger.debug('Waiting for device to finish booting (adb shell getprop sys.boot_completed)') except TimeoutError: Logger.debug('Timed out while waiting for sys.boot_completed, there might not be a default launcher set, trying to run anyway') pass class Bootstrap(object): _map = { 'no_app': 'https://github.com/EverythingMe/magneto-init/archive/master.zip', 'calc': 'https://github.com/EverythingMe/magneto-demo-calc/archive/master.zip' } def __init__(self, name): if name not in self._map: raise Exception('{} not recognized'.format(name)) filename, headers = urllib.urlretrieve(self._map[name]) with zipfile.ZipFile(filename) as zip_file: rootdir = zip_file.namelist()[0] for member in zip_file.namelist()[1:]: if not os.path.basename(member): # create dir from zipfile os.mkdir(os.path.join(os.path.curdir, member.replace(rootdir, ''))) else: # copy file (taken from zipfile's extract) source = zip_file.open(member) target = file(os.path.join(os.path.curdir, member.replace(rootdir, '')), "wb") with source, target: shutil.copyfileobj(source, target)