1# Protocol Buffers - Google's data interchange format 2# Copyright 2008 Google Inc. All rights reserved. 3# https://developers.google.com/protocol-buffers/ 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions are 7# met: 8# 9# * Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# * Redistributions in binary form must reproduce the above 12# copyright notice, this list of conditions and the following disclaimer 13# in the documentation and/or other materials provided with the 14# distribution. 15# * Neither the name of Google Inc. nor the names of its 16# contributors may be used to endorse or promote products derived from 17# this software without specific prior written permission. 18# 19# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31"""Tests for google.protobuf.message_factory.""" 32 33__author__ = 'matthewtoia@google.com (Matt Toia)' 34 35import unittest 36 37from google.protobuf import descriptor_pb2 38from google.protobuf.internal import api_implementation 39from google.protobuf.internal import factory_test1_pb2 40from google.protobuf.internal import factory_test2_pb2 41from google.protobuf.internal import testing_refleaks 42from google.protobuf import descriptor_database 43from google.protobuf import descriptor_pool 44from google.protobuf import message_factory 45 46 47@testing_refleaks.TestCase 48class MessageFactoryTest(unittest.TestCase): 49 50 def setUp(self): 51 self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString( 52 factory_test1_pb2.DESCRIPTOR.serialized_pb) 53 self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString( 54 factory_test2_pb2.DESCRIPTOR.serialized_pb) 55 56 def _ExerciseDynamicClass(self, cls): 57 msg = cls() 58 msg.mandatory = 42 59 msg.nested_factory_2_enum = 0 60 msg.nested_factory_2_message.value = 'nested message value' 61 msg.factory_1_message.factory_1_enum = 1 62 msg.factory_1_message.nested_factory_1_enum = 0 63 msg.factory_1_message.nested_factory_1_message.value = ( 64 'nested message value') 65 msg.factory_1_message.scalar_value = 22 66 msg.factory_1_message.list_value.extend([u'one', u'two', u'three']) 67 msg.factory_1_message.list_value.append(u'four') 68 msg.factory_1_enum = 1 69 msg.nested_factory_1_enum = 0 70 msg.nested_factory_1_message.value = 'nested message value' 71 msg.circular_message.mandatory = 1 72 msg.circular_message.circular_message.mandatory = 2 73 msg.circular_message.scalar_value = 'one deep' 74 msg.scalar_value = 'zero deep' 75 msg.list_value.extend([u'four', u'three', u'two']) 76 msg.list_value.append(u'one') 77 msg.grouped.add() 78 msg.grouped[0].part_1 = 'hello' 79 msg.grouped[0].part_2 = 'world' 80 msg.grouped.add(part_1='testing', part_2='123') 81 msg.loop.loop.mandatory = 2 82 msg.loop.loop.loop.loop.mandatory = 4 83 serialized = msg.SerializeToString() 84 converted = factory_test2_pb2.Factory2Message.FromString(serialized) 85 reserialized = converted.SerializeToString() 86 self.assertEqual(serialized, reserialized) 87 result = cls.FromString(reserialized) 88 self.assertEqual(msg, result) 89 90 def testGetPrototype(self): 91 db = descriptor_database.DescriptorDatabase() 92 pool = descriptor_pool.DescriptorPool(db) 93 db.Add(self.factory_test1_fd) 94 db.Add(self.factory_test2_fd) 95 factory = message_factory.MessageFactory() 96 cls = factory.GetPrototype(pool.FindMessageTypeByName( 97 'google.protobuf.python.internal.Factory2Message')) 98 self.assertFalse(cls is factory_test2_pb2.Factory2Message) 99 self._ExerciseDynamicClass(cls) 100 cls2 = factory.GetPrototype(pool.FindMessageTypeByName( 101 'google.protobuf.python.internal.Factory2Message')) 102 self.assertTrue(cls is cls2) 103 104 def testCreatePrototypeOverride(self): 105 class MyMessageFactory(message_factory.MessageFactory): 106 107 def CreatePrototype(self, descriptor): 108 cls = super(MyMessageFactory, self).CreatePrototype(descriptor) 109 cls.additional_field = 'Some value' 110 return cls 111 112 db = descriptor_database.DescriptorDatabase() 113 pool = descriptor_pool.DescriptorPool(db) 114 db.Add(self.factory_test1_fd) 115 db.Add(self.factory_test2_fd) 116 factory = MyMessageFactory() 117 cls = factory.GetPrototype(pool.FindMessageTypeByName( 118 'google.protobuf.python.internal.Factory2Message')) 119 self.assertTrue(hasattr(cls, 'additional_field')) 120 121 def testGetMessages(self): 122 # performed twice because multiple calls with the same input must be allowed 123 for _ in range(2): 124 # GetMessage should work regardless of the order the FileDescriptorProto 125 # are provided. In particular, the function should succeed when the files 126 # are not in the topological order of dependencies. 127 128 # Assuming factory_test2_fd depends on factory_test1_fd. 129 self.assertIn(self.factory_test1_fd.name, 130 self.factory_test2_fd.dependency) 131 # Get messages should work when a file comes before its dependencies: 132 # factory_test2_fd comes before factory_test1_fd. 133 messages = message_factory.GetMessages([self.factory_test2_fd, 134 self.factory_test1_fd]) 135 self.assertTrue( 136 set(['google.protobuf.python.internal.Factory2Message', 137 'google.protobuf.python.internal.Factory1Message'], 138 ).issubset(set(messages.keys()))) 139 self._ExerciseDynamicClass( 140 messages['google.protobuf.python.internal.Factory2Message']) 141 factory_msg1 = messages['google.protobuf.python.internal.Factory1Message'] 142 self.assertTrue(set( 143 ['google.protobuf.python.internal.Factory2Message.one_more_field', 144 'google.protobuf.python.internal.another_field'],).issubset(set( 145 ext.full_name 146 for ext in factory_msg1.DESCRIPTOR.file.pool.FindAllExtensions( 147 factory_msg1.DESCRIPTOR)))) 148 msg1 = messages['google.protobuf.python.internal.Factory1Message']() 149 ext1 = msg1.Extensions._FindExtensionByName( 150 'google.protobuf.python.internal.Factory2Message.one_more_field') 151 ext2 = msg1.Extensions._FindExtensionByName( 152 'google.protobuf.python.internal.another_field') 153 self.assertEqual(0, len(msg1.Extensions)) 154 msg1.Extensions[ext1] = 'test1' 155 msg1.Extensions[ext2] = 'test2' 156 self.assertEqual('test1', msg1.Extensions[ext1]) 157 self.assertEqual('test2', msg1.Extensions[ext2]) 158 self.assertEqual(None, 159 msg1.Extensions._FindExtensionByNumber(12321)) 160 self.assertEqual(2, len(msg1.Extensions)) 161 if api_implementation.Type() == 'cpp': 162 self.assertRaises(TypeError, 163 msg1.Extensions._FindExtensionByName, 0) 164 self.assertRaises(TypeError, 165 msg1.Extensions._FindExtensionByNumber, '') 166 else: 167 self.assertEqual(None, 168 msg1.Extensions._FindExtensionByName(0)) 169 self.assertEqual(None, 170 msg1.Extensions._FindExtensionByNumber('')) 171 172 def testDuplicateExtensionNumber(self): 173 pool = descriptor_pool.DescriptorPool() 174 factory = message_factory.MessageFactory(pool=pool) 175 176 # Add Container message. 177 f = descriptor_pb2.FileDescriptorProto() 178 f.name = 'google/protobuf/internal/container.proto' 179 f.package = 'google.protobuf.python.internal' 180 msg = f.message_type.add() 181 msg.name = 'Container' 182 rng = msg.extension_range.add() 183 rng.start = 1 184 rng.end = 10 185 pool.Add(f) 186 msgs = factory.GetMessages([f.name]) 187 self.assertIn('google.protobuf.python.internal.Container', msgs) 188 189 # Extend container. 190 f = descriptor_pb2.FileDescriptorProto() 191 f.name = 'google/protobuf/internal/extension.proto' 192 f.package = 'google.protobuf.python.internal' 193 f.dependency.append('google/protobuf/internal/container.proto') 194 msg = f.message_type.add() 195 msg.name = 'Extension' 196 ext = msg.extension.add() 197 ext.name = 'extension_field' 198 ext.number = 2 199 ext.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL 200 ext.type_name = 'Extension' 201 ext.extendee = 'Container' 202 pool.Add(f) 203 msgs = factory.GetMessages([f.name]) 204 self.assertIn('google.protobuf.python.internal.Extension', msgs) 205 206 # Add Duplicate extending the same field number. 207 f = descriptor_pb2.FileDescriptorProto() 208 f.name = 'google/protobuf/internal/duplicate.proto' 209 f.package = 'google.protobuf.python.internal' 210 f.dependency.append('google/protobuf/internal/container.proto') 211 msg = f.message_type.add() 212 msg.name = 'Duplicate' 213 ext = msg.extension.add() 214 ext.name = 'extension_field' 215 ext.number = 2 216 ext.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL 217 ext.type_name = 'Duplicate' 218 ext.extendee = 'Container' 219 pool.Add(f) 220 221 with self.assertRaises(Exception) as cm: 222 factory.GetMessages([f.name]) 223 224 self.assertIn(str(cm.exception), 225 ['Extensions ' 226 '"google.protobuf.python.internal.Duplicate.extension_field" and' 227 ' "google.protobuf.python.internal.Extension.extension_field"' 228 ' both try to extend message type' 229 ' "google.protobuf.python.internal.Container"' 230 ' with field number 2.', 231 'Double registration of Extensions']) 232 233 234if __name__ == '__main__': 235 unittest.main() 236