// 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 #include #include "base/json/json_reader.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.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; ScopedVector ints; ScopedVector string_values; SimpleMessage() : foo(0), baz(false), bstruct(false), simple_enum(FOO) {} static bool ParseSimpleEnum(const StringPiece& value, SimpleEnum* field) { if (value == "foo") { *field = FOO; return true; } else if (value == "bar") { *field = BAR; return true; } return false; } static bool HasFieldPresent(const base::Value* value, bool* result) { *result = value != NULL; return true; } static bool GetValueString(const base::Value* value, std::string* result) { const base::DictionaryValue* dict = NULL; if (!value->GetAsDictionary(&dict)) return false; if (!dict->GetString("val", result)) return false; return true; } static void RegisterJSONConverter( base::JSONValueConverter* converter) { converter->RegisterIntField("foo", &SimpleMessage::foo); converter->RegisterStringField("bar", &SimpleMessage::bar); converter->RegisterBoolField("baz", &SimpleMessage::baz); converter->RegisterCustomField( "simple_enum", &SimpleMessage::simple_enum, &ParseSimpleEnum); converter->RegisterRepeatedInt("ints", &SimpleMessage::ints); converter->RegisterCustomValueField("bstruct", &SimpleMessage::bstruct, &HasFieldPresent); converter->RegisterRepeatedCustomValue( "string_values", &SimpleMessage::string_values, &GetValueString); } }; // For nested messages. struct NestedMessage { double foo; SimpleMessage child; ScopedVector children; NestedMessage() : foo(0) {} static void RegisterJSONConverter( base::JSONValueConverter* 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"; scoped_ptr value = base::JSONReader::Read(normal_data); SimpleMessage message; base::JSONValueConverter 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(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"; scoped_ptr value = base::JSONReader::Read(normal_data); NestedMessage message; base::JSONValueConverter 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(message.children.size())); const SimpleMessage* first_child = message.children[0]; 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]; 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"; scoped_ptr value = base::JSONReader::Read(normal_data); SimpleMessage message; base::JSONValueConverter 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"; scoped_ptr value = base::JSONReader::Read(normal_data); SimpleMessage message; base::JSONValueConverter 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(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"; scoped_ptr value = base::JSONReader::Read(normal_data); SimpleMessage message; base::JSONValueConverter 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"; scoped_ptr value = base::JSONReader::Read(normal_data); SimpleMessage message; base::JSONValueConverter converter; EXPECT_FALSE(converter.Convert(*value.get(), &message)); // No check the values as mentioned above. } } // namespace base