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