Skip to content

Commit

Permalink
Change EntityData and EntityMetadata according to the design doc.
Browse files Browse the repository at this point in the history
BUG=553638

Review URL: https://codereview.chromium.org/1430343002

Cr-Commit-Position: refs/heads/master@{#358993}
  • Loading branch information
stanisc authored and Commit bot committed Nov 11, 2015
1 parent e5603b0 commit 87bc966
Show file tree
Hide file tree
Showing 9 changed files with 222 additions and 117 deletions.
1 change: 1 addition & 0 deletions sync/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ test("sync_unit_tests") {
"api/attachments/attachment_id_unittest.cc",
"api/attachments/attachment_metadata_unittest.cc",
"api/attachments/attachment_unittest.cc",
"api/entity_data_unittest.cc",
"api/sync_change_unittest.cc",
"api/sync_data_unittest.cc",
"api/sync_error_unittest.cc",
Expand Down
56 changes: 20 additions & 36 deletions sync/api/entity_data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,38 @@

#include "sync/api/entity_data.h"

#include "sync/protocol/entity_metadata.pb.h"
#include "sync/protocol/sync.pb.h"
#include "sync/util/time.h"
#include "base/logging.h"

namespace syncer_v2 {

EntityData::EntityData(const std::string& client_id,
const std::string& non_unique_name,
const sync_pb::EntitySpecifics* specifics,
bool is_deleted)
: client_id_(client_id),
non_unique_name_(non_unique_name),
is_deleted_(is_deleted) {
if (!is_deleted) {
DCHECK(specifics);
specifics_.set_value(*specifics);
}
}

EntityData::EntityData() {}
EntityData::~EntityData() {}

// Static.
EntityData EntityData::CreateData(const std::string& client_id,
const std::string& non_unique_name,
const sync_pb::EntitySpecifics& specifics) {
return EntityData(client_id, non_unique_name, &specifics, false);
}
void EntityData::Swap(EntityData* other) {
server_id.swap(other->server_id);
client_tag_hash.swap(other->client_tag_hash);
non_unique_name.swap(other->non_unique_name);

specifics.Swap(&other->specifics);

std::swap(creation_time, other->creation_time);
std::swap(modification_time, other->modification_time);

// Static.
EntityData EntityData::CreateDelete(const std::string& sync_tag,
const std::string& non_unique_name) {
return EntityData(sync_tag, non_unique_name, nullptr, true);
parent_id.swap(other->parent_id);
unique_position.Swap(&other->unique_position);
}

const sync_pb::EntitySpecifics& EntityData::specifics() const {
DCHECK(!is_deleted());
return specifics_.value();
void EntityDataTraits::SwapValue(EntityData* dest, EntityData* src) {
dest->Swap(src);
}

bool EntityData::HasMetadata() const {
return metadata_.client_id() == client_id_;
bool EntityDataTraits::HasValue(const EntityData& value) {
return !value.client_tag_hash.empty();
}

void EntityData::SetMetadata(const sync_pb::EntityMetadata& metadata) {
DCHECK(!HasMetadata());
DCHECK_EQ(client_id_, metadata.client_id());
modification_time_ = syncer::ProtoTimeToTime(metadata.modification_time());
metadata_ = metadata;
const EntityData& EntityDataTraits::DefaultValue() {
CR_DEFINE_STATIC_LOCAL(EntityData, default_instance, ());
return default_instance;
}

} // namespace syncer_v2
97 changes: 49 additions & 48 deletions sync/api/entity_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,70 +8,71 @@
#include <string>
#include <vector>

#include "base/macros.h"
#include "base/time/time.h"
#include "sync/base/sync_export.h"
#include "sync/internal_api/public/util/proto_value_ptr.h"
#include "sync/protocol/entity_metadata.pb.h"

namespace sync_pb {
class EntityMetadata;
class EntitySpecifics;
} // namespace sync_pb
#include "sync/protocol/sync.pb.h"

namespace syncer_v2 {

// A light-weight container for sync entity data.
//
// EntityData objects either have specifics data or represent a deleted
// entity that does not have specifics. This component is immutable.
//
// EntityData objects can also be modified by adding EntityMetadata to them,
// which is stored in its serialized form for persistence to storage.
class SYNC_EXPORT EntityData {
// A light-weight container for sync entity data which represents either
// local data created on the ModelTypeService side or remote data created
// on ModelTypeWorker.
// EntityData is supposed to be wrapped and passed by reference.
struct SYNC_EXPORT EntityData {
public:
EntityData();
~EntityData();

// Default copy and assign welcome.
// Typically this is a server assigned sync ID, although for a local change
// that represents a new entity this field might be either empty or contain
// a temporary client sync ID.
std::string server_id;

// A hash based on the client tag and model type.
// Used for various map lookups. Should always be available.
// Sent to the server as SyncEntity::client_defined_unique_tag.
std::string client_tag_hash;

// Entity name, used mostly for Debug purposes.
std::string non_unique_name;

// Model type specific sync data.
sync_pb::EntitySpecifics specifics;

static EntityData CreateData(const std::string& client_id,
const std::string& non_unique_name,
const sync_pb::EntitySpecifics& specifics);
// Entity creation and modification timestamps.
base::Time creation_time;
base::Time modification_time;

static EntityData CreateDelete(const std::string& client_id,
const std::string& non_unique_name);
// Sync ID of the parent entity. This is supposed to be set only for
// hierarchical datatypes (e.g. Bookmarks).
std::string parent_id;

const std::string& client_id() const { return client_id_; }
const std::string& non_unique_name() const { return non_unique_name_; }
bool is_deleted() const { return is_deleted_; }
const sync_pb::EntitySpecifics& specifics() const;
bool HasMetadata() const;
void SetMetadata(const sync_pb::EntityMetadata& metadata);
// Maybe this should be private with SMTP and ModelTypeWorker as friends?
const sync_pb::EntityMetadata& GetMetadata() const { return metadata_; }
// Unique position of an entity among its siblings. This is supposed to be
// set only for datatypes that support positioning (e.g. Bookmarks).
sync_pb::UniquePosition unique_position;

// True if EntityData represents deleted entity; otherwise false.
// Note that EntityData would be considered to represent a deletion if it
// specifics hasn't been set.
bool is_deleted() { return specifics.ByteSize() == 0; }

private:
EntityData(const std::string& client_id,
const std::string& non_unique_name,
const sync_pb::EntitySpecifics* specifics,
bool is_deleted);

// Fields that are always present and don't change.
const std::string client_id_;
const std::string non_unique_name_;
syncer::syncable::EntitySpecificsPtr specifics_;
// TODO(maxbogue): This might be replaced by a ChangeType enum.
const bool is_deleted_;

// Present sometimes.
base::Time modification_time_;
sync_pb::EntityMetadata metadata_;

// Only set for bookmarks; should really be in bookmark specifics.
// std::string parent_tag_hash_;
// sync_pb::UniquePosition unique_position_;
friend struct EntityDataTraits;
// Used to transfer the data without copying.
void Swap(EntityData* other);

DISALLOW_COPY_AND_ASSIGN(EntityData);
};

struct SYNC_EXPORT EntityDataTraits {
static void SwapValue(EntityData* dest, EntityData* src);
static bool HasValue(const EntityData& value);
static const EntityData& DefaultValue();
};

typedef std::vector<EntityData> EntityDataList;
typedef syncer::ProtoValuePtr<EntityData, EntityDataTraits> EntityDataPtr;

} // namespace syncer_v2

Expand Down
66 changes: 66 additions & 0 deletions sync/api/entity_data_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2015 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/api/entity_data.h"

#include "sync/internal_api/public/base/model_type.h"
#include "sync/internal_api/public/base/unique_position.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace syncer_v2 {

class EntityDataTest : public testing::Test {
protected:
EntityDataTest() {}
~EntityDataTest() override {}
};

TEST_F(EntityDataTest, IsDeleted) {
EntityData data;
EXPECT_TRUE(data.is_deleted());

syncer::AddDefaultFieldValue(syncer::BOOKMARKS, &data.specifics);
EXPECT_FALSE(data.is_deleted());
}

TEST_F(EntityDataTest, Swap) {
EntityData data;
syncer::AddDefaultFieldValue(syncer::BOOKMARKS, &data.specifics);
data.server_id = "server_id";
data.client_tag_hash = "client_tag_hash";
data.non_unique_name = "non_unique_name";
data.creation_time = base::Time::FromTimeT(10);
data.modification_time = base::Time::FromTimeT(20);
data.parent_id = "parent_id";

using syncer::UniquePosition;
UniquePosition unique_position =
UniquePosition::InitialPosition(UniquePosition::RandomSuffix());

unique_position.ToProto(&data.unique_position);

// Remember addresses of some data within EntitySpecific and UniquePosition
// to make sure that the underlying data isn't copied.
const sync_pb::BookmarkSpecifics* bookmark_specifics =
&data.specifics.bookmark();
const std::string* unique_position_value = &data.unique_position.value();

EntityDataPtr ptr;
ptr.swap_value(&data);

// Compare addresses of the data wrapped by EntityDataPtr to make sure that
// the underlying objects are exactly the same.
EXPECT_EQ(bookmark_specifics, &ptr->specifics.bookmark());
EXPECT_EQ(unique_position_value, &ptr->unique_position.value());

// Compare other fields.
EXPECT_EQ("server_id", ptr->server_id);
EXPECT_EQ("client_tag_hash", ptr->client_tag_hash);
EXPECT_EQ("non_unique_name", ptr->non_unique_name);
EXPECT_EQ("parent_id", ptr->parent_id);
EXPECT_EQ(base::Time::FromTimeT(10), ptr->creation_time);
EXPECT_EQ(base::Time::FromTimeT(20), ptr->modification_time);
}

} // namespace syncer_v2
35 changes: 24 additions & 11 deletions sync/internal_api/public/util/proto_value_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,25 @@

#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "sync/protocol/attachments.pb.h"
#include "sync/protocol/sync.pb.h"

namespace syncer_v2 {
class EntityData;
}
FORWARD_DECLARE_TEST(EntityDataTest, Swap);
} // namespace syncable

namespace syncer {

namespace syncable {
struct EntryKernel;
} // namespace syncable

// Default traits struct for ProtoValuePtr - adapts a
// ::google::protobuf::MessageLite derived type to be used with ProtoValuePtr.
template <typename T>
struct DefaultProtoValuePtrTraits {
// Deep copy the value from |src| to |dest|.
static void CopyValue(T* dest, const T& src) { dest->CopyFrom(src); }
// Swap the value with the source (to avoid deep copying).
static void SwapValue(T* dest, T* src) { dest->Swap(src); }
// Parse the value from BLOB.
static void ParseFromBlob(T* dest, const void* blob, int length) {
dest->ParseFromArray(blob, length);
Expand Down Expand Up @@ -54,6 +57,8 @@ class ProtoValuePtr {
class Wrapper : public base::RefCountedThreadSafe<Wrapper> {
public:
Wrapper(const T& value) { Traits::CopyValue(&value_, value); }
Wrapper(T* value) { Traits::SwapValue(&value_, value); }

const T& value() const { return value_; }
// Create wrapper by deserializing a BLOB.
static Wrapper* ParseFromBlob(const void* blob, int length) {
Expand Down Expand Up @@ -84,12 +89,14 @@ class ProtoValuePtr {
}

private:
friend class syncer_v2::EntityData;
friend struct EntryKernel;
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, BasicTest);
friend struct syncable::EntryKernel;
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ValueAssignment);
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ValueSwap);
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, SharingTest);
FRIEND_TEST_ALL_PREFIXES(ProtoValuePtrTest, ParsingTest);
FRIEND_TEST_ALL_PREFIXES(syncer_v2::EntityDataTest, Swap);

// set the value to copy of |new_value|.
void set_value(const T& new_value) {
if (Traits::HasValue(new_value)) {
wrapper_ = new Wrapper(new_value);
Expand All @@ -99,17 +106,23 @@ class ProtoValuePtr {
}
}

// Take over |src| value (swap).
void swap_value(T* src) {
if (Traits::HasValue(*src)) {
wrapper_ = new Wrapper(src);
} else {
// Don't store default value.
wrapper_ = nullptr;
}
}

void load(const void* blob, int length) {
wrapper_ = Wrapper::ParseFromBlob(blob, length);
}

scoped_refptr<Wrapper> wrapper_;
};

typedef ProtoValuePtr<sync_pb::EntitySpecifics> EntitySpecificsPtr;
typedef ProtoValuePtr<sync_pb::AttachmentMetadata> AttachmentMetadataPtr;

} // namespace syncable
} // namespace syncer

#endif // SYNC_SYNCABLE_ENTRY_PROTO_FIELD_PTR_H_
Loading

0 comments on commit 87bc966

Please sign in to comment.