xref: /aosp_15_r20/art/tools/common/common.py (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*795d594fSAndroid Build Coastguard Worker#
3*795d594fSAndroid Build Coastguard Worker# Copyright (C) 2016 The Android Open Source Project
4*795d594fSAndroid Build Coastguard Worker#
5*795d594fSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
6*795d594fSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
7*795d594fSAndroid Build Coastguard Worker# You may obtain a copy of the License at
8*795d594fSAndroid Build Coastguard Worker#
9*795d594fSAndroid Build Coastguard Worker#   http://www.apache.org/licenses/LICENSE-2.0
10*795d594fSAndroid Build Coastguard Worker#
11*795d594fSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*795d594fSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
13*795d594fSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*795d594fSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
15*795d594fSAndroid Build Coastguard Worker# limitations under the License.
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker"""Module containing common logic from python testing tools."""
18*795d594fSAndroid Build Coastguard Worker
19*795d594fSAndroid Build Coastguard Workerimport abc
20*795d594fSAndroid Build Coastguard Workerimport os
21*795d594fSAndroid Build Coastguard Workerimport signal
22*795d594fSAndroid Build Coastguard Workerimport shlex
23*795d594fSAndroid Build Coastguard Workerimport shutil
24*795d594fSAndroid Build Coastguard Workerimport time
25*795d594fSAndroid Build Coastguard Worker
26*795d594fSAndroid Build Coastguard Workerfrom enum import Enum
27*795d594fSAndroid Build Coastguard Workerfrom enum import unique
28*795d594fSAndroid Build Coastguard Worker
29*795d594fSAndroid Build Coastguard Workerfrom subprocess import DEVNULL
30*795d594fSAndroid Build Coastguard Workerfrom subprocess import check_call
31*795d594fSAndroid Build Coastguard Workerfrom subprocess import PIPE
32*795d594fSAndroid Build Coastguard Workerfrom subprocess import Popen
33*795d594fSAndroid Build Coastguard Workerfrom subprocess import STDOUT
34*795d594fSAndroid Build Coastguard Workerfrom subprocess import TimeoutExpired
35*795d594fSAndroid Build Coastguard Worker
36*795d594fSAndroid Build Coastguard Workerfrom tempfile import mkdtemp
37*795d594fSAndroid Build Coastguard Workerfrom tempfile import NamedTemporaryFile
38*795d594fSAndroid Build Coastguard Worker
39*795d594fSAndroid Build Coastguard Worker# Temporary directory path on device.
40*795d594fSAndroid Build Coastguard WorkerDEVICE_TMP_PATH = '/data/local/tmp'
41*795d594fSAndroid Build Coastguard Worker
42*795d594fSAndroid Build Coastguard Worker# Architectures supported in dalvik cache.
43*795d594fSAndroid Build Coastguard WorkerDALVIK_CACHE_ARCHS = ['arm', 'arm64', 'x86', 'x86_64']
44*795d594fSAndroid Build Coastguard Worker
45*795d594fSAndroid Build Coastguard Worker
46*795d594fSAndroid Build Coastguard Worker@unique
47*795d594fSAndroid Build Coastguard Workerclass RetCode(Enum):
48*795d594fSAndroid Build Coastguard Worker  """Enum representing normalized return codes."""
49*795d594fSAndroid Build Coastguard Worker  SUCCESS = 0
50*795d594fSAndroid Build Coastguard Worker  TIMEOUT = 1
51*795d594fSAndroid Build Coastguard Worker  ERROR = 2
52*795d594fSAndroid Build Coastguard Worker  NOTCOMPILED = 3
53*795d594fSAndroid Build Coastguard Worker  NOTRUN = 4
54*795d594fSAndroid Build Coastguard Worker
55*795d594fSAndroid Build Coastguard Worker
56*795d594fSAndroid Build Coastguard Worker@unique
57*795d594fSAndroid Build Coastguard Workerclass LogSeverity(Enum):
58*795d594fSAndroid Build Coastguard Worker  VERBOSE = 0
59*795d594fSAndroid Build Coastguard Worker  DEBUG = 1
60*795d594fSAndroid Build Coastguard Worker  INFO = 2
61*795d594fSAndroid Build Coastguard Worker  WARNING = 3
62*795d594fSAndroid Build Coastguard Worker  ERROR = 4
63*795d594fSAndroid Build Coastguard Worker  FATAL = 5
64*795d594fSAndroid Build Coastguard Worker  SILENT = 6
65*795d594fSAndroid Build Coastguard Worker
66*795d594fSAndroid Build Coastguard Worker  @property
67*795d594fSAndroid Build Coastguard Worker  def symbol(self):
68*795d594fSAndroid Build Coastguard Worker    return self.name[0]
69*795d594fSAndroid Build Coastguard Worker
70*795d594fSAndroid Build Coastguard Worker  @classmethod
71*795d594fSAndroid Build Coastguard Worker  def FromSymbol(cls, s):
72*795d594fSAndroid Build Coastguard Worker    for log_severity in LogSeverity:
73*795d594fSAndroid Build Coastguard Worker      if log_severity.symbol == s:
74*795d594fSAndroid Build Coastguard Worker        return log_severity
75*795d594fSAndroid Build Coastguard Worker    raise ValueError("{0} is not a valid log severity symbol".format(s))
76*795d594fSAndroid Build Coastguard Worker
77*795d594fSAndroid Build Coastguard Worker  def __ge__(self, other):
78*795d594fSAndroid Build Coastguard Worker    if self.__class__ is other.__class__:
79*795d594fSAndroid Build Coastguard Worker      return self.value >= other.value
80*795d594fSAndroid Build Coastguard Worker    return NotImplemented
81*795d594fSAndroid Build Coastguard Worker
82*795d594fSAndroid Build Coastguard Worker  def __gt__(self, other):
83*795d594fSAndroid Build Coastguard Worker    if self.__class__ is other.__class__:
84*795d594fSAndroid Build Coastguard Worker      return self.value > other.value
85*795d594fSAndroid Build Coastguard Worker    return NotImplemented
86*795d594fSAndroid Build Coastguard Worker
87*795d594fSAndroid Build Coastguard Worker  def __le__(self, other):
88*795d594fSAndroid Build Coastguard Worker    if self.__class__ is other.__class__:
89*795d594fSAndroid Build Coastguard Worker      return self.value <= other.value
90*795d594fSAndroid Build Coastguard Worker    return NotImplemented
91*795d594fSAndroid Build Coastguard Worker
92*795d594fSAndroid Build Coastguard Worker  def __lt__(self, other):
93*795d594fSAndroid Build Coastguard Worker    if self.__class__ is other.__class__:
94*795d594fSAndroid Build Coastguard Worker      return self.value < other.value
95*795d594fSAndroid Build Coastguard Worker    return NotImplemented
96*795d594fSAndroid Build Coastguard Worker
97*795d594fSAndroid Build Coastguard Worker
98*795d594fSAndroid Build Coastguard Workerdef GetEnvVariableOrError(variable_name):
99*795d594fSAndroid Build Coastguard Worker  """Gets value of an environmental variable.
100*795d594fSAndroid Build Coastguard Worker
101*795d594fSAndroid Build Coastguard Worker  If the variable is not set raises FatalError.
102*795d594fSAndroid Build Coastguard Worker
103*795d594fSAndroid Build Coastguard Worker  Args:
104*795d594fSAndroid Build Coastguard Worker    variable_name: string, name of variable to get.
105*795d594fSAndroid Build Coastguard Worker
106*795d594fSAndroid Build Coastguard Worker  Returns:
107*795d594fSAndroid Build Coastguard Worker    string, value of requested variable.
108*795d594fSAndroid Build Coastguard Worker
109*795d594fSAndroid Build Coastguard Worker  Raises:
110*795d594fSAndroid Build Coastguard Worker    FatalError: Requested variable is not set.
111*795d594fSAndroid Build Coastguard Worker  """
112*795d594fSAndroid Build Coastguard Worker  top = os.environ.get(variable_name)
113*795d594fSAndroid Build Coastguard Worker  if top is None:
114*795d594fSAndroid Build Coastguard Worker    raise FatalError('{0} environmental variable not set.'.format(
115*795d594fSAndroid Build Coastguard Worker        variable_name))
116*795d594fSAndroid Build Coastguard Worker  return top
117*795d594fSAndroid Build Coastguard Worker
118*795d594fSAndroid Build Coastguard Worker
119*795d594fSAndroid Build Coastguard Workerdef _DexArchCachePaths(android_data_path):
120*795d594fSAndroid Build Coastguard Worker  """Returns paths to architecture specific caches.
121*795d594fSAndroid Build Coastguard Worker
122*795d594fSAndroid Build Coastguard Worker  Args:
123*795d594fSAndroid Build Coastguard Worker    android_data_path: string, path dalvik-cache resides in.
124*795d594fSAndroid Build Coastguard Worker
125*795d594fSAndroid Build Coastguard Worker  Returns:
126*795d594fSAndroid Build Coastguard Worker    Iterable paths to architecture specific caches.
127*795d594fSAndroid Build Coastguard Worker  """
128*795d594fSAndroid Build Coastguard Worker  return ('{0}/dalvik-cache/{1}'.format(android_data_path, arch)
129*795d594fSAndroid Build Coastguard Worker          for arch in DALVIK_CACHE_ARCHS)
130*795d594fSAndroid Build Coastguard Worker
131*795d594fSAndroid Build Coastguard Worker
132*795d594fSAndroid Build Coastguard Workerdef RunCommandForOutput(cmd, env, stdout, stderr, timeout=60):
133*795d594fSAndroid Build Coastguard Worker  """Runs command piping output to files, stderr or stdout.
134*795d594fSAndroid Build Coastguard Worker
135*795d594fSAndroid Build Coastguard Worker  Args:
136*795d594fSAndroid Build Coastguard Worker    cmd: list of strings, command to run.
137*795d594fSAndroid Build Coastguard Worker    env: shell environment to run the command with.
138*795d594fSAndroid Build Coastguard Worker    stdout: file handle or one of Subprocess.PIPE, Subprocess.STDOUT,
139*795d594fSAndroid Build Coastguard Worker      Subprocess.DEVNULL, see Popen.
140*795d594fSAndroid Build Coastguard Worker    stderr: file handle or one of Subprocess.PIPE, Subprocess.STDOUT,
141*795d594fSAndroid Build Coastguard Worker      Subprocess.DEVNULL, see Popen.
142*795d594fSAndroid Build Coastguard Worker    timeout: int, timeout in seconds.
143*795d594fSAndroid Build Coastguard Worker
144*795d594fSAndroid Build Coastguard Worker  Returns:
145*795d594fSAndroid Build Coastguard Worker    tuple (string, string, RetCode) stdout output, stderr output, normalized
146*795d594fSAndroid Build Coastguard Worker      return code.
147*795d594fSAndroid Build Coastguard Worker  """
148*795d594fSAndroid Build Coastguard Worker  proc = Popen(cmd, stdout=stdout, stderr=stderr, env=env,
149*795d594fSAndroid Build Coastguard Worker               universal_newlines=True, start_new_session=True)
150*795d594fSAndroid Build Coastguard Worker  try:
151*795d594fSAndroid Build Coastguard Worker    (output, stderr_output) = proc.communicate(timeout=timeout)
152*795d594fSAndroid Build Coastguard Worker    if proc.returncode == 0:
153*795d594fSAndroid Build Coastguard Worker      retcode = RetCode.SUCCESS
154*795d594fSAndroid Build Coastguard Worker    else:
155*795d594fSAndroid Build Coastguard Worker      retcode = RetCode.ERROR
156*795d594fSAndroid Build Coastguard Worker  except TimeoutExpired:
157*795d594fSAndroid Build Coastguard Worker    os.killpg(os.getpgid(proc.pid), signal.SIGTERM)
158*795d594fSAndroid Build Coastguard Worker    (output, stderr_output) = proc.communicate()
159*795d594fSAndroid Build Coastguard Worker    retcode = RetCode.TIMEOUT
160*795d594fSAndroid Build Coastguard Worker  return (output, stderr_output, retcode)
161*795d594fSAndroid Build Coastguard Worker
162*795d594fSAndroid Build Coastguard Worker
163*795d594fSAndroid Build Coastguard Workerdef _LogCmdOutput(logfile, cmd, output, retcode):
164*795d594fSAndroid Build Coastguard Worker  """Logs output of a command.
165*795d594fSAndroid Build Coastguard Worker
166*795d594fSAndroid Build Coastguard Worker  Args:
167*795d594fSAndroid Build Coastguard Worker    logfile: file handle to logfile.
168*795d594fSAndroid Build Coastguard Worker    cmd: list of strings, command.
169*795d594fSAndroid Build Coastguard Worker    output: command output.
170*795d594fSAndroid Build Coastguard Worker    retcode: RetCode, normalized retcode.
171*795d594fSAndroid Build Coastguard Worker  """
172*795d594fSAndroid Build Coastguard Worker  logfile.write('Command:\n{0}\n{1}\nReturn code: {2}\n'.format(
173*795d594fSAndroid Build Coastguard Worker      CommandListToCommandString(cmd), output, retcode))
174*795d594fSAndroid Build Coastguard Worker
175*795d594fSAndroid Build Coastguard Worker
176*795d594fSAndroid Build Coastguard Workerdef RunCommand(cmd, out, err, timeout=5):
177*795d594fSAndroid Build Coastguard Worker  """Executes a command, and returns its return code.
178*795d594fSAndroid Build Coastguard Worker
179*795d594fSAndroid Build Coastguard Worker  Args:
180*795d594fSAndroid Build Coastguard Worker    cmd: list of strings, a command to execute
181*795d594fSAndroid Build Coastguard Worker    out: string, file name to open for stdout (or None)
182*795d594fSAndroid Build Coastguard Worker    err: string, file name to open for stderr (or None)
183*795d594fSAndroid Build Coastguard Worker    timeout: int, time out in seconds
184*795d594fSAndroid Build Coastguard Worker  Returns:
185*795d594fSAndroid Build Coastguard Worker    RetCode, return code of running command (forced RetCode.TIMEOUT
186*795d594fSAndroid Build Coastguard Worker    on timeout)
187*795d594fSAndroid Build Coastguard Worker  """
188*795d594fSAndroid Build Coastguard Worker  devnull = DEVNULL
189*795d594fSAndroid Build Coastguard Worker  outf = devnull
190*795d594fSAndroid Build Coastguard Worker  if out is not None:
191*795d594fSAndroid Build Coastguard Worker    outf = open(out, mode='w')
192*795d594fSAndroid Build Coastguard Worker  errf = devnull
193*795d594fSAndroid Build Coastguard Worker  if err is not None:
194*795d594fSAndroid Build Coastguard Worker    errf = open(err, mode='w')
195*795d594fSAndroid Build Coastguard Worker  (_, _, retcode) = RunCommandForOutput(cmd, None, outf, errf, timeout)
196*795d594fSAndroid Build Coastguard Worker  if outf != devnull:
197*795d594fSAndroid Build Coastguard Worker    outf.close()
198*795d594fSAndroid Build Coastguard Worker  if errf != devnull:
199*795d594fSAndroid Build Coastguard Worker    errf.close()
200*795d594fSAndroid Build Coastguard Worker  return retcode
201*795d594fSAndroid Build Coastguard Worker
202*795d594fSAndroid Build Coastguard Worker
203*795d594fSAndroid Build Coastguard Workerdef CommandListToCommandString(cmd):
204*795d594fSAndroid Build Coastguard Worker  """Converts shell command represented as list of strings to a single string.
205*795d594fSAndroid Build Coastguard Worker
206*795d594fSAndroid Build Coastguard Worker  Each element of the list is wrapped in double quotes.
207*795d594fSAndroid Build Coastguard Worker
208*795d594fSAndroid Build Coastguard Worker  Args:
209*795d594fSAndroid Build Coastguard Worker    cmd: list of strings, shell command.
210*795d594fSAndroid Build Coastguard Worker
211*795d594fSAndroid Build Coastguard Worker  Returns:
212*795d594fSAndroid Build Coastguard Worker    string, shell command.
213*795d594fSAndroid Build Coastguard Worker  """
214*795d594fSAndroid Build Coastguard Worker  return ' '.join([shlex.quote(segment) for segment in cmd])
215*795d594fSAndroid Build Coastguard Worker
216*795d594fSAndroid Build Coastguard Worker
217*795d594fSAndroid Build Coastguard Workerclass FatalError(Exception):
218*795d594fSAndroid Build Coastguard Worker  """Fatal error in script."""
219*795d594fSAndroid Build Coastguard Worker
220*795d594fSAndroid Build Coastguard Worker
221*795d594fSAndroid Build Coastguard Workerclass ITestEnv(object):
222*795d594fSAndroid Build Coastguard Worker  """Test environment abstraction.
223*795d594fSAndroid Build Coastguard Worker
224*795d594fSAndroid Build Coastguard Worker  Provides unified interface for interacting with host and device test
225*795d594fSAndroid Build Coastguard Worker  environments. Creates a test directory and expose methods to modify test files
226*795d594fSAndroid Build Coastguard Worker  and run commands.
227*795d594fSAndroid Build Coastguard Worker  """
228*795d594fSAndroid Build Coastguard Worker  __meta_class__ = abc.ABCMeta
229*795d594fSAndroid Build Coastguard Worker
230*795d594fSAndroid Build Coastguard Worker  @abc.abstractmethod
231*795d594fSAndroid Build Coastguard Worker  def CreateFile(self, name=None):
232*795d594fSAndroid Build Coastguard Worker    """Creates a file in test directory.
233*795d594fSAndroid Build Coastguard Worker
234*795d594fSAndroid Build Coastguard Worker    Returned path to file can be used in commands run in the environment.
235*795d594fSAndroid Build Coastguard Worker
236*795d594fSAndroid Build Coastguard Worker    Args:
237*795d594fSAndroid Build Coastguard Worker      name: string, file name. If None file is named arbitrarily.
238*795d594fSAndroid Build Coastguard Worker
239*795d594fSAndroid Build Coastguard Worker    Returns:
240*795d594fSAndroid Build Coastguard Worker      string, environment specific path to file.
241*795d594fSAndroid Build Coastguard Worker    """
242*795d594fSAndroid Build Coastguard Worker
243*795d594fSAndroid Build Coastguard Worker  @abc.abstractmethod
244*795d594fSAndroid Build Coastguard Worker  def WriteLines(self, file_path, lines):
245*795d594fSAndroid Build Coastguard Worker    """Writes lines to a file in test directory.
246*795d594fSAndroid Build Coastguard Worker
247*795d594fSAndroid Build Coastguard Worker    If file exists it gets overwritten. If file doest not exist it is created.
248*795d594fSAndroid Build Coastguard Worker
249*795d594fSAndroid Build Coastguard Worker    Args:
250*795d594fSAndroid Build Coastguard Worker      file_path: string, environment specific path to file.
251*795d594fSAndroid Build Coastguard Worker      lines: list of strings to write.
252*795d594fSAndroid Build Coastguard Worker    """
253*795d594fSAndroid Build Coastguard Worker
254*795d594fSAndroid Build Coastguard Worker  @abc.abstractmethod
255*795d594fSAndroid Build Coastguard Worker  def RunCommand(self, cmd, log_severity=LogSeverity.ERROR):
256*795d594fSAndroid Build Coastguard Worker    """Runs command in environment.
257*795d594fSAndroid Build Coastguard Worker
258*795d594fSAndroid Build Coastguard Worker    Args:
259*795d594fSAndroid Build Coastguard Worker      cmd: list of strings, command to run.
260*795d594fSAndroid Build Coastguard Worker      log_severity: LogSeverity, minimum severity of logs included in output.
261*795d594fSAndroid Build Coastguard Worker    Returns:
262*795d594fSAndroid Build Coastguard Worker      tuple (string, int) output, return code.
263*795d594fSAndroid Build Coastguard Worker    """
264*795d594fSAndroid Build Coastguard Worker
265*795d594fSAndroid Build Coastguard Worker  @abc.abstractproperty
266*795d594fSAndroid Build Coastguard Worker  def logfile(self):
267*795d594fSAndroid Build Coastguard Worker    """Gets file handle to logfile residing on host."""
268*795d594fSAndroid Build Coastguard Worker
269*795d594fSAndroid Build Coastguard Worker
270*795d594fSAndroid Build Coastguard Workerclass HostTestEnv(ITestEnv):
271*795d594fSAndroid Build Coastguard Worker  """Host test environment. Concrete implementation of ITestEnv.
272*795d594fSAndroid Build Coastguard Worker
273*795d594fSAndroid Build Coastguard Worker  Maintains a test directory in /tmp/. Runs commands on the host in modified
274*795d594fSAndroid Build Coastguard Worker  shell environment. Mimics art script behavior.
275*795d594fSAndroid Build Coastguard Worker
276*795d594fSAndroid Build Coastguard Worker  For methods documentation see base class.
277*795d594fSAndroid Build Coastguard Worker  """
278*795d594fSAndroid Build Coastguard Worker
279*795d594fSAndroid Build Coastguard Worker  def __init__(self, directory_prefix, cleanup=True, logfile_path=None,
280*795d594fSAndroid Build Coastguard Worker               timeout=60, x64=False):
281*795d594fSAndroid Build Coastguard Worker    """Constructor.
282*795d594fSAndroid Build Coastguard Worker
283*795d594fSAndroid Build Coastguard Worker    Args:
284*795d594fSAndroid Build Coastguard Worker      directory_prefix: string, prefix for environment directory name.
285*795d594fSAndroid Build Coastguard Worker      cleanup: boolean, if True remove test directory in destructor.
286*795d594fSAndroid Build Coastguard Worker      logfile_path: string, can be used to specify custom logfile location.
287*795d594fSAndroid Build Coastguard Worker      timeout: int, seconds, time to wait for single test run to finish.
288*795d594fSAndroid Build Coastguard Worker      x64: boolean, whether to setup in x64 mode.
289*795d594fSAndroid Build Coastguard Worker    """
290*795d594fSAndroid Build Coastguard Worker    self._cleanup = cleanup
291*795d594fSAndroid Build Coastguard Worker    self._timeout = timeout
292*795d594fSAndroid Build Coastguard Worker    self._env_path = mkdtemp(dir='/tmp/', prefix=directory_prefix)
293*795d594fSAndroid Build Coastguard Worker    if logfile_path is None:
294*795d594fSAndroid Build Coastguard Worker      self._logfile = open('{0}/log'.format(self._env_path), 'w+')
295*795d594fSAndroid Build Coastguard Worker    else:
296*795d594fSAndroid Build Coastguard Worker      self._logfile = open(logfile_path, 'w+')
297*795d594fSAndroid Build Coastguard Worker    os.mkdir('{0}/dalvik-cache'.format(self._env_path))
298*795d594fSAndroid Build Coastguard Worker    for arch_cache_path in _DexArchCachePaths(self._env_path):
299*795d594fSAndroid Build Coastguard Worker      os.mkdir(arch_cache_path)
300*795d594fSAndroid Build Coastguard Worker    lib = 'lib64' if x64 else 'lib'
301*795d594fSAndroid Build Coastguard Worker    android_root = GetEnvVariableOrError('ANDROID_HOST_OUT')
302*795d594fSAndroid Build Coastguard Worker    android_i18n_root = android_root + '/com.android.i18n'
303*795d594fSAndroid Build Coastguard Worker    android_art_root = android_root + '/com.android.art'
304*795d594fSAndroid Build Coastguard Worker    android_tzdata_root = android_root + '/com.android.tzdata'
305*795d594fSAndroid Build Coastguard Worker    library_path = android_root + '/' + lib
306*795d594fSAndroid Build Coastguard Worker    path = android_root + '/bin'
307*795d594fSAndroid Build Coastguard Worker    self._shell_env = os.environ.copy()
308*795d594fSAndroid Build Coastguard Worker    self._shell_env['ANDROID_DATA'] = self._env_path
309*795d594fSAndroid Build Coastguard Worker    self._shell_env['ANDROID_ROOT'] = android_root
310*795d594fSAndroid Build Coastguard Worker    self._shell_env['ANDROID_I18N_ROOT'] = android_i18n_root
311*795d594fSAndroid Build Coastguard Worker    self._shell_env['ANDROID_ART_ROOT'] = android_art_root
312*795d594fSAndroid Build Coastguard Worker    self._shell_env['ANDROID_TZDATA_ROOT'] = android_tzdata_root
313*795d594fSAndroid Build Coastguard Worker    self._shell_env['LD_LIBRARY_PATH'] = library_path
314*795d594fSAndroid Build Coastguard Worker    self._shell_env['DYLD_LIBRARY_PATH'] = library_path
315*795d594fSAndroid Build Coastguard Worker    self._shell_env['PATH'] = (path + ':' + self._shell_env['PATH'])
316*795d594fSAndroid Build Coastguard Worker    # Using dlopen requires load bias on the host.
317*795d594fSAndroid Build Coastguard Worker    self._shell_env['LD_USE_LOAD_BIAS'] = '1'
318*795d594fSAndroid Build Coastguard Worker
319*795d594fSAndroid Build Coastguard Worker  def __del__(self):
320*795d594fSAndroid Build Coastguard Worker    if self._cleanup:
321*795d594fSAndroid Build Coastguard Worker      shutil.rmtree(self._env_path)
322*795d594fSAndroid Build Coastguard Worker
323*795d594fSAndroid Build Coastguard Worker  def CreateFile(self, name=None):
324*795d594fSAndroid Build Coastguard Worker    if name is None:
325*795d594fSAndroid Build Coastguard Worker      f = NamedTemporaryFile(dir=self._env_path, delete=False)
326*795d594fSAndroid Build Coastguard Worker    else:
327*795d594fSAndroid Build Coastguard Worker      f = open('{0}/{1}'.format(self._env_path, name), 'w+')
328*795d594fSAndroid Build Coastguard Worker    return f.name
329*795d594fSAndroid Build Coastguard Worker
330*795d594fSAndroid Build Coastguard Worker  def WriteLines(self, file_path, lines):
331*795d594fSAndroid Build Coastguard Worker    with open(file_path, 'w') as f:
332*795d594fSAndroid Build Coastguard Worker      f.writelines('{0}\n'.format(line) for line in lines)
333*795d594fSAndroid Build Coastguard Worker    return
334*795d594fSAndroid Build Coastguard Worker
335*795d594fSAndroid Build Coastguard Worker  def RunCommand(self, cmd, log_severity=LogSeverity.ERROR):
336*795d594fSAndroid Build Coastguard Worker    self._EmptyDexCache()
337*795d594fSAndroid Build Coastguard Worker    env = self._shell_env.copy()
338*795d594fSAndroid Build Coastguard Worker    env.update({'ANDROID_LOG_TAGS':'*:' + log_severity.symbol.lower()})
339*795d594fSAndroid Build Coastguard Worker    (output, err_output, retcode) = RunCommandForOutput(
340*795d594fSAndroid Build Coastguard Worker        cmd, env, PIPE, PIPE, self._timeout)
341*795d594fSAndroid Build Coastguard Worker    # We append err_output to output to stay consistent with DeviceTestEnv
342*795d594fSAndroid Build Coastguard Worker    # implementation.
343*795d594fSAndroid Build Coastguard Worker    output += err_output
344*795d594fSAndroid Build Coastguard Worker    _LogCmdOutput(self._logfile, cmd, output, retcode)
345*795d594fSAndroid Build Coastguard Worker    return (output, retcode)
346*795d594fSAndroid Build Coastguard Worker
347*795d594fSAndroid Build Coastguard Worker  @property
348*795d594fSAndroid Build Coastguard Worker  def logfile(self):
349*795d594fSAndroid Build Coastguard Worker    return self._logfile
350*795d594fSAndroid Build Coastguard Worker
351*795d594fSAndroid Build Coastguard Worker  def _EmptyDexCache(self):
352*795d594fSAndroid Build Coastguard Worker    """Empties dex cache.
353*795d594fSAndroid Build Coastguard Worker
354*795d594fSAndroid Build Coastguard Worker    Iterate over files in architecture specific cache directories and remove
355*795d594fSAndroid Build Coastguard Worker    them.
356*795d594fSAndroid Build Coastguard Worker    """
357*795d594fSAndroid Build Coastguard Worker    for arch_cache_path in _DexArchCachePaths(self._env_path):
358*795d594fSAndroid Build Coastguard Worker      for file_path in os.listdir(arch_cache_path):
359*795d594fSAndroid Build Coastguard Worker        file_path = '{0}/{1}'.format(arch_cache_path, file_path)
360*795d594fSAndroid Build Coastguard Worker        if os.path.isfile(file_path):
361*795d594fSAndroid Build Coastguard Worker          os.unlink(file_path)
362*795d594fSAndroid Build Coastguard Worker
363*795d594fSAndroid Build Coastguard Worker
364*795d594fSAndroid Build Coastguard Workerclass DeviceTestEnv(ITestEnv):
365*795d594fSAndroid Build Coastguard Worker  """Device test environment. Concrete implementation of ITestEnv.
366*795d594fSAndroid Build Coastguard Worker
367*795d594fSAndroid Build Coastguard Worker  For methods documentation see base class.
368*795d594fSAndroid Build Coastguard Worker  """
369*795d594fSAndroid Build Coastguard Worker
370*795d594fSAndroid Build Coastguard Worker  def __init__(self, directory_prefix, cleanup=True, logfile_path=None,
371*795d594fSAndroid Build Coastguard Worker               timeout=60, specific_device=None):
372*795d594fSAndroid Build Coastguard Worker    """Constructor.
373*795d594fSAndroid Build Coastguard Worker
374*795d594fSAndroid Build Coastguard Worker    Args:
375*795d594fSAndroid Build Coastguard Worker      directory_prefix: string, prefix for environment directory name.
376*795d594fSAndroid Build Coastguard Worker      cleanup: boolean, if True remove test directory in destructor.
377*795d594fSAndroid Build Coastguard Worker      logfile_path: string, can be used to specify custom logfile location.
378*795d594fSAndroid Build Coastguard Worker      timeout: int, seconds, time to wait for single test run to finish.
379*795d594fSAndroid Build Coastguard Worker      specific_device: string, serial number of device to use.
380*795d594fSAndroid Build Coastguard Worker    """
381*795d594fSAndroid Build Coastguard Worker    self._cleanup = cleanup
382*795d594fSAndroid Build Coastguard Worker    self._timeout = timeout
383*795d594fSAndroid Build Coastguard Worker    self._specific_device = specific_device
384*795d594fSAndroid Build Coastguard Worker    self._host_env_path = mkdtemp(dir='/tmp/', prefix=directory_prefix)
385*795d594fSAndroid Build Coastguard Worker    if logfile_path is None:
386*795d594fSAndroid Build Coastguard Worker      self._logfile = open('{0}/log'.format(self._host_env_path), 'w+')
387*795d594fSAndroid Build Coastguard Worker    else:
388*795d594fSAndroid Build Coastguard Worker      self._logfile = open(logfile_path, 'w+')
389*795d594fSAndroid Build Coastguard Worker    self._device_env_path = '{0}/{1}'.format(
390*795d594fSAndroid Build Coastguard Worker        DEVICE_TMP_PATH, os.path.basename(self._host_env_path))
391*795d594fSAndroid Build Coastguard Worker    self._shell_env = os.environ.copy()
392*795d594fSAndroid Build Coastguard Worker
393*795d594fSAndroid Build Coastguard Worker    self._AdbMkdir('{0}/dalvik-cache'.format(self._device_env_path))
394*795d594fSAndroid Build Coastguard Worker    for arch_cache_path in _DexArchCachePaths(self._device_env_path):
395*795d594fSAndroid Build Coastguard Worker      self._AdbMkdir(arch_cache_path)
396*795d594fSAndroid Build Coastguard Worker
397*795d594fSAndroid Build Coastguard Worker  def __del__(self):
398*795d594fSAndroid Build Coastguard Worker    if self._cleanup:
399*795d594fSAndroid Build Coastguard Worker      shutil.rmtree(self._host_env_path)
400*795d594fSAndroid Build Coastguard Worker      check_call(shlex.split(
401*795d594fSAndroid Build Coastguard Worker          'adb shell if [ -d "{0}" ]; then rm -rf "{0}"; fi'
402*795d594fSAndroid Build Coastguard Worker          .format(self._device_env_path)))
403*795d594fSAndroid Build Coastguard Worker
404*795d594fSAndroid Build Coastguard Worker  def CreateFile(self, name=None):
405*795d594fSAndroid Build Coastguard Worker    with NamedTemporaryFile(mode='w') as temp_file:
406*795d594fSAndroid Build Coastguard Worker      self._AdbPush(temp_file.name, self._device_env_path)
407*795d594fSAndroid Build Coastguard Worker      if name is None:
408*795d594fSAndroid Build Coastguard Worker        name = os.path.basename(temp_file.name)
409*795d594fSAndroid Build Coastguard Worker      return '{0}/{1}'.format(self._device_env_path, name)
410*795d594fSAndroid Build Coastguard Worker
411*795d594fSAndroid Build Coastguard Worker  def WriteLines(self, file_path, lines):
412*795d594fSAndroid Build Coastguard Worker    with NamedTemporaryFile(mode='w') as temp_file:
413*795d594fSAndroid Build Coastguard Worker      temp_file.writelines('{0}\n'.format(line) for line in lines)
414*795d594fSAndroid Build Coastguard Worker      temp_file.flush()
415*795d594fSAndroid Build Coastguard Worker      self._AdbPush(temp_file.name, file_path)
416*795d594fSAndroid Build Coastguard Worker    return
417*795d594fSAndroid Build Coastguard Worker
418*795d594fSAndroid Build Coastguard Worker  def _ExtractPid(self, brief_log_line):
419*795d594fSAndroid Build Coastguard Worker    """Extracts PID from a single logcat line in brief format."""
420*795d594fSAndroid Build Coastguard Worker    pid_start_idx = brief_log_line.find('(') + 2
421*795d594fSAndroid Build Coastguard Worker    if pid_start_idx == -1:
422*795d594fSAndroid Build Coastguard Worker      return None
423*795d594fSAndroid Build Coastguard Worker    pid_end_idx = brief_log_line.find(')', pid_start_idx)
424*795d594fSAndroid Build Coastguard Worker    if pid_end_idx == -1:
425*795d594fSAndroid Build Coastguard Worker      return None
426*795d594fSAndroid Build Coastguard Worker    return brief_log_line[pid_start_idx:pid_end_idx]
427*795d594fSAndroid Build Coastguard Worker
428*795d594fSAndroid Build Coastguard Worker  def _ExtractSeverity(self, brief_log_line):
429*795d594fSAndroid Build Coastguard Worker    """Extracts LogSeverity from a single logcat line in brief format."""
430*795d594fSAndroid Build Coastguard Worker    if not brief_log_line:
431*795d594fSAndroid Build Coastguard Worker      return None
432*795d594fSAndroid Build Coastguard Worker    return LogSeverity.FromSymbol(brief_log_line[0])
433*795d594fSAndroid Build Coastguard Worker
434*795d594fSAndroid Build Coastguard Worker  def RunCommand(self, cmd, log_severity=LogSeverity.ERROR):
435*795d594fSAndroid Build Coastguard Worker    self._EmptyDexCache()
436*795d594fSAndroid Build Coastguard Worker    env_vars_cmd = 'ANDROID_DATA={0} ANDROID_LOG_TAGS=*:i'.format(
437*795d594fSAndroid Build Coastguard Worker        self._device_env_path)
438*795d594fSAndroid Build Coastguard Worker    adb_cmd = ['adb']
439*795d594fSAndroid Build Coastguard Worker    if self._specific_device:
440*795d594fSAndroid Build Coastguard Worker      adb_cmd += ['-s', self._specific_device]
441*795d594fSAndroid Build Coastguard Worker    logcat_cmd = adb_cmd + ['logcat', '-v', 'brief', '-s', '-b', 'main',
442*795d594fSAndroid Build Coastguard Worker                            '-T', '1', 'dex2oat:*', 'dex2oatd:*']
443*795d594fSAndroid Build Coastguard Worker    logcat_proc = Popen(logcat_cmd, stdout=PIPE, stderr=STDOUT,
444*795d594fSAndroid Build Coastguard Worker                        universal_newlines=True)
445*795d594fSAndroid Build Coastguard Worker    cmd_str = CommandListToCommandString(cmd)
446*795d594fSAndroid Build Coastguard Worker    # Print PID of the shell and exec command. We later retrieve this PID and
447*795d594fSAndroid Build Coastguard Worker    # use it to filter dex2oat logs, keeping those with matching parent PID.
448*795d594fSAndroid Build Coastguard Worker    device_cmd = ('echo $$ && ' + env_vars_cmd + ' exec ' + cmd_str)
449*795d594fSAndroid Build Coastguard Worker    cmd = adb_cmd + ['shell', device_cmd]
450*795d594fSAndroid Build Coastguard Worker    (output, _, retcode) = RunCommandForOutput(cmd, self._shell_env, PIPE,
451*795d594fSAndroid Build Coastguard Worker                                               STDOUT, self._timeout)
452*795d594fSAndroid Build Coastguard Worker    # We need to make sure to only kill logcat once all relevant logs arrive.
453*795d594fSAndroid Build Coastguard Worker    # Sleep is used for simplicity.
454*795d594fSAndroid Build Coastguard Worker    time.sleep(0.5)
455*795d594fSAndroid Build Coastguard Worker    logcat_proc.kill()
456*795d594fSAndroid Build Coastguard Worker    end_of_first_line = output.find('\n')
457*795d594fSAndroid Build Coastguard Worker    if end_of_first_line != -1:
458*795d594fSAndroid Build Coastguard Worker      parent_pid = output[:end_of_first_line]
459*795d594fSAndroid Build Coastguard Worker      output = output[end_of_first_line + 1:]
460*795d594fSAndroid Build Coastguard Worker      logcat_output, _ = logcat_proc.communicate()
461*795d594fSAndroid Build Coastguard Worker      logcat_lines = logcat_output.splitlines(keepends=True)
462*795d594fSAndroid Build Coastguard Worker      dex2oat_pids = []
463*795d594fSAndroid Build Coastguard Worker      for line in logcat_lines:
464*795d594fSAndroid Build Coastguard Worker        # Dex2oat was started by our runtime instance.
465*795d594fSAndroid Build Coastguard Worker        if 'Running dex2oat (parent PID = ' + parent_pid in line:
466*795d594fSAndroid Build Coastguard Worker          dex2oat_pids.append(self._ExtractPid(line))
467*795d594fSAndroid Build Coastguard Worker          break
468*795d594fSAndroid Build Coastguard Worker      if dex2oat_pids:
469*795d594fSAndroid Build Coastguard Worker        for line in logcat_lines:
470*795d594fSAndroid Build Coastguard Worker          if (self._ExtractPid(line) in dex2oat_pids and
471*795d594fSAndroid Build Coastguard Worker              self._ExtractSeverity(line) >= log_severity):
472*795d594fSAndroid Build Coastguard Worker            output += line
473*795d594fSAndroid Build Coastguard Worker    _LogCmdOutput(self._logfile, cmd, output, retcode)
474*795d594fSAndroid Build Coastguard Worker    return (output, retcode)
475*795d594fSAndroid Build Coastguard Worker
476*795d594fSAndroid Build Coastguard Worker  @property
477*795d594fSAndroid Build Coastguard Worker  def logfile(self):
478*795d594fSAndroid Build Coastguard Worker    return self._logfile
479*795d594fSAndroid Build Coastguard Worker
480*795d594fSAndroid Build Coastguard Worker  def PushClasspath(self, classpath):
481*795d594fSAndroid Build Coastguard Worker    """Push classpath to on-device test directory.
482*795d594fSAndroid Build Coastguard Worker
483*795d594fSAndroid Build Coastguard Worker    Classpath can contain multiple colon separated file paths, each file is
484*795d594fSAndroid Build Coastguard Worker    pushed. Returns analogous classpath with paths valid on device.
485*795d594fSAndroid Build Coastguard Worker
486*795d594fSAndroid Build Coastguard Worker    Args:
487*795d594fSAndroid Build Coastguard Worker      classpath: string, classpath in format 'a/b/c:d/e/f'.
488*795d594fSAndroid Build Coastguard Worker    Returns:
489*795d594fSAndroid Build Coastguard Worker      string, classpath valid on device.
490*795d594fSAndroid Build Coastguard Worker    """
491*795d594fSAndroid Build Coastguard Worker    paths = classpath.split(':')
492*795d594fSAndroid Build Coastguard Worker    device_paths = []
493*795d594fSAndroid Build Coastguard Worker    for path in paths:
494*795d594fSAndroid Build Coastguard Worker      device_paths.append('{0}/{1}'.format(
495*795d594fSAndroid Build Coastguard Worker          self._device_env_path, os.path.basename(path)))
496*795d594fSAndroid Build Coastguard Worker      self._AdbPush(path, self._device_env_path)
497*795d594fSAndroid Build Coastguard Worker    return ':'.join(device_paths)
498*795d594fSAndroid Build Coastguard Worker
499*795d594fSAndroid Build Coastguard Worker  def _AdbPush(self, what, where):
500*795d594fSAndroid Build Coastguard Worker    check_call(shlex.split('adb push "{0}" "{1}"'.format(what, where)),
501*795d594fSAndroid Build Coastguard Worker               stdout=self._logfile, stderr=self._logfile)
502*795d594fSAndroid Build Coastguard Worker
503*795d594fSAndroid Build Coastguard Worker  def _AdbMkdir(self, path):
504*795d594fSAndroid Build Coastguard Worker    check_call(shlex.split('adb shell mkdir "{0}" -p'.format(path)),
505*795d594fSAndroid Build Coastguard Worker               stdout=self._logfile, stderr=self._logfile)
506*795d594fSAndroid Build Coastguard Worker
507*795d594fSAndroid Build Coastguard Worker  def _EmptyDexCache(self):
508*795d594fSAndroid Build Coastguard Worker    """Empties dex cache."""
509*795d594fSAndroid Build Coastguard Worker    for arch_cache_path in _DexArchCachePaths(self._device_env_path):
510*795d594fSAndroid Build Coastguard Worker      cmd = 'adb shell if [ -d "{0}" ]; then rm -f "{0}"/*; fi'.format(
511*795d594fSAndroid Build Coastguard Worker          arch_cache_path)
512*795d594fSAndroid Build Coastguard Worker      check_call(shlex.split(cmd), stdout=self._logfile, stderr=self._logfile)
513