blob: 70cd232b8a5bf0f3d084deaf0ae2c2692d631648 [file] [log] [blame]
// 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 "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace syncer {
// Start with some helper functions and classes.
using ::testing::MakeMatcher;
using ::testing::MatchResultListener;
using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::PrintToString;
void PrintTo(
const UnackedInvalidationSet& invalidations, ::std::ostream* os);
void PrintTo(
const UnackedInvalidationsMap& map, ::std::ostream* os);
::testing::Matcher<const UnackedInvalidationSet&> Eq(
const UnackedInvalidationSet& expected);
::testing::Matcher<const UnackedInvalidationsMap&> Eq(
const UnackedInvalidationsMap& expected);
class UnackedInvalidationSetEqMatcher
: public testing::MatcherInterface<const UnackedInvalidationSet&> {
public:
explicit UnackedInvalidationSetEqMatcher(
const UnackedInvalidationSet& expected);
virtual bool MatchAndExplain(
const UnackedInvalidationSet& actual,
MatchResultListener* listener) const OVERRIDE;
virtual void DescribeTo(::std::ostream* os) const OVERRIDE;
virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE;
private:
const UnackedInvalidationSet expected_;
DISALLOW_COPY_AND_ASSIGN(UnackedInvalidationSetEqMatcher);
};
UnackedInvalidationSetEqMatcher::UnackedInvalidationSetEqMatcher(
const UnackedInvalidationSet& expected)
: expected_(expected) {}
namespace {
struct InvalidationEq {
bool operator()(const syncer::Invalidation& a,
const syncer::Invalidation& b) const {
return a.Equals(b);
}
};
} // namespace
bool UnackedInvalidationSetEqMatcher::MatchAndExplain(
const UnackedInvalidationSet& actual,
MatchResultListener* listener) const {
// Use our friendship with this class to compare the internals of two
// instances.
//
// Note that the registration status is intentionally not considered
// when performing this comparison.
return expected_.object_id_ == actual.object_id_
&& std::equal(expected_.invalidations_.begin(),
expected_.invalidations_.end(),
actual.invalidations_.begin(),
InvalidationEq());
}
void UnackedInvalidationSetEqMatcher::DescribeTo(::std::ostream* os) const {
*os << " is equal to " << PrintToString(expected_);
}
void UnackedInvalidationSetEqMatcher::DescribeNegationTo(
::std::ostream* os) const {
*os << " isn't equal to " << PrintToString(expected_);
}
namespace {
ObjectIdInvalidationMap UnackedInvalidationsMapToObjectIdInvalidationMap(
const UnackedInvalidationsMap& state_map) {
ObjectIdInvalidationMap object_id_invalidation_map;
for (UnackedInvalidationsMap::const_iterator it = state_map.begin();
it != state_map.end(); ++it) {
it->second.ExportInvalidations(syncer::WeakHandle<AckHandler>(),
&object_id_invalidation_map);
}
return object_id_invalidation_map;
}
class UnackedInvalidationsMapEqMatcher
: public testing::MatcherInterface<const UnackedInvalidationsMap&> {
public:
explicit UnackedInvalidationsMapEqMatcher(
const UnackedInvalidationsMap& expected);
virtual bool MatchAndExplain(const UnackedInvalidationsMap& actual,
MatchResultListener* listener) const;
virtual void DescribeTo(::std::ostream* os) const;
virtual void DescribeNegationTo(::std::ostream* os) const;
private:
const UnackedInvalidationsMap expected_;
DISALLOW_COPY_AND_ASSIGN(UnackedInvalidationsMapEqMatcher);
};
UnackedInvalidationsMapEqMatcher::UnackedInvalidationsMapEqMatcher(
const UnackedInvalidationsMap& expected)
: expected_(expected) {
}
bool UnackedInvalidationsMapEqMatcher::MatchAndExplain(
const UnackedInvalidationsMap& actual,
MatchResultListener* listener) const {
ObjectIdInvalidationMap expected_inv =
UnackedInvalidationsMapToObjectIdInvalidationMap(expected_);
ObjectIdInvalidationMap actual_inv =
UnackedInvalidationsMapToObjectIdInvalidationMap(actual);
return expected_inv == actual_inv;
}
void UnackedInvalidationsMapEqMatcher::DescribeTo(
::std::ostream* os) const {
*os << " is equal to " << PrintToString(expected_);
}
void UnackedInvalidationsMapEqMatcher::DescribeNegationTo(
::std::ostream* os) const {
*os << " isn't equal to " << PrintToString(expected_);
}
} // namespace
void PrintTo(const UnackedInvalidationSet& invalidations,
::std::ostream* os) {
scoped_ptr<base::DictionaryValue> value = invalidations.ToValue();
std::string output;
JSONStringValueSerializer serializer(&output);
serializer.set_pretty_print(true);
serializer.Serialize(*value.get());
(*os) << output;
}
void PrintTo(const UnackedInvalidationsMap& map, ::std::ostream* os) {
scoped_ptr<base::ListValue> list(new base::ListValue);
for (UnackedInvalidationsMap::const_iterator it = map.begin();
it != map.end(); ++it) {
list->Append(it->second.ToValue().release());
}
std::string output;
JSONStringValueSerializer serializer(&output);
serializer.set_pretty_print(true);
serializer.Serialize(*list.get());
(*os) << output;
}
Matcher<const UnackedInvalidationSet&> Eq(
const UnackedInvalidationSet& expected) {
return MakeMatcher(new UnackedInvalidationSetEqMatcher(expected));
}
Matcher<const UnackedInvalidationsMap&> Eq(
const UnackedInvalidationsMap& expected) {
return MakeMatcher(new UnackedInvalidationsMapEqMatcher(expected));
}
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_, Eq(deserialized));
}
TEST_F(UnackedInvalidationSetSerializationTest, OneInvalidation) {
Invalidation inv = Invalidation::Init(kObjectId_, 10, "payload");
unacked_invalidations_.Add(inv);
UnackedInvalidationSet deserialized = SerializeDeserialize();
EXPECT_THAT(unacked_invalidations_, 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_, Eq(deserialized));
}
} // namespace
} // namespace syncer