| # Copyright 2019 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| |
| import logging |
| |
| from six.moves import range # pylint: disable=redefined-builtin |
| from devil import base_error |
| from devil.android import device_errors |
| from devil.utils import parallelizer |
| from devil.utils import reraiser_thread |
| from devil.utils import timeout_retry |
| from pylib.local.device import local_device_environment |
| from pylib.local.emulator import avd |
| |
| # Mirroring https://bit.ly/2OjuxcS#23 |
| _MAX_ANDROID_EMULATORS = 16 |
| |
| |
| # TODO(1262303): After Telemetry is supported by python3 we can re-add |
| # super without arguments in this script. |
| # pylint: disable=super-with-arguments |
| class LocalEmulatorEnvironment(local_device_environment.LocalDeviceEnvironment): |
| |
| def __init__(self, args, output_manager, error_func): |
| super(LocalEmulatorEnvironment, self).__init__(args, output_manager, |
| error_func) |
| self._avd_config = avd.AvdConfig(args.avd_config) |
| if args.emulator_count < 1: |
| error_func('--emulator-count must be >= 1') |
| elif args.emulator_count >= _MAX_ANDROID_EMULATORS: |
| logging.warning('--emulator-count capped at 16.') |
| self._emulator_count = min(_MAX_ANDROID_EMULATORS, args.emulator_count) |
| self._emulator_window = args.emulator_window |
| self._emulator_debug_tags = args.emulator_debug_tags |
| self._writable_system = ((hasattr(args, 'use_webview_provider') |
| and args.use_webview_provider) |
| or (hasattr(args, 'replace_system_package') |
| and args.replace_system_package) |
| or (hasattr(args, 'system_packages_to_remove') |
| and args.system_packages_to_remove)) |
| |
| self._emulator_instances = [] |
| self._device_serials = [] |
| |
| #override |
| def SetUp(self): |
| self._avd_config.Install() |
| |
| emulator_instances = [ |
| self._avd_config.CreateInstance(output_manager=self.output_manager) |
| for _ in range(self._emulator_count) |
| ] |
| |
| def start_emulator_instance(inst): |
| def is_timeout_error(exc): |
| return isinstance( |
| exc, |
| (device_errors.CommandTimeoutError, reraiser_thread.TimeoutError)) |
| |
| def impl(inst): |
| try: |
| inst.Start(window=self._emulator_window, |
| writable_system=self._writable_system, |
| debug_tags=self._emulator_debug_tags, |
| require_fast_start=True) |
| except avd.AvdException: |
| logging.exception('Failed to start emulator instance.') |
| return None |
| except base_error.BaseError as e: |
| # Timeout error usually indicates the emulator is not responding. |
| # In this case, we should stop it forcely. |
| inst.Stop(force=is_timeout_error(e)) |
| raise |
| return inst |
| |
| return timeout_retry.Run(impl, |
| timeout=120 if self._writable_system else 60, |
| retries=2, |
| args=[inst], |
| retry_if_func=is_timeout_error) |
| |
| parallel_emulators = parallelizer.SyncParallelizer(emulator_instances) |
| self._emulator_instances = [ |
| emu |
| for emu in parallel_emulators.pMap(start_emulator_instance).pGet(None) |
| if emu is not None |
| ] |
| self._device_serials = [e.serial for e in self._emulator_instances] |
| |
| if not self._emulator_instances: |
| raise Exception('Failed to start any instances of the emulator.') |
| if len(self._emulator_instances) < self._emulator_count: |
| logging.warning( |
| 'Running with fewer emulator instances than requested (%d vs %d)', |
| len(self._emulator_instances), self._emulator_count) |
| |
| super(LocalEmulatorEnvironment, self).SetUp() |
| |
| #override |
| def TearDown(self): |
| try: |
| super(LocalEmulatorEnvironment, self).TearDown() |
| finally: |
| parallelizer.SyncParallelizer(self._emulator_instances).Stop() |