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/internal_api/public/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/internal_api/public/attachments/attachment_service.h" 18#include "sync/internal_api/public/base/model_type.h" 19#include "sync/protocol/sync.pb.h" 20#include "testing/gtest/include/gtest/gtest.h" 21 22namespace syncer { 23 24// A stub implementation of AttachmentService that counts the number of times 25// its methods are invoked. 26class StubAttachmentService : public AttachmentService, 27 public base::NonThreadSafe { 28 public: 29 StubAttachmentService() : call_count_(0), weak_ptr_factory_(this) { 30 // DetachFromThread because we will be constructed in one thread and 31 // used/destroyed in another. 32 DetachFromThread(); 33 } 34 35 virtual ~StubAttachmentService() {} 36 37 virtual AttachmentStore* GetStore() OVERRIDE { return NULL; } 38 39 virtual void GetOrDownloadAttachments(const AttachmentIdList& attachment_ids, 40 const GetOrDownloadCallback& callback) 41 OVERRIDE { 42 CalledOnValidThread(); 43 Increment(); 44 scoped_ptr<AttachmentMap> attachments(new AttachmentMap()); 45 base::MessageLoop::current()->PostTask( 46 FROM_HERE, 47 base::Bind(callback, 48 AttachmentService::GET_UNSPECIFIED_ERROR, 49 base::Passed(&attachments))); 50 } 51 52 virtual void DropAttachments(const AttachmentIdList& attachment_ids, 53 const DropCallback& callback) OVERRIDE { 54 CalledOnValidThread(); 55 Increment(); 56 base::MessageLoop::current()->PostTask( 57 FROM_HERE, base::Bind(callback, AttachmentService::DROP_SUCCESS)); 58 } 59 60 virtual void UploadAttachments( 61 const AttachmentIdSet& attachments_ids) OVERRIDE { 62 CalledOnValidThread(); 63 Increment(); 64 } 65 66 virtual base::WeakPtr<AttachmentService> AsWeakPtr() { 67 return weak_ptr_factory_.GetWeakPtr(); 68 } 69 70 // Return the number of method invocations. 71 int GetCallCount() const { 72 base::AutoLock lock(mutex_); 73 return call_count_; 74 } 75 76 private: 77 // Protects call_count_. 78 mutable base::Lock mutex_; 79 int call_count_; 80 81 // Must be last data member. 82 base::WeakPtrFactory<AttachmentService> weak_ptr_factory_; 83 84 void Increment() { 85 base::AutoLock lock(mutex_); 86 ++call_count_; 87 } 88}; 89 90class AttachmentServiceProxyTest : public testing::Test, 91 public base::NonThreadSafe { 92 protected: 93 AttachmentServiceProxyTest() {} 94 95 virtual void SetUp() { 96 CalledOnValidThread(); 97 stub_thread.reset(new base::Thread("attachment service stub thread")); 98 stub_thread->Start(); 99 stub.reset(new StubAttachmentService); 100 proxy.reset(new AttachmentServiceProxy(stub_thread->message_loop_proxy(), 101 stub->AsWeakPtr())); 102 103 callback_get_or_download = 104 base::Bind(&AttachmentServiceProxyTest::IncrementGetOrDownload, 105 base::Unretained(this)); 106 callback_drop = base::Bind(&AttachmentServiceProxyTest::IncrementDrop, 107 base::Unretained(this)); 108 count_callback_get_or_download = 0; 109 count_callback_drop = 0; 110 } 111 112 virtual void TearDown() 113 OVERRIDE { 114 // We must take care to call the stub's destructor on the stub_thread 115 // because that's the thread to which its WeakPtrs are bound. 116 if (stub) { 117 stub_thread->message_loop()->DeleteSoon(FROM_HERE, stub.release()); 118 WaitForStubThread(); 119 } 120 stub_thread->Stop(); 121 } 122 123 // a GetOrDownloadCallback 124 void IncrementGetOrDownload(const AttachmentService::GetOrDownloadResult&, 125 scoped_ptr<AttachmentMap>) { 126 CalledOnValidThread(); 127 ++count_callback_get_or_download; 128 } 129 130 // a DropCallback 131 void IncrementDrop(const AttachmentService::DropResult&) { 132 CalledOnValidThread(); 133 ++count_callback_drop; 134 } 135 136 void WaitForStubThread() { 137 base::WaitableEvent done(false, false); 138 stub_thread->message_loop()->PostTask( 139 FROM_HERE, 140 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done))); 141 done.Wait(); 142 } 143 144 base::MessageLoop loop; 145 scoped_ptr<base::Thread> stub_thread; 146 scoped_ptr<StubAttachmentService> stub; 147 scoped_ptr<AttachmentServiceProxy> proxy; 148 149 AttachmentService::GetOrDownloadCallback callback_get_or_download; 150 AttachmentService::DropCallback callback_drop; 151 152 // number of times callback_get_or_download was invoked 153 int count_callback_get_or_download; 154 // number of times callback_drop was invoked 155 int count_callback_drop; 156}; 157 158TEST_F(AttachmentServiceProxyTest, GetStore) { 159 EXPECT_EQ(NULL, proxy->GetStore()); 160} 161 162// Verify that each of AttachmentServiceProxy's methods are invoked on the stub. 163// Verify that the methods that take callbacks invoke passed callbacks on this 164// thread. 165TEST_F(AttachmentServiceProxyTest, MethodsAreProxied) { 166 proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download); 167 proxy->DropAttachments(AttachmentIdList(), callback_drop); 168 proxy->UploadAttachments(AttachmentIdSet()); 169 // Wait for the posted calls to execute in the stub thread. 170 WaitForStubThread(); 171 EXPECT_EQ(3, stub->GetCallCount()); 172 // At this point the stub thread has finished executed the calls. However, the 173 // result callbacks it has posted may not have executed yet. Wait a second 174 // time to ensure the stub thread has executed the posted result callbacks. 175 WaitForStubThread(); 176 177 loop.RunUntilIdle(); 178 EXPECT_EQ(1, count_callback_get_or_download); 179 EXPECT_EQ(1, count_callback_drop); 180} 181 182// Verify that it's safe to use an AttachmentServiceProxy even after its wrapped 183// AttachmentService has been destroyed. 184TEST_F(AttachmentServiceProxyTest, WrappedIsDestroyed) { 185 proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download); 186 // Wait for the posted calls to execute in the stub thread. 187 WaitForStubThread(); 188 EXPECT_EQ(1, stub->GetCallCount()); 189 // Wait a second time ensure the stub thread has executed the posted result 190 // callbacks. 191 WaitForStubThread(); 192 193 loop.RunUntilIdle(); 194 EXPECT_EQ(1, count_callback_get_or_download); 195 196 // Destroy the stub and call GetOrDownloadAttachments again. 197 stub_thread->message_loop()->DeleteSoon(FROM_HERE, stub.release()); 198 WaitForStubThread(); 199 200 // Now that the wrapped object has been destroyed, call again and see that we 201 // don't crash and the count remains the same. 202 proxy->GetOrDownloadAttachments(AttachmentIdList(), callback_get_or_download); 203 WaitForStubThread(); 204 WaitForStubThread(); 205 loop.RunUntilIdle(); 206 EXPECT_EQ(1, count_callback_get_or_download); 207} 208 209} // namespace syncer 210