blob: d7c82158366296732f7c1ea44afa23d376a6f4d4 [file] [log] [blame]
// Copyright 2014 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 "chrome/browser/extensions/api/synced_notifications_private/synced_notifications_shim.h"
#include "base/json/json_writer.h"
#include "extensions/browser/event_router.h"
#include "sync/api/fake_sync_change_processor.h"
#include "sync/api/sync_change.h"
#include "sync/api/sync_error_factory.h"
#include "sync/protocol/sync.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
using namespace extensions;
using namespace extensions::api;
namespace {
// Builds a SyncData for the specified |type| based on |key|.
syncer::SyncData BuildData(syncer::ModelType type, const std::string& key) {
sync_pb::EntitySpecifics specifics;
if (type == syncer::SYNCED_NOTIFICATIONS) {
specifics.mutable_synced_notification()
->mutable_coalesced_notification()
->set_key(key);
} else {
specifics.mutable_synced_notification_app_info()
->add_synced_notification_app_info()
->add_app_id(key);
}
syncer::SyncData data = syncer::SyncData::CreateLocalData(
key, key, specifics);
return data;
}
// Builds a SyncChange with an update to the specified |type| based on |key|.
syncer::SyncChange BuildChange(syncer::ModelType type, const std::string& key) {
syncer::SyncChangeList change_list;
syncer::SyncData data = BuildData(type, key);
return syncer::SyncChange(FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data);
}
// Verifies that the specifics within |data| match the serialized specifics
// within |serialized|.
testing::AssertionResult DataSpecificsMatch(const syncer::SyncData& data,
const std::string& serialized) {
if (data.GetDataType() == syncer::SYNCED_NOTIFICATIONS) {
const sync_pb::SyncedNotificationSpecifics& proto =
data.GetSpecifics().synced_notification();
if (serialized != proto.SerializeAsString())
return testing::AssertionFailure() << "Notification specifics mismatch";
} else {
const sync_pb::SyncedNotificationAppInfoSpecifics& proto =
data.GetSpecifics().synced_notification_app_info();
if (serialized != proto.SerializeAsString())
return testing::AssertionFailure() << "App info specifics mismatch";
}
return testing::AssertionSuccess();
}
// Verifies that the update within |change| matchs the serialized specifics
// within |serialized|.
testing::AssertionResult ChangeSpecificsMatch(const syncer::SyncChange& change,
const std::string& serialized) {
if (change.change_type() != syncer::SyncChange::ACTION_UPDATE)
return testing::AssertionFailure() << "Invalid change type";
return DataSpecificsMatch(change.sync_data(), serialized);
}
class SyncedNotificationsShimTest : public testing::Test {
public:
SyncedNotificationsShimTest();
virtual ~SyncedNotificationsShimTest();
// Starts sync for both sync types.
void StartSync();
// Starts sync for the specified datatype |type|.
void StartSync(syncer::ModelType type);
// Transfers ownership of the last event received.
scoped_ptr<Event> GetLastEvent();
// Records that a refresh has been requested.
void RequestRefresh();
SyncedNotificationsShim* shim() { return &shim_; }
syncer::FakeSyncChangeProcessor* notification_processor() {
return notification_processor_;
}
syncer::FakeSyncChangeProcessor* app_info_processor() {
return app_info_processor_;
}
bool refresh_requested() const { return refresh_requested_; }
private:
void EventCallback(scoped_ptr<Event> event);
// Shim being tested.
SyncedNotificationsShim shim_;
syncer::FakeSyncChangeProcessor* notification_processor_;
syncer::FakeSyncChangeProcessor* app_info_processor_;
// The last event fired by the shim.
scoped_ptr<Event> last_event_fired_;
// Whether a refresh has been requested;
bool refresh_requested_;
};
SyncedNotificationsShimTest::SyncedNotificationsShimTest()
: shim_(base::Bind(&SyncedNotificationsShimTest::EventCallback,
base::Unretained(this)),
base::Bind(&SyncedNotificationsShimTest::RequestRefresh,
base::Unretained(this))),
notification_processor_(NULL),
app_info_processor_(NULL),
refresh_requested_(false) {}
SyncedNotificationsShimTest::~SyncedNotificationsShimTest() {}
void SyncedNotificationsShimTest::EventCallback(scoped_ptr<Event> event) {
ASSERT_FALSE(last_event_fired_);
last_event_fired_ = event.Pass();
}
void SyncedNotificationsShimTest::RequestRefresh() {
ASSERT_FALSE(refresh_requested_);
refresh_requested_ = true;
}
scoped_ptr<Event> SyncedNotificationsShimTest::GetLastEvent() {
return last_event_fired_.Pass();
}
void SyncedNotificationsShimTest::StartSync() {
StartSync(syncer::SYNCED_NOTIFICATIONS);
StartSync(syncer::SYNCED_NOTIFICATION_APP_INFO);
GetLastEvent();
}
void SyncedNotificationsShimTest::StartSync(syncer::ModelType type) {
scoped_ptr<syncer::FakeSyncChangeProcessor> change_processor(
new syncer::FakeSyncChangeProcessor());
if (type == syncer::SYNCED_NOTIFICATIONS)
notification_processor_ = change_processor.get();
else
app_info_processor_ = change_processor.get();
syncer::SyncDataList sync_data;
shim_.MergeDataAndStartSyncing(
type, sync_data, change_processor.Pass(), nullptr);
}
// Starting sync should fire the sync started event, but only after both types
// have started.
TEST_F(SyncedNotificationsShimTest, StartSync) {
EXPECT_FALSE(shim()->IsSyncReady());
StartSync(syncer::SYNCED_NOTIFICATIONS);
EXPECT_FALSE(shim()->IsSyncReady());
EXPECT_FALSE(GetLastEvent());
StartSync(syncer::SYNCED_NOTIFICATION_APP_INFO);
EXPECT_TRUE(shim()->IsSyncReady());
scoped_ptr<Event> event = GetLastEvent();
ASSERT_TRUE(event);
EXPECT_EQ(synced_notifications_private::OnSyncStartup::kEventName,
event->event_name);
EXPECT_TRUE(notification_processor()->changes().empty());
EXPECT_TRUE(app_info_processor()->changes().empty());
}
// A sync update should fire the OnDataChanges event with the updated
// notification.
TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesSingleNotification) {
StartSync();
syncer::SyncChangeList change_list;
change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key"));
shim()->ProcessSyncChanges(FROM_HERE, change_list);
scoped_ptr<Event> event = GetLastEvent();
ASSERT_TRUE(event);
EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName,
event->event_name);
ASSERT_TRUE(event->event_args);
EXPECT_EQ(1U, event->event_args->GetSize());
base::ListValue* args = NULL;
ASSERT_TRUE(event->event_args->GetList(0, &args));
EXPECT_EQ(1U, args->GetSize());
base::DictionaryValue* sync_change_value = NULL;
ASSERT_TRUE(args->GetDictionary(0, &sync_change_value));
scoped_ptr<synced_notifications_private::SyncChange> sync_change =
synced_notifications_private::SyncChange::FromValue(*sync_change_value);
ASSERT_TRUE(sync_change);
EXPECT_TRUE(ChangeSpecificsMatch(change_list[0],
sync_change->data.data_item));
EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
sync_change->change_type);
}
// Verify that multiple notification updates can be sent in one event.
TEST_F(SyncedNotificationsShimTest, ProcessSyncChangesMultipleNotification) {
StartSync();
syncer::SyncChangeList change_list;
change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key"));
change_list.push_back(BuildChange(syncer::SYNCED_NOTIFICATIONS, "key2"));
shim()->ProcessSyncChanges(FROM_HERE, change_list);
scoped_ptr<Event> event = GetLastEvent();
ASSERT_TRUE(event);
EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName,
event->event_name);
ASSERT_TRUE(event->event_args);
base::ListValue* args = NULL;
ASSERT_TRUE(event->event_args->GetList(0, &args));
EXPECT_EQ(2U, args->GetSize());
base::DictionaryValue* sync_change_value = NULL;
ASSERT_TRUE(args->GetDictionary(0, &sync_change_value));
scoped_ptr<synced_notifications_private::SyncChange> sync_change =
synced_notifications_private::SyncChange::FromValue(*sync_change_value);
ASSERT_TRUE(sync_change);
EXPECT_TRUE(ChangeSpecificsMatch(change_list[0],
sync_change->data.data_item));
EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
sync_change->change_type);
ASSERT_TRUE(args->GetDictionary(1, &sync_change_value));
sync_change =
synced_notifications_private::SyncChange::FromValue(*sync_change_value);
ASSERT_TRUE(sync_change);
EXPECT_TRUE(ChangeSpecificsMatch(change_list[1],
sync_change->data.data_item));
EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
sync_change->change_type);
}
// Verify AppInfo updates trigger OnDataChanges events.
TEST_F(SyncedNotificationsShimTest, ProcessSyncChangeAppInfo) {
StartSync();
syncer::SyncChangeList change_list;
change_list.push_back(
BuildChange(syncer::SYNCED_NOTIFICATION_APP_INFO, "key"));
shim()->ProcessSyncChanges(FROM_HERE, change_list);
scoped_ptr<Event> event = GetLastEvent();
ASSERT_TRUE(event);
EXPECT_EQ(synced_notifications_private::OnDataChanges::kEventName,
event->event_name);
ASSERT_TRUE(event->event_args);
EXPECT_EQ(1U, event->event_args->GetSize());
base::ListValue* args = NULL;
ASSERT_TRUE(event->event_args->GetList(0, &args));
EXPECT_EQ(1U, args->GetSize());
base::DictionaryValue* sync_change_value = NULL;
ASSERT_TRUE(args->GetDictionary(0, &sync_change_value));
scoped_ptr<synced_notifications_private::SyncChange> sync_change =
synced_notifications_private::SyncChange::FromValue(*sync_change_value);
ASSERT_TRUE(sync_change);
EXPECT_TRUE(ChangeSpecificsMatch(change_list[0],
sync_change->data.data_item));
EXPECT_EQ(synced_notifications_private::CHANGE_TYPE_UPDATED,
sync_change->change_type);
}
// Attempt to get the initial sync data both before and after sync starts.
TEST_F(SyncedNotificationsShimTest, GetInitialData) {
std::vector<linked_ptr<synced_notifications_private::SyncData> > data;
EXPECT_FALSE(shim()->GetInitialData(
synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data));
EXPECT_FALSE(shim()->GetInitialData(
synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data));
StartSync();
EXPECT_TRUE(shim()->GetInitialData(
synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data));
EXPECT_TRUE(data.empty());
EXPECT_TRUE(shim()->GetInitialData(
synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data));
EXPECT_TRUE(data.empty());
notification_processor()->data().push_back(BuildData(
syncer::SYNCED_NOTIFICATIONS, "notif_key"));
EXPECT_TRUE(shim()->GetInitialData(
synced_notifications_private::SYNC_DATA_TYPE_SYNCED_NOTIFICATION, &data));
EXPECT_EQ(1U, data.size());
EXPECT_TRUE(DataSpecificsMatch(notification_processor()->data()[0],
data[0]->data_item));
data.clear();
app_info_processor()->data().push_back(BuildData(
syncer::SYNCED_NOTIFICATION_APP_INFO, "app_key"));
EXPECT_TRUE(shim()->GetInitialData(
synced_notifications_private::SYNC_DATA_TYPE_APP_INFO, &data));
EXPECT_EQ(1U, data.size());
EXPECT_TRUE(DataSpecificsMatch(app_info_processor()->data()[0],
data[0]->data_item));
}
// Verify that notification updates are properly handled.
TEST_F(SyncedNotificationsShimTest, UpdateNotification) {
syncer::SyncData data =
BuildData(syncer::SYNCED_NOTIFICATIONS, "notif_key");
std::string serialized =
data.GetSpecifics().synced_notification().SerializeAsString();
EXPECT_FALSE(shim()->UpdateNotification(serialized));
StartSync();
EXPECT_FALSE(shim()->UpdateNotification("gibberish"));
EXPECT_TRUE(notification_processor()->changes().empty());
EXPECT_TRUE(shim()->UpdateNotification(serialized));
EXPECT_EQ(1U, notification_processor()->changes().size());
EXPECT_EQ(syncer::SyncChange::ACTION_UPDATE,
notification_processor()->changes()[0].change_type());
EXPECT_EQ(syncer::SYNCED_NOTIFICATIONS,
notification_processor()->changes()[0].sync_data().GetDataType());
EXPECT_EQ(serialized,
notification_processor()
->changes()[0]
.sync_data()
.GetSpecifics()
.synced_notification()
.SerializeAsString());
}
// Verify that SetRenderContext updates the datatype context properly.
TEST_F(SyncedNotificationsShimTest, SetRenderContext) {
const std::string kContext = "context";
EXPECT_FALSE(shim()->SetRenderContext(
synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext));
EXPECT_FALSE(refresh_requested());
StartSync();
EXPECT_TRUE(shim()->SetRenderContext(
synced_notifications_private::REFRESH_REQUEST_REFRESH_NEEDED, kContext));
EXPECT_EQ(kContext, notification_processor()->context());
EXPECT_TRUE(refresh_requested());
}
} // namespace