1#! /usr/bin/python
2#
3# Protocol Buffers - Google's data interchange format
4# Copyright 2008 Google Inc.  All rights reserved.
5# http://code.google.com/p/protobuf/
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/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
44import unittest
45from google.protobuf.internal import test_bad_identifiers_pb2
46from google.protobuf import unittest_custom_options_pb2
47from google.protobuf import unittest_import_pb2
48from google.protobuf import unittest_import_public_pb2
49from google.protobuf import unittest_mset_pb2
50from google.protobuf import unittest_pb2
51from google.protobuf import unittest_no_generic_services_pb2
52from google.protobuf import service
53
54MAX_EXTENSION = 536870912
55
56
57class GeneratorTest(unittest.TestCase):
58
59  def testNestedMessageDescriptor(self):
60    field_name = 'optional_nested_message'
61    proto_type = unittest_pb2.TestAllTypes
62    self.assertEqual(
63        proto_type.NestedMessage.DESCRIPTOR,
64        proto_type.DESCRIPTOR.fields_by_name[field_name].message_type)
65
66  def testEnums(self):
67    # We test only module-level enums here.
68    # TODO(robinson): Examine descriptors directly to check
69    # enum descriptor output.
70    self.assertEqual(4, unittest_pb2.FOREIGN_FOO)
71    self.assertEqual(5, unittest_pb2.FOREIGN_BAR)
72    self.assertEqual(6, unittest_pb2.FOREIGN_BAZ)
73
74    proto = unittest_pb2.TestAllTypes()
75    self.assertEqual(1, proto.FOO)
76    self.assertEqual(1, unittest_pb2.TestAllTypes.FOO)
77    self.assertEqual(2, proto.BAR)
78    self.assertEqual(2, unittest_pb2.TestAllTypes.BAR)
79    self.assertEqual(3, proto.BAZ)
80    self.assertEqual(3, unittest_pb2.TestAllTypes.BAZ)
81
82  def testExtremeDefaultValues(self):
83    message = unittest_pb2.TestExtremeDefaultValues()
84
85    # Python pre-2.6 does not have isinf() or isnan() functions, so we have
86    # to provide our own.
87    def isnan(val):
88      # NaN is never equal to itself.
89      return val != val
90    def isinf(val):
91      # Infinity times zero equals NaN.
92      return not isnan(val) and isnan(val * 0)
93
94    self.assertTrue(isinf(message.inf_double))
95    self.assertTrue(message.inf_double > 0)
96    self.assertTrue(isinf(message.neg_inf_double))
97    self.assertTrue(message.neg_inf_double < 0)
98    self.assertTrue(isnan(message.nan_double))
99
100    self.assertTrue(isinf(message.inf_float))
101    self.assertTrue(message.inf_float > 0)
102    self.assertTrue(isinf(message.neg_inf_float))
103    self.assertTrue(message.neg_inf_float < 0)
104    self.assertTrue(isnan(message.nan_float))
105    self.assertEqual("? ? ?? ?? ??? ??/ ??-", message.cpp_trigraph)
106
107  def testHasDefaultValues(self):
108    desc = unittest_pb2.TestAllTypes.DESCRIPTOR
109
110    expected_has_default_by_name = {
111        'optional_int32': False,
112        'repeated_int32': False,
113        'optional_nested_message': False,
114        'default_int32': True,
115    }
116
117    has_default_by_name = dict(
118        [(f.name, f.has_default_value)
119         for f in desc.fields
120         if f.name in expected_has_default_by_name])
121    self.assertEqual(expected_has_default_by_name, has_default_by_name)
122
123  def testContainingTypeBehaviorForExtensions(self):
124    self.assertEqual(unittest_pb2.optional_int32_extension.containing_type,
125                     unittest_pb2.TestAllExtensions.DESCRIPTOR)
126    self.assertEqual(unittest_pb2.TestRequired.single.containing_type,
127                     unittest_pb2.TestAllExtensions.DESCRIPTOR)
128
129  def testExtensionScope(self):
130    self.assertEqual(unittest_pb2.optional_int32_extension.extension_scope,
131                     None)
132    self.assertEqual(unittest_pb2.TestRequired.single.extension_scope,
133                     unittest_pb2.TestRequired.DESCRIPTOR)
134
135  def testIsExtension(self):
136    self.assertTrue(unittest_pb2.optional_int32_extension.is_extension)
137    self.assertTrue(unittest_pb2.TestRequired.single.is_extension)
138
139    message_descriptor = unittest_pb2.TestRequired.DESCRIPTOR
140    non_extension_descriptor = message_descriptor.fields_by_name['a']
141    self.assertTrue(not non_extension_descriptor.is_extension)
142
143  def testOptions(self):
144    proto = unittest_mset_pb2.TestMessageSet()
145    self.assertTrue(proto.DESCRIPTOR.GetOptions().message_set_wire_format)
146
147  def testMessageWithCustomOptions(self):
148    proto = unittest_custom_options_pb2.TestMessageWithCustomOptions()
149    enum_options = proto.DESCRIPTOR.enum_types_by_name['AnEnum'].GetOptions()
150    self.assertTrue(enum_options is not None)
151    # TODO(gps): We really should test for the presense of the enum_opt1
152    # extension and for its value to be set to -789.
153
154  def testNestedTypes(self):
155    self.assertEquals(
156        set(unittest_pb2.TestAllTypes.DESCRIPTOR.nested_types),
157        set([
158            unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR,
159            unittest_pb2.TestAllTypes.OptionalGroup.DESCRIPTOR,
160            unittest_pb2.TestAllTypes.RepeatedGroup.DESCRIPTOR,
161        ]))
162    self.assertEqual(unittest_pb2.TestEmptyMessage.DESCRIPTOR.nested_types, [])
163    self.assertEqual(
164        unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR.nested_types, [])
165
166  def testContainingType(self):
167    self.assertTrue(
168        unittest_pb2.TestEmptyMessage.DESCRIPTOR.containing_type is None)
169    self.assertTrue(
170        unittest_pb2.TestAllTypes.DESCRIPTOR.containing_type is None)
171    self.assertEqual(
172        unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR.containing_type,
173        unittest_pb2.TestAllTypes.DESCRIPTOR)
174    self.assertEqual(
175        unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR.containing_type,
176        unittest_pb2.TestAllTypes.DESCRIPTOR)
177    self.assertEqual(
178        unittest_pb2.TestAllTypes.RepeatedGroup.DESCRIPTOR.containing_type,
179        unittest_pb2.TestAllTypes.DESCRIPTOR)
180
181  def testContainingTypeInEnumDescriptor(self):
182    self.assertTrue(unittest_pb2._FOREIGNENUM.containing_type is None)
183    self.assertEqual(unittest_pb2._TESTALLTYPES_NESTEDENUM.containing_type,
184                     unittest_pb2.TestAllTypes.DESCRIPTOR)
185
186  def testPackage(self):
187    self.assertEqual(
188        unittest_pb2.TestAllTypes.DESCRIPTOR.file.package,
189        'protobuf_unittest')
190    desc = unittest_pb2.TestAllTypes.NestedMessage.DESCRIPTOR
191    self.assertEqual(desc.file.package, 'protobuf_unittest')
192    self.assertEqual(
193        unittest_import_pb2.ImportMessage.DESCRIPTOR.file.package,
194        'protobuf_unittest_import')
195
196    self.assertEqual(
197        unittest_pb2._FOREIGNENUM.file.package, 'protobuf_unittest')
198    self.assertEqual(
199        unittest_pb2._TESTALLTYPES_NESTEDENUM.file.package,
200        'protobuf_unittest')
201    self.assertEqual(
202        unittest_import_pb2._IMPORTENUM.file.package,
203        'protobuf_unittest_import')
204
205  def testExtensionRange(self):
206    self.assertEqual(
207        unittest_pb2.TestAllTypes.DESCRIPTOR.extension_ranges, [])
208    self.assertEqual(
209        unittest_pb2.TestAllExtensions.DESCRIPTOR.extension_ranges,
210        [(1, MAX_EXTENSION)])
211    self.assertEqual(
212        unittest_pb2.TestMultipleExtensionRanges.DESCRIPTOR.extension_ranges,
213        [(42, 43), (4143, 4244), (65536, MAX_EXTENSION)])
214
215  def testFileDescriptor(self):
216    self.assertEqual(unittest_pb2.DESCRIPTOR.name,
217                     'google/protobuf/unittest.proto')
218    self.assertEqual(unittest_pb2.DESCRIPTOR.package, 'protobuf_unittest')
219    self.assertFalse(unittest_pb2.DESCRIPTOR.serialized_pb is None)
220
221  def testNoGenericServices(self):
222    self.assertTrue(hasattr(unittest_no_generic_services_pb2, "TestMessage"))
223    self.assertTrue(hasattr(unittest_no_generic_services_pb2, "FOO"))
224    self.assertTrue(hasattr(unittest_no_generic_services_pb2, "test_extension"))
225
226    # Make sure unittest_no_generic_services_pb2 has no services subclassing
227    # Proto2 Service class.
228    if hasattr(unittest_no_generic_services_pb2, "TestService"):
229      self.assertFalse(issubclass(unittest_no_generic_services_pb2.TestService,
230                                  service.Service))
231
232  def testMessageTypesByName(self):
233    file_type = unittest_pb2.DESCRIPTOR
234    self.assertEqual(
235        unittest_pb2._TESTALLTYPES,
236        file_type.message_types_by_name[unittest_pb2._TESTALLTYPES.name])
237
238    # Nested messages shouldn't be included in the message_types_by_name
239    # dictionary (like in the C++ API).
240    self.assertFalse(
241        unittest_pb2._TESTALLTYPES_NESTEDMESSAGE.name in
242        file_type.message_types_by_name)
243
244  def testPublicImports(self):
245    # Test public imports as embedded message.
246    all_type_proto = unittest_pb2.TestAllTypes()
247    self.assertEqual(0, all_type_proto.optional_public_import_message.e)
248
249    # PublicImportMessage is actually defined in unittest_import_public_pb2
250    # module, and is public imported by unittest_import_pb2 module.
251    public_import_proto = unittest_import_pb2.PublicImportMessage()
252    self.assertEqual(0, public_import_proto.e)
253    self.assertTrue(unittest_import_public_pb2.PublicImportMessage is
254                    unittest_import_pb2.PublicImportMessage)
255
256  def testBadIdentifiers(self):
257    # We're just testing that the code was imported without problems.
258    message = test_bad_identifiers_pb2.TestBadIdentifiers()
259    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.message],
260                     "foo")
261    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.descriptor],
262                     "bar")
263    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.reflection],
264                     "baz")
265    self.assertEqual(message.Extensions[test_bad_identifiers_pb2.service],
266                     "qux")
267
268if __name__ == '__main__':
269  unittest.main()
270