1# Copyright 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4import glob
5import imp
6import inspect
7import logging
8import os
9import socket
10import sys
11import time
12
13from catapult_base import util as catapult_util  # pylint: disable=import-error
14
15from telemetry.core import exceptions
16
17
18IsRunningOnCrosDevice = catapult_util.IsRunningOnCrosDevice
19GetCatapultDir = catapult_util.GetCatapultDir
20
21
22def GetBaseDir():
23  main_module = sys.modules['__main__']
24  if hasattr(main_module, '__file__'):
25    return os.path.dirname(os.path.abspath(main_module.__file__))
26  else:
27    return os.getcwd()
28
29
30def GetCatapultThirdPartyDir():
31  return os.path.normpath(os.path.join(GetCatapultDir(), 'third_party'))
32
33
34def GetTelemetryDir():
35  return os.path.normpath(os.path.join(
36      os.path.abspath(__file__), '..', '..', '..'))
37
38
39def GetTelemetryThirdPartyDir():
40  return os.path.join(GetTelemetryDir(), 'third_party')
41
42
43def GetUnittestDataDir():
44  return os.path.join(GetTelemetryDir(), 'telemetry', 'internal', 'testing')
45
46
47def GetChromiumSrcDir():
48  return os.path.normpath(os.path.join(GetTelemetryDir(), '..', '..', '..'))
49
50
51_counter = [0]
52
53
54def _GetUniqueModuleName():
55  _counter[0] += 1
56  return "page_set_module_" + str(_counter[0])
57
58
59def GetPythonPageSetModule(file_path):
60  return imp.load_source(_GetUniqueModuleName(), file_path)
61
62
63def WaitFor(condition, timeout):
64  """Waits for up to |timeout| secs for the function |condition| to return True.
65
66  Polling frequency is (elapsed_time / 10), with a min of .1s and max of 5s.
67
68  Returns:
69    Result of |condition| function (if present).
70  """
71  min_poll_interval = 0.1
72  max_poll_interval = 5
73  output_interval = 300
74
75  def GetConditionString():
76    if condition.__name__ == '<lambda>':
77      try:
78        return inspect.getsource(condition).strip()
79      except IOError:
80        pass
81    return condition.__name__
82
83  start_time = time.time()
84  last_output_time = start_time
85  while True:
86    res = condition()
87    if res:
88      return res
89    now = time.time()
90    elapsed_time = now - start_time
91    last_output_elapsed_time = now - last_output_time
92    if elapsed_time > timeout:
93      raise exceptions.TimeoutException('Timed out while waiting %ds for %s.' %
94                                        (timeout, GetConditionString()))
95    if last_output_elapsed_time > output_interval:
96      logging.info('Continuing to wait %ds for %s. Elapsed: %ds.', timeout,
97                   GetConditionString(), elapsed_time)
98      last_output_time = time.time()
99    poll_interval = min(
100        max(elapsed_time / 10., min_poll_interval), max_poll_interval)
101    time.sleep(poll_interval)
102
103
104class PortKeeper(object):
105  """Port keeper hold an available port on the system.
106
107  Before actually use the port, you must call Release().
108  """
109
110  def __init__(self):
111    self._temp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
112    self._temp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
113    self._temp_socket.bind(('', 0))
114    self._port = self._temp_socket.getsockname()[1]
115
116  @property
117  def port(self):
118    return self._port
119
120  def Release(self):
121    assert self._temp_socket, 'Already released'
122    self._temp_socket.close()
123    self._temp_socket = None
124
125
126def GetUnreservedAvailableLocalPort():
127  """Returns an available port on the system.
128
129  WARNING: This method does not reserve the port it returns, so it may be used
130  by something else before you get to use it. This can lead to flake.
131  """
132  tmp = socket.socket()
133  tmp.bind(('', 0))
134  port = tmp.getsockname()[1]
135  tmp.close()
136
137  return port
138
139
140def GetBuildDirectories(chrome_root=None):
141  """Yields all combination of Chromium build output directories."""
142  # chrome_root can be set to something else via --chrome-root.
143  if not chrome_root:
144    chrome_root = GetChromiumSrcDir()
145
146  # CHROMIUM_OUTPUT_DIR can be set by --chromium-output-directory.
147  output_dir = os.environ.get('CHROMIUM_OUTPUT_DIR')
148  if output_dir:
149    yield os.path.join(chrome_root, output_dir)
150  elif os.path.exists('build.ninja'):
151    yield os.getcwd()
152  else:
153    out_dir = os.environ.get('CHROMIUM_OUT_DIR')
154    if out_dir:
155      build_dirs = [out_dir]
156    else:
157      build_dirs = ['build',
158                    'out',
159                    'xcodebuild']
160
161    build_types = ['Debug', 'Debug_x64', 'Release', 'Release_x64', 'Default']
162
163    for build_dir in build_dirs:
164      for build_type in build_types:
165        yield os.path.join(chrome_root, build_dir, build_type)
166
167
168def GetSequentialFileName(base_name):
169  """Returns the next sequential file name based on |base_name| and the
170  existing files. base_name should not contain extension.
171  e.g: if base_name is /tmp/test, and /tmp/test_000.json,
172  /tmp/test_001.mp3 exist, this returns /tmp/test_002. In case no
173  other sequential file name exist, this will return /tmp/test_000
174  """
175  name, ext = os.path.splitext(base_name)
176  assert ext == '', 'base_name cannot contain file extension.'
177  index = 0
178  while True:
179    output_name = '%s_%03d' % (name, index)
180    if not glob.glob(output_name + '.*'):
181      break
182    index = index + 1
183  return output_name
184