Index: sync/notifier/sync_invalidation_listener_unittest.cc |
diff --git a/sync/notifier/sync_invalidation_listener_unittest.cc b/sync/notifier/sync_invalidation_listener_unittest.cc |
index 18dd12317c0c236184531f2704e8ed6a6a038a39..2808b9727a7e0df2471ada7c0fb0ae561c3e24a8 100644 |
--- a/sync/notifier/sync_invalidation_listener_unittest.cc |
+++ b/sync/notifier/sync_invalidation_listener_unittest.cc |
@@ -3,23 +3,24 @@ |
// found in the LICENSE file. |
#include <cstddef> |
+#include <map> |
#include <set> |
#include <string> |
+#include <vector> |
#include "base/compiler_specific.h" |
#include "base/message_loop/message_loop.h" |
#include "base/stl_util.h" |
-#include "base/time/tick_clock.h" |
-#include "base/time/time.h" |
#include "google/cacheinvalidation/include/invalidation-client.h" |
#include "google/cacheinvalidation/include/types.h" |
#include "jingle/notifier/listener/fake_push_client.h" |
-#include "sync/internal_api/public/base/invalidation_test_util.h" |
#include "sync/internal_api/public/util/weak_handle.h" |
-#include "sync/notifier/ack_tracker.h" |
+#include "sync/notifier/dropped_invalidation_tracker.h" |
#include "sync/notifier/fake_invalidation_state_tracker.h" |
#include "sync/notifier/invalidation_util.h" |
+#include "sync/notifier/object_id_invalidation_map.h" |
#include "sync/notifier/sync_invalidation_listener.h" |
+#include "sync/notifier/unacked_invalidation_set_test_util.h" |
#include "testing/gtest/include/gtest/gtest.h" |
namespace syncer { |
@@ -38,7 +39,6 @@ const char kNewState[] = "new_state"; |
const char kPayload1[] = "payload1"; |
const char kPayload2[] = "payload2"; |
-const int64 kMinVersion = FakeInvalidationStateTracker::kMinVersion; |
const int64 kVersion1 = 1LL; |
const int64 kVersion2 = 2LL; |
@@ -137,8 +137,8 @@ class FakeInvalidationClient : public invalidation::InvalidationClient { |
class FakeDelegate : public SyncInvalidationListener::Delegate { |
public: |
explicit FakeDelegate(SyncInvalidationListener* listener) |
- : listener_(listener), |
- state_(TRANSIENT_INVALIDATION_ERROR) {} |
+ : state_(TRANSIENT_INVALIDATION_ERROR), |
+ drop_handlers_deleter_(&drop_handlers_) {} |
virtual ~FakeDelegate() {} |
size_t GetInvalidationCount(const ObjectId& id) const { |
@@ -164,7 +164,7 @@ class FakeDelegate : public SyncInvalidationListener::Delegate { |
Map::const_iterator it = invalidations_.find(id); |
if (it == invalidations_.end()) { |
ADD_FAILURE() << "No invalidations for ID " << ObjectIdToString(id); |
- return ""; |
+ return 0; |
} else { |
return it->second.back().payload(); |
} |
@@ -179,16 +179,58 @@ class FakeDelegate : public SyncInvalidationListener::Delegate { |
return it->second.back().is_unknown_version(); |
} |
} |
+ |
+ bool StartsWithUnknownVersion(const ObjectId& id) const { |
+ Map::const_iterator it = invalidations_.find(id); |
+ if (it == invalidations_.end()) { |
+ ADD_FAILURE() << "No invalidations for ID " << ObjectIdToString(id); |
+ return false; |
+ } else { |
+ return it->second.front().is_unknown_version(); |
+ } |
+ } |
+ |
InvalidatorState GetInvalidatorState() const { |
return state_; |
} |
- void Acknowledge(const ObjectId& id) { |
- listener_->Acknowledge(id, invalidations_[id].back().ack_handle()); |
+ DroppedInvalidationTracker* GetDropTrackerForObject(const ObjectId& id) { |
+ DropHandlers::iterator it = drop_handlers_.find(id); |
+ if (it == drop_handlers_.end()) { |
+ drop_handlers_.insert( |
+ std::make_pair(id, new DroppedInvalidationTracker(id))); |
+ return drop_handlers_.find(id)->second; |
+ } else { |
+ return it->second; |
+ } |
} |
- // SyncInvalidationListener::Delegate implementation. |
+ void AcknowledgeNthInvalidation(const ObjectId& id, size_t n) { |
+ List& list = invalidations_[id]; |
+ List::iterator it = list.begin() + n; |
+ it->Acknowledge(); |
+ } |
+ void AcknowledgeAll(const ObjectId& id) { |
+ List& list = invalidations_[id]; |
+ for (List::iterator it = list.begin(); it != list.end(); ++it) { |
+ it->Acknowledge(); |
+ } |
+ } |
+ |
+ void DropNthInvalidation(const ObjectId& id, size_t n) { |
+ DroppedInvalidationTracker* drop_tracker = GetDropTrackerForObject(id); |
+ List& list = invalidations_[id]; |
+ List::iterator it = list.begin() + n; |
+ it->Drop(drop_tracker); |
+ } |
+ |
+ void RecoverFromDropEvent(const ObjectId& id) { |
+ DroppedInvalidationTracker* drop_tracker = GetDropTrackerForObject(id); |
+ drop_tracker->RecordRecoveryFromDropEvent(); |
+ } |
+ |
+ // SyncInvalidationListener::Delegate implementation. |
virtual void OnInvalidate( |
const ObjectIdInvalidationMap& invalidation_map) OVERRIDE { |
ObjectIdSet ids = invalidation_map.GetObjectIds(); |
@@ -205,12 +247,16 @@ class FakeDelegate : public SyncInvalidationListener::Delegate { |
} |
private: |
- typedef std::map<ObjectId, int, ObjectIdLessThan> ObjectIdCountMap; |
typedef std::vector<Invalidation> List; |
typedef std::map<ObjectId, List, ObjectIdLessThan> Map; |
+ typedef std::map<ObjectId, |
+ DroppedInvalidationTracker*, |
+ ObjectIdLessThan> DropHandlers; |
+ |
Map invalidations_; |
- SyncInvalidationListener* listener_; |
InvalidatorState state_; |
+ DropHandlers drop_handlers_; |
+ STLValueDeleter<DropHandlers> drop_handlers_deleter_; |
}; |
invalidation::InvalidationClient* CreateFakeInvalidationClient( |
@@ -224,50 +270,6 @@ invalidation::InvalidationClient* CreateFakeInvalidationClient( |
return *fake_invalidation_client; |
} |
-// TODO(dcheng): FakeTickClock and FakeBackoffEntry ought to be factored out |
-// into a helpers file so it can be shared with the AckTracker unittest. |
-class FakeTickClock : public base::TickClock { |
- public: |
- FakeTickClock() {} |
- virtual ~FakeTickClock() {} |
- |
- void LeapForward(int seconds) { |
- ASSERT_GT(seconds, 0); |
- fake_now_ticks_ += base::TimeDelta::FromSeconds(seconds); |
- } |
- |
- virtual base::TimeTicks NowTicks() OVERRIDE { |
- return fake_now_ticks_; |
- } |
- |
- private: |
- base::TimeTicks fake_now_ticks_; |
- |
- DISALLOW_COPY_AND_ASSIGN(FakeTickClock); |
-}; |
- |
-class FakeBackoffEntry : public net::BackoffEntry { |
- public: |
- FakeBackoffEntry(const Policy *const policy, base::TickClock* tick_clock) |
- : BackoffEntry(policy), tick_clock_(tick_clock) { |
- } |
- |
- protected: |
- virtual base::TimeTicks ImplGetTimeNow() const OVERRIDE { |
- return tick_clock_->NowTicks(); |
- } |
- |
- private: |
- base::TickClock* const tick_clock_; |
-}; |
- |
-scoped_ptr<net::BackoffEntry> CreateMockEntry( |
- base::TickClock* tick_clock, |
- const net::BackoffEntry::Policy *const policy) { |
- return scoped_ptr<net::BackoffEntry>( |
- new FakeBackoffEntry(policy, tick_clock)); |
-} |
- |
class SyncInvalidationListenerTest : public testing::Test { |
protected: |
SyncInvalidationListenerTest() |
@@ -277,8 +279,7 @@ class SyncInvalidationListenerTest : public testing::Test { |
kAppsId_(kChromeSyncSourceId, "APP"), |
fake_push_client_(new notifier::FakePushClient()), |
fake_invalidation_client_(NULL), |
- listener_(&tick_clock_, |
- scoped_ptr<notifier::PushClient>(fake_push_client_)), |
+ listener_(scoped_ptr<notifier::PushClient>(fake_push_client_)), |
fake_delegate_(&listener_) {} |
virtual void SetUp() { |
@@ -304,7 +305,7 @@ class SyncInvalidationListenerTest : public testing::Test { |
listener_.Start(base::Bind(&CreateFakeInvalidationClient, |
&fake_invalidation_client_), |
kClientId, kClientInfo, kState, |
- fake_tracker_.GetAllInvalidationStates(), |
+ fake_tracker_.GetSavedInvalidations(), |
MakeWeakHandle(fake_tracker_.AsWeakPtr()), |
&fake_delegate_); |
DCHECK(fake_invalidation_client_); |
@@ -317,12 +318,12 @@ class SyncInvalidationListenerTest : public testing::Test { |
// avoid leaking the inner task. listener_.StopForTest() does not |
// schedule any tasks, so it's both necessary and sufficient to |
// drain the task queue before calling it. |
- message_loop_.RunUntilIdle(); |
+ FlushPendingWrites(); |
fake_invalidation_client_ = NULL; |
listener_.StopForTest(); |
} |
- int GetInvalidationCount(const ObjectId& id) const { |
+ size_t GetInvalidationCount(const ObjectId& id) const { |
return fake_delegate_.GetInvalidationCount(id); |
} |
@@ -338,12 +339,28 @@ class SyncInvalidationListenerTest : public testing::Test { |
return fake_delegate_.IsUnknownVersion(id); |
} |
- InvalidatorState GetInvalidatorState() const { |
- return fake_delegate_.GetInvalidatorState(); |
+ bool StartsWithUnknownVersion(const ObjectId& id) const { |
+ return fake_delegate_.StartsWithUnknownVersion(id); |
+ } |
+ |
+ void AcknowledgeNthInvalidation(const ObjectId& id, size_t n) { |
+ fake_delegate_.AcknowledgeNthInvalidation(id, n); |
+ } |
+ |
+ void DropNthInvalidation(const ObjectId& id, size_t n) { |
+ return fake_delegate_.DropNthInvalidation(id, n); |
+ } |
+ |
+ void RecoverFromDropEvent(const ObjectId& id) { |
+ return fake_delegate_.RecoverFromDropEvent(id); |
} |
- int64 GetMaxVersion(const ObjectId& id) const { |
- return fake_tracker_.GetMaxVersion(id); |
+ void AcknowledgeAll(const ObjectId& id) { |
+ fake_delegate_.AcknowledgeAll(id); |
+ } |
+ |
+ InvalidatorState GetInvalidatorState() const { |
+ return fake_delegate_.GetInvalidatorState(); |
} |
std::string GetInvalidatorClientId() const { |
@@ -354,6 +371,29 @@ class SyncInvalidationListenerTest : public testing::Test { |
return fake_tracker_.GetBootstrapData(); |
} |
+ UnackedInvalidationsMap GetSavedInvalidations() { |
+ // Allow any queued writes to go through first. |
+ FlushPendingWrites(); |
+ return fake_tracker_.GetSavedInvalidations(); |
+ } |
+ |
+ SingleObjectInvalidationSet GetSavedInvalidationsForType(const ObjectId& id) { |
+ const UnackedInvalidationsMap& saved_state = GetSavedInvalidations(); |
+ UnackedInvalidationsMap::const_iterator it = |
+ saved_state.find(kBookmarksId_); |
+ if (it == saved_state.end()) { |
+ ADD_FAILURE() << "No state saved for ID " << ObjectIdToString(id); |
+ return SingleObjectInvalidationSet(); |
+ } |
+ ObjectIdInvalidationMap map; |
+ it->second.ExportInvalidations(WeakHandle<AckHandler>(), &map); |
+ if (map.Empty()) { |
+ return SingleObjectInvalidationSet(); |
+ } else { |
+ return map.ForObject(id); |
+ } |
+ } |
+ |
ObjectIdSet GetRegisteredIds() const { |
return fake_invalidation_client_->GetRegisteredIds(); |
} |
@@ -370,9 +410,6 @@ class SyncInvalidationListenerTest : public testing::Test { |
const AckHandle ack_handle("fakedata"); |
fake_invalidation_client_->ClearAckedHandles(); |
listener_.Invalidate(fake_invalidation_client_, inv, ack_handle); |
- // Pump message loop to trigger InvalidationStateTracker::SetMaxVersion() |
- // and callback from InvalidationStateTracker::GenerateAckHandles(). |
- message_loop_.RunUntilIdle(); |
EXPECT_TRUE(fake_invalidation_client_->IsAckedHandle(ack_handle)); |
} |
@@ -380,11 +417,9 @@ class SyncInvalidationListenerTest : public testing::Test { |
void FireInvalidateUnknownVersion(const ObjectId& object_id) { |
const AckHandle ack_handle("fakedata_unknown"); |
fake_invalidation_client_->ClearAckedHandles(); |
- listener_.InvalidateUnknownVersion(fake_invalidation_client_, object_id, |
- ack_handle); |
- // Pump message loop to trigger callback from |
- // InvalidationStateTracker::GenerateAckHandles(). |
- message_loop_.RunUntilIdle(); |
+ listener_.InvalidateUnknownVersion(fake_invalidation_client_, |
+ object_id, |
+ ack_handle); |
EXPECT_TRUE(fake_invalidation_client_->IsAckedHandle(ack_handle)); |
} |
@@ -392,16 +427,18 @@ class SyncInvalidationListenerTest : public testing::Test { |
const AckHandle ack_handle("fakedata_all"); |
fake_invalidation_client_->ClearAckedHandles(); |
listener_.InvalidateAll(fake_invalidation_client_, ack_handle); |
- // Pump message loop to trigger callback from |
- // InvalidationStateTracker::GenerateAckHandles(). |
- message_loop_.RunUntilIdle(); |
EXPECT_TRUE(fake_invalidation_client_->IsAckedHandle(ack_handle)); |
} |
void WriteState(const std::string& new_state) { |
listener_.WriteState(new_state); |
+ |
// Pump message loop to trigger |
// InvalidationStateTracker::WriteState(). |
+ FlushPendingWrites(); |
+ } |
+ |
+ void FlushPendingWrites() { |
message_loop_.RunUntilIdle(); |
} |
@@ -413,29 +450,6 @@ class SyncInvalidationListenerTest : public testing::Test { |
fake_push_client_->DisableNotifications(reason); |
} |
- void VerifyUnacknowledged(const ObjectId& object_id) { |
- InvalidationStateMap state_map = fake_tracker_.GetAllInvalidationStates(); |
- EXPECT_THAT(state_map[object_id].current, |
- Not(Eq(state_map[object_id].expected))); |
- EXPECT_EQ(listener_.GetStateMapForTest(), state_map); |
- } |
- |
- void VerifyAcknowledged(const ObjectId& object_id) { |
- InvalidationStateMap state_map = fake_tracker_.GetAllInvalidationStates(); |
- EXPECT_THAT(state_map[object_id].current, |
- Eq(state_map[object_id].expected)); |
- EXPECT_EQ(listener_.GetStateMapForTest(), state_map); |
- } |
- |
- void AcknowledgeAndVerify(const ObjectId& object_id) { |
- VerifyUnacknowledged(object_id); |
- fake_delegate_.Acknowledge(object_id); |
- // Pump message loop to trigger |
- // InvalidationStateTracker::Acknowledge(). |
- message_loop_.RunUntilIdle(); |
- VerifyAcknowledged(object_id); |
- } |
- |
const ObjectId kBookmarksId_; |
const ObjectId kPreferencesId_; |
const ObjectId kExtensionsId_; |
@@ -445,13 +459,14 @@ class SyncInvalidationListenerTest : public testing::Test { |
private: |
base::MessageLoop message_loop_; |
- FakeInvalidationStateTracker fake_tracker_; |
notifier::FakePushClient* const fake_push_client_; |
protected: |
+ // A derrived test needs direct access to this. |
+ FakeInvalidationStateTracker fake_tracker_; |
+ |
// Tests need to access these directly. |
FakeInvalidationClient* fake_invalidation_client_; |
- FakeTickClock tick_clock_; |
SyncInvalidationListener listener_; |
private: |
@@ -475,11 +490,10 @@ TEST_F(SyncInvalidationListenerTest, InvalidateNoPayload) { |
FireInvalidate(id, kVersion1, NULL); |
- EXPECT_EQ(1, GetInvalidationCount(id)); |
+ ASSERT_EQ(1U, GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
EXPECT_EQ(kVersion1, GetVersion(id)); |
EXPECT_EQ("", GetPayload(id)); |
- EXPECT_EQ(kVersion1, GetMaxVersion(id)); |
- AcknowledgeAndVerify(id); |
} |
// Fire an invalidation with an empty payload. It should be |
@@ -490,11 +504,10 @@ TEST_F(SyncInvalidationListenerTest, InvalidateEmptyPayload) { |
FireInvalidate(id, kVersion1, ""); |
- EXPECT_EQ(1, GetInvalidationCount(id)); |
+ ASSERT_EQ(1U, GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
EXPECT_EQ(kVersion1, GetVersion(id)); |
EXPECT_EQ("", GetPayload(id)); |
- EXPECT_EQ(kVersion1, GetMaxVersion(id)); |
- AcknowledgeAndVerify(id); |
} |
// Fire an invalidation with a payload. It should be processed, and |
@@ -504,251 +517,133 @@ TEST_F(SyncInvalidationListenerTest, InvalidateWithPayload) { |
FireInvalidate(id, kVersion1, kPayload1); |
- EXPECT_EQ(1, GetInvalidationCount(id)); |
+ ASSERT_EQ(1U, GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
EXPECT_EQ(kVersion1, GetVersion(id)); |
EXPECT_EQ(kPayload1, GetPayload(id)); |
- EXPECT_EQ(kVersion1, GetMaxVersion(id)); |
- AcknowledgeAndVerify(id); |
+} |
+ |
+// Fire ten invalidations in a row. All should be received. |
+TEST_F(SyncInvalidationListenerTest, ManyInvalidations_NoDrop) { |
+ const int kRepeatCount = 10; |
+ const ObjectId& id = kPreferencesId_; |
+ int64 initial_version = kVersion1; |
+ for (int64 i = initial_version; i < initial_version + kRepeatCount; ++i) { |
+ FireInvalidate(id, i, kPayload1); |
+ } |
+ ASSERT_EQ(static_cast<size_t>(kRepeatCount), GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
+ EXPECT_EQ(kPayload1, GetPayload(id)); |
+ EXPECT_EQ(initial_version + kRepeatCount - 1, GetVersion(id)); |
} |
// Fire an invalidation for an unregistered object ID with a payload. It should |
// still be processed, and both the payload and the version should be updated. |
-TEST_F(SyncInvalidationListenerTest, InvalidateUnregisteredWithPayload) { |
- const ObjectId kUnregisteredId( |
- kChromeSyncSourceId, "unregistered"); |
+TEST_F(SyncInvalidationListenerTest, InvalidateBeforeRegistration_Simple) { |
+ const ObjectId kUnregisteredId(kChromeSyncSourceId, "unregistered"); |
const ObjectId& id = kUnregisteredId; |
+ ObjectIdSet ids; |
+ ids.insert(id); |
- EXPECT_EQ(0, GetInvalidationCount(id)); |
- EXPECT_EQ(kMinVersion, GetMaxVersion(id)); |
+ EXPECT_EQ(0U, GetInvalidationCount(id)); |
- FireInvalidate(id, kVersion1, "unregistered payload"); |
+ FireInvalidate(id, kVersion1, kPayload1); |
+ |
+ ASSERT_EQ(0U, GetInvalidationCount(id)); |
+ |
+ EnableNotifications(); |
+ listener_.Ready(fake_invalidation_client_); |
+ listener_.UpdateRegisteredIds(ids); |
- EXPECT_EQ(1, GetInvalidationCount(id)); |
+ ASSERT_EQ(1U, GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
EXPECT_EQ(kVersion1, GetVersion(id)); |
- EXPECT_EQ("unregistered payload", GetPayload(id)); |
- EXPECT_EQ(kVersion1, GetMaxVersion(id)); |
- AcknowledgeAndVerify(id); |
+ EXPECT_EQ(kPayload1, GetPayload(id)); |
} |
-// Fire an invalidation, then fire another one with a lower version. |
-// The first one should be processed and should update the payload and |
-// version, but the second one shouldn't. |
+// Fire ten invalidations before an object registers. Some invalidations will |
+// be dropped an replaced with an unknown version invalidation. |
+TEST_F(SyncInvalidationListenerTest, InvalidateBeforeRegistration_Drop) { |
+ const int kRepeatCount = |
+ UnackedInvalidationSet::kMaxBufferedInvalidations + 1; |
+ const ObjectId kUnregisteredId(kChromeSyncSourceId, "unregistered"); |
+ const ObjectId& id = kUnregisteredId; |
+ ObjectIdSet ids; |
+ ids.insert(id); |
+ |
+ EXPECT_EQ(0U, GetInvalidationCount(id)); |
+ |
+ int64 initial_version = kVersion1; |
+ for (int64 i = initial_version; i < initial_version + kRepeatCount; ++i) { |
+ FireInvalidate(id, i, kPayload1); |
+ } |
+ |
+ EnableNotifications(); |
+ listener_.Ready(fake_invalidation_client_); |
+ listener_.UpdateRegisteredIds(ids); |
+ |
+ ASSERT_EQ(UnackedInvalidationSet::kMaxBufferedInvalidations, |
+ GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
+ EXPECT_EQ(initial_version + kRepeatCount - 1, GetVersion(id)); |
+ EXPECT_EQ(kPayload1, GetPayload(id)); |
+ EXPECT_TRUE(StartsWithUnknownVersion(id)); |
+} |
+ |
+// Fire an invalidation, then fire another one with a lower version. Both |
+// should be received. |
TEST_F(SyncInvalidationListenerTest, InvalidateVersion) { |
const ObjectId& id = kPreferencesId_; |
FireInvalidate(id, kVersion2, kPayload2); |
- EXPECT_EQ(1, GetInvalidationCount(id)); |
+ ASSERT_EQ(1U, GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
EXPECT_EQ(kVersion2, GetVersion(id)); |
EXPECT_EQ(kPayload2, GetPayload(id)); |
- EXPECT_EQ(kVersion2, GetMaxVersion(id)); |
- AcknowledgeAndVerify(id); |
FireInvalidate(id, kVersion1, kPayload1); |
- EXPECT_EQ(1, GetInvalidationCount(id)); |
- EXPECT_EQ(kVersion2, GetVersion(id)); |
- EXPECT_EQ(kPayload2, GetPayload(id)); |
- EXPECT_EQ(kVersion2, GetMaxVersion(id)); |
- VerifyAcknowledged(id); |
+ ASSERT_EQ(2U, GetInvalidationCount(id)); |
+ ASSERT_FALSE(IsUnknownVersion(id)); |
+ |
+ EXPECT_EQ(kVersion1, GetVersion(id)); |
+ EXPECT_EQ(kPayload1, GetPayload(id)); |
} |
-// Fire an invalidation with an unknown version twice. It shouldn't update the |
-// version either time, but it should still be processed. |
+// Fire an invalidation with an unknown version. |
TEST_F(SyncInvalidationListenerTest, InvalidateUnknownVersion) { |
const ObjectId& id = kBookmarksId_; |
FireInvalidateUnknownVersion(id); |
- EXPECT_EQ(1, GetInvalidationCount(id)); |
+ ASSERT_EQ(1U, GetInvalidationCount(id)); |
EXPECT_TRUE(IsUnknownVersion(id)); |
- AcknowledgeAndVerify(id); |
- |
- FireInvalidateUnknownVersion(id); |
- EXPECT_EQ(kMinVersion, GetMaxVersion(id)); |
- AcknowledgeAndVerify(id); |
} |
-// Fire an invalidation for all enabled IDs. It shouldn't update the |
-// payload or version, but it should still invalidate the IDs. |
+// Fire an invalidation for all enabled IDs. |
TEST_F(SyncInvalidationListenerTest, InvalidateAll) { |
FireInvalidateAll(); |
for (ObjectIdSet::const_iterator it = registered_ids_.begin(); |
it != registered_ids_.end(); ++it) { |
- EXPECT_EQ(1, GetInvalidationCount(*it)); |
+ ASSERT_EQ(1U, GetInvalidationCount(*it)); |
EXPECT_TRUE(IsUnknownVersion(*it)); |
- EXPECT_EQ(kMinVersion, GetMaxVersion(*it)); |
- AcknowledgeAndVerify(*it); |
} |
} |
-// Comprehensive test of various scenarios for multiple IDs. |
+// Test a simple scenario for multiple IDs. |
TEST_F(SyncInvalidationListenerTest, InvalidateMultipleIds) { |
FireInvalidate(kBookmarksId_, 3, NULL); |
- EXPECT_EQ(1, GetInvalidationCount(kBookmarksId_)); |
+ ASSERT_EQ(1U, GetInvalidationCount(kBookmarksId_)); |
+ ASSERT_FALSE(IsUnknownVersion(kBookmarksId_)); |
EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- AcknowledgeAndVerify(kBookmarksId_); |
+ // kExtensionId is not registered, so the invalidation should not get through. |
FireInvalidate(kExtensionsId_, 2, NULL); |
- |
- EXPECT_EQ(1, GetInvalidationCount(kExtensionsId_)); |
- EXPECT_EQ(2, GetVersion(kExtensionsId_)); |
- EXPECT_EQ("", GetPayload(kExtensionsId_)); |
- EXPECT_EQ(2, GetMaxVersion(kExtensionsId_)); |
- AcknowledgeAndVerify(kExtensionsId_); |
- |
- // Invalidations with lower version numbers should be ignored. |
- |
- FireInvalidate(kBookmarksId_, 1, NULL); |
- |
- EXPECT_EQ(1, GetInvalidationCount(kBookmarksId_)); |
- EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- |
- FireInvalidate(kExtensionsId_, 1, NULL); |
- |
- EXPECT_EQ(1, GetInvalidationCount(kExtensionsId_)); |
- EXPECT_EQ(2, GetVersion(kExtensionsId_)); |
- EXPECT_EQ("", GetPayload(kExtensionsId_)); |
- EXPECT_EQ(2, GetMaxVersion(kExtensionsId_)); |
- |
- // InvalidateAll shouldn't change any version state. |
- |
- FireInvalidateAll(); |
- |
- EXPECT_EQ(2, GetInvalidationCount(kBookmarksId_)); |
- EXPECT_TRUE(IsUnknownVersion(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- AcknowledgeAndVerify(kBookmarksId_); |
- |
- EXPECT_EQ(1, GetInvalidationCount(kPreferencesId_)); |
- EXPECT_TRUE(IsUnknownVersion(kBookmarksId_)); |
- EXPECT_EQ(kMinVersion, GetMaxVersion(kPreferencesId_)); |
- AcknowledgeAndVerify(kPreferencesId_); |
- |
- // Note that kExtensionsId_ is not registered, so InvalidateAll() shouldn't |
- // affect it. |
- EXPECT_EQ(1, GetInvalidationCount(kExtensionsId_)); |
- EXPECT_EQ(2, GetVersion(kExtensionsId_)); |
- EXPECT_EQ("", GetPayload(kExtensionsId_)); |
- EXPECT_EQ(2, GetMaxVersion(kExtensionsId_)); |
- VerifyAcknowledged(kExtensionsId_); |
- |
- // Invalidations with higher version numbers should be processed. |
- |
- FireInvalidate(kPreferencesId_, 5, NULL); |
- EXPECT_EQ(2, GetInvalidationCount(kPreferencesId_)); |
- EXPECT_EQ(5, GetVersion(kPreferencesId_)); |
- EXPECT_EQ("", GetPayload(kPreferencesId_)); |
- EXPECT_EQ(5, GetMaxVersion(kPreferencesId_)); |
- AcknowledgeAndVerify(kPreferencesId_); |
- |
- FireInvalidate(kExtensionsId_, 3, NULL); |
- EXPECT_EQ(2, GetInvalidationCount(kExtensionsId_)); |
- EXPECT_EQ(3, GetVersion(kExtensionsId_)); |
- EXPECT_EQ("", GetPayload(kExtensionsId_)); |
- EXPECT_EQ(3, GetMaxVersion(kExtensionsId_)); |
- AcknowledgeAndVerify(kExtensionsId_); |
- |
- FireInvalidate(kBookmarksId_, 4, NULL); |
- EXPECT_EQ(3, GetInvalidationCount(kBookmarksId_)); |
- EXPECT_EQ(4, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(4, GetMaxVersion(kBookmarksId_)); |
- AcknowledgeAndVerify(kBookmarksId_); |
-} |
- |
-// Various tests for the local invalidation feature. |
-// Tests a "normal" scenario. We allow one timeout period to expire by sending |
-// ack handles that are not the "latest" ack handle. Once the timeout expires, |
-// we verify that we get a second callback and then acknowledge it. Once |
-// acknowledged, no further timeouts should occur. |
-TEST_F(SyncInvalidationListenerTest, InvalidateOneTimeout) { |
- listener_.GetAckTrackerForTest()->SetCreateBackoffEntryCallbackForTest( |
- base::Bind(&CreateMockEntry, &tick_clock_)); |
- |
- // Trigger the initial invalidation. |
- FireInvalidate(kBookmarksId_, 3, NULL); |
- EXPECT_EQ(1, GetInvalidationCount(kBookmarksId_)); |
- EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- VerifyUnacknowledged(kBookmarksId_); |
- |
- // Trigger one timeout. |
- tick_clock_.LeapForward(60); |
- EXPECT_TRUE(listener_.GetAckTrackerForTest()->TriggerTimeoutAtForTest( |
- tick_clock_.NowTicks())); |
- EXPECT_EQ(2, GetInvalidationCount(kBookmarksId_)); |
- // Other properties should remain the same. |
- EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- |
- AcknowledgeAndVerify(kBookmarksId_); |
- |
- // No more invalidations should remain in the queue. |
- EXPECT_TRUE(listener_.GetAckTrackerForTest()->IsQueueEmptyForTest()); |
-} |
- |
-// Test that an unacknowledged invalidation triggers reminders if the listener |
-// is restarted. |
-TEST_F(SyncInvalidationListenerTest, InvalidationTimeoutRestart) { |
- listener_.GetAckTrackerForTest()->SetCreateBackoffEntryCallbackForTest( |
- base::Bind(&CreateMockEntry, &tick_clock_)); |
- |
- FireInvalidate(kBookmarksId_, 3, NULL); |
- EXPECT_EQ(1, GetInvalidationCount(kBookmarksId_)); |
- EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- |
- // Trigger one timeout. |
- tick_clock_.LeapForward(60); |
- EXPECT_TRUE(listener_.GetAckTrackerForTest()->TriggerTimeoutAtForTest( |
- tick_clock_.NowTicks())); |
- EXPECT_EQ(2, GetInvalidationCount(kBookmarksId_)); |
- // Other properties should remain the same. |
- EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- |
- // Restarting the client should reset the retry count and the timeout period |
- // (e.g. it shouldn't increase to 120 seconds). Skip ahead 1200 seconds to be |
- // on the safe side. |
- StopClient(); |
- tick_clock_.LeapForward(1200); |
- StartClient(); |
- |
- // The bookmark invalidation state should not have changed. |
- EXPECT_EQ(2, GetInvalidationCount(kBookmarksId_)); |
- EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- |
- // Now trigger the invalidation reminder after the client restarts. |
- tick_clock_.LeapForward(60); |
- EXPECT_TRUE(listener_.GetAckTrackerForTest()->TriggerTimeoutAtForTest( |
- tick_clock_.NowTicks())); |
- EXPECT_EQ(3, GetInvalidationCount(kBookmarksId_)); |
- // Other properties should remain the same. |
- EXPECT_EQ(3, GetVersion(kBookmarksId_)); |
- EXPECT_EQ("", GetPayload(kBookmarksId_)); |
- EXPECT_EQ(3, GetMaxVersion(kBookmarksId_)); |
- |
- AcknowledgeAndVerify(kBookmarksId_); |
- |
- // No more invalidations should remain in the queue. |
- EXPECT_TRUE(listener_.GetAckTrackerForTest()->IsQueueEmptyForTest()); |
- |
- // The queue should remain empty when we restart now. |
- RestartClient(); |
- EXPECT_TRUE(listener_.GetAckTrackerForTest()->IsQueueEmptyForTest()); |
+ ASSERT_EQ(0U, GetInvalidationCount(kExtensionsId_)); |
} |
// Registration tests. |
@@ -890,22 +785,183 @@ TEST_F(SyncInvalidationListenerTest, RegisterTypesPreserved) { |
// Make sure that state is correctly purged from the local invalidation state |
// map cache when an ID is unregistered. |
TEST_F(SyncInvalidationListenerTest, UnregisterCleansUpStateMapCache) { |
+ const ObjectId& id = kBookmarksId_; |
listener_.Ready(fake_invalidation_client_); |
- EXPECT_TRUE(listener_.GetStateMapForTest().empty()); |
- FireInvalidate(kBookmarksId_, 1, "hello"); |
- EXPECT_EQ(1U, listener_.GetStateMapForTest().size()); |
- EXPECT_TRUE(ContainsKey(listener_.GetStateMapForTest(), kBookmarksId_)); |
+ EXPECT_TRUE(GetSavedInvalidations().empty()); |
+ FireInvalidate(id, 1, "hello"); |
+ EXPECT_EQ(1U, GetSavedInvalidations().size()); |
+ EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), id)); |
FireInvalidate(kPreferencesId_, 2, "world"); |
- EXPECT_EQ(2U, listener_.GetStateMapForTest().size()); |
- EXPECT_TRUE(ContainsKey(listener_.GetStateMapForTest(), kBookmarksId_)); |
- EXPECT_TRUE(ContainsKey(listener_.GetStateMapForTest(), kPreferencesId_)); |
+ EXPECT_EQ(2U, GetSavedInvalidations().size()); |
+ |
+ EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), id)); |
+ EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), kPreferencesId_)); |
ObjectIdSet ids; |
- ids.insert(kBookmarksId_); |
+ ids.insert(id); |
listener_.UpdateRegisteredIds(ids); |
- EXPECT_EQ(1U, listener_.GetStateMapForTest().size()); |
- EXPECT_TRUE(ContainsKey(listener_.GetStateMapForTest(), kBookmarksId_)); |
+ EXPECT_EQ(1U, GetSavedInvalidations().size()); |
+ EXPECT_TRUE(ContainsKey(GetSavedInvalidations(), id)); |
+} |
+ |
+TEST_F(SyncInvalidationListenerTest, DuplicateInvaldiations_Simple) { |
+ const ObjectId& id = kBookmarksId_; |
+ listener_.Ready(fake_invalidation_client_); |
+ |
+ // Send a stream of invalidations, including two copies of the second. |
+ FireInvalidate(id, 1, "one"); |
+ FireInvalidate(id, 2, "two"); |
+ FireInvalidate(id, 3, "three"); |
+ FireInvalidate(id, 2, "two"); |
+ |
+ // Expect that the duplicate was discarded. |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ EXPECT_EQ(3U, list.GetSize()); |
+ SingleObjectInvalidationSet::const_iterator it = list.begin(); |
+ EXPECT_EQ(1, it->version()); |
+ it++; |
+ EXPECT_EQ(2, it->version()); |
+ it++; |
+ EXPECT_EQ(3, it->version()); |
+} |
+ |
+TEST_F(SyncInvalidationListenerTest, DuplicateInvalidations_NearBufferLimit) { |
+ const size_t kPairsToSend = UnackedInvalidationSet::kMaxBufferedInvalidations; |
+ const ObjectId& id = kBookmarksId_; |
+ listener_.Ready(fake_invalidation_client_); |
+ |
+ // We will have enough buffer space in the state tracker for all these |
+ // invalidations only if duplicates are ignored. |
+ for (size_t i = 0; i < kPairsToSend; ++i) { |
+ FireInvalidate(id, i, "payload"); |
+ FireInvalidate(id, i, "payload"); |
+ } |
+ |
+ // Expect that the state map ignored duplicates. |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ EXPECT_EQ(kPairsToSend, list.GetSize()); |
+ EXPECT_FALSE(list.begin()->is_unknown_version()); |
+ |
+ // Expect that all invalidations (including duplicates) were emitted. |
+ EXPECT_EQ(kPairsToSend*2, GetInvalidationCount(id)); |
+ |
+ // Acknowledge all invalidations to clear the internal state. |
+ AcknowledgeAll(id); |
+ EXPECT_TRUE(GetSavedInvalidationsForType(id).IsEmpty()); |
+} |
+ |
+TEST_F(SyncInvalidationListenerTest, DuplicateInvalidations_UnknownVersion) { |
+ const ObjectId& id = kBookmarksId_; |
+ listener_.Ready(fake_invalidation_client_); |
+ |
+ FireInvalidateUnknownVersion(id); |
+ FireInvalidateUnknownVersion(id); |
+ |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ EXPECT_EQ(1U, list.GetSize()); |
+ } |
+ |
+ // Acknowledge the second. There should be no effect on the stored list. |
+ ASSERT_EQ(2U, GetInvalidationCount(id)); |
+ AcknowledgeNthInvalidation(id, 1); |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ EXPECT_EQ(1U, list.GetSize()); |
+ } |
+ |
+ // Acknowledge the first. This should remove the invalidation from the list. |
+ ASSERT_EQ(2U, GetInvalidationCount(id)); |
+ AcknowledgeNthInvalidation(id, 0); |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ EXPECT_EQ(0U, list.GetSize()); |
+ } |
+} |
+ |
+// Make sure that acknowledgements erase items from the local store. |
+TEST_F(SyncInvalidationListenerTest, AcknowledgementsCleanUpStateMapCache) { |
+ const ObjectId& id = kBookmarksId_; |
+ listener_.Ready(fake_invalidation_client_); |
+ |
+ EXPECT_TRUE(GetSavedInvalidations().empty()); |
+ FireInvalidate(id, 10, "hello"); |
+ FireInvalidate(id, 20, "world"); |
+ FireInvalidateUnknownVersion(id); |
+ |
+ // Expect that all three invalidations have been saved to permanent storage. |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ ASSERT_EQ(3U, list.GetSize()); |
+ EXPECT_TRUE(list.begin()->is_unknown_version()); |
+ EXPECT_EQ(20, list.back().version()); |
+ } |
+ |
+ // Acknowledge the second sent invaldiation (version 20) and verify it was |
+ // removed from storage. |
+ AcknowledgeNthInvalidation(id, 1); |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ ASSERT_EQ(2U, list.GetSize()); |
+ EXPECT_TRUE(list.begin()->is_unknown_version()); |
+ EXPECT_EQ(10, list.back().version()); |
+ } |
+ |
+ // Acknowledge the last sent invalidation (unknown version) and verify it was |
+ // removed from storage. |
+ AcknowledgeNthInvalidation(id, 2); |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ ASSERT_EQ(1U, list.GetSize()); |
+ EXPECT_FALSE(list.begin()->is_unknown_version()); |
+ EXPECT_EQ(10, list.back().version()); |
+ } |
+} |
+ |
+// Make sure that drops erase items from the local store. |
+TEST_F(SyncInvalidationListenerTest, DropsCleanUpStateMapCache) { |
+ const ObjectId& id = kBookmarksId_; |
+ listener_.Ready(fake_invalidation_client_); |
+ |
+ EXPECT_TRUE(GetSavedInvalidations().empty()); |
+ FireInvalidate(id, 10, "hello"); |
+ FireInvalidate(id, 20, "world"); |
+ FireInvalidateUnknownVersion(id); |
+ |
+ // Expect that all three invalidations have been saved to permanent storage. |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ ASSERT_EQ(3U, list.GetSize()); |
+ EXPECT_TRUE(list.begin()->is_unknown_version()); |
+ EXPECT_EQ(20, list.back().version()); |
+ } |
+ |
+ // Drop the second sent invalidation (version 20) and verify it was removed |
+ // from storage. Also verify we still have an unknown version invalidation. |
+ DropNthInvalidation(id, 1); |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ ASSERT_EQ(2U, list.GetSize()); |
+ EXPECT_TRUE(list.begin()->is_unknown_version()); |
+ EXPECT_EQ(10, list.back().version()); |
+ } |
+ |
+ // Drop the remaining invalidation. Verify an unknown version is all that |
+ // remains. |
+ DropNthInvalidation(id, 0); |
+ { |
+ SingleObjectInvalidationSet list = GetSavedInvalidationsForType(id); |
+ ASSERT_EQ(1U, list.GetSize()); |
+ EXPECT_TRUE(list.begin()->is_unknown_version()); |
+ } |
+ |
+ // Announce that the delegate has recovered from the drop. Verify no |
+ // invalidations remain saved. |
+ RecoverFromDropEvent(id); |
+ EXPECT_TRUE(GetSavedInvalidationsForType(id).IsEmpty()); |
+ |
+ RecoverFromDropEvent(id); |
} |
// Without readying the client, disable notifications, then enable |
@@ -1012,6 +1068,60 @@ TEST_F(SyncInvalidationListenerTest, InvalidationClientAuthError) { |
EXPECT_EQ(INVALIDATIONS_ENABLED, GetInvalidatorState()); |
} |
+// A variant of SyncInvalidationListenerTest that starts with some initial |
+// state. We make not attempt to abstract away the contents of this state. The |
+// tests that make use of this harness depend on its implementation details. |
+class SyncInvalidationListenerTest_WithInitialState |
+ : public SyncInvalidationListenerTest { |
+ public: |
+ virtual void SetUp() { |
+ UnackedInvalidationSet bm_state(kBookmarksId_); |
+ UnackedInvalidationSet ext_state(kExtensionsId_); |
+ |
+ Invalidation bm_unknown = Invalidation::InitUnknownVersion(kBookmarksId_); |
+ Invalidation bm_v100 = Invalidation::Init(kBookmarksId_, 100, "hundred"); |
+ bm_state.Add(bm_unknown); |
+ bm_state.Add(bm_v100); |
+ |
+ Invalidation ext_v10 = Invalidation::Init(kExtensionsId_, 10, "ten"); |
+ Invalidation ext_v20 = Invalidation::Init(kExtensionsId_, 20, "twenty"); |
+ ext_state.Add(ext_v10); |
+ ext_state.Add(ext_v20); |
+ |
+ initial_state.insert(std::make_pair(kBookmarksId_, bm_state)); |
+ initial_state.insert(std::make_pair(kExtensionsId_, ext_state)); |
+ |
+ fake_tracker_.SetSavedInvalidations(initial_state); |
+ |
+ SyncInvalidationListenerTest::SetUp(); |
+ } |
+ |
+ UnackedInvalidationsMap initial_state; |
+}; |
+ |
+// Verify that saved invalidations are forwarded when handlers register. |
+TEST_F(SyncInvalidationListenerTest_WithInitialState, |
+ ReceiveSavedInvalidations) { |
+ EnableNotifications(); |
+ listener_.Ready(fake_invalidation_client_); |
+ |
+ EXPECT_THAT(initial_state, test_util::Eq(GetSavedInvalidations())); |
+ |
+ ASSERT_EQ(2U, GetInvalidationCount(kBookmarksId_)); |
+ EXPECT_EQ(100, GetVersion(kBookmarksId_)); |
+ |
+ ASSERT_EQ(0U, GetInvalidationCount(kExtensionsId_)); |
+ |
+ FireInvalidate(kExtensionsId_, 30, "thirty"); |
+ |
+ ObjectIdSet ids = GetRegisteredIds(); |
+ ids.insert(kExtensionsId_); |
+ listener_.UpdateRegisteredIds(ids); |
+ |
+ ASSERT_EQ(3U, GetInvalidationCount(kExtensionsId_)); |
+ EXPECT_EQ(30, GetVersion(kExtensionsId_)); |
+} |
+ |
} // namespace |
} // namespace syncer |