Skip to content

Commit

Permalink
move proc_communicate_timeout to utils/compat.py, remove AdbTimeExpir…
Browse files Browse the repository at this point in the history
…ed error, add test_cmd_timeout to test_adb.py

(cherry picked from commit 3bc8234)
  • Loading branch information
yimelia committed Jan 17, 2020
1 parent 509e2c7 commit 2f08784
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 36 deletions.
36 changes: 4 additions & 32 deletions airtest/core/android/adb.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,17 @@
from six.moves import reduce

from airtest.core.android.constant import (DEFAULT_ADB_PATH, IP_PATTERN,
SDK_VERISON_ANDROID7)
SDK_VERISON_ANDROID7, SUBPROCESS_FLAG)
from airtest.core.error import (AdbError, AdbShellError, AirtestError,
DeviceConnectionError, AdbTimeoutExpired)
from airtest.utils.compat import decode_path, raisefrom
from airtest.utils.compat import decode_path, raisefrom, proc_communicate_timeout
from airtest.utils.logger import get_logger
from airtest.utils.nbsp import NonBlockingStreamReader
from airtest.utils.retry import retries
from airtest.utils.snippet import get_std_encoding, reg_cleanup, split_cmd

LOGGING = get_logger(__name__)

if sys.platform.startswith("win"):
# Don't display the Windows GPF dialog if the invoked program dies.
try:
SUBPROCESS_FLAG = subprocess.CREATE_NO_WINDOW # in Python 3.7+
except AttributeError:
import ctypes
SEM_NOGPFAULTERRORBOX = 0x0002 # From MSDN
ctypes.windll.kernel32.SetErrorMode(SEM_NOGPFAULTERRORBOX) # win32con.CREATE_NO_WINDOW?
SUBPROCESS_FLAG = 0x8000000
else:
SUBPROCESS_FLAG = 0


class ADB(object):
"""adb client object class"""
Expand Down Expand Up @@ -187,23 +175,7 @@ def cmd(self, cmds, device=True, ensure_unicode=True, timeout=None):
"""
proc = self.start_cmd(cmds, device)
if timeout:
if sys.version_info[:2] >= (3, 3):
# in Python 3.3+
try:
stdout, stderr = proc.communicate(timeout=timeout)
except subprocess.TimeoutExpired as e:
proc.kill()
stdout, stderr = proc.communicate()
raise_from(AdbTimeoutExpired(stdout, stderr, e), None)
else:
timer = threading.Timer(timeout, proc.kill)
try:
timer.start()
stdout, stderr = proc.communicate()
finally:
timer.cancel()
if proc.returncode > 0:
raise AdbTimeoutExpired(stdout, stderr, ("Command '%s' timed out after %s seconds" % (cmds, timeout)))
stdout, stderr = proc_communicate_timeout(proc, timeout)
else:
stdout, stderr = proc.communicate()

Expand Down Expand Up @@ -324,7 +296,7 @@ def wait_for_device(self, timeout=5):
"""
try:
self.cmd("wait-for-device", timeout=timeout)
except AdbTimeoutExpired as e:
except RuntimeError as e:
raisefrom(DeviceConnectionError, "device not ready", e)

def start_shell(self, cmds):
Expand Down
15 changes: 15 additions & 0 deletions airtest/core/android/constant.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
import os
import re
import sys
import subprocess
from airtest.utils.compat import decode_path

THISPATH = decode_path(os.path.dirname(os.path.realpath(__file__)))
Expand All @@ -26,6 +28,19 @@
IP_PATTERN = re.compile(r'(\d+\.){3}\d+')


if sys.platform.startswith("win"):
# Don't display the Windows GPF dialog if the invoked program dies.
try:
SUBPROCESS_FLAG = subprocess.CREATE_NO_WINDOW # in Python 3.7+
except AttributeError:
import ctypes
SEM_NOGPFAULTERRORBOX = 0x0002 # From MSDN
ctypes.windll.kernel32.SetErrorMode(SEM_NOGPFAULTERRORBOX) # win32con.CREATE_NO_WINDOW?
SUBPROCESS_FLAG = 0x8000000
else:
SUBPROCESS_FLAG = 0


class CAP_METHOD(object):
MINICAP = "MINICAP"
MINICAP_STREAM = "MINICAP_STREAM"
Expand Down
37 changes: 36 additions & 1 deletion airtest/utils/compat.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import sys
import os
import subprocess
import threading
from six import PY3, raise_from, reraise


Expand Down Expand Up @@ -39,4 +41,37 @@ def raisefrom(exc_type, message, exc):
if sys.version_info[:2] >= (3, 2):
raise_from(exc_type(message), exc)
else:
reraise(exc_type, '%s - %s' % (message, exc), sys.exc_info()[2])
reraise(exc_type, '%s - %s' % (message, exc), sys.exc_info()[2])


def proc_communicate_timeout(proc, timeout):
"""
Enable subprocess.Popen to accept timeout parameters, compatible with py2 and py3
:param proc: subprocess.Popen()
:param timeout: timeout in seconds
:return: result of proc.communicate()
:raises: RuntimeError when timeout
"""
if sys.version_info[:2] >= (3, 3):
# in Python 3.3+
try:
stdout, stderr = proc.communicate(timeout=timeout)
except subprocess.TimeoutExpired as e:
proc.kill()
stdout, stderr = proc.communicate()
exp = RuntimeError("Command {cmd} timed out after {timeout} seconds: stdout['{stdout}'], "
"stderr['{stderr}']".format(cmd=proc.args, timeout=e.timeout,
stdout=stdout, stderr=stderr))
raise_from(exp, None)
else:
timer = threading.Timer(timeout, proc.kill)
try:
timer.start()
stdout, stderr = proc.communicate()
finally:
timer.cancel()
if proc.returncode > 0:
raise RuntimeError("Command timed out after {timeout} seconds: stdout['{stdout}'], "
"stderr['{stderr}']".format(timeout=timeout, stdout=stdout, stderr=stderr))
return stdout, stderr
3 changes: 3 additions & 0 deletions airtest/utils/snippet.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,8 @@ def ready_method(func):
@wraps(func)
def wrapper(inst, *args, **kwargs):
ret = func(inst, *args, **kwargs)
key = "_%s_ready" % func.__name__
if not getattr(inst, key, None):
setattr(inst, key, True)
return ret
return wrapper
13 changes: 10 additions & 3 deletions tests/test_adb.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# encoding=utf-8
from airtest.core.android.adb import ADB, AdbError, AdbShellError, DeviceConnectionError
from testconf import IMG, APK, PKG, try_remove
from .testconf import IMG, APK, PKG, try_remove
from types import GeneratorType
import os
import unittest
Expand All @@ -21,11 +21,11 @@ def test_start_server(self):
self.adb.start_server()

def test_version(self):
self.assertIn("1.0.39", self.adb.version())
self.assertIn("1.0.40", self.adb.version())

def test_other_adb_server(self):
adb = ADB(server_addr=("localhost", 5037))
self.assertIn("1.0.39", adb.version())
self.assertIn("1.0.40", adb.version())

def test_start_cmd(self):
proc = self.adb.start_cmd("devices", device=False)
Expand Down Expand Up @@ -66,6 +66,13 @@ def test_devices(self):
def test_get_status(self):
self.assertEqual(self.adb.get_status(), self.adb.status_device)

def test_cmd(self):
output = self.adb.cmd("shell whoami")
self.assertIsInstance(output, text_type)

with self.assertRaises(RuntimeError):
self.adb.cmd("shell top", timeout=2)

def test_wait_for_device(self):
self.adb.wait_for_device()

Expand Down

0 comments on commit 2f08784

Please sign in to comment.