Index: cc/layers/delegated_frame_resource_collection_unittest.cc |
diff --git a/cc/layers/delegated_frame_resource_collection_unittest.cc b/cc/layers/delegated_frame_resource_collection_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ecbc7b13652227847f0e5084f70c434704376b33 |
--- /dev/null |
+++ b/cc/layers/delegated_frame_resource_collection_unittest.cc |
@@ -0,0 +1,158 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/bind.h" |
+#include "base/run_loop.h" |
+#include "base/synchronization/waitable_event.h" |
+#include "base/threading/thread.h" |
+#include "cc/layers/delegated_frame_resource_collection.h" |
+#include "cc/resources/returned_resource.h" |
+#include "cc/resources/transferable_resource.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace cc { |
+namespace { |
+ |
+class DelegatedFrameResourceCollectionTest |
+ : public testing::Test, |
+ public DelegatedFrameResourceCollectionClient { |
+ protected: |
+ DelegatedFrameResourceCollectionTest() : resources_available_(false) {} |
+ |
+ virtual void SetUp() OVERRIDE { CreateResourceCollection(); } |
+ |
+ virtual void TearDown() OVERRIDE { DestroyResourceCollection(); } |
+ |
+ void CreateResourceCollection() { |
+ DCHECK(!resource_collection_); |
+ resource_collection_ = new DelegatedFrameResourceCollection; |
+ resource_collection_->SetClient(this); |
+ } |
+ |
+ void DestroyResourceCollection() { |
+ if (resource_collection_) { |
+ resource_collection_->SetClient(NULL); |
+ resource_collection_ = NULL; |
+ } |
+ } |
+ |
+ TransferableResourceArray CreateResourceArray() { |
+ TransferableResourceArray resources; |
+ TransferableResource resource; |
+ resource.id = 444; |
+ resources.push_back(resource); |
+ return resources; |
+ } |
+ |
+ virtual void UnusedResourcesAreAvailable() OVERRIDE { |
+ resources_available_ = true; |
+ resource_collection_->TakeUnusedResourcesForChildCompositor( |
+ &returned_resources_); |
+ if (!resources_available_closure_.is_null()) |
+ resources_available_closure_.Run(); |
+ } |
+ |
+ bool ReturnAndResetResourcesAvailable() { |
+ bool r = resources_available_; |
+ resources_available_ = false; |
+ return r; |
+ } |
+ |
+ scoped_refptr<DelegatedFrameResourceCollection> resource_collection_; |
+ bool resources_available_; |
+ ReturnedResourceArray returned_resources_; |
+ base::Closure resources_available_closure_; |
+}; |
+ |
+// This checks that taking the return callback doesn't take extra refcounts, |
+// since it's sent to other threads. |
+TEST_F(DelegatedFrameResourceCollectionTest, NoRef) { |
+ // Start with one ref. |
+ EXPECT_TRUE(resource_collection_->HasOneRef()); |
+ |
+ ReturnCallback return_callback = |
+ resource_collection_->GetReturnResourcesCallbackForImplThread(); |
+ |
+ // Callback shouldn't take a ref since it's sent to other threads. |
+ EXPECT_TRUE(resource_collection_->HasOneRef()); |
+} |
+ |
+void ReturnResourcesOnThread(ReturnCallback callback, |
+ const ReturnedResourceArray& resources, |
+ base::WaitableEvent* event) { |
+ callback.Run(resources); |
+ event->Wait(); |
+} |
+ |
+// Tests that the ReturnCallback can run safely on threads even after the |
+// last references to the collection were dropped. |
+TEST_F(DelegatedFrameResourceCollectionTest, Thread) { |
+ base::Thread thread("test thread"); |
+ thread.Start(); |
+ |
+ TransferableResourceArray resources = CreateResourceArray(); |
+ resource_collection_->ReceivedResources(resources); |
+ resource_collection_->RefResources(resources); |
+ |
+ ReturnedResourceArray returned_resources; |
+ TransferableResource::ReturnResources(resources, &returned_resources); |
+ |
+ base::WaitableEvent event(false, false); |
+ |
+ { |
+ base::RunLoop run_loop; |
+ resources_available_closure_ = run_loop.QuitClosure(); |
+ |
+ thread.message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind( |
+ &ReturnResourcesOnThread, |
+ resource_collection_->GetReturnResourcesCallbackForImplThread(), |
+ returned_resources, |
+ &event)); |
+ |
+ run_loop.Run(); |
+ } |
+ EXPECT_TRUE(ReturnAndResetResourcesAvailable()); |
+ EXPECT_EQ(1u, returned_resources_.size()); |
+ EXPECT_EQ(444u, returned_resources_[0].id); |
+ EXPECT_EQ(1, returned_resources_[0].count); |
+ returned_resources_.clear(); |
+ |
+ // The event prevents the return resources callback from being deleted. |
+ // Destroy the last reference from this thread to the collection before |
+ // signaling the event, to ensure any reference taken by the callback, if any, |
+ // would be the last one. |
+ DestroyResourceCollection(); |
+ event.Signal(); |
+ |
+ CreateResourceCollection(); |
+ resource_collection_->ReceivedResources(resources); |
+ resource_collection_->RefResources(resources); |
+ |
+ // Destroy the collection before we have a chance to run the return callback. |
+ ReturnCallback return_callback = |
+ resource_collection_->GetReturnResourcesCallbackForImplThread(); |
+ resource_collection_->LoseAllResources(); |
+ DestroyResourceCollection(); |
+ |
+ EXPECT_TRUE(ReturnAndResetResourcesAvailable()); |
+ EXPECT_EQ(1u, returned_resources_.size()); |
+ EXPECT_EQ(444u, returned_resources_[0].id); |
+ EXPECT_EQ(1, returned_resources_[0].count); |
+ EXPECT_TRUE(returned_resources_[0].lost); |
+ returned_resources_.clear(); |
+ |
+ thread.message_loop()->PostTask(FROM_HERE, |
+ base::Bind(&ReturnResourcesOnThread, |
+ return_callback, |
+ returned_resources, |
+ &event)); |
+ event.Signal(); |
+ |
+ thread.Stop(); |
+} |
+ |
+} // namespace |
+} // namespace cc |