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__ = '[email protected] (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 name='google/protobuf/internal/container.proto', 179 package='google.protobuf.python.internal') 180 f.message_type.add(name='Container').extension_range.add(start=1, end=10) 181 pool.Add(f) 182 msgs = factory.GetMessages([f.name]) 183 self.assertIn('google.protobuf.python.internal.Container', msgs) 184 185 # Extend container. 186 f = descriptor_pb2.FileDescriptorProto( 187 name='google/protobuf/internal/extension.proto', 188 package='google.protobuf.python.internal', 189 dependency=['google/protobuf/internal/container.proto']) 190 msg = f.message_type.add(name='Extension') 191 msg.extension.add( 192 name='extension_field', 193 number=2, 194 label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL, 195 type_name='Extension', 196 extendee='Container') 197 pool.Add(f) 198 msgs = factory.GetMessages([f.name]) 199 self.assertIn('google.protobuf.python.internal.Extension', msgs) 200 201 # Add Duplicate extending the same field number. 202 f = descriptor_pb2.FileDescriptorProto( 203 name='google/protobuf/internal/duplicate.proto', 204 package='google.protobuf.python.internal', 205 dependency=['google/protobuf/internal/container.proto']) 206 msg = f.message_type.add(name='Duplicate') 207 msg.extension.add( 208 name='extension_field', 209 number=2, 210 label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL, 211 type_name='Duplicate', 212 extendee='Container') 213 pool.Add(f) 214 215 with self.assertRaises(Exception) as cm: 216 factory.GetMessages([f.name]) 217 218 self.assertIn(str(cm.exception), 219 ['Extensions ' 220 '"google.protobuf.python.internal.Duplicate.extension_field" and' 221 ' "google.protobuf.python.internal.Extension.extension_field"' 222 ' both try to extend message type' 223 ' "google.protobuf.python.internal.Container"' 224 ' with field number 2.', 225 'Double registration of Extensions']) 226 227 def testExtensionValueInDifferentFile(self): 228 # Add Container message. 229 f1 = descriptor_pb2.FileDescriptorProto( 230 name='google/protobuf/internal/container.proto', 231 package='google.protobuf.python.internal') 232 f1.message_type.add(name='Container').extension_range.add(start=1, end=10) 233 234 # Add ValueType message. 235 f2 = descriptor_pb2.FileDescriptorProto( 236 name='google/protobuf/internal/value_type.proto', 237 package='google.protobuf.python.internal') 238 f2.message_type.add(name='ValueType').field.add( 239 name='setting', 240 number=1, 241 label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL, 242 type=descriptor_pb2.FieldDescriptorProto.TYPE_INT32, 243 default_value='123') 244 245 # Extend container with field of ValueType. 246 f3 = descriptor_pb2.FileDescriptorProto( 247 name='google/protobuf/internal/extension.proto', 248 package='google.protobuf.python.internal', 249 dependency=[f1.name, f2.name]) 250 f3.extension.add( 251 name='top_level_extension_field', 252 number=2, 253 label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL, 254 type_name='ValueType', 255 extendee='Container') 256 f3.message_type.add(name='Extension').extension.add( 257 name='nested_extension_field', 258 number=3, 259 label=descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL, 260 type_name='ValueType', 261 extendee='Container') 262 263 class SimpleDescriptorDB: 264 265 def __init__(self, files): 266 self._files = files 267 268 def FindFileByName(self, name): 269 return self._files[name] 270 271 db = SimpleDescriptorDB({f1.name: f1, f2.name: f2, f3.name: f3}) 272 273 pool = descriptor_pool.DescriptorPool(db) 274 factory = message_factory.MessageFactory(pool=pool) 275 msgs = factory.GetMessages([f1.name, f3.name]) # Deliberately not f2. 276 msg = msgs['google.protobuf.python.internal.Container'] 277 desc = msgs['google.protobuf.python.internal.Extension'].DESCRIPTOR 278 ext1 = desc.file.extensions_by_name['top_level_extension_field'] 279 ext2 = desc.extensions_by_name['nested_extension_field'] 280 m = msg() 281 m.Extensions[ext1].setting = 234 282 m.Extensions[ext2].setting = 345 283 serialized = m.SerializeToString() 284 285 pool = descriptor_pool.DescriptorPool(db) 286 factory = message_factory.MessageFactory(pool=pool) 287 msgs = factory.GetMessages([f1.name, f3.name]) # Deliberately not f2. 288 msg = msgs['google.protobuf.python.internal.Container'] 289 desc = msgs['google.protobuf.python.internal.Extension'].DESCRIPTOR 290 ext1 = desc.file.extensions_by_name['top_level_extension_field'] 291 ext2 = desc.extensions_by_name['nested_extension_field'] 292 m = msg.FromString(serialized) 293 self.assertEqual(2, len(m.ListFields())) 294 self.assertEqual(234, m.Extensions[ext1].setting) 295 self.assertEqual(345, m.Extensions[ext2].setting) 296 297 298if __name__ == '__main__': 299 unittest.main() 300