| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/json/json_value_converter.h" |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "base/json/json_reader.h" |
| #include "base/strings/string_piece.h" |
| #include "base/values.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base { |
| namespace { |
| |
| // Very simple messages. |
| struct SimpleMessage { |
| enum SimpleEnum { |
| FOO, BAR, |
| }; |
| int foo; |
| std::string bar; |
| bool baz; |
| bool bstruct; |
| SimpleEnum simple_enum; |
| std::vector<std::unique_ptr<int>> ints; |
| std::vector<std::unique_ptr<std::string>> string_values; |
| SimpleMessage() : foo(0), baz(false), bstruct(false), simple_enum(FOO) {} |
| |
| static bool ParseSimpleEnum(StringPiece value, SimpleEnum* field) { |
| if (value == "foo") { |
| *field = FOO; |
| return true; |
| } |
| if (value == "bar") { |
| *field = BAR; |
| return true; |
| } |
| return false; |
| } |
| |
| static bool HasFieldPresent(const base::Value* value, bool* result) { |
| *result = value != nullptr; |
| return true; |
| } |
| |
| static bool GetValueString(const base::Value* value, std::string* result) { |
| const base::DictionaryValue* dict = nullptr; |
| if (!value->GetAsDictionary(&dict)) |
| return false; |
| |
| if (!dict->GetString("val", result)) |
| return false; |
| |
| return true; |
| } |
| |
| static void RegisterJSONConverter( |
| base::JSONValueConverter<SimpleMessage>* converter) { |
| converter->RegisterIntField("foo", &SimpleMessage::foo); |
| converter->RegisterStringField("bar", &SimpleMessage::bar); |
| converter->RegisterBoolField("baz", &SimpleMessage::baz); |
| converter->RegisterCustomField<SimpleEnum>( |
| "simple_enum", &SimpleMessage::simple_enum, &ParseSimpleEnum); |
| converter->RegisterRepeatedInt("ints", &SimpleMessage::ints); |
| converter->RegisterCustomValueField<bool>("bstruct", |
| &SimpleMessage::bstruct, |
| &HasFieldPresent); |
| converter->RegisterRepeatedCustomValue<std::string>( |
| "string_values", |
| &SimpleMessage::string_values, |
| &GetValueString); |
| } |
| }; |
| |
| // For nested messages. |
| struct NestedMessage { |
| double foo; |
| SimpleMessage child; |
| std::vector<std::unique_ptr<SimpleMessage>> children; |
| |
| NestedMessage() : foo(0) {} |
| |
| static void RegisterJSONConverter( |
| base::JSONValueConverter<NestedMessage>* converter) { |
| converter->RegisterDoubleField("foo", &NestedMessage::foo); |
| converter->RegisterNestedField("child", &NestedMessage::child); |
| converter->RegisterRepeatedMessage("children", &NestedMessage::children); |
| } |
| }; |
| |
| } // namespace |
| |
| TEST(JSONValueConverterTest, ParseSimpleMessage) { |
| const char normal_data[] = |
| "{\n" |
| " \"foo\": 1,\n" |
| " \"bar\": \"bar\",\n" |
| " \"baz\": true,\n" |
| " \"bstruct\": {},\n" |
| " \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}]," |
| " \"simple_enum\": \"foo\"," |
| " \"ints\": [1, 2]" |
| "}\n"; |
| |
| std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); |
| SimpleMessage message; |
| base::JSONValueConverter<SimpleMessage> converter; |
| EXPECT_TRUE(converter.Convert(*value.get(), &message)); |
| |
| EXPECT_EQ(1, message.foo); |
| EXPECT_EQ("bar", message.bar); |
| EXPECT_TRUE(message.baz); |
| EXPECT_EQ(SimpleMessage::FOO, message.simple_enum); |
| EXPECT_EQ(2, static_cast<int>(message.ints.size())); |
| ASSERT_EQ(2U, message.string_values.size()); |
| EXPECT_EQ("value_1", *message.string_values[0]); |
| EXPECT_EQ("value_2", *message.string_values[1]); |
| EXPECT_EQ(1, *(message.ints[0])); |
| EXPECT_EQ(2, *(message.ints[1])); |
| } |
| |
| TEST(JSONValueConverterTest, ParseNestedMessage) { |
| const char normal_data[] = |
| "{\n" |
| " \"foo\": 1.0,\n" |
| " \"child\": {\n" |
| " \"foo\": 1,\n" |
| " \"bar\": \"bar\",\n" |
| " \"bstruct\": {},\n" |
| " \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}]," |
| " \"baz\": true\n" |
| " },\n" |
| " \"children\": [{\n" |
| " \"foo\": 2,\n" |
| " \"bar\": \"foobar\",\n" |
| " \"bstruct\": \"\",\n" |
| " \"string_values\": [{\"val\": \"value_1\"}]," |
| " \"baz\": true\n" |
| " },\n" |
| " {\n" |
| " \"foo\": 3,\n" |
| " \"bar\": \"barbaz\",\n" |
| " \"baz\": false\n" |
| " }]\n" |
| "}\n"; |
| |
| std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); |
| NestedMessage message; |
| base::JSONValueConverter<NestedMessage> converter; |
| EXPECT_TRUE(converter.Convert(*value.get(), &message)); |
| |
| EXPECT_EQ(1.0, message.foo); |
| EXPECT_EQ(1, message.child.foo); |
| EXPECT_EQ("bar", message.child.bar); |
| EXPECT_TRUE(message.child.baz); |
| EXPECT_TRUE(message.child.bstruct); |
| ASSERT_EQ(2U, message.child.string_values.size()); |
| EXPECT_EQ("value_1", *message.child.string_values[0]); |
| EXPECT_EQ("value_2", *message.child.string_values[1]); |
| |
| EXPECT_EQ(2, static_cast<int>(message.children.size())); |
| const SimpleMessage* first_child = message.children[0].get(); |
| ASSERT_TRUE(first_child); |
| EXPECT_EQ(2, first_child->foo); |
| EXPECT_EQ("foobar", first_child->bar); |
| EXPECT_TRUE(first_child->baz); |
| EXPECT_TRUE(first_child->bstruct); |
| ASSERT_EQ(1U, first_child->string_values.size()); |
| EXPECT_EQ("value_1", *first_child->string_values[0]); |
| |
| const SimpleMessage* second_child = message.children[1].get(); |
| ASSERT_TRUE(second_child); |
| EXPECT_EQ(3, second_child->foo); |
| EXPECT_EQ("barbaz", second_child->bar); |
| EXPECT_FALSE(second_child->baz); |
| EXPECT_FALSE(second_child->bstruct); |
| EXPECT_EQ(0U, second_child->string_values.size()); |
| } |
| |
| TEST(JSONValueConverterTest, ParseFailures) { |
| const char normal_data[] = |
| "{\n" |
| " \"foo\": 1,\n" |
| " \"bar\": 2,\n" // "bar" is an integer here. |
| " \"baz\": true,\n" |
| " \"ints\": [1, 2]" |
| "}\n"; |
| |
| std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); |
| SimpleMessage message; |
| base::JSONValueConverter<SimpleMessage> converter; |
| EXPECT_FALSE(converter.Convert(*value.get(), &message)); |
| // Do not check the values below. |message| may be modified during |
| // Convert() even it fails. |
| } |
| |
| TEST(JSONValueConverterTest, ParseWithMissingFields) { |
| const char normal_data[] = |
| "{\n" |
| " \"foo\": 1,\n" |
| " \"baz\": true,\n" |
| " \"ints\": [1, 2]" |
| "}\n"; |
| |
| std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); |
| SimpleMessage message; |
| base::JSONValueConverter<SimpleMessage> converter; |
| // Convert() still succeeds even if the input doesn't have "bar" field. |
| EXPECT_TRUE(converter.Convert(*value.get(), &message)); |
| |
| EXPECT_EQ(1, message.foo); |
| EXPECT_TRUE(message.baz); |
| EXPECT_EQ(2, static_cast<int>(message.ints.size())); |
| EXPECT_EQ(1, *(message.ints[0])); |
| EXPECT_EQ(2, *(message.ints[1])); |
| } |
| |
| TEST(JSONValueConverterTest, EnumParserFails) { |
| const char normal_data[] = |
| "{\n" |
| " \"foo\": 1,\n" |
| " \"bar\": \"bar\",\n" |
| " \"baz\": true,\n" |
| " \"simple_enum\": \"baz\"," |
| " \"ints\": [1, 2]" |
| "}\n"; |
| |
| std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); |
| SimpleMessage message; |
| base::JSONValueConverter<SimpleMessage> converter; |
| EXPECT_FALSE(converter.Convert(*value.get(), &message)); |
| // No check the values as mentioned above. |
| } |
| |
| TEST(JSONValueConverterTest, RepeatedValueErrorInTheMiddle) { |
| const char normal_data[] = |
| "{\n" |
| " \"foo\": 1,\n" |
| " \"bar\": \"bar\",\n" |
| " \"baz\": true,\n" |
| " \"simple_enum\": \"baz\"," |
| " \"ints\": [1, false]" |
| "}\n"; |
| |
| std::unique_ptr<Value> value = base::JSONReader::Read(normal_data); |
| SimpleMessage message; |
| base::JSONValueConverter<SimpleMessage> converter; |
| EXPECT_FALSE(converter.Convert(*value.get(), &message)); |
| // No check the values as mentioned above. |
| } |
| |
| } // namespace base |