Fixes for Python/C++ implementation in open-source:
* Rosy hack doesn't apply (that test should be removed for the open-source release). * Added our own copy of parameterized.py (the open-source version of Google Apputils doesn't contain it). * The C++ Descriptor object didn't implement extension_ranges. * Had to implement a hack around returning EncodeError, to work around the module-loading behavior of the test runner.
This commit is contained in:
parent
ada6556785
commit
0b70a43736
11 changed files with 490 additions and 27 deletions
|
@ -197,7 +197,6 @@ javanano_EXTRA_DIST=
|
|||
python_EXTRA_DIST= \
|
||||
python/google/protobuf/internal/api_implementation.cc \
|
||||
python/google/protobuf/internal/api_implementation.py \
|
||||
python/google/protobuf/internal/api_implementation_default_test.py \
|
||||
python/google/protobuf/internal/containers.py \
|
||||
python/google/protobuf/internal/cpp_message.py \
|
||||
python/google/protobuf/internal/decoder.py \
|
||||
|
@ -221,6 +220,7 @@ python_EXTRA_DIST= \
|
|||
python/google/protobuf/internal/more_extensions.proto \
|
||||
python/google/protobuf/internal/more_extensions_dynamic.proto \
|
||||
python/google/protobuf/internal/more_messages.proto \
|
||||
python/google/protobuf/internal/_parameterized.py \
|
||||
python/google/protobuf/internal/proto_builder_test.py \
|
||||
python/google/protobuf/internal/python_message.py \
|
||||
python/google/protobuf/internal/reflection_test.py \
|
||||
|
@ -242,16 +242,17 @@ python_EXTRA_DIST= \
|
|||
python/google/protobuf/pyext/cpp_message.py \
|
||||
python/google/protobuf/pyext/descriptor.h \
|
||||
python/google/protobuf/pyext/descriptor.cc \
|
||||
python/google/protobuf/pyext/descriptor_cpp2_test.py \
|
||||
python/google/protobuf/pyext/descriptor_pool.h \
|
||||
python/google/protobuf/pyext/descriptor_pool.cc \
|
||||
python/google/protobuf/pyext/descriptor_containers.h \
|
||||
python/google/protobuf/pyext/descriptor_containers.cc \
|
||||
python/google/protobuf/pyext/extension_dict.h \
|
||||
python/google/protobuf/pyext/extension_dict.cc \
|
||||
python/google/protobuf/pyext/message.h \
|
||||
python/google/protobuf/pyext/message.cc \
|
||||
python/google/protobuf/pyext/message_factory_cpp2_test.py \
|
||||
python/google/protobuf/pyext/proto2_api_test.proto \
|
||||
python/google/protobuf/pyext/python.proto \
|
||||
python/google/protobuf/pyext/python_protobuf.h \
|
||||
python/google/protobuf/pyext/reflection_cpp2_generated_test.py \
|
||||
python/google/protobuf/pyext/repeated_composite_container.h \
|
||||
python/google/protobuf/pyext/repeated_composite_container.cc \
|
||||
python/google/protobuf/pyext/repeated_scalar_container.h \
|
||||
|
|
438
python/google/protobuf/internal/_parameterized.py
Executable file
438
python/google/protobuf/internal/_parameterized.py
Executable file
|
@ -0,0 +1,438 @@
|
|||
#! /usr/bin/python
|
||||
#
|
||||
# Protocol Buffers - Google's data interchange format
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
# https://developers.google.com/protocol-buffers/
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""Adds support for parameterized tests to Python's unittest TestCase class.
|
||||
|
||||
A parameterized test is a method in a test case that is invoked with different
|
||||
argument tuples.
|
||||
|
||||
A simple example:
|
||||
|
||||
class AdditionExample(parameterized.ParameterizedTestCase):
|
||||
@parameterized.Parameters(
|
||||
(1, 2, 3),
|
||||
(4, 5, 9),
|
||||
(1, 1, 3))
|
||||
def testAddition(self, op1, op2, result):
|
||||
self.assertEquals(result, op1 + op2)
|
||||
|
||||
|
||||
Each invocation is a separate test case and properly isolated just
|
||||
like a normal test method, with its own setUp/tearDown cycle. In the
|
||||
example above, there are three separate testcases, one of which will
|
||||
fail due to an assertion error (1 + 1 != 3).
|
||||
|
||||
Parameters for invididual test cases can be tuples (with positional parameters)
|
||||
or dictionaries (with named parameters):
|
||||
|
||||
class AdditionExample(parameterized.ParameterizedTestCase):
|
||||
@parameterized.Parameters(
|
||||
{'op1': 1, 'op2': 2, 'result': 3},
|
||||
{'op1': 4, 'op2': 5, 'result': 9},
|
||||
)
|
||||
def testAddition(self, op1, op2, result):
|
||||
self.assertEquals(result, op1 + op2)
|
||||
|
||||
If a parameterized test fails, the error message will show the
|
||||
original test name (which is modified internally) and the arguments
|
||||
for the specific invocation, which are part of the string returned by
|
||||
the shortDescription() method on test cases.
|
||||
|
||||
The id method of the test, used internally by the unittest framework,
|
||||
is also modified to show the arguments. To make sure that test names
|
||||
stay the same across several invocations, object representations like
|
||||
|
||||
>>> class Foo(object):
|
||||
... pass
|
||||
>>> repr(Foo())
|
||||
'<__main__.Foo object at 0x23d8610>'
|
||||
|
||||
are turned into '<__main__.Foo>'. For even more descriptive names,
|
||||
especially in test logs, you can use the NamedParameters decorator. In
|
||||
this case, only tuples are supported, and the first parameters has to
|
||||
be a string (or an object that returns an apt name when converted via
|
||||
str()):
|
||||
|
||||
class NamedExample(parameterized.ParameterizedTestCase):
|
||||
@parameterized.NamedParameters(
|
||||
('Normal', 'aa', 'aaa', True),
|
||||
('EmptyPrefix', '', 'abc', True),
|
||||
('BothEmpty', '', '', True))
|
||||
def testStartsWith(self, prefix, string, result):
|
||||
self.assertEquals(result, strings.startswith(prefix))
|
||||
|
||||
Named tests also have the benefit that they can be run individually
|
||||
from the command line:
|
||||
|
||||
$ testmodule.py NamedExample.testStartsWithNormal
|
||||
.
|
||||
--------------------------------------------------------------------
|
||||
Ran 1 test in 0.000s
|
||||
|
||||
OK
|
||||
|
||||
Parameterized Classes
|
||||
=====================
|
||||
If invocation arguments are shared across test methods in a single
|
||||
ParameterizedTestCase class, instead of decorating all test methods
|
||||
individually, the class itself can be decorated:
|
||||
|
||||
@parameterized.Parameters(
|
||||
(1, 2, 3)
|
||||
(4, 5, 9))
|
||||
class ArithmeticTest(parameterized.ParameterizedTestCase):
|
||||
def testAdd(self, arg1, arg2, result):
|
||||
self.assertEqual(arg1 + arg2, result)
|
||||
|
||||
def testSubtract(self, arg2, arg2, result):
|
||||
self.assertEqual(result - arg1, arg2)
|
||||
|
||||
Inputs from Iterables
|
||||
=====================
|
||||
If parameters should be shared across several test cases, or are dynamically
|
||||
created from other sources, a single non-tuple iterable can be passed into
|
||||
the decorator. This iterable will be used to obtain the test cases:
|
||||
|
||||
class AdditionExample(parameterized.ParameterizedTestCase):
|
||||
@parameterized.Parameters(
|
||||
c.op1, c.op2, c.result for c in testcases
|
||||
)
|
||||
def testAddition(self, op1, op2, result):
|
||||
self.assertEquals(result, op1 + op2)
|
||||
|
||||
|
||||
Single-Argument Test Methods
|
||||
============================
|
||||
If a test method takes only one argument, the single argument does not need to
|
||||
be wrapped into a tuple:
|
||||
|
||||
class NegativeNumberExample(parameterized.ParameterizedTestCase):
|
||||
@parameterized.Parameters(
|
||||
-1, -3, -4, -5
|
||||
)
|
||||
def testIsNegative(self, arg):
|
||||
self.assertTrue(IsNegative(arg))
|
||||
"""
|
||||
|
||||
__author__ = 'tmarek@google.com (Torsten Marek)'
|
||||
|
||||
import collections
|
||||
import functools
|
||||
import re
|
||||
import types
|
||||
import unittest
|
||||
import uuid
|
||||
|
||||
from google.apputils import basetest
|
||||
|
||||
ADDR_RE = re.compile(r'\<([a-zA-Z0-9_\-\.]+) object at 0x[a-fA-F0-9]+\>')
|
||||
_SEPARATOR = uuid.uuid1().hex
|
||||
_FIRST_ARG = object()
|
||||
_ARGUMENT_REPR = object()
|
||||
|
||||
|
||||
def _CleanRepr(obj):
|
||||
return ADDR_RE.sub(r'<\1>', repr(obj))
|
||||
|
||||
|
||||
# Helper function formerly from the unittest module, removed from it in
|
||||
# Python 2.7.
|
||||
def _StrClass(cls):
|
||||
return '%s.%s' % (cls.__module__, cls.__name__)
|
||||
|
||||
|
||||
def _NonStringIterable(obj):
|
||||
return (isinstance(obj, collections.Iterable) and not
|
||||
isinstance(obj, basestring))
|
||||
|
||||
|
||||
def _FormatParameterList(testcase_params):
|
||||
if isinstance(testcase_params, collections.Mapping):
|
||||
return ', '.join('%s=%s' % (argname, _CleanRepr(value))
|
||||
for argname, value in testcase_params.iteritems())
|
||||
elif _NonStringIterable(testcase_params):
|
||||
return ', '.join(map(_CleanRepr, testcase_params))
|
||||
else:
|
||||
return _FormatParameterList((testcase_params,))
|
||||
|
||||
|
||||
class _ParameterizedTestIter(object):
|
||||
"""Callable and iterable class for producing new test cases."""
|
||||
|
||||
def __init__(self, test_method, testcases, naming_type):
|
||||
"""Returns concrete test functions for a test and a list of parameters.
|
||||
|
||||
The naming_type is used to determine the name of the concrete
|
||||
functions as reported by the unittest framework. If naming_type is
|
||||
_FIRST_ARG, the testcases must be tuples, and the first element must
|
||||
have a string representation that is a valid Python identifier.
|
||||
|
||||
Args:
|
||||
test_method: The decorated test method.
|
||||
testcases: (list of tuple/dict) A list of parameter
|
||||
tuples/dicts for individual test invocations.
|
||||
naming_type: The test naming type, either _NAMED or _ARGUMENT_REPR.
|
||||
"""
|
||||
self._test_method = test_method
|
||||
self.testcases = testcases
|
||||
self._naming_type = naming_type
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
raise RuntimeError('You appear to be running a parameterized test case '
|
||||
'without having inherited from parameterized.'
|
||||
'ParameterizedTestCase. This is bad because none of '
|
||||
'your test cases are actually being run.')
|
||||
|
||||
def __iter__(self):
|
||||
test_method = self._test_method
|
||||
naming_type = self._naming_type
|
||||
|
||||
def MakeBoundParamTest(testcase_params):
|
||||
@functools.wraps(test_method)
|
||||
def BoundParamTest(self):
|
||||
if isinstance(testcase_params, collections.Mapping):
|
||||
test_method(self, **testcase_params)
|
||||
elif _NonStringIterable(testcase_params):
|
||||
test_method(self, *testcase_params)
|
||||
else:
|
||||
test_method(self, testcase_params)
|
||||
|
||||
if naming_type is _FIRST_ARG:
|
||||
# Signal the metaclass that the name of the test function is unique
|
||||
# and descriptive.
|
||||
BoundParamTest.__x_use_name__ = True
|
||||
BoundParamTest.__name__ += str(testcase_params[0])
|
||||
testcase_params = testcase_params[1:]
|
||||
elif naming_type is _ARGUMENT_REPR:
|
||||
# __x_extra_id__ is used to pass naming information to the __new__
|
||||
# method of TestGeneratorMetaclass.
|
||||
# The metaclass will make sure to create a unique, but nondescriptive
|
||||
# name for this test.
|
||||
BoundParamTest.__x_extra_id__ = '(%s)' % (
|
||||
_FormatParameterList(testcase_params),)
|
||||
else:
|
||||
raise RuntimeError('%s is not a valid naming type.' % (naming_type,))
|
||||
|
||||
BoundParamTest.__doc__ = '%s(%s)' % (
|
||||
BoundParamTest.__name__, _FormatParameterList(testcase_params))
|
||||
if test_method.__doc__:
|
||||
BoundParamTest.__doc__ += '\n%s' % (test_method.__doc__,)
|
||||
return BoundParamTest
|
||||
return (MakeBoundParamTest(c) for c in self.testcases)
|
||||
|
||||
|
||||
def _IsSingletonList(testcases):
|
||||
"""True iff testcases contains only a single non-tuple element."""
|
||||
return len(testcases) == 1 and not isinstance(testcases[0], tuple)
|
||||
|
||||
|
||||
def _ModifyClass(class_object, testcases, naming_type):
|
||||
assert not getattr(class_object, '_id_suffix', None), (
|
||||
'Cannot add parameters to %s,'
|
||||
' which already has parameterized methods.' % (class_object,))
|
||||
class_object._id_suffix = id_suffix = {}
|
||||
for name, obj in class_object.__dict__.items():
|
||||
if (name.startswith(unittest.TestLoader.testMethodPrefix)
|
||||
and isinstance(obj, types.FunctionType)):
|
||||
delattr(class_object, name)
|
||||
methods = {}
|
||||
_UpdateClassDictForParamTestCase(
|
||||
methods, id_suffix, name,
|
||||
_ParameterizedTestIter(obj, testcases, naming_type))
|
||||
for name, meth in methods.iteritems():
|
||||
setattr(class_object, name, meth)
|
||||
|
||||
|
||||
def _ParameterDecorator(naming_type, testcases):
|
||||
"""Implementation of the parameterization decorators.
|
||||
|
||||
Args:
|
||||
naming_type: The naming type.
|
||||
testcases: Testcase parameters.
|
||||
|
||||
Returns:
|
||||
A function for modifying the decorated object.
|
||||
"""
|
||||
def _Apply(obj):
|
||||
if isinstance(obj, type):
|
||||
_ModifyClass(
|
||||
obj,
|
||||
list(testcases) if not isinstance(testcases, collections.Sequence)
|
||||
else testcases,
|
||||
naming_type)
|
||||
return obj
|
||||
else:
|
||||
return _ParameterizedTestIter(obj, testcases, naming_type)
|
||||
|
||||
if _IsSingletonList(testcases):
|
||||
assert _NonStringIterable(testcases[0]), (
|
||||
'Single parameter argument must be a non-string iterable')
|
||||
testcases = testcases[0]
|
||||
|
||||
return _Apply
|
||||
|
||||
|
||||
def Parameters(*testcases):
|
||||
"""A decorator for creating parameterized tests.
|
||||
|
||||
See the module docstring for a usage example.
|
||||
Args:
|
||||
*testcases: Parameters for the decorated method, either a single
|
||||
iterable, or a list of tuples/dicts/objects (for tests
|
||||
with only one argument).
|
||||
|
||||
Returns:
|
||||
A test generator to be handled by TestGeneratorMetaclass.
|
||||
"""
|
||||
return _ParameterDecorator(_ARGUMENT_REPR, testcases)
|
||||
|
||||
|
||||
def NamedParameters(*testcases):
|
||||
"""A decorator for creating parameterized tests.
|
||||
|
||||
See the module docstring for a usage example. The first element of
|
||||
each parameter tuple should be a string and will be appended to the
|
||||
name of the test method.
|
||||
|
||||
Args:
|
||||
*testcases: Parameters for the decorated method, either a single
|
||||
iterable, or a list of tuples.
|
||||
|
||||
Returns:
|
||||
A test generator to be handled by TestGeneratorMetaclass.
|
||||
"""
|
||||
return _ParameterDecorator(_FIRST_ARG, testcases)
|
||||
|
||||
|
||||
class TestGeneratorMetaclass(type):
|
||||
"""Metaclass for test cases with test generators.
|
||||
|
||||
A test generator is an iterable in a testcase that produces callables. These
|
||||
callables must be single-argument methods. These methods are injected into
|
||||
the class namespace and the original iterable is removed. If the name of the
|
||||
iterable conforms to the test pattern, the injected methods will be picked
|
||||
up as tests by the unittest framework.
|
||||
|
||||
In general, it is supposed to be used in conjuction with the
|
||||
Parameters decorator.
|
||||
"""
|
||||
|
||||
def __new__(mcs, class_name, bases, dct):
|
||||
dct['_id_suffix'] = id_suffix = {}
|
||||
for name, obj in dct.items():
|
||||
if (name.startswith(unittest.TestLoader.testMethodPrefix) and
|
||||
_NonStringIterable(obj)):
|
||||
iterator = iter(obj)
|
||||
dct.pop(name)
|
||||
_UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator)
|
||||
|
||||
return type.__new__(mcs, class_name, bases, dct)
|
||||
|
||||
|
||||
def _UpdateClassDictForParamTestCase(dct, id_suffix, name, iterator):
|
||||
"""Adds individual test cases to a dictionary.
|
||||
|
||||
Args:
|
||||
dct: The target dictionary.
|
||||
id_suffix: The dictionary for mapping names to test IDs.
|
||||
name: The original name of the test case.
|
||||
iterator: The iterator generating the individual test cases.
|
||||
"""
|
||||
for idx, func in enumerate(iterator):
|
||||
assert callable(func), 'Test generators must yield callables, got %r' % (
|
||||
func,)
|
||||
if getattr(func, '__x_use_name__', False):
|
||||
new_name = func.__name__
|
||||
else:
|
||||
new_name = '%s%s%d' % (name, _SEPARATOR, idx)
|
||||
assert new_name not in dct, (
|
||||
'Name of parameterized test case "%s" not unique' % (new_name,))
|
||||
dct[new_name] = func
|
||||
id_suffix[new_name] = getattr(func, '__x_extra_id__', '')
|
||||
|
||||
|
||||
class ParameterizedTestCase(basetest.TestCase):
|
||||
"""Base class for test cases using the Parameters decorator."""
|
||||
__metaclass__ = TestGeneratorMetaclass
|
||||
|
||||
def _OriginalName(self):
|
||||
return self._testMethodName.split(_SEPARATOR)[0]
|
||||
|
||||
def __str__(self):
|
||||
return '%s (%s)' % (self._OriginalName(), _StrClass(self.__class__))
|
||||
|
||||
def id(self): # pylint: disable=invalid-name
|
||||
"""Returns the descriptive ID of the test.
|
||||
|
||||
This is used internally by the unittesting framework to get a name
|
||||
for the test to be used in reports.
|
||||
|
||||
Returns:
|
||||
The test id.
|
||||
"""
|
||||
return '%s.%s%s' % (_StrClass(self.__class__),
|
||||
self._OriginalName(),
|
||||
self._id_suffix.get(self._testMethodName, ''))
|
||||
|
||||
|
||||
def CoopParameterizedTestCase(other_base_class):
|
||||
"""Returns a new base class with a cooperative metaclass base.
|
||||
|
||||
This enables the ParameterizedTestCase to be used in combination
|
||||
with other base classes that have custom metaclasses, such as
|
||||
mox.MoxTestBase.
|
||||
|
||||
Only works with metaclasses that do not override type.__new__.
|
||||
|
||||
Example:
|
||||
|
||||
import google3
|
||||
import mox
|
||||
|
||||
from google3.testing.pybase import parameterized
|
||||
|
||||
class ExampleTest(parameterized.CoopParameterizedTestCase(mox.MoxTestBase)):
|
||||
...
|
||||
|
||||
Args:
|
||||
other_base_class: (class) A test case base class.
|
||||
|
||||
Returns:
|
||||
A new class object.
|
||||
"""
|
||||
metaclass = type(
|
||||
'CoopMetaclass',
|
||||
(other_base_class.__metaclass__,
|
||||
TestGeneratorMetaclass), {})
|
||||
return metaclass(
|
||||
'CoopParameterizedTestCase',
|
||||
(other_base_class, ParameterizedTestCase), {})
|
|
@ -51,10 +51,10 @@ import sys
|
|||
import unittest
|
||||
|
||||
from google.apputils import basetest
|
||||
from google.apputils.pybase import parameterized
|
||||
from google.protobuf import unittest_pb2
|
||||
from google.protobuf import unittest_proto3_arena_pb2
|
||||
from google.protobuf.internal import api_implementation
|
||||
from google.protobuf.internal import _parameterized
|
||||
from google.protobuf.internal import test_util
|
||||
from google.protobuf import message
|
||||
|
||||
|
@ -72,7 +72,7 @@ def IsNegInf(val):
|
|||
return isinf(val) and (val < 0)
|
||||
|
||||
|
||||
@parameterized.Parameters(
|
||||
@_parameterized.Parameters(
|
||||
(unittest_pb2),
|
||||
(unittest_proto3_arena_pb2))
|
||||
class MessageTest(basetest.TestCase):
|
||||
|
@ -982,7 +982,6 @@ class Proto2Test(basetest.TestCase):
|
|||
# This is still an incomplete proto - so serializing should fail
|
||||
self.assertRaises(message.EncodeError, unpickled_message.SerializeToString)
|
||||
|
||||
|
||||
# TODO(haberman): this isn't really a proto2-specific test except that this
|
||||
# message has a required field in it. Should probably be factored out so
|
||||
# that we can test the other parts with proto3.
|
||||
|
|
|
@ -1803,16 +1803,6 @@ class ReflectionTest(basetest.TestCase):
|
|||
self.assertRaises(TypeError,
|
||||
unittest_pb2.TestAllTypes().__getattribute__, 42)
|
||||
|
||||
@basetest.unittest.skipIf(
|
||||
api_implementation.Type() != 'cpp' or api_implementation.Version() != 2,
|
||||
'CPPv2-specific test')
|
||||
def testRosyHack(self):
|
||||
from google.protobuf.pyext import _message
|
||||
from google3.gdata.rosy.proto import core_api2_pb2
|
||||
from google3.gdata.rosy.proto import core_pb2
|
||||
self.assertEqual(_message.Message, core_pb2.PageSelection.__base__)
|
||||
self.assertEqual(_message.Message, core_api2_pb2.PageSelection.__base__)
|
||||
|
||||
|
||||
# Since we had so many tests for protocol buffer equality, we broke these out
|
||||
# into separate TestCase classes.
|
||||
|
|
|
@ -37,12 +37,12 @@ __author__ = 'kenton@google.com (Kenton Varda)'
|
|||
import re
|
||||
|
||||
from google.apputils import basetest
|
||||
from google.apputils.pybase import parameterized
|
||||
|
||||
from google.protobuf import unittest_mset_pb2
|
||||
from google.protobuf import unittest_pb2
|
||||
from google.protobuf import unittest_proto3_arena_pb2
|
||||
from google.protobuf.internal import api_implementation
|
||||
from google.protobuf.internal import _parameterized
|
||||
from google.protobuf.internal import test_util
|
||||
from google.protobuf import text_format
|
||||
|
||||
|
@ -72,7 +72,7 @@ class TextFormatBase(basetest.TestCase):
|
|||
return text
|
||||
|
||||
|
||||
@parameterized.Parameters(
|
||||
@_parameterized.Parameters(
|
||||
(unittest_pb2),
|
||||
(unittest_proto3_arena_pb2))
|
||||
class TextFormatTest(TextFormatBase):
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
__author__ = 'robinson@google.com (Will Robinson)'
|
||||
|
||||
|
||||
class Error(Exception): pass
|
||||
class DecodeError(Error): pass
|
||||
class EncodeError(Error): pass
|
||||
|
|
|
@ -433,6 +433,20 @@ static PyObject* IsExtendable(PyBaseDescriptor *self, void *closure) {
|
|||
}
|
||||
}
|
||||
|
||||
static PyObject* GetExtensionRanges(PyBaseDescriptor *self, void *closure) {
|
||||
const Descriptor* descriptor = _GetDescriptor(self);
|
||||
PyObject* range_list = PyList_New(descriptor->extension_range_count());
|
||||
|
||||
for (int i = 0; i < descriptor->extension_range_count(); i++) {
|
||||
const Descriptor::ExtensionRange* range = descriptor->extension_range(i);
|
||||
PyObject* start = PyInt_FromLong(range->start);
|
||||
PyObject* end = PyInt_FromLong(range->end);
|
||||
PyList_SetItem(range_list, i, PyTuple_Pack(2, start, end));
|
||||
}
|
||||
|
||||
return range_list;
|
||||
}
|
||||
|
||||
static PyObject* GetContainingType(PyBaseDescriptor *self, void *closure) {
|
||||
const Descriptor* containing_type =
|
||||
_GetDescriptor(self)->containing_type();
|
||||
|
@ -512,6 +526,7 @@ static PyGetSetDef Getters[] = {
|
|||
{ C("nested_types_by_name"), (getter)GetNestedTypesByName, NULL, "Nested types by name", NULL},
|
||||
{ C("extensions"), (getter)GetExtensions, NULL, "Extensions Sequence", NULL},
|
||||
{ C("extensions_by_name"), (getter)GetExtensionsByName, NULL, "Extensions by name", NULL},
|
||||
{ C("extension_ranges"), (getter)GetExtensionRanges, NULL, "Extension ranges", NULL},
|
||||
{ C("enum_types"), (getter)GetEnumsSeq, NULL, "Enum sequence", NULL},
|
||||
{ C("enum_types_by_name"), (getter)GetEnumTypesByName, NULL, "Enum types by name", NULL},
|
||||
{ C("enum_values_by_name"), (getter)GetEnumValuesByName, NULL, "Enum values by name", NULL},
|
||||
|
|
|
@ -1264,7 +1264,27 @@ static PyObject* SerializeToString(CMessage* self, PyObject* args) {
|
|||
if (joined == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyErr_Format(EncodeError_class, "Message %s is missing required fields: %s",
|
||||
|
||||
// TODO(haberman): this is a (hopefully temporary) hack. The unit testing
|
||||
// infrastructure reloads all pure-Python modules for every test, but not
|
||||
// C++ modules (because that's generally impossible:
|
||||
// http://bugs.python.org/issue1144263). But if we cache EncodeError, we'll
|
||||
// return the EncodeError from a previous load of the module, which won't
|
||||
// match a user's attempt to catch EncodeError. So we have to look it up
|
||||
// again every time.
|
||||
ScopedPyObjectPtr message_module(PyImport_ImportModule(
|
||||
"google.protobuf.message"));
|
||||
if (message_module.get() == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ScopedPyObjectPtr encode_error(
|
||||
PyObject_GetAttrString(message_module, "EncodeError"));
|
||||
if (encode_error.get() == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
PyErr_Format(encode_error.get(),
|
||||
"Message %s is missing required fields: %s",
|
||||
GetMessageName(self).c_str(), PyString_AsString(joined));
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2284,8 +2304,8 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) {
|
|||
|
||||
PyTypeObject CMessage_Type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
"google.protobuf.internal."
|
||||
"cpp._message.CMessage", // tp_name
|
||||
"google.protobuf."
|
||||
"pyext._message.CMessage", // tp_name
|
||||
sizeof(CMessage), // tp_basicsize
|
||||
0, // tp_itemsize
|
||||
(destructor)cmessage::Dealloc, // tp_dealloc
|
||||
|
|
|
@ -732,8 +732,8 @@ static PyMethodDef Methods[] = {
|
|||
|
||||
PyTypeObject RepeatedCompositeContainer_Type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
"google.protobuf.internal."
|
||||
"cpp._message.RepeatedCompositeContainer", // tp_name
|
||||
"google.protobuf.pyext."
|
||||
"_message.RepeatedCompositeContainer", // tp_name
|
||||
sizeof(RepeatedCompositeContainer), // tp_basicsize
|
||||
0, // tp_itemsize
|
||||
(destructor)repeated_composite_container::Dealloc, // tp_dealloc
|
||||
|
|
|
@ -769,8 +769,8 @@ static PyMethodDef Methods[] = {
|
|||
|
||||
PyTypeObject RepeatedScalarContainer_Type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
"google.protobuf.internal."
|
||||
"cpp._message.RepeatedScalarContainer", // tp_name
|
||||
"google.protobuf."
|
||||
"pyext._message.RepeatedScalarContainer", // tp_name
|
||||
sizeof(RepeatedScalarContainer), // tp_basicsize
|
||||
0, // tp_itemsize
|
||||
(destructor)repeated_scalar_container::Dealloc, // tp_dealloc
|
||||
|
|
|
@ -307,6 +307,7 @@ EXTRA_DIST = \
|
|||
google/protobuf/io/gzip_stream_unittest.sh \
|
||||
google/protobuf/testdata/golden_message \
|
||||
google/protobuf/testdata/golden_message_oneof_implemented \
|
||||
google/protobuf/testdata/golden_message_proto3 \
|
||||
google/protobuf/testdata/golden_packed_fields_message \
|
||||
google/protobuf/testdata/bad_utf8_string \
|
||||
google/protobuf/testdata/text_format_unittest_data.txt \
|
||||
|
|
Loading…
Add table
Reference in a new issue