github.com/grumpyhome/grumpy@v0.3.1-0.20201208125205-7b775405bdf1/grumpy-runtime-src/third_party/stdlib/unittest_util.py (about)

     1  """Various utility functions."""
     2  # from collections import namedtuple, OrderedDict
     3  
     4  import operator
     5  _itemgetter = operator.itemgetter
     6  _property = property
     7  _tuple = tuple
     8  
     9  __unittest = True
    10  
    11  _MAX_LENGTH = 80
    12  def safe_repr(obj, short=False):
    13      try:
    14          result = repr(obj)
    15      except Exception:
    16          result = object.__repr__(obj)
    17      if not short or len(result) < _MAX_LENGTH:
    18          return result
    19      return result[:_MAX_LENGTH] + ' [truncated]...'
    20  
    21  
    22  def strclass(cls):
    23      return "%s.%s" % (cls.__module__, cls.__name__)
    24  
    25  def sorted_list_difference(expected, actual):
    26      """Finds elements in only one or the other of two, sorted input lists.
    27  
    28      Returns a two-element tuple of lists.    The first list contains those
    29      elements in the "expected" list but not in the "actual" list, and the
    30      second contains those elements in the "actual" list but not in the
    31      "expected" list.    Duplicate elements in either input list are ignored.
    32      """
    33      i = j = 0
    34      missing = []
    35      unexpected = []
    36      while True:
    37          try:
    38              e = expected[i]
    39              a = actual[j]
    40              if e < a:
    41                  missing.append(e)
    42                  i += 1
    43                  while expected[i] == e:
    44                      i += 1
    45              elif e > a:
    46                  unexpected.append(a)
    47                  j += 1
    48                  while actual[j] == a:
    49                      j += 1
    50              else:
    51                  i += 1
    52                  try:
    53                      while expected[i] == e:
    54                          i += 1
    55                  finally:
    56                      j += 1
    57                      while actual[j] == a:
    58                          j += 1
    59          except IndexError:
    60              missing.extend(expected[i:])
    61              unexpected.extend(actual[j:])
    62              break
    63      return missing, unexpected
    64  
    65  
    66  def unorderable_list_difference(expected, actual, ignore_duplicate=False):
    67      """Same behavior as sorted_list_difference but
    68      for lists of unorderable items (like dicts).
    69  
    70      As it does a linear search per item (remove) it
    71      has O(n*n) performance.
    72      """
    73      missing = []
    74      unexpected = []
    75      while expected:
    76          item = expected.pop()
    77          try:
    78              actual.remove(item)
    79          except ValueError:
    80              missing.append(item)
    81          if ignore_duplicate:
    82              for lst in expected, actual:
    83                  try:
    84                      while True:
    85                          lst.remove(item)
    86                  except ValueError:
    87                      pass
    88      if ignore_duplicate:
    89          while actual:
    90              item = actual.pop()
    91              unexpected.append(item)
    92              try:
    93                  while True:
    94                      actual.remove(item)
    95              except ValueError:
    96                  pass
    97          return missing, unexpected
    98  
    99      # anything left in actual is unexpected
   100      return missing, actual
   101  
   102  # _Mismatch = namedtuple('Mismatch', 'actual expected value')
   103  class _Mismatch(tuple):
   104      'Mismatch(actual, expected, value)'
   105  
   106      __slots__ = ()
   107  
   108      _fields = ('actual', 'expected', 'value')
   109  
   110      def __new__(_cls, actual, expected, value):
   111          'Create new instance of Mismatch(actual, expected, value)'
   112          return _tuple.__new__(_cls, (actual, expected, value))
   113  
   114      # @classmethod
   115      def _make(cls, iterable, new=tuple.__new__, len=len):
   116          'Make a new Mismatch object from a sequence or iterable'
   117          result = new(cls, iterable)
   118          if len(result) != 3:
   119              raise TypeError('Expected 3 arguments, got %d' % len(result))
   120          return result
   121      _make = classmethod(_make)
   122  
   123      def __repr__(self):
   124          'Return a nicely formatted representation string'
   125          return 'Mismatch(actual=%r, expected=%r, value=%r)' % self
   126  
   127      def _asdict(self):
   128          'Return a new OrderedDict which maps field names to their values'
   129          # return OrderedDict(zip(self._fields, self))
   130          return dict(zip(self._fields, self))
   131  
   132      def _replace(_self, **kwds):
   133          'Return a new Mismatch object replacing specified fields with new values'
   134          result = _self._make(map(kwds.pop, ('actual', 'expected', 'value'), _self))
   135          if kwds:
   136              raise ValueError('Got unexpected field names: %r' % kwds.keys())
   137          return result
   138  
   139      def __getnewargs__(self):
   140          'Return self as a plain tuple.  Used by copy and pickle.'
   141          return tuple(self)
   142  
   143      __dict__ = _property(_asdict)
   144  
   145      def __getstate__(self):
   146          'Exclude the OrderedDict from pickling'
   147          pass
   148  
   149      actual = _property(_itemgetter(0), doc='Alias for field number 0')
   150  
   151      expected = _property(_itemgetter(1), doc='Alias for field number 1')
   152  
   153      value = _property(_itemgetter(2), doc='Alias for field number 2')
   154  
   155  def _count_diff_all_purpose(actual, expected):
   156      'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
   157      # elements need not be hashable
   158      s, t = list(actual), list(expected)
   159      m, n = len(s), len(t)
   160      NULL = object()
   161      result = []
   162      for i, elem in enumerate(s):
   163          if elem is NULL:
   164              continue
   165          cnt_s = cnt_t = 0
   166          for j in range(i, m):
   167              if s[j] == elem:
   168                  cnt_s += 1
   169                  s[j] = NULL
   170          for j, other_elem in enumerate(t):
   171              if other_elem == elem:
   172                  cnt_t += 1
   173                  t[j] = NULL
   174          if cnt_s != cnt_t:
   175              diff = _Mismatch(cnt_s, cnt_t, elem)
   176              result.append(diff)
   177  
   178      for i, elem in enumerate(t):
   179          if elem is NULL:
   180              continue
   181          cnt_t = 0
   182          for j in range(i, n):
   183              if t[j] == elem:
   184                  cnt_t += 1
   185                  t[j] = NULL
   186          diff = _Mismatch(0, cnt_t, elem)
   187          result.append(diff)
   188      return result
   189  
   190  def _ordered_count(iterable):
   191      'Return dict of element counts, in the order they were first seen'
   192      c = {} #OrderedDict()
   193      for elem in iterable:
   194          c[elem] = c.get(elem, 0) + 1
   195      return c
   196  
   197  def _count_diff_hashable(actual, expected):
   198      'Returns list of (cnt_act, cnt_exp, elem) triples where the counts differ'
   199      # elements must be hashable
   200      s, t = _ordered_count(actual), _ordered_count(expected)
   201      result = []
   202      for elem, cnt_s in s.items():
   203          cnt_t = t.get(elem, 0)
   204          if cnt_s != cnt_t:
   205              diff = _Mismatch(cnt_s, cnt_t, elem)
   206              result.append(diff)
   207      for elem, cnt_t in t.items():
   208          if elem not in s:
   209              diff = _Mismatch(0, cnt_t, elem)
   210              result.append(diff)
   211      return result