tracked_callback_unittest.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1// Copyright (c) 2012 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 "base/bind.h"
6#include "base/memory/ref_counted.h"
7#include "base/message_loop/message_loop.h"
8#include "ppapi/c/pp_completion_callback.h"
9#include "ppapi/c/pp_errors.h"
10#include "ppapi/shared_impl/callback_tracker.h"
11#include "ppapi/shared_impl/proxy_lock.h"
12#include "ppapi/shared_impl/resource.h"
13#include "ppapi/shared_impl/resource_tracker.h"
14#include "ppapi/shared_impl/test_globals.h"
15#include "ppapi/shared_impl/tracked_callback.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace ppapi {
19
20namespace {
21
22class TrackedCallbackTest : public testing::Test {
23 public:
24  TrackedCallbackTest()
25      : message_loop_(base::MessageLoop::TYPE_DEFAULT), pp_instance_(1234) {}
26
27  PP_Instance pp_instance() const { return pp_instance_; }
28
29  virtual void SetUp() OVERRIDE {
30    ProxyLock::EnableLockingOnThreadForTest();
31    ProxyAutoLock lock;
32    globals_.GetResourceTracker()->DidCreateInstance(pp_instance_);
33  }
34  virtual void TearDown() OVERRIDE {
35    ProxyAutoLock lock;
36    globals_.GetResourceTracker()->DidDeleteInstance(pp_instance_);
37  }
38
39 private:
40  base::MessageLoop message_loop_;
41  TestGlobals globals_;
42  PP_Instance pp_instance_;
43};
44
45// All valid results (PP_OK, PP_ERROR_...) are nonpositive.
46const int32_t kInitializedResultValue = 1;
47const int32_t kOverrideResultValue = 2;
48
49struct CallbackRunInfo {
50  CallbackRunInfo()
51      : run_count(0),
52        result(kInitializedResultValue),
53        completion_task_run_count(0),
54        completion_task_result(kInitializedResultValue) {}
55  unsigned run_count;
56  int32_t result;
57  unsigned completion_task_run_count;
58  int32_t completion_task_result;
59};
60
61void TestCallback(void* user_data, int32_t result) {
62  CallbackRunInfo* info = reinterpret_cast<CallbackRunInfo*>(user_data);
63  info->run_count++;
64  if (info->run_count == 1)
65    info->result = result;
66}
67
68}  // namespace
69
70// CallbackShutdownTest --------------------------------------------------------
71
72namespace {
73
74class CallbackShutdownTest : public TrackedCallbackTest {
75 public:
76  CallbackShutdownTest() {}
77
78  // Cases:
79  // (1) A callback which is run (so shouldn't be aborted on shutdown).
80  // (2) A callback which is aborted (so shouldn't be aborted on shutdown).
81  // (3) A callback which isn't run (so should be aborted on shutdown).
82  CallbackRunInfo& info_did_run() { return info_did_run_; }  // (1)
83  CallbackRunInfo& info_did_abort() { return info_did_abort_; }  // (2)
84  CallbackRunInfo& info_didnt_run() { return info_didnt_run_; }  // (3)
85
86 private:
87  CallbackRunInfo info_did_run_;
88  CallbackRunInfo info_did_abort_;
89  CallbackRunInfo info_didnt_run_;
90};
91
92}  // namespace
93
94// Tests that callbacks are properly aborted on module shutdown.
95TEST_F(CallbackShutdownTest, AbortOnShutdown) {
96  ProxyAutoLock lock;
97  scoped_refptr<Resource> resource(new Resource(OBJECT_IS_IMPL, pp_instance()));
98
99  // Set up case (1) (see above).
100  EXPECT_EQ(0U, info_did_run().run_count);
101  scoped_refptr<TrackedCallback> callback_did_run = new TrackedCallback(
102      resource.get(),
103      PP_MakeCompletionCallback(&TestCallback, &info_did_run()));
104  EXPECT_EQ(0U, info_did_run().run_count);
105  callback_did_run->Run(PP_OK);
106  EXPECT_EQ(1U, info_did_run().run_count);
107  EXPECT_EQ(PP_OK, info_did_run().result);
108
109  // Set up case (2).
110  EXPECT_EQ(0U, info_did_abort().run_count);
111  scoped_refptr<TrackedCallback> callback_did_abort = new TrackedCallback(
112      resource.get(),
113      PP_MakeCompletionCallback(&TestCallback, &info_did_abort()));
114  EXPECT_EQ(0U, info_did_abort().run_count);
115  callback_did_abort->Abort();
116  EXPECT_EQ(1U, info_did_abort().run_count);
117  EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort().result);
118
119  // Set up case (3).
120  EXPECT_EQ(0U, info_didnt_run().run_count);
121  scoped_refptr<TrackedCallback> callback_didnt_run = new TrackedCallback(
122      resource.get(),
123      PP_MakeCompletionCallback(&TestCallback, &info_didnt_run()));
124  EXPECT_EQ(0U, info_didnt_run().run_count);
125
126  PpapiGlobals::Get()->GetCallbackTrackerForInstance(pp_instance())->AbortAll();
127
128  // Check case (1).
129  EXPECT_EQ(1U, info_did_run().run_count);
130
131  // Check case (2).
132  EXPECT_EQ(1U, info_did_abort().run_count);
133
134  // Check case (3).
135  EXPECT_EQ(1U, info_didnt_run().run_count);
136  EXPECT_EQ(PP_ERROR_ABORTED, info_didnt_run().result);
137}
138
139// CallbackResourceTest --------------------------------------------------------
140
141namespace {
142
143class CallbackResourceTest : public TrackedCallbackTest {
144 public:
145  CallbackResourceTest() {}
146};
147
148class CallbackMockResource : public Resource {
149 public:
150  CallbackMockResource(PP_Instance instance)
151      : Resource(OBJECT_IS_IMPL, instance) {}
152  ~CallbackMockResource() {}
153
154  PP_Resource SetupForTest() {
155    PP_Resource resource_id = GetReference();
156    EXPECT_NE(0, resource_id);
157
158    callback_did_run_ = new TrackedCallback(
159        this,
160        PP_MakeCompletionCallback(&TestCallback, &info_did_run_));
161    EXPECT_EQ(0U, info_did_run_.run_count);
162    EXPECT_EQ(0U, info_did_run_.completion_task_run_count);
163
164    // In order to test that the completion task can override the callback
165    // result, we need to test callbacks with and without a completion task.
166    callback_did_run_with_completion_task_ = new TrackedCallback(
167        this,
168        PP_MakeCompletionCallback(&TestCallback,
169                                  &info_did_run_with_completion_task_));
170    callback_did_run_with_completion_task_->set_completion_task(
171        Bind(&CallbackMockResource::CompletionTask, this,
172             &info_did_run_with_completion_task_));
173    EXPECT_EQ(0U, info_did_run_with_completion_task_.run_count);
174    EXPECT_EQ(0U, info_did_run_with_completion_task_.completion_task_run_count);
175
176    callback_did_abort_ = new TrackedCallback(
177        this,
178        PP_MakeCompletionCallback(&TestCallback, &info_did_abort_));
179    callback_did_abort_->set_completion_task(
180        Bind(&CallbackMockResource::CompletionTask, this, &info_did_abort_));
181    EXPECT_EQ(0U, info_did_abort_.run_count);
182    EXPECT_EQ(0U, info_did_abort_.completion_task_run_count);
183
184    callback_didnt_run_ = new TrackedCallback(
185        this,
186        PP_MakeCompletionCallback(&TestCallback, &info_didnt_run_));
187    callback_didnt_run_->set_completion_task(
188        Bind(&CallbackMockResource::CompletionTask, this, &info_didnt_run_));
189    EXPECT_EQ(0U, info_didnt_run_.run_count);
190    EXPECT_EQ(0U, info_didnt_run_.completion_task_run_count);
191
192    callback_did_run_->Run(PP_OK);
193    callback_did_run_with_completion_task_->Run(PP_OK);
194    callback_did_abort_->Abort();
195
196    CheckIntermediateState();
197
198    return resource_id;
199  }
200
201  int32_t CompletionTask(CallbackRunInfo* info, int32_t result) {
202    // We should run before the callback.
203    EXPECT_EQ(0U, info->run_count);
204    info->completion_task_run_count++;
205    if (info->completion_task_run_count == 1)
206      info->completion_task_result = result;
207    return kOverrideResultValue;
208  }
209
210  void CheckIntermediateState() {
211    EXPECT_EQ(1U, info_did_run_.run_count);
212    EXPECT_EQ(PP_OK, info_did_run_.result);
213    EXPECT_EQ(0U, info_did_run_.completion_task_run_count);
214
215    EXPECT_EQ(1U, info_did_run_with_completion_task_.run_count);
216    // completion task should override the result.
217    EXPECT_EQ(kOverrideResultValue, info_did_run_with_completion_task_.result);
218    EXPECT_EQ(1U, info_did_run_with_completion_task_.completion_task_run_count);
219    EXPECT_EQ(PP_OK,
220              info_did_run_with_completion_task_.completion_task_result);
221
222    EXPECT_EQ(1U, info_did_abort_.run_count);
223    // completion task shouldn't override an abort.
224    EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.result);
225    EXPECT_EQ(1U, info_did_abort_.completion_task_run_count);
226    EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.completion_task_result);
227
228    EXPECT_EQ(0U, info_didnt_run_.completion_task_run_count);
229    EXPECT_EQ(0U, info_didnt_run_.run_count);
230  }
231
232  void CheckFinalState() {
233    EXPECT_EQ(1U, info_did_run_.run_count);
234    EXPECT_EQ(PP_OK, info_did_run_.result);
235    EXPECT_EQ(1U, info_did_abort_.run_count);
236    EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.result);
237    EXPECT_EQ(1U, info_didnt_run_.run_count);
238    EXPECT_EQ(PP_ERROR_ABORTED, info_didnt_run_.result);
239  }
240
241  scoped_refptr<TrackedCallback> callback_did_run_;
242  CallbackRunInfo info_did_run_;
243
244  scoped_refptr<TrackedCallback> callback_did_run_with_completion_task_;
245  CallbackRunInfo info_did_run_with_completion_task_;
246
247  scoped_refptr<TrackedCallback> callback_did_abort_;
248  CallbackRunInfo info_did_abort_;
249
250  scoped_refptr<TrackedCallback> callback_didnt_run_;
251  CallbackRunInfo info_didnt_run_;
252};
253
254}  // namespace
255
256// Test that callbacks get aborted on the last resource unref.
257TEST_F(CallbackResourceTest, AbortOnNoRef) {
258  ProxyAutoLock lock;
259  ResourceTracker* resource_tracker =
260      PpapiGlobals::Get()->GetResourceTracker();
261
262  // Test several things: Unref-ing a resource (to zero refs) with callbacks
263  // which (1) have been run, (2) have been aborted, (3) haven't been completed.
264  // Check that the uncompleted one gets aborted, and that the others don't get
265  // called again.
266  scoped_refptr<CallbackMockResource> resource_1(
267      new CallbackMockResource(pp_instance()));
268  PP_Resource resource_1_id = resource_1->SetupForTest();
269
270  // Also do the same for a second resource, and make sure that unref-ing the
271  // first resource doesn't much up the second resource.
272  scoped_refptr<CallbackMockResource> resource_2(
273      new CallbackMockResource(pp_instance()));
274  PP_Resource resource_2_id = resource_2->SetupForTest();
275
276  // Double-check that resource #1 is still okay.
277  resource_1->CheckIntermediateState();
278
279  // Kill resource #1, spin the message loop to run posted calls, and check that
280  // things are in the expected states.
281  resource_tracker->ReleaseResource(resource_1_id);
282  {
283    ProxyAutoUnlock unlock;
284    base::MessageLoop::current()->RunUntilIdle();
285  }
286  resource_1->CheckFinalState();
287  resource_2->CheckIntermediateState();
288
289  // Kill resource #2.
290  resource_tracker->ReleaseResource(resource_2_id);
291  {
292    ProxyAutoUnlock unlock;
293    base::MessageLoop::current()->RunUntilIdle();
294  }
295  resource_1->CheckFinalState();
296  resource_2->CheckFinalState();
297
298  // This shouldn't be needed, but make sure there are no stranded tasks.
299  {
300    ProxyAutoUnlock unlock;
301    base::MessageLoop::current()->RunUntilIdle();
302  }
303}
304
305// Test that "resurrecting" a resource (getting a new ID for a |Resource|)
306// doesn't resurrect callbacks.
307TEST_F(CallbackResourceTest, Resurrection) {
308  ProxyAutoLock lock;
309  ResourceTracker* resource_tracker =
310      PpapiGlobals::Get()->GetResourceTracker();
311
312  scoped_refptr<CallbackMockResource> resource(
313      new CallbackMockResource(pp_instance()));
314  PP_Resource resource_id = resource->SetupForTest();
315
316  // Unref it, spin the message loop to run posted calls, and check that things
317  // are in the expected states.
318  resource_tracker->ReleaseResource(resource_id);
319  {
320    ProxyAutoUnlock unlock;
321    base::MessageLoop::current()->RunUntilIdle();
322  }
323  resource->CheckFinalState();
324
325  // "Resurrect" it and check that the callbacks are still dead.
326  PP_Resource new_resource_id = resource->GetReference();
327  {
328    ProxyAutoUnlock unlock;
329    base::MessageLoop::current()->RunUntilIdle();
330  }
331  resource->CheckFinalState();
332
333  // Unref it again and do the same.
334  resource_tracker->ReleaseResource(new_resource_id);
335  {
336    ProxyAutoUnlock unlock;
337    base::MessageLoop::current()->RunUntilIdle();
338  }
339  resource->CheckFinalState();
340
341  // This shouldn't be needed, but make sure there are no stranded tasks.
342  {
343    ProxyAutoUnlock unlock;
344    base::MessageLoop::current()->RunUntilIdle();
345  }
346}
347
348}  // namespace ppapi
349