mirror of
https://github.com/protocolbuffers/protobuf
synced 2026-08-26 02:23:14 -04:00
All Languages * Repeated fields of primitive types (types other that string, group, and nested messages) may now use the option [packed = true] to get a more efficient encoding. In the new encoding, the entire list is written as a single byte blob using the "length-delimited" wire type. Within this blob, the individual values are encoded the same way they would be normally except without a tag before each value (thus, they are tightly "packed"). C++ * UnknownFieldSet now supports STL-like iteration. * Message interface has method ParseFromBoundedZeroCopyStream() which parses a limited number of bytes from an input stream rather than parsing until EOF. Java * Fixed bug where Message.mergeFrom(Message) failed to merge extensions. * Message interface has new method toBuilder() which is equivalent to newBuilderForType().mergeFrom(this). * All enums now implement the ProtocolMessageEnum interface. * Setting a field to null now throws NullPointerException. * Fixed tendency for TextFormat's parsing to overflow the stack when parsing large string values. The underlying problem is with Java's regex implementation (which unfortunately uses recursive backtracking rather than building an NFA). Worked around by making use of possesive quantifiers. Python * Updated RPC interfaces to allow for blocking operation. A client may now pass None for a callback when making an RPC, in which case the call will block until the response is received, and the response object will be returned directly to the caller. This interface change cannot be used in practice until RPC implementations are updated to implement it.
252 lines
10 KiB
Python
Executable file
252 lines
10 KiB
Python
Executable file
#! /usr/bin/python
|
|
#
|
|
# Protocol Buffers - Google's data interchange format
|
|
# Copyright 2008 Google Inc. All rights reserved.
|
|
# http://code.google.com/p/protobuf/
|
|
#
|
|
# 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.
|
|
|
|
"""Test for google.protobuf.internal.decoder."""
|
|
|
|
__author__ = 'robinson@google.com (Will Robinson)'
|
|
|
|
import struct
|
|
import unittest
|
|
from google.protobuf.internal import wire_format
|
|
from google.protobuf.internal import encoder
|
|
from google.protobuf.internal import decoder
|
|
import logging
|
|
from google.protobuf.internal import input_stream
|
|
from google.protobuf import message
|
|
import mox
|
|
|
|
|
|
class DecoderTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
self.mox = mox.Mox()
|
|
self.mock_stream = self.mox.CreateMock(input_stream.InputStream)
|
|
self.mock_message = self.mox.CreateMock(message.Message)
|
|
|
|
def testReadFieldNumberAndWireType(self):
|
|
# Test field numbers that will require various varint sizes.
|
|
for expected_field_number in (1, 15, 16, 2047, 2048):
|
|
for expected_wire_type in range(6): # Highest-numbered wiretype is 5.
|
|
e = encoder.Encoder()
|
|
e.AppendTag(expected_field_number, expected_wire_type)
|
|
s = e.ToString()
|
|
d = decoder.Decoder(s)
|
|
field_number, wire_type = d.ReadFieldNumberAndWireType()
|
|
self.assertEqual(expected_field_number, field_number)
|
|
self.assertEqual(expected_wire_type, wire_type)
|
|
|
|
def ReadScalarTestHelper(self, test_name, decoder_method, expected_result,
|
|
expected_stream_method_name,
|
|
stream_method_return, *args):
|
|
"""Helper for testReadScalars below.
|
|
|
|
Calls one of the Decoder.Read*() methods and ensures that the results are
|
|
as expected.
|
|
|
|
Args:
|
|
test_name: Name of this test, used for logging only.
|
|
decoder_method: Unbound decoder.Decoder method to call.
|
|
expected_result: Value we expect returned from decoder_method().
|
|
expected_stream_method_name: (string) Name of the InputStream
|
|
method we expect Decoder to call to actually read the value
|
|
on the wire.
|
|
stream_method_return: Value our mocked-out stream method should
|
|
return to the decoder.
|
|
args: Additional arguments that we expect to be passed to the
|
|
stream method.
|
|
"""
|
|
logging.info('Testing %s scalar input.\n'
|
|
'Calling %r(), and expecting that to call the '
|
|
'stream method %s(%r), which will return %r. Finally, '
|
|
'expecting the Decoder method to return %r'% (
|
|
test_name, decoder_method,
|
|
expected_stream_method_name, args, stream_method_return,
|
|
expected_result))
|
|
|
|
d = decoder.Decoder('')
|
|
d._stream = self.mock_stream
|
|
if decoder_method in (decoder.Decoder.ReadString,
|
|
decoder.Decoder.ReadBytes):
|
|
self.mock_stream.ReadVarUInt32().AndReturn(len(stream_method_return))
|
|
# We have to use names instead of methods to work around some
|
|
# mox weirdness. (ResetAll() is overzealous).
|
|
expected_stream_method = getattr(self.mock_stream,
|
|
expected_stream_method_name)
|
|
expected_stream_method(*args).AndReturn(stream_method_return)
|
|
|
|
self.mox.ReplayAll()
|
|
result = decoder_method(d)
|
|
self.assertEqual(expected_result, result)
|
|
self.assert_(isinstance(result, type(expected_result)))
|
|
self.mox.VerifyAll()
|
|
self.mox.ResetAll()
|
|
|
|
def testReadScalars(self):
|
|
test_string = 'I can feel myself getting sutpider.'
|
|
scalar_tests = [
|
|
['int32', decoder.Decoder.ReadInt32, 0, 'ReadVarint32', 0],
|
|
['int64', decoder.Decoder.ReadInt64, 0, 'ReadVarint64', 0],
|
|
['uint32', decoder.Decoder.ReadUInt32, 0, 'ReadVarUInt32', 0],
|
|
['uint64', decoder.Decoder.ReadUInt64, 0, 'ReadVarUInt64', 0],
|
|
['fixed32', decoder.Decoder.ReadFixed32, 0xffffffff,
|
|
'ReadLittleEndian32', 0xffffffff],
|
|
['fixed64', decoder.Decoder.ReadFixed64, 0xffffffffffffffff,
|
|
'ReadLittleEndian64', 0xffffffffffffffff],
|
|
['sfixed32', decoder.Decoder.ReadSFixed32, long(-1),
|
|
'ReadLittleEndian32', 0xffffffff],
|
|
['sfixed64', decoder.Decoder.ReadSFixed64, long(-1),
|
|
'ReadLittleEndian64', 0xffffffffffffffff],
|
|
['float', decoder.Decoder.ReadFloat, 0.0,
|
|
'ReadBytes', struct.pack('f', 0.0), 4],
|
|
['double', decoder.Decoder.ReadDouble, 0.0,
|
|
'ReadBytes', struct.pack('d', 0.0), 8],
|
|
['bool', decoder.Decoder.ReadBool, True, 'ReadVarUInt32', 1],
|
|
['enum', decoder.Decoder.ReadEnum, 23, 'ReadVarUInt32', 23],
|
|
['string', decoder.Decoder.ReadString,
|
|
unicode(test_string, 'utf-8'), 'ReadBytes', test_string,
|
|
len(test_string)],
|
|
['utf8-string', decoder.Decoder.ReadString,
|
|
unicode(test_string, 'utf-8'), 'ReadBytes', test_string,
|
|
len(test_string)],
|
|
['bytes', decoder.Decoder.ReadBytes,
|
|
test_string, 'ReadBytes', test_string, len(test_string)],
|
|
# We test zigzag decoding routines more extensively below.
|
|
['sint32', decoder.Decoder.ReadSInt32, -1, 'ReadVarUInt32', 1],
|
|
['sint64', decoder.Decoder.ReadSInt64, -1, 'ReadVarUInt64', 1],
|
|
]
|
|
# Ensure that we're testing different Decoder methods and using
|
|
# different test names in all test cases above.
|
|
self.assertEqual(len(scalar_tests), len(set(t[0] for t in scalar_tests)))
|
|
self.assert_(len(scalar_tests) >= len(set(t[1] for t in scalar_tests)))
|
|
for args in scalar_tests:
|
|
self.ReadScalarTestHelper(*args)
|
|
|
|
def testReadMessageInto(self):
|
|
length = 23
|
|
def Test(simulate_error):
|
|
d = decoder.Decoder('')
|
|
d._stream = self.mock_stream
|
|
self.mock_stream.ReadVarUInt32().AndReturn(length)
|
|
sub_buffer = object()
|
|
self.mock_stream.GetSubBuffer(length).AndReturn(sub_buffer)
|
|
|
|
if simulate_error:
|
|
self.mock_message.MergeFromString(sub_buffer).AndReturn(length - 1)
|
|
self.mox.ReplayAll()
|
|
self.assertRaises(
|
|
message.DecodeError, d.ReadMessageInto, self.mock_message)
|
|
else:
|
|
self.mock_message.MergeFromString(sub_buffer).AndReturn(length)
|
|
self.mock_stream.SkipBytes(length)
|
|
self.mox.ReplayAll()
|
|
d.ReadMessageInto(self.mock_message)
|
|
|
|
self.mox.VerifyAll()
|
|
self.mox.ResetAll()
|
|
|
|
Test(simulate_error=False)
|
|
Test(simulate_error=True)
|
|
|
|
def testReadGroupInto_Success(self):
|
|
# Test both the empty and nonempty cases.
|
|
for num_bytes in (5, 0):
|
|
field_number = expected_field_number = 10
|
|
d = decoder.Decoder('')
|
|
d._stream = self.mock_stream
|
|
sub_buffer = object()
|
|
self.mock_stream.GetSubBuffer().AndReturn(sub_buffer)
|
|
self.mock_message.MergeFromString(sub_buffer).AndReturn(num_bytes)
|
|
self.mock_stream.SkipBytes(num_bytes)
|
|
self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag(
|
|
field_number, wire_format.WIRETYPE_END_GROUP))
|
|
self.mox.ReplayAll()
|
|
d.ReadGroupInto(expected_field_number, self.mock_message)
|
|
self.mox.VerifyAll()
|
|
self.mox.ResetAll()
|
|
|
|
def ReadGroupInto_FailureTestHelper(self, bytes_read):
|
|
d = decoder.Decoder('')
|
|
d._stream = self.mock_stream
|
|
sub_buffer = object()
|
|
self.mock_stream.GetSubBuffer().AndReturn(sub_buffer)
|
|
self.mock_message.MergeFromString(sub_buffer).AndReturn(bytes_read)
|
|
return d
|
|
|
|
def testReadGroupInto_NegativeBytesReported(self):
|
|
expected_field_number = 10
|
|
d = self.ReadGroupInto_FailureTestHelper(bytes_read=-1)
|
|
self.mox.ReplayAll()
|
|
self.assertRaises(message.DecodeError,
|
|
d.ReadGroupInto, expected_field_number,
|
|
self.mock_message)
|
|
self.mox.VerifyAll()
|
|
|
|
def testReadGroupInto_NoEndGroupTag(self):
|
|
field_number = expected_field_number = 10
|
|
num_bytes = 5
|
|
d = self.ReadGroupInto_FailureTestHelper(bytes_read=num_bytes)
|
|
self.mock_stream.SkipBytes(num_bytes)
|
|
# Right field number, wrong wire type.
|
|
self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag(
|
|
field_number, wire_format.WIRETYPE_LENGTH_DELIMITED))
|
|
self.mox.ReplayAll()
|
|
self.assertRaises(message.DecodeError,
|
|
d.ReadGroupInto, expected_field_number,
|
|
self.mock_message)
|
|
self.mox.VerifyAll()
|
|
|
|
def testReadGroupInto_WrongFieldNumberInEndGroupTag(self):
|
|
expected_field_number = 10
|
|
field_number = expected_field_number + 1
|
|
num_bytes = 5
|
|
d = self.ReadGroupInto_FailureTestHelper(bytes_read=num_bytes)
|
|
self.mock_stream.SkipBytes(num_bytes)
|
|
# Wrong field number, right wire type.
|
|
self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag(
|
|
field_number, wire_format.WIRETYPE_END_GROUP))
|
|
self.mox.ReplayAll()
|
|
self.assertRaises(message.DecodeError,
|
|
d.ReadGroupInto, expected_field_number,
|
|
self.mock_message)
|
|
self.mox.VerifyAll()
|
|
|
|
def testSkipBytes(self):
|
|
d = decoder.Decoder('')
|
|
num_bytes = 1024
|
|
self.mock_stream.SkipBytes(num_bytes)
|
|
d._stream = self.mock_stream
|
|
self.mox.ReplayAll()
|
|
d.SkipBytes(num_bytes)
|
|
self.mox.VerifyAll()
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|