15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)# found in the LICENSE file.
45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)# pylint: disable=W0212
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import functools
75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import inspect
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import types
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def Cache(obj):
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Decorator for caching read-only properties.
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Example usage (always returns the same Foo instance):
155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Cache
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def CreateFoo():
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return Foo()
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  If CreateFoo() accepts parameters, a separate cached value is maintained
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for each unique parameter combination.
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  Cached methods maintain their cache for the lifetime of the /instance/, while
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  cached functions maintain their cache for the lifetime of the /module/.
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  """
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  @functools.wraps(obj)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def Cacher(*args, **kwargs):
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cacher = args[0] if inspect.getargspec(obj).args[:1] == ['self'] else obj
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    cacher.__cache = cacher.__cache if hasattr(cacher, '__cache') else {}
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    key = str(obj) + str(args) + str(kwargs)
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if key not in cacher.__cache:
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      cacher.__cache[key] = obj(*args, **kwargs)
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return cacher.__cache[key]
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return Cacher
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def Disabled(*args):
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Decorator for disabling tests/benchmarks.
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  May be used without args to unconditionally disable:
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Disabled  # Unconditionally disabled.
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  If args are given, the test will be disabled if ANY of the args match the
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  browser type, OS name or OS version:
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Disabled('canary')        # Disabled for canary browsers
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Disabled('win')           # Disabled on Windows.
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Disabled('win', 'linux')  # Disabled on both Windows and Linux.
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Disabled('mavericks')     # Disabled on Mac Mavericks (10.9) only.
485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """
495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def _Disabled(func):
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not isinstance(func, types.FunctionType):
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      func._disabled_strings = disabled_strings
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return func
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @functools.wraps(func)
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def wrapper(*args, **kwargs):
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      func(*args, **kwargs)
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    wrapper._disabled_strings = disabled_strings
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return wrapper
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if len(args) == 1 and callable(args[0]):
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    disabled_strings = []
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return _Disabled(args[0])
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  disabled_strings = list(args)
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for disabled_string in disabled_strings:
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # TODO(tonyg): Validate that these strings are recognized.
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert isinstance(disabled_string, str), '@Disabled accepts a list of strs'
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return _Disabled
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)def Enabled(*args):
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """Decorator for enabling tests/benchmarks.
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  The test will be enabled if ANY of the args match the browser type, OS name
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  or OS version:
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Enabled('canary')        # Enabled only for canary browsers
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Enabled('win')           # Enabled only on Windows.
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Enabled('win', 'linux')  # Enabled only on Windows or Linux.
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Enabled('mavericks')     # Enabled only on Mac Mavericks (10.9).
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def _Enabled(func):
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not isinstance(func, types.FunctionType):
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      func._enabled_strings = enabled_strings
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return func
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @functools.wraps(func)
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def wrapper(*args, **kwargs):
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      func(*args, **kwargs)
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    wrapper._enabled_strings = enabled_strings
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return wrapper
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  assert args and not callable(args[0]), '@Enabled requires argumentas'
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  enabled_strings = list(args)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for enabled_string in enabled_strings:
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    # TODO(tonyg): Validate that these strings are recognized.
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    assert isinstance(enabled_string, str), '@Enabled accepts a list of strs'
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return _Enabled
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
95116680a4aac90f2aa7413d9095a592090648e557Ben Murdochdef IsEnabled(test, possible_browser):
96116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  """Returns True iff |test| is enabled given the |possible_browser|.
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Use to respect the @Enabled / @Disabled decorators.
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Args:
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    test: A function or class that may contain _disabled_strings and/or
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          _enabled_strings attributes.
103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    possible_browser: A PossibleBrowser to check whether |test| may run against.
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  """
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  platform_attributes = [a.lower() for a in [
106116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      possible_browser.browser_type,
107116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      possible_browser.platform.GetOSName(),
108116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      possible_browser.platform.GetOSVersionName(),
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ]]
110116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if possible_browser.supports_tab_control:
111116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    platform_attributes.append('has tabs')
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if hasattr(test, '__name__'):
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    name = test.__name__
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  elif hasattr(test, '__class__'):
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    name = test.__class__.__name__
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  else:
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    name = str(test)
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if hasattr(test, '_disabled_strings'):
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    disabled_strings = test._disabled_strings
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not disabled_strings:
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return False  # No arguments to @Disabled means always disable.
1245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for disabled_string in disabled_strings:
1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if disabled_string in platform_attributes:
1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        print (
1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            'Skipping %s because it is disabled for %s. '
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            'You are running %s.' % (name,
1295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                     ' and '.join(disabled_strings),
1305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                     ' '.join(platform_attributes)))
1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return False
1325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if hasattr(test, '_enabled_strings'):
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    enabled_strings = test._enabled_strings
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if not enabled_strings:
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return True  # No arguments to @Enabled means always enable.
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    for enabled_string in enabled_strings:
1385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      if enabled_string in platform_attributes:
1395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return True
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    print (
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        'Skipping %s because it is only enabled for %s. '
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        'You are running %s.' % (name,
143a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 ' or '.join(enabled_strings),
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                 ' '.join(platform_attributes)))
1455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return False
1465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return True
148