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"""Tests for google.protobuf.message_factory.""" 34 35__author__ = 'matthewtoia@google.com (Matt Toia)' 36 37try: 38 import unittest2 as unittest #PY26 39except ImportError: 40 import unittest 41 42from google.protobuf import descriptor_pb2 43from google.protobuf.internal import api_implementation 44from google.protobuf.internal import factory_test1_pb2 45from google.protobuf.internal import factory_test2_pb2 46from google.protobuf.internal import testing_refleaks 47from google.protobuf import descriptor_database 48from google.protobuf import descriptor_pool 49from google.protobuf import message_factory 50 51 52@testing_refleaks.TestCase 53class MessageFactoryTest(unittest.TestCase): 54 55 def setUp(self): 56 self.factory_test1_fd = descriptor_pb2.FileDescriptorProto.FromString( 57 factory_test1_pb2.DESCRIPTOR.serialized_pb) 58 self.factory_test2_fd = descriptor_pb2.FileDescriptorProto.FromString( 59 factory_test2_pb2.DESCRIPTOR.serialized_pb) 60 61 def _ExerciseDynamicClass(self, cls): 62 msg = cls() 63 msg.mandatory = 42 64 msg.nested_factory_2_enum = 0 65 msg.nested_factory_2_message.value = 'nested message value' 66 msg.factory_1_message.factory_1_enum = 1 67 msg.factory_1_message.nested_factory_1_enum = 0 68 msg.factory_1_message.nested_factory_1_message.value = ( 69 'nested message value') 70 msg.factory_1_message.scalar_value = 22 71 msg.factory_1_message.list_value.extend([u'one', u'two', u'three']) 72 msg.factory_1_message.list_value.append(u'four') 73 msg.factory_1_enum = 1 74 msg.nested_factory_1_enum = 0 75 msg.nested_factory_1_message.value = 'nested message value' 76 msg.circular_message.mandatory = 1 77 msg.circular_message.circular_message.mandatory = 2 78 msg.circular_message.scalar_value = 'one deep' 79 msg.scalar_value = 'zero deep' 80 msg.list_value.extend([u'four', u'three', u'two']) 81 msg.list_value.append(u'one') 82 msg.grouped.add() 83 msg.grouped[0].part_1 = 'hello' 84 msg.grouped[0].part_2 = 'world' 85 msg.grouped.add(part_1='testing', part_2='123') 86 msg.loop.loop.mandatory = 2 87 msg.loop.loop.loop.loop.mandatory = 4 88 serialized = msg.SerializeToString() 89 converted = factory_test2_pb2.Factory2Message.FromString(serialized) 90 reserialized = converted.SerializeToString() 91 self.assertEqual(serialized, reserialized) 92 result = cls.FromString(reserialized) 93 self.assertEqual(msg, result) 94 95 def testGetPrototype(self): 96 db = descriptor_database.DescriptorDatabase() 97 pool = descriptor_pool.DescriptorPool(db) 98 db.Add(self.factory_test1_fd) 99 db.Add(self.factory_test2_fd) 100 factory = message_factory.MessageFactory() 101 cls = factory.GetPrototype(pool.FindMessageTypeByName( 102 'google.protobuf.python.internal.Factory2Message')) 103 self.assertFalse(cls is factory_test2_pb2.Factory2Message) 104 self._ExerciseDynamicClass(cls) 105 cls2 = factory.GetPrototype(pool.FindMessageTypeByName( 106 'google.protobuf.python.internal.Factory2Message')) 107 self.assertTrue(cls is cls2) 108 109 def testGetMessages(self): 110 # performed twice because multiple calls with the same input must be allowed 111 for _ in range(2): 112 # GetMessage should work regardless of the order the FileDescriptorProto 113 # are provided. In particular, the function should succeed when the files 114 # are not in the topological order of dependencies. 115 116 # Assuming factory_test2_fd depends on factory_test1_fd. 117 self.assertIn(self.factory_test1_fd.name, 118 self.factory_test2_fd.dependency) 119 # Get messages should work when a file comes before its dependencies: 120 # factory_test2_fd comes before factory_test1_fd. 121 messages = message_factory.GetMessages([self.factory_test2_fd, 122 self.factory_test1_fd]) 123 self.assertTrue( 124 set(['google.protobuf.python.internal.Factory2Message', 125 'google.protobuf.python.internal.Factory1Message'], 126 ).issubset(set(messages.keys()))) 127 self._ExerciseDynamicClass( 128 messages['google.protobuf.python.internal.Factory2Message']) 129 factory_msg1 = messages['google.protobuf.python.internal.Factory1Message'] 130 self.assertTrue(set( 131 ['google.protobuf.python.internal.Factory2Message.one_more_field', 132 'google.protobuf.python.internal.another_field'],).issubset(set( 133 ext.full_name 134 for ext in factory_msg1.DESCRIPTOR.file.pool.FindAllExtensions( 135 factory_msg1.DESCRIPTOR)))) 136 msg1 = messages['google.protobuf.python.internal.Factory1Message']() 137 ext1 = msg1.Extensions._FindExtensionByName( 138 'google.protobuf.python.internal.Factory2Message.one_more_field') 139 ext2 = msg1.Extensions._FindExtensionByName( 140 'google.protobuf.python.internal.another_field') 141 self.assertEqual(0, len(msg1.Extensions)) 142 msg1.Extensions[ext1] = 'test1' 143 msg1.Extensions[ext2] = 'test2' 144 self.assertEqual('test1', msg1.Extensions[ext1]) 145 self.assertEqual('test2', msg1.Extensions[ext2]) 146 self.assertEqual(None, 147 msg1.Extensions._FindExtensionByNumber(12321)) 148 self.assertEqual(2, len(msg1.Extensions)) 149 if api_implementation.Type() == 'cpp': 150 self.assertRaises(TypeError, 151 msg1.Extensions._FindExtensionByName, 0) 152 self.assertRaises(TypeError, 153 msg1.Extensions._FindExtensionByNumber, '') 154 else: 155 self.assertEqual(None, 156 msg1.Extensions._FindExtensionByName(0)) 157 self.assertEqual(None, 158 msg1.Extensions._FindExtensionByNumber('')) 159 160 def testDuplicateExtensionNumber(self): 161 pool = descriptor_pool.DescriptorPool() 162 factory = message_factory.MessageFactory(pool=pool) 163 164 # Add Container message. 165 f = descriptor_pb2.FileDescriptorProto() 166 f.name = 'google/protobuf/internal/container.proto' 167 f.package = 'google.protobuf.python.internal' 168 msg = f.message_type.add() 169 msg.name = 'Container' 170 rng = msg.extension_range.add() 171 rng.start = 1 172 rng.end = 10 173 pool.Add(f) 174 msgs = factory.GetMessages([f.name]) 175 self.assertIn('google.protobuf.python.internal.Container', msgs) 176 177 # Extend container. 178 f = descriptor_pb2.FileDescriptorProto() 179 f.name = 'google/protobuf/internal/extension.proto' 180 f.package = 'google.protobuf.python.internal' 181 f.dependency.append('google/protobuf/internal/container.proto') 182 msg = f.message_type.add() 183 msg.name = 'Extension' 184 ext = msg.extension.add() 185 ext.name = 'extension_field' 186 ext.number = 2 187 ext.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL 188 ext.type_name = 'Extension' 189 ext.extendee = 'Container' 190 pool.Add(f) 191 msgs = factory.GetMessages([f.name]) 192 self.assertIn('google.protobuf.python.internal.Extension', msgs) 193 194 # Add Duplicate extending the same field number. 195 f = descriptor_pb2.FileDescriptorProto() 196 f.name = 'google/protobuf/internal/duplicate.proto' 197 f.package = 'google.protobuf.python.internal' 198 f.dependency.append('google/protobuf/internal/container.proto') 199 msg = f.message_type.add() 200 msg.name = 'Duplicate' 201 ext = msg.extension.add() 202 ext.name = 'extension_field' 203 ext.number = 2 204 ext.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL 205 ext.type_name = 'Duplicate' 206 ext.extendee = 'Container' 207 pool.Add(f) 208 209 with self.assertRaises(Exception) as cm: 210 factory.GetMessages([f.name]) 211 212 self.assertIn(str(cm.exception), 213 ['Extensions ' 214 '"google.protobuf.python.internal.Duplicate.extension_field" and' 215 ' "google.protobuf.python.internal.Extension.extension_field"' 216 ' both try to extend message type' 217 ' "google.protobuf.python.internal.Container"' 218 ' with field number 2.', 219 'Double registration of Extensions']) 220 221 222if __name__ == '__main__': 223 unittest.main() 224