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