// Copyright 2013 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 "sync/notifier/unacked_invalidation_set.h"
#include "base/json/json_string_value_serializer.h"
#include "sync/notifier/object_id_invalidation_map.h"
#include "sync/notifier/single_object_invalidation_set.h"
#include "sync/notifier/unacked_invalidation_set_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
class UnackedInvalidationSetTest : public testing::Test {
public:
UnackedInvalidationSetTest()
: kObjectId_(10, "ASDF"),
unacked_invalidations_(kObjectId_) {}
SingleObjectInvalidationSet GetStoredInvalidations() {
ObjectIdInvalidationMap map;
unacked_invalidations_.ExportInvalidations(WeakHandle<AckHandler>(), &map);
ObjectIdSet ids = map.GetObjectIds();
if (ids.find(kObjectId_) != ids.end()) {
return map.ForObject(kObjectId_);
} else {
return SingleObjectInvalidationSet();
}
}
const invalidation::ObjectId kObjectId_;
UnackedInvalidationSet unacked_invalidations_;
};
namespace {
// Test storage and retrieval of zero invalidations.
TEST_F(UnackedInvalidationSetTest, Empty) {
EXPECT_EQ(0U, GetStoredInvalidations().GetSize());
}
// Test storage and retrieval of a single invalidation.
TEST_F(UnackedInvalidationSetTest, OneInvalidation) {
Invalidation inv1 = Invalidation::Init(kObjectId_, 10, "payload");
unacked_invalidations_.Add(inv1);
SingleObjectInvalidationSet set = GetStoredInvalidations();
ASSERT_EQ(1U, set.GetSize());
EXPECT_FALSE(set.StartsWithUnknownVersion());
}
// Test that calling Clear() returns us to the empty state.
TEST_F(UnackedInvalidationSetTest, Clear) {
Invalidation inv1 = Invalidation::Init(kObjectId_, 10, "payload");
unacked_invalidations_.Add(inv1);
unacked_invalidations_.Clear();
EXPECT_EQ(0U, GetStoredInvalidations().GetSize());
}
// Test that repeated unknown version invalidations are squashed together.
TEST_F(UnackedInvalidationSetTest, UnknownVersions) {
Invalidation inv1 = Invalidation::Init(kObjectId_, 10, "payload");
Invalidation inv2 = Invalidation::InitUnknownVersion(kObjectId_);
Invalidation inv3 = Invalidation::InitUnknownVersion(kObjectId_);
unacked_invalidations_.Add(inv1);
unacked_invalidations_.Add(inv2);
unacked_invalidations_.Add(inv3);
SingleObjectInvalidationSet set = GetStoredInvalidations();
ASSERT_EQ(2U, set.GetSize());
EXPECT_TRUE(set.StartsWithUnknownVersion());
}
// Tests that no truncation occurs while we're under the limit.
TEST_F(UnackedInvalidationSetTest, NoTruncation) {
size_t kMax = UnackedInvalidationSet::kMaxBufferedInvalidations;
for (size_t i = 0; i < kMax; ++i) {
Invalidation inv = Invalidation::Init(kObjectId_, i, "payload");
unacked_invalidations_.Add(inv);
}
SingleObjectInvalidationSet set = GetStoredInvalidations();
ASSERT_EQ(kMax, set.GetSize());
EXPECT_FALSE(set.StartsWithUnknownVersion());
EXPECT_EQ(0, set.begin()->version());
EXPECT_EQ(kMax-1, static_cast<size_t>(set.rbegin()->version()));
}
// Test that truncation happens as we reach the limit.
TEST_F(UnackedInvalidationSetTest, Truncation) {
size_t kMax = UnackedInvalidationSet::kMaxBufferedInvalidations;
for (size_t i = 0; i < kMax + 1; ++i) {
Invalidation inv = Invalidation::Init(kObjectId_, i, "payload");
unacked_invalidations_.Add(inv);
}
SingleObjectInvalidationSet set = GetStoredInvalidations();
ASSERT_EQ(kMax, set.GetSize());
EXPECT_TRUE(set.StartsWithUnknownVersion());
EXPECT_TRUE(set.begin()->is_unknown_version());
EXPECT_EQ(kMax, static_cast<size_t>(set.rbegin()->version()));
}
// Test that we don't truncate while a handler is registered.
TEST_F(UnackedInvalidationSetTest, RegistrationAndTruncation) {
unacked_invalidations_.SetHandlerIsRegistered();
size_t kMax = UnackedInvalidationSet::kMaxBufferedInvalidations;
for (size_t i = 0; i < kMax + 1; ++i) {
Invalidation inv = Invalidation::Init(kObjectId_, i, "payload");
unacked_invalidations_.Add(inv);
}
SingleObjectInvalidationSet set = GetStoredInvalidations();
ASSERT_EQ(kMax+1, set.GetSize());
EXPECT_FALSE(set.StartsWithUnknownVersion());
EXPECT_EQ(0, set.begin()->version());
EXPECT_EQ(kMax, static_cast<size_t>(set.rbegin()->version()));
// Unregistering should re-enable truncation.
unacked_invalidations_.SetHandlerIsUnregistered();
SingleObjectInvalidationSet set2 = GetStoredInvalidations();
ASSERT_EQ(kMax, set2.GetSize());
EXPECT_TRUE(set2.StartsWithUnknownVersion());
EXPECT_TRUE(set2.begin()->is_unknown_version());
EXPECT_EQ(kMax, static_cast<size_t>(set2.rbegin()->version()));
}
// Test acknowledgement.
TEST_F(UnackedInvalidationSetTest, Acknowledge) {
// inv2 is included in this test just to make sure invalidations that
// are supposed to be unaffected by this operation will be unaffected.
// We don't expect to be receiving acks or drops unless this flag is set.
// Not that it makes much of a difference in behavior.
unacked_invalidations_.SetHandlerIsRegistered();
Invalidation inv1 = Invalidation::Init(kObjectId_, 10, "payload");
Invalidation inv2 = Invalidation::InitUnknownVersion(kObjectId_);
AckHandle inv1_handle = inv1.ack_handle();
unacked_invalidations_.Add(inv1);
unacked_invalidations_.Add(inv2);
unacked_invalidations_.Acknowledge(inv1_handle);
SingleObjectInvalidationSet set = GetStoredInvalidations();
EXPECT_EQ(1U, set.GetSize());
EXPECT_TRUE(set.StartsWithUnknownVersion());
}
// Test drops.
TEST_F(UnackedInvalidationSetTest, Drop) {
// inv2 is included in this test just to make sure invalidations that
// are supposed to be unaffected by this operation will be unaffected.
// We don't expect to be receiving acks or drops unless this flag is set.
// Not that it makes much of a difference in behavior.
unacked_invalidations_.SetHandlerIsRegistered();
Invalidation inv1 = Invalidation::Init(kObjectId_, 10, "payload");
Invalidation inv2 = Invalidation::Init(kObjectId_, 15, "payload");
AckHandle inv1_handle = inv1.ack_handle();
unacked_invalidations_.Add(inv1);
unacked_invalidations_.Add(inv2);
unacked_invalidations_.Drop(inv1_handle);
SingleObjectInvalidationSet set = GetStoredInvalidations();
ASSERT_EQ(2U, set.GetSize());
EXPECT_TRUE(set.StartsWithUnknownVersion());
EXPECT_EQ(15, set.rbegin()->version());
}
class UnackedInvalidationSetSerializationTest
: public UnackedInvalidationSetTest {
public:
UnackedInvalidationSet SerializeDeserialize() {
scoped_ptr<base::DictionaryValue> value = unacked_invalidations_.ToValue();
UnackedInvalidationSet deserialized(kObjectId_);
deserialized.ResetFromValue(*value.get());
return deserialized;
}
};
TEST_F(UnackedInvalidationSetSerializationTest, Empty) {
UnackedInvalidationSet deserialized = SerializeDeserialize();
EXPECT_THAT(unacked_invalidations_, test_util::Eq(deserialized));
}
TEST_F(UnackedInvalidationSetSerializationTest, OneInvalidation) {
Invalidation inv = Invalidation::Init(kObjectId_, 10, "payload");
unacked_invalidations_.Add(inv);
UnackedInvalidationSet deserialized = SerializeDeserialize();
EXPECT_THAT(unacked_invalidations_, test_util::Eq(deserialized));
}
TEST_F(UnackedInvalidationSetSerializationTest, WithUnknownVersion) {
Invalidation inv1 = Invalidation::Init(kObjectId_, 10, "payload");
Invalidation inv2 = Invalidation::InitUnknownVersion(kObjectId_);
Invalidation inv3 = Invalidation::InitUnknownVersion(kObjectId_);
unacked_invalidations_.Add(inv1);
unacked_invalidations_.Add(inv2);
unacked_invalidations_.Add(inv3);
UnackedInvalidationSet deserialized = SerializeDeserialize();
EXPECT_THAT(unacked_invalidations_, test_util::Eq(deserialized));
}
} // namespace
} // namespace syncer