blob: 92f697f12a2d2690f05dea5dfd46f57f13ae668a [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 <string>
#include "base/message_loop.h"
#include "google/cacheinvalidation/include/invalidation-client.h"
#include "google/cacheinvalidation/include/types.h"
#include "google/cacheinvalidation/v2/types.pb.h"
#include "jingle/notifier/listener/fake_push_client.h"
#include "sync/internal_api/public/syncable/model_type.h"
#include "sync/internal_api/public/syncable/model_type_payload_map.h"
#include "sync/notifier/chrome_invalidation_client.h"
#include "sync/notifier/mock_invalidation_state_tracker.h"
#include "sync/util/weak_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace sync_notifier {
using ::testing::_;
using ::testing::Return;
using ::testing::StrictMock;
namespace {
const char kClientId[] = "client_id";
const char kClientInfo[] = "client_info";
const char kState[] = "state";
const char kNewState[] = "new_state";
class MockInvalidationClient : public invalidation::InvalidationClient {
public:
MOCK_METHOD0(Start, void());
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(Register, void(const invalidation::ObjectId&));
MOCK_METHOD1(Register, void(const std::vector<invalidation::ObjectId>&));
MOCK_METHOD1(Unregister, void(const invalidation::ObjectId&));
MOCK_METHOD1(Unregister, void(const std::vector<invalidation::ObjectId>&));
MOCK_METHOD1(Acknowledge, void(const invalidation::AckHandle&));
};
class MockListener : public ChromeInvalidationClient::Listener {
public:
MOCK_METHOD1(OnInvalidate, void(const syncable::ModelTypePayloadMap&));
MOCK_METHOD1(OnSessionStatusChanged, void(bool));
};
} // namespace
class ChromeInvalidationClientTest : public testing::Test {
protected:
ChromeInvalidationClientTest()
: client_(
scoped_ptr<notifier::PushClient>(
new notifier::FakePushClient())) {}
virtual void SetUp() {
client_.Start(kClientId, kClientInfo, kState,
InvalidationVersionMap(),
browser_sync::MakeWeakHandle(
mock_invalidation_state_tracker_.AsWeakPtr()),
&mock_listener_);
}
virtual void TearDown() {
// client_.Stop() stops the invalidation scheduler, which deletes any
// pending tasks without running them. Some tasks "run and delete" another
// task, so they must be run in order to avoid leaking the inner task.
// client_.Stop() does not schedule any tasks, so it's both necessary and
// sufficient to drain the task queue before calling it.
message_loop_.RunAllPending();
client_.Stop();
}
// |payload| can be NULL, but not |type_name|.
void FireInvalidate(const char* type_name,
int64 version, const char* payload) {
const invalidation::ObjectId object_id(
ipc::invalidation::ObjectSource::CHROME_SYNC, type_name);
std::string payload_tmp = payload ? payload : "";
invalidation::Invalidation inv;
if (payload) {
inv = invalidation::Invalidation(object_id, version, payload);
} else {
inv = invalidation::Invalidation(object_id, version);
}
invalidation::AckHandle ack_handle("fakedata");
EXPECT_CALL(mock_invalidation_client_, Acknowledge(ack_handle));
client_.Invalidate(&mock_invalidation_client_, inv, ack_handle);
// Pump message loop to trigger
// InvalidationStateTracker::SetMaxVersion().
message_loop_.RunAllPending();
}
// |payload| can be NULL, but not |type_name|.
void FireInvalidateUnknownVersion(const char* type_name) {
const invalidation::ObjectId object_id(
ipc::invalidation::ObjectSource::CHROME_SYNC, type_name);
invalidation::AckHandle ack_handle("fakedata");
EXPECT_CALL(mock_invalidation_client_, Acknowledge(ack_handle));
client_.InvalidateUnknownVersion(&mock_invalidation_client_, object_id,
ack_handle);
}
void FireInvalidateAll() {
invalidation::AckHandle ack_handle("fakedata");
EXPECT_CALL(mock_invalidation_client_, Acknowledge(ack_handle));
client_.InvalidateAll(&mock_invalidation_client_, ack_handle);
}
MessageLoop message_loop_;
StrictMock<MockListener> mock_listener_;
StrictMock<MockInvalidationStateTracker>
mock_invalidation_state_tracker_;
StrictMock<MockInvalidationClient> mock_invalidation_client_;
ChromeInvalidationClient client_;
};
namespace {
syncable::ModelTypePayloadMap MakeMap(syncable::ModelType model_type,
const std::string& payload) {
syncable::ModelTypePayloadMap type_payloads;
type_payloads[model_type] = payload;
return type_payloads;
}
syncable::ModelTypePayloadMap MakeMapFromSet(syncable::ModelTypeSet types,
const std::string& payload) {
return syncable::ModelTypePayloadMapFromEnumSet(types, payload);
}
} // namespace
TEST_F(ChromeInvalidationClientTest, InvalidateBadObjectId) {
syncable::ModelTypeSet types(syncable::BOOKMARKS, syncable::APPS);
client_.RegisterTypes(types);
EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
FireInvalidate("bad", 1, NULL);
message_loop_.RunAllPending();
}
TEST_F(ChromeInvalidationClientTest, InvalidateNoPayload) {
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::BOOKMARKS, "")));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::BOOKMARKS, 1));
FireInvalidate("BOOKMARK", 1, NULL);
}
TEST_F(ChromeInvalidationClientTest, InvalidateWithPayload) {
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::PREFERENCES, "payload")));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::PREFERENCES, 1));
FireInvalidate("PREFERENCE", 1, "payload");
}
TEST_F(ChromeInvalidationClientTest, WriteState) {
EXPECT_CALL(mock_invalidation_state_tracker_,
SetInvalidationState(kNewState));
client_.WriteState(kNewState);
}
TEST_F(ChromeInvalidationClientTest, InvalidateVersion) {
using ::testing::Mock;
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::APPS, "")));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::APPS, 1));
// Should trigger.
FireInvalidate("APP", 1, NULL);
Mock::VerifyAndClearExpectations(&mock_listener_);
// Should be dropped.
FireInvalidate("APP", 1, NULL);
}
TEST_F(ChromeInvalidationClientTest, InvalidateUnknownVersion) {
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::EXTENSIONS, "")))
.Times(2);
// Should trigger twice.
FireInvalidateUnknownVersion("EXTENSION");
FireInvalidateUnknownVersion("EXTENSION");
}
TEST_F(ChromeInvalidationClientTest, InvalidateVersionMultipleTypes) {
using ::testing::Mock;
syncable::ModelTypeSet types(syncable::BOOKMARKS, syncable::APPS);
client_.RegisterTypes(types);
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::APPS, "")));
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::EXTENSIONS, "")));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::APPS, 3));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::EXTENSIONS, 2));
// Should trigger both.
FireInvalidate("APP", 3, NULL);
FireInvalidate("EXTENSION", 2, NULL);
Mock::VerifyAndClearExpectations(&mock_listener_);
Mock::VerifyAndClearExpectations(&mock_invalidation_state_tracker_);
// Should both be dropped.
FireInvalidate("APP", 1, NULL);
FireInvalidate("EXTENSION", 1, NULL);
Mock::VerifyAndClearExpectations(&mock_listener_);
Mock::VerifyAndClearExpectations(&mock_invalidation_state_tracker_);
// InvalidateAll shouldn't change any version state.
EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
FireInvalidateAll();
Mock::VerifyAndClearExpectations(&mock_listener_);
Mock::VerifyAndClearExpectations(&mock_invalidation_state_tracker_);
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::PREFERENCES, "")));
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::EXTENSIONS, "")));
EXPECT_CALL(mock_listener_,
OnInvalidate(MakeMap(syncable::APPS, "")));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::PREFERENCES, 5));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::EXTENSIONS, 3));
EXPECT_CALL(mock_invalidation_state_tracker_,
SetMaxVersion(syncable::APPS, 4));
// Should trigger all three.
FireInvalidate("PREFERENCE", 5, NULL);
FireInvalidate("EXTENSION", 3, NULL);
FireInvalidate("APP", 4, NULL);
}
TEST_F(ChromeInvalidationClientTest, InvalidateAll) {
syncable::ModelTypeSet types(syncable::PREFERENCES, syncable::EXTENSIONS);
client_.RegisterTypes(types);
EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
FireInvalidateAll();
}
TEST_F(ChromeInvalidationClientTest, RegisterTypes) {
syncable::ModelTypeSet types(syncable::PREFERENCES, syncable::EXTENSIONS);
client_.RegisterTypes(types);
// Registered types should be preserved across Stop/Start.
TearDown();
SetUp();
EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
FireInvalidateAll();
}
// TODO(akalin): Flesh out unit tests.
} // namespace sync_notifier