protobuf/python/google/protobuf/internal/_parameterized.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 #
3 # Protocol Buffers - Google's data interchange format
4 # Copyright 2008 Google Inc. All rights reserved.
5 # https://developers.google.com/protocol-buffers/
6 #
7 # Redistribution and use in source and binary forms, with or without
8 # modification, are permitted provided that the following conditions are
9 # met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above
14 # copyright notice, this list of conditions and the following disclaimer
15 # in the documentation and/or other materials provided with the
16 # distribution.
17 # * Neither the name of Google Inc. nor the names of its
18 # contributors may be used to endorse or promote products derived from
19 # this software without specific prior written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 
33 """Adds support for parameterized tests to Python's unittest TestCase class.
34 
35 A parameterized test is a method in a test case that is invoked with different
36 argument tuples.
37 
38 A simple example:
39 
40  class AdditionExample(parameterized.TestCase):
41  @parameterized.parameters(
42  (1, 2, 3),
43  (4, 5, 9),
44  (1, 1, 3))
45  def testAddition(self, op1, op2, result):
46  self.assertEqual(result, op1 + op2)
47 
48 
49 Each invocation is a separate test case and properly isolated just
50 like a normal test method, with its own setUp/tearDown cycle. In the
51 example above, there are three separate testcases, one of which will
52 fail due to an assertion error (1 + 1 != 3).
53 
54 Parameters for individual test cases can be tuples (with positional parameters)
55 or dictionaries (with named parameters):
56 
57  class AdditionExample(parameterized.TestCase):
58  @parameterized.parameters(
59  {'op1': 1, 'op2': 2, 'result': 3},
60  {'op1': 4, 'op2': 5, 'result': 9},
61  )
62  def testAddition(self, op1, op2, result):
63  self.assertEqual(result, op1 + op2)
64 
65 If a parameterized test fails, the error message will show the
66 original test name (which is modified internally) and the arguments
67 for the specific invocation, which are part of the string returned by
68 the shortDescription() method on test cases.
69 
70 The id method of the test, used internally by the unittest framework,
71 is also modified to show the arguments. To make sure that test names
72 stay the same across several invocations, object representations like
73 
74  >>> class Foo(object):
75  ... pass
76  >>> repr(Foo())
77  '<__main__.Foo object at 0x23d8610>'
78 
79 are turned into '<__main__.Foo>'. For even more descriptive names,
80 especially in test logs, you can use the named_parameters decorator. In
81 this case, only tuples are supported, and the first parameters has to
82 be a string (or an object that returns an apt name when converted via
83 str()):
84 
85  class NamedExample(parameterized.TestCase):
86  @parameterized.named_parameters(
87  ('Normal', 'aa', 'aaa', True),
88  ('EmptyPrefix', '', 'abc', True),
89  ('BothEmpty', '', '', True))
90  def testStartsWith(self, prefix, string, result):
91  self.assertEqual(result, strings.startswith(prefix))
92 
93 Named tests also have the benefit that they can be run individually
94 from the command line:
95 
96  $ testmodule.py NamedExample.testStartsWithNormal
97  .
98  --------------------------------------------------------------------
99  Ran 1 test in 0.000s
100 
101  OK
102 
103 Parameterized Classes
104 =====================
105 If invocation arguments are shared across test methods in a single
106 TestCase class, instead of decorating all test methods
107 individually, the class itself can be decorated:
108 
109  @parameterized.parameters(
110  (1, 2, 3)
111  (4, 5, 9))
112  class ArithmeticTest(parameterized.TestCase):
113  def testAdd(self, arg1, arg2, result):
114  self.assertEqual(arg1 + arg2, result)
115 
116  def testSubtract(self, arg2, arg2, result):
117  self.assertEqual(result - arg1, arg2)
118 
119 Inputs from Iterables
120 =====================
121 If parameters should be shared across several test cases, or are dynamically
122 created from other sources, a single non-tuple iterable can be passed into
123 the decorator. This iterable will be used to obtain the test cases:
124 
125  class AdditionExample(parameterized.TestCase):
126  @parameterized.parameters(
127  c.op1, c.op2, c.result for c in testcases
128  )
129  def testAddition(self, op1, op2, result):
130  self.assertEqual(result, op1 + op2)
131 
132 
133 Single-Argument Test Methods
134 ============================
135 If a test method takes only one argument, the single argument does not need to
136 be wrapped into a tuple:
137 
138  class NegativeNumberExample(parameterized.TestCase):
139  @parameterized.parameters(
140  -1, -3, -4, -5
141  )
142  def testIsNegative(self, arg):
143  self.assertTrue(IsNegative(arg))
144 """
145 
146 __author__ = 'tmarek@google.com (Torsten Marek)'
147 
148 import functools
149 import re
150 import types
151 try:
152  import unittest2 as unittest
153 except ImportError:
154  import unittest
155 import uuid
156 
157 try:
158  # Since python 3
159  import collections.abc as collections_abc
160 except ImportError:
161  # Won't work after python 3.8
162  import collections as collections_abc
163 
164 ADDR_RE = re.compile(r'<([a-zA-Z0-9_\-\.]+) object at 0x[a-fA-F0-9]+>')
165 _SEPARATOR = uuid.uuid1().hex
166 _FIRST_ARG = object()
167 _ARGUMENT_REPR = object()
168 
169 
170 def _CleanRepr(obj):
171  return ADDR_RE.sub(r'<\1>', repr(obj))
172 
173 
174 # Helper function formerly from the unittest module, removed from it in
175 # Python 2.7.
176 def _StrClass(cls):
177  return '%s.%s' % (cls.__module__, cls.__name__)
178 
179 
180 def _NonStringIterable(obj):
181  return (isinstance(obj, collections_abc.Iterable) and
182  not isinstance(obj, str))
183 
184 
185 def _FormatParameterList(testcase_params):
186  if isinstance(testcase_params, collections_abc.Mapping):
187  return ', '.join('%s=%s' % (argname, _CleanRepr(value))
188  for argname, value in testcase_params.items())
189  elif _NonStringIterable(testcase_params):
190  return ', '.join(map(_CleanRepr, testcase_params))
191  else:
192  return _FormatParameterList((testcase_params,))
193 
194 
195 class _ParameterizedTestIter(object):
196  """Callable and iterable class for producing new test cases."""
197 
198  def __init__(self, test_method, testcases, naming_type):
199  """Returns concrete test functions for a test and a list of parameters.
200 
201  The naming_type is used to determine the name of the concrete
202  functions as reported by the unittest framework. If naming_type is
203  _FIRST_ARG, the testcases must be tuples, and the first element must
204  have a string representation that is a valid Python identifier.
205 
206  Args:
207  test_method: The decorated test method.
208  testcases: (list of tuple/dict) A list of parameter
209  tuples/dicts for individual test invocations.
210  naming_type: The test naming type, either _NAMED or _ARGUMENT_REPR.
211  """
212  self._test_method = test_method
213  self.testcases = testcases
214  self._naming_type = naming_type
215 
216  def __call__(self, *args, **kwargs):
217  raise RuntimeError('You appear to be running a parameterized test case '
218  'without having inherited from parameterized.'
219  'TestCase. This is bad because none of '
220  'your test cases are actually being run.')
221 
222  def __iter__(self):
223  test_method = self._test_method
224  naming_type = self._naming_type
225 
226  def MakeBoundParamTest(testcase_params):
227  @functools.wraps(test_method)
228  def BoundParamTest(self):
229  if isinstance(testcase_params, collections_abc.Mapping):
230  test_method(self, **testcase_params)
231  elif _NonStringIterable(testcase_params):
232  test_method(self, *testcase_params)
233  else:
234  test_method(self, testcase_params)
235 
236  if naming_type is _FIRST_ARG:
237  # Signal the metaclass that the name of the test function is unique
238  # and descriptive.
239  BoundParamTest.__x_use_name__ = True
240  BoundParamTest.__name__ += str(testcase_params[0])
241  testcase_params = testcase_params[1:]
242  elif naming_type is _ARGUMENT_REPR:
243  # __x_extra_id__ is used to pass naming information to the __new__
244  # method of TestGeneratorMetaclass.
245  # The metaclass will make sure to create a unique, but nondescriptive
246  # name for this test.
247  BoundParamTest.__x_extra_id__ = '(%s)' % (
248  _FormatParameterList(testcase_params),)
249  else:
250  raise RuntimeError('%s is not a valid naming type.' % (naming_type,))
251 
252  BoundParamTest.__doc__ = '%s(%s)' % (
253  BoundParamTest.__name__, _FormatParameterList(testcase_params))
254  if test_method.__doc__:
255  BoundParamTest.__doc__ += '\n%s' % (test_method.__doc__,)
256  return BoundParamTest
257  return (MakeBoundParamTest(c) for c in self.testcases)
258 
259 
260 def _IsSingletonList(testcases):
261  """True iff testcases contains only a single non-tuple element."""
262  return len(testcases) == 1 and not isinstance(testcases[0], tuple)
263 
264 
265 def _ModifyClass(class_object, testcases, naming_type):
266  assert not getattr(class_object, '_id_suffix', None), (
267  'Cannot add parameters to %s,'
268  ' which already has parameterized methods.' % (class_object,))
269  class_object._id_suffix = id_suffix = {}
270  # We change the size of __dict__ while we iterate over it,
271  # which Python 3.x will complain about, so use copy().
272  for name, obj in class_object.__dict__.copy().items():
273  if (name.startswith(unittest.TestLoader.testMethodPrefix)
274  and isinstance(obj, types.FunctionType)):
275  delattr(class_object, name)
276  methods = {}
278  methods, id_suffix, name,
279  _ParameterizedTestIter(obj, testcases, naming_type))
280  for name, meth in methods.items():
281  setattr(class_object, name, meth)
282 
283 
284 def _ParameterDecorator(naming_type, testcases):
285  """Implementation of the parameterization decorators.
286 
287  Args:
288  naming_type: The naming type.
289  testcases: Testcase parameters.
290 
291  Returns:
292  A function for modifying the decorated object.
293  """
294  def _Apply(obj):
295  if isinstance(obj, type):
296  _ModifyClass(
297  obj,
298  list(testcases) if not isinstance(testcases, collections_abc.Sequence)
299  else testcases,
300  naming_type)
301  return obj
302  else:
303  return _ParameterizedTestIter(obj, testcases, naming_type)
304 
305  if _IsSingletonList(testcases):
306  assert _NonStringIterable(testcases[0]), (
307  'Single parameter argument must be a non-string iterable')
308  testcases = testcases[0]
309 
310  return _Apply
311 
312 
313 def parameters(*testcases): # pylint: disable=invalid-name
314  """A decorator for creating parameterized tests.
315 
316  See the module docstring for a usage example.
317  Args:
318  *testcases: Parameters for the decorated method, either a single
319  iterable, or a list of tuples/dicts/objects (for tests
320  with only one argument).
321 
322  Returns:
323  A test generator to be handled by TestGeneratorMetaclass.
324  """
325  return _ParameterDecorator(_ARGUMENT_REPR, testcases)
326 
327 
328 def named_parameters(*testcases): # pylint: disable=invalid-name
329  """A decorator for creating parameterized tests.
330 
331  See the module docstring for a usage example. The first element of
332  each parameter tuple should be a string and will be appended to the
333  name of the test method.
334 
335  Args:
336  *testcases: Parameters for the decorated method, either a single
337  iterable, or a list of tuples.
338 
339  Returns:
340  A test generator to be handled by TestGeneratorMetaclass.
341  """
342  return _ParameterDecorator(_FIRST_ARG, testcases)
343 
344 
346  """Metaclass for test cases with test generators.
347 
348  A test generator is an iterable in a testcase that produces callables. These
349  callables must be single-argument methods. These methods are injected into
350  the class namespace and the original iterable is removed. If the name of the
351  iterable conforms to the test pattern, the injected methods will be picked
352  up as tests by the unittest framework.
353 
354  In general, it is supposed to be used in conjunction with the
355  parameters decorator.
356  """
357 
358  def __new__(mcs, class_name, bases, dct):
359  dct['_id_suffix'] = id_suffix = {}
360  for name, obj in dct.items():
361  if (name.startswith(unittest.TestLoader.testMethodPrefix) and
362  _NonStringIterable(obj)):
363  iterator = iter(obj)
364  dct.pop(name)
365  _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator)
366 
367  return type.__new__(mcs, class_name, bases, dct)
368 
369 
370 def _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator):
371  """Adds individual test cases to a dictionary.
372 
373  Args:
374  dct: The target dictionary.
375  id_suffix: The dictionary for mapping names to test IDs.
376  name: The original name of the test case.
377  iterator: The iterator generating the individual test cases.
378  """
379  for idx, func in enumerate(iterator):
380  assert callable(func), 'Test generators must yield callables, got %r' % (
381  func,)
382  if getattr(func, '__x_use_name__', False):
383  new_name = func.__name__
384  else:
385  new_name = '%s%s%d' % (name, _SEPARATOR, idx)
386  assert new_name not in dct, (
387  'Name of parameterized test case "%s" not unique' % (new_name,))
388  dct[new_name] = func
389  id_suffix[new_name] = getattr(func, '__x_extra_id__', '')
390 
391 
392 class TestCase(unittest.TestCase):
393  """Base class for test cases using the parameters decorator."""
394  __metaclass__ = TestGeneratorMetaclass
395 
396  def _OriginalName(self):
397  return self._testMethodName.split(_SEPARATOR)[0]
398 
399  def __str__(self):
400  return '%s (%s)' % (self._OriginalName(), _StrClass(self.__class__))
401 
402  def id(self): # pylint: disable=invalid-name
403  """Returns the descriptive ID of the test.
404 
405  This is used internally by the unittesting framework to get a name
406  for the test to be used in reports.
407 
408  Returns:
409  The test id.
410  """
411  return '%s.%s%s' % (_StrClass(self.__class__),
412  self._OriginalName(),
413  self._id_suffix.get(self._testMethodName, ''))
414 
415 
416 def CoopTestCase(other_base_class):
417  """Returns a new base class with a cooperative metaclass base.
418 
419  This enables the TestCase to be used in combination
420  with other base classes that have custom metaclasses, such as
421  mox.MoxTestBase.
422 
423  Only works with metaclasses that do not override type.__new__.
424 
425  Example:
426 
427  import google3
428  import mox
429 
430  from google3.testing.pybase import parameterized
431 
432  class ExampleTest(parameterized.CoopTestCase(mox.MoxTestBase)):
433  ...
434 
435  Args:
436  other_base_class: (class) A test case base class.
437 
438  Returns:
439  A new class object.
440  """
441  metaclass = type(
442  'CoopMetaclass',
443  (other_base_class.__metaclass__,
444  TestGeneratorMetaclass), {})
445  return metaclass(
446  'CoopTestCase',
447  (other_base_class, TestCase), {})
xds_interop_client.str
str
Definition: xds_interop_client.py:487
get
absl::string_view get(const Cont &c)
Definition: abseil-cpp/absl/strings/str_replace_test.cc:185
google::protobuf.internal._parameterized._ParameterizedTestIter.testcases
testcases
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:215
google::protobuf.internal._parameterized._ParameterizedTestIter.__init__
def __init__(self, test_method, testcases, naming_type)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:200
google::protobuf.internal._parameterized.TestGeneratorMetaclass
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:347
google::protobuf.internal._parameterized.parameters
def parameters(*testcases)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:315
google::protobuf.internal._parameterized._ParameterizedTestIter.__iter__
def __iter__(self)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:224
google::protobuf.internal._parameterized._ParameterizedTestIter
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:197
google::protobuf.internal._parameterized.TestCase
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:394
google::protobuf.internal._parameterized.CoopTestCase
def CoopTestCase(other_base_class)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:418
google::protobuf.internal._parameterized._ModifyClass
def _ModifyClass(class_object, testcases, naming_type)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:267
map
zval * map
Definition: php/ext/google/protobuf/encode_decode.c:480
google::protobuf.internal._parameterized._ParameterizedTestIter._test_method
_test_method
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:214
google::protobuf.internal._parameterized._IsSingletonList
def _IsSingletonList(testcases)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:262
google::protobuf.internal._parameterized.TestCase.__str__
def __str__(self)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:401
google::protobuf.internal._parameterized._CleanRepr
def _CleanRepr(obj)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:172
google::protobuf.internal._parameterized._ParameterDecorator
def _ParameterDecorator(naming_type, testcases)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:286
google::protobuf.internal._parameterized._NonStringIterable
def _NonStringIterable(obj)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:182
google::protobuf.internal._parameterized._FormatParameterList
def _FormatParameterList(testcase_params)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:187
google::protobuf.internal._parameterized._ParameterizedTestIter._naming_type
_naming_type
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:216
xds_manager.items
items
Definition: xds_manager.py:55
google::protobuf.internal._parameterized.TestCase.id
def id(self)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:404
google::protobuf.internal._parameterized._StrClass
def _StrClass(cls)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:178
google::protobuf.internal._parameterized.named_parameters
def named_parameters(*testcases)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:330
google::protobuf.internal._parameterized.TestCase._OriginalName
def _OriginalName(self)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:398
google::protobuf.internal._parameterized._ParameterizedTestIter.__call__
def __call__(self, *args, **kwargs)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:218
iter
Definition: test_winkernel.cpp:47
asyncio_get_stats.type
type
Definition: asyncio_get_stats.py:37
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
split
static void split(const char *s, char ***ss, size_t *ns)
Definition: debug/trace.cc:111
google::protobuf.internal._parameterized._UpdateClassDictForParamTestCase
def _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:372
google::protobuf.internal._parameterized.TestGeneratorMetaclass.__new__
def __new__(mcs, class_name, bases, dct)
Definition: bloaty/third_party/protobuf/python/google/protobuf/internal/_parameterized.py:360


grpc
Author(s):
autogenerated on Fri May 16 2025 02:57:38