diff options
Diffstat (limited to 'src/google/protobuf/util/json_util_test.cc')
-rw-r--r-- | src/google/protobuf/util/json_util_test.cc | 370 |
1 files changed, 323 insertions, 47 deletions
diff --git a/src/google/protobuf/util/json_util_test.cc b/src/google/protobuf/util/json_util_test.cc index da68495f..ed9092df 100644 --- a/src/google/protobuf/util/json_util_test.cc +++ b/src/google/protobuf/util/json_util_test.cc @@ -34,6 +34,10 @@ #include <string> #include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> +#include <google/protobuf/descriptor_database.h> +#include <google/protobuf/dynamic_message.h> +#include <google/protobuf/util/internal/testdata/maps.pb.h> #include <google/protobuf/util/json_format_proto3.pb.h> #include <google/protobuf/util/type_resolver.h> #include <google/protobuf/util/type_resolver_util.h> @@ -44,52 +48,49 @@ namespace protobuf { namespace util { namespace { +using google::protobuf::testing::MapIn; using proto3::FOO; using proto3::BAR; using proto3::TestMessage; using proto3::TestMap; +using proto3::TestOneof; +using proto3::TestEnumValue; static const char kTypeUrlPrefix[] = "type.googleapis.com"; -static string GetTypeUrl(const Descriptor* message) { - return string(kTypeUrlPrefix) + "/" + message->full_name(); -} - // As functions defined in json_util.h are just thin wrappers around the // JSON conversion code in //net/proto2/util/converter, in this test we // only cover some very basic cases to make sure the wrappers have forwarded // parameters to the underlying implementation correctly. More detailed // tests are contained in the //net/proto2/util/converter directory. -class JsonUtilTest : public testing::Test { +class JsonUtilTest : public ::testing::Test { protected: JsonUtilTest() { - resolver_.reset(NewTypeResolverForDescriptorPool( - kTypeUrlPrefix, DescriptorPool::generated_pool())); } - string ToJson(const Message& message, const JsonOptions& options) { + string ToJson(const Message& message, const JsonPrintOptions& options) { string result; - GOOGLE_CHECK_OK(BinaryToJsonString(resolver_.get(), - GetTypeUrl(message.GetDescriptor()), - message.SerializeAsString(), &result, options)); + GOOGLE_CHECK_OK(MessageToJsonString(message, &result, options)); return result; } + bool FromJson(const string& json, Message* message, + const JsonParseOptions& options) { + return JsonStringToMessage(json, message, options).ok(); + } + bool FromJson(const string& json, Message* message) { - string binary; - GOOGLE_CHECK_OK(JsonToBinaryString( - resolver_.get(), GetTypeUrl(message->GetDescriptor()), json, &binary)); - return message->ParseFromString(binary); + return FromJson(json, message, JsonParseOptions()); } - google::protobuf::scoped_ptr<TypeResolver> resolver_; + std::unique_ptr<TypeResolver> resolver_; }; TEST_F(JsonUtilTest, TestWhitespaces) { TestMessage m; m.mutable_message_value(); - JsonOptions options; + JsonPrintOptions options; EXPECT_EQ("{\"messageValue\":{}}", ToJson(m, options)); options.add_whitespace = true; EXPECT_EQ( @@ -99,28 +100,146 @@ TEST_F(JsonUtilTest, TestWhitespaces) { ToJson(m, options)); } -// TODO(skarvaje): Uncomment after cl/96232915 is submitted. -// TEST_F(JsonUtilTest, TestDefaultValues) { - // TestMessage m; - // JsonOptions options; - // EXPECT_EQ("{}", ToJson(m, options)); - // options.always_print_primitive_fields = true; - // EXPECT_EQ( - // "{\"boolValue\":false," - // "\"int32Value\":0," - // "\"int64Value\":\"0\"," - // "\"uint32Value\":0," - // "\"uint64Value\":\"0\"," - // "\"floatValue\":0," - // "\"doubleValue\":0," - // "\"stringValue\":\"\"," - // "\"bytesValue\":\"\"," - // // TODO(xiaofeng): The default enum value should be FOO. I believe - // // this is a bug in DefaultValueObjectWriter. - // "\"enumValue\":null" - // "}", - // ToJson(m, options)); -// } +TEST_F(JsonUtilTest, TestDefaultValues) { + TestMessage m; + JsonPrintOptions options; + EXPECT_EQ("{}", ToJson(m, options)); + options.always_print_primitive_fields = true; + EXPECT_EQ( + "{\"boolValue\":false," + "\"int32Value\":0," + "\"int64Value\":\"0\"," + "\"uint32Value\":0," + "\"uint64Value\":\"0\"," + "\"floatValue\":0," + "\"doubleValue\":0," + "\"stringValue\":\"\"," + "\"bytesValue\":\"\"," + "\"enumValue\":\"FOO\"," + "\"repeatedBoolValue\":[]," + "\"repeatedInt32Value\":[]," + "\"repeatedInt64Value\":[]," + "\"repeatedUint32Value\":[]," + "\"repeatedUint64Value\":[]," + "\"repeatedFloatValue\":[]," + "\"repeatedDoubleValue\":[]," + "\"repeatedStringValue\":[]," + "\"repeatedBytesValue\":[]," + "\"repeatedEnumValue\":[]," + "\"repeatedMessageValue\":[]" + "}", + ToJson(m, options)); + + options.always_print_primitive_fields = true; + m.set_string_value("i am a test string value"); + m.set_bytes_value("i am a test bytes value"); + EXPECT_EQ( + "{\"boolValue\":false," + "\"int32Value\":0," + "\"int64Value\":\"0\"," + "\"uint32Value\":0," + "\"uint64Value\":\"0\"," + "\"floatValue\":0," + "\"doubleValue\":0," + "\"stringValue\":\"i am a test string value\"," + "\"bytesValue\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\"," + "\"enumValue\":\"FOO\"," + "\"repeatedBoolValue\":[]," + "\"repeatedInt32Value\":[]," + "\"repeatedInt64Value\":[]," + "\"repeatedUint32Value\":[]," + "\"repeatedUint64Value\":[]," + "\"repeatedFloatValue\":[]," + "\"repeatedDoubleValue\":[]," + "\"repeatedStringValue\":[]," + "\"repeatedBytesValue\":[]," + "\"repeatedEnumValue\":[]," + "\"repeatedMessageValue\":[]" + "}", + ToJson(m, options)); + + options.preserve_proto_field_names = true; + m.set_string_value("i am a test string value"); + m.set_bytes_value("i am a test bytes value"); + EXPECT_EQ( + "{\"bool_value\":false," + "\"int32_value\":0," + "\"int64_value\":\"0\"," + "\"uint32_value\":0," + "\"uint64_value\":\"0\"," + "\"float_value\":0," + "\"double_value\":0," + "\"string_value\":\"i am a test string value\"," + "\"bytes_value\":\"aSBhbSBhIHRlc3QgYnl0ZXMgdmFsdWU=\"," + "\"enum_value\":\"FOO\"," + "\"repeated_bool_value\":[]," + "\"repeated_int32_value\":[]," + "\"repeated_int64_value\":[]," + "\"repeated_uint32_value\":[]," + "\"repeated_uint64_value\":[]," + "\"repeated_float_value\":[]," + "\"repeated_double_value\":[]," + "\"repeated_string_value\":[]," + "\"repeated_bytes_value\":[]," + "\"repeated_enum_value\":[]," + "\"repeated_message_value\":[]" + "}", + ToJson(m, options)); +} + +TEST_F(JsonUtilTest, TestPreserveProtoFieldNames) { + TestMessage m; + m.mutable_message_value(); + + JsonPrintOptions options; + options.preserve_proto_field_names = true; + EXPECT_EQ("{\"message_value\":{}}", ToJson(m, options)); +} + +TEST_F(JsonUtilTest, TestAlwaysPrintEnumsAsInts) { + TestMessage orig; + orig.set_enum_value(proto3::BAR); + orig.add_repeated_enum_value(proto3::FOO); + orig.add_repeated_enum_value(proto3::BAR); + + JsonPrintOptions print_options; + print_options.always_print_enums_as_ints = true; + + string expected_json = "{\"enumValue\":1,\"repeatedEnumValue\":[0,1]}"; + EXPECT_EQ(expected_json, ToJson(orig, print_options)); + + TestMessage parsed; + JsonParseOptions parse_options; + ASSERT_TRUE(FromJson(expected_json, &parsed, parse_options)); + + EXPECT_EQ(proto3::BAR, parsed.enum_value()); + EXPECT_EQ(2, parsed.repeated_enum_value_size()); + EXPECT_EQ(proto3::FOO, parsed.repeated_enum_value(0)); + EXPECT_EQ(proto3::BAR, parsed.repeated_enum_value(1)); +} + +TEST_F(JsonUtilTest, TestPrintEnumsAsIntsWithDefaultValue) { + TestEnumValue orig; + //orig.set_enum_value1(proto3::FOO) + orig.set_enum_value2(proto3::FOO); + orig.set_enum_value3(proto3::BAR); + + JsonPrintOptions print_options; + print_options.always_print_enums_as_ints = true; + print_options.always_print_primitive_fields = true; + + string expected_json = "{\"enumValue1\":0,\"enumValue2\":0,\"enumValue3\":1}"; + EXPECT_EQ(expected_json, ToJson(orig, print_options)); + + TestEnumValue parsed; + JsonParseOptions parse_options; + ASSERT_TRUE(FromJson(expected_json, &parsed, parse_options)); + + EXPECT_EQ(proto3::FOO, parsed.enum_value1()); + EXPECT_EQ(proto3::FOO, parsed.enum_value2()); + EXPECT_EQ(proto3::BAR, parsed.enum_value3()); + +} TEST_F(JsonUtilTest, ParseMessage) { // Some random message but good enough to verify that the parsing warpper @@ -136,8 +255,9 @@ TEST_F(JsonUtilTest, ParseMessage) { " {\"value\": 40}, {\"value\": 96}\n" " ]\n" "}\n"; + JsonParseOptions options; TestMessage m; - ASSERT_TRUE(FromJson(input, &m)); + ASSERT_TRUE(FromJson(input, &m, options)); EXPECT_EQ(1024, m.int32_value()); ASSERT_EQ(2, m.repeated_int32_value_size()); EXPECT_EQ(1, m.repeated_int32_value(0)); @@ -151,18 +271,155 @@ TEST_F(JsonUtilTest, ParseMessage) { TEST_F(JsonUtilTest, ParseMap) { TestMap message; (*message.mutable_string_map())["hello"] = 1234; - JsonOptions options; - EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, options)); + JsonPrintOptions print_options; + JsonParseOptions parse_options; + EXPECT_EQ("{\"stringMap\":{\"hello\":1234}}", ToJson(message, print_options)); TestMap other; - ASSERT_TRUE(FromJson(ToJson(message, options), &other)); + ASSERT_TRUE(FromJson(ToJson(message, print_options), &other, parse_options)); + EXPECT_EQ(message.DebugString(), other.DebugString()); +} + +TEST_F(JsonUtilTest, ParsePrimitiveMapIn) { + MapIn message; + JsonPrintOptions print_options; + print_options.always_print_primitive_fields = true; + JsonParseOptions parse_options; + EXPECT_EQ("{\"other\":\"\",\"things\":[],\"mapInput\":{}}", + ToJson(message, print_options)); + MapIn other; + ASSERT_TRUE(FromJson(ToJson(message, print_options), &other, parse_options)); EXPECT_EQ(message.DebugString(), other.DebugString()); } -typedef pair<char*, int> Segment; +TEST_F(JsonUtilTest, PrintPrimitiveOneof) { + TestOneof message; + JsonPrintOptions options; + options.always_print_primitive_fields = true; + message.mutable_oneof_message_value(); + EXPECT_EQ("{\"oneofMessageValue\":{\"value\":0}}", ToJson(message, options)); + + message.set_oneof_int32_value(1); + EXPECT_EQ("{\"oneofInt32Value\":1}", ToJson(message, options)); +} + +TEST_F(JsonUtilTest, TestParseIgnoreUnknownFields) { + TestMessage m; + JsonParseOptions options; + options.ignore_unknown_fields = true; + EXPECT_TRUE(FromJson("{\"unknownName\":0}", &m, options)); +} + +TEST_F(JsonUtilTest, TestParseErrors) { + TestMessage m; + JsonParseOptions options; + // Parsing should fail if the field name can not be recognized. + EXPECT_FALSE(FromJson("{\"unknownName\":0}", &m, options)); + // Parsing should fail if the value is invalid. + EXPECT_FALSE(FromJson("{\"int32Value\":2147483648}", &m, options)); +} + +TEST_F(JsonUtilTest, TestDynamicMessage) { + // Some random message but good enough to test the wrapper functions. + string input = + "{\n" + " \"int32Value\": 1024,\n" + " \"repeatedInt32Value\": [1, 2],\n" + " \"messageValue\": {\n" + " \"value\": 2048\n" + " },\n" + " \"repeatedMessageValue\": [\n" + " {\"value\": 40}, {\"value\": 96}\n" + " ]\n" + "}\n"; + + // Create a new DescriptorPool with the same protos as the generated one. + DescriptorPoolDatabase database(*DescriptorPool::generated_pool()); + DescriptorPool pool(&database); + // A dynamic version of the test proto. + DynamicMessageFactory factory; + std::unique_ptr<Message> message(factory.GetPrototype( + pool.FindMessageTypeByName("proto3.TestMessage"))->New()); + EXPECT_TRUE(FromJson(input, message.get())); + + // Convert to generated message for easy inspection. + TestMessage generated; + EXPECT_TRUE(generated.ParseFromString(message->SerializeAsString())); + EXPECT_EQ(1024, generated.int32_value()); + ASSERT_EQ(2, generated.repeated_int32_value_size()); + EXPECT_EQ(1, generated.repeated_int32_value(0)); + EXPECT_EQ(2, generated.repeated_int32_value(1)); + EXPECT_EQ(2048, generated.message_value().value()); + ASSERT_EQ(2, generated.repeated_message_value_size()); + EXPECT_EQ(40, generated.repeated_message_value(0).value()); + EXPECT_EQ(96, generated.repeated_message_value(1).value()); + + JsonOptions options; + EXPECT_EQ(ToJson(generated, options), ToJson(*message, options)); +} + +TEST_F(JsonUtilTest, TestParsingUnknownEnumsAs0) { + TestMessage m; + { + JsonParseOptions options; + ASSERT_FALSE(options.ignore_unknown_fields); + string input = + "{\n" + " \"enum_value\":\"UNKNOWN_VALUE\"\n" + "}"; + m.set_enum_value(proto3::BAR); + EXPECT_FALSE(FromJson(input, &m, options)); + ASSERT_EQ(proto3::BAR, m.enum_value()); // Keep previous value + + options.ignore_unknown_fields = true; + EXPECT_TRUE(FromJson(input, &m, options)); + EXPECT_EQ(0, m.enum_value()); // Unknown enum value must be decoded as 0 + } + // Integer values are read as usual + { + JsonParseOptions options; + string input = + "{\n" + " \"enum_value\":12345\n" + "}"; + m.set_enum_value(proto3::BAR); + EXPECT_TRUE(FromJson(input, &m, options)); + ASSERT_EQ(12345, m.enum_value()); + + options.ignore_unknown_fields = true; + EXPECT_TRUE(FromJson(input, &m, options)); + EXPECT_EQ(12345, m.enum_value()); + } + + // Trying to pass an object as an enum field value is always treated as an error + { + JsonParseOptions options; + string input = + "{\n" + " \"enum_value\":{}\n" + "}"; + options.ignore_unknown_fields = true; + EXPECT_FALSE(FromJson(input, &m, options)); + options.ignore_unknown_fields = false; + EXPECT_FALSE(FromJson(input, &m, options)); + } + // Trying to pass an array as an enum field value is always treated as an error + { + JsonParseOptions options; + string input = + "{\n" + " \"enum_value\":[]\n" + "}"; + EXPECT_FALSE(FromJson(input, &m, options)); + options.ignore_unknown_fields = true; + EXPECT_FALSE(FromJson(input, &m, options)); + } +} + +typedef std::pair<char*, int> Segment; // A ZeroCopyOutputStream that writes to multiple buffers. class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { public: - explicit SegmentedZeroCopyOutputStream(list<Segment> segments) + explicit SegmentedZeroCopyOutputStream(std::list<Segment> segments) : segments_(segments), last_segment_(static_cast<char*>(NULL), 0), byte_count_(0) {} virtual bool Next(void** buffer, int* length) { @@ -188,7 +445,7 @@ class SegmentedZeroCopyOutputStream : public io::ZeroCopyOutputStream { virtual int64 ByteCount() const { return byte_count_; } private: - list<Segment> segments_; + std::list<Segment> segments_; Segment last_segment_; int64 byte_count_; }; @@ -206,7 +463,7 @@ TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { for (int split_pattern = 0; split_pattern < (1 << (kOutputBufferLength - 1)); split_pattern += kSkippedPatternCount) { // Split the buffer into small segments according to the split_pattern. - list<Segment> segments; + std::list<Segment> segments; int segment_start = 0; for (int i = 0; i < kOutputBufferLength - 1; ++i) { if (split_pattern & (1 << i)) { @@ -283,6 +540,25 @@ TEST(ZeroCopyStreamByteSinkTest, TestAllInputOutputPatterns) { } } +TEST_F(JsonUtilTest, TestWrongJsonInput) { + const char json[] = "{\"unknown_field\":\"some_value\"}"; + io::ArrayInputStream input_stream(json, strlen(json)); + char proto_buffer[10000]; + io::ArrayOutputStream output_stream(proto_buffer, sizeof(proto_buffer)); + std::string message_type = "type.googleapis.com/proto3.TestMessage"; + TypeResolver* resolver = NewTypeResolverForDescriptorPool( + "type.googleapis.com", DescriptorPool::generated_pool()); + + auto result_status = util::JsonToBinaryStream( + resolver, message_type, &input_stream, &output_stream); + + delete resolver; + + EXPECT_FALSE(result_status.ok()); + EXPECT_EQ(result_status.error_code(), + util::error::INVALID_ARGUMENT); +} + } // namespace } // namespace util } // namespace protobuf |