1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Android module to prepare APKs and emulators to run webdriver-based tests.
"""
import re
import os
import subprocess
import sys
import packaging
import logging
from typing import List, Optional
from chrome.test.variations.test_utils import SRC_DIR
# The root for the module pylib/android is under build/android.
sys.path.append(os.path.join(SRC_DIR, 'build', 'android'))
# This import adds `devil` to `sys.path`.
import devil_chromium
from devil.android import apk_helper
from devil.android import device_utils
from devil.android import forwarder
from devil.android.sdk import adb_wrapper
from pylib.local.emulator import avd
_INSTALLER_SCRIPT_PY = os.path.join(
SRC_DIR, 'clank', 'bin', 'utils', 'installer_script_wrapper.py')
def _package_name(channel: str):
if channel in ('beta', 'dev', 'canary'):
return f'com.chrome.{channel}'
return 'com.android.chrome'
def _is_require_signed(channel: str) -> bool:
"""Check if we need to install a signed build."""
# The stable build has the same package name as prebuilt one, in order
# to avoid the signature mismatch, we need to install the one with the
# same signed build.
return channel == 'stable'
def install_chrome(channel: str, device: device_utils.DeviceUtils) -> str:
"""Installs Chrome to the device and returns the package name."""
args = [
_INSTALLER_SCRIPT_PY, f'--product=chrome',
f'--channel={channel}', f'--serial={device.serial}',
f'--adb={adb_wrapper.AdbWrapper.GetAdbPath()}',
]
args.append('--signed' if _is_require_signed(channel) else '--unsigned')
try:
subprocess.check_output(args=args)
except subprocess.CalledProcessError as e:
logging.error('Subprocess error caught %s', e.output.decode())
raise RuntimeError('Chrome installation failed.')
return _package_name(channel)
def install_webview(
channel: str,
device: device_utils.DeviceUtils
) -> packaging.version.Version:
"""Installs Webview to the device and returns the installed version."""
args = [
_INSTALLER_SCRIPT_PY, f'--product=webview',
f'--channel={channel}', f'--serial={device.serial}',
f'--adb={adb_wrapper.AdbWrapper.GetAdbPath()}',
]
args.append('--signed' if _is_require_signed(channel) else '--unsigned')
try:
subprocess.check_output(args=args)
except subprocess.CalledProcessError as e:
logging.error('Subprocess error caught %s', e.output.decode())
raise RuntimeError('Webview installation failed.')
version_regex = r'\s*Preferred WebView package[^:]*[^\d]*([^\)]+)'
version_output = device.RunShellCommand(['dumpsys' ,'webviewupdate'])
version = [
m.group(1)
for line in version_output if (m := re.match(version_regex, line))
]
return packaging.version.parse(version[0]) if version else None
def _forward_port(device: device_utils.DeviceUtils,
ports: Optional[List[int]] = None):
# Ideally, we would dynamically allocate ports from the device, and
# remember the mapping here, it requires how the client redirects ports.
# Since we currently only allocate ports from a user space whose value is
# always 3xxxx and above, there is a very rare case to cause issues here.
# It is possible that the port is already used on the device, however,
# the likelihood is small, and we will fix once it shows a problem.
if ports:
forwarder.Forwarder.Map([(port, port) for port in ports], device)
def launch_emulator(avd_config: str,
emulator_window: bool,
ports: Optional[List[int]] = None) -> avd._AvdInstance:
"""Launches the emulator and forwards ports from device to host."""
avd_config = avd.AvdConfig(avd_config)
avd_config.Install()
instance = avd_config.CreateInstance()
instance.Start(writable_system=True,
window=emulator_window)
_forward_port(instance.device, ports)
return instance
|