blob: 86465d5a8e83a3f763239233dd915dde7c1b97ed [file] [log] [blame]
// 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 "components/sync/protocol/proto_value_conversions.h"
#include <string>
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/sync/base/model_type.h"
#include "components/sync/base/unique_position.h"
#include "components/sync/protocol/app_notification_specifics.pb.h"
#include "components/sync/protocol/app_setting_specifics.pb.h"
#include "components/sync/protocol/app_specifics.pb.h"
#include "components/sync/protocol/autofill_specifics.pb.h"
#include "components/sync/protocol/bookmark_specifics.pb.h"
#include "components/sync/protocol/device_info_specifics.pb.h"
#include "components/sync/protocol/encryption.pb.h"
#include "components/sync/protocol/experiments_specifics.pb.h"
#include "components/sync/protocol/extension_setting_specifics.pb.h"
#include "components/sync/protocol/extension_specifics.pb.h"
#include "components/sync/protocol/favicon_image_specifics.pb.h"
#include "components/sync/protocol/favicon_tracking_specifics.pb.h"
#include "components/sync/protocol/managed_user_setting_specifics.pb.h"
#include "components/sync/protocol/managed_user_shared_setting_specifics.pb.h"
#include "components/sync/protocol/managed_user_specifics.pb.h"
#include "components/sync/protocol/managed_user_whitelist_specifics.pb.h"
#include "components/sync/protocol/nigori_specifics.pb.h"
#include "components/sync/protocol/password_specifics.pb.h"
#include "components/sync/protocol/preference_specifics.pb.h"
#include "components/sync/protocol/priority_preference_specifics.pb.h"
#include "components/sync/protocol/search_engine_specifics.pb.h"
#include "components/sync/protocol/session_specifics.pb.h"
#include "components/sync/protocol/sync.pb.h"
#include "components/sync/protocol/theme_specifics.pb.h"
#include "components/sync/protocol/typed_url_specifics.pb.h"
#include "components/sync/protocol/wifi_credential_specifics.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
namespace {
// Keep this file in sync with the .proto files in this directory.
#define DEFINE_SPECIFICS_TO_VALUE_TEST(Key) \
TEST(ProtoValueConversionsTest, Proto_##Key##_SpecificsToValue) { \
sync_pb::EntitySpecifics specifics; \
specifics.mutable_##Key(); \
std::unique_ptr<base::DictionaryValue> value( \
EntitySpecificsToValue(specifics)); \
EXPECT_EQ(1, static_cast<int>(value->size())); \
}
// We'd also like to check if we changed any field in our messages. However,
// that's hard to do: sizeof could work, but it's platform-dependent.
// default_instance().ByteSize() won't change for most changes, since most of
// our fields are optional. So we just settle for comments in the proto files.
DEFINE_SPECIFICS_TO_VALUE_TEST(encrypted);
static_assert(43 == syncer::MODEL_TYPE_COUNT,
"When adding a new field, add a DEFINE_SPECIFICS_TO_VALUE_TEST "
"for your field below, and optionally a test for the specific "
"conversions.");
DEFINE_SPECIFICS_TO_VALUE_TEST(app);
DEFINE_SPECIFICS_TO_VALUE_TEST(app_list);
DEFINE_SPECIFICS_TO_VALUE_TEST(app_notification);
DEFINE_SPECIFICS_TO_VALUE_TEST(app_setting);
DEFINE_SPECIFICS_TO_VALUE_TEST(arc_package);
DEFINE_SPECIFICS_TO_VALUE_TEST(article);
DEFINE_SPECIFICS_TO_VALUE_TEST(autofill);
DEFINE_SPECIFICS_TO_VALUE_TEST(autofill_profile);
DEFINE_SPECIFICS_TO_VALUE_TEST(autofill_wallet);
DEFINE_SPECIFICS_TO_VALUE_TEST(bookmark);
DEFINE_SPECIFICS_TO_VALUE_TEST(device_info);
DEFINE_SPECIFICS_TO_VALUE_TEST(dictionary);
DEFINE_SPECIFICS_TO_VALUE_TEST(experiments);
DEFINE_SPECIFICS_TO_VALUE_TEST(extension);
DEFINE_SPECIFICS_TO_VALUE_TEST(extension_setting);
DEFINE_SPECIFICS_TO_VALUE_TEST(favicon_image);
DEFINE_SPECIFICS_TO_VALUE_TEST(favicon_tracking);
DEFINE_SPECIFICS_TO_VALUE_TEST(history_delete_directive);
DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user);
DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_setting);
DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_shared_setting);
DEFINE_SPECIFICS_TO_VALUE_TEST(managed_user_whitelist);
DEFINE_SPECIFICS_TO_VALUE_TEST(mountain_share);
DEFINE_SPECIFICS_TO_VALUE_TEST(nigori);
DEFINE_SPECIFICS_TO_VALUE_TEST(password);
DEFINE_SPECIFICS_TO_VALUE_TEST(preference);
DEFINE_SPECIFICS_TO_VALUE_TEST(printer);
DEFINE_SPECIFICS_TO_VALUE_TEST(priority_preference);
DEFINE_SPECIFICS_TO_VALUE_TEST(reading_list);
DEFINE_SPECIFICS_TO_VALUE_TEST(search_engine);
DEFINE_SPECIFICS_TO_VALUE_TEST(send_tab_to_self);
DEFINE_SPECIFICS_TO_VALUE_TEST(session);
DEFINE_SPECIFICS_TO_VALUE_TEST(synced_notification);
DEFINE_SPECIFICS_TO_VALUE_TEST(synced_notification_app_info);
DEFINE_SPECIFICS_TO_VALUE_TEST(theme);
DEFINE_SPECIFICS_TO_VALUE_TEST(typed_url);
DEFINE_SPECIFICS_TO_VALUE_TEST(user_consent);
DEFINE_SPECIFICS_TO_VALUE_TEST(user_event);
DEFINE_SPECIFICS_TO_VALUE_TEST(wallet_metadata);
DEFINE_SPECIFICS_TO_VALUE_TEST(wifi_credential);
TEST(ProtoValueConversionsTest, PasswordSpecifics) {
sync_pb::PasswordSpecifics specifics;
specifics.mutable_client_only_encrypted_data();
auto value = PasswordSpecificsToValue(specifics);
EXPECT_FALSE(value->Get("client_only_encrypted_data", nullptr));
}
TEST(ProtoValueConversionsTest, PasswordSpecificsData) {
sync_pb::PasswordSpecificsData specifics;
specifics.set_password_value("secret");
std::unique_ptr<base::DictionaryValue> value(
PasswordSpecificsDataToValue(specifics));
EXPECT_FALSE(value->empty());
std::string password_value;
EXPECT_TRUE(value->GetString("password_value", &password_value));
EXPECT_EQ("<redacted>", password_value);
}
TEST(ProtoValueConversionsTest, AppSettingSpecificsToValue) {
sync_pb::AppNotificationSettings specifics;
specifics.set_disabled(true);
specifics.set_oauth_client_id("some_id_value");
std::unique_ptr<base::DictionaryValue>
value(AppNotificationSettingsToValue(specifics));
EXPECT_FALSE(value->empty());
bool disabled_value = false;
std::string oauth_client_id_value;
EXPECT_TRUE(value->GetBoolean("disabled", &disabled_value));
EXPECT_EQ(true, disabled_value);
EXPECT_TRUE(value->GetString("oauth_client_id", &oauth_client_id_value));
EXPECT_EQ("some_id_value", oauth_client_id_value);
}
TEST(ProtoValueConversionsTest, AutofillWalletSpecificsToValue) {
sync_pb::AutofillWalletSpecifics specifics;
specifics.mutable_masked_card()->set_name_on_card("Igloo");
specifics.mutable_address()->set_recipient_name("John");
specifics.mutable_customer_data()->set_id("123456");
specifics.set_type(sync_pb::AutofillWalletSpecifics::UNKNOWN);
auto value = AutofillWalletSpecificsToValue(specifics);
EXPECT_FALSE(value->Get("masked_card", nullptr));
EXPECT_FALSE(value->Get("address", nullptr));
EXPECT_FALSE(value->Get("customer_data", nullptr));
specifics.set_type(sync_pb::AutofillWalletSpecifics::MASKED_CREDIT_CARD);
value = AutofillWalletSpecificsToValue(specifics);
EXPECT_TRUE(value->Get("masked_card", nullptr));
EXPECT_FALSE(value->Get("address", nullptr));
EXPECT_FALSE(value->Get("customer_data", nullptr));
specifics.set_type(sync_pb::AutofillWalletSpecifics::POSTAL_ADDRESS);
value = AutofillWalletSpecificsToValue(specifics);
EXPECT_FALSE(value->Get("masked_card", nullptr));
EXPECT_TRUE(value->Get("address", nullptr));
EXPECT_FALSE(value->Get("customer_data", nullptr));
specifics.set_type(sync_pb::AutofillWalletSpecifics::CUSTOMER_DATA);
value = AutofillWalletSpecificsToValue(specifics);
EXPECT_FALSE(value->Get("masked_card", nullptr));
EXPECT_FALSE(value->Get("address", nullptr));
EXPECT_TRUE(value->Get("customer_data", nullptr));
}
TEST(ProtoValueConversionsTest, BookmarkSpecificsData) {
const base::Time creation_time(base::Time::Now());
const std::string icon_url = "http://www.google.com/favicon.ico";
sync_pb::BookmarkSpecifics specifics;
specifics.set_creation_time_us(creation_time.ToInternalValue());
specifics.set_icon_url(icon_url);
sync_pb::MetaInfo* meta_1 = specifics.add_meta_info();
meta_1->set_key("key1");
meta_1->set_value("value1");
sync_pb::MetaInfo* meta_2 = specifics.add_meta_info();
meta_2->set_key("key2");
meta_2->set_value("value2");
std::unique_ptr<base::DictionaryValue> value(
BookmarkSpecificsToValue(specifics));
EXPECT_FALSE(value->empty());
std::string encoded_time;
EXPECT_TRUE(value->GetString("creation_time_us", &encoded_time));
EXPECT_EQ(base::Int64ToString(creation_time.ToInternalValue()), encoded_time);
std::string encoded_icon_url;
EXPECT_TRUE(value->GetString("icon_url", &encoded_icon_url));
EXPECT_EQ(icon_url, encoded_icon_url);
base::ListValue* meta_info_list;
ASSERT_TRUE(value->GetList("meta_info", &meta_info_list));
EXPECT_EQ(2u, meta_info_list->GetSize());
base::DictionaryValue* meta_info;
std::string meta_key;
std::string meta_value;
ASSERT_TRUE(meta_info_list->GetDictionary(0, &meta_info));
EXPECT_TRUE(meta_info->GetString("key", &meta_key));
EXPECT_TRUE(meta_info->GetString("value", &meta_value));
EXPECT_EQ("key1", meta_key);
EXPECT_EQ("value1", meta_value);
ASSERT_TRUE(meta_info_list->GetDictionary(1, &meta_info));
EXPECT_TRUE(meta_info->GetString("key", &meta_key));
EXPECT_TRUE(meta_info->GetString("value", &meta_value));
EXPECT_EQ("key2", meta_key);
EXPECT_EQ("value2", meta_value);
}
TEST(ProtoValueConversionsTest, ExperimentsSpecificsToValue) {
#define TEST_EXPERIMENT_ENABLED_FIELD(field) \
{ \
sync_pb::ExperimentsSpecifics specifics; \
specifics.mutable_##field(); \
auto value = ExperimentsSpecificsToValue(specifics); \
EXPECT_TRUE(value->empty()); \
} \
{ \
sync_pb::ExperimentsSpecifics specifics; \
specifics.mutable_##field()->set_enabled(false); \
auto value = ExperimentsSpecificsToValue(specifics); \
bool field_enabled = true; \
EXPECT_EQ(1u, value->size()); \
EXPECT_TRUE(value->GetBoolean(#field, &field_enabled)); \
EXPECT_FALSE(field_enabled); \
}
TEST_EXPERIMENT_ENABLED_FIELD(keystore_encryption);
TEST_EXPERIMENT_ENABLED_FIELD(history_delete_directives);
TEST_EXPERIMENT_ENABLED_FIELD(autofill_culling);
TEST_EXPERIMENT_ENABLED_FIELD(pre_commit_update_avoidance);
TEST_EXPERIMENT_ENABLED_FIELD(gcm_channel);
TEST_EXPERIMENT_ENABLED_FIELD(gcm_invalidations);
#undef TEST_EXPERIMENT_ENABLED_FIELD
}
TEST(ProtoValueConversionsTest, UniquePositionToValue) {
sync_pb::SyncEntity entity;
entity.mutable_unique_position()->set_custom_compressed_v1("test");
auto value = SyncEntityToValue(entity, false);
std::string unique_position;
EXPECT_TRUE(value->GetString("unique_position", &unique_position));
std::string expected_unique_position =
UniquePosition::FromProto(entity.unique_position()).ToDebugString();
EXPECT_EQ(expected_unique_position, unique_position);
}
TEST(ProtoValueConversionsTest, SyncEntityToValueIncludeSpecifics) {
sync_pb::SyncEntity entity;
entity.mutable_specifics();
auto value = SyncEntityToValue(entity, true /* include_specifics */);
EXPECT_TRUE(value->GetDictionary("specifics", nullptr));
value = SyncEntityToValue(entity, false /* include_specifics */);
EXPECT_FALSE(value->GetDictionary("specifics", nullptr));
}
namespace {
// Returns whether the given value has specifics under the entries in the given
// path.
bool ValueHasSpecifics(const base::DictionaryValue& value,
const std::string& path) {
const base::ListValue* entities_list = nullptr;
const base::DictionaryValue* entry_dictionary = nullptr;
const base::DictionaryValue* specifics_dictionary = nullptr;
if (!value.GetList(path, &entities_list))
return false;
if (!entities_list->GetDictionary(0, &entry_dictionary))
return false;
return entry_dictionary->GetDictionary("specifics", &specifics_dictionary);
}
} // namespace
// Create a ClientToServerMessage with an EntitySpecifics. Converting it to
// a value should respect the |include_specifics| flag.
TEST(ProtoValueConversionsTest, ClientToServerMessageToValue) {
sync_pb::ClientToServerMessage message;
sync_pb::CommitMessage* commit_message = message.mutable_commit();
sync_pb::SyncEntity* entity = commit_message->add_entries();
entity->mutable_specifics();
std::unique_ptr<base::DictionaryValue> value_with_specifics(
ClientToServerMessageToValue(message, true /* include_specifics */));
EXPECT_FALSE(value_with_specifics->empty());
EXPECT_TRUE(
ValueHasSpecifics(*(value_with_specifics.get()), "commit.entries"));
std::unique_ptr<base::DictionaryValue> value_without_specifics(
ClientToServerMessageToValue(message, false /* include_specifics */));
EXPECT_FALSE(value_without_specifics->empty());
EXPECT_FALSE(
ValueHasSpecifics(*(value_without_specifics.get()), "commit.entries"));
}
// Create a ClientToServerResponse with an EntitySpecifics. Converting it to
// a value should respect the |include_specifics| flag.
TEST(ProtoValueConversionsTest, ClientToServerResponseToValue) {
sync_pb::ClientToServerResponse message;
sync_pb::GetUpdatesResponse* response = message.mutable_get_updates();
sync_pb::SyncEntity* entity = response->add_entries();
entity->mutable_specifics();
std::unique_ptr<base::DictionaryValue> value_with_specifics(
ClientToServerResponseToValue(message, true /* include_specifics */));
EXPECT_FALSE(value_with_specifics->empty());
EXPECT_TRUE(
ValueHasSpecifics(*(value_with_specifics.get()), "get_updates.entries"));
std::unique_ptr<base::DictionaryValue> value_without_specifics(
ClientToServerResponseToValue(message, false /* include_specifics */));
EXPECT_FALSE(value_without_specifics->empty());
EXPECT_FALSE(ValueHasSpecifics(*(value_without_specifics.get()),
"get_updates.entries"));
}
} // namespace
} // namespace syncer