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