147f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik#!/usr/bin/env python
247f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik
347f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# Copyright (c) 2016 The Chromium Authors. All rights reserved.
447f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# Use of this source code is governed by a BSD-style license that can be
547f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# found in the LICENSE file.
647f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik
77332cdb42368a904cbf7418de329868989e592daChris Craikimport functools
833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport inspect
97332cdb42368a904cbf7418de329868989e592daChris Craikimport os
107332cdb42368a904cbf7418de329868989e592daChris Craikimport sys
1133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport time
12b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wangimport platform
137332cdb42368a904cbf7418de329868989e592daChris Craik
147332cdb42368a904cbf7418de329868989e592daChris Craik
157332cdb42368a904cbf7418de329868989e592daChris Craikdef GetCatapultDir():
167332cdb42368a904cbf7418de329868989e592daChris Craik  return os.path.normpath(
177332cdb42368a904cbf7418de329868989e592daChris Craik      os.path.join(os.path.dirname(__file__), '..', '..', '..'))
187332cdb42368a904cbf7418de329868989e592daChris Craik
197332cdb42368a904cbf7418de329868989e592daChris Craik
207332cdb42368a904cbf7418de329868989e592daChris Craikdef IsRunningOnCrosDevice():
217332cdb42368a904cbf7418de329868989e592daChris Craik  """Returns True if we're on a ChromeOS device."""
227332cdb42368a904cbf7418de329868989e592daChris Craik  lsb_release = '/etc/lsb-release'
237332cdb42368a904cbf7418de329868989e592daChris Craik  if sys.platform.startswith('linux') and os.path.exists(lsb_release):
247332cdb42368a904cbf7418de329868989e592daChris Craik    with open(lsb_release, 'r') as f:
257332cdb42368a904cbf7418de329868989e592daChris Craik      res = f.read()
267332cdb42368a904cbf7418de329868989e592daChris Craik      if res.count('CHROMEOS_RELEASE_NAME'):
277332cdb42368a904cbf7418de329868989e592daChris Craik        return True
287332cdb42368a904cbf7418de329868989e592daChris Craik  return False
297332cdb42368a904cbf7418de329868989e592daChris Craik
307332cdb42368a904cbf7418de329868989e592daChris Craik
31b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wangdef GetHostOsName():
32b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang  if IsRunningOnCrosDevice():
33b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang    return 'chromeos'
34b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang  elif sys.platform.startswith('linux'):
35b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang    return 'linux'
36b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang  elif sys.platform == 'darwin':
37b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang    return 'mac'
38b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang  elif sys.platform == 'win32':
39b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang    return 'win'
40b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang
41b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang
42b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wangdef GetHostArchName():
43b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang  return platform.machine()
44b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang
45b2cf025c7d5cebd43084f38c6c7ff9cc17da428aWei Wang
467332cdb42368a904cbf7418de329868989e592daChris Craikdef _ExecutableExtensions():
477332cdb42368a904cbf7418de329868989e592daChris Craik  # pathext is, e.g. '.com;.exe;.bat;.cmd'
487332cdb42368a904cbf7418de329868989e592daChris Craik  exts = os.getenv('PATHEXT').split(';') #e.g. ['.com','.exe','.bat','.cmd']
497332cdb42368a904cbf7418de329868989e592daChris Craik  return [x[1:].upper() for x in exts] #e.g. ['COM','EXE','BAT','CMD']
507332cdb42368a904cbf7418de329868989e592daChris Craik
517332cdb42368a904cbf7418de329868989e592daChris Craik
527332cdb42368a904cbf7418de329868989e592daChris Craikdef IsExecutable(path):
537332cdb42368a904cbf7418de329868989e592daChris Craik  if os.path.isfile(path):
547332cdb42368a904cbf7418de329868989e592daChris Craik    if hasattr(os, 'name') and os.name == 'nt':
557332cdb42368a904cbf7418de329868989e592daChris Craik      return path.split('.')[-1].upper() in _ExecutableExtensions()
567332cdb42368a904cbf7418de329868989e592daChris Craik    else:
577332cdb42368a904cbf7418de329868989e592daChris Craik      return os.access(path, os.X_OK)
587332cdb42368a904cbf7418de329868989e592daChris Craik  else:
597332cdb42368a904cbf7418de329868989e592daChris Craik    return False
607332cdb42368a904cbf7418de329868989e592daChris Craik
617332cdb42368a904cbf7418de329868989e592daChris Craik
627332cdb42368a904cbf7418de329868989e592daChris Craikdef _AddDirToPythonPath(*path_parts):
637332cdb42368a904cbf7418de329868989e592daChris Craik  path = os.path.abspath(os.path.join(*path_parts))
647332cdb42368a904cbf7418de329868989e592daChris Craik  if os.path.isdir(path) and path not in sys.path:
657332cdb42368a904cbf7418de329868989e592daChris Craik    # Some callsite that use telemetry assumes that sys.path[0] is the directory
667332cdb42368a904cbf7418de329868989e592daChris Craik    # containing the script, so we add these extra paths to right after it.
677332cdb42368a904cbf7418de329868989e592daChris Craik    sys.path.insert(1, path)
687332cdb42368a904cbf7418de329868989e592daChris Craik
697332cdb42368a904cbf7418de329868989e592daChris Craik_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'devil'))
707332cdb42368a904cbf7418de329868989e592daChris Craik_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'dependency_manager'))
717332cdb42368a904cbf7418de329868989e592daChris Craik_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'third_party', 'mock'))
727332cdb42368a904cbf7418de329868989e592daChris Craik# mox3 is needed for pyfakefs usage, but not for pylint.
737332cdb42368a904cbf7418de329868989e592daChris Craik_AddDirToPythonPath(os.path.join(GetCatapultDir(), 'third_party', 'mox3'))
747332cdb42368a904cbf7418de329868989e592daChris Craik_AddDirToPythonPath(
757332cdb42368a904cbf7418de329868989e592daChris Craik    os.path.join(GetCatapultDir(), 'third_party', 'pyfakefs'))
7647f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik
7747f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craikfrom devil.utils import timeout_retry
7847f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craikfrom devil.utils import reraiser_thread
7947f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik
807332cdb42368a904cbf7418de329868989e592daChris Craik
817332cdb42368a904cbf7418de329868989e592daChris Craik# Decorator that adds timeout functionality to a function.
8247f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craikdef Timeout(default_timeout):
837332cdb42368a904cbf7418de329868989e592daChris Craik  return lambda func: TimeoutDeco(func, default_timeout)
8447f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik
8547f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# Note: Even though the "timeout" keyword argument is the only
8647f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# keyword argument that will need to be given to the decorated function,
8747f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# we still have to use the **kwargs syntax, because we have to use
8847f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# the *args syntax here before (since the decorator decorates functions
8947f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# with different numbers of positional arguments) and Python doesn't allow
9047f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# a single named keyword argument after *args.
9147f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik# (e.g., 'def foo(*args, bar=42):' is a syntax error)
9247f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik
937332cdb42368a904cbf7418de329868989e592daChris Craikdef TimeoutDeco(func, default_timeout):
9447f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik  @functools.wraps(func)
957332cdb42368a904cbf7418de329868989e592daChris Craik  def RunWithTimeout(*args, **kwargs):
9647f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik    if 'timeout' in kwargs:
9747f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik      timeout = kwargs['timeout']
9847f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik    else:
9947f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik      timeout = default_timeout
10047f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik    try:
10147f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik      return timeout_retry.Run(func, timeout, 0, args=args)
10247f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik    except reraiser_thread.TimeoutError:
10347f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik      print '%s timed out.' % func.__name__
10447f0f1e200da8a481462f364f822c98fe1b1cd5bChris Craik      return False
1057332cdb42368a904cbf7418de329868989e592daChris Craik  return RunWithTimeout
10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John ReckMIN_POLL_INTERVAL_IN_SECONDS = 0.1
10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John ReckMAX_POLL_INTERVAL_IN_SECONDS = 5
11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John ReckOUTPUT_INTERVAL_IN_SECONDS = 300
11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef WaitFor(condition, timeout):
11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """Waits for up to |timeout| secs for the function |condition| to return True.
11433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  Polling frequency is (elapsed_time / 10), with a min of .1s and max of 5s.
11633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
11733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  Returns:
11833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    Result of |condition| function (if present).
11933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """
12033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def GetConditionString():
12133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if condition.__name__ == '<lambda>':
12233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      try:
12333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return inspect.getsource(condition).strip()
12433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      except IOError:
12533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        pass
12633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return condition.__name__
12733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
12833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # Do an initial check to see if its true.
12933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  res = condition()
13033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  if res:
13133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return res
13233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  start_time = time.time()
13333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  last_output_time = start_time
13433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  elapsed_time = time.time() - start_time
13533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  while elapsed_time < timeout:
13633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    res = condition()
13733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if res:
13833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      return res
13933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    now = time.time()
14033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    elapsed_time = now - start_time
14133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    last_output_elapsed_time = now - last_output_time
14233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if last_output_elapsed_time > OUTPUT_INTERVAL_IN_SECONDS:
14333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      last_output_time = time.time()
14433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    poll_interval = min(max(elapsed_time / 10., MIN_POLL_INTERVAL_IN_SECONDS),
14533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                        MAX_POLL_INTERVAL_IN_SECONDS)
14633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    time.sleep(poll_interval)
14733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  raise TimeoutException('Timed out while waiting %ds for %s.' %
14833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                         (timeout, GetConditionString()))
14933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
15033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass TimeoutException(Exception):
15133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """The operation failed to complete because of a timeout.
15233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
15333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  It is possible that waiting for a longer period of time would result in a
15433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  successful operation.
15533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """
15633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  pass
157