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# TODO(robinson): Flesh this out considerably.  We focused on reflection_test.py
34# first, since it's testing the subtler code, and since it provides decent
35# indirect testing of the protocol compiler output.
36
37"""Unittest that directly tests the output of the pure-Python protocol
38compiler.  See //google/protobuf/internal/reflection_test.py for a test which
39further ensures that we can use Python protocol message objects as we expect.
40"""
41
42__author__ = 'robinson@google.com (Will Robinson)'
43
44try:
45  import unittest2 as unittest  #PY26
46except ImportError:
47  import unittest
48
49from google.protobuf.internal import test_bad_identifiers_pb2
50from google.protobuf import unittest_custom_options_pb2
51from google.protobuf import unittest_import_pb2
52from google.protobuf import unittest_import_public_pb2
53from google.protobuf import unittest_mset_pb2
54from google.protobuf import unittest_mset_wire_format_pb2
55from google.protobuf import unittest_no_generic_services_pb2
56from google.protobuf import unittest_pb2
57from google.protobuf import service
58from google.protobuf import symbol_database
59
60MAX_EXTENSION = 536870912
61
62
63class GeneratorTest(unittest.TestCase):
64
65  def testNestedMessageDescriptor(self):
66    field_name = 'optional_nested_message'
67    proto_type = unittest_pb2.TestAllTypes
68    self.assertEqual(
69        proto_type.NestedMessage.DESCRIPTOR,
70        proto_type.DESCRIPTOR.fields_by_name[field_name].message_type)
71
72  def testEnums(self):
73    # We test only module-level enums here.
74    # TODO(robinson): Examine descriptors directly to check
75    # enum descriptor output.
76    self.assertEqual(4, unittest_pb2.FOREIGN_FOO)
77    self.assertEqual(5, unittest_pb2.FOREIGN_BAR)
78    self.assertEqual(6, unittest_pb2.FOREIGN_BAZ)
79
80    proto = unittest_pb2.TestAllTypes()
81    self.assertEqual(1, proto.FOO)
82    self.assertEqual(1, unittest_pb2.TestAllTypes.FOO)
83    self.assertEqual(2, proto.BAR)
84    self.assertEqual(2, unittest_pb2.TestAllTypes.BAR)
85    self.assertEqual(3, proto.BAZ)
86    self.assertEqual(3, unittest_pb2.TestAllTypes.BAZ)
87
88  def testExtremeDefaultValues(self):
89    message = unittest_pb2.TestExtremeDefaultValues()
90
91    # Python pre-2.6 does not have isinf() or isnan() functions, so we have
92    # to provide our own.
93    def isnan(val):
94      # NaN is never equal to itself.
95      return val != val
96    def isinf(val):
97      # Infinity times zero equals NaN.
98      return not isnan(val) and isnan(val * 0)
99
100    self.assertTrue(isinf(message.inf_double))
101    self.assertTrue(message.inf_double > 0)
102    self.assertTrue(isinf(message.neg_inf_double))
103    self.assertTrue(message.neg_inf_double < 0)
104    self.assertTrue(isnan(message.nan_double))
105
106    self.assertTrue(isinf(message.inf_float))
107    self.assertTrue(message.inf_float > 0)
108    self.assertTrue(isinf(message.neg_inf_float))
109    self.assertTrue(message.neg_inf_float < 0)
110    self.assertTrue(isnan(message.nan_float))
111    self.assertEqual("? ? ?? ?? ??? ??/ ??-", message.cpp_trigraph)
112
113  def testHasDefaultValues(self):
114    desc = unittest_pb2.TestAllTypes.DESCRIPTOR
115
116    expected_has_default_by_name = {
117        'optional_int32': False,
118        'repeated_int32': False,
119        'optional_nested_message': False,
120        'default_int32': True,
121    }
122
123    has_default_by_name = dict(
124        [(f.name, f.has_default_value)
125         for f in desc.fields
126         if f.name in expected_has_default_by_name])
127    self.assertEqual(expected_has_default_by_name, has_default_by_name)
128
129  def testContainingTypeBehaviorForExtensions(self):
130    self.assertEqual(unittest_pb2.optional_int32_extension.containing_type,
131                     unittest_pb2.TestAllExtensions.DESCRIPTOR)
132    self.assertEqual(unittest_pb2.TestRequired.single.containing_type,
133                     unittest_pb2.TestAllExtensions.DESCRIPTOR)
134
135  def testExtensionScope(self):
136    self.assertEqual(unittest_pb2.optional_int32_extension.extension_scope,
137                     None)
138    self.assertEqual(unittest_pb2.TestRequired.single.extension_scope,
139                     unittest_pb2.TestRequired.DESCRIPTOR)
140
141  def testIsExtension(self):
142    self.assertTrue(unittest_pb2.optional_int32_extension.is_extension)
143    self.assertTrue(unittest_pb2.TestRequired.single.is_extension)
144
145    message_descriptor = unittest_pb2.TestRequired.DESCRIPTOR
146    non_extension_descriptor = message_descriptor.fields_by_name['a']
147    self.assertTrue(not non_extension_descriptor.is_extension)
148
149  def testOptions(self):
150    proto = unittest_mset_wire_format_pb2.TestMessageSet()
151    self.assertTrue(proto.DESCRIPTOR.GetOptions().message_set_wire_format)
152
153  def testMessageWithCustomOptions(self):
154    proto = unittest_custom_options_pb2.TestMessageWithCustomOptions()
155    enum_options = proto.DESCRIPTOR.enum_types_by_name['AnEnum'].GetOptions()
156    self.assertTrue(enum_options is not None)
157    # TODO(gps): We really should test for the presence of the enum_opt1
158    # extension and for its value to be set to -789.
159
160  def testNestedTypes(self):
161    self.assertEqual(
162        set(unittest_pb2.TestAllTypes.DESCRIPTOR.nested_types),
163        set([
164            unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR,
165            unittest_pb2.TestAllTypes.OptionalGroup.DESCRIPTOR,
166            unittest_pb2.TestAllTypes.RepeatedGroup.DESCRIPTOR,
167        ]))
168    self.assertEqual(unittest_pb2.TestEmptyMessage.DESCRIPTOR.nested_types, [])
169    self.assertEqual(
170        unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR.nested_types, [])
171
172  def testContainingType(self):
173    self.assertTrue(
174        unittest_pb2.TestEmptyMessage.DESCRIPTOR.containing_type is None)
175    self.assertTrue(
176        unittest_pb2.TestAllTypes.DESCRIPTOR.containing_type is None)
177    self.assertEqual(
178        unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR.containing_type,
179        unittest_pb2.TestAllTypes.DESCRIPTOR)
180    self.assertEqual(
181        unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR.containing_type,
182        unittest_pb2.TestAllTypes.DESCRIPTOR)
183    self.assertEqual(
184        unittest_pb2.TestAllTypes.RepeatedGroup.DESCRIPTOR.containing_type,
185        unittest_pb2.TestAllTypes.DESCRIPTOR)
186
187  def testContainingTypeInEnumDescriptor(self):
188    self.assertTrue(unittest_pb2._FOREIGNENUM.containing_type is None)
189    self.assertEqual(unittest_pb2._TESTALLTYPES_NESTEDENUM.containing_type,
190                     unittest_pb2.TestAllTypes.DESCRIPTOR)
191
192  def testPackage(self):
193    self.assertEqual(
194        unittest_pb2.TestAllTypes.DESCRIPTOR.file.package,
195        'protobuf_unittest')
196    desc = unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR
197    self.assertEqual(desc.file.package, 'protobuf_unittest')
198    self.assertEqual(
199        unittest_import_pb2.ImportMessage.DESCRIPTOR.file.package,
200        'protobuf_unittest_import')
201
202    self.assertEqual(
203        unittest_pb2._FOREIGNENUM.file.package, 'protobuf_unittest')
204    self.assertEqual(
205        unittest_pb2._TESTALLTYPES_NESTEDENUM.file.package,
206        'protobuf_unittest')
207    self.assertEqual(
208        unittest_import_pb2._IMPORTENUM.file.package,
209        'protobuf_unittest_import')
210
211  def testExtensionRange(self):
212    self.assertEqual(
213        unittest_pb2.TestAllTypes.DESCRIPTOR.extension_ranges, [])
214    self.assertEqual(
215        unittest_pb2.TestAllExtensions.DESCRIPTOR.extension_ranges,
216        [(1, MAX_EXTENSION)])
217    self.assertEqual(
218        unittest_pb2.TestMultipleExtensionRanges.DESCRIPTOR.extension_ranges,
219        [(42, 43), (4143, 4244), (65536, MAX_EXTENSION)])
220
221  def testFileDescriptor(self):
222    self.assertEqual(unittest_pb2.DESCRIPTOR.name,
223                     'google/protobuf/unittest.proto')
224    self.assertEqual(unittest_pb2.DESCRIPTOR.package, 'protobuf_unittest')
225    self.assertFalse(unittest_pb2.DESCRIPTOR.serialized_pb is None)
226    self.assertEqual(unittest_pb2.DESCRIPTOR.dependencies,
227                     [unittest_import_pb2.DESCRIPTOR])
228    self.assertEqual(unittest_import_pb2.DESCRIPTOR.dependencies,
229                     [unittest_import_public_pb2.DESCRIPTOR])
230
231  def testNoGenericServices(self):
232    self.assertTrue(hasattr(unittest_no_generic_services_pb2, "TestMessage"))
233    self.assertTrue(hasattr(unittest_no_generic_services_pb2, "FOO"))
234    self.assertTrue(hasattr(unittest_no_generic_services_pb2, "test_extension"))
235
236    # Make sure unittest_no_generic_services_pb2 has no services subclassing
237    # Proto2 Service class.
238    if hasattr(unittest_no_generic_services_pb2, "TestService"):
239      self.assertFalse(issubclass(unittest_no_generic_services_pb2.TestService,
240                                  service.Service))
241
242  def testMessageTypesByName(self):
243    file_type = unittest_pb2.DESCRIPTOR
244    self.assertEqual(
245        unittest_pb2._TESTALLTYPES,
246        file_type.message_types_by_name[unittest_pb2._TESTALLTYPES.name])
247
248    # Nested messages shouldn't be included in the message_types_by_name
249    # dictionary (like in the C++ API).
250    self.assertFalse(
251        unittest_pb2._TESTALLTYPES_NESTEDMESSAGE.name in
252        file_type.message_types_by_name)
253
254  def testEnumTypesByName(self):
255    file_type = unittest_pb2.DESCRIPTOR
256    self.assertEqual(
257        unittest_pb2._FOREIGNENUM,
258        file_type.enum_types_by_name[unittest_pb2._FOREIGNENUM.name])
259
260  def testExtensionsByName(self):
261    file_type = unittest_pb2.DESCRIPTOR
262    self.assertEqual(
263        unittest_pb2.my_extension_string,
264        file_type.extensions_by_name[unittest_pb2.my_extension_string.name])
265
266  def testPublicImports(self):
267    # Test public imports as embedded message.
268    all_type_proto = unittest_pb2.TestAllTypes()
269    self.assertEqual(0, all_type_proto.optional_public_import_message.e)
270
271    # PublicImportMessage is actually defined in unittest_import_public_pb2
272    # module, and is public imported by unittest_import_pb2 module.
273    public_import_proto = unittest_import_pb2.PublicImportMessage()
274    self.assertEqual(0, public_import_proto.e)
275    self.assertTrue(unittest_import_public_pb2.PublicImportMessage is
276                    unittest_import_pb2.PublicImportMessage)
277
278  def testBadIdentifiers(self):
279    # We're just testing that the code was imported without problems.
280    message = test_bad_identifiers_pb2.TestBadIdentifiers()
281    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.message],
282                     "foo")
283    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.descriptor],
284                     "bar")
285    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.reflection],
286                     "baz")
287    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.service],
288                     "qux")
289
290  def testOneof(self):
291    desc = unittest_pb2.TestAllTypes.DESCRIPTOR
292    self.assertEqual(1, len(desc.oneofs))
293    self.assertEqual('oneof_field', desc.oneofs[0].name)
294    self.assertEqual(0, desc.oneofs[0].index)
295    self.assertIs(desc, desc.oneofs[0].containing_type)
296    self.assertIs(desc.oneofs[0], desc.oneofs_by_name['oneof_field'])
297    nested_names = set(['oneof_uint32', 'oneof_nested_message',
298                        'oneof_string', 'oneof_bytes'])
299    self.assertEqual(
300        nested_names,
301        set([field.name for field in desc.oneofs[0].fields]))
302    for field_name, field_desc in desc.fields_by_name.items():
303      if field_name in nested_names:
304        self.assertIs(desc.oneofs[0], field_desc.containing_oneof)
305      else:
306        self.assertIsNone(field_desc.containing_oneof)
307
308
309class SymbolDatabaseRegistrationTest(unittest.TestCase):
310  """Checks that messages, enums and files are correctly registered."""
311
312  def testGetSymbol(self):
313    self.assertEqual(
314        unittest_pb2.TestAllTypes, symbol_database.Default().GetSymbol(
315            'protobuf_unittest.TestAllTypes'))
316    self.assertEqual(
317        unittest_pb2.TestAllTypes.NestedMessage,
318        symbol_database.Default().GetSymbol(
319            'protobuf_unittest.TestAllTypes.NestedMessage'))
320    with self.assertRaises(KeyError):
321      symbol_database.Default().GetSymbol('protobuf_unittest.NestedMessage')
322    self.assertEqual(
323        unittest_pb2.TestAllTypes.OptionalGroup,
324        symbol_database.Default().GetSymbol(
325            'protobuf_unittest.TestAllTypes.OptionalGroup'))
326    self.assertEqual(
327        unittest_pb2.TestAllTypes.RepeatedGroup,
328        symbol_database.Default().GetSymbol(
329            'protobuf_unittest.TestAllTypes.RepeatedGroup'))
330
331  def testEnums(self):
332    self.assertEqual(
333        'protobuf_unittest.ForeignEnum',
334        symbol_database.Default().pool.FindEnumTypeByName(
335            'protobuf_unittest.ForeignEnum').full_name)
336    self.assertEqual(
337        'protobuf_unittest.TestAllTypes.NestedEnum',
338        symbol_database.Default().pool.FindEnumTypeByName(
339            'protobuf_unittest.TestAllTypes.NestedEnum').full_name)
340
341  def testFindFileByName(self):
342    self.assertEqual(
343        'google/protobuf/unittest.proto',
344        symbol_database.Default().pool.FindFileByName(
345            'google/protobuf/unittest.proto').name)
346
347if __name__ == '__main__':
348  unittest.main()
349