<commit_before># Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import inspect
import os
import re

from collections import namedtuple

# These have to do with deriving classes from namedtuple return values.
# Pylint can't tell that namedtuple returns a new-style type() object.
#
# "no __init__ method" pylint: disable=W0232
# "use of super on an old style class" pylint: disable=E1002

UnknownError = namedtuple('UnknownError', 'message')
NoMatchingTestsError = namedtuple('NoMatchingTestsError', '')
Result = namedtuple('Result', 'data')
MultiResult = namedtuple('MultiResult', 'results')

class ResultStageAbort(Exception):
  pass


class Failure(object):
  pass


class TestError(namedtuple('TestError', 'test message log_lines')):
  def __new__(cls, test, message, log_lines=()):
    return super(TestError, cls).__new__(cls, test, message, log_lines)


class Bind(namedtuple('_Bind', 'loc name')):
  """A placeholder argument for a FuncCall.

  A Bind instance either indicates a 0-based index into the args argument,
  or a name in kwargs when calling .bind().
  """

  def __new__(cls, loc=None, name=None):
    """Either loc or name must be defined."""
    assert ((loc is None and isinstance(name, str)) or
            (name is None and 0 <= loc))
    return super(Bind, cls).__new__(cls, loc, name)

  def bind(self, args=(), kwargs=None):
    """Return the appropriate value for this Bind when binding against args and
    kwargs.

    >>> b = Bind(2)
    >>> # A bind will return itself if a matching arg value isn't present
    >>> b.bind(['cat'], {'arg': 100}) is b
    True
    >>> # Otherwise the matching value is returned
    >>> v = 'money'
    >>> b.bind(['happy', 'cool', v]) is v
    True
    >>> b2 = Bind(name='cat')
    >>> b2.bind((), {'cat': 'cool'})
    'cool'
    """
    kwargs = kwargs or {}
    if self.loc is not None:
      v = args[self.loc:self.loc+1]
      return self if not v else v[0]
    else:
      return kwargs.get(self.name, self)

  @staticmethod
  def maybe_bind(value, args, kwargs):
    """Helper which binds value with (args, kwargs) if value is a Bind."""
    return value.bind(args, kwargs) if isinstance(value, Bind) else value


class FuncCall(object):
  def __init__(self, func, *args, **kwargs):
    """FuncCall is a trivial single-function closure which is pickleable.

    This assumes that func, args and kwargs are all pickleable.

    When constructing the FuncCall, you may also set any positional or named
    argument to a Bind instance. A FuncCall can then be bound with the
    .bind(*args, **kwargs) method, and finally called by invoking func_call().

    A FuncCall may also be directly invoked with func_call(*args, **kwargs),
    which is equivalent to func_call.bind(*args, **kwargs)().

    Invoking a FuncCall with an unbound Bind instance is an error.

    >>> def func(alpha, beta=None, gamma=None):
    ...   return '%s-%s-%s' % (alpha, beta, gamma)
    >>> f = FuncCall(func, Bind(2), beta=Bind(name='context'), gamma=Bind(2))
    >>> # the first arg and the named arg 'gamma' are bound to index 2 of args.
    >>> # the named arg 'beta' is bound to the named kwarg 'context'.
    >>> #
    >>> # The FuncCall is equivalent to (py3 pattern syntax):
    >>> #   UNSET = object()
    >>> #   def f(_, _, arg1, *_, context=UNSET, **_):
    >>> #      assert pickle is not UNSET
    >>> #      return func(arg1, beta=context, gamma=arg1)
    >>> bound = f.bind('foo', 'bar', 'baz', context=100, extra=None)
    >>> # At this point, bound is a FuncCall with no Bind arguments, and can be
    >>> # invoked. This would be equivalent to:
    >>> #   func('baz', beta=100, gamma='baz')
    >>> bound()
    baz-100-baz

    Unused arguments in the .bind() call are ignored, which allows you to build
    value-agnostic invocations to FuncCall.bind().
    """
    self._func = func
    self._args = args
    self._kwargs = kwargs
    self._fully_bound = None

  # "access to a protected member" pylint: disable=W0212
  func = property(lambda self: self._func)
  args = property(lambda self: self._args)
  kwargs = property(lambda self: self._kwargs)

  @property
  def fully_bound(self):
    if self._fully_bound is None:
      self._fully_bound = not (
          any(isinstance(v, Bind) for v in self._args) or
          any(isinstance(v, Bind) for v in self._kwargs.itervalues())
      )
    return self._fully_bound

  def bind(self, *args, **kwargs):
    if self.fully_bound or not (args or kwargs):
      return self

    new = FuncCall(self._func)
    new._args = [Bind.maybe_bind(a, args, kwargs) for a in self.args]
    new._kwargs = {k: Bind.maybe_bind(v, args, kwargs)
                   for k, v in self.kwargs.iteritems()}
    return new

  def __call__(self, *args, **kwargs):
    f = self.bind(args, kwargs)
    assert f.fully_bound
    return f.func(*f.args, **f.kwargs)

  def __repr__(self):
    return 'FuncCall(%r, *%r, **%r)' % (self.func, self.args, self.kwargs)


_Test = namedtuple(
    'Test', 'name func_call expect_dir expect_base ext covers breakpoints')

class Test(_Test):
  TEST_COVERS_MATCH = re.compile('.*/test/([^/]*)_test\.py$')

  def __new__(cls, name, func_call, expect_dir=None, expect_base=None,
              ext='json', covers=None, breakpoints=None, break_funcs=()):
    """Create a new test.

    @param name: The name of the test. Will be used as the default expect_base

    @param func_call: A FuncCall object

    @param expect_dir: The directory which holds the expectation file for this
                       Test.
    @param expect_base: The basename (without extension) of the expectation
                        file. Defaults to |name|.
    @param ext: The extension of the expectation file. Affects the serializer
                used to write the expectations to disk. Valid values are
                'json' and 'yaml' (Keys in SERIALIZERS).
    @param covers: A list of coverage file patterns to include for this Test.
                   By default, a Test covers the file in which its function
                   was defined, as well as the source file matching the test
                   according to TEST_COVERS_MATCH.

    @param breakpoints: A list of (path, lineno, func_name) tuples. These will
                        turn into breakpoints when the tests are run in 'debug'
                        mode. See |break_funcs| for an easier way to set this.
    @param break_funcs: A list of functions for which to set breakpoints.
    """
    breakpoints = breakpoints or []
    if not breakpoints or break_funcs:
      for f in (break_funcs or (func_call.func,)):
        if hasattr(f, 'im_func'):
          f = f.im_func
        breakpoints.append((f.func_code.co_filename,
                            f.func_code.co_firstlineno,
                            f.func_code.co_name))

    return super(Test, cls).__new__(cls, name, func_call, expect_dir,
                                    expect_base, ext, covers, breakpoints)

  def coverage_includes(self):
    if self.covers is not None:
      return self.covers

    test_file = inspect.getabsfile(self.func_call.func)
    covers = [test_file]<commit_msg>Bug fix for expect tests handling of trailing slashes on directories.<commit_after># Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import inspect
import os
import re

from collections import namedtuple

# These have to do with deriving classes from namedtuple return values.
# Pylint can't tell that namedtuple returns a new-style type() object.
#
# "no __init__ method" pylint: disable=W0232
# "use of super on an old style class" pylint: disable=E1002

UnknownError = namedtuple('UnknownError', 'message')
NoMatchingTestsError = namedtuple('NoMatchingTestsError', '')
Result = namedtuple('Result', 'data')
MultiResult = namedtuple('MultiResult', 'results')

class ResultStageAbort(Exception):
  pass


class Failure(object):
  pass


class TestError(namedtuple('TestError', 'test message log_lines')):
  def __new__(cls, test, message, log_lines=()):
    return super(TestError, cls).__new__(cls, test, message, log_lines)


class Bind(namedtuple('_Bind', 'loc name')):
  """A placeholder argument for a FuncCall.

  A Bind instance either indicates a 0-based index into the args argument,
  or a name in kwargs when calling .bind().
  """

  def __new__(cls, loc=None, name=None):
    """Either loc or name must be defined."""
    assert ((loc is None and isinstance(name, str)) or
            (name is None and 0 <= loc))
    return super(Bind, cls).__new__(cls, loc, name)

  def bind(self, args=(), kwargs=None):
    """Return the appropriate value for this Bind when binding against args and
    kwargs.

    >>> b = Bind(2)
    >>> # A bind will return itself if a matching arg value isn't present
    >>> b.bind(['cat'], {'arg': 100}) is b
    True
    >>> # Otherwise the matching value is returned
    >>> v = 'money'
    >>> b.bind(['happy', 'cool', v]) is v
    True
    >>> b2 = Bind(name='cat')
    >>> b2.bind((), {'cat': 'cool'})
    'cool'
    """
    kwargs = kwargs or {}
    if self.loc is not None:
      v = args[self.loc:self.loc+1]
      return self if not v else v[0]
    else:
      return kwargs.get(self.name, self)

  @staticmethod
  def maybe_bind(value, args, kwargs):
    """Helper which binds value with (args, kwargs) if value is a Bind."""
    return value.bind(args, kwargs) if isinstance(value, Bind) else value


class FuncCall(object):
  def __init__(self, func, *args, **kwargs):
    """FuncCall is a trivial single-function closure which is pickleable.

    This assumes that func, args and kwargs are all pickleable.

    When constructing the FuncCall, you may also set any positional or named
    argument to a Bind instance. A FuncCall can then be bound with the
    .bind(*args, **kwargs) method, and finally called by invoking func_call().

    A FuncCall may also be directly invoked with func_call(*args, **kwargs),
    which is equivalent to func_call.bind(*args, **kwargs)().

    Invoking a FuncCall with an unbound Bind instance is an error.

    >>> def func(alpha, beta=None, gamma=None):
    ...   return '%s-%s-%s' % (alpha, beta, gamma)
    >>> f = FuncCall(func, Bind(2), beta=Bind(name='context'), gamma=Bind(2))
    >>> # the first arg and the named arg 'gamma' are bound to index 2 of args.
    >>> # the named arg 'beta' is bound to the named kwarg 'context'.
    >>> #
    >>> # The FuncCall is equivalent to (py3 pattern syntax):
    >>> #   UNSET = object()
    >>> #   def f(_, _, arg1, *_, context=UNSET, **_):
    >>> #      assert pickle is not UNSET
    >>> #      return func(arg1, beta=context, gamma=arg1)
    >>> bound = f.bind('foo', 'bar', 'baz', context=100, extra=None)
    >>> # At this point, bound is a FuncCall with no Bind arguments, and can be
    >>> # invoked. This would be equivalent to:
    >>> #   func('baz', beta=100, gamma='baz')
    >>> bound()
    baz-100-baz

    Unused arguments in the .bind() call are ignored, which allows you to build
    value-agnostic invocations to FuncCall.bind().
    """
    self._func = func
    self._args = args
    self._kwargs = kwargs
    self._fully_bound = None

  # "access to a protected member" pylint: disable=W0212
  func = property(lambda self: self._func)
  args = property(lambda self: self._args)
  kwargs = property(lambda self: self._kwargs)

  @property
  def fully_bound(self):
    if self._fully_bound is None:
      self._fully_bound = not (
          any(isinstance(v, Bind) for v in self._args) or
          any(isinstance(v, Bind) for v in self._kwargs.itervalues())
      )
    return self._fully_bound

  def bind(self, *args, **kwargs):
    if self.fully_bound or not (args or kwargs):
      return self

    new = FuncCall(self._func)
    new._args = [Bind.maybe_bind(a, args, kwargs) for a in self.args]
    new._kwargs = {k: Bind.maybe_bind(v, args, kwargs)
                   for k, v in self.kwargs.iteritems()}
    return new

  def __call__(self, *args, **kwargs):
    f = self.bind(args, kwargs)
    assert f.fully_bound
    return f.func(*f.args, **f.kwargs)

  def __repr__(self):
    return 'FuncCall(%r, *%r, **%r)' % (self.func, self.args, self.kwargs)


_Test = namedtuple(
    'Test', 'name func_call expect_dir expect_base ext covers breakpoints')

class Test(_Test):
  TEST_COVERS_MATCH = re.compile('.*/test/([^/]*)_test\.py$')

  def __new__(cls, name, func_call, expect_dir=None, expect_base=None,
              ext='json', covers=None, breakpoints=None, break_funcs=()):
    """Create a new test.

    @param name: The name of the test. Will be used as the default expect_base

    @param func_call: A FuncCall object

    @param expect_dir: The directory which holds the expectation file for this
                       Test.
    @param expect_base: The basename (without extension) of the expectation
                        file. Defaults to |name|.
    @param ext: The extension of the expectation file. Affects the serializer
                used to write the expectations to disk. Valid values are
                'json' and 'yaml' (Keys in SERIALIZERS).
    @param covers: A list of coverage file patterns to include for this Test.
                   By default, a Test covers the file in which its function
                   was defined, as well as the source file matching the test
                   according to TEST_COVERS_MATCH.

    @param breakpoints: A list of (path, lineno, func_name) tuples. These will
                        turn into breakpoints when the tests are run in 'debug'
                        mode. See |break_funcs| for an easier way to set this.
    @param break_funcs: A list of functions for which to set breakpoints.
    """
    breakpoints = breakpoints or []
    if not breakpoints or break_funcs:
      for f in (break_funcs or (func_call.func,)):
        if hasattr(f, 'im_func'):
          f = f.im_func
        breakpoints.append((f.func_code.co_filename,
                            f.func_code.co_firstlineno,
                            f.func_code.co_name))

    expect_dir = expect_dir.rstrip('/')
    return super(Test, cls).__new__(cls, name, func_call, expect_dir,
                                    expect_base, ext, covers, breakpoints)

  def coverage_includes(self):
    if self.covers is not None:
      return self.covers

    test_file = inspect.getabsfile(self.func_call.func)
    covers = [test_file]
{'fraction_non_alphanumeric': 0.11278401313988502, 'fraction_numerical': 0.005748699698877635, 'mean_word_length': 2.912449799196787, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 141, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2888436', 'n_tokens_mistral': 4513, 'n_tokens_neox': 4356, 'n_words': 1609}