Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(383)

Side by Side Diff: third_party/google-endpoints/apitools/base/protorpclite/protojson_test.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 #
3 # Copyright 2010 Google Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 """Tests for apitools.base.protorpclite.protojson."""
18 import datetime
19 import json
20 import unittest
21
22 from apitools.base.protorpclite import message_types
23 from apitools.base.protorpclite import messages
24 from apitools.base.protorpclite import protojson
25 from apitools.base.protorpclite import test_util
26
27
28 class CustomField(messages.MessageField):
29 """Custom MessageField class."""
30
31 type = int
32 message_type = message_types.VoidMessage
33
34 def __init__(self, number, **kwargs):
35 super(CustomField, self).__init__(self.message_type, number, **kwargs)
36
37 def value_to_message(self, value):
38 return self.message_type() # pylint:disable=not-callable
39
40
41 class MyMessage(messages.Message):
42 """Test message containing various types."""
43
44 class Color(messages.Enum):
45
46 RED = 1
47 GREEN = 2
48 BLUE = 3
49
50 class Nested(messages.Message):
51
52 nested_value = messages.StringField(1)
53
54 a_string = messages.StringField(2)
55 an_integer = messages.IntegerField(3)
56 a_float = messages.FloatField(4)
57 a_boolean = messages.BooleanField(5)
58 an_enum = messages.EnumField(Color, 6)
59 a_nested = messages.MessageField(Nested, 7)
60 a_repeated = messages.IntegerField(8, repeated=True)
61 a_repeated_float = messages.FloatField(9, repeated=True)
62 a_datetime = message_types.DateTimeField(10)
63 a_repeated_datetime = message_types.DateTimeField(11, repeated=True)
64 a_custom = CustomField(12)
65 a_repeated_custom = CustomField(13, repeated=True)
66
67
68 class ModuleInterfaceTest(test_util.ModuleInterfaceTest,
69 test_util.TestCase):
70
71 MODULE = protojson
72
73
74 # TODO(rafek): Convert this test to the compliance test in test_util.
75 class ProtojsonTest(test_util.TestCase,
76 test_util.ProtoConformanceTestBase):
77 """Test JSON encoding and decoding."""
78
79 PROTOLIB = protojson
80
81 def CompareEncoded(self, expected_encoded, actual_encoded):
82 """JSON encoding will be laundered to remove string differences."""
83 self.assertEquals(json.loads(expected_encoded),
84 json.loads(actual_encoded))
85
86 encoded_empty_message = '{}'
87
88 encoded_partial = """{
89 "double_value": 1.23,
90 "int64_value": -100000000000,
91 "int32_value": 1020,
92 "string_value": "a string",
93 "enum_value": "VAL2"
94 }
95 """
96
97 # pylint:disable=anomalous-unicode-escape-in-string
98 encoded_full = """{
99 "double_value": 1.23,
100 "float_value": -2.5,
101 "int64_value": -100000000000,
102 "uint64_value": 102020202020,
103 "int32_value": 1020,
104 "bool_value": true,
105 "string_value": "a string\u044f",
106 "bytes_value": "YSBieXRlc//+",
107 "enum_value": "VAL2"
108 }
109 """
110
111 encoded_repeated = """{
112 "double_value": [1.23, 2.3],
113 "float_value": [-2.5, 0.5],
114 "int64_value": [-100000000000, 20],
115 "uint64_value": [102020202020, 10],
116 "int32_value": [1020, 718],
117 "bool_value": [true, false],
118 "string_value": ["a string\u044f", "another string"],
119 "bytes_value": ["YSBieXRlc//+", "YW5vdGhlciBieXRlcw=="],
120 "enum_value": ["VAL2", "VAL1"]
121 }
122 """
123
124 encoded_nested = """{
125 "nested": {
126 "a_value": "a string"
127 }
128 }
129 """
130
131 encoded_repeated_nested = """{
132 "repeated_nested": [{"a_value": "a string"},
133 {"a_value": "another string"}]
134 }
135 """
136
137 unexpected_tag_message = '{"unknown": "value"}'
138
139 encoded_default_assigned = '{"a_value": "a default"}'
140
141 encoded_nested_empty = '{"nested": {}}'
142
143 encoded_repeated_nested_empty = '{"repeated_nested": [{}, {}]}'
144
145 encoded_extend_message = '{"int64_value": [400, 50, 6000]}'
146
147 encoded_string_types = '{"string_value": "Latin"}'
148
149 encoded_invalid_enum = '{"enum_value": "undefined"}'
150
151 def testConvertIntegerToFloat(self):
152 """Test that integers passed in to float fields are converted.
153
154 This is necessary because JSON outputs integers for numbers
155 with 0 decimals.
156
157 """
158 message = protojson.decode_message(MyMessage, '{"a_float": 10}')
159
160 self.assertTrue(isinstance(message.a_float, float))
161 self.assertEquals(10.0, message.a_float)
162
163 def testConvertStringToNumbers(self):
164 """Test that strings passed to integer fields are converted."""
165 message = protojson.decode_message(MyMessage,
166 """{"an_integer": "10",
167 "a_float": "3.5",
168 "a_repeated": ["1", "2"],
169 "a_repeated_float": ["1.5", "2", 10]
170 }""")
171
172 self.assertEquals(MyMessage(an_integer=10,
173 a_float=3.5,
174 a_repeated=[1, 2],
175 a_repeated_float=[1.5, 2.0, 10.0]),
176 message)
177
178 def testWrongTypeAssignment(self):
179 """Test when wrong type is assigned to a field."""
180 self.assertRaises(messages.ValidationError,
181 protojson.decode_message,
182 MyMessage, '{"a_string": 10}')
183 self.assertRaises(messages.ValidationError,
184 protojson.decode_message,
185 MyMessage, '{"an_integer": 10.2}')
186 self.assertRaises(messages.ValidationError,
187 protojson.decode_message,
188 MyMessage, '{"an_integer": "10.2"}')
189
190 def testNumericEnumeration(self):
191 """Test that numbers work for enum values."""
192 message = protojson.decode_message(MyMessage, '{"an_enum": 2}')
193
194 expected_message = MyMessage()
195 expected_message.an_enum = MyMessage.Color.GREEN
196
197 self.assertEquals(expected_message, message)
198
199 def testNumericEnumerationNegativeTest(self):
200 """Test with an invalid number for the enum value."""
201 self.assertRaisesRegexp(
202 messages.DecodeError,
203 'Invalid enum value "89"',
204 protojson.decode_message,
205 MyMessage,
206 '{"an_enum": 89}')
207
208 def testAlphaEnumeration(self):
209 """Test that alpha enum values work."""
210 message = protojson.decode_message(MyMessage, '{"an_enum": "RED"}')
211
212 expected_message = MyMessage()
213 expected_message.an_enum = MyMessage.Color.RED
214
215 self.assertEquals(expected_message, message)
216
217 def testAlphaEnumerationNegativeTest(self):
218 """The alpha enum value is invalid."""
219 self.assertRaisesRegexp(
220 messages.DecodeError,
221 'Invalid enum value "IAMINVALID"',
222 protojson.decode_message,
223 MyMessage,
224 '{"an_enum": "IAMINVALID"}')
225
226 def testEnumerationNegativeTestWithEmptyString(self):
227 """The enum value is an empty string."""
228 self.assertRaisesRegexp(
229 messages.DecodeError,
230 'Invalid enum value ""',
231 protojson.decode_message,
232 MyMessage,
233 '{"an_enum": ""}')
234
235 def testNullValues(self):
236 """Test that null values overwrite existing values."""
237 self.assertEquals(MyMessage(),
238 protojson.decode_message(MyMessage,
239 ('{"an_integer": null,'
240 ' "a_nested": null,'
241 ' "an_enum": null'
242 '}')))
243
244 def testEmptyList(self):
245 """Test that empty lists are ignored."""
246 self.assertEquals(MyMessage(),
247 protojson.decode_message(MyMessage,
248 '{"a_repeated": []}'))
249
250 def testNotJSON(self):
251 """Test error when string is not valid JSON."""
252 self.assertRaises(
253 ValueError,
254 protojson.decode_message, MyMessage,
255 '{this is not json}')
256
257 def testDoNotEncodeStrangeObjects(self):
258 """Test trying to encode a strange object.
259
260 The main purpose of this test is to complete coverage. It
261 ensures that the default behavior of the JSON encoder is
262 preserved when someone tries to serialized an unexpected type.
263
264 """
265 class BogusObject(object):
266
267 def check_initialized(self):
268 pass
269
270 self.assertRaises(TypeError,
271 protojson.encode_message,
272 BogusObject())
273
274 def testMergeEmptyString(self):
275 """Test merging the empty or space only string."""
276 message = protojson.decode_message(test_util.OptionalMessage, '')
277 self.assertEquals(test_util.OptionalMessage(), message)
278
279 message = protojson.decode_message(test_util.OptionalMessage, ' ')
280 self.assertEquals(test_util.OptionalMessage(), message)
281
282 def testProtojsonUnrecognizedFieldName(self):
283 """Test that unrecognized fields are saved and can be accessed."""
284 decoded = protojson.decode_message(
285 MyMessage,
286 ('{"an_integer": 1, "unknown_val": 2}'))
287 self.assertEquals(decoded.an_integer, 1)
288 self.assertEquals(1, len(decoded.all_unrecognized_fields()))
289 self.assertEquals('unknown_val', decoded.all_unrecognized_fields()[0])
290 self.assertEquals((2, messages.Variant.INT64),
291 decoded.get_unrecognized_field_info('unknown_val'))
292
293 def testProtojsonUnrecognizedFieldNumber(self):
294 """Test that unrecognized fields are saved and can be accessed."""
295 decoded = protojson.decode_message(
296 MyMessage,
297 '{"an_integer": 1, "1001": "unknown", "-123": "negative", '
298 '"456_mixed": 2}')
299 self.assertEquals(decoded.an_integer, 1)
300 self.assertEquals(3, len(decoded.all_unrecognized_fields()))
301 self.assertFalse(1001 in decoded.all_unrecognized_fields())
302 self.assertTrue('1001' in decoded.all_unrecognized_fields())
303 self.assertEquals(('unknown', messages.Variant.STRING),
304 decoded.get_unrecognized_field_info('1001'))
305 self.assertTrue('-123' in decoded.all_unrecognized_fields())
306 self.assertEquals(('negative', messages.Variant.STRING),
307 decoded.get_unrecognized_field_info('-123'))
308 self.assertTrue('456_mixed' in decoded.all_unrecognized_fields())
309 self.assertEquals((2, messages.Variant.INT64),
310 decoded.get_unrecognized_field_info('456_mixed'))
311
312 def testProtojsonUnrecognizedNull(self):
313 """Test that unrecognized fields that are None are skipped."""
314 decoded = protojson.decode_message(
315 MyMessage,
316 '{"an_integer": 1, "unrecognized_null": null}')
317 self.assertEquals(decoded.an_integer, 1)
318 self.assertEquals(decoded.all_unrecognized_fields(), [])
319
320 def testUnrecognizedFieldVariants(self):
321 """Test that unrecognized fields are mapped to the right variants."""
322 for encoded, expected_variant in (
323 ('{"an_integer": 1, "unknown_val": 2}',
324 messages.Variant.INT64),
325 ('{"an_integer": 1, "unknown_val": 2.0}',
326 messages.Variant.DOUBLE),
327 ('{"an_integer": 1, "unknown_val": "string value"}',
328 messages.Variant.STRING),
329 ('{"an_integer": 1, "unknown_val": [1, 2, 3]}',
330 messages.Variant.INT64),
331 ('{"an_integer": 1, "unknown_val": [1, 2.0, 3]}',
332 messages.Variant.DOUBLE),
333 ('{"an_integer": 1, "unknown_val": [1, "foo", 3]}',
334 messages.Variant.STRING),
335 ('{"an_integer": 1, "unknown_val": true}',
336 messages.Variant.BOOL)):
337 decoded = protojson.decode_message(MyMessage, encoded)
338 self.assertEquals(decoded.an_integer, 1)
339 self.assertEquals(1, len(decoded.all_unrecognized_fields()))
340 self.assertEquals(
341 'unknown_val', decoded.all_unrecognized_fields()[0])
342 _, decoded_variant = decoded.get_unrecognized_field_info(
343 'unknown_val')
344 self.assertEquals(expected_variant, decoded_variant)
345
346 def testDecodeDateTime(self):
347 for datetime_string, datetime_vals in (
348 ('2012-09-30T15:31:50.262', (2012, 9, 30, 15, 31, 50, 262000)),
349 ('2012-09-30T15:31:50', (2012, 9, 30, 15, 31, 50, 0))):
350 message = protojson.decode_message(
351 MyMessage, '{"a_datetime": "%s"}' % datetime_string)
352 expected_message = MyMessage(
353 a_datetime=datetime.datetime(*datetime_vals))
354
355 self.assertEquals(expected_message, message)
356
357 def testDecodeInvalidDateTime(self):
358 self.assertRaises(messages.DecodeError, protojson.decode_message,
359 MyMessage, '{"a_datetime": "invalid"}')
360
361 def testEncodeDateTime(self):
362 for datetime_string, datetime_vals in (
363 ('2012-09-30T15:31:50.262000',
364 (2012, 9, 30, 15, 31, 50, 262000)),
365 ('2012-09-30T15:31:50.262123',
366 (2012, 9, 30, 15, 31, 50, 262123)),
367 ('2012-09-30T15:31:50',
368 (2012, 9, 30, 15, 31, 50, 0))):
369 decoded_message = protojson.encode_message(
370 MyMessage(a_datetime=datetime.datetime(*datetime_vals)))
371 expected_decoding = '{"a_datetime": "%s"}' % datetime_string
372 self.CompareEncoded(expected_decoding, decoded_message)
373
374 def testDecodeRepeatedDateTime(self):
375 message = protojson.decode_message(
376 MyMessage,
377 '{"a_repeated_datetime": ["2012-09-30T15:31:50.262", '
378 '"2010-01-21T09:52:00", "2000-01-01T01:00:59.999999"]}')
379 expected_message = MyMessage(
380 a_repeated_datetime=[
381 datetime.datetime(2012, 9, 30, 15, 31, 50, 262000),
382 datetime.datetime(2010, 1, 21, 9, 52),
383 datetime.datetime(2000, 1, 1, 1, 0, 59, 999999)])
384
385 self.assertEquals(expected_message, message)
386
387 def testDecodeCustom(self):
388 message = protojson.decode_message(MyMessage, '{"a_custom": 1}')
389 self.assertEquals(MyMessage(a_custom=1), message)
390
391 def testDecodeInvalidCustom(self):
392 self.assertRaises(messages.ValidationError, protojson.decode_message,
393 MyMessage, '{"a_custom": "invalid"}')
394
395 def testEncodeCustom(self):
396 decoded_message = protojson.encode_message(MyMessage(a_custom=1))
397 self.CompareEncoded('{"a_custom": 1}', decoded_message)
398
399 def testDecodeRepeatedCustom(self):
400 message = protojson.decode_message(
401 MyMessage, '{"a_repeated_custom": [1, 2, 3]}')
402 self.assertEquals(MyMessage(a_repeated_custom=[1, 2, 3]), message)
403
404 def testDecodeBadBase64BytesField(self):
405 """Test decoding improperly encoded base64 bytes value."""
406 self.assertRaisesWithRegexpMatch(
407 messages.DecodeError,
408 'Base64 decoding error: Incorrect padding',
409 protojson.decode_message,
410 test_util.OptionalMessage,
411 '{"bytes_value": "abcdefghijklmnopq"}')
412
413
414 class CustomProtoJson(protojson.ProtoJson):
415
416 def encode_field(self, field, value):
417 return '{encoded}' + value
418
419 def decode_field(self, field, value):
420 return '{decoded}' + value
421
422
423 class CustomProtoJsonTest(test_util.TestCase):
424 """Tests for serialization overriding functionality."""
425
426 def setUp(self):
427 self.protojson = CustomProtoJson()
428
429 def testEncode(self):
430 self.assertEqual(
431 '{"a_string": "{encoded}xyz"}',
432 self.protojson.encode_message(MyMessage(a_string='xyz')))
433
434 def testDecode(self):
435 self.assertEqual(
436 MyMessage(a_string='{decoded}xyz'),
437 self.protojson.decode_message(MyMessage, '{"a_string": "xyz"}'))
438
439 def testDecodeEmptyMessage(self):
440 self.assertEqual(
441 MyMessage(a_string='{decoded}'),
442 self.protojson.decode_message(MyMessage, '{"a_string": ""}'))
443
444 def testDefault(self):
445 self.assertTrue(protojson.ProtoJson.get_default(),
446 protojson.ProtoJson.get_default())
447
448 instance = CustomProtoJson()
449 protojson.ProtoJson.set_default(instance)
450 self.assertTrue(instance is protojson.ProtoJson.get_default())
451
452
453 if __name__ == '__main__':
454 unittest.main()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698