[Android] Convert to DeviceUtils versions of WaitUntilFullyBooted and GetExternalStoragePath.

BUG=267773

Review URL: https://codereview.chromium.org/294113003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272434 0039d316-1c4b-4281-b951-d872f2087c98
parent 4b202aef
...@@ -52,7 +52,7 @@ class BaseTestRunner(object): ...@@ -52,7 +52,7 @@ class BaseTestRunner(object):
def _PushTestServerPortInfoToDevice(self): def _PushTestServerPortInfoToDevice(self):
"""Pushes the latest port information to device.""" """Pushes the latest port information to device."""
self.device.old_interface.SetFileContents( self.device.old_interface.SetFileContents(
self.device.old_interface.GetExternalStorage() + '/' + self.device.GetExternalStoragePath() + '/' +
NET_TEST_SERVER_PORT_INFO_FILE, NET_TEST_SERVER_PORT_INFO_FILE,
'%d:%d' % (self.test_server_spawner_port, self.test_server_port)) '%d:%d' % (self.test_server_spawner_port, self.test_server_port))
......
...@@ -21,12 +21,30 @@ sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'third_party', ...@@ -21,12 +21,30 @@ sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'third_party',
'android_testrunner')) 'android_testrunner'))
import errors as old_errors import errors as old_errors
DEFAULT_TIMEOUT_ATTR = '_default_timeout'
DEFAULT_RETRIES_ATTR = '_default_retries'
def _TimeoutRetryWrapper(f, timeout_func, retries_func):
def _TimeoutRetryWrapper(f, timeout_func, retries_func, pass_values=False):
""" Wraps a funcion with timeout and retry handling logic.
Args:
f: The function to wrap.
timeout_func: A callable that returns the timeout value.
retries_func: A callable that returns the retries value.
pass_values: If True, passes the values returned by |timeout_func| and
|retries_func| to the wrapped function as 'timeout' and
'retries' kwargs, respectively.
Returns:
The wrapped function.
"""
@functools.wraps(f) @functools.wraps(f)
def TimeoutRetryWrapper(*args, **kwargs): def TimeoutRetryWrapper(*args, **kwargs):
timeout = timeout_func(*args, **kwargs) timeout = timeout_func(*args, **kwargs)
retries = retries_func(*args, **kwargs) retries = retries_func(*args, **kwargs)
if pass_values:
kwargs['timeout'] = timeout
kwargs['retries'] = retries
def impl(): def impl():
return f(*args, **kwargs) return f(*args, **kwargs)
try: try:
...@@ -41,15 +59,32 @@ def _TimeoutRetryWrapper(f, timeout_func, retries_func): ...@@ -41,15 +59,32 @@ def _TimeoutRetryWrapper(f, timeout_func, retries_func):
def WithTimeoutAndRetries(f): def WithTimeoutAndRetries(f):
"""A decorator that handles timeouts and retries.""" """A decorator that handles timeouts and retries.
'timeout' and 'retries' kwargs must be passed to the function.
Args:
f: The function to decorate.
Returns:
The decorated function.
"""
get_timeout = lambda *a, **kw: kw['timeout'] get_timeout = lambda *a, **kw: kw['timeout']
get_retries = lambda *a, **kw: kw['retries'] get_retries = lambda *a, **kw: kw['retries']
return _TimeoutRetryWrapper(f, get_timeout, get_retries) return _TimeoutRetryWrapper(f, get_timeout, get_retries)
def WithExplicitTimeoutAndRetries(timeout, retries): def WithExplicitTimeoutAndRetries(timeout, retries):
""" """Returns a decorator that handles timeouts and retries.
A decorator that handles timeouts and retries using the provided values.
The provided |timeout| and |retries| values are always used.
Args:
timeout: The number of seconds to wait for the decorated function to
return. Always used.
retries: The number of times the decorated function should be retried on
failure. Always used.
Returns:
The actual decorator.
""" """
def decorator(f): def decorator(f):
get_timeout = lambda *a, **kw: timeout get_timeout = lambda *a, **kw: timeout
...@@ -59,26 +94,52 @@ def WithExplicitTimeoutAndRetries(timeout, retries): ...@@ -59,26 +94,52 @@ def WithExplicitTimeoutAndRetries(timeout, retries):
def WithTimeoutAndRetriesDefaults(default_timeout, default_retries): def WithTimeoutAndRetriesDefaults(default_timeout, default_retries):
""" """Returns a decorator that handles timeouts and retries.
A decorator that handles timeouts and retries using the provided defaults.
The provided |default_timeout| and |default_retries| values are used only
if timeout and retries values are not provided.
Args:
default_timeout: The number of seconds to wait for the decorated function
to return. Only used if a 'timeout' kwarg is not passed
to the decorated function.
default_retries: The number of times the decorated function should be
retried on failure. Only used if a 'retries' kwarg is not
passed to the decorated function.
Returns:
The actual decorator.
""" """
def decorator(f): def decorator(f):
get_timeout = lambda *a, **kw: kw.get('timeout', default_timeout) get_timeout = lambda *a, **kw: kw.get('timeout', default_timeout)
get_retries = lambda *a, **kw: kw.get('retries', default_retries) get_retries = lambda *a, **kw: kw.get('retries', default_retries)
return _TimeoutRetryWrapper(f, get_timeout, get_retries) return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True)
return decorator return decorator
def WithTimeoutAndRetriesFromInstance( def WithTimeoutAndRetriesFromInstance(
default_timeout_name, default_retries_name): default_timeout_name=DEFAULT_TIMEOUT_ATTR,
""" default_retries_name=DEFAULT_RETRIES_ATTR):
A decorator that handles timeouts and retries using instance defaults. """Returns a decorator that handles timeouts and retries.
The provided |default_timeout_name| and |default_retries_name| are used to
get the default timeout value and the default retries value from the object
instance if timeout and retries values are not provided.
Note that this should only be used to decorate methods, not functions.
Args:
default_timeout_name: The name of the default timeout attribute of the
instance.
default_retries_name: The name of the default retries attribute of the
instance.
Returns:
The actual decorator.
""" """
def decorator(f): def decorator(f):
def get_timeout(inst, *_args, **kwargs): def get_timeout(inst, *_args, **kwargs):
return kwargs.get('timeout', getattr(inst, default_timeout_name)) return kwargs.get('timeout', getattr(inst, default_timeout_name))
def get_retries(inst, *_args, **kwargs): def get_retries(inst, *_args, **kwargs):
return kwargs.get('retries', getattr(inst, default_retries_name)) return kwargs.get('retries', getattr(inst, default_retries_name))
return _TimeoutRetryWrapper(f, get_timeout, get_retries) return _TimeoutRetryWrapper(f, get_timeout, get_retries, pass_values=True)
return decorator return decorator
...@@ -8,12 +8,22 @@ Unit tests for decorators.py. ...@@ -8,12 +8,22 @@ Unit tests for decorators.py.
# pylint: disable=W0613 # pylint: disable=W0613
import os
import sys
import time import time
import traceback import traceback
import unittest import unittest
from pylib import constants
from pylib.device import decorators from pylib.device import decorators
from pylib.device import device_errors from pylib.device import device_errors
from pylib.utils import reraiser_thread
# TODO(jbudorick) Remove once the DeviceUtils implementations are no longer
# backed by AndroidCommands / android_testrunner.
sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'third_party',
'android_testrunner'))
import errors as old_errors
_DEFAULT_TIMEOUT = 30 _DEFAULT_TIMEOUT = 30
_DEFAULT_RETRIES = 3 _DEFAULT_RETRIES = 3
...@@ -69,6 +79,39 @@ class DecoratorsTest(unittest.TestCase): ...@@ -69,6 +79,39 @@ class DecoratorsTest(unittest.TestCase):
self.assertEquals(expected_timeout, actual_timeout) self.assertEquals(expected_timeout, actual_timeout)
self.assertEquals(expected_retries, actual_retries) self.assertEquals(expected_retries, actual_retries)
def testFunctionDecoratorTranslatesOldExceptions(self):
"""Tests that the explicit decorator translates old exceptions."""
@decorators.WithTimeoutAndRetries
def alwaysRaisesProvidedException(exception, timeout=None, retries=None):
raise exception
exception_desc = 'Old response timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
alwaysRaisesProvidedException(
old_errors.WaitForResponseTimedOutError(exception_desc),
timeout=10, retries=1)
self.assertEquals(exception_desc, str(e.exception))
exception_desc = 'Old device error'
with self.assertRaises(device_errors.DeviceUnreachableError) as e:
alwaysRaisesProvidedException(
old_errors.DeviceUnresponsiveError(exception_desc),
timeout=10, retries=1)
self.assertEquals(exception_desc, str(e.exception))
def testFunctionDecoratorTranslatesReraiserExceptions(self):
"""Tests that the explicit decorator translates reraiser exceptions."""
@decorators.WithTimeoutAndRetries
def alwaysRaisesProvidedException(exception, timeout=None, retries=None):
raise exception
exception_desc = 'Reraiser thread timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
alwaysRaisesProvidedException(
reraiser_thread.TimeoutError(exception_desc),
timeout=10, retries=1)
self.assertEquals(exception_desc, str(e.exception))
def testDefaultsFunctionDecoratorDoesTimeouts(self): def testDefaultsFunctionDecoratorDoesTimeouts(self):
"""Tests that the defaults decorator handles timeout logic.""" """Tests that the defaults decorator handles timeout logic."""
DecoratorsTest._decorated_function_called_count = 0 DecoratorsTest._decorated_function_called_count = 0
...@@ -109,6 +152,52 @@ class DecoratorsTest(unittest.TestCase): ...@@ -109,6 +152,52 @@ class DecoratorsTest(unittest.TestCase):
alwaysRaisesCommandFailedError(retries=5) alwaysRaisesCommandFailedError(retries=5)
self.assertEquals(6, DecoratorsTest._decorated_function_called_count) self.assertEquals(6, DecoratorsTest._decorated_function_called_count)
def testDefaultsFunctionDecoratorPassesValues(self):
"""Tests that the defaults decorator passes timeout and retries kwargs."""
@decorators.WithTimeoutAndRetriesDefaults(30, 10)
def alwaysReturnsTimeouts(timeout=None, retries=None):
return timeout
self.assertEquals(30, alwaysReturnsTimeouts())
self.assertEquals(120, alwaysReturnsTimeouts(timeout=120))
@decorators.WithTimeoutAndRetriesDefaults(30, 10)
def alwaysReturnsRetries(timeout=None, retries=None):
return retries
self.assertEquals(10, alwaysReturnsRetries())
self.assertEquals(1, alwaysReturnsRetries(retries=1))
def testDefaultsFunctionDecoratorTranslatesOldExceptions(self):
"""Tests that the explicit decorator translates old exceptions."""
@decorators.WithTimeoutAndRetriesDefaults(30, 10)
def alwaysRaisesProvidedException(exception, timeout=None, retries=None):
raise exception
exception_desc = 'Old response timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
alwaysRaisesProvidedException(
old_errors.WaitForResponseTimedOutError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
exception_desc = 'Old device error'
with self.assertRaises(device_errors.DeviceUnreachableError) as e:
alwaysRaisesProvidedException(
old_errors.DeviceUnresponsiveError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
def testDefaultsFunctionDecoratorTranslatesReraiserExceptions(self):
"""Tests that the explicit decorator translates reraiser exceptions."""
@decorators.WithTimeoutAndRetriesDefaults(30, 10)
def alwaysRaisesProvidedException(exception, timeout=None, retries=None):
raise exception
exception_desc = 'Reraiser thread timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
alwaysRaisesProvidedException(
reraiser_thread.TimeoutError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
def testExplicitFunctionDecoratorDoesTimeouts(self): def testExplicitFunctionDecoratorDoesTimeouts(self):
"""Tests that the explicit decorator handles timeout logic.""" """Tests that the explicit decorator handles timeout logic."""
DecoratorsTest._decorated_function_called_count = 0 DecoratorsTest._decorated_function_called_count = 0
...@@ -137,6 +226,36 @@ class DecoratorsTest(unittest.TestCase): ...@@ -137,6 +226,36 @@ class DecoratorsTest(unittest.TestCase):
alwaysRaisesCommandFailedError() alwaysRaisesCommandFailedError()
self.assertEquals(11, DecoratorsTest._decorated_function_called_count) self.assertEquals(11, DecoratorsTest._decorated_function_called_count)
def testExplicitDecoratorTranslatesOldExceptions(self):
"""Tests that the explicit decorator translates old exceptions."""
@decorators.WithExplicitTimeoutAndRetries(30, 10)
def alwaysRaisesProvidedException(exception):
raise exception
exception_desc = 'Old response timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
alwaysRaisesProvidedException(
old_errors.WaitForResponseTimedOutError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
exception_desc = 'Old device error'
with self.assertRaises(device_errors.DeviceUnreachableError) as e:
alwaysRaisesProvidedException(
old_errors.DeviceUnresponsiveError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
def testExplicitDecoratorTranslatesReraiserExceptions(self):
"""Tests that the explicit decorator translates reraiser exceptions."""
@decorators.WithExplicitTimeoutAndRetries(30, 10)
def alwaysRaisesProvidedException(exception):
raise exception
exception_desc = 'Reraiser thread timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
alwaysRaisesProvidedException(
reraiser_thread.TimeoutError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
class _MethodDecoratorTestObject(object): class _MethodDecoratorTestObject(object):
"""An object suitable for testing the method decorator.""" """An object suitable for testing the method decorator."""
...@@ -165,6 +284,26 @@ class DecoratorsTest(unittest.TestCase): ...@@ -165,6 +284,26 @@ class DecoratorsTest(unittest.TestCase):
raise device_errors.CommandFailedError(['testCommand'], raise device_errors.CommandFailedError(['testCommand'],
'testCommand failed') 'testCommand failed')
# pylint: disable=R0201
@decorators.WithTimeoutAndRetriesFromInstance(
'default_timeout', 'default_retries')
def alwaysReturnsTimeout(self, timeout=None, retries=None):
return timeout
@decorators.WithTimeoutAndRetriesFromInstance(
'default_timeout', 'default_retries')
def alwaysReturnsRetries(self, timeout=None, retries=None):
return retries
@decorators.WithTimeoutAndRetriesFromInstance(
'default_timeout', 'default_retries')
def alwaysRaisesProvidedException(self, exception, timeout=None,
retries=None):
raise exception
# pylint: enable=R0201
def testMethodDecoratorDoesTimeout(self): def testMethodDecoratorDoesTimeout(self):
"""Tests that the method decorator handles timeout logic.""" """Tests that the method decorator handles timeout logic."""
...@@ -181,7 +320,7 @@ class DecoratorsTest(unittest.TestCase): ...@@ -181,7 +320,7 @@ class DecoratorsTest(unittest.TestCase):
self.assertEquals(1, test_obj.function_call_counters['alwaysTimesOut']) self.assertEquals(1, test_obj.function_call_counters['alwaysTimesOut'])
def testMethodDecoratorDoesRetries(self): def testMethodDecoratorDoesRetries(self):
""" Tests that the method decorator handles retries logic.""" """Tests that the method decorator handles retries logic."""
test_obj = self._MethodDecoratorTestObject(self) test_obj = self._MethodDecoratorTestObject(self)
with self.assertRaises(device_errors.CommandFailedError): with self.assertRaises(device_errors.CommandFailedError):
try: try:
...@@ -192,8 +331,39 @@ class DecoratorsTest(unittest.TestCase): ...@@ -192,8 +331,39 @@ class DecoratorsTest(unittest.TestCase):
self.assertEquals( self.assertEquals(
11, test_obj.function_call_counters['alwaysRaisesCommandFailedError']) 11, test_obj.function_call_counters['alwaysRaisesCommandFailedError'])
def testMethodDecoratorPassesValues(self):
"""Tests that the method decorator passes timeout and retries kwargs."""
test_obj = self._MethodDecoratorTestObject(
self, default_timeout=42, default_retries=31)
self.assertEquals(42, test_obj.alwaysReturnsTimeout())
self.assertEquals(41, test_obj.alwaysReturnsTimeout(timeout=41))
self.assertEquals(31, test_obj.alwaysReturnsRetries())
self.assertEquals(32, test_obj.alwaysReturnsRetries(retries=32))
if __name__ == '__main__': def testMethodDecoratorTranslatesOldExceptions(self):
unittest.main() test_obj = self._MethodDecoratorTestObject(self)
exception_desc = 'Old response timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
test_obj.alwaysRaisesProvidedException(
old_errors.WaitForResponseTimedOutError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
exception_desc = 'Old device error'
with self.assertRaises(device_errors.DeviceUnreachableError) as e:
test_obj.alwaysRaisesProvidedException(
old_errors.DeviceUnresponsiveError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
def testMethodDecoratorTranslatesReraiserExceptions(self):
test_obj = self._MethodDecoratorTestObject(self)
exception_desc = 'Reraiser thread timeout error'
with self.assertRaises(device_errors.CommandTimeoutError) as e:
test_obj.alwaysRaisesProvidedException(
reraiser_thread.TimeoutError(exception_desc))
self.assertEquals(exception_desc, str(e.exception))
if __name__ == '__main__':
unittest.main(verbosity=2)
...@@ -9,6 +9,8 @@ Eventually, this will be based on adb_wrapper. ...@@ -9,6 +9,8 @@ Eventually, this will be based on adb_wrapper.
""" """
# pylint: disable=W0613 # pylint: disable=W0613
import time
import pylib.android_commands import pylib.android_commands
from pylib.device import adb_wrapper from pylib.device import adb_wrapper
from pylib.device import decorators from pylib.device import decorators
...@@ -70,9 +72,10 @@ class DeviceUtils(object): ...@@ -70,9 +72,10 @@ class DeviceUtils(object):
raise ValueError('Unsupported type passed for argument "device"') raise ValueError('Unsupported type passed for argument "device"')
self._default_timeout = default_timeout self._default_timeout = default_timeout
self._default_retries = default_retries self._default_retries = default_retries
assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR))
assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR))
@decorators.WithTimeoutAndRetriesFromInstance( @decorators.WithTimeoutAndRetriesFromInstance()
'_default_timeout', '_default_retries')
def IsOnline(self, timeout=None, retries=None): def IsOnline(self, timeout=None, retries=None):
""" Checks whether the device is online. """ Checks whether the device is online.
...@@ -86,8 +89,7 @@ class DeviceUtils(object): ...@@ -86,8 +89,7 @@ class DeviceUtils(object):
""" """
return self.old_interface.IsOnline() return self.old_interface.IsOnline()
@decorators.WithTimeoutAndRetriesFromInstance( @decorators.WithTimeoutAndRetriesFromInstance()
'_default_timeout', '_default_retries')
def HasRoot(self, timeout=None, retries=None): def HasRoot(self, timeout=None, retries=None):
""" Checks whether or not adbd has root privileges. """ Checks whether or not adbd has root privileges.
...@@ -99,8 +101,7 @@ class DeviceUtils(object): ...@@ -99,8 +101,7 @@ class DeviceUtils(object):
""" """
return self.old_interface.IsRootEnabled() return self.old_interface.IsRootEnabled()
@decorators.WithTimeoutAndRetriesFromInstance( @decorators.WithTimeoutAndRetriesFromInstance()
'_default_timeout', '_default_retries')
def EnableRoot(self, timeout=None, retries=None): def EnableRoot(self, timeout=None, retries=None):
""" Restarts adbd with root privileges. """ Restarts adbd with root privileges.
...@@ -114,12 +115,51 @@ class DeviceUtils(object): ...@@ -114,12 +115,51 @@ class DeviceUtils(object):
raise device_errors.CommandFailedError( raise device_errors.CommandFailedError(
'adb root', 'Could not enable root.') 'adb root', 'Could not enable root.')
@decorators.WithTimeoutAndRetriesFromInstance()
def GetExternalStoragePath(self, timeout=None, retries=None):
""" Get the device's path to its SD card.
Args:
timeout: Same as for |IsOnline|.
retries: Same as for |IsOnline|.
Returns:
The device's path to its SD card.
"""
try:
return self.old_interface.GetExternalStorage()
except AssertionError as e:
raise device_errors.CommandFailedError(str(e))
@decorators.WithTimeoutAndRetriesFromInstance()
def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
""" Wait for the device to fully boot.
This means waiting for the device to boot, the package manager to be
available, and the SD card to be ready. It can optionally mean waiting
for wifi to come up, too.
Args:
wifi: A boolean indicating if we should wait for wifi to come up or not.
timeout: Same as for |IsOnline|.
retries: Same as for |IsOnline|.
Raises:
CommandTimeoutError if one of the component waits times out.
DeviceUnreachableError if the device becomes unresponsive.
"""
self.old_interface.WaitForSystemBootCompleted(timeout)
self.old_interface.WaitForDevicePm()
self.old_interface.WaitForSdCardReady(timeout)
if wifi:
while not 'Wi-Fi is enabled' in (
self.old_interface.RunShellCommand('dumpsys wifi')):
time.sleep(0.1)
def __str__(self): def __str__(self):
"""Returns the device serial.""" """Returns the device serial."""
return self.old_interface.GetDevice() return self.old_interface.GetDevice()
@staticmethod @staticmethod
def parallel(devices): def parallel(devices=None, async=False):
""" Creates a Parallelizer to operate over the provided list of devices. """ Creates a Parallelizer to operate over the provided list of devices.
If |devices| is either |None| or an empty list, the Parallelizer will If |devices| is either |None| or an empty list, the Parallelizer will
...@@ -127,13 +167,18 @@ class DeviceUtils(object): ...@@ -127,13 +167,18 @@ class DeviceUtils(object):
Args: Args:
devices: A list of either DeviceUtils instances or objects from devices: A list of either DeviceUtils instances or objects from
from which DeviceUtils instances can be constructed. from which DeviceUtils instances can be constructed. If None,
all attached devices will be used.
async: If true, returns a Parallelizer that runs operations
asynchronously.
Returns: Returns:
A Parallelizer operating over |devices|. A Parallelizer operating over |devices|.
""" """
if not devices or len(devices) == 0: if not devices or len(devices) == 0:
devices = pylib.android_commands.AndroidCommands.GetAttachedDevices() devices = pylib.android_commands.GetAttachedDevices()
return parallelizer.Parallelizer([ parallelizer_type = (parallelizer.Parallelizer if async
else parallelizer.SyncParallelizer)
return parallelizer_type([
d if isinstance(d, DeviceUtils) else DeviceUtils(d) d if isinstance(d, DeviceUtils) else DeviceUtils(d)
for d in devices]) for d in devices])
...@@ -10,6 +10,7 @@ Unit tests for the contents of device_utils.py (mostly DeviceUtils). ...@@ -10,6 +10,7 @@ Unit tests for the contents of device_utils.py (mostly DeviceUtils).
# pylint: disable=W0613 # pylint: disable=W0613
import random import random
import time
import unittest import unittest
from pylib import android_commands from pylib import android_commands
...@@ -70,9 +71,13 @@ class DeviceUtilsTest(unittest.TestCase): ...@@ -70,9 +71,13 @@ class DeviceUtilsTest(unittest.TestCase):
@staticmethod @staticmethod
def _getTestAdbWrapper(): def _getTestAdbWrapper():
devices = adb_wrapper.AdbWrapper.GetDevices() devices = adb_wrapper.AdbWrapper.GetDevices()
if devices: start_time = time.time()
return random.choice(devices) while time.time() - start_time < device_utils._DEFAULT_TIMEOUT:
return None if devices:
return random.choice(devices)
time.sleep(1)
raise device_errors.DeviceUnreachableError(
'No devices available for testing...')
@staticmethod @staticmethod
def _getUnusedSerial(): def _getUnusedSerial():
...@@ -143,6 +148,30 @@ class DeviceUtilsTest(unittest.TestCase): ...@@ -143,6 +148,30 @@ class DeviceUtilsTest(unittest.TestCase):
d.EnableRoot() d.EnableRoot()
self.assertTrue(d.HasRoot()) self.assertTrue(d.HasRoot())
def testGetExternalStorage(self):
a = self._getTestAdbWrapper()
d = device_utils.DeviceUtils(a)
actual_external_storage = a.Shell('echo $EXTERNAL_STORAGE').splitlines()[0]
if actual_external_storage and len(actual_external_storage) != 0:
self.assertEquals(actual_external_storage, d.GetExternalStoragePath())
def testWaitUntilFullyBooted(self):
a = self._getTestAdbWrapper()
d = device_utils.DeviceUtils(a)
a.Reboot()
while a.GetState() == 'device':
time.sleep(0.1)
d.WaitUntilFullyBooted(wifi=True)
self.assertEquals(
'1', a.Shell('getprop sys.boot_completed').splitlines()[0])
self.assertTrue(
a.Shell('pm path android').splitlines()[0].startswith('package:'))
self.assertTrue(a.Shell('ls $EXTERNAL_STORAGE'))
self.assertTrue(
'Wi-Fi is enabled' in a.Shell('dumpsys wifi').splitlines())
if __name__ == '__main__': if __name__ == '__main__':
unittest.main(verbosity=2, buffer=True) unittest.main(verbosity=2, buffer=True)
......
...@@ -85,9 +85,14 @@ class TestPackageApk(TestPackage): ...@@ -85,9 +85,14 @@ class TestPackageApk(TestPackage):
# Content shell creates a profile on the sdscard which accumulates cache # Content shell creates a profile on the sdscard which accumulates cache
# files over time. # files over time.
if self.suite_name == 'content_browsertests': if self.suite_name == 'content_browsertests':
device.old_interface.RunShellCommand( try:
'rm -r %s/content_shell' % device.old_interface.GetExternalStorage(), device.old_interface.RunShellCommand(
timeout_time=60 * 2) 'rm -r %s/content_shell' % device.GetExternalStoragePath(),
timeout_time=60 * 2)
except device_errors.CommandFailedError:
# TODO(jbudorick) Handle this exception appropriately once the
# conversions are done.
pass
#override #override
def CreateCommandLineFileOnDevice(self, device, test_filter, test_arguments): def CreateCommandLineFileOnDevice(self, device, test_filter, test_arguments):
......
...@@ -12,6 +12,7 @@ import tempfile ...@@ -12,6 +12,7 @@ import tempfile
from pylib import cmd_helper from pylib import cmd_helper
from pylib import constants from pylib import constants
from pylib import pexpect from pylib import pexpect
from pylib.device import device_errors
from pylib.gtest.test_package import TestPackage from pylib.gtest.test_package import TestPackage
...@@ -62,14 +63,17 @@ class TestPackageExecutable(TestPackage): ...@@ -62,14 +63,17 @@ class TestPackageExecutable(TestPackage):
# /code/chrome if GCOV_PREFIX_STRIP=3 # /code/chrome if GCOV_PREFIX_STRIP=3
try: try:
depth = os.environ['NATIVE_COVERAGE_DEPTH_STRIP'] depth = os.environ['NATIVE_COVERAGE_DEPTH_STRIP']
export_string = ('export GCOV_PREFIX="%s/gcov"\n' %
device.GetExternalStoragePath())
export_string += 'export GCOV_PREFIX_STRIP=%s\n' % depth
return export_string
except KeyError: except KeyError:
logging.info('NATIVE_COVERAGE_DEPTH_STRIP is not defined: ' logging.info('NATIVE_COVERAGE_DEPTH_STRIP is not defined: '
'No native coverage.') 'No native coverage.')
return '' return ''
export_string = ('export GCOV_PREFIX="%s/gcov"\n' % except device_errors.CommandFailedError:
device.old_interface.GetExternalStorage()) logging.info('No external storage found: No native coverage.')
export_string += 'export GCOV_PREFIX_STRIP=%s\n' % depth return ''
return export_string
#override #override
def ClearApplicationState(self, device): def ClearApplicationState(self, device):
......
...@@ -61,14 +61,15 @@ class TestRunner(base_test_runner.BaseTestRunner): ...@@ -61,14 +61,15 @@ class TestRunner(base_test_runner.BaseTestRunner):
#override #override
def PushDataDeps(self): def PushDataDeps(self):
self.device.old_interface.WaitForSdCardReady(20) self.device.WaitUntilFullyBooted(timeout=20)
self.tool.CopyFiles() self.tool.CopyFiles()
if os.path.exists(constants.ISOLATE_DEPS_DIR): if os.path.exists(constants.ISOLATE_DEPS_DIR):
device_dir = self.device.old_interface.GetExternalStorage()
# TODO(frankf): linux_dumper_unittest_helper needs to be in the same dir # TODO(frankf): linux_dumper_unittest_helper needs to be in the same dir
# as breakpad_unittests exe. Find a better way to do this. # as breakpad_unittests exe. Find a better way to do this.
if self.test_package.suite_name == 'breakpad_unittests': if self.test_package.suite_name == 'breakpad_unittests':
device_dir = constants.TEST_EXECUTABLE_DIR device_dir = constants.TEST_EXECUTABLE_DIR
else:
device_dir = self.device.GetExternalStoragePath()
for p in os.listdir(constants.ISOLATE_DEPS_DIR): for p in os.listdir(constants.ISOLATE_DEPS_DIR):
self.device.old_interface.PushIfNeeded( self.device.old_interface.PushIfNeeded(
os.path.join(constants.ISOLATE_DEPS_DIR, p), os.path.join(constants.ISOLATE_DEPS_DIR, p),
......
...@@ -104,11 +104,11 @@ class TestRunner(base_test_runner.BaseTestRunner): ...@@ -104,11 +104,11 @@ class TestRunner(base_test_runner.BaseTestRunner):
test_data = _GetDataFilesForTestSuite(self.test_pkg.GetApkName()) test_data = _GetDataFilesForTestSuite(self.test_pkg.GetApkName())
if test_data: if test_data:
# Make sure SD card is ready. # Make sure SD card is ready.
self.device.old_interface.WaitForSdCardReady(20) self.device.WaitUntilFullyBooted(timeout=20)
for p in test_data: for p in test_data:
self.device.old_interface.PushIfNeeded( self.device.old_interface.PushIfNeeded(
os.path.join(constants.DIR_SOURCE_ROOT, p), os.path.join(constants.DIR_SOURCE_ROOT, p),
os.path.join(self.device.old_interface.GetExternalStorage(), p)) os.path.join(self.device.GetExternalStoragePath(), p))
# TODO(frankf): Specify test data in this file as opposed to passing # TODO(frankf): Specify test data in this file as opposed to passing
# as command-line. # as command-line.
...@@ -121,7 +121,7 @@ class TestRunner(base_test_runner.BaseTestRunner): ...@@ -121,7 +121,7 @@ class TestRunner(base_test_runner.BaseTestRunner):
self.device.old_interface.PushIfNeeded( self.device.old_interface.PushIfNeeded(
host_test_files_path, host_test_files_path,
'%s/%s/%s' % ( '%s/%s/%s' % (
self.device.old_interface.GetExternalStorage(), self.device.GetExternalStoragePath(),
TestRunner._DEVICE_DATA_DIR, TestRunner._DEVICE_DATA_DIR,
dst_layer)) dst_layer))
self.tool.CopyFiles() self.tool.CopyFiles()
...@@ -184,7 +184,7 @@ class TestRunner(base_test_runner.BaseTestRunner): ...@@ -184,7 +184,7 @@ class TestRunner(base_test_runner.BaseTestRunner):
if self.coverage_dir: if self.coverage_dir:
coverage_basename = '%s.ec' % test coverage_basename = '%s.ec' % test
self.coverage_device_file = '%s/%s/%s' % ( self.coverage_device_file = '%s/%s/%s' % (
self.device.old_interface.GetExternalStorage(), self.device.GetExternalStoragePath(),
TestRunner._DEVICE_COVERAGE_DIR, coverage_basename) TestRunner._DEVICE_COVERAGE_DIR, coverage_basename)
self.coverage_host_file = os.path.join( self.coverage_host_file = os.path.join(
self.coverage_dir, coverage_basename) self.coverage_dir, coverage_basename)
......
...@@ -31,7 +31,7 @@ class VideoRecorder(object): ...@@ -31,7 +31,7 @@ class VideoRecorder(object):
device = pylib.device.device_utils.DeviceUtils(device) device = pylib.device.device_utils.DeviceUtils(device)
self._device = device self._device = device
self._device_file = ( self._device_file = (
'%s/screen-recording.mp4' % device.old_interface.GetExternalStorage()) '%s/screen-recording.mp4' % device.GetExternalStoragePath())
self._host_file = host_file or ('screen-recording-%s.mp4' % self._host_file = host_file or ('screen-recording-%s.mp4' %
device.old_interface.GetTimestamp()) device.old_interface.GetTimestamp())
self._host_file = os.path.abspath(self._host_file) self._host_file = os.path.abspath(self._host_file)
......
...@@ -416,6 +416,8 @@ class Emulator(object): ...@@ -416,6 +416,8 @@ class Emulator(object):
if wait_for_boot: if wait_for_boot:
# Now that we checked for obvious problems, wait for a boot complete. # Now that we checked for obvious problems, wait for a boot complete.
# Waiting for the package manager is sometimes problematic. # Waiting for the package manager is sometimes problematic.
# TODO(jbudorick) Convert this once waiting for the package manager and
# the external storage is no longer problematic.
d = device_utils.DeviceUtils(self.device_serial) d = device_utils.DeviceUtils(self.device_serial)
d.old_interface.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT) d.old_interface.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT)
......
...@@ -80,7 +80,7 @@ class Parallelizer(object): ...@@ -80,7 +80,7 @@ class Parallelizer(object):
""" """
self.pGet(None) self.pGet(None)
r = Parallelizer(self._orig_objs) r = type(self)(self._orig_objs)
r._objs = [getattr(o, name) for o in self._objs] r._objs = [getattr(o, name) for o in self._objs]
return r return r
...@@ -92,7 +92,7 @@ class Parallelizer(object): ...@@ -92,7 +92,7 @@ class Parallelizer(object):
""" """
self.pGet(None) self.pGet(None)
r = Parallelizer(self._orig_objs) r = type(self)(self._orig_objs)
r._objs = [o[index] for o in self._objs] r._objs = [o[index] for o in self._objs]
return r return r
...@@ -116,7 +116,7 @@ class Parallelizer(object): ...@@ -116,7 +116,7 @@ class Parallelizer(object):
if not callable(o): if not callable(o):
raise AttributeError("'%s' is not callable" % o.__name__) raise AttributeError("'%s' is not callable" % o.__name__)
r = Parallelizer(self._orig_objs) r = type(self)(self._orig_objs)
r._objs = reraiser_thread.ReraiserThreadGroup( r._objs = reraiser_thread.ReraiserThreadGroup(
[reraiser_thread.ReraiserThread( [reraiser_thread.ReraiserThread(
o, args=args, kwargs=kwargs, o, args=args, kwargs=kwargs,
......
...@@ -6,7 +6,6 @@ import logging ...@@ -6,7 +6,6 @@ import logging
import psutil import psutil
import signal import signal
from pylib import android_commands
from pylib.device import device_errors from pylib.device import device_errors
from pylib.device import device_utils from pylib.device import device_utils
...@@ -31,23 +30,17 @@ def _KillWebServers(): ...@@ -31,23 +30,17 @@ def _KillWebServers():
logging.warning('Failed waiting for %s to die. %s', p.pid, e) logging.warning('Failed waiting for %s to die. %s', p.pid, e)
def CleanupLeftoverProcesses(): def CleanupLeftoverProcesses():
"""Clean up the test environment, restarting fresh adb and HTTP daemons.""" """Clean up the test environment, restarting fresh adb and HTTP daemons."""
_KillWebServers() _KillWebServers()
did_restart_host_adb = False device_utils.RestartServer()
for device_serial in android_commands.GetAttachedDevices(): p = device_utils.DeviceUtils.parallel()
device = device_utils.DeviceUtils(device_serial) p.old_interface.RestartAdbdOnDevice()
# Make sure we restart the host adb server only once. try:
if not did_restart_host_adb: p.EnableRoot()
device_utils.RestartServer() except device_errors.CommandFailedError as e:
did_restart_host_adb = True # TODO(jbudorick) Handle this exception appropriately after interface
device.old_interface.RestartAdbdOnDevice() # conversions are finished.
try: logging.error(str(e))
device.EnableRoot() p.WaitUntilFullyBooted()
except device_errors.CommandFailedError as e:
# TODO(jbudorick) Handle this exception appropriately after interface
# conversions are finished.
logging.error(str(e))
device.old_interface.WaitForDevicePm()
...@@ -120,7 +120,7 @@ class AddressSanitizerTool(BaseTool): ...@@ -120,7 +120,7 @@ class AddressSanitizerTool(BaseTool):
'--device', self._device.old_interface.GetDevice(), '--device', self._device.old_interface.GetDevice(),
'--lib', self._lib, '--lib', self._lib,
'--extra-options', AddressSanitizerTool.EXTRA_OPTIONS]) '--extra-options', AddressSanitizerTool.EXTRA_OPTIONS])
self._device.old_interface.WaitForDevicePm() self._device.WaitUntilFullyBooted()
def GetTestWrapper(self): def GetTestWrapper(self):
return AddressSanitizerTool.WRAPPER_NAME return AddressSanitizerTool.WRAPPER_NAME
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment