Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(124)

Side by Side Diff: build/android/pylib/device/device_utils.py

Issue 659533002: New run shell implementation for DeviceUtils (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Provides a variety of device interactions based on adb. 5 """Provides a variety of device interactions based on adb.
6 6
7 Eventually, this will be based on adb_wrapper. 7 Eventually, this will be based on adb_wrapper.
8 """ 8 """
9 # pylint: disable=W0613 9 # pylint: disable=W0613
10 10
11 import multiprocessing 11 import multiprocessing
12 import os 12 import os
13 import pipes 13 import pipes
14 import re
14 import sys 15 import sys
15 import tempfile 16 import tempfile
16 import time 17 import time
17 import zipfile 18 import zipfile
18 19
19 import pylib.android_commands 20 import pylib.android_commands
20 from pylib.device import adb_wrapper 21 from pylib.device import adb_wrapper
21 from pylib.device import decorators 22 from pylib.device import decorators
22 from pylib.device import device_errors 23 from pylib.device import device_errors
23 from pylib.device.commands import install_commands 24 from pylib.device.commands import install_commands
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 self.adb = adb_wrapper.AdbWrapper(device.GetDevice()) 80 self.adb = adb_wrapper.AdbWrapper(device.GetDevice())
80 self.old_interface = device 81 self.old_interface = device
81 elif not device: 82 elif not device:
82 self.adb = adb_wrapper.AdbWrapper('') 83 self.adb = adb_wrapper.AdbWrapper('')
83 self.old_interface = pylib.android_commands.AndroidCommands() 84 self.old_interface = pylib.android_commands.AndroidCommands()
84 else: 85 else:
85 raise ValueError('Unsupported type passed for argument "device"') 86 raise ValueError('Unsupported type passed for argument "device"')
86 self._commands_installed = False 87 self._commands_installed = False
87 self._default_timeout = default_timeout 88 self._default_timeout = default_timeout
88 self._default_retries = default_retries 89 self._default_retries = default_retries
90 self._cache = {}
89 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR)) 91 assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR))
90 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR)) 92 assert(hasattr(self, decorators.DEFAULT_RETRIES_ATTR))
91 93
92 @decorators.WithTimeoutAndRetriesFromInstance() 94 @decorators.WithTimeoutAndRetriesFromInstance()
93 def IsOnline(self, timeout=None, retries=None): 95 def IsOnline(self, timeout=None, retries=None):
94 """Checks whether the device is online. 96 """Checks whether the device is online.
95 97
96 Args: 98 Args:
97 timeout: timeout in seconds 99 timeout: timeout in seconds
98 retries: number of retries 100 retries: number of retries
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 retries: number of retries 139 retries: number of retries
138 140
139 Raises: 141 Raises:
140 CommandFailedError if root could not be enabled. 142 CommandFailedError if root could not be enabled.
141 CommandTimeoutError on timeout. 143 CommandTimeoutError on timeout.
142 """ 144 """
143 if not self.old_interface.EnableAdbRoot(): 145 if not self.old_interface.EnableAdbRoot():
144 raise device_errors.CommandFailedError( 146 raise device_errors.CommandFailedError(
145 'Could not enable root.', device=str(self)) 147 'Could not enable root.', device=str(self))
146 148
147 @decorators.WithTimeoutAndRetriesFromInstance() 149 def GetExternalStoragePath(self, timeout=_DEFAULT_TIMEOUT,
jbudorick 2014/10/14 16:55:19 I'm not crazy about moving the timeout/retry bound
148 def GetExternalStoragePath(self, timeout=None, retries=None): 150 retries=_DEFAULT_RETRIES):
149 """Get the device's path to its SD card. 151 """Get the device's path to its SD card.
150 152
151 Args: 153 Args:
152 timeout: timeout in seconds 154 timeout: timeout in seconds
153 retries: number of retries 155 retries: number of retries
154 156
155 Returns: 157 Returns:
156 The device's path to its SD card. 158 The device's path to its SD card.
157 159
158 Raises: 160 Raises:
159 CommandFailedError if the external storage path could not be determined. 161 CommandFailedError if the external storage path could not be determined.
160 CommandTimeoutError on timeout. 162 CommandTimeoutError on timeout.
161 DeviceUnreachableError on missing device. 163 DeviceUnreachableError on missing device.
162 """ 164 """
163 return self._GetExternalStoragePathImpl() 165 if 'external_storage' in self._cache:
166 return self._cache['external_storage']
164 167
165 def _GetExternalStoragePathImpl(self): 168 value = self._NewRunShellImpl('echo $EXTERNAL_STORAGE', timeout=timeout,
166 try: 169 retries=retries).rstrip()
167 return self.old_interface.GetExternalStorage() 170 if not value:
jbudorick 2014/10/14 16:55:19 I'm not sure that this _has_ to be an error (altho
perezju 2014/10/15 09:22:54 This is what the current implementation does, so n
168 except AssertionError as e: 171 raise device_errors.CommandFailedError('$EXTERNAL_STORAGE is not set',
169 raise device_errors.CommandFailedError( 172 str(self))
170 str(e), device=str(self)), None, sys.exc_info()[2] 173 self._cache['external_storage'] = value
jbudorick 2014/10/14 16:55:19 I like the idea of caching things like this and ce
perezju 2014/10/15 09:22:54 Acknowledged.
174 return value
171 175
172 @decorators.WithTimeoutAndRetriesFromInstance() 176 @decorators.WithTimeoutAndRetriesFromInstance()
173 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None): 177 def WaitUntilFullyBooted(self, wifi=False, timeout=None, retries=None):
174 """Wait for the device to fully boot. 178 """Wait for the device to fully boot.
175 179
176 This means waiting for the device to boot, the package manager to be 180 This means waiting for the device to boot, the package manager to be
177 available, and the SD card to be ready. It can optionally mean waiting 181 available, and the SD card to be ready. It can optionally mean waiting
178 for wifi to come up, too. 182 for wifi to come up, too.
179 183
180 Args: 184 Args:
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 if check_return: 323 if check_return:
320 code, output = self.old_interface.GetShellCommandStatusAndOutput( 324 code, output = self.old_interface.GetShellCommandStatusAndOutput(
321 cmd, timeout_time=timeout) 325 cmd, timeout_time=timeout)
322 if int(code) != 0: 326 if int(code) != 0:
323 raise device_errors.AdbCommandFailedError( 327 raise device_errors.AdbCommandFailedError(
324 cmd.split(), 'Nonzero exit code (%d)' % code, device=str(self)) 328 cmd.split(), 'Nonzero exit code (%d)' % code, device=str(self))
325 else: 329 else:
326 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout) 330 output = self.old_interface.RunShellCommand(cmd, timeout_time=timeout)
327 return output 331 return output
328 332
333 def _NewRunShellImpl(self, cmd, cwd=None, env=None, as_root=False,
jbudorick 2014/10/14 16:55:19 I'm not sure about eliminating check_return here.
perezju 2014/10/15 09:22:54 I would argue that we shouldn't make it easy for c
334 timeout=_DEFAULT_TIMEOUT, retries=_DEFAULT_RETRIES):
335 """Proposed new interface to run ADB shell commands.
336
337 When the command to run |cmd| is given as a string, it will be interpreted
338 and run by the shell on the device.
339
340 Alternatively, one can supply |cmd| as a sequence containing the name of
341 the command to run followed by its arguments. In this case, arguments are
342 passed to the command exactly as given, without any further processing by
343 the shell. This allows to easily pass arguments containing spaces or
344 special characters without having to worry about getting quoting right.
345
346 Args:
347 cmd: A string with the full command to run on the device, or a sequence
348 containing the command and its arguments.
349 cwd: The device directory in which the command should be run.
350 env: The environment variables with which the command should be run.
351 as_root: A boolean indicating whether the shell command should be run
352 with root privileges.
353 timeout: timeout in seconds
354 retries: number of retries
355
356 Returns:
357 The output of the command as a single string.
358
359 Raises:
360 CommandFailedError if the exit status of the command run is non-zero.
361 CommandTimeoutError on timeout.
362 """
363 def env_quote(key, value):
364 if not re.match('^[a-zA-Z_][a-zA-Z0-9_]*$', key):
jbudorick 2014/10/14 16:55:19 Compile the regex as a constant and then use that
perezju 2014/10/15 09:22:54 Acknowledged.
365 raise KeyError('Invalid shell variable name %r' % key)
366 return '%s=%s' % (key, pipes.quote(value))
367
368 if not isinstance(cmd, basestring):
369 cmd = ' '.join(pipes.quote(s) for s in cmd)
370 if as_root and not self._HasRootImpl():
371 cmd = 'su -c %s' % cmd
372 if env is not None:
373 cmd = '%s %s' % (' '.join(env_quote(*kv) for kv in env.iteritems()), cmd)
jbudorick 2014/10/14 16:55:19 This seems a little more readable to me: env_qu
perezju 2014/10/15 09:22:54 Acknowledged.
374 if cwd is not None:
375 cmd = 'cd %s && %s' % (pipes.quote(cwd), cmd)
376 return self.adb.Shell(cmd, expect_rc=0, timeout=timeout, retries=retries)
377
329 @decorators.WithTimeoutAndRetriesFromInstance() 378 @decorators.WithTimeoutAndRetriesFromInstance()
330 def KillAll(self, process_name, signum=9, as_root=False, blocking=False, 379 def KillAll(self, process_name, signum=9, as_root=False, blocking=False,
331 timeout=None, retries=None): 380 timeout=None, retries=None):
332 """Kill all processes with the given name on the device. 381 """Kill all processes with the given name on the device.
333 382
334 Args: 383 Args:
335 process_name: A string containing the name of the process to kill. 384 process_name: A string containing the name of the process to kill.
336 signum: An integer containing the signal number to send to kill. Defaults 385 signum: An integer containing the signal number to send to kill. Defaults
337 to 9 (SIGKILL). 386 to 9 (SIGKILL).
338 as_root: A boolean indicating whether the kill should be executed with 387 as_root: A boolean indicating whether the kill should be executed with
(...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after
615 664
616 self._InstallCommands() 665 self._InstallCommands()
617 666
618 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file: 667 with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
619 zip_proc = multiprocessing.Process( 668 zip_proc = multiprocessing.Process(
620 target=DeviceUtils._CreateDeviceZip, 669 target=DeviceUtils._CreateDeviceZip,
621 args=(zip_file.name, files)) 670 args=(zip_file.name, files))
622 zip_proc.start() 671 zip_proc.start()
623 zip_proc.join() 672 zip_proc.join()
624 673
625 zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl() 674 zip_on_device = '%s/tmp.zip' % self.GetExternalStoragePath()
626 try: 675 try:
627 self.adb.Push(zip_file.name, zip_on_device) 676 self.adb.Push(zip_file.name, zip_on_device)
628 self._RunShellCommandImpl( 677 self._RunShellCommandImpl(
629 ['unzip', zip_on_device], 678 ['unzip', zip_on_device],
630 as_root=True, check_return=True, 679 as_root=True, check_return=True,
631 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR}) 680 env={'PATH': '$PATH:%s' % install_commands.BIN_DIR})
632 finally: 681 finally:
633 if zip_proc.is_alive(): 682 if zip_proc.is_alive():
634 zip_proc.terminate() 683 zip_proc.terminate()
635 if self._IsOnlineImpl(): 684 if self._IsOnlineImpl():
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after
965 A Parallelizer operating over |devices|. 1014 A Parallelizer operating over |devices|.
966 """ 1015 """
967 if not devices or len(devices) == 0: 1016 if not devices or len(devices) == 0:
968 devices = pylib.android_commands.GetAttachedDevices() 1017 devices = pylib.android_commands.GetAttachedDevices()
969 parallelizer_type = (parallelizer.Parallelizer if async 1018 parallelizer_type = (parallelizer.Parallelizer if async
970 else parallelizer.SyncParallelizer) 1019 else parallelizer.SyncParallelizer)
971 return parallelizer_type([ 1020 return parallelizer_type([
972 d if isinstance(d, DeviceUtils) else DeviceUtils(d) 1021 d if isinstance(d, DeviceUtils) else DeviceUtils(d)
973 for d in devices]) 1022 for d in devices])
974 1023
OLDNEW
« no previous file with comments | « no previous file | build/android/pylib/device/device_utils_test.py » ('j') | build/android/pylib/device/device_utils_test.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698