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 17 namespace ppapi { 18 19 namespace { 20 21 class 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. 42 const int32_t kInitializedResultValue = 1; 43 const int32_t kOverrideResultValue = 2; 44 45 struct 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 57 void 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 68 namespace { 69 70 class 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. 91 TEST_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 136 namespace { 137 138 class CallbackResourceTest : public TrackedCallbackTest { 139 public: 140 CallbackResourceTest() {} 141 }; 142 143 class 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. 252 TEST_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. 292 TEST_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