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