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"""Test for google.protobuf.internal.well_known_types."""
34
35__author__ = 'jieluo@google.com (Jie Luo)'
36
37from datetime import datetime
38
39try:
40  import unittest2 as unittest  #PY26
41except ImportError:
42  import unittest
43
44from google.protobuf import any_pb2
45from google.protobuf import duration_pb2
46from google.protobuf import field_mask_pb2
47from google.protobuf import struct_pb2
48from google.protobuf import timestamp_pb2
49from google.protobuf import unittest_pb2
50from google.protobuf.internal import any_test_pb2
51from google.protobuf.internal import test_util
52from google.protobuf.internal import well_known_types
53from google.protobuf import descriptor
54from google.protobuf import text_format
55
56
57class TimeUtilTestBase(unittest.TestCase):
58
59  def CheckTimestampConversion(self, message, text):
60    self.assertEqual(text, message.ToJsonString())
61    parsed_message = timestamp_pb2.Timestamp()
62    parsed_message.FromJsonString(text)
63    self.assertEqual(message, parsed_message)
64
65  def CheckDurationConversion(self, message, text):
66    self.assertEqual(text, message.ToJsonString())
67    parsed_message = duration_pb2.Duration()
68    parsed_message.FromJsonString(text)
69    self.assertEqual(message, parsed_message)
70
71
72class TimeUtilTest(TimeUtilTestBase):
73
74  def testTimestampSerializeAndParse(self):
75    message = timestamp_pb2.Timestamp()
76    # Generated output should contain 3, 6, or 9 fractional digits.
77    message.seconds = 0
78    message.nanos = 0
79    self.CheckTimestampConversion(message, '1970-01-01T00:00:00Z')
80    message.nanos = 10000000
81    self.CheckTimestampConversion(message, '1970-01-01T00:00:00.010Z')
82    message.nanos = 10000
83    self.CheckTimestampConversion(message, '1970-01-01T00:00:00.000010Z')
84    message.nanos = 10
85    self.CheckTimestampConversion(message, '1970-01-01T00:00:00.000000010Z')
86    # Test min timestamps.
87    message.seconds = -62135596800
88    message.nanos = 0
89    self.CheckTimestampConversion(message, '0001-01-01T00:00:00Z')
90    # Test max timestamps.
91    message.seconds = 253402300799
92    message.nanos = 999999999
93    self.CheckTimestampConversion(message, '9999-12-31T23:59:59.999999999Z')
94    # Test negative timestamps.
95    message.seconds = -1
96    self.CheckTimestampConversion(message, '1969-12-31T23:59:59.999999999Z')
97
98    # Parsing accepts an fractional digits as long as they fit into nano
99    # precision.
100    message.FromJsonString('1970-01-01T00:00:00.1Z')
101    self.assertEqual(0, message.seconds)
102    self.assertEqual(100000000, message.nanos)
103    # Parsing accpets offsets.
104    message.FromJsonString('1970-01-01T00:00:00-08:00')
105    self.assertEqual(8 * 3600, message.seconds)
106    self.assertEqual(0, message.nanos)
107
108  def testDurationSerializeAndParse(self):
109    message = duration_pb2.Duration()
110    # Generated output should contain 3, 6, or 9 fractional digits.
111    message.seconds = 0
112    message.nanos = 0
113    self.CheckDurationConversion(message, '0s')
114    message.nanos = 10000000
115    self.CheckDurationConversion(message, '0.010s')
116    message.nanos = 10000
117    self.CheckDurationConversion(message, '0.000010s')
118    message.nanos = 10
119    self.CheckDurationConversion(message, '0.000000010s')
120
121    # Test min and max
122    message.seconds = 315576000000
123    message.nanos = 999999999
124    self.CheckDurationConversion(message, '315576000000.999999999s')
125    message.seconds = -315576000000
126    message.nanos = -999999999
127    self.CheckDurationConversion(message, '-315576000000.999999999s')
128
129    # Parsing accepts an fractional digits as long as they fit into nano
130    # precision.
131    message.FromJsonString('0.1s')
132    self.assertEqual(100000000, message.nanos)
133    message.FromJsonString('0.0000001s')
134    self.assertEqual(100, message.nanos)
135
136  def testTimestampIntegerConversion(self):
137    message = timestamp_pb2.Timestamp()
138    message.FromNanoseconds(1)
139    self.assertEqual('1970-01-01T00:00:00.000000001Z',
140                     message.ToJsonString())
141    self.assertEqual(1, message.ToNanoseconds())
142
143    message.FromNanoseconds(-1)
144    self.assertEqual('1969-12-31T23:59:59.999999999Z',
145                     message.ToJsonString())
146    self.assertEqual(-1, message.ToNanoseconds())
147
148    message.FromMicroseconds(1)
149    self.assertEqual('1970-01-01T00:00:00.000001Z',
150                     message.ToJsonString())
151    self.assertEqual(1, message.ToMicroseconds())
152
153    message.FromMicroseconds(-1)
154    self.assertEqual('1969-12-31T23:59:59.999999Z',
155                     message.ToJsonString())
156    self.assertEqual(-1, message.ToMicroseconds())
157
158    message.FromMilliseconds(1)
159    self.assertEqual('1970-01-01T00:00:00.001Z',
160                     message.ToJsonString())
161    self.assertEqual(1, message.ToMilliseconds())
162
163    message.FromMilliseconds(-1)
164    self.assertEqual('1969-12-31T23:59:59.999Z',
165                     message.ToJsonString())
166    self.assertEqual(-1, message.ToMilliseconds())
167
168    message.FromSeconds(1)
169    self.assertEqual('1970-01-01T00:00:01Z',
170                     message.ToJsonString())
171    self.assertEqual(1, message.ToSeconds())
172
173    message.FromSeconds(-1)
174    self.assertEqual('1969-12-31T23:59:59Z',
175                     message.ToJsonString())
176    self.assertEqual(-1, message.ToSeconds())
177
178    message.FromNanoseconds(1999)
179    self.assertEqual(1, message.ToMicroseconds())
180    # For negative values, Timestamp will be rounded down.
181    # For example, "1969-12-31T23:59:59.5Z" (i.e., -0.5s) rounded to seconds
182    # will be "1969-12-31T23:59:59Z" (i.e., -1s) rather than
183    # "1970-01-01T00:00:00Z" (i.e., 0s).
184    message.FromNanoseconds(-1999)
185    self.assertEqual(-2, message.ToMicroseconds())
186
187  def testDurationIntegerConversion(self):
188    message = duration_pb2.Duration()
189    message.FromNanoseconds(1)
190    self.assertEqual('0.000000001s',
191                     message.ToJsonString())
192    self.assertEqual(1, message.ToNanoseconds())
193
194    message.FromNanoseconds(-1)
195    self.assertEqual('-0.000000001s',
196                     message.ToJsonString())
197    self.assertEqual(-1, message.ToNanoseconds())
198
199    message.FromMicroseconds(1)
200    self.assertEqual('0.000001s',
201                     message.ToJsonString())
202    self.assertEqual(1, message.ToMicroseconds())
203
204    message.FromMicroseconds(-1)
205    self.assertEqual('-0.000001s',
206                     message.ToJsonString())
207    self.assertEqual(-1, message.ToMicroseconds())
208
209    message.FromMilliseconds(1)
210    self.assertEqual('0.001s',
211                     message.ToJsonString())
212    self.assertEqual(1, message.ToMilliseconds())
213
214    message.FromMilliseconds(-1)
215    self.assertEqual('-0.001s',
216                     message.ToJsonString())
217    self.assertEqual(-1, message.ToMilliseconds())
218
219    message.FromSeconds(1)
220    self.assertEqual('1s', message.ToJsonString())
221    self.assertEqual(1, message.ToSeconds())
222
223    message.FromSeconds(-1)
224    self.assertEqual('-1s',
225                     message.ToJsonString())
226    self.assertEqual(-1, message.ToSeconds())
227
228    # Test truncation behavior.
229    message.FromNanoseconds(1999)
230    self.assertEqual(1, message.ToMicroseconds())
231
232    # For negative values, Duration will be rounded towards 0.
233    message.FromNanoseconds(-1999)
234    self.assertEqual(-1, message.ToMicroseconds())
235
236  def testDatetimeConverison(self):
237    message = timestamp_pb2.Timestamp()
238    dt = datetime(1970, 1, 1)
239    message.FromDatetime(dt)
240    self.assertEqual(dt, message.ToDatetime())
241
242    message.FromMilliseconds(1999)
243    self.assertEqual(datetime(1970, 1, 1, 0, 0, 1, 999000),
244                     message.ToDatetime())
245
246  def testTimedeltaConversion(self):
247    message = duration_pb2.Duration()
248    message.FromNanoseconds(1999999999)
249    td = message.ToTimedelta()
250    self.assertEqual(1, td.seconds)
251    self.assertEqual(999999, td.microseconds)
252
253    message.FromNanoseconds(-1999999999)
254    td = message.ToTimedelta()
255    self.assertEqual(-1, td.days)
256    self.assertEqual(86398, td.seconds)
257    self.assertEqual(1, td.microseconds)
258
259    message.FromMicroseconds(-1)
260    td = message.ToTimedelta()
261    self.assertEqual(-1, td.days)
262    self.assertEqual(86399, td.seconds)
263    self.assertEqual(999999, td.microseconds)
264    converted_message = duration_pb2.Duration()
265    converted_message.FromTimedelta(td)
266    self.assertEqual(message, converted_message)
267
268  def testInvalidTimestamp(self):
269    message = timestamp_pb2.Timestamp()
270    self.assertRaisesRegexp(
271        ValueError,
272        'time data \'10000-01-01T00:00:00\' does not match'
273        ' format \'%Y-%m-%dT%H:%M:%S\'',
274        message.FromJsonString, '10000-01-01T00:00:00.00Z')
275    self.assertRaisesRegexp(
276        well_known_types.ParseError,
277        'nanos 0123456789012 more than 9 fractional digits.',
278        message.FromJsonString,
279        '1970-01-01T00:00:00.0123456789012Z')
280    self.assertRaisesRegexp(
281        well_known_types.ParseError,
282        (r'Invalid timezone offset value: \+08.'),
283        message.FromJsonString,
284        '1972-01-01T01:00:00.01+08',)
285    self.assertRaisesRegexp(
286        ValueError,
287        'year is out of range',
288        message.FromJsonString,
289        '0000-01-01T00:00:00Z')
290    message.seconds = 253402300800
291    self.assertRaisesRegexp(
292        OverflowError,
293        'date value out of range',
294        message.ToJsonString)
295
296  def testInvalidDuration(self):
297    message = duration_pb2.Duration()
298    self.assertRaisesRegexp(
299        well_known_types.ParseError,
300        'Duration must end with letter "s": 1.',
301        message.FromJsonString, '1')
302    self.assertRaisesRegexp(
303        well_known_types.ParseError,
304        'Couldn\'t parse duration: 1...2s.',
305        message.FromJsonString, '1...2s')
306
307
308class FieldMaskTest(unittest.TestCase):
309
310  def testStringFormat(self):
311    mask = field_mask_pb2.FieldMask()
312    self.assertEqual('', mask.ToJsonString())
313    mask.paths.append('foo')
314    self.assertEqual('foo', mask.ToJsonString())
315    mask.paths.append('bar')
316    self.assertEqual('foo,bar', mask.ToJsonString())
317
318    mask.FromJsonString('')
319    self.assertEqual('', mask.ToJsonString())
320    mask.FromJsonString('foo')
321    self.assertEqual(['foo'], mask.paths)
322    mask.FromJsonString('foo,bar')
323    self.assertEqual(['foo', 'bar'], mask.paths)
324
325  def testDescriptorToFieldMask(self):
326    mask = field_mask_pb2.FieldMask()
327    msg_descriptor = unittest_pb2.TestAllTypes.DESCRIPTOR
328    mask.AllFieldsFromDescriptor(msg_descriptor)
329    self.assertEqual(75, len(mask.paths))
330    self.assertTrue(mask.IsValidForDescriptor(msg_descriptor))
331    for field in msg_descriptor.fields:
332      self.assertTrue(field.name in mask.paths)
333    mask.paths.append('optional_nested_message.bb')
334    self.assertTrue(mask.IsValidForDescriptor(msg_descriptor))
335    mask.paths.append('repeated_nested_message.bb')
336    self.assertFalse(mask.IsValidForDescriptor(msg_descriptor))
337
338  def testCanonicalFrom(self):
339    mask = field_mask_pb2.FieldMask()
340    out_mask = field_mask_pb2.FieldMask()
341    # Paths will be sorted.
342    mask.FromJsonString('baz.quz,bar,foo')
343    out_mask.CanonicalFormFromMask(mask)
344    self.assertEqual('bar,baz.quz,foo', out_mask.ToJsonString())
345    # Duplicated paths will be removed.
346    mask.FromJsonString('foo,bar,foo')
347    out_mask.CanonicalFormFromMask(mask)
348    self.assertEqual('bar,foo', out_mask.ToJsonString())
349    # Sub-paths of other paths will be removed.
350    mask.FromJsonString('foo.b1,bar.b1,foo.b2,bar')
351    out_mask.CanonicalFormFromMask(mask)
352    self.assertEqual('bar,foo.b1,foo.b2', out_mask.ToJsonString())
353
354    # Test more deeply nested cases.
355    mask.FromJsonString(
356        'foo.bar.baz1,foo.bar.baz2.quz,foo.bar.baz2')
357    out_mask.CanonicalFormFromMask(mask)
358    self.assertEqual('foo.bar.baz1,foo.bar.baz2',
359                     out_mask.ToJsonString())
360    mask.FromJsonString(
361        'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz')
362    out_mask.CanonicalFormFromMask(mask)
363    self.assertEqual('foo.bar.baz1,foo.bar.baz2',
364                     out_mask.ToJsonString())
365    mask.FromJsonString(
366        'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo.bar')
367    out_mask.CanonicalFormFromMask(mask)
368    self.assertEqual('foo.bar', out_mask.ToJsonString())
369    mask.FromJsonString(
370        'foo.bar.baz1,foo.bar.baz2,foo.bar.baz2.quz,foo')
371    out_mask.CanonicalFormFromMask(mask)
372    self.assertEqual('foo', out_mask.ToJsonString())
373
374  def testUnion(self):
375    mask1 = field_mask_pb2.FieldMask()
376    mask2 = field_mask_pb2.FieldMask()
377    out_mask = field_mask_pb2.FieldMask()
378    mask1.FromJsonString('foo,baz')
379    mask2.FromJsonString('bar,quz')
380    out_mask.Union(mask1, mask2)
381    self.assertEqual('bar,baz,foo,quz', out_mask.ToJsonString())
382    # Overlap with duplicated paths.
383    mask1.FromJsonString('foo,baz.bb')
384    mask2.FromJsonString('baz.bb,quz')
385    out_mask.Union(mask1, mask2)
386    self.assertEqual('baz.bb,foo,quz', out_mask.ToJsonString())
387    # Overlap with paths covering some other paths.
388    mask1.FromJsonString('foo.bar.baz,quz')
389    mask2.FromJsonString('foo.bar,bar')
390    out_mask.Union(mask1, mask2)
391    self.assertEqual('bar,foo.bar,quz', out_mask.ToJsonString())
392
393  def testIntersect(self):
394    mask1 = field_mask_pb2.FieldMask()
395    mask2 = field_mask_pb2.FieldMask()
396    out_mask = field_mask_pb2.FieldMask()
397    # Test cases without overlapping.
398    mask1.FromJsonString('foo,baz')
399    mask2.FromJsonString('bar,quz')
400    out_mask.Intersect(mask1, mask2)
401    self.assertEqual('', out_mask.ToJsonString())
402    # Overlap with duplicated paths.
403    mask1.FromJsonString('foo,baz.bb')
404    mask2.FromJsonString('baz.bb,quz')
405    out_mask.Intersect(mask1, mask2)
406    self.assertEqual('baz.bb', out_mask.ToJsonString())
407    # Overlap with paths covering some other paths.
408    mask1.FromJsonString('foo.bar.baz,quz')
409    mask2.FromJsonString('foo.bar,bar')
410    out_mask.Intersect(mask1, mask2)
411    self.assertEqual('foo.bar.baz', out_mask.ToJsonString())
412    mask1.FromJsonString('foo.bar,bar')
413    mask2.FromJsonString('foo.bar.baz,quz')
414    out_mask.Intersect(mask1, mask2)
415    self.assertEqual('foo.bar.baz', out_mask.ToJsonString())
416
417  def testMergeMessage(self):
418    # Test merge one field.
419    src = unittest_pb2.TestAllTypes()
420    test_util.SetAllFields(src)
421    for field in src.DESCRIPTOR.fields:
422      if field.containing_oneof:
423        continue
424      field_name = field.name
425      dst = unittest_pb2.TestAllTypes()
426      # Only set one path to mask.
427      mask = field_mask_pb2.FieldMask()
428      mask.paths.append(field_name)
429      mask.MergeMessage(src, dst)
430      # The expected result message.
431      msg = unittest_pb2.TestAllTypes()
432      if field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
433        repeated_src = getattr(src, field_name)
434        repeated_msg = getattr(msg, field_name)
435        if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
436          for item in repeated_src:
437            repeated_msg.add().CopyFrom(item)
438        else:
439          repeated_msg.extend(repeated_src)
440      elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
441        getattr(msg, field_name).CopyFrom(getattr(src, field_name))
442      else:
443        setattr(msg, field_name, getattr(src, field_name))
444      # Only field specified in mask is merged.
445      self.assertEqual(msg, dst)
446
447    # Test merge nested fields.
448    nested_src = unittest_pb2.NestedTestAllTypes()
449    nested_dst = unittest_pb2.NestedTestAllTypes()
450    nested_src.child.payload.optional_int32 = 1234
451    nested_src.child.child.payload.optional_int32 = 5678
452    mask = field_mask_pb2.FieldMask()
453    mask.FromJsonString('child.payload')
454    mask.MergeMessage(nested_src, nested_dst)
455    self.assertEqual(1234, nested_dst.child.payload.optional_int32)
456    self.assertEqual(0, nested_dst.child.child.payload.optional_int32)
457
458    mask.FromJsonString('child.child.payload')
459    mask.MergeMessage(nested_src, nested_dst)
460    self.assertEqual(1234, nested_dst.child.payload.optional_int32)
461    self.assertEqual(5678, nested_dst.child.child.payload.optional_int32)
462
463    nested_dst.Clear()
464    mask.FromJsonString('child.child.payload')
465    mask.MergeMessage(nested_src, nested_dst)
466    self.assertEqual(0, nested_dst.child.payload.optional_int32)
467    self.assertEqual(5678, nested_dst.child.child.payload.optional_int32)
468
469    nested_dst.Clear()
470    mask.FromJsonString('child')
471    mask.MergeMessage(nested_src, nested_dst)
472    self.assertEqual(1234, nested_dst.child.payload.optional_int32)
473    self.assertEqual(5678, nested_dst.child.child.payload.optional_int32)
474
475    # Test MergeOptions.
476    nested_dst.Clear()
477    nested_dst.child.payload.optional_int64 = 4321
478    # Message fields will be merged by default.
479    mask.FromJsonString('child.payload')
480    mask.MergeMessage(nested_src, nested_dst)
481    self.assertEqual(1234, nested_dst.child.payload.optional_int32)
482    self.assertEqual(4321, nested_dst.child.payload.optional_int64)
483    # Change the behavior to replace message fields.
484    mask.FromJsonString('child.payload')
485    mask.MergeMessage(nested_src, nested_dst, True, False)
486    self.assertEqual(1234, nested_dst.child.payload.optional_int32)
487    self.assertEqual(0, nested_dst.child.payload.optional_int64)
488
489    # By default, fields missing in source are not cleared in destination.
490    nested_dst.payload.optional_int32 = 1234
491    self.assertTrue(nested_dst.HasField('payload'))
492    mask.FromJsonString('payload')
493    mask.MergeMessage(nested_src, nested_dst)
494    self.assertTrue(nested_dst.HasField('payload'))
495    # But they are cleared when replacing message fields.
496    nested_dst.Clear()
497    nested_dst.payload.optional_int32 = 1234
498    mask.FromJsonString('payload')
499    mask.MergeMessage(nested_src, nested_dst, True, False)
500    self.assertFalse(nested_dst.HasField('payload'))
501
502    nested_src.payload.repeated_int32.append(1234)
503    nested_dst.payload.repeated_int32.append(5678)
504    # Repeated fields will be appended by default.
505    mask.FromJsonString('payload.repeated_int32')
506    mask.MergeMessage(nested_src, nested_dst)
507    self.assertEqual(2, len(nested_dst.payload.repeated_int32))
508    self.assertEqual(5678, nested_dst.payload.repeated_int32[0])
509    self.assertEqual(1234, nested_dst.payload.repeated_int32[1])
510    # Change the behavior to replace repeated fields.
511    mask.FromJsonString('payload.repeated_int32')
512    mask.MergeMessage(nested_src, nested_dst, False, True)
513    self.assertEqual(1, len(nested_dst.payload.repeated_int32))
514    self.assertEqual(1234, nested_dst.payload.repeated_int32[0])
515
516
517class StructTest(unittest.TestCase):
518
519  def testStruct(self):
520    struct = struct_pb2.Struct()
521    struct_class = struct.__class__
522
523    struct['key1'] = 5
524    struct['key2'] = 'abc'
525    struct['key3'] = True
526    struct.get_or_create_struct('key4')['subkey'] = 11.0
527    struct_list = struct.get_or_create_list('key5')
528    struct_list.extend([6, 'seven', True, False, None])
529    struct_list.add_struct()['subkey2'] = 9
530
531    self.assertTrue(isinstance(struct, well_known_types.Struct))
532    self.assertEquals(5, struct['key1'])
533    self.assertEquals('abc', struct['key2'])
534    self.assertIs(True, struct['key3'])
535    self.assertEquals(11, struct['key4']['subkey'])
536    inner_struct = struct_class()
537    inner_struct['subkey2'] = 9
538    self.assertEquals([6, 'seven', True, False, None, inner_struct],
539                      list(struct['key5'].items()))
540
541    serialized = struct.SerializeToString()
542
543    struct2 = struct_pb2.Struct()
544    struct2.ParseFromString(serialized)
545
546    self.assertEquals(struct, struct2)
547
548    self.assertTrue(isinstance(struct2, well_known_types.Struct))
549    self.assertEquals(5, struct2['key1'])
550    self.assertEquals('abc', struct2['key2'])
551    self.assertIs(True, struct2['key3'])
552    self.assertEquals(11, struct2['key4']['subkey'])
553    self.assertEquals([6, 'seven', True, False, None, inner_struct],
554                      list(struct2['key5'].items()))
555
556    struct_list = struct2['key5']
557    self.assertEquals(6, struct_list[0])
558    self.assertEquals('seven', struct_list[1])
559    self.assertEquals(True, struct_list[2])
560    self.assertEquals(False, struct_list[3])
561    self.assertEquals(None, struct_list[4])
562    self.assertEquals(inner_struct, struct_list[5])
563
564    struct_list[1] = 7
565    self.assertEquals(7, struct_list[1])
566
567    struct_list.add_list().extend([1, 'two', True, False, None])
568    self.assertEquals([1, 'two', True, False, None],
569                      list(struct_list[6].items()))
570
571    text_serialized = str(struct)
572    struct3 = struct_pb2.Struct()
573    text_format.Merge(text_serialized, struct3)
574    self.assertEquals(struct, struct3)
575
576    struct.get_or_create_struct('key3')['replace'] = 12
577    self.assertEquals(12, struct['key3']['replace'])
578
579
580class AnyTest(unittest.TestCase):
581
582  def testAnyMessage(self):
583    # Creates and sets message.
584    msg = any_test_pb2.TestAny()
585    msg_descriptor = msg.DESCRIPTOR
586    all_types = unittest_pb2.TestAllTypes()
587    all_descriptor = all_types.DESCRIPTOR
588    all_types.repeated_string.append(u'\u00fc\ua71f')
589    # Packs to Any.
590    msg.value.Pack(all_types)
591    self.assertEqual(msg.value.type_url,
592                     'type.googleapis.com/%s' % all_descriptor.full_name)
593    self.assertEqual(msg.value.value,
594                     all_types.SerializeToString())
595    # Tests Is() method.
596    self.assertTrue(msg.value.Is(all_descriptor))
597    self.assertFalse(msg.value.Is(msg_descriptor))
598    # Unpacks Any.
599    unpacked_message = unittest_pb2.TestAllTypes()
600    self.assertTrue(msg.value.Unpack(unpacked_message))
601    self.assertEqual(all_types, unpacked_message)
602    # Unpacks to different type.
603    self.assertFalse(msg.value.Unpack(msg))
604    # Only Any messages have Pack method.
605    try:
606      msg.Pack(all_types)
607    except AttributeError:
608      pass
609    else:
610      raise AttributeError('%s should not have Pack method.' %
611                           msg_descriptor.full_name)
612
613  def testMessageName(self):
614    # Creates and sets message.
615    submessage = any_test_pb2.TestAny()
616    submessage.int_value = 12345
617    msg = any_pb2.Any()
618    msg.Pack(submessage)
619    self.assertEqual(msg.TypeName(), 'google.protobuf.internal.TestAny')
620
621  def testPackWithCustomTypeUrl(self):
622    submessage = any_test_pb2.TestAny()
623    submessage.int_value = 12345
624    msg = any_pb2.Any()
625    # Pack with a custom type URL prefix.
626    msg.Pack(submessage, 'type.myservice.com')
627    self.assertEqual(msg.type_url,
628                     'type.myservice.com/%s' % submessage.DESCRIPTOR.full_name)
629    # Pack with a custom type URL prefix ending with '/'.
630    msg.Pack(submessage, 'type.myservice.com/')
631    self.assertEqual(msg.type_url,
632                     'type.myservice.com/%s' % submessage.DESCRIPTOR.full_name)
633    # Pack with an empty type URL prefix.
634    msg.Pack(submessage, '')
635    self.assertEqual(msg.type_url,
636                     '/%s' % submessage.DESCRIPTOR.full_name)
637    # Test unpacking the type.
638    unpacked_message = any_test_pb2.TestAny()
639    self.assertTrue(msg.Unpack(unpacked_message))
640    self.assertEqual(submessage, unpacked_message)
641
642
643if __name__ == '__main__':
644  unittest.main()
645