OLD | NEW |
(Empty) | |
| 1 # |
| 2 # Copyright 2015 Google Inc. |
| 3 # |
| 4 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 # you may not use this file except in compliance with the License. |
| 6 # You may obtain a copy of the License at |
| 7 # |
| 8 # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 # |
| 10 # Unless required by applicable law or agreed to in writing, software |
| 11 # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 # See the License for the specific language governing permissions and |
| 14 # limitations under the License. |
| 15 |
| 16 import base64 |
| 17 import datetime |
| 18 import json |
| 19 import sys |
| 20 |
| 21 import unittest2 |
| 22 |
| 23 from apitools.base.protorpclite import message_types |
| 24 from apitools.base.protorpclite import messages |
| 25 from apitools.base.protorpclite import util |
| 26 from apitools.base.py import encoding |
| 27 from apitools.base.py import exceptions |
| 28 from apitools.base.py import extra_types |
| 29 |
| 30 |
| 31 class SimpleMessage(messages.Message): |
| 32 field = messages.StringField(1) |
| 33 repfield = messages.StringField(2, repeated=True) |
| 34 |
| 35 |
| 36 class BytesMessage(messages.Message): |
| 37 field = messages.BytesField(1) |
| 38 repfield = messages.BytesField(2, repeated=True) |
| 39 |
| 40 |
| 41 class TimeMessage(messages.Message): |
| 42 timefield = message_types.DateTimeField(3) |
| 43 |
| 44 |
| 45 @encoding.MapUnrecognizedFields('additional_properties') |
| 46 class AdditionalPropertiesMessage(messages.Message): |
| 47 |
| 48 class AdditionalProperty(messages.Message): |
| 49 key = messages.StringField(1) |
| 50 value = messages.StringField(2) |
| 51 |
| 52 additional_properties = messages.MessageField( |
| 53 'AdditionalProperty', 1, repeated=True) |
| 54 |
| 55 |
| 56 @encoding.MapUnrecognizedFields('additional_properties') |
| 57 class UnrecognizedEnumMessage(messages.Message): |
| 58 |
| 59 class ThisEnum(messages.Enum): |
| 60 VALUE_ONE = 1 |
| 61 VALUE_TWO = 2 |
| 62 |
| 63 class AdditionalProperty(messages.Message): |
| 64 key = messages.StringField(1) |
| 65 value = messages.EnumField('UnrecognizedEnumMessage.ThisEnum', 2) |
| 66 |
| 67 additional_properties = messages.MessageField( |
| 68 AdditionalProperty, 1, repeated=True) |
| 69 |
| 70 |
| 71 class CompoundPropertyType(messages.Message): |
| 72 index = messages.IntegerField(1) |
| 73 name = messages.StringField(2) |
| 74 |
| 75 |
| 76 class MessageWithEnum(messages.Message): |
| 77 |
| 78 class ThisEnum(messages.Enum): |
| 79 VALUE_ONE = 1 |
| 80 VALUE_TWO = 2 |
| 81 |
| 82 field_one = messages.EnumField(ThisEnum, 1) |
| 83 field_two = messages.EnumField(ThisEnum, 2, default=ThisEnum.VALUE_TWO) |
| 84 ignored_field = messages.EnumField(ThisEnum, 3) |
| 85 |
| 86 |
| 87 @encoding.MapUnrecognizedFields('additional_properties') |
| 88 class AdditionalMessagePropertiesMessage(messages.Message): |
| 89 |
| 90 class AdditionalProperty(messages.Message): |
| 91 key = messages.StringField(1) |
| 92 value = messages.MessageField(CompoundPropertyType, 2) |
| 93 |
| 94 additional_properties = messages.MessageField( |
| 95 'AdditionalProperty', 1, repeated=True) |
| 96 |
| 97 |
| 98 class HasNestedMessage(messages.Message): |
| 99 nested = messages.MessageField(AdditionalPropertiesMessage, 1) |
| 100 nested_list = messages.StringField(2, repeated=True) |
| 101 |
| 102 |
| 103 class ExtraNestedMessage(messages.Message): |
| 104 nested = messages.MessageField(HasNestedMessage, 1) |
| 105 |
| 106 |
| 107 class MessageWithRemappings(messages.Message): |
| 108 |
| 109 class SomeEnum(messages.Enum): |
| 110 enum_value = 1 |
| 111 second_value = 2 |
| 112 |
| 113 enum_field = messages.EnumField(SomeEnum, 1) |
| 114 double_encoding = messages.EnumField(SomeEnum, 2) |
| 115 another_field = messages.StringField(3) |
| 116 repeated_enum = messages.EnumField(SomeEnum, 4, repeated=True) |
| 117 repeated_field = messages.StringField(5, repeated=True) |
| 118 |
| 119 |
| 120 @encoding.MapUnrecognizedFields('additional_properties') |
| 121 class RepeatedJsonValueMessage(messages.Message): |
| 122 |
| 123 class AdditionalProperty(messages.Message): |
| 124 key = messages.StringField(1) |
| 125 value = messages.MessageField(extra_types.JsonValue, 2, repeated=True) |
| 126 |
| 127 additional_properties = messages.MessageField('AdditionalProperty', 1, |
| 128 repeated=True) |
| 129 |
| 130 |
| 131 encoding.AddCustomJsonEnumMapping(MessageWithRemappings.SomeEnum, |
| 132 'enum_value', 'wire_name') |
| 133 encoding.AddCustomJsonFieldMapping(MessageWithRemappings, |
| 134 'double_encoding', 'doubleEncoding') |
| 135 encoding.AddCustomJsonFieldMapping(MessageWithRemappings, |
| 136 'another_field', 'anotherField') |
| 137 encoding.AddCustomJsonFieldMapping(MessageWithRemappings, |
| 138 'repeated_field', 'repeatedField') |
| 139 |
| 140 |
| 141 class EncodingTest(unittest2.TestCase): |
| 142 |
| 143 def testCopyProtoMessage(self): |
| 144 msg = SimpleMessage(field='abc') |
| 145 new_msg = encoding.CopyProtoMessage(msg) |
| 146 self.assertEqual(msg.field, new_msg.field) |
| 147 msg.field = 'def' |
| 148 self.assertNotEqual(msg.field, new_msg.field) |
| 149 |
| 150 def testBytesEncoding(self): |
| 151 b64_str = 'AAc+' |
| 152 b64_msg = '{"field": "%s"}' % b64_str |
| 153 urlsafe_b64_str = 'AAc-' |
| 154 urlsafe_b64_msg = '{"field": "%s"}' % urlsafe_b64_str |
| 155 data = base64.b64decode(b64_str) |
| 156 msg = BytesMessage(field=data) |
| 157 self.assertEqual( |
| 158 msg, encoding.JsonToMessage(BytesMessage, urlsafe_b64_msg)) |
| 159 self.assertEqual(msg, encoding.JsonToMessage(BytesMessage, b64_msg)) |
| 160 self.assertEqual(urlsafe_b64_msg, encoding.MessageToJson(msg)) |
| 161 |
| 162 enc_rep_msg = '{"repfield": ["%(b)s", "%(b)s"]}' % { |
| 163 'b': urlsafe_b64_str} |
| 164 rep_msg = BytesMessage(repfield=[data, data]) |
| 165 self.assertEqual( |
| 166 rep_msg, encoding.JsonToMessage(BytesMessage, enc_rep_msg)) |
| 167 self.assertEqual(enc_rep_msg, encoding.MessageToJson(rep_msg)) |
| 168 |
| 169 def testIncludeFields(self): |
| 170 msg = SimpleMessage() |
| 171 self.assertEqual('{}', encoding.MessageToJson(msg)) |
| 172 self.assertEqual( |
| 173 '{"field": null}', |
| 174 encoding.MessageToJson(msg, include_fields=['field'])) |
| 175 self.assertEqual( |
| 176 '{"repfield": []}', |
| 177 encoding.MessageToJson(msg, include_fields=['repfield'])) |
| 178 |
| 179 def testNestedIncludeFields(self): |
| 180 msg = HasNestedMessage( |
| 181 nested=AdditionalPropertiesMessage( |
| 182 additional_properties=[])) |
| 183 self.assertEqual( |
| 184 '{"nested": null}', |
| 185 encoding.MessageToJson(msg, include_fields=['nested'])) |
| 186 self.assertEqual( |
| 187 '{"nested": {"additional_properties": []}}', |
| 188 encoding.MessageToJson( |
| 189 msg, include_fields=['nested.additional_properties'])) |
| 190 # pylint: disable=redefined-variable-type |
| 191 msg = ExtraNestedMessage(nested=msg) |
| 192 self.assertEqual( |
| 193 '{"nested": {"nested": null}}', |
| 194 encoding.MessageToJson(msg, include_fields=['nested.nested'])) |
| 195 self.assertEqual( |
| 196 '{"nested": {"nested_list": []}}', |
| 197 encoding.MessageToJson(msg, include_fields=['nested.nested_list'])) |
| 198 self.assertEqual( |
| 199 '{"nested": {"nested": {"additional_properties": []}}}', |
| 200 encoding.MessageToJson( |
| 201 msg, include_fields=['nested.nested.additional_properties'])) |
| 202 |
| 203 def testAdditionalPropertyMapping(self): |
| 204 msg = AdditionalPropertiesMessage() |
| 205 msg.additional_properties = [ |
| 206 AdditionalPropertiesMessage.AdditionalProperty( |
| 207 key='key_one', value='value_one'), |
| 208 AdditionalPropertiesMessage.AdditionalProperty( |
| 209 key='key_two', value='value_two'), |
| 210 ] |
| 211 |
| 212 encoded_msg = encoding.MessageToJson(msg) |
| 213 self.assertEqual( |
| 214 {'key_one': 'value_one', 'key_two': 'value_two'}, |
| 215 json.loads(encoded_msg)) |
| 216 |
| 217 new_msg = encoding.JsonToMessage(type(msg), encoded_msg) |
| 218 self.assertEqual( |
| 219 set(('key_one', 'key_two')), |
| 220 set([x.key for x in new_msg.additional_properties])) |
| 221 self.assertIsNot(msg, new_msg) |
| 222 |
| 223 new_msg.additional_properties.pop() |
| 224 self.assertEqual(1, len(new_msg.additional_properties)) |
| 225 self.assertEqual(2, len(msg.additional_properties)) |
| 226 |
| 227 def testNumericPropertyName(self): |
| 228 json_msg = '{"nested": {"123": "def"}}' |
| 229 msg = encoding.JsonToMessage(HasNestedMessage, json_msg) |
| 230 self.assertEqual(1, len(msg.nested.additional_properties)) |
| 231 |
| 232 def testAdditionalMessageProperties(self): |
| 233 json_msg = '{"input": {"index": 0, "name": "output"}}' |
| 234 result = encoding.JsonToMessage( |
| 235 AdditionalMessagePropertiesMessage, json_msg) |
| 236 self.assertEqual(1, len(result.additional_properties)) |
| 237 self.assertEqual(0, result.additional_properties[0].value.index) |
| 238 |
| 239 def testUnrecognizedEnum(self): |
| 240 json_msg = '{"input": "VALUE_ONE"}' |
| 241 result = encoding.JsonToMessage( |
| 242 UnrecognizedEnumMessage, json_msg) |
| 243 self.assertEqual(1, len(result.additional_properties)) |
| 244 self.assertEqual(UnrecognizedEnumMessage.ThisEnum.VALUE_ONE, |
| 245 result.additional_properties[0].value) |
| 246 |
| 247 def testNestedFieldMapping(self): |
| 248 nested_msg = AdditionalPropertiesMessage() |
| 249 nested_msg.additional_properties = [ |
| 250 AdditionalPropertiesMessage.AdditionalProperty( |
| 251 key='key_one', value='value_one'), |
| 252 AdditionalPropertiesMessage.AdditionalProperty( |
| 253 key='key_two', value='value_two'), |
| 254 ] |
| 255 msg = HasNestedMessage(nested=nested_msg) |
| 256 |
| 257 encoded_msg = encoding.MessageToJson(msg) |
| 258 self.assertEqual( |
| 259 {'nested': {'key_one': 'value_one', 'key_two': 'value_two'}}, |
| 260 json.loads(encoded_msg)) |
| 261 |
| 262 new_msg = encoding.JsonToMessage(type(msg), encoded_msg) |
| 263 self.assertEqual( |
| 264 set(('key_one', 'key_two')), |
| 265 set([x.key for x in new_msg.nested.additional_properties])) |
| 266 |
| 267 new_msg.nested.additional_properties.pop() |
| 268 self.assertEqual(1, len(new_msg.nested.additional_properties)) |
| 269 self.assertEqual(2, len(msg.nested.additional_properties)) |
| 270 |
| 271 def testValidEnums(self): |
| 272 message_json = '{"field_one": "VALUE_ONE"}' |
| 273 message = encoding.JsonToMessage(MessageWithEnum, message_json) |
| 274 self.assertEqual(MessageWithEnum.ThisEnum.VALUE_ONE, message.field_one) |
| 275 self.assertEqual(MessageWithEnum.ThisEnum.VALUE_TWO, message.field_two) |
| 276 self.assertEqual(json.loads(message_json), |
| 277 json.loads(encoding.MessageToJson(message))) |
| 278 |
| 279 def testIgnoredEnums(self): |
| 280 json_with_typo = '{"field_one": "VALUE_OEN"}' |
| 281 message = encoding.JsonToMessage(MessageWithEnum, json_with_typo) |
| 282 self.assertEqual(None, message.field_one) |
| 283 self.assertEqual(('VALUE_OEN', messages.Variant.ENUM), |
| 284 message.get_unrecognized_field_info('field_one')) |
| 285 self.assertEqual(json.loads(json_with_typo), |
| 286 json.loads(encoding.MessageToJson(message))) |
| 287 |
| 288 empty_json = '' |
| 289 message = encoding.JsonToMessage(MessageWithEnum, empty_json) |
| 290 self.assertEqual(None, message.field_one) |
| 291 |
| 292 def testIgnoredEnumsWithDefaults(self): |
| 293 json_with_typo = '{"field_two": "VALUE_OEN"}' |
| 294 message = encoding.JsonToMessage(MessageWithEnum, json_with_typo) |
| 295 self.assertEqual(MessageWithEnum.ThisEnum.VALUE_TWO, message.field_two) |
| 296 self.assertEqual(json.loads(json_with_typo), |
| 297 json.loads(encoding.MessageToJson(message))) |
| 298 |
| 299 def testUnknownNestedRoundtrip(self): |
| 300 json_message = '{"field": "abc", "submessage": {"a": 1, "b": "foo"}}' |
| 301 message = encoding.JsonToMessage(SimpleMessage, json_message) |
| 302 self.assertEqual(json.loads(json_message), |
| 303 json.loads(encoding.MessageToJson(message))) |
| 304 |
| 305 def testJsonDatetime(self): |
| 306 msg = TimeMessage(timefield=datetime.datetime( |
| 307 2014, 7, 2, 23, 33, 25, 541000, |
| 308 tzinfo=util.TimeZoneOffset(datetime.timedelta(0)))) |
| 309 self.assertEqual( |
| 310 '{"timefield": "2014-07-02T23:33:25.541000+00:00"}', |
| 311 encoding.MessageToJson(msg)) |
| 312 |
| 313 def testEnumRemapping(self): |
| 314 msg = MessageWithRemappings( |
| 315 enum_field=MessageWithRemappings.SomeEnum.enum_value) |
| 316 json_message = encoding.MessageToJson(msg) |
| 317 self.assertEqual('{"enum_field": "wire_name"}', json_message) |
| 318 self.assertEqual( |
| 319 msg, encoding.JsonToMessage(MessageWithRemappings, json_message)) |
| 320 |
| 321 def testRepeatedEnumRemapping(self): |
| 322 msg = MessageWithRemappings( |
| 323 repeated_enum=[ |
| 324 MessageWithRemappings.SomeEnum.enum_value, |
| 325 MessageWithRemappings.SomeEnum.second_value, |
| 326 ]) |
| 327 json_message = encoding.MessageToJson(msg) |
| 328 self.assertEqual('{"repeated_enum": ["wire_name", "second_value"]}', |
| 329 json_message) |
| 330 self.assertEqual( |
| 331 msg, encoding.JsonToMessage(MessageWithRemappings, json_message)) |
| 332 |
| 333 def testFieldRemapping(self): |
| 334 msg = MessageWithRemappings(another_field='abc') |
| 335 json_message = encoding.MessageToJson(msg) |
| 336 self.assertEqual('{"anotherField": "abc"}', json_message) |
| 337 self.assertEqual( |
| 338 msg, encoding.JsonToMessage(MessageWithRemappings, json_message)) |
| 339 |
| 340 def testRepeatedFieldRemapping(self): |
| 341 msg = MessageWithRemappings(repeated_field=['abc', 'def']) |
| 342 json_message = encoding.MessageToJson(msg) |
| 343 self.assertEqual('{"repeatedField": ["abc", "def"]}', json_message) |
| 344 self.assertEqual( |
| 345 msg, encoding.JsonToMessage(MessageWithRemappings, json_message)) |
| 346 |
| 347 def testMultipleRemapping(self): |
| 348 msg = MessageWithRemappings( |
| 349 double_encoding=MessageWithRemappings.SomeEnum.enum_value) |
| 350 json_message = encoding.MessageToJson(msg) |
| 351 self.assertEqual('{"doubleEncoding": "wire_name"}', json_message) |
| 352 self.assertEqual( |
| 353 msg, encoding.JsonToMessage(MessageWithRemappings, json_message)) |
| 354 |
| 355 def testRepeatedRemapping(self): |
| 356 # Should allow remapping if the mapping remains the same. |
| 357 encoding.AddCustomJsonEnumMapping(MessageWithRemappings.SomeEnum, |
| 358 'enum_value', 'wire_name') |
| 359 encoding.AddCustomJsonFieldMapping(MessageWithRemappings, |
| 360 'double_encoding', 'doubleEncoding') |
| 361 encoding.AddCustomJsonFieldMapping(MessageWithRemappings, |
| 362 'another_field', 'anotherField') |
| 363 encoding.AddCustomJsonFieldMapping(MessageWithRemappings, |
| 364 'repeated_field', 'repeatedField') |
| 365 |
| 366 # Should raise errors if the remapping changes the mapping. |
| 367 self.assertRaises( |
| 368 exceptions.InvalidDataError, |
| 369 encoding.AddCustomJsonFieldMapping, |
| 370 MessageWithRemappings, 'double_encoding', 'something_else') |
| 371 self.assertRaises( |
| 372 exceptions.InvalidDataError, |
| 373 encoding.AddCustomJsonFieldMapping, |
| 374 MessageWithRemappings, 'enum_field', 'anotherField') |
| 375 self.assertRaises( |
| 376 exceptions.InvalidDataError, |
| 377 encoding.AddCustomJsonEnumMapping, |
| 378 MessageWithRemappings.SomeEnum, 'enum_value', 'another_name') |
| 379 self.assertRaises( |
| 380 exceptions.InvalidDataError, |
| 381 encoding.AddCustomJsonEnumMapping, |
| 382 MessageWithRemappings.SomeEnum, 'second_value', 'wire_name') |
| 383 |
| 384 def testMessageToRepr(self): |
| 385 # Using the same string returned by MessageToRepr, with the |
| 386 # module names fixed. |
| 387 # pylint: disable=bad-whitespace |
| 388 msg = SimpleMessage(field='field', repfield=['field', 'field', ],) |
| 389 # pylint: enable=bad-whitespace |
| 390 self.assertEqual( |
| 391 encoding.MessageToRepr(msg), |
| 392 r"%s.SimpleMessage(field='field',repfield=['field','field',],)" % ( |
| 393 __name__,)) |
| 394 self.assertEqual( |
| 395 encoding.MessageToRepr(msg, no_modules=True), |
| 396 r"SimpleMessage(field='field',repfield=['field','field',],)") |
| 397 |
| 398 def testMessageToReprWithTime(self): |
| 399 msg = TimeMessage(timefield=datetime.datetime( |
| 400 2014, 7, 2, 23, 33, 25, 541000, |
| 401 tzinfo=util.TimeZoneOffset(datetime.timedelta(0)))) |
| 402 self.assertEqual( |
| 403 encoding.MessageToRepr(msg, multiline=True), |
| 404 ('%s.TimeMessage(\n ' |
| 405 'timefield=datetime.datetime(2014, 7, 2, 23, 33, 25, 541000, ' |
| 406 'tzinfo=apitools.base.protorpclite.util.TimeZoneOffset(' |
| 407 'datetime.timedelta(0))),\n)') % __name__) |
| 408 self.assertEqual( |
| 409 encoding.MessageToRepr(msg, multiline=True, no_modules=True), |
| 410 'TimeMessage(\n ' |
| 411 'timefield=datetime.datetime(2014, 7, 2, 23, 33, 25, 541000, ' |
| 412 'tzinfo=TimeZoneOffset(datetime.timedelta(0))),\n)') |
| 413 |
| 414 def testPackageMappingsNoPackage(self): |
| 415 this_module_name = util.get_package_for_module(__name__) |
| 416 full_type_name = 'MessageWithEnum.ThisEnum' |
| 417 full_key = '%s.%s' % (this_module_name, full_type_name) |
| 418 self.assertEqual(full_key, |
| 419 encoding._GetTypeKey(MessageWithEnum.ThisEnum, '')) |
| 420 |
| 421 def testPackageMappingsWithPackage(self): |
| 422 this_module_name = util.get_package_for_module(__name__) |
| 423 full_type_name = 'MessageWithEnum.ThisEnum' |
| 424 full_key = '%s.%s' % (this_module_name, full_type_name) |
| 425 this_module = sys.modules[__name__] |
| 426 new_package = 'new_package' |
| 427 try: |
| 428 setattr(this_module, 'package', new_package) |
| 429 new_key = '%s.%s' % (new_package, full_type_name) |
| 430 self.assertEqual( |
| 431 new_key, |
| 432 encoding._GetTypeKey(MessageWithEnum.ThisEnum, '')) |
| 433 self.assertEqual( |
| 434 full_key, |
| 435 encoding._GetTypeKey(MessageWithEnum.ThisEnum, new_package)) |
| 436 finally: |
| 437 delattr(this_module, 'package') |
| 438 |
| 439 def testRepeatedJsonValuesAsRepeatedProperty(self): |
| 440 encoded_msg = '{"a": [{"one": 1}]}' |
| 441 msg = encoding.JsonToMessage(RepeatedJsonValueMessage, encoded_msg) |
| 442 self.assertEqual(encoded_msg, encoding.MessageToJson(msg)) |
OLD | NEW |