|  | // 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 |