1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "sync/api/attachments/attachment_service_proxy.h" 6 7 #include "base/bind.h" 8 #include "base/memory/ref_counted_memory.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/message_loop/message_loop.h" 11 #include "base/run_loop.h" 12 #include "base/synchronization/lock.h" 13 #include "base/synchronization/waitable_event.h" 14 #include "base/threading/non_thread_safe.h" 15 #include "base/threading/thread.h" 16 #include "sync/api/attachments/attachment.h" 17 #include "sync/api/attachments/attachment_service.h" 18 #include "sync/api/sync_data.h" 19 #include "sync/internal_api/public/base/model_type.h" 20 #include "sync/protocol/sync.pb.h" 21 #include "testing/gtest/include/gtest/gtest.h" 22 23 namespace syncer { 24 25 // A stub implementation of AttachmentService that counts the number of times 26 // its methods are invoked. 27 class StubAttachmentService : public AttachmentService, 28 public base::NonThreadSafe { 29 public: 30 StubAttachmentService() : call_count_(0), weak_ptr_factory_(this) { 31 // DetachFromThread because we will be constructed in one thread and 32 // used/destroyed in another. 33 DetachFromThread(); 34 } 35 36 virtual ~StubAttachmentService() {} 37 38 virtual void GetOrDownloadAttachments(const AttachmentIdList& attachment_ids, 39 const GetOrDownloadCallback& callback) 40 OVERRIDE { 41 CalledOnValidThread(); 42 Increment(); 43 scoped_ptr<AttachmentMap> attachments(new AttachmentMap()); 44 base::MessageLoop::current()->PostTask( 45 FROM_HERE, 46 base::Bind(callback, 47 AttachmentService::GET_UNSPECIFIED_ERROR, 48 base::Passed(&attachments))); 49 } 50 51 virtual void DropAttachments(const AttachmentIdList& attachment_ids, 52 const DropCallback& callback) OVERRIDE { 53 CalledOnValidThread(); 54 Increment(); 55 base::MessageLoop::current()->PostTask( 56 FROM_HERE, base::Bind(callback, AttachmentService::DROP_SUCCESS)); 57 } 58 59 virtual void StoreAttachments(const AttachmentList& attachments, 60 const StoreCallback& callback) OVERRIDE { 61 CalledOnValidThread(); 62 Increment(); 63 base::MessageLoop::current()->PostTask( 64 FROM_HERE, base::Bind(callback, AttachmentService::STORE_SUCCESS)); 65 } 66 67 virtual void OnSyncDataDelete(const SyncData& sync_data) OVERRIDE { 68 CalledOnValidThread(); 69 Increment(); 70 } 71 72 virtual void OnSyncDataUpdate(const AttachmentIdList& old_attachment_ids, 73 const SyncData& updated_sync_data) OVERRIDE { 74 CalledOnValidThread(); 75 Increment(); 76 } 77 78 virtual base::WeakPtr<AttachmentService> AsWeakPtr() { 79 return weak_ptr_factory_.GetWeakPtr(); 80 } 81 82 // Return the number of method invocations. 83 int GetCallCount() const { 84 base::AutoLock lock(mutex_); 85 return call_count_; 86 } 87 88 private: 89 // Protects call_count_. 90 mutable base::Lock mutex_; 91 int call_count_; 92 93 // Must be last data member. 94 base::WeakPtrFactory<AttachmentService> weak_ptr_factory_; 95 96 void Increment() { 97 base::AutoLock lock(mutex_); 98 ++call_count_; 99 } 100 }; 101 102 class AttachmentServiceProxyTest : public testing::Test, 103 public base::NonThreadSafe { 104 protected: 105 AttachmentServiceProxyTest() {} 106 107 virtual void SetUp() { 108 CalledOnValidThread(); 109 stub_thread.reset(new base::Thread("attachment service stub thread")); 110 stub_thread->Start(); 111 stub.reset(new StubAttachmentService); 112 proxy.reset(new AttachmentServiceProxy(stub_thread->message_loop_proxy(), 113 stub->AsWeakPtr())); 114 115 sync_data = 116 SyncData::CreateLocalData("tag", "title", sync_pb::EntitySpecifics()); 117 sync_data_delete = 118 SyncData::CreateLocalDelete("tag", syncer::PREFERENCES); 119 120 callback_get_or_download = 121 base::Bind(&AttachmentServiceProxyTest::IncrementGetOrDownload, 122 base::Unretained(this)); 123 callback_drop = base::Bind(&AttachmentServiceProxyTest::IncrementDrop, 124 base::Unretained(this)); 125 callback_store = base::Bind(&AttachmentServiceProxyTest::IncrementStore, 126 base::Unretained(this)); 127 count_callback_get_or_download = 0; 128 count_callback_drop = 0; 129 count_callback_store = 0; 130 } 131 132 virtual void TearDown() 133 OVERRIDE { 134 // We must take care to call the stub's destructor on the stub_thread 135 // because that's the thread to which its WeakPtrs are bound. 136 if (stub) { 137 stub_thread->message_loop()->DeleteSoon(FROM_HERE, stub.release()); 138 WaitForStubThread(); 139 } 140 stub_thread->Stop(); 141 } 142 143 // a GetOrDownloadCallback 144 void IncrementGetOrDownload(const AttachmentService::GetOrDownloadResult&, 145 scoped_ptr<AttachmentMap>) { 146 CalledOnValidThread(); 147 ++count_callback_get_or_download; 148 } 149 150 // a DropCallback 151 void IncrementDrop(const AttachmentService::DropResult&) { 152 CalledOnValidThread(); 153 ++count_callback_drop; 154 } 155 156 // a StoreCallback 157 void IncrementStore(const AttachmentService::StoreResult&) { 158 CalledOnValidThread(); 159 ++count_callback_store; 160 } 161 162 void WaitForStubThread() { 163 base::WaitableEvent done(false, false); 164 stub_thread->message_loop()->PostTask( 165 FROM_HERE, 166 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); 167 done.Wait(); 168 } 169 170 base::MessageLoop loop; 171 scoped_ptr<base::Thread> stub_thread; 172 scoped_ptr<StubAttachmentService> stub; 173 scoped_ptr<AttachmentServiceProxy> proxy; 174 175 SyncData sync_data; 176 SyncData sync_data_delete; 177 178 AttachmentService::GetOrDownloadCallback callback_get_or_download; 179 AttachmentService::DropCallback callback_drop; 180 AttachmentService::StoreCallback callback_store; 181 182 // number of times callback_get_or_download was invoked 183 int count_callback_get_or_download; 184 // number of times callback_drop was invoked 185 int count_callback_drop; 186 // number of times callback_store was invoked 187 int count_callback_store; 188 }; 189 190 // Verify that each of AttachmentServiceProxy's regular methods (those that 191 // don't take callbacks) are invoked on the stub. 192 TEST_F(AttachmentServiceProxyTest, MethodsAreProxied) { 193 proxy->OnSyncDataDelete(sync_data_delete); 194 proxy->OnSyncDataUpdate(AttachmentIdList(), sync_data); 195 WaitForStubThread(); 196 EXPECT_EQ(2, stub->GetCallCount()); 197 } 198 199 // Verify that each of AttachmentServiceProxy's callback methods (those that 200 // take callbacks) are invoked on the stub and that the passed callbacks are 201 // invoked in this thread. 202 TEST_F(AttachmentServiceProxyTest, MethodsWithCallbacksAreProxied) { 203 proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download); 204 proxy->DropAttachments(AttachmentIdList(), callback_drop); 205 proxy->StoreAttachments(AttachmentList(), callback_store); 206 // Wait for the posted calls to execute in the stub thread. 207 WaitForStubThread(); 208 EXPECT_EQ(3, stub->GetCallCount()); 209 // At this point the stub thread has finished executed the calls. However, the 210 // result callbacks it has posted may not have executed yet. Wait a second 211 // time to ensure the stub thread has executed the posted result callbacks. 212 WaitForStubThread(); 213 214 loop.RunUntilIdle(); 215 EXPECT_EQ(1, count_callback_get_or_download); 216 EXPECT_EQ(1, count_callback_drop); 217 EXPECT_EQ(1, count_callback_store); 218 } 219 220 // Verify that it's safe to use an AttachmentServiceProxy even after its wrapped 221 // AttachmentService has been destroyed. 222 TEST_F(AttachmentServiceProxyTest, WrappedIsDestroyed) { 223 proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download); 224 // Wait for the posted calls to execute in the stub thread. 225 WaitForStubThread(); 226 EXPECT_EQ(1, stub->GetCallCount()); 227 // Wait a second time ensure the stub thread has executed the posted result 228 // callbacks. 229 WaitForStubThread(); 230 231 loop.RunUntilIdle(); 232 EXPECT_EQ(1, count_callback_get_or_download); 233 234 // Destroy the stub and call GetOrDownloadAttachments again. 235 stub_thread->message_loop()->DeleteSoon(FROM_HERE, stub.release()); 236 WaitForStubThread(); 237 238 // Now that the wrapped object has been destroyed, call again and see that we 239 // don't crash and the count remains the same. 240 proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download); 241 WaitForStubThread(); 242 WaitForStubThread(); 243 loop.RunUntilIdle(); 244 EXPECT_EQ(1, count_callback_get_or_download); 245 } 246 247 } // namespace syncer 248