133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#!/usr/bin/env python
233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright 2012 Google Inc. All Rights Reserved.
333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Licensed under the Apache License, Version 2.0 (the "License");
533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# you may not use this file except in compliance with the License.
633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# You may obtain a copy of the License at
733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#      http://www.apache.org/licenses/LICENSE-2.0
933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
1033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Unless required by applicable law or agreed to in writing, software
1133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# distributed under the License is distributed on an "AS IS" BASIS,
1233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# See the License for the specific language governing permissions and
1433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# limitations under the License.
1533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck"""Miscellaneous utility functions."""
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport inspect
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport logging
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport time
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Recktry:
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # pkg_resources (part of setuptools) is needed when WPR is
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # distributed as a package. (Resources may need to be extracted from
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # the package.)
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  import pkg_resources
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def resource_exists(resource_name):
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return pkg_resources.resource_exists(__name__, resource_name)
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def resource_string(resource_name):
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return pkg_resources.resource_string(__name__, resource_name)
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckexcept ImportError:
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # Import of pkg_resources failed, so fall back to getting resources
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  # from the file system.
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  import os
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def _resource_path(resource_name):
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    _replay_dir = os.path.dirname(os.path.abspath(__file__))
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return os.path.join(_replay_dir, resource_name)
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def resource_exists(resource_name):
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return os.path.exists(_resource_path(resource_name))
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def resource_string(resource_name):
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return open(_resource_path(resource_name)).read()
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckclass TimeoutException(Exception):
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  pass
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef WaitFor(condition, timeout):
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """Waits for up to |timeout| secs for the function |condition| to return True.
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  Polling frequency is (elapsed_time / 10), with a min of .1s and max of 5s.
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  Returns:
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    Result of |condition| function (if present).
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  """
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  min_poll_interval = 0.1
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  max_poll_interval = 5
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  output_interval = 300
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  def GetConditionString():
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if condition.__name__ == '<lambda>':
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      try:
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return inspect.getsource(condition).strip()
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      except IOError:
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        pass
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return condition.__name__
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  start_time = time.time()
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  last_output_time = start_time
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck  while True:
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    res = condition()
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if res:
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      return res
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    now = time.time()
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    elapsed_time = now - start_time
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    last_output_elapsed_time = now - last_output_time
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if elapsed_time > timeout:
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      raise TimeoutException('Timed out while waiting %ds for %s.' %
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                        (timeout, GetConditionString()))
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if last_output_elapsed_time > output_interval:
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      logging.info('Continuing to wait %ds for %s. Elapsed: %ds.',
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                   timeout, GetConditionString(), elapsed_time)
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck      last_output_time = time.time()
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    poll_interval = min(max(elapsed_time / 10., min_poll_interval),
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                        max_poll_interval)
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    time.sleep(poll_interval)
96