| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """Provides an interface to start and stop Android emulator. | 7 """Provides an interface to start and stop Android emulator. |
| 8 | 8 |
| 9 Assumes system environment ANDROID_NDK_ROOT has been set. | 9 Assumes system environment ANDROID_NDK_ROOT has been set. |
| 10 | 10 |
| 11 Emulator: The class provides the methods to launch/shutdown the emulator with | 11 Emulator: The class provides the methods to launch/shutdown the emulator with |
| 12 the android virtual device named 'avd_armeabi' . | 12 the android virtual device named 'avd_armeabi' . |
| 13 """ | 13 """ |
| 14 | 14 |
| 15 import logging | 15 import logging |
| 16 import os | 16 import os |
| 17 import signal | 17 import signal |
| 18 import subprocess | 18 import subprocess |
| 19 import sys | 19 import sys |
| 20 import time | 20 import time |
| 21 | 21 |
| 22 import time_profile | 22 import time_profile |
| 23 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. | 23 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. |
| 24 from pylib import android_commands | 24 from pylib import android_commands |
| 25 from pylib import cmd_helper | 25 from pylib import cmd_helper |
| 26 from pylib import constants |
| 26 | 27 |
| 27 # adb_interface.py is under ../../third_party/android_testrunner/ | |
| 28 sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..', | |
| 29 '..', 'third_party', 'android_testrunner')) | |
| 30 import adb_interface | |
| 31 import errors | 28 import errors |
| 32 import run_command | 29 import run_command |
| 33 | 30 |
| 31 # Android API level |
| 32 API_TARGET = 'android-%s' % constants.ANDROID_SDK_VERSION |
| 33 |
| 34 |
| 34 class EmulatorLaunchException(Exception): | 35 class EmulatorLaunchException(Exception): |
| 35 """Emulator failed to launch.""" | 36 """Emulator failed to launch.""" |
| 36 pass | 37 pass |
| 37 | 38 |
| 38 def _KillAllEmulators(): | 39 def _KillAllEmulators(): |
| 39 """Kill all running emulators that look like ones we started. | 40 """Kill all running emulators that look like ones we started. |
| 40 | 41 |
| 41 There are odd 'sticky' cases where there can be no emulator process | 42 There are odd 'sticky' cases where there can be no emulator process |
| 42 running but a device slot is taken. A little bot trouble and and | 43 running but a device slot is taken. A little bot trouble and and |
| 43 we're out of room forever. | 44 we're out of room forever. |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 """Returns an available TCP port for the console.""" | 95 """Returns an available TCP port for the console.""" |
| 95 used_ports = [] | 96 used_ports = [] |
| 96 emulators = android_commands.GetEmulators() | 97 emulators = android_commands.GetEmulators() |
| 97 for emulator in emulators: | 98 for emulator in emulators: |
| 98 used_ports.append(emulator.split('-')[1]) | 99 used_ports.append(emulator.split('-')[1]) |
| 99 for port in PortPool.port_range(): | 100 for port in PortPool.port_range(): |
| 100 if str(port) not in used_ports: | 101 if str(port) not in used_ports: |
| 101 return port | 102 return port |
| 102 | 103 |
| 103 | 104 |
| 104 def LaunchEmulators(emulator_count, wait_for_boot=True): | 105 def LaunchEmulators(android_sdk_root, emulator_count, abi, wait_for_boot=True): |
| 105 """Launch multiple emulators and wait for them to boot. | 106 """Launch multiple emulators and wait for them to boot. |
| 106 | 107 |
| 107 Args: | 108 Args: |
| 109 android_sdk_root: location of SDK with the system images installed |
| 108 emulator_count: number of emulators to launch. | 110 emulator_count: number of emulators to launch. |
| 111 abi: the emulator target platform |
| 112 wait_for_boot: whether or not to wait for emulators to boot up |
| 109 | 113 |
| 110 Returns: | 114 Returns: |
| 111 List of emulators. | 115 List of emulators. |
| 112 """ | 116 """ |
| 113 emulators = [] | 117 emulators = [] |
| 118 print 'emulator_count: %s' % emulator_count |
| 114 for n in xrange(emulator_count): | 119 for n in xrange(emulator_count): |
| 115 t = time_profile.TimeProfile('Emulator launch %d' % n) | 120 t = time_profile.TimeProfile('Emulator launch %d' % n) |
| 116 avd_name = None | 121 # Creates a temporary AVD. |
| 117 if n > 0: | 122 avd_name = 'run_tests_avd_%d' % n |
| 118 # Creates a temporary AVD for the extra emulators. | |
| 119 avd_name = 'run_tests_avd_%d' % n | |
| 120 logging.info('Emulator launch %d with avd_name=%s', n, avd_name) | 123 logging.info('Emulator launch %d with avd_name=%s', n, avd_name) |
| 121 emulator = Emulator(avd_name) | 124 emulator = Emulator(android_sdk_root, avd_name, abi) |
| 122 emulator.Launch(kill_all_emulators=n == 0) | 125 emulator.Launch(kill_all_emulators=n == 0) |
| 123 t.Stop() | 126 t.Stop() |
| 124 emulators.append(emulator) | 127 emulators.append(emulator) |
| 125 # Wait for all emulators to boot completed. | 128 # Wait for all emulators to boot completed. |
| 126 if wait_for_boot: | 129 if wait_for_boot: |
| 127 for emulator in emulators: | 130 for emulator in emulators: |
| 128 emulator.ConfirmLaunch(True) | 131 emulator.ConfirmLaunch(True) |
| 129 return emulators | 132 return emulators |
| 130 | 133 |
| 131 | 134 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 152 # the time to launch the emulator and a wait-for-device command. | 155 # the time to launch the emulator and a wait-for-device command. |
| 153 _LAUNCH_TIMEOUT = 120 | 156 _LAUNCH_TIMEOUT = 120 |
| 154 | 157 |
| 155 # Timeout interval of wait-for-device command before bouncing to a a | 158 # Timeout interval of wait-for-device command before bouncing to a a |
| 156 # process life check. | 159 # process life check. |
| 157 _WAITFORDEVICE_TIMEOUT = 5 | 160 _WAITFORDEVICE_TIMEOUT = 5 |
| 158 | 161 |
| 159 # Time to wait for a "wait for boot complete" (property set on device). | 162 # Time to wait for a "wait for boot complete" (property set on device). |
| 160 _WAITFORBOOT_TIMEOUT = 300 | 163 _WAITFORBOOT_TIMEOUT = 300 |
| 161 | 164 |
| 162 def __init__(self, new_avd_name): | 165 def __init__(self, android_sdk_root, avd_name, abi='x86'): |
| 163 """Init an Emulator. | 166 """Init an Emulator. |
| 164 | 167 |
| 165 Args: | 168 Args: |
| 166 nwe_avd_name: If set, will create a new temporary AVD. | 169 android_sdk_root: location of SDK with the system images installed |
| 170 avd_name: name of the AVD to create |
| 171 abi: target platform for emulator being created |
| 167 """ | 172 """ |
| 168 try: | |
| 169 android_sdk_root = os.environ['ANDROID_SDK_ROOT'] | |
| 170 except KeyError: | |
| 171 logging.critical('The ANDROID_SDK_ROOT must be set to run the test on ' | |
| 172 'emulator.') | |
| 173 raise | |
| 174 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') | 173 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') |
| 175 self.android = os.path.join(android_sdk_root, 'tools', 'android') | 174 self.android = os.path.join(android_sdk_root, 'tools', 'android') |
| 176 self.popen = None | 175 self.popen = None |
| 177 self.device = None | 176 self.device = None |
| 178 self.default_avd = True | 177 self.abi = abi |
| 179 self.abi = 'armeabi-v7a' | 178 self.avd_name = avd_name |
| 180 self.avd = 'avd_armeabi' | 179 self._CreateAVD() |
| 181 if 'x86' in os.environ.get('TARGET_PRODUCT', ''): | |
| 182 self.abi = 'x86' | |
| 183 self.avd = 'avd_x86' | |
| 184 if new_avd_name: | |
| 185 self.default_avd = False | |
| 186 self.avd = self._CreateAVD(new_avd_name) | |
| 187 | 180 |
| 188 def _DeviceName(self): | 181 def _DeviceName(self): |
| 189 """Return our device name.""" | 182 """Return our device name.""" |
| 190 port = _GetAvailablePort() | 183 port = _GetAvailablePort() |
| 191 return ('emulator-%d' % port, port) | 184 return ('emulator-%d' % port, port) |
| 192 | 185 |
| 193 def _CreateAVD(self, avd_name): | 186 def _CreateAVD(self): |
| 194 """Creates an AVD with the given name. | 187 """Creates an AVD with the given name. |
| 195 | 188 |
| 196 Return avd_name. | 189 Return avd_name. |
| 197 """ | 190 """ |
| 191 print 'self.android: ' + self.android |
| 198 avd_command = [ | 192 avd_command = [ |
| 199 self.android, | 193 self.android, |
| 200 '--silent', | 194 '--silent', |
| 201 'create', 'avd', | 195 'create', 'avd', |
| 202 '--name', avd_name, | 196 '--name', self.avd_name, |
| 203 '--abi', self.abi, | 197 '--abi', self.abi, |
| 204 '--target', 'android-16', | 198 '--target', API_TARGET, |
| 205 '-c', '128M', | 199 '-c', '128M', |
| 206 '--force', | 200 '--force', |
| 207 ] | 201 ] |
| 208 avd_process = subprocess.Popen(args=avd_command, | 202 avd_process = subprocess.Popen(args=avd_command, |
| 209 stdin=subprocess.PIPE, | 203 stdin=subprocess.PIPE, |
| 210 stdout=subprocess.PIPE, | 204 stdout=subprocess.PIPE, |
| 211 stderr=subprocess.STDOUT) | 205 stderr=subprocess.STDOUT) |
| 212 avd_process.stdin.write('no\n') | 206 avd_process.stdin.write('no\n') |
| 213 avd_process.wait() | 207 avd_process.wait() |
| 214 logging.info('Create AVD command: %s', ' '.join(avd_command)) | 208 logging.info('Create AVD command: %s', ' '.join(avd_command)) |
| 215 return avd_name | 209 return self.avd_name |
| 216 | 210 |
| 217 def _DeleteAVD(self): | 211 def _DeleteAVD(self): |
| 218 """Delete the AVD of this emulator.""" | 212 """Delete the AVD of this emulator.""" |
| 219 avd_command = [ | 213 avd_command = [ |
| 220 self.android, | 214 self.android, |
| 221 '--silent', | 215 '--silent', |
| 222 'delete', | 216 'delete', |
| 223 'avd', | 217 'avd', |
| 224 '--name', self.avd, | 218 '--name', self.avd, |
| 225 ] | 219 ] |
| (...skipping 13 matching lines...) Expand all Loading... |
| 239 _KillAllEmulators() # just to be sure | 233 _KillAllEmulators() # just to be sure |
| 240 self._AggressiveImageCleanup() | 234 self._AggressiveImageCleanup() |
| 241 (self.device, port) = self._DeviceName() | 235 (self.device, port) = self._DeviceName() |
| 242 emulator_command = [ | 236 emulator_command = [ |
| 243 self.emulator, | 237 self.emulator, |
| 244 # Speed up emulator launch by 40%. Really. | 238 # Speed up emulator launch by 40%. Really. |
| 245 '-no-boot-anim', | 239 '-no-boot-anim', |
| 246 # The default /data size is 64M. | 240 # The default /data size is 64M. |
| 247 # That's not enough for 8 unit test bundles and their data. | 241 # That's not enough for 8 unit test bundles and their data. |
| 248 '-partition-size', '512', | 242 '-partition-size', '512', |
| 243 # Use a familiar name and port. |
| 244 '-avd', self.avd_name, |
| 245 '-port', str(port), |
| 246 # Wipe the data. We've seen cases where an emulator gets 'stuck' if we |
| 247 # don't do this (every thousand runs or so). |
| 248 '-wipe-data', |
| 249 # Enable GPU by default. | 249 # Enable GPU by default. |
| 250 '-gpu', 'on', | 250 '-gpu', 'on', |
| 251 # Use a familiar name and port. | 251 '-qemu', '-m', '1024', |
| 252 '-avd', self.avd, | 252 ] |
| 253 '-port', str(port)] | 253 print 'self.abi = %s' % self.abi |
| 254 emulator_command.extend([ | 254 if self.abi == 'x86': |
| 255 # Wipe the data. We've seen cases where an emulator | 255 emulator_command.extend([ |
| 256 # gets 'stuck' if we don't do this (every thousand runs or | 256 # For x86 emulator --enable-kvm will fail early, avoiding accidental |
| 257 # so). | 257 # runs in a slow mode (i.e. without hardware virtualization support). |
| 258 '-wipe-data', | 258 '--enable-kvm', |
| 259 ]) | 259 ]) |
| 260 |
| 260 logging.info('Emulator launch command: %s', ' '.join(emulator_command)) | 261 logging.info('Emulator launch command: %s', ' '.join(emulator_command)) |
| 261 self.popen = subprocess.Popen(args=emulator_command, | 262 self.popen = subprocess.Popen(args=emulator_command, |
| 262 stderr=subprocess.STDOUT) | 263 stderr=subprocess.STDOUT) |
| 263 self._InstallKillHandler() | 264 self._InstallKillHandler() |
| 264 | 265 |
| 265 def _AggressiveImageCleanup(self): | 266 def _AggressiveImageCleanup(self): |
| 266 """Aggressive cleanup of emulator images. | 267 """Aggressive cleanup of emulator images. |
| 267 | 268 |
| 268 Experimentally it looks like our current emulator use on the bot | 269 Experimentally it looks like our current emulator use on the bot |
| 269 leaves image files around in /tmp/android-$USER. If a "random" | 270 leaves image files around in /tmp/android-$USER. If a "random" |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 310 raise EmulatorLaunchException('TIMEOUT with wait-for-device') | 311 raise EmulatorLaunchException('TIMEOUT with wait-for-device') |
| 311 logging.info('Seconds waited on wait-for-device: %d', seconds_waited) | 312 logging.info('Seconds waited on wait-for-device: %d', seconds_waited) |
| 312 if wait_for_boot: | 313 if wait_for_boot: |
| 313 # Now that we checked for obvious problems, wait for a boot complete. | 314 # Now that we checked for obvious problems, wait for a boot complete. |
| 314 # Waiting for the package manager is sometimes problematic. | 315 # Waiting for the package manager is sometimes problematic. |
| 315 a = android_commands.AndroidCommands(self.device) | 316 a = android_commands.AndroidCommands(self.device) |
| 316 a.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT) | 317 a.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT) |
| 317 | 318 |
| 318 def Shutdown(self): | 319 def Shutdown(self): |
| 319 """Shuts down the process started by launch.""" | 320 """Shuts down the process started by launch.""" |
| 320 if not self.default_avd: | 321 self._DeleteAVD() |
| 321 self._DeleteAVD() | |
| 322 if self.popen: | 322 if self.popen: |
| 323 self.popen.poll() | 323 self.popen.poll() |
| 324 if self.popen.returncode == None: | 324 if self.popen.returncode == None: |
| 325 self.popen.kill() | 325 self.popen.kill() |
| 326 self.popen = None | 326 self.popen = None |
| 327 | 327 |
| 328 def _ShutdownOnSignal(self, signum, frame): | 328 def _ShutdownOnSignal(self, signum, frame): |
| 329 logging.critical('emulator _ShutdownOnSignal') | 329 logging.critical('emulator _ShutdownOnSignal') |
| 330 for sig in self._SIGNALS: | 330 for sig in self._SIGNALS: |
| 331 signal.signal(sig, signal.SIG_DFL) | 331 signal.signal(sig, signal.SIG_DFL) |
| 332 self.Shutdown() | 332 self.Shutdown() |
| 333 raise KeyboardInterrupt # print a stack | 333 raise KeyboardInterrupt # print a stack |
| 334 | 334 |
| 335 def _InstallKillHandler(self): | 335 def _InstallKillHandler(self): |
| 336 """Install a handler to kill the emulator when we exit unexpectedly.""" | 336 """Install a handler to kill the emulator when we exit unexpectedly.""" |
| 337 for sig in self._SIGNALS: | 337 for sig in self._SIGNALS: |
| 338 signal.signal(sig, self._ShutdownOnSignal) | 338 signal.signal(sig, self._ShutdownOnSignal) |
| OLD | NEW |