Index: third_party/gsutil/third_party/protorpc/protorpc/protojson_test.py |
diff --git a/third_party/gsutil/third_party/protorpc/protorpc/protojson_test.py b/third_party/gsutil/third_party/protorpc/protorpc/protojson_test.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..1ed23e201a3dd8a02c40f9b0ac0fe8485c78c2ff |
--- /dev/null |
+++ b/third_party/gsutil/third_party/protorpc/protorpc/protojson_test.py |
@@ -0,0 +1,554 @@ |
+#!/usr/bin/env python |
+# |
+# Copyright 2010 Google Inc. |
+# |
+# Licensed under the Apache License, Version 2.0 (the "License"); |
+# you may not use this file except in compliance with the License. |
+# You may obtain a copy of the License at |
+# |
+# http://www.apache.org/licenses/LICENSE-2.0 |
+# |
+# Unless required by applicable law or agreed to in writing, software |
+# distributed under the License is distributed on an "AS IS" BASIS, |
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+# See the License for the specific language governing permissions and |
+# limitations under the License. |
+# |
+ |
+"""Tests for protorpc.protojson.""" |
+ |
+__author__ = 'rafek@google.com (Rafe Kaplan)' |
+ |
+ |
+import datetime |
+import imp |
+import sys |
+import unittest |
+ |
+from protorpc import message_types |
+from protorpc import messages |
+from protorpc import protojson |
+from protorpc import test_util |
+ |
+import simplejson |
+ |
+ |
+class CustomField(messages.MessageField): |
+ """Custom MessageField class.""" |
+ |
+ type = int |
+ message_type = message_types.VoidMessage |
+ |
+ def __init__(self, number, **kwargs): |
+ super(CustomField, self).__init__(self.message_type, number, **kwargs) |
+ |
+ def value_to_message(self, value): |
+ return self.message_type() |
+ |
+ |
+class MyMessage(messages.Message): |
+ """Test message containing various types.""" |
+ |
+ class Color(messages.Enum): |
+ |
+ RED = 1 |
+ GREEN = 2 |
+ BLUE = 3 |
+ |
+ class Nested(messages.Message): |
+ |
+ nested_value = messages.StringField(1) |
+ |
+ a_string = messages.StringField(2) |
+ an_integer = messages.IntegerField(3) |
+ a_float = messages.FloatField(4) |
+ a_boolean = messages.BooleanField(5) |
+ an_enum = messages.EnumField(Color, 6) |
+ a_nested = messages.MessageField(Nested, 7) |
+ a_repeated = messages.IntegerField(8, repeated=True) |
+ a_repeated_float = messages.FloatField(9, repeated=True) |
+ a_datetime = message_types.DateTimeField(10) |
+ a_repeated_datetime = message_types.DateTimeField(11, repeated=True) |
+ a_custom = CustomField(12) |
+ a_repeated_custom = CustomField(13, repeated=True) |
+ |
+ |
+class ModuleInterfaceTest(test_util.ModuleInterfaceTest, |
+ test_util.TestCase): |
+ |
+ MODULE = protojson |
+ |
+ |
+# TODO(rafek): Convert this test to the compliance test in test_util. |
+class ProtojsonTest(test_util.TestCase, |
+ test_util.ProtoConformanceTestBase): |
+ """Test JSON encoding and decoding.""" |
+ |
+ PROTOLIB = protojson |
+ |
+ def CompareEncoded(self, expected_encoded, actual_encoded): |
+ """JSON encoding will be laundered to remove string differences.""" |
+ self.assertEquals(simplejson.loads(expected_encoded), |
+ simplejson.loads(actual_encoded)) |
+ |
+ encoded_empty_message = '{}' |
+ |
+ encoded_partial = """{ |
+ "double_value": 1.23, |
+ "int64_value": -100000000000, |
+ "int32_value": 1020, |
+ "string_value": "a string", |
+ "enum_value": "VAL2" |
+ } |
+ """ |
+ |
+ encoded_full = """{ |
+ "double_value": 1.23, |
+ "float_value": -2.5, |
+ "int64_value": -100000000000, |
+ "uint64_value": 102020202020, |
+ "int32_value": 1020, |
+ "bool_value": true, |
+ "string_value": "a string\u044f", |
+ "bytes_value": "YSBieXRlc//+", |
+ "enum_value": "VAL2" |
+ } |
+ """ |
+ |
+ encoded_repeated = """{ |
+ "double_value": [1.23, 2.3], |
+ "float_value": [-2.5, 0.5], |
+ "int64_value": [-100000000000, 20], |
+ "uint64_value": [102020202020, 10], |
+ "int32_value": [1020, 718], |
+ "bool_value": [true, false], |
+ "string_value": ["a string\u044f", "another string"], |
+ "bytes_value": ["YSBieXRlc//+", "YW5vdGhlciBieXRlcw=="], |
+ "enum_value": ["VAL2", "VAL1"] |
+ } |
+ """ |
+ |
+ encoded_nested = """{ |
+ "nested": { |
+ "a_value": "a string" |
+ } |
+ } |
+ """ |
+ |
+ encoded_repeated_nested = """{ |
+ "repeated_nested": [{"a_value": "a string"}, |
+ {"a_value": "another string"}] |
+ } |
+ """ |
+ |
+ unexpected_tag_message = '{"unknown": "value"}' |
+ |
+ encoded_default_assigned = '{"a_value": "a default"}' |
+ |
+ encoded_nested_empty = '{"nested": {}}' |
+ |
+ encoded_repeated_nested_empty = '{"repeated_nested": [{}, {}]}' |
+ |
+ encoded_extend_message = '{"int64_value": [400, 50, 6000]}' |
+ |
+ encoded_string_types = '{"string_value": "Latin"}' |
+ |
+ encoded_invalid_enum = '{"enum_value": "undefined"}' |
+ |
+ def testConvertIntegerToFloat(self): |
+ """Test that integers passed in to float fields are converted. |
+ |
+ This is necessary because JSON outputs integers for numbers with 0 decimals. |
+ """ |
+ message = protojson.decode_message(MyMessage, '{"a_float": 10}') |
+ |
+ self.assertTrue(isinstance(message.a_float, float)) |
+ self.assertEquals(10.0, message.a_float) |
+ |
+ def testConvertStringToNumbers(self): |
+ """Test that strings passed to integer fields are converted.""" |
+ message = protojson.decode_message(MyMessage, |
+ """{"an_integer": "10", |
+ "a_float": "3.5", |
+ "a_repeated": ["1", "2"], |
+ "a_repeated_float": ["1.5", "2", 10] |
+ }""") |
+ |
+ self.assertEquals(MyMessage(an_integer=10, |
+ a_float=3.5, |
+ a_repeated=[1, 2], |
+ a_repeated_float=[1.5, 2.0, 10.0]), |
+ message) |
+ |
+ def testWrongTypeAssignment(self): |
+ """Test when wrong type is assigned to a field.""" |
+ self.assertRaises(messages.ValidationError, |
+ protojson.decode_message, |
+ MyMessage, '{"a_string": 10}') |
+ self.assertRaises(messages.ValidationError, |
+ protojson.decode_message, |
+ MyMessage, '{"an_integer": 10.2}') |
+ self.assertRaises(messages.ValidationError, |
+ protojson.decode_message, |
+ MyMessage, '{"an_integer": "10.2"}') |
+ |
+ def testNumericEnumeration(self): |
+ """Test that numbers work for enum values.""" |
+ message = protojson.decode_message(MyMessage, '{"an_enum": 2}') |
+ |
+ expected_message = MyMessage() |
+ expected_message.an_enum = MyMessage.Color.GREEN |
+ |
+ self.assertEquals(expected_message, message) |
+ |
+ def testNumericEnumerationNegativeTest(self): |
+ """Test with an invalid number for the enum value.""" |
+ self.assertRaisesRegexp( |
+ messages.DecodeError, |
+ 'Invalid enum value "89"', |
+ protojson.decode_message, |
+ MyMessage, |
+ '{"an_enum": 89}') |
+ |
+ def testAlphaEnumeration(self): |
+ """Test that alpha enum values work.""" |
+ message = protojson.decode_message(MyMessage, '{"an_enum": "RED"}') |
+ |
+ expected_message = MyMessage() |
+ expected_message.an_enum = MyMessage.Color.RED |
+ |
+ self.assertEquals(expected_message, message) |
+ |
+ def testAlphaEnumerationNegativeTest(self): |
+ """The alpha enum value is invalid.""" |
+ self.assertRaisesRegexp( |
+ messages.DecodeError, |
+ 'Invalid enum value "IAMINVALID"', |
+ protojson.decode_message, |
+ MyMessage, |
+ '{"an_enum": "IAMINVALID"}') |
+ |
+ def testEnumerationNegativeTestWithEmptyString(self): |
+ """The enum value is an empty string.""" |
+ self.assertRaisesRegexp( |
+ messages.DecodeError, |
+ 'Invalid enum value ""', |
+ protojson.decode_message, |
+ MyMessage, |
+ '{"an_enum": ""}') |
+ |
+ def testNullValues(self): |
+ """Test that null values overwrite existing values.""" |
+ self.assertEquals(MyMessage(), |
+ protojson.decode_message(MyMessage, |
+ ('{"an_integer": null,' |
+ ' "a_nested": null,' |
+ ' "an_enum": null' |
+ '}'))) |
+ |
+ def testEmptyList(self): |
+ """Test that empty lists are ignored.""" |
+ self.assertEquals(MyMessage(), |
+ protojson.decode_message(MyMessage, |
+ '{"a_repeated": []}')) |
+ |
+ def testNotJSON(self): |
+ """Test error when string is not valid JSON.""" |
+ self.assertRaises(ValueError, |
+ protojson.decode_message, MyMessage, '{this is not json}') |
+ |
+ def testDoNotEncodeStrangeObjects(self): |
+ """Test trying to encode a strange object. |
+ |
+ The main purpose of this test is to complete coverage. It ensures that |
+ the default behavior of the JSON encoder is preserved when someone tries to |
+ serialized an unexpected type. |
+ """ |
+ class BogusObject(object): |
+ |
+ def check_initialized(self): |
+ pass |
+ |
+ self.assertRaises(TypeError, |
+ protojson.encode_message, |
+ BogusObject()) |
+ |
+ def testMergeEmptyString(self): |
+ """Test merging the empty or space only string.""" |
+ message = protojson.decode_message(test_util.OptionalMessage, '') |
+ self.assertEquals(test_util.OptionalMessage(), message) |
+ |
+ message = protojson.decode_message(test_util.OptionalMessage, ' ') |
+ self.assertEquals(test_util.OptionalMessage(), message) |
+ |
+ def testProtojsonUnrecognizedFieldName(self): |
+ """Test that unrecognized fields are saved and can be accessed.""" |
+ decoded = protojson.decode_message(MyMessage, |
+ ('{"an_integer": 1, "unknown_val": 2}')) |
+ self.assertEquals(decoded.an_integer, 1) |
+ self.assertEquals(1, len(decoded.all_unrecognized_fields())) |
+ self.assertEquals('unknown_val', decoded.all_unrecognized_fields()[0]) |
+ self.assertEquals((2, messages.Variant.INT64), |
+ decoded.get_unrecognized_field_info('unknown_val')) |
+ |
+ def testProtojsonUnrecognizedFieldNumber(self): |
+ """Test that unrecognized fields are saved and can be accessed.""" |
+ decoded = protojson.decode_message( |
+ MyMessage, |
+ '{"an_integer": 1, "1001": "unknown", "-123": "negative", ' |
+ '"456_mixed": 2}') |
+ self.assertEquals(decoded.an_integer, 1) |
+ self.assertEquals(3, len(decoded.all_unrecognized_fields())) |
+ self.assertTrue(1001 in decoded.all_unrecognized_fields()) |
+ self.assertEquals(('unknown', messages.Variant.STRING), |
+ decoded.get_unrecognized_field_info(1001)) |
+ self.assertTrue('-123' in decoded.all_unrecognized_fields()) |
+ self.assertEquals(('negative', messages.Variant.STRING), |
+ decoded.get_unrecognized_field_info('-123')) |
+ self.assertTrue('456_mixed' in decoded.all_unrecognized_fields()) |
+ self.assertEquals((2, messages.Variant.INT64), |
+ decoded.get_unrecognized_field_info('456_mixed')) |
+ |
+ def testProtojsonUnrecognizedNull(self): |
+ """Test that unrecognized fields that are None are skipped.""" |
+ decoded = protojson.decode_message( |
+ MyMessage, |
+ '{"an_integer": 1, "unrecognized_null": null}') |
+ self.assertEquals(decoded.an_integer, 1) |
+ self.assertEquals(decoded.all_unrecognized_fields(), []) |
+ |
+ def testUnrecognizedFieldVariants(self): |
+ """Test that unrecognized fields are mapped to the right variants.""" |
+ for encoded, expected_variant in ( |
+ ('{"an_integer": 1, "unknown_val": 2}', messages.Variant.INT64), |
+ ('{"an_integer": 1, "unknown_val": 2.0}', messages.Variant.DOUBLE), |
+ ('{"an_integer": 1, "unknown_val": "string value"}', |
+ messages.Variant.STRING), |
+ ('{"an_integer": 1, "unknown_val": [1, 2, 3]}', messages.Variant.INT64), |
+ ('{"an_integer": 1, "unknown_val": [1, 2.0, 3]}', |
+ messages.Variant.DOUBLE), |
+ ('{"an_integer": 1, "unknown_val": [1, "foo", 3]}', |
+ messages.Variant.STRING), |
+ ('{"an_integer": 1, "unknown_val": true}', messages.Variant.BOOL)): |
+ decoded = protojson.decode_message(MyMessage, encoded) |
+ self.assertEquals(decoded.an_integer, 1) |
+ self.assertEquals(1, len(decoded.all_unrecognized_fields())) |
+ self.assertEquals('unknown_val', decoded.all_unrecognized_fields()[0]) |
+ _, decoded_variant = decoded.get_unrecognized_field_info('unknown_val') |
+ self.assertEquals(expected_variant, decoded_variant) |
+ |
+ def testDecodeDateTime(self): |
+ for datetime_string, datetime_vals in ( |
+ ('2012-09-30T15:31:50.262', (2012, 9, 30, 15, 31, 50, 262000)), |
+ ('2012-09-30T15:31:50', (2012, 9, 30, 15, 31, 50, 0))): |
+ message = protojson.decode_message( |
+ MyMessage, '{"a_datetime": "%s"}' % datetime_string) |
+ expected_message = MyMessage( |
+ a_datetime=datetime.datetime(*datetime_vals)) |
+ |
+ self.assertEquals(expected_message, message) |
+ |
+ def testDecodeInvalidDateTime(self): |
+ self.assertRaises(messages.DecodeError, protojson.decode_message, |
+ MyMessage, '{"a_datetime": "invalid"}') |
+ |
+ def testEncodeDateTime(self): |
+ for datetime_string, datetime_vals in ( |
+ ('2012-09-30T15:31:50.262000', (2012, 9, 30, 15, 31, 50, 262000)), |
+ ('2012-09-30T15:31:50.262123', (2012, 9, 30, 15, 31, 50, 262123)), |
+ ('2012-09-30T15:31:50', (2012, 9, 30, 15, 31, 50, 0))): |
+ decoded_message = protojson.encode_message( |
+ MyMessage(a_datetime=datetime.datetime(*datetime_vals))) |
+ expected_decoding = '{"a_datetime": "%s"}' % datetime_string |
+ self.CompareEncoded(expected_decoding, decoded_message) |
+ |
+ def testDecodeRepeatedDateTime(self): |
+ message = protojson.decode_message( |
+ MyMessage, |
+ '{"a_repeated_datetime": ["2012-09-30T15:31:50.262", ' |
+ '"2010-01-21T09:52:00", "2000-01-01T01:00:59.999999"]}') |
+ expected_message = MyMessage( |
+ a_repeated_datetime=[ |
+ datetime.datetime(2012, 9, 30, 15, 31, 50, 262000), |
+ datetime.datetime(2010, 1, 21, 9, 52), |
+ datetime.datetime(2000, 1, 1, 1, 0, 59, 999999)]) |
+ |
+ self.assertEquals(expected_message, message) |
+ |
+ def testDecodeCustom(self): |
+ message = protojson.decode_message(MyMessage, '{"a_custom": 1}') |
+ self.assertEquals(MyMessage(a_custom=1), message) |
+ |
+ def testDecodeInvalidCustom(self): |
+ self.assertRaises(messages.ValidationError, protojson.decode_message, |
+ MyMessage, '{"a_custom": "invalid"}') |
+ |
+ def testEncodeCustom(self): |
+ decoded_message = protojson.encode_message(MyMessage(a_custom=1)) |
+ self.CompareEncoded('{"a_custom": 1}', decoded_message) |
+ |
+ def testDecodeRepeatedCustom(self): |
+ message = protojson.decode_message( |
+ MyMessage, '{"a_repeated_custom": [1, 2, 3]}') |
+ self.assertEquals(MyMessage(a_repeated_custom=[1, 2, 3]), message) |
+ |
+ def testDecodeBadBase64BytesField(self): |
+ """Test decoding improperly encoded base64 bytes value.""" |
+ self.assertRaisesWithRegexpMatch( |
+ messages.DecodeError, |
+ 'Base64 decoding error: Incorrect padding', |
+ protojson.decode_message, |
+ test_util.OptionalMessage, |
+ '{"bytes_value": "abcdefghijklmnopq"}') |
+ |
+ |
+class CustomProtoJson(protojson.ProtoJson): |
+ |
+ def encode_field(self, field, value): |
+ return '{encoded}' + value |
+ |
+ def decode_field(self, field, value): |
+ return '{decoded}' + value |
+ |
+ |
+class CustomProtoJsonTest(test_util.TestCase): |
+ """Tests for serialization overriding functionality.""" |
+ |
+ def setUp(self): |
+ self.protojson = CustomProtoJson() |
+ |
+ def testEncode(self): |
+ self.assertEqual('{"a_string": "{encoded}xyz"}', |
+ self.protojson.encode_message(MyMessage(a_string='xyz'))) |
+ |
+ def testDecode(self): |
+ self.assertEqual( |
+ MyMessage(a_string='{decoded}xyz'), |
+ self.protojson.decode_message(MyMessage, '{"a_string": "xyz"}')) |
+ |
+ def testDecodeEmptyMessage(self): |
+ self.assertEqual( |
+ MyMessage(a_string='{decoded}'), |
+ self.protojson.decode_message(MyMessage, '{"a_string": ""}')) |
+ |
+ def testDefault(self): |
+ self.assertTrue(protojson.ProtoJson.get_default(), |
+ protojson.ProtoJson.get_default()) |
+ |
+ instance = CustomProtoJson() |
+ protojson.ProtoJson.set_default(instance) |
+ self.assertTrue(instance is protojson.ProtoJson.get_default()) |
+ |
+ |
+class InvalidJsonModule(object): |
+ pass |
+ |
+ |
+class ValidJsonModule(object): |
+ class JSONEncoder(object): |
+ pass |
+ |
+ |
+class TestJsonDependencyLoading(test_util.TestCase): |
+ """Test loading various implementations of json.""" |
+ |
+ def get_import(self): |
+ """Get __import__ method. |
+ |
+ Returns: |
+ The current __import__ method. |
+ """ |
+ if isinstance(__builtins__, dict): |
+ return __builtins__['__import__'] |
+ else: |
+ return __builtins__.__import__ |
+ |
+ def set_import(self, new_import): |
+ """Set __import__ method. |
+ |
+ Args: |
+ new_import: Function to replace __import__. |
+ """ |
+ if isinstance(__builtins__, dict): |
+ __builtins__['__import__'] = new_import |
+ else: |
+ __builtins__.__import__ = new_import |
+ |
+ def setUp(self): |
+ """Save original import function.""" |
+ self.simplejson = sys.modules.pop('simplejson', None) |
+ self.json = sys.modules.pop('json', None) |
+ self.original_import = self.get_import() |
+ def block_all_jsons(name, *args, **kwargs): |
+ if 'json' in name: |
+ if name in sys.modules: |
+ module = sys.modules[name] |
+ module.name = name |
+ return module |
+ raise ImportError('Unable to find %s' % name) |
+ else: |
+ return self.original_import(name, *args, **kwargs) |
+ self.set_import(block_all_jsons) |
+ |
+ def tearDown(self): |
+ """Restore original import functions and any loaded modules.""" |
+ |
+ def reset_module(name, module): |
+ if module: |
+ sys.modules[name] = module |
+ else: |
+ sys.modules.pop(name, None) |
+ reset_module('simplejson', self.simplejson) |
+ reset_module('json', self.json) |
+ imp.reload(protojson) |
+ |
+ def testLoadProtojsonWithValidJsonModule(self): |
+ """Test loading protojson module with a valid json dependency.""" |
+ sys.modules['json'] = ValidJsonModule |
+ |
+ # This will cause protojson to reload with the default json module |
+ # instead of simplejson. |
+ imp.reload(protojson) |
+ self.assertEquals('json', protojson.json.name) |
+ |
+ def testLoadProtojsonWithSimplejsonModule(self): |
+ """Test loading protojson module with simplejson dependency.""" |
+ sys.modules['simplejson'] = ValidJsonModule |
+ |
+ # This will cause protojson to reload with the default json module |
+ # instead of simplejson. |
+ imp.reload(protojson) |
+ self.assertEquals('simplejson', protojson.json.name) |
+ |
+ def testLoadProtojsonWithInvalidJsonModule(self): |
+ """Loading protojson module with an invalid json defaults to simplejson.""" |
+ sys.modules['json'] = InvalidJsonModule |
+ sys.modules['simplejson'] = ValidJsonModule |
+ |
+ # Ignore bad module and default back to simplejson. |
+ imp.reload(protojson) |
+ self.assertEquals('simplejson', protojson.json.name) |
+ |
+ def testLoadProtojsonWithInvalidJsonModuleAndNoSimplejson(self): |
+ """Loading protojson module with invalid json and no simplejson.""" |
+ sys.modules['json'] = InvalidJsonModule |
+ |
+ # Bad module without simplejson back raises errors. |
+ self.assertRaisesWithRegexpMatch( |
+ ImportError, |
+ 'json library "json" is not compatible with ProtoRPC', |
+ imp.reload, |
+ protojson) |
+ |
+ def testLoadProtojsonWithNoJsonModules(self): |
+ """Loading protojson module with invalid json and no simplejson.""" |
+ # No json modules raise the first exception. |
+ self.assertRaisesWithRegexpMatch( |
+ ImportError, |
+ 'Unable to find json', |
+ imp.reload, |
+ protojson) |
+ |
+ |
+if __name__ == '__main__': |
+ unittest.main() |