257 lines
7.7 KiB
C++
257 lines
7.7 KiB
C++
|
// 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 <string>
|
||
|
#include <vector>
|
||
|
|
||
|
#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<int> ints;
|
||
|
ScopedVector<std::string> 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<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;
|
||
|
ScopedVector<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";
|
||
|
|
||
|
scoped_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";
|
||
|
|
||
|
scoped_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];
|
||
|
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> 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";
|
||
|
|
||
|
scoped_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";
|
||
|
|
||
|
scoped_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";
|
||
|
|
||
|
scoped_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
|