diff options
author | navabi@google.com <navabi@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-28 00:23:22 +0000 |
---|---|---|
committer | navabi@google.com <navabi@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-28 00:23:22 +0000 |
commit | 8afcc5a5e98a4c944260dfe3cfbef5a36321466e (patch) | |
tree | 62fefab8e635cd64194cd6387bb23bb06cc5db4d | |
parent | b1ba95542aefdb07d07df44f7af1c3ab99fc5301 (diff) | |
download | chromium_src-8afcc5a5e98a4c944260dfe3cfbef5a36321466e.zip chromium_src-8afcc5a5e98a4c944260dfe3cfbef5a36321466e.tar.gz chromium_src-8afcc5a5e98a4c944260dfe3cfbef5a36321466e.tar.bz2 |
Add script to download SDK, system images and create and start AVDs for testing.
BUG=164911
Review URL: https://chromiumcodereview.appspot.com/12407004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@191075 0039d316-1c4b-4281-b951-d872f2087c98
-rwxr-xr-x | build/android/avd.py | 65 | ||||
-rwxr-xr-x | build/android/emulator.py | 32 | ||||
-rwxr-xr-x | build/android/install_emulator_deps.py | 147 | ||||
-rw-r--r-- | build/android/pylib/constants.py | 2 | ||||
-rwxr-xr-x | build/android/pylib/utils/emulator.py | 81 |
5 files changed, 253 insertions, 74 deletions
diff --git a/build/android/avd.py b/build/android/avd.py new file mode 100755 index 0000000..c9da30f --- /dev/null +++ b/build/android/avd.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Launches Android Virtual Devices with a set configuration for testing Chrome. + +The script will launch a specified number of Android Virtual Devices (AVD's). +""" + + +import install_emulator_deps +import logging +import optparse +import os +import subprocess +import sys + +from pylib import constants +from pylib.utils import emulator + + +def main(argv): + # ANDROID_SDK_ROOT needs to be set to the location of the SDK used to launch + # the emulator to find the system images upon launch. + emulator_sdk = os.path.join(constants.EMULATOR_SDK_ROOT, 'android_tools', + 'sdk') + os.environ['ANDROID_SDK_ROOT'] = emulator_sdk + + opt_parser = optparse.OptionParser(description='AVD script.') + opt_parser.add_option('-n', '--num', dest='emulator_count', + help='Number of emulators to launch.', + type='int', default='1') + opt_parser.add_option('--abi', default='arm', + help='Platform of emulators to launch.') + + options, _ = opt_parser.parse_args(argv[1:]) + if options.abi == 'arm': + options.abi = 'armeabi-v7a' + + logging.basicConfig(level=logging.INFO, + format='# %(asctime)-15s: %(message)s') + logging.root.setLevel(logging.INFO) + + # Check if KVM is enabled for x86 AVD's and check for x86 system images. + if options.abi =='x86': + if not install_emulator_deps.CheckKVM(): + logging.critical('ERROR: KVM must be enabled in BIOS, and installed. Run ' + 'install_emulator_deps.py') + return 1 + elif not install_emulator_deps.CheckX86Image(): + logging.critical('ERROR: System image for x86 AVD not installed. Run ' + 'install_emulator_deps.py') + return 1 + + if not install_emulator_deps.CheckSDK(): + logging.critical('ERROR: Emulator SDK not installed. Run ' + 'install_emulator_deps.py.') + return 1 + + emulator.LaunchEmulators(options.emulator_count, options.abi, True) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/build/android/emulator.py b/build/android/emulator.py deleted file mode 100755 index 1c4fb55..0000000 --- a/build/android/emulator.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2012 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Script to launch Android emulators. - -Assumes system environment ANDROID_NDK_ROOT has been set. -""" - -import optparse -import sys - -from pylib.utils import emulator - - -def main(argv): - option_parser = optparse.OptionParser() - option_parser.add_option('-n', '--num', dest='emulator_count', - help='Number of emulators to launch.', - type='int', - default=1) - option_parser.add_option('-w', '--wait', dest='wait_for_boot', - action='store_true', - help='If set, wait for the emulators to boot.') - options, args = option_parser.parse_args(argv) - emulator.LaunchEmulators(options.emulator_count, options.wait_for_boot) - - -if __name__ == '__main__': - main(sys.argv) diff --git a/build/android/install_emulator_deps.py b/build/android/install_emulator_deps.py new file mode 100755 index 0000000..ff84367 --- /dev/null +++ b/build/android/install_emulator_deps.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# Copyright (c) 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Installs deps for using SDK emulator for testing. + +The script will download the SDK and system images, if they are not present, and +install and enable KVM, if virtualization has been enabled in the BIOS. +""" + + +import logging +import os +import shutil +import subprocess +import sys + +from pylib import cmd_helper +from pylib import constants +from pylib.utils import run_tests_helper + +# From the Android Developer's website. +SDK_BASE_URL = 'http://dl.google.com/android/adt' +SDK_ZIP = 'adt-bundle-linux-x86_64-20130219.zip' + +# Android x86 system image from the Intel website: +# http://software.intel.com/en-us/articles/intel-eula-x86-android-4-2-jelly-bean-bin +X86_IMG_URL = 'http://download-software.intel.com/sites/landingpage/android/sysimg_x86-17_r01.zip' + +# Android API level +API_TARGET = 'android-%s' % constants.ANDROID_SDK_VERSION + + +def CheckSDK(): + """Check if SDK is already installed. + + Returns: + True if android_tools directory exists in current directory. + """ + return os.path.exists(os.path.join(constants.EMULATOR_SDK_ROOT, + 'android_tools')) + + +def CheckX86Image(): + """Check if Android system images have been installed. + + Returns: + True if android_tools/sdk/system-images directory exists. + """ + return os.path.exists(os.path.join(constants.EMULATOR_SDK_ROOT, + 'android_tools', 'sdk', 'system-images', + API_TARGET, 'x86')) + + +def CheckKVM(): + """Check if KVM is enabled. + + Returns: + True if kvm-ok returns 0 (already enabled) + """ + rc = cmd_helper.RunCmd(['kvm-ok']) + return not rc + + +def GetSDK(): + """Download the SDK and unzip in android_tools directory.""" + logging.info('Download Android SDK.') + sdk_url = '%s/%s' % (SDK_BASE_URL, SDK_ZIP) + try: + cmd_helper.RunCmd(['curl', '-o', '/tmp/sdk.zip', sdk_url]) + print 'curled unzipping...' + rc = cmd_helper.RunCmd(['unzip', '-o', '/tmp/sdk.zip', '-d', '/tmp/']) + if rc: + logging.critical('ERROR: could not download/unzip Android SDK.') + raise + # Get the name of the sub-directory that everything will be extracted to. + dirname, _ = os.path.splitext(SDK_ZIP) + zip_dir = '/tmp/%s' % dirname + # Move the extracted directory to EMULATOR_SDK_ROOT + dst = os.path.join(constants.EMULATOR_SDK_ROOT, 'android_tools') + shutil.move(zip_dir, dst) + finally: + os.unlink('/tmp/sdk.zip') + + +def InstallKVM(): + """Installs KVM packages.""" + rc = cmd_helper.RunCmd(['sudo', 'apt-get', 'install', 'kvm']) + if rc: + logging.critical('ERROR: Did not install KVM. Make sure hardware ' + 'virtualization is enabled in BIOS (i.e. Intel VT-x or ' + 'AMD SVM).') + # TODO(navabi): Use modprobe kvm-amd on AMD processors. + rc = cmd_helper.RunCmd(['sudo', 'modprobe', 'kvm-intel']) + if rc: + logging.critical('ERROR: Did not add KVM module to Linux Kernal. Make sure ' + 'hardware virtualization is enabled in BIOS.') + # Now check to ensure KVM acceleration can be used. + rc = cmd_helper.RunCmd(['kvm-ok']) + if rc: + logging.critical('ERROR: Can not use KVM acceleration. Make sure hardware ' + 'virtualization is enabled in BIOS (i.e. Intel VT-x or ' + 'AMD SVM).') + + +def GetX86Image(): + """Download x86 system image from Intel's website.""" + logging.info('Download x86 system image directory into sdk directory.') + try: + cmd_helper.RunCmd(['curl', '-o', '/tmp/x86_img.zip', X86_IMG_URL]) + rc = cmd_helper.RunCmd(['unzip', '-o', '/tmp/x86_img.zip', '-d', '/tmp/']) + if rc: + logging.critical('ERROR: Could not download/unzip image zip.') + raise + sys_imgs = os.path.join(constants.EMULATOR_SDK_ROOT, 'android_tools', 'sdk', + 'system-images', API_TARGET, 'x86') + shutil.move('/tmp/x86', sys_imgs) + finally: + os.unlink('/tmp/x86_img.zip') + + +def main(argv): + logging.basicConfig(level=logging.INFO, + format='# %(asctime)-15s: %(message)s') + run_tests_helper.SetLogLevel(verbose_count=1) + + # Calls below will download emulator SDK and/or system images only if needed. + if CheckSDK(): + logging.info('android_tools directory already exists (not downloading).') + else: + GetSDK() + + if CheckX86Image(): + logging.info('system-images directory already exists.') + else: + GetX86Image() + + # Make sure KVM packages are installed and enabled. + if CheckKVM(): + logging.info('KVM already installed and enabled.') + else: + InstallKVM() + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py index 7e001c9..4b2413e 100644 --- a/build/android/pylib/constants.py +++ b/build/android/pylib/constants.py @@ -9,6 +9,8 @@ import os CHROME_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)) +EMULATOR_SDK_ROOT = os.path.abspath(os.path.join(CHROME_DIR, os.pardir, + os.pardir)) CHROME_PACKAGE = 'com.google.android.apps.chrome' CHROME_ACTIVITY = 'com.google.android.apps.chrome.Main' diff --git a/build/android/pylib/utils/emulator.py b/build/android/pylib/utils/emulator.py index 70afffb..df77c52 100755 --- a/build/android/pylib/utils/emulator.py +++ b/build/android/pylib/utils/emulator.py @@ -23,14 +23,15 @@ import time_profile # TODO(craigdh): Move these pylib dependencies to pylib/utils/. from pylib import android_commands from pylib import cmd_helper +from pylib import constants -# adb_interface.py is under ../../third_party/android_testrunner/ -sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', - '..', 'third_party', 'android_testrunner')) -import adb_interface import errors import run_command +# Android API level +API_TARGET = 'android-%s' % constants.ANDROID_SDK_VERSION + + class EmulatorLaunchException(Exception): """Emulator failed to launch.""" pass @@ -101,11 +102,13 @@ def _GetAvailablePort(): return port -def LaunchEmulators(emulator_count, wait_for_boot=True): +def LaunchEmulators(emulator_count, abi, wait_for_boot=True): """Launch multiple emulators and wait for them to boot. Args: emulator_count: number of emulators to launch. + abi: the emulator target platform + wait_for_boot: whether or not to wait for emulators to boot up Returns: List of emulators. @@ -113,12 +116,10 @@ def LaunchEmulators(emulator_count, wait_for_boot=True): emulators = [] for n in xrange(emulator_count): t = time_profile.TimeProfile('Emulator launch %d' % n) - avd_name = None - if n > 0: - # Creates a temporary AVD for the extra emulators. - avd_name = 'run_tests_avd_%d' % n + # Creates a temporary AVD. + avd_name = 'run_tests_avd_%d' % n logging.info('Emulator launch %d with avd_name=%s', n, avd_name) - emulator = Emulator(avd_name) + emulator = Emulator(avd_name, abi) emulator.Launch(kill_all_emulators=n == 0) t.Stop() emulators.append(emulator) @@ -159,38 +160,29 @@ class Emulator(object): # Time to wait for a "wait for boot complete" (property set on device). _WAITFORBOOT_TIMEOUT = 300 - def __init__(self, new_avd_name): + def __init__(self, avd_name, abi='x86'): """Init an Emulator. Args: - nwe_avd_name: If set, will create a new temporary AVD. + avd_name: name of the AVD to create + abi: target platform for emulator being created """ - try: - android_sdk_root = os.environ['ANDROID_SDK_ROOT'] - except KeyError: - logging.critical('The ANDROID_SDK_ROOT must be set to run the test on ' - 'emulator.') - raise + android_sdk_root = os.path.join(constants.EMULATOR_SDK_ROOT, + 'android_tools', 'sdk') self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') self.android = os.path.join(android_sdk_root, 'tools', 'android') self.popen = None self.device = None - self.default_avd = True - self.abi = 'armeabi-v7a' - self.avd = 'avd_armeabi' - if 'x86' in os.environ.get('TARGET_PRODUCT', ''): - self.abi = 'x86' - self.avd = 'avd_x86' - if new_avd_name: - self.default_avd = False - self.avd = self._CreateAVD(new_avd_name) + self.abi = abi + self.avd_name = avd_name + self._CreateAVD() def _DeviceName(self): """Return our device name.""" port = _GetAvailablePort() return ('emulator-%d' % port, port) - def _CreateAVD(self, avd_name): + def _CreateAVD(self): """Creates an AVD with the given name. Return avd_name. @@ -199,9 +191,9 @@ class Emulator(object): self.android, '--silent', 'create', 'avd', - '--name', avd_name, + '--name', self.avd_name, '--abi', self.abi, - '--target', 'android-16', + '--target', API_TARGET, '-c', '128M', '--force', ] @@ -212,7 +204,7 @@ class Emulator(object): avd_process.stdin.write('no\n') avd_process.wait() logging.info('Create AVD command: %s', ' '.join(avd_command)) - return avd_name + return self.avd_name def _DeleteAVD(self): """Delete the AVD of this emulator.""" @@ -246,17 +238,23 @@ class Emulator(object): # The default /data size is 64M. # That's not enough for 8 unit test bundles and their data. '-partition-size', '512', - # Enable GPU by default. - '-gpu', 'on', # Use a familiar name and port. - '-avd', self.avd, - '-port', str(port)] - emulator_command.extend([ - # Wipe the data. We've seen cases where an emulator - # gets 'stuck' if we don't do this (every thousand runs or - # so). + '-avd', self.avd_name, + '-port', str(port), + # Wipe the data. We've seen cases where an emulator gets 'stuck' if we + # don't do this (every thousand runs or so). '-wipe-data', - ]) + # Enable GPU by default. + '-gpu', 'on', + '-qemu', '-m', '1024', + ] + if self.abi == 'x86': + emulator_command.extend([ + # For x86 emulator --enable-kvm will fail early, avoiding accidental + # runs in a slow mode (i.e. without hardware virtualization support). + '--enable-kvm', + ]) + logging.info('Emulator launch command: %s', ' '.join(emulator_command)) self.popen = subprocess.Popen(args=emulator_command, stderr=subprocess.STDOUT) @@ -317,8 +315,7 @@ class Emulator(object): def Shutdown(self): """Shuts down the process started by launch.""" - if not self.default_avd: - self._DeleteAVD() + self._DeleteAVD() if self.popen: self.popen.poll() if self.popen.returncode == None: |