summaryrefslogtreecommitdiffstats
path: root/sync
diff options
context:
space:
mode:
authormaniscalco@chromium.org <maniscalco@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-01 05:04:46 +0000
committermaniscalco@chromium.org <maniscalco@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-01 05:04:46 +0000
commit1069f58d60a175fb1ebb4c65fbfec2711367c1ea (patch)
tree0d180a1150a8aafd9dc2ebfc1adf2f792e124cee /sync
parentd35e0d5c14cefc63528e51b624ea985d02406371 (diff)
downloadchromium_src-1069f58d60a175fb1ebb4c65fbfec2711367c1ea.zip
chromium_src-1069f58d60a175fb1ebb4c65fbfec2711367c1ea.tar.gz
chromium_src-1069f58d60a175fb1ebb4c65fbfec2711367c1ea.tar.bz2
Update sync API to support attachments.
The purpose of this change is to start exposing attachment support in the sync API. At this point there is no real functionality added. SyncData and SyncChangeProcessor now know about attachments. GenericChangeProcessor's ctor now requires an AttachmentService. Add AttachmentService and FakeAttachmentService. Update Attachment::Create* methods to return by value. Make Attachment::CreateId public because it is useful in sync_data_unittest.cc. Make some ctors explicit. BUG=348624 Review URL: https://codereview.chromium.org/187303006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260778 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sync')
-rw-r--r--sync/api/attachments/attachment.cc6
-rw-r--r--sync/api/attachments/attachment.h9
-rw-r--r--sync/api/attachments/attachment_id.cc51
-rw-r--r--sync/api/attachments/attachment_id.h33
-rw-r--r--sync/api/attachments/attachment_service.cc12
-rw-r--r--sync/api/attachments/attachment_service.h75
-rw-r--r--sync/api/attachments/attachment_service_proxy.cc103
-rw-r--r--sync/api/attachments/attachment_service_proxy.h70
-rw-r--r--sync/api/attachments/attachment_service_proxy_unittest.cc229
-rw-r--r--sync/api/attachments/attachment_store.h2
-rw-r--r--sync/api/attachments/attachment_unittest.cc21
-rw-r--r--sync/api/attachments/fake_attachment_service.cc72
-rw-r--r--sync/api/attachments/fake_attachment_service.h45
-rw-r--r--sync/api/attachments/fake_attachment_store.cc25
-rw-r--r--sync/api/attachments/fake_attachment_store.h2
-rw-r--r--sync/api/attachments/fake_attachment_store_unittest.cc4
-rw-r--r--sync/api/sync_change_processor.h17
-rw-r--r--sync/api/sync_data.cc152
-rw-r--r--sync/api/sync_data.h82
-rw-r--r--sync/api/sync_data_unittest.cc78
-rw-r--r--sync/internal_api/base_node.cc7
-rw-r--r--sync/internal_api/public/DEPS1
-rw-r--r--sync/internal_api/public/base_node.h4
-rw-r--r--sync/protocol/attachments.proto22
-rw-r--r--sync/protocol/proto_value_conversions.cc7
-rw-r--r--sync/protocol/proto_value_conversions.h4
-rw-r--r--sync/protocol/proto_value_conversions_unittest.cc4
-rw-r--r--sync/protocol/sync.proto5
-rw-r--r--sync/sync_api.gypi7
-rw-r--r--sync/sync_proto.gypi1
-rw-r--r--sync/sync_tests.gypi1
31 files changed, 1056 insertions, 95 deletions
diff --git a/sync/api/attachments/attachment.cc b/sync/api/attachments/attachment.cc
index 94601ab..b5cfe5d 100644
--- a/sync/api/attachments/attachment.cc
+++ b/sync/api/attachments/attachment.cc
@@ -11,16 +11,16 @@ namespace syncer {
Attachment::~Attachment() {}
// Static.
-scoped_ptr<Attachment> Attachment::Create(
+Attachment Attachment::Create(
const scoped_refptr<base::RefCountedMemory>& data) {
return CreateWithId(AttachmentId::Create(), data);
}
// Static.
-scoped_ptr<Attachment> Attachment::CreateWithId(
+Attachment Attachment::CreateWithId(
const AttachmentId& id,
const scoped_refptr<base::RefCountedMemory>& data) {
- return scoped_ptr<Attachment>(new Attachment(id, data)).Pass();
+ return Attachment(id, data);
}
const AttachmentId& Attachment::GetId() const { return id_; }
diff --git a/sync/api/attachments/attachment.h b/sync/api/attachments/attachment.h
index da00798..af4c36d 100644
--- a/sync/api/attachments/attachment.h
+++ b/sync/api/attachments/attachment.h
@@ -9,7 +9,6 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_ptr.h"
@@ -33,14 +32,13 @@ class SYNC_EXPORT Attachment {
// Creates an attachment with a unique id and the supplied data.
//
// Used when creating a brand new attachment.
- static scoped_ptr<Attachment> Create(
- const scoped_refptr<base::RefCountedMemory>& data);
+ static Attachment Create(const scoped_refptr<base::RefCountedMemory>& data);
// Creates an attachment with the supplied id and data.
//
// Used when you want to recreate a specific attachment. E.g. creating a local
// copy of an attachment that already exists on the sync server.
- static scoped_ptr<Attachment> CreateWithId(
+ static Attachment CreateWithId(
const AttachmentId& id,
const scoped_refptr<base::RefCountedMemory>& data);
@@ -54,9 +52,6 @@ class SYNC_EXPORT Attachment {
AttachmentId id_;
scoped_refptr<base::RefCountedMemory> data_;
- friend class AttachmentTest;
- FRIEND_TEST_ALL_PREFIXES(AttachmentTest, CreateId_UniqueIdIsUnique);
-
Attachment(const AttachmentId& id,
const scoped_refptr<base::RefCountedMemory>& data);
};
diff --git a/sync/api/attachments/attachment_id.cc b/sync/api/attachments/attachment_id.cc
index 181f380..6f3e0a2 100644
--- a/sync/api/attachments/attachment_id.cc
+++ b/sync/api/attachments/attachment_id.cc
@@ -6,13 +6,41 @@
#include "base/logging.h"
#include "base/rand_util.h"
+#include "sync/protocol/sync.pb.h"
namespace syncer {
+void AttachmentId::ImmutableAttachmentIdProtoTraits::InitializeWrapper(
+ Wrapper* wrapper) {
+ *wrapper = new sync_pb::AttachmentIdProto();
+}
+
+void AttachmentId::ImmutableAttachmentIdProtoTraits::DestroyWrapper(
+ Wrapper* wrapper) {
+ delete *wrapper;
+}
+
+const sync_pb::AttachmentIdProto&
+AttachmentId::ImmutableAttachmentIdProtoTraits::Unwrap(const Wrapper& wrapper) {
+ return *wrapper;
+}
+
+sync_pb::AttachmentIdProto*
+AttachmentId::ImmutableAttachmentIdProtoTraits::UnwrapMutable(
+ Wrapper* wrapper) {
+ return *wrapper;
+}
+
+void AttachmentId::ImmutableAttachmentIdProtoTraits::Swap(
+ sync_pb::AttachmentIdProto* t1,
+ sync_pb::AttachmentIdProto* t2) {
+ t1->Swap(t2);
+}
+
AttachmentId::~AttachmentId() {}
bool AttachmentId::operator==(const AttachmentId& other) const {
- return unique_id_ == other.unique_id_;
+ return proto_.Get().unique_id() == other.proto_.Get().unique_id();
}
bool AttachmentId::operator!=(const AttachmentId& other) const {
@@ -20,19 +48,30 @@ bool AttachmentId::operator!=(const AttachmentId& other) const {
}
bool AttachmentId::operator<(const AttachmentId& other) const {
- return unique_id_ < other.unique_id_;
+ return proto_.Get().unique_id() < other.proto_.Get().unique_id();
}
// Static.
AttachmentId AttachmentId::Create() {
// Only requirement here is that this id must be globally unique.
// TODO(maniscalco): Consider making this base64 encoded.
- return AttachmentId(base::RandBytesAsString(16));
+ sync_pb::AttachmentIdProto proto;
+ proto.set_unique_id(base::RandBytesAsString(16));
+ return AttachmentId(&proto);
}
-AttachmentId::AttachmentId(const std::string& unique_id)
- : unique_id_(unique_id) {
- DCHECK(!unique_id_.empty());
+// Static.
+AttachmentId AttachmentId::CreateFromProto(
+ const sync_pb::AttachmentIdProto& proto) {
+ sync_pb::AttachmentIdProto copy_of_proto(proto);
+ return AttachmentId(&copy_of_proto);
}
+const sync_pb::AttachmentIdProto& AttachmentId::GetProto() const {
+ return proto_.Get();
+}
+
+AttachmentId::AttachmentId(sync_pb::AttachmentIdProto* proto)
+ : proto_(proto) {}
+
} // namespace syncer
diff --git a/sync/api/attachments/attachment_id.h b/sync/api/attachments/attachment_id.h
index 9fd29dc..57a0088 100644
--- a/sync/api/attachments/attachment_id.h
+++ b/sync/api/attachments/attachment_id.h
@@ -9,6 +9,11 @@
#include <vector>
#include "sync/base/sync_export.h"
+#include "sync/internal_api/public/util/immutable.h"
+
+namespace sync_pb {
+class AttachmentIdProto;
+} // namespace sync_pb
namespace syncer {
@@ -29,14 +34,34 @@ class SYNC_EXPORT AttachmentId {
// Needed for using AttachmentId as key in std::map.
bool operator<(const AttachmentId& other) const;
-
// Creates a unique attachment id.
static AttachmentId Create();
- private:
- std::string unique_id_;
+ // Creates an attachment id from an initialized proto.
+ static AttachmentId CreateFromProto(const sync_pb::AttachmentIdProto& proto);
- AttachmentId(const std::string& unique_id);
+ const sync_pb::AttachmentIdProto& GetProto() const;
+
+ private:
+ // Necessary since we forward-declare sync_pb::AttachmentIdProto; see comments
+ // in immutable.h.
+ struct ImmutableAttachmentIdProtoTraits {
+ typedef sync_pb::AttachmentIdProto* Wrapper;
+ static void InitializeWrapper(Wrapper* wrapper);
+ static void DestroyWrapper(Wrapper* wrapper);
+ static const sync_pb::AttachmentIdProto& Unwrap(const Wrapper& wrapper);
+ static sync_pb::AttachmentIdProto* UnwrapMutable(Wrapper* wrapper);
+ static void Swap(sync_pb::AttachmentIdProto* t1,
+ sync_pb::AttachmentIdProto* t2);
+ };
+
+ typedef Immutable<sync_pb::AttachmentIdProto,
+ ImmutableAttachmentIdProtoTraits>
+ ImmutableAttachmentIdProto;
+
+ ImmutableAttachmentIdProto proto_;
+
+ AttachmentId(sync_pb::AttachmentIdProto* proto);
};
typedef std::vector<AttachmentId> AttachmentIdList;
diff --git a/sync/api/attachments/attachment_service.cc b/sync/api/attachments/attachment_service.cc
new file mode 100644
index 0000000..44727d5
--- /dev/null
+++ b/sync/api/attachments/attachment_service.cc
@@ -0,0 +1,12 @@
+// Copyright 2014 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/attachments/attachment_service.h"
+
+namespace syncer {
+
+AttachmentService::AttachmentService() {}
+AttachmentService::~AttachmentService() {}
+
+} // namespace syncer
diff --git a/sync/api/attachments/attachment_service.h b/sync/api/attachments/attachment_service.h
new file mode 100644
index 0000000..d148b0c
--- /dev/null
+++ b/sync/api/attachments/attachment_service.h
@@ -0,0 +1,75 @@
+// Copyright 2014 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.
+
+#ifndef SYNC_API_ATTACHMENTS_ATTACHMENT_SERVICE_H_
+#define SYNC_API_ATTACHMENTS_ATTACHMENT_SERVICE_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "sync/api/attachments/attachment.h"
+#include "sync/base/sync_export.h"
+
+namespace syncer {
+
+class SyncData;
+
+// AttachmentService is responsible for managing a model type's attachments.
+//
+// Outside of sync code, AttachmentService shouldn't be used directly. Instead
+// use the functionality provided by SyncData and SyncChangeProcessor.
+class SYNC_EXPORT AttachmentService {
+ public:
+ // The result of a GetOrDownloadAttachments operation.
+ enum GetOrDownloadResult {
+ GET_SUCCESS, // No error.
+ GET_NOT_FOUND, // Attachment was not found or does not exist.
+ GET_UNSPECIFIED_ERROR, // An unspecified error occurred.
+ };
+
+ typedef base::Callback<
+ void(const GetOrDownloadResult&, const AttachmentMap& attachments)>
+ GetOrDownloadCallback;
+
+ // The result of a DropAttachments operation.
+ enum DropResult {
+ DROP_SUCCESS, // No error.
+ DROP_UNSPECIFIED_ERROR, // An unspecified error occurred.
+ };
+
+ typedef base::Callback<void(const DropResult&)> DropCallback;
+
+ AttachmentService();
+ virtual ~AttachmentService();
+
+ // See SyncData::GetOrDownloadAttachments.
+ virtual void GetOrDownloadAttachments(
+ const AttachmentIdList& attachment_ids,
+ const GetOrDownloadCallback& callback) = 0;
+
+ // See SyncData::DropAttachments.
+ virtual void DropAttachments(const AttachmentIdList& attachment_ids,
+ const DropCallback& callback) = 0;
+
+ // This method should be called when a SyncData is about to be added to the
+ // sync database so we have a chance to persist the Attachment locally and
+ // schedule it for upload to the sync server.
+ virtual void OnSyncDataAdd(const SyncData& sync_data) = 0;
+
+ // This method should be called when a SyncData is about to be deleted from
+ // the sync database so we can remove any unreferenced attachments from local
+ // storage.
+ virtual void OnSyncDataDelete(const SyncData& sync_data) = 0;
+
+ // This method should be called when a SyncData is about to be updated so we
+ // can remove unreferenced attachments from local storage and ensure new
+ // attachments are persisted and uploaded to the sync server.
+ virtual void OnSyncDataUpdate(const AttachmentIdList& old_attachment_ids,
+ const SyncData& updated_sync_data) = 0;
+};
+
+} // namespace syncer
+
+#endif // SYNC_API_ATTACHMENTS_ATTACHMENT_SERVICE_H_
diff --git a/sync/api/attachments/attachment_service_proxy.cc b/sync/api/attachments/attachment_service_proxy.cc
new file mode 100644
index 0000000..065f467
--- /dev/null
+++ b/sync/api/attachments/attachment_service_proxy.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 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/attachments/attachment_service_proxy.h"
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "sync/api/sync_data.h"
+
+namespace syncer {
+
+namespace {
+
+// These ProxyFooCallback functions are used to invoke a callback in a specific
+// thread.
+
+// Invokes |callback| with |result| and |attachments| in the |task_runner|
+// thread.
+void ProxyGetOrDownloadCallback(
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ const AttachmentService::GetOrDownloadCallback& callback,
+ const AttachmentService::GetOrDownloadResult& result,
+ const AttachmentMap& attachments) {
+ task_runner->PostTask(FROM_HERE, base::Bind(callback, result, attachments));
+}
+
+// Invokes |callback| with |result| and |attachments| in the |task_runner|
+// thread.
+void ProxyDropCallback(
+ const scoped_refptr<base::SequencedTaskRunner>& task_runner,
+ const AttachmentService::DropCallback& callback,
+ const AttachmentService::DropResult& result) {
+ task_runner->PostTask(FROM_HERE, base::Bind(callback, result));
+}
+
+} // namespace
+
+AttachmentServiceProxy::AttachmentServiceProxy() {}
+
+AttachmentServiceProxy::AttachmentServiceProxy(
+ const scoped_refptr<base::SequencedTaskRunner>& wrapped_task_runner,
+ base::WeakPtr<syncer::AttachmentService> wrapped)
+ : wrapped_task_runner_(wrapped_task_runner), wrapped_(wrapped) {
+ DCHECK(wrapped_task_runner_);
+}
+
+AttachmentServiceProxy::~AttachmentServiceProxy() {}
+
+void AttachmentServiceProxy::GetOrDownloadAttachments(
+ const AttachmentIdList& attachment_ids,
+ const GetOrDownloadCallback& callback) {
+ DCHECK(wrapped_task_runner_);
+ GetOrDownloadCallback proxy_callback = base::Bind(
+ &ProxyGetOrDownloadCallback, base::MessageLoopProxy::current(), callback);
+ wrapped_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AttachmentService::GetOrDownloadAttachments,
+ wrapped_,
+ attachment_ids,
+ proxy_callback));
+}
+
+void AttachmentServiceProxy::DropAttachments(
+ const AttachmentIdList& attachment_ids,
+ const DropCallback& callback) {
+ DCHECK(wrapped_task_runner_);
+ DropCallback proxy_callback = base::Bind(
+ &ProxyDropCallback, base::MessageLoopProxy::current(), callback);
+ wrapped_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&AttachmentService::DropAttachments,
+ wrapped_,
+ attachment_ids,
+ proxy_callback));
+}
+
+void AttachmentServiceProxy::OnSyncDataAdd(const SyncData& sync_data) {
+ DCHECK(wrapped_task_runner_);
+ wrapped_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AttachmentService::OnSyncDataAdd, wrapped_, sync_data));
+}
+
+void AttachmentServiceProxy::OnSyncDataDelete(const SyncData& sync_data) {
+ DCHECK(wrapped_task_runner_);
+ wrapped_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AttachmentService::OnSyncDataDelete, wrapped_, sync_data));
+}
+
+void AttachmentServiceProxy::OnSyncDataUpdate(
+ const AttachmentIdList& old_attachment_ids,
+ const SyncData& updated_sync_data) {
+ DCHECK(wrapped_task_runner_);
+ wrapped_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&AttachmentService::OnSyncDataUpdate,
+ wrapped_,
+ old_attachment_ids,
+ updated_sync_data));
+}
+
+} // namespace syncer
diff --git a/sync/api/attachments/attachment_service_proxy.h b/sync/api/attachments/attachment_service_proxy.h
new file mode 100644
index 0000000..5bec1b3
--- /dev/null
+++ b/sync/api/attachments/attachment_service_proxy.h
@@ -0,0 +1,70 @@
+// Copyright 2014 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.
+
+#ifndef SYNC_API_ATTACHMENTS_ATTACHMENT_SERVICE_PROXY_H_
+#define SYNC_API_ATTACHMENTS_ATTACHMENT_SERVICE_PROXY_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task_runner.h"
+#include "sync/api/attachments/attachment.h"
+#include "sync/api/attachments/attachment_service.h"
+#include "sync/base/sync_export.h"
+
+namespace syncer {
+
+class SyncData;
+
+// AttachmentServiceProxy wraps an AttachmentService allowing multiple threads
+// to share the wrapped AttachmentService.
+//
+// Callbacks passed to methods on this class will be invoked in the same thread
+// from which the method was called.
+//
+// This class does not own its wrapped AttachmentService object. This class
+// holds a WeakPtr to the wrapped object. Once the the wrapped object is
+// destroyed, method calls on this object will be no-ops.
+//
+// Users of this class should take care to destroy the wrapped object on the
+// correct thread (wrapped_task_runner).
+//
+// This class is thread-safe.
+class SYNC_EXPORT AttachmentServiceProxy : public AttachmentService {
+ public:
+ // Default copy and assignment are welcome.
+
+ // Construct an invalid AttachmentServiceProxy.
+ AttachmentServiceProxy();
+
+ // Construct an AttachmentServiceProxy that forwards calls to |wrapped| on the
+ // |wrapped_task_runner| thread.
+ AttachmentServiceProxy(
+ const scoped_refptr<base::SequencedTaskRunner>& wrapped_task_runner,
+ base::WeakPtr<syncer::AttachmentService> wrapped);
+
+ virtual ~AttachmentServiceProxy();
+
+ // AttachmentService implementation.
+ virtual void GetOrDownloadAttachments(const AttachmentIdList& attachment_ids,
+ const GetOrDownloadCallback& callback)
+ OVERRIDE;
+ virtual void DropAttachments(const AttachmentIdList& attachment_ids,
+ const DropCallback& callback) OVERRIDE;
+ virtual void OnSyncDataAdd(const SyncData& sync_data) OVERRIDE;
+ virtual void OnSyncDataDelete(const SyncData& sync_data) OVERRIDE;
+ virtual void OnSyncDataUpdate(const AttachmentIdList& old_attachment_ids,
+ const SyncData& updated_sync_data) OVERRIDE;
+
+ private:
+ scoped_refptr<base::SequencedTaskRunner> wrapped_task_runner_;
+ // wrapped_ must only be dereferenced on the wrapped_task_runner_ thread.
+ base::WeakPtr<AttachmentService> wrapped_;
+};
+
+} // namespace syncer
+
+#endif // SYNC_API_ATTACHMENTS_ATTACHMENT_SERVICE_PROXY_H_
diff --git a/sync/api/attachments/attachment_service_proxy_unittest.cc b/sync/api/attachments/attachment_service_proxy_unittest.cc
new file mode 100644
index 0000000..eed5e6b
--- /dev/null
+++ b/sync/api/attachments/attachment_service_proxy_unittest.cc
@@ -0,0 +1,229 @@
+// Copyright 2014 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/attachments/attachment_service_proxy.h"
+
+#include "base/bind.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/threading/thread.h"
+#include "sync/api/attachments/attachment.h"
+#include "sync/api/attachments/attachment_service.h"
+#include "sync/api/sync_data.h"
+#include "sync/internal_api/public/base/model_type.h"
+#include "sync/protocol/sync.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncer {
+
+// A stub implementation of AttachmentService that counts the number of times
+// its methods are invoked.
+class StubAttachmentService : public AttachmentService,
+ public base::NonThreadSafe {
+ public:
+ StubAttachmentService() : call_count_(0), weak_ptr_factory_(this) {
+ // DetachFromThread because we will be constructed in one thread and
+ // used/destroyed in another.
+ DetachFromThread();
+ }
+
+ virtual ~StubAttachmentService() {}
+
+ virtual void GetOrDownloadAttachments(const AttachmentIdList& attachment_ids,
+ const GetOrDownloadCallback& callback)
+ OVERRIDE {
+ CalledOnValidThread();
+ Increment();
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ callback, AttachmentService::GET_NOT_FOUND, AttachmentMap()));
+ }
+
+ virtual void DropAttachments(const AttachmentIdList& attachment_ids,
+ const DropCallback& callback) OVERRIDE {
+ CalledOnValidThread();
+ Increment();
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(callback, AttachmentService::DROP_SUCCESS));
+ }
+
+ virtual void OnSyncDataAdd(const SyncData& sync_data) OVERRIDE {
+ CalledOnValidThread();
+ Increment();
+ }
+
+ virtual void OnSyncDataDelete(const SyncData& sync_data) OVERRIDE {
+ CalledOnValidThread();
+ Increment();
+ }
+
+ virtual void OnSyncDataUpdate(const AttachmentIdList& old_attachment_ids,
+ const SyncData& updated_sync_data) OVERRIDE {
+ CalledOnValidThread();
+ Increment();
+ }
+
+ virtual base::WeakPtr<AttachmentService> AsWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ // Return the number of method invocations.
+ int GetCallCount() const {
+ base::AutoLock lock(mutex_);
+ return call_count_;
+ }
+
+ private:
+ // Protects call_count_.
+ mutable base::Lock mutex_;
+ int call_count_;
+
+ // Must be last data member.
+ base::WeakPtrFactory<AttachmentService> weak_ptr_factory_;
+
+ void Increment() {
+ base::AutoLock lock(mutex_);
+ ++call_count_;
+ }
+};
+
+class AttachmentServiceProxyTest : public testing::Test,
+ public base::NonThreadSafe {
+ protected:
+ AttachmentServiceProxyTest() {}
+
+ virtual void SetUp() {
+ CalledOnValidThread();
+ stub_thread.reset(new base::Thread("attachment service stub thread"));
+ stub_thread->Start();
+ stub.reset(new StubAttachmentService);
+ proxy.reset(new AttachmentServiceProxy(stub_thread->message_loop_proxy(),
+ stub->AsWeakPtr()));
+
+ sync_data =
+ SyncData::CreateLocalData("tag", "title", sync_pb::EntitySpecifics());
+ sync_data_delete =
+ SyncData::CreateLocalDelete("tag", syncer::PREFERENCES);
+
+ callback_get_or_download =
+ base::Bind(&AttachmentServiceProxyTest::IncrementGetOrDownload,
+ base::Unretained(this));
+ callback_drop = base::Bind(&AttachmentServiceProxyTest::IncrementDrop,
+ base::Unretained(this));
+ count_callback_get_or_download = 0;
+ count_callback_drop = 0;
+ }
+
+ virtual void TearDown()
+ OVERRIDE {
+ // We must take care to call the stub's destructor on the stub_thread
+ // because that's the thread to which its WeakPtrs are bound.
+ if (stub) {
+ stub_thread->message_loop()->DeleteSoon(FROM_HERE, stub.release());
+ WaitForStubThread();
+ }
+ stub_thread->Stop();
+ }
+
+ // a GetOrDownloadCallback
+ void IncrementGetOrDownload(const AttachmentService::GetOrDownloadResult&,
+ const AttachmentMap&) {
+ CalledOnValidThread();
+ ++count_callback_get_or_download;
+ }
+
+ // a DropCallback
+ void IncrementDrop(const AttachmentService::DropResult&) {
+ CalledOnValidThread();
+ ++count_callback_drop;
+ }
+
+ void WaitForStubThread() {
+ base::WaitableEvent done(false, false);
+ stub_thread->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done)));
+ done.Wait();
+ }
+
+ base::MessageLoop loop;
+ scoped_ptr<base::Thread> stub_thread;
+ scoped_ptr<StubAttachmentService> stub;
+ scoped_ptr<AttachmentServiceProxy> proxy;
+
+ SyncData sync_data;
+ SyncData sync_data_delete;
+
+ AttachmentService::GetOrDownloadCallback callback_get_or_download;
+ AttachmentService::DropCallback callback_drop;
+
+ // number of times callback_get_or_download was invoked
+ int count_callback_get_or_download;
+ // number of times callback_drop was invoked
+ int count_callback_drop;
+};
+
+// Verify that each of AttachmentServiceProxy's regular methods (those that
+// don't take callbacks) are invoked on the stub.
+TEST_F(AttachmentServiceProxyTest, MethodsAreProxied) {
+ proxy->OnSyncDataAdd(sync_data);
+ proxy->OnSyncDataDelete(sync_data_delete);
+ proxy->OnSyncDataUpdate(AttachmentIdList(), sync_data);
+ WaitForStubThread();
+ EXPECT_EQ(3, stub->GetCallCount());
+}
+
+// Verify that each of AttachmentServiceProxy's callback methods (those that
+// take callbacks) are invoked on the stub and that the passed callbacks are
+// invoked in this thread.
+TEST_F(AttachmentServiceProxyTest, MethodsWithCallbacksAreProxied) {
+ proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download);
+ proxy->DropAttachments(AttachmentIdList(), callback_drop);
+ // Wait for the posted calls to execute in the stub thread.
+ WaitForStubThread();
+ EXPECT_EQ(2, stub->GetCallCount());
+ // At this point the stub thread has finished executed the calls. However, the
+ // result callbacks it has posted may not have executed yet. Wait a second
+ // time to ensure the stub thread has executed the posted result callbacks.
+ WaitForStubThread();
+
+ loop.RunUntilIdle();
+ EXPECT_EQ(1, count_callback_get_or_download);
+ EXPECT_EQ(1, count_callback_drop);
+}
+
+// Verify that it's safe to use an AttachmentServiceProxy even after its wrapped
+// AttachmentService has been destroyed.
+TEST_F(AttachmentServiceProxyTest, WrappedIsDestroyed) {
+ proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download);
+ // Wait for the posted calls to execute in the stub thread.
+ WaitForStubThread();
+ EXPECT_EQ(1, stub->GetCallCount());
+ // Wait a second time ensure the stub thread has executed the posted result
+ // callbacks.
+ WaitForStubThread();
+
+ loop.RunUntilIdle();
+ EXPECT_EQ(1, count_callback_get_or_download);
+
+ // Destroy the stub and call GetOrDownloadAttachments again.
+ stub_thread->message_loop()->DeleteSoon(FROM_HERE, stub.release());
+ WaitForStubThread();
+
+ // Now that the wrapped object has been destroyed, call again and see that we
+ // don't crash and the count remains the same.
+ proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download);
+ WaitForStubThread();
+ WaitForStubThread();
+ loop.RunUntilIdle();
+ EXPECT_EQ(1, count_callback_get_or_download);
+}
+
+} // namespace syncer
diff --git a/sync/api/attachments/attachment_store.h b/sync/api/attachments/attachment_store.h
index 01cfd87..1d623ce 100644
--- a/sync/api/attachments/attachment_store.h
+++ b/sync/api/attachments/attachment_store.h
@@ -26,7 +26,7 @@ class SYNC_EXPORT AttachmentStore {
virtual ~AttachmentStore();
// TODO(maniscalco): Consider udpating Read and Write methods to support
- // resumable transfers.
+ // resumable transfers (bug 353292).
enum Result {
SUCCESS, // No error.
diff --git a/sync/api/attachments/attachment_unittest.cc b/sync/api/attachments/attachment_unittest.cc
index b61d623..c9e5550 100644
--- a/sync/api/attachments/attachment_unittest.cc
+++ b/sync/api/attachments/attachment_unittest.cc
@@ -8,7 +8,6 @@
#include "base/memory/ref_counted.h"
#include "base/memory/ref_counted_memory.h"
-#include "base/memory/scoped_ptr.h"
#include "sync/protocol/sync.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,25 +24,25 @@ class AttachmentTest : public testing::Test {};
TEST_F(AttachmentTest, Create_UniqueIdIsUnique) {
scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
some_data->data() = kAttachmentData;
- scoped_ptr<Attachment> a1 = Attachment::Create(some_data);
- scoped_ptr<Attachment> a2 = Attachment::Create(some_data);
- EXPECT_NE(a1->GetId(), a2->GetId());
- EXPECT_EQ(a1->GetData(), a2->GetData());
+ Attachment a1 = Attachment::Create(some_data);
+ Attachment a2 = Attachment::Create(some_data);
+ EXPECT_NE(a1.GetId(), a2.GetId());
+ EXPECT_EQ(a1.GetData(), a2.GetData());
}
TEST_F(AttachmentTest, Create_WithEmptyData) {
- scoped_refptr<base::RefCountedString> emptyData(new base::RefCountedString);
- scoped_ptr<Attachment> a = Attachment::Create(emptyData);
- EXPECT_EQ(emptyData, a->GetData());
+ scoped_refptr<base::RefCountedString> empty_data(new base::RefCountedString);
+ Attachment a = Attachment::Create(empty_data);
+ EXPECT_EQ(empty_data, a.GetData());
}
TEST_F(AttachmentTest, CreateWithId_HappyCase) {
AttachmentId id = AttachmentId::Create();
scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
some_data->data() = kAttachmentData;
- scoped_ptr<Attachment> a = Attachment::CreateWithId(id, some_data);
- EXPECT_EQ(id, a->GetId());
- EXPECT_EQ(some_data, a->GetData());
+ Attachment a = Attachment::CreateWithId(id, some_data);
+ EXPECT_EQ(id, a.GetId());
+ EXPECT_EQ(some_data, a.GetData());
}
} // namespace syncer
diff --git a/sync/api/attachments/fake_attachment_service.cc b/sync/api/attachments/fake_attachment_service.cc
new file mode 100644
index 0000000..86dfaeb
--- /dev/null
+++ b/sync/api/attachments/fake_attachment_service.cc
@@ -0,0 +1,72 @@
+// Copyright 2014 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/attachments/fake_attachment_service.h"
+
+#include "base/test/test_simple_task_runner.h"
+#include "sync/api/attachments/fake_attachment_store.h"
+
+namespace syncer {
+
+FakeAttachmentService::FakeAttachmentService(
+ scoped_ptr<AttachmentStore> attachment_store)
+ : attachment_store_(attachment_store.Pass()) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(attachment_store_);
+}
+
+FakeAttachmentService::~FakeAttachmentService() {
+ DCHECK(CalledOnValidThread());
+}
+
+// Static.
+scoped_ptr<syncer::AttachmentService> FakeAttachmentService::CreateForTest() {
+ scoped_ptr<syncer::AttachmentStore> attachment_store(
+ new syncer::FakeAttachmentStore(scoped_refptr<base::SequencedTaskRunner>(
+ new base::TestSimpleTaskRunner)));
+ scoped_ptr<syncer::AttachmentService> attachment_service(
+ new syncer::FakeAttachmentService(attachment_store.Pass()));
+ return attachment_service.Pass();
+}
+
+void FakeAttachmentService::GetOrDownloadAttachments(
+ const AttachmentIdList& attachment_ids,
+ const GetOrDownloadCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ // TODO(maniscalco): Fire off a bunch of AttachmentStore operations. Invoke
+ // callback after all have completed (bug 356351).
+}
+
+void FakeAttachmentService::DropAttachments(
+ const AttachmentIdList& attachment_ids,
+ const DropCallback& callback) {
+ DCHECK(CalledOnValidThread());
+ // TODO(maniscalco): Fire off a bunch of AttachmentStore operations. Invoke
+ // callback after all have completed (bug 356351).
+}
+
+void FakeAttachmentService::OnSyncDataAdd(const SyncData& sync_data) {
+ DCHECK(CalledOnValidThread());
+ // TODO(maniscalco): Ensure the linked attachments get persisted in local
+ // storage and schedule them for upload to the server (bug 356351).
+}
+
+void FakeAttachmentService::OnSyncDataDelete(const SyncData& sync_data) {
+ DCHECK(CalledOnValidThread());
+ // TODO(maniscalco): One or more of sync_data's attachments may no longer be
+ // referenced anywhere. We should probably delete them at this point (bug
+ // 356351).
+}
+
+void FakeAttachmentService::OnSyncDataUpdate(
+ const AttachmentIdList& old_attachment_ids,
+ const SyncData& updated_sync_data) {
+ DCHECK(CalledOnValidThread());
+ // TODO(maniscalco): At this point we need to ensure we write all new
+ // attachments referenced by updated_sync_data to local storage and schedule
+ // them up upload to the server. We also need to remove any no unreferenced
+ // attachments from local storage (bug 356351).
+}
+
+} // namespace syncer
diff --git a/sync/api/attachments/fake_attachment_service.h b/sync/api/attachments/fake_attachment_service.h
new file mode 100644
index 0000000..3bbd5d6d
--- /dev/null
+++ b/sync/api/attachments/fake_attachment_service.h
@@ -0,0 +1,45 @@
+// Copyright 2014 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.
+
+#ifndef SYNC_API_ATTACHMENTS_FAKE_ATTACHMENT_SERVICE_H_
+#define SYNC_API_ATTACHMENTS_FAKE_ATTACHMENT_SERVICE_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/threading/non_thread_safe.h"
+#include "sync/api/attachments/attachment_service.h"
+#include "sync/api/attachments/attachment_service_proxy.h"
+#include "sync/api/attachments/attachment_store.h"
+
+namespace syncer {
+
+// A fake implementation of AttachmentService.
+class SYNC_EXPORT FakeAttachmentService : public AttachmentService,
+ public base::NonThreadSafe {
+ public:
+ explicit FakeAttachmentService(scoped_ptr<AttachmentStore> attachment_store);
+ virtual ~FakeAttachmentService();
+
+ // Create a FakeAttachmentService suitable for use in tests.
+ static scoped_ptr<syncer::AttachmentService> CreateForTest();
+
+ // AttachmentService implementation.
+ virtual void GetOrDownloadAttachments(const AttachmentIdList& attachment_ids,
+ const GetOrDownloadCallback& callback)
+ OVERRIDE;
+ virtual void DropAttachments(const AttachmentIdList& attachment_ids,
+ const DropCallback& callback) OVERRIDE;
+ virtual void OnSyncDataAdd(const SyncData& sync_data) OVERRIDE;
+ virtual void OnSyncDataDelete(const SyncData& sync_data) OVERRIDE;
+ virtual void OnSyncDataUpdate(const AttachmentIdList& old_attachment_ids,
+ const SyncData& updated_sync_data) OVERRIDE;
+
+ private:
+ const scoped_ptr<AttachmentStore> attachment_store_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeAttachmentService);
+};
+
+} // namespace syncer
+
+#endif // SYNC_API_ATTACHMENTS_FAKE_ATTACHMENT_SERVICE_H_
diff --git a/sync/api/attachments/fake_attachment_store.cc b/sync/api/attachments/fake_attachment_store.cc
index c3e5e38..53e42a2 100644
--- a/sync/api/attachments/fake_attachment_store.cc
+++ b/sync/api/attachments/fake_attachment_store.cc
@@ -29,20 +29,16 @@ class FakeAttachmentStore::Backend
private:
friend class base::RefCountedThreadSafe<Backend>;
- typedef std::map<AttachmentId, Attachment*> AttachmentMap;
~Backend();
- Result Remove(const AttachmentId& id);
scoped_refptr<base::SingleThreadTaskRunner> frontend_task_runner_;
AttachmentMap attachments_;
- STLValueDeleter<AttachmentMap> attachments_value_deleter_;
};
FakeAttachmentStore::Backend::Backend(
const scoped_refptr<base::SingleThreadTaskRunner>& frontend_task_runner)
- : frontend_task_runner_(frontend_task_runner),
- attachments_value_deleter_(&attachments_) {}
+ : frontend_task_runner_(frontend_task_runner) {}
FakeAttachmentStore::Backend::~Backend() {}
@@ -52,7 +48,7 @@ void FakeAttachmentStore::Backend::Read(const AttachmentId& id,
scoped_ptr<Attachment> attachment;
Result result = NOT_FOUND;
if (iter != attachments_.end()) {
- attachment.reset(new Attachment(*iter->second));
+ attachment.reset(new Attachment(iter->second));
result = SUCCESS;
}
frontend_task_runner_->PostTask(
@@ -62,31 +58,22 @@ void FakeAttachmentStore::Backend::Read(const AttachmentId& id,
void FakeAttachmentStore::Backend::Write(
const scoped_refptr<base::RefCountedMemory>& bytes,
const WriteCallback& callback) {
- scoped_ptr<Attachment> attachment = Attachment::Create(bytes);
- DCHECK(attachment.get());
- AttachmentId attachment_id(attachment->GetId());
- attachments_.insert(
- AttachmentMap::value_type(attachment_id, attachment.release()));
+ Attachment attachment = Attachment::Create(bytes);
+ AttachmentId attachment_id(attachment.GetId());
+ attachments_.insert(AttachmentMap::value_type(attachment_id, attachment));
frontend_task_runner_->PostTask(FROM_HERE,
base::Bind(callback, SUCCESS, attachment_id));
}
void FakeAttachmentStore::Backend::Drop(const AttachmentId& id,
const DropCallback& callback) {
- Result result = Remove(id);
- frontend_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
-}
-
-AttachmentStore::Result FakeAttachmentStore::Backend::Remove(
- const AttachmentId& id) {
Result result = NOT_FOUND;
AttachmentMap::iterator iter = attachments_.find(id);
if (iter != attachments_.end()) {
- delete iter->second;
attachments_.erase(iter);
result = SUCCESS;
}
- return result;
+ frontend_task_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
}
FakeAttachmentStore::FakeAttachmentStore(
diff --git a/sync/api/attachments/fake_attachment_store.h b/sync/api/attachments/fake_attachment_store.h
index c673b09..41fd927 100644
--- a/sync/api/attachments/fake_attachment_store.h
+++ b/sync/api/attachments/fake_attachment_store.h
@@ -33,7 +33,7 @@ class SYNC_EXPORT FakeAttachmentStore : public AttachmentStore {
public:
// Construct a FakeAttachmentStore whose "IO" will be performed in
// |backend_task_runner|.
- FakeAttachmentStore(
+ explicit FakeAttachmentStore(
const scoped_refptr<base::SequencedTaskRunner>& backend_task_runner);
virtual ~FakeAttachmentStore();
diff --git a/sync/api/attachments/fake_attachment_store_unittest.cc b/sync/api/attachments/fake_attachment_store_unittest.cc
index 797c3a6..e560697 100644
--- a/sync/api/attachments/fake_attachment_store_unittest.cc
+++ b/sync/api/attachments/fake_attachment_store_unittest.cc
@@ -98,8 +98,8 @@ TEST_F(FakeAttachmentStoreTest, WriteReadRoundTrip) {
TEST_F(FakeAttachmentStoreTest, Read_NotFound) {
FakeAttachmentStore store(base::MessageLoopProxy::current());
scoped_refptr<base::RefCountedString> some_data(new base::RefCountedString);
- scoped_ptr<Attachment> some_attachment = Attachment::Create(some_data);
- AttachmentId some_id = some_attachment->GetId();
+ Attachment some_attachment = Attachment::Create(some_data);
+ AttachmentId some_id = some_attachment.GetId();
store.Read(some_id, read_callback);
ClearAndPumpLoop();
EXPECT_EQ(result, AttachmentStore::NOT_FOUND);
diff --git a/sync/api/sync_change_processor.h b/sync/api/sync_change_processor.h
index 69494f0..3cdf6a8 100644
--- a/sync/api/sync_change_processor.h
+++ b/sync/api/sync_change_processor.h
@@ -25,6 +25,8 @@ typedef std::vector<SyncChange> SyncChangeList;
// An interface for services that handle receiving SyncChanges.
class SYNC_EXPORT SyncChangeProcessor {
public:
+ typedef base::Callback<void(const SyncData&)> GetSyncDataCallback;
+
SyncChangeProcessor();
virtual ~SyncChangeProcessor();
@@ -46,6 +48,21 @@ class SYNC_EXPORT SyncChangeProcessor {
// WARNING: This can be a potentially slow & memory intensive operation and
// should only be used when absolutely necessary / sparingly.
virtual SyncDataList GetAllSyncData(ModelType type) const = 0;
+
+ // Retrieves the SyncData identified by |type| and |sync_tag| and invokes
+ // |callback| asynchronously. If no such SyncData exists locally, IsValid on
+ // the SyncData passed to |callback| will return false.
+ //
+ // This is an asynchronous local operation that may result in disk IO.
+ //
+ // Refer to sync_data.h for a description of |sync_tag|.
+ //
+ // TODO:(maniscalco): N.B. this method should really be pure virtual. An
+ // implentation is provided here just to verify that everything compiles.
+ // Update this method to be pure virtual (bug 353300).
+ virtual void GetSyncData(const ModelType& type,
+ const std::string& sync_tag,
+ const GetSyncDataCallback& callback) const {}
};
} // namespace syncer
diff --git a/sync/api/sync_data.cc b/sync/api/sync_data.cc
index d52e04e..0324fc1 100644
--- a/sync/api/sync_data.cc
+++ b/sync/api/sync_data.cc
@@ -10,20 +10,41 @@
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
+#include "sync/api/attachments/attachment_service_proxy.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/internal_api/public/base_node.h"
#include "sync/protocol/proto_value_conversions.h"
#include "sync/protocol/sync.pb.h"
+using syncer::Attachment;
+using syncer::AttachmentIdList;
+using syncer::AttachmentList;
+
+namespace {
+
+sync_pb::AttachmentIdProto AttachmentToProto(
+ const syncer::Attachment& attachment) {
+ return attachment.GetId().GetProto();
+}
+
+sync_pb::AttachmentIdProto IdToProto(
+ const syncer::AttachmentId& attachment_id) {
+ return attachment_id.GetProto();
+}
+
+syncer::AttachmentId ProtoToId(const sync_pb::AttachmentIdProto& proto) {
+ return syncer::AttachmentId::CreateFromProto(proto);
+}
+
+} // namespace
+
namespace syncer {
-void SyncData::ImmutableSyncEntityTraits::InitializeWrapper(
- Wrapper* wrapper) {
+void SyncData::ImmutableSyncEntityTraits::InitializeWrapper(Wrapper* wrapper) {
*wrapper = new sync_pb::SyncEntity();
}
-void SyncData::ImmutableSyncEntityTraits::DestroyWrapper(
- Wrapper* wrapper) {
+void SyncData::ImmutableSyncEntityTraits::DestroyWrapper(Wrapper* wrapper) {
delete *wrapper;
}
@@ -42,55 +63,100 @@ void SyncData::ImmutableSyncEntityTraits::Swap(sync_pb::SyncEntity* t1,
t1->Swap(t2);
}
-SyncData::SyncData()
- : is_valid_(false),
- id_(kInvalidId) {}
+SyncData::SyncData() : is_valid_(false), id_(kInvalidId) {}
-SyncData::SyncData(int64 id,
- sync_pb::SyncEntity* entity,
- const base::Time& remote_modification_time)
+SyncData::SyncData(
+ int64 id,
+ sync_pb::SyncEntity* entity,
+ AttachmentList* attachments,
+ const base::Time& remote_modification_time,
+ const syncer::AttachmentServiceProxy& attachment_service)
: is_valid_(true),
id_(id),
remote_modification_time_(remote_modification_time),
- immutable_entity_(entity) {}
+ immutable_entity_(entity),
+ attachments_(attachments),
+ attachment_service_(attachment_service) {}
SyncData::~SyncData() {}
// Static.
-SyncData SyncData::CreateLocalDelete(
- const std::string& sync_tag,
- ModelType datatype) {
+SyncData SyncData::CreateLocalDelete(const std::string& sync_tag,
+ ModelType datatype) {
sync_pb::EntitySpecifics specifics;
AddDefaultFieldValue(datatype, &specifics);
return CreateLocalData(sync_tag, std::string(), specifics);
}
// Static.
-SyncData SyncData::CreateLocalData(
+SyncData SyncData::CreateLocalData(const std::string& sync_tag,
+ const std::string& non_unique_title,
+ const sync_pb::EntitySpecifics& specifics) {
+ syncer::AttachmentList attachments;
+ return CreateLocalDataWithAttachments(
+ sync_tag, non_unique_title, specifics, attachments);
+}
+
+// TODO(maniscalco): What should happen if the same Attachment appears in the
+// list twice? Document the behavior and add a test case.
+//
+// Static.
+SyncData SyncData::CreateLocalDataWithAttachments(
const std::string& sync_tag,
const std::string& non_unique_title,
- const sync_pb::EntitySpecifics& specifics) {
+ const sync_pb::EntitySpecifics& specifics,
+ const AttachmentList& attachments) {
sync_pb::SyncEntity entity;
entity.set_client_defined_unique_tag(sync_tag);
entity.set_non_unique_name(non_unique_title);
entity.mutable_specifics()->CopyFrom(specifics);
- return SyncData(kInvalidId, &entity, base::Time());
+ std::transform(attachments.begin(),
+ attachments.end(),
+ RepeatedFieldBackInserter(entity.mutable_attachment_id()),
+ AttachmentToProto);
+ // TODO(maniscalco): Actually pass the attachments to the ctor and make them
+ // available to the AttachmentService once this SyncData gets passed into
+ // GenericChangeProcesso::ProcessSyncChanges (bug 354530).
+ AttachmentList copy_of_attachments(attachments);
+ return SyncData(kInvalidId,
+ &entity,
+ &copy_of_attachments,
+ base::Time(),
+ AttachmentServiceProxy());
}
// Static.
SyncData SyncData::CreateRemoteData(
- int64 id, const sync_pb::EntitySpecifics& specifics,
- const base::Time& modification_time) {
+ int64 id,
+ const sync_pb::EntitySpecifics& specifics,
+ const base::Time& modification_time,
+ const AttachmentIdList& attachment_ids,
+ const AttachmentServiceProxy& attachment_service) {
DCHECK_NE(id, kInvalidId);
sync_pb::SyncEntity entity;
entity.mutable_specifics()->CopyFrom(specifics);
- return SyncData(id, &entity, modification_time);
+ std::transform(attachment_ids.begin(),
+ attachment_ids.end(),
+ RepeatedFieldBackInserter(entity.mutable_attachment_id()),
+ IdToProto);
+ AttachmentList attachments;
+ return SyncData(
+ id, &entity, &attachments, modification_time, attachment_service);
}
-bool SyncData::IsValid() const {
- return is_valid_;
+// Static.
+SyncData SyncData::CreateRemoteData(int64 id,
+ const sync_pb::EntitySpecifics& specifics,
+ const base::Time& modification_time) {
+ return CreateRemoteData(id,
+ specifics,
+ modification_time,
+ AttachmentIdList(),
+ AttachmentServiceProxy());
}
+bool SyncData::IsValid() const { return is_valid_; }
+
const sync_pb::EntitySpecifics& SyncData::GetSpecifics() const {
return immutable_entity_.Get().specifics();
}
@@ -120,9 +186,7 @@ int64 SyncData::GetRemoteId() const {
return id_;
}
-bool SyncData::IsLocal() const {
- return id_ == kInvalidId;
-}
+bool SyncData::IsLocal() const { return id_ == kInvalidId; }
std::string SyncData::ToString() const {
if (!IsValid())
@@ -132,22 +196,50 @@ std::string SyncData::ToString() const {
std::string specifics;
scoped_ptr<base::DictionaryValue> value(
EntitySpecificsToValue(GetSpecifics()));
- base::JSONWriter::WriteWithOptions(value.get(),
- base::JSONWriter::OPTIONS_PRETTY_PRINT,
- &specifics);
+ base::JSONWriter::WriteWithOptions(
+ value.get(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &specifics);
if (IsLocal()) {
return "{ isLocal: true, type: " + type + ", tag: " + GetTag() +
- ", title: " + GetTitle() + ", specifics: " + specifics + "}";
+ ", title: " + GetTitle() + ", specifics: " + specifics + "}";
}
std::string id = base::Int64ToString(GetRemoteId());
return "{ isLocal: false, type: " + type + ", specifics: " + specifics +
- ", id: " + id + "}";
+ ", id: " + id + "}";
}
void PrintTo(const SyncData& sync_data, std::ostream* os) {
*os << sync_data.ToString();
}
+AttachmentIdList SyncData::GetAttachmentIds() const {
+ AttachmentIdList result;
+ const sync_pb::SyncEntity& entity = immutable_entity_.Get();
+ std::transform(entity.attachment_id().begin(),
+ entity.attachment_id().end(),
+ std::back_inserter(result),
+ ProtoToId);
+ return result;
+}
+
+const AttachmentList& SyncData::GetLocalAttachmentsForUpload() const {
+ DCHECK(IsLocal());
+ return attachments_.Get();
+}
+
+void SyncData::GetOrDownloadAttachments(
+ const AttachmentIdList& attachment_ids,
+ const AttachmentService::GetOrDownloadCallback& callback) {
+ DCHECK(!IsLocal());
+ attachment_service_.GetOrDownloadAttachments(attachment_ids, callback);
+}
+
+void SyncData::DropAttachments(
+ const AttachmentIdList& attachment_ids,
+ const AttachmentService::DropCallback& callback) {
+ DCHECK(!IsLocal());
+ attachment_service_.DropAttachments(attachment_ids, callback);
+}
+
} // namespace syncer
diff --git a/sync/api/sync_data.h b/sync/api/sync_data.h
index 43f64f9..d801149 100644
--- a/sync/api/sync_data.h
+++ b/sync/api/sync_data.h
@@ -10,10 +10,15 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/stl_util.h"
#include "base/time/time.h"
+#include "sync/api/attachments/attachment.h"
+#include "sync/api/attachments/attachment_service_proxy.h"
#include "sync/base/sync_export.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/internal_api/public/util/immutable.h"
+#include "sync/internal_api/public/util/weak_handle.h"
namespace sync_pb {
class EntitySpecifics;
@@ -22,15 +27,17 @@ class SyncEntity;
namespace syncer {
+class AttachmentService;
+
// A light-weight container for immutable sync data. Pass-by-value and storage
// in STL containers are supported and encouraged if helpful.
class SYNC_EXPORT SyncData {
public:
// Creates an empty and invalid SyncData.
SyncData();
- ~SyncData();
+ ~SyncData();
- // Default copy and assign welcome.
+ // Default copy and assign welcome.
// Helper methods for creating SyncData objects for local data.
// The sync tag must be a string unique to this datatype and is used as a node
@@ -48,12 +55,25 @@ class SYNC_EXPORT SyncData {
const std::string& sync_tag,
const std::string& non_unique_title,
const sync_pb::EntitySpecifics& specifics);
+ static SyncData CreateLocalDataWithAttachments(
+ const std::string& sync_tag,
+ const std::string& non_unique_title,
+ const sync_pb::EntitySpecifics& specifics,
+ const AttachmentList& attachments);
// Helper method for creating SyncData objects originating from the syncer.
+ //
+ // TODO(maniscalco): Replace all calls to 3-arg CreateRemoteData with calls to
+ // the 5-arg version (bug 353296).
static SyncData CreateRemoteData(
int64 id,
const sync_pb::EntitySpecifics& specifics,
- const base::Time& last_modified_time);
+ const base::Time& last_modified_time,
+ const AttachmentIdList& attachment_ids,
+ const syncer::AttachmentServiceProxy& attachment_service);
+ static SyncData CreateRemoteData(int64 id,
+ const sync_pb::EntitySpecifics& specifics,
+ const base::Time& last_modified_time);
// Whether this SyncData holds valid data. The only way to have a SyncData
// without valid data is to use the default constructor.
@@ -85,8 +105,49 @@ class SYNC_EXPORT SyncData {
// Whether this sync data is for local data or data coming from the syncer.
bool IsLocal() const;
+ // TODO(maniscalco): Reduce the dependence on knowing whether a SyncData is
+ // local (in the IsLocal() == true sense) or remote. Make it harder for users
+ // of SyncData to accidentally call local-only methods on a remote SyncData
+ // (bug 357305).
+
std::string ToString() const;
+ // Return a list of this SyncData's attachment ids.
+ //
+ // The attachments may or may not be present on this device.
+ AttachmentIdList GetAttachmentIds() const;
+
+ // Return a list of this SyncData's attachments.
+ //
+ // May only be called when IsLocal() is true.
+ const AttachmentList& GetLocalAttachmentsForUpload() const;
+
+ // Retrieve the attachments indentified by |attachment_ids|. Invoke |callback|
+ // with the requested attachments.
+ //
+ // May only be called when IsLocal() is false.
+ //
+ // |callback| will be invoked when the operation is complete (successfully or
+ // otherwise).
+ //
+ // Retrieving the requested attachments may require reading local storage or
+ // requesting the attachments from the network.
+ //
+ void GetOrDownloadAttachments(
+ const AttachmentIdList& attachment_ids,
+ const AttachmentService::GetOrDownloadCallback& callback);
+
+ // Drop (delete from local storage) the attachments associated with this
+ // SyncData specified in |attachment_ids|. This method will not delete
+ // attachments from the server.
+ //
+ // May only be called when IsLocal() is false.
+ //
+ // |callback| will be invoked when the operation is complete (successfully or
+ // otherwise).
+ void DropAttachments(const AttachmentIdList& attachment_ids,
+ const AttachmentService::DropCallback& callback);
+
// TODO(zea): Query methods for other sync properties: parent, successor, etc.
private:
@@ -109,10 +170,13 @@ class SYNC_EXPORT SyncData {
typedef Immutable<sync_pb::SyncEntity, ImmutableSyncEntityTraits>
ImmutableSyncEntity;
- // Clears |entity|.
- SyncData(int64 id,
- sync_pb::SyncEntity* entity,
- const base::Time& remote_modification_time);
+ // Clears |entity| and |attachments|.
+ SyncData(
+ int64 id,
+ sync_pb::SyncEntity* entity,
+ AttachmentList* attachments,
+ const base::Time& remote_modification_time,
+ const syncer::AttachmentServiceProxy& attachment_service);
// Whether this SyncData holds valid data.
bool is_valid_;
@@ -126,6 +190,10 @@ class SYNC_EXPORT SyncData {
// The actual shared sync entity being held.
ImmutableSyncEntity immutable_entity_;
+
+ Immutable<AttachmentList> attachments_;
+
+ AttachmentServiceProxy attachment_service_;
};
// gmock printer helper.
diff --git a/sync/api/sync_data_unittest.cc b/sync/api/sync_data_unittest.cc
index 19d88d4..bf964c8 100644
--- a/sync/api/sync_data_unittest.cc
+++ b/sync/api/sync_data_unittest.cc
@@ -6,7 +6,14 @@
#include <string>
+#include "base/memory/ref_counted_memory.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
#include "base/time/time.h"
+#include "sync/api/attachments/attachment_id.h"
+#include "sync/api/attachments/attachment_service.h"
+#include "sync/api/attachments/attachment_service_proxy.h"
+#include "sync/api/attachments/fake_attachment_service.h"
#include "sync/protocol/sync.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -22,7 +29,20 @@ const string kNonUniqueTitle = "my preference";
const int64 kId = 439829;
const base::Time kLastModifiedTime = base::Time();
-typedef testing::Test SyncDataTest;
+class SyncDataTest : public testing::Test {
+ protected:
+ SyncDataTest()
+ : attachment_service(FakeAttachmentService::CreateForTest()),
+ attachment_service_weak_ptr_factory(attachment_service.get()),
+ attachment_service_proxy(
+ base::MessageLoopProxy::current(),
+ attachment_service_weak_ptr_factory.GetWeakPtr()) {}
+ base::MessageLoop loop;
+ sync_pb::EntitySpecifics specifics;
+ scoped_ptr<AttachmentService> attachment_service;
+ base::WeakPtrFactory<AttachmentService> attachment_service_weak_ptr_factory;
+ AttachmentServiceProxy attachment_service_proxy;
+};
TEST_F(SyncDataTest, NoArgCtor) {
SyncData data;
@@ -38,7 +58,6 @@ TEST_F(SyncDataTest, CreateLocalDelete) {
}
TEST_F(SyncDataTest, CreateLocalData) {
- sync_pb::EntitySpecifics specifics;
specifics.mutable_preference();
SyncData data =
SyncData::CreateLocalData(kSyncTag, kNonUniqueTitle, specifics);
@@ -50,8 +69,58 @@ TEST_F(SyncDataTest, CreateLocalData) {
EXPECT_TRUE(data.GetSpecifics().has_preference());
}
+TEST_F(SyncDataTest, CreateLocalDataWithAttachments) {
+ specifics.mutable_preference();
+ scoped_refptr<base::RefCountedMemory> bytes(new base::RefCountedString);
+ AttachmentList attachments;
+ attachments.push_back(Attachment::Create(bytes));
+ attachments.push_back(Attachment::Create(bytes));
+ attachments.push_back(Attachment::Create(bytes));
+
+ SyncData data = SyncData::CreateLocalDataWithAttachments(
+ kSyncTag, kNonUniqueTitle, specifics, attachments);
+ EXPECT_TRUE(data.IsValid());
+ EXPECT_TRUE(data.IsLocal());
+ EXPECT_EQ(kSyncTag, data.GetTag());
+ EXPECT_EQ(kDatatype, data.GetDataType());
+ EXPECT_EQ(kNonUniqueTitle, data.GetTitle());
+ EXPECT_TRUE(data.GetSpecifics().has_preference());
+ AttachmentIdList attachment_ids = data.GetAttachmentIds();
+ EXPECT_EQ(3U, attachment_ids.size());
+ EXPECT_EQ(3U, data.GetLocalAttachmentsForUpload().size());
+}
+
+TEST_F(SyncDataTest, CreateLocalDataWithAttachments_EmptyListOfAttachments) {
+ specifics.mutable_preference();
+ AttachmentList attachments;
+ SyncData data = SyncData::CreateLocalDataWithAttachments(
+ kSyncTag, kNonUniqueTitle, specifics, attachments);
+ EXPECT_TRUE(data.IsValid());
+ EXPECT_TRUE(data.IsLocal());
+ EXPECT_EQ(kSyncTag, data.GetTag());
+ EXPECT_EQ(kDatatype, data.GetDataType());
+ EXPECT_EQ(kNonUniqueTitle, data.GetTitle());
+ EXPECT_TRUE(data.GetSpecifics().has_preference());
+ EXPECT_TRUE(data.GetAttachmentIds().empty());
+ EXPECT_TRUE(data.GetLocalAttachmentsForUpload().empty());
+}
+
TEST_F(SyncDataTest, CreateRemoteData) {
- sync_pb::EntitySpecifics specifics;
+ specifics.mutable_preference();
+ SyncData data = SyncData::CreateRemoteData(kId,
+ specifics,
+ kLastModifiedTime,
+ AttachmentIdList(),
+ attachment_service_proxy);
+ EXPECT_TRUE(data.IsValid());
+ EXPECT_FALSE(data.IsLocal());
+ EXPECT_EQ(kId, data.GetRemoteId());
+ EXPECT_EQ(kLastModifiedTime, data.GetRemoteModifiedTime());
+ EXPECT_TRUE(data.GetSpecifics().has_preference());
+ EXPECT_TRUE(data.GetAttachmentIds().empty());
+}
+
+TEST_F(SyncDataTest, CreateRemoteData_WithoutAttachmentService) {
specifics.mutable_preference();
SyncData data = SyncData::CreateRemoteData(kId, specifics, kLastModifiedTime);
EXPECT_TRUE(data.IsValid());
@@ -61,6 +130,9 @@ TEST_F(SyncDataTest, CreateRemoteData) {
EXPECT_TRUE(data.GetSpecifics().has_preference());
}
+// TODO(maniscalco): Add test cases that verify GetLocalAttachmentsForUpload and
+// DropAttachments calls are passed through to the underlying AttachmentService.
+
} // namespace
} // namespace syncer
diff --git a/sync/internal_api/base_node.cc b/sync/internal_api/base_node.cc
index ac04768..0788395 100644
--- a/sync/internal_api/base_node.cc
+++ b/sync/internal_api/base_node.cc
@@ -290,6 +290,13 @@ ModelType BaseNode::GetModelType() const {
return GetEntry()->GetModelType();
}
+const syncer::AttachmentIdList BaseNode::GetAttachmentIds() const {
+ // TODO(maniscalco): Once EntryKernel is capable of storing AttachmentIds,
+ // update this method to retrieve the list of AttachmentIds from read_node and
+ // pass it to CreateRemoteData (bug 348625).
+ return AttachmentIdList();
+}
+
void BaseNode::SetUnencryptedSpecifics(
const sync_pb::EntitySpecifics& specifics) {
ModelType type = GetModelTypeFromSpecifics(specifics);
diff --git a/sync/internal_api/public/DEPS b/sync/internal_api/public/DEPS
index 877a88d..b9d4591 100644
--- a/sync/internal_api/public/DEPS
+++ b/sync/internal_api/public/DEPS
@@ -1,5 +1,6 @@
include_rules = [
"-sync",
+ "+sync/api",
"+sync/base",
"+sync/internal_api/public",
"+sync/notifier",
diff --git a/sync/internal_api/public/base_node.h b/sync/internal_api/public/base_node.h
index 425a81f..93d2baf 100644
--- a/sync/internal_api/public/base_node.h
+++ b/sync/internal_api/public/base_node.h
@@ -12,6 +12,7 @@
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
+#include "sync/api/attachments/attachment.h"
#include "sync/base/sync_export.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/protocol/sync.pb.h"
@@ -200,6 +201,9 @@ class SYNC_EXPORT BaseNode {
// (ie. non-bookmarks).
int GetPositionIndex() const;
+ // Returns this item's attachment ids.
+ const syncer::AttachmentIdList GetAttachmentIds() const;
+
// These virtual accessors provide access to data members of derived classes.
virtual const syncable::Entry* GetEntry() const = 0;
virtual const BaseTransaction* GetTransaction() const = 0;
diff --git a/sync/protocol/attachments.proto b/sync/protocol/attachments.proto
new file mode 100644
index 0000000..8ba8710
--- /dev/null
+++ b/sync/protocol/attachments.proto
@@ -0,0 +1,22 @@
+// Copyright 2014 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.
+//
+// Sync protocol for attachments.
+
+// Update proto_{value,enum}_conversions{.h,.cc,_unittest.cc} if you change any
+// fields in this file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option retain_unknown_fields = true;
+
+package sync_pb;
+
+// Identifies an attachment.
+message AttachmentIdProto {
+ // Uniquely identifies the attachment. Two attachments with the same unique_id
+ // are considered equivalent.
+ optional string unique_id = 1;
+}
diff --git a/sync/protocol/proto_value_conversions.cc b/sync/protocol/proto_value_conversions.cc
index 496a35d..4e89928 100644
--- a/sync/protocol/proto_value_conversions.cc
+++ b/sync/protocol/proto_value_conversions.cc
@@ -1053,6 +1053,13 @@ base::DictionaryValue* ClientConfigParamsToValue(
return value;
}
+base::DictionaryValue* AttachmentIdProtoToValue(
+ const sync_pb::AttachmentIdProto& proto) {
+ base::DictionaryValue* value = new base::DictionaryValue();
+ SET_STR(unique_id);
+ return value;
+}
+
#undef SET
#undef SET_REP
diff --git a/sync/protocol/proto_value_conversions.h b/sync/protocol/proto_value_conversions.h
index b09e50a..bf936ee 100644
--- a/sync/protocol/proto_value_conversions.h
+++ b/sync/protocol/proto_value_conversions.h
@@ -20,6 +20,7 @@ class AppNotificationSettings;
class AppSettingSpecifics;
class AppSpecifics;
class ArticleSpecifics;
+class AttachmentIdProto;
class AutofillProfileSpecifics;
class AutofillSpecifics;
class BookmarkSpecifics;
@@ -296,6 +297,9 @@ base::DictionaryValue* SyncCycleCompletedEventInfoToValue(
base::DictionaryValue* ClientConfigParamsToValue(
const sync_pb::ClientConfigParams& proto);
+SYNC_EXPORT_PRIVATE base::DictionaryValue* AttachmentIdProtoToValue(
+ const sync_pb::AttachmentIdProto& proto);
+
} // namespace syncer
#endif // SYNC_PROTOCOL_PROTO_VALUE_CONVERSIONS_H_
diff --git a/sync/protocol/proto_value_conversions_unittest.cc b/sync/protocol/proto_value_conversions_unittest.cc
index 90d5423..b2c84e6 100644
--- a/sync/protocol/proto_value_conversions_unittest.cc
+++ b/sync/protocol/proto_value_conversions_unittest.cc
@@ -368,5 +368,9 @@ TEST_F(ProtoValueConversionsTest, ClientToServerResponseToValue) {
"get_updates.entries"));
}
+TEST_F(ProtoValueConversionsTest, AttachmentIdProtoToValue) {
+ TestSpecificsToValue(AttachmentIdProtoToValue);
+}
+
} // namespace
} // namespace syncer
diff --git a/sync/protocol/sync.proto b/sync/protocol/sync.proto
index b9f0de5..7b0c673 100644
--- a/sync/protocol/sync.proto
+++ b/sync/protocol/sync.proto
@@ -19,6 +19,7 @@ import "app_notification_specifics.proto";
import "app_setting_specifics.proto";
import "app_specifics.proto";
import "article_specifics.proto";
+import "attachments.proto";
import "autofill_specifics.proto";
import "bookmark_specifics.proto";
import "client_commands.proto";
@@ -366,6 +367,9 @@ message SyncEntity {
// Refer to its definition in unique_position.proto for more information about
// its internal representation.
optional UniquePosition unique_position = 25;
+
+ // Attachment ids of attachments associated with this SyncEntity.
+ repeated AttachmentIdProto attachment_id = 26;
};
// This message contains diagnostic information used to correlate
@@ -930,4 +934,3 @@ message ClientToServerResponse {
// sent with any subsequent ClientToServerMessages.
optional ChipBag new_bag_of_chips = 14;
};
-
diff --git a/sync/sync_api.gypi b/sync/sync_api.gypi
index 023d69b..02ff2c4 100644
--- a/sync/sync_api.gypi
+++ b/sync/sync_api.gypi
@@ -11,14 +11,21 @@
],
'dependencies': [
'../base/base.gyp:base',
+ '../base/base.gyp:test_support_base',
],
'sources': [
'api/attachments/attachment.cc',
'api/attachments/attachment.h',
'api/attachments/attachment_id.cc',
'api/attachments/attachment_id.h',
+ 'api/attachments/attachment_service.cc',
+ 'api/attachments/attachment_service.h',
+ 'api/attachments/attachment_service_proxy.cc',
+ 'api/attachments/attachment_service_proxy.h',
'api/attachments/attachment_store.cc',
'api/attachments/attachment_store.h',
+ 'api/attachments/fake_attachment_service.cc',
+ 'api/attachments/fake_attachment_service.h',
'api/attachments/fake_attachment_store.cc',
'api/attachments/fake_attachment_store.h',
'api/string_ordinal.h',
diff --git a/sync/sync_proto.gypi b/sync/sync_proto.gypi
index 536828d..3385317 100644
--- a/sync/sync_proto.gypi
+++ b/sync/sync_proto.gypi
@@ -15,6 +15,7 @@
'protocol/app_specifics.proto',
'protocol/app_list_specifics.proto',
'protocol/article_specifics.proto',
+ 'protocol/attachments.proto',
'protocol/autofill_specifics.proto',
'protocol/bookmark_specifics.proto',
'protocol/client_commands.proto',
diff --git a/sync/sync_tests.gypi b/sync/sync_tests.gypi
index a2366a9..5fc0087 100644
--- a/sync/sync_tests.gypi
+++ b/sync/sync_tests.gypi
@@ -465,6 +465,7 @@
'sources': [
'api/attachments/attachment_unittest.cc',
'api/attachments/attachment_id_unittest.cc',
+ 'api/attachments/attachment_service_proxy_unittest.cc',
'api/attachments/fake_attachment_store_unittest.cc',
'api/sync_change_unittest.cc',
'api/sync_data_unittest.cc',