Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3158)

Unified Diff: cc/tiles/image_controller_unittest.cc

Issue 2537683002: cc: Add image decode queue functionality to image manager. (Closed)
Patch Set: test fix Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « cc/tiles/image_controller.cc ('k') | cc/tiles/image_decode_cache.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: cc/tiles/image_controller_unittest.cc
diff --git a/cc/tiles/image_controller_unittest.cc b/cc/tiles/image_controller_unittest.cc
index fd9841cdd59844c2b89f3ce4f04d853aa3dd7555..b963ff1c9aa53fb826a310edd3d6837290164382 100644
--- a/cc/tiles/image_controller_unittest.cc
+++ b/cc/tiles/image_controller_unittest.cc
@@ -2,18 +2,102 @@
// 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/optional.h"
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "cc/tiles/image_controller.h"
#include "cc/tiles/image_decode_cache.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace cc {
+namespace {
+class TestWorkerThread : public base::SimpleThread {
+ public:
+ TestWorkerThread()
+ : base::SimpleThread("test_worker_thread"), condition_(&lock_) {}
+
+ void Run() override {
+ for (;;) {
+ base::AutoLock hold(lock_);
+ if (shutdown_)
+ break;
+
+ if (queue_.empty()) {
+ condition_.Wait();
+ continue;
+ }
+
+ queue_.front().Run();
+ queue_.erase(queue_.begin());
+ }
+ }
+
+ void Shutdown() {
+ base::AutoLock hold(lock_);
+ shutdown_ = true;
+ condition_.Signal();
+ }
+
+ void PostTask(const base::Closure& task) {
+ base::AutoLock hold(lock_);
+ queue_.push_back(task);
+ condition_.Signal();
+ }
+
+ private:
+ base::Lock lock_;
+ base::ConditionVariable condition_;
+ std::vector<base::Closure> queue_;
+ bool shutdown_ = false;
+};
+
+class WorkerTaskRunner : public base::SequencedTaskRunner {
+ public:
+ WorkerTaskRunner() { thread_.Start(); }
+
+ bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) override {
+ return PostDelayedTask(from_here, task, delay);
+ }
+
+ bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) override {
+ thread_.PostTask(task);
+ return true;
+ }
+
+ bool RunsTasksOnCurrentThread() const override { return false; }
+
+ protected:
+ ~WorkerTaskRunner() override {
+ thread_.Shutdown();
+ thread_.Join();
+ }
+
+ TestWorkerThread thread_;
+};
+
+// Image decode cache with introspection!
class TestableCache : public ImageDecodeCache {
public:
+ ~TestableCache() override { EXPECT_EQ(number_of_refs_, 0); }
+
bool GetTaskForImageAndRef(const DrawImage& image,
const TracingInfo& tracing_info,
scoped_refptr<TileTask>* task) override {
- *task = nullptr;
+ *task = task_to_use_;
+ ++number_of_refs_;
+ return true;
+ }
+ bool GetOutOfRasterDecodeTaskForImageAndRef(
+ const DrawImage& image,
+ scoped_refptr<TileTask>* task) override {
+ *task = task_to_use_;
++number_of_refs_;
return true;
}
@@ -32,26 +116,332 @@ class TestableCache : public ImageDecodeCache {
bool aggressively_free_resources) override {}
int number_of_refs() const { return number_of_refs_; }
+ void SetTaskToUse(scoped_refptr<TileTask> task) { task_to_use_ = task; }
private:
int number_of_refs_ = 0;
+ scoped_refptr<TileTask> task_to_use_;
+};
+
+// A simple class that can receive decode callbacks.
+class DecodeClient {
+ public:
+ DecodeClient() {}
+ void Callback(const base::Closure& quit_closure,
+ ImageController::ImageDecodeRequestId id) {
+ id_ = id;
+ quit_closure.Run();
+ }
+
+ ImageController::ImageDecodeRequestId id() { return id_; }
+
+ private:
+ ImageController::ImageDecodeRequestId id_ = 0;
+};
+
+// A dummy task that does nothing.
+class SimpleTask : public TileTask {
+ public:
+ SimpleTask() : TileTask(true /* supports_concurrent_execution */) {
+ EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+ }
+
+ void RunOnWorkerThread() override {
+ EXPECT_FALSE(HasCompleted());
+ has_run_ = true;
+ }
+ void OnTaskCompleted() override {
+ EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+ }
+
+ bool has_run() { return has_run_; }
+
+ private:
+ ~SimpleTask() override = default;
+
+ base::ThreadChecker thread_checker_;
+ bool has_run_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(SimpleTask);
+};
+
+// A task that blocks until instructed otherwise.
+class BlockingTask : public TileTask {
+ public:
+ BlockingTask()
+ : TileTask(true /* supports_concurrent_execution */), run_cv_(&lock_) {
+ EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+ }
+
+ void RunOnWorkerThread() override {
+ EXPECT_FALSE(HasCompleted());
+ EXPECT_FALSE(thread_checker_.CalledOnValidThread());
+ base::AutoLock hold(lock_);
+ if (!can_run_)
+ run_cv_.Wait();
+ has_run_ = true;
+ }
+
+ void OnTaskCompleted() override {
+ EXPECT_TRUE(thread_checker_.CalledOnValidThread());
+ }
+
+ void AllowToRun() {
+ base::AutoLock hold(lock_);
+ can_run_ = true;
+ run_cv_.Signal();
+ }
+
+ bool has_run() { return has_run_; }
+
+ private:
+ ~BlockingTask() override = default;
+
+ base::ThreadChecker thread_checker_;
+ bool has_run_ = false;
+ base::Lock lock_;
+ base::ConditionVariable run_cv_;
+ bool can_run_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(BlockingTask);
};
-TEST(ImageControllerTest, NullCacheUnrefsImages) {
- TestableCache cache;
- ImageController controller;
- controller.SetImageDecodeCache(&cache);
+// For tests that exercise image controller's thread, this is the timeout value
+// to
+// allow the worker thread to do its work.
+int kDefaultTimeoutSeconds = 10;
+
+class ImageControllerTest : public testing::Test {
+ public:
+ ImageControllerTest() : task_runner_(base::SequencedTaskRunnerHandle::Get()) {
+ bitmap_.allocN32Pixels(1, 1);
+ image_ = SkImage::MakeFromBitmap(bitmap_);
+ }
+ ~ImageControllerTest() override = default;
+
+ void SetUp() override {
+ worker_task_runner_ = make_scoped_refptr(new WorkerTaskRunner);
+ controller_.reset(
+ new ImageController(task_runner_.get(), worker_task_runner_));
+ cache_ = TestableCache();
+ controller_->SetImageDecodeCache(&cache_);
+ }
+
+ void TearDown() override {
+ controller_.reset();
+ worker_task_runner_ = nullptr;
+ }
+
+ base::SequencedTaskRunner* task_runner() { return task_runner_.get(); }
+ ImageController* controller() { return controller_.get(); }
+ TestableCache* cache() { return &cache_; }
+ sk_sp<const SkImage> image() const { return image_; }
+
+ // Timeout callback, which errors and exits the runloop.
+ static void Timeout(base::RunLoop* run_loop) {
+ ADD_FAILURE() << "Timeout.";
+ run_loop->Quit();
+ }
+
+ // Convenience method to run the run loop with a timeout.
+ void RunOrTimeout(base::RunLoop* run_loop) {
+ task_runner_->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ImageControllerTest::Timeout, base::Unretained(run_loop)),
+ base::TimeDelta::FromSeconds(kDefaultTimeoutSeconds));
+ run_loop->Run();
+ }
+
+ private:
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+ scoped_refptr<WorkerTaskRunner> worker_task_runner_;
+ TestableCache cache_;
+ std::unique_ptr<ImageController> controller_;
+ SkBitmap bitmap_;
+ sk_sp<const SkImage> image_;
+};
+TEST_F(ImageControllerTest, NullControllerUnrefsImages) {
std::vector<DrawImage> images(10);
ImageDecodeCache::TracingInfo tracing_info;
ASSERT_EQ(10u, images.size());
- auto tasks = controller.SetPredecodeImages(std::move(images), tracing_info);
+ auto tasks =
+ controller()->SetPredecodeImages(std::move(images), tracing_info);
EXPECT_EQ(0u, tasks.size());
- EXPECT_EQ(10, cache.number_of_refs());
+ EXPECT_EQ(10, cache()->number_of_refs());
+
+ controller()->SetImageDecodeCache(nullptr);
+ EXPECT_EQ(0, cache()->number_of_refs());
+}
+
+TEST_F(ImageControllerTest, QueueImageDecode) {
+ base::RunLoop run_loop;
+ DecodeClient decode_client;
+ EXPECT_EQ(image()->bounds().width(), 1);
+ ImageController::ImageDecodeRequestId expected_id =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client),
+ run_loop.QuitClosure()));
+ RunOrTimeout(&run_loop);
+ EXPECT_EQ(expected_id, decode_client.id());
+}
+
+TEST_F(ImageControllerTest, QueueImageDecodeMultipleImages) {
+ base::RunLoop run_loop;
+ DecodeClient decode_client1;
+ ImageController::ImageDecodeRequestId expected_id1 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1),
+ base::Bind([] {})));
+ DecodeClient decode_client2;
+ ImageController::ImageDecodeRequestId expected_id2 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2),
+ base::Bind([] {})));
+ DecodeClient decode_client3;
+ ImageController::ImageDecodeRequestId expected_id3 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client3),
+ run_loop.QuitClosure()));
+ RunOrTimeout(&run_loop);
+ EXPECT_EQ(expected_id1, decode_client1.id());
+ EXPECT_EQ(expected_id2, decode_client2.id());
+ EXPECT_EQ(expected_id3, decode_client3.id());
+}
+
+TEST_F(ImageControllerTest, QueueImageDecodeWithTask) {
+ scoped_refptr<SimpleTask> task(new SimpleTask);
+ cache()->SetTaskToUse(task);
+
+ base::RunLoop run_loop;
+ DecodeClient decode_client;
+ ImageController::ImageDecodeRequestId expected_id =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client),
+ run_loop.QuitClosure()));
+ RunOrTimeout(&run_loop);
+ EXPECT_EQ(expected_id, decode_client.id());
+ EXPECT_TRUE(task->has_run());
+ EXPECT_TRUE(task->HasCompleted());
+}
+
+TEST_F(ImageControllerTest, QueueImageDecodeMultipleImagesSameTask) {
+ scoped_refptr<SimpleTask> task(new SimpleTask);
+ cache()->SetTaskToUse(task);
+
+ base::RunLoop run_loop;
+ DecodeClient decode_client1;
+ ImageController::ImageDecodeRequestId expected_id1 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1),
+ base::Bind([] {})));
+ DecodeClient decode_client2;
+ ImageController::ImageDecodeRequestId expected_id2 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2),
+ base::Bind([] {})));
+ DecodeClient decode_client3;
+ ImageController::ImageDecodeRequestId expected_id3 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client3),
+ run_loop.QuitClosure()));
+ RunOrTimeout(&run_loop);
+ EXPECT_EQ(expected_id1, decode_client1.id());
+ EXPECT_EQ(expected_id2, decode_client2.id());
+ EXPECT_EQ(expected_id3, decode_client3.id());
+ EXPECT_TRUE(task->has_run());
+ EXPECT_TRUE(task->HasCompleted());
+}
+
+TEST_F(ImageControllerTest, QueueImageDecodeChangeControllerWithTaskQueued) {
+ scoped_refptr<BlockingTask> task_one(new BlockingTask);
+ cache()->SetTaskToUse(task_one);
+
+ DecodeClient decode_client1;
+ ImageController::ImageDecodeRequestId expected_id1 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1),
+ base::Bind([] {})));
+
+ scoped_refptr<BlockingTask> task_two(new BlockingTask);
+ cache()->SetTaskToUse(task_two);
+
+ base::RunLoop run_loop;
+ DecodeClient decode_client2;
+ ImageController::ImageDecodeRequestId expected_id2 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2),
+ run_loop.QuitClosure()));
+
+ task_one->AllowToRun();
+ task_two->AllowToRun();
+ controller()->SetImageDecodeCache(nullptr);
+
+ RunOrTimeout(&run_loop);
+
+ EXPECT_TRUE(task_one->state().IsCanceled() || task_one->HasCompleted());
+ EXPECT_TRUE(task_two->state().IsCanceled() || task_two->HasCompleted());
+ EXPECT_EQ(expected_id1, decode_client1.id());
+ EXPECT_EQ(expected_id2, decode_client2.id());
+}
+
+TEST_F(ImageControllerTest, QueueImageDecodeImageAlreadyLocked) {
+ scoped_refptr<SimpleTask> task(new SimpleTask);
+ cache()->SetTaskToUse(task);
+
+ base::RunLoop run_loop1;
+ DecodeClient decode_client1;
+ ImageController::ImageDecodeRequestId expected_id1 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1),
+ run_loop1.QuitClosure()));
+ RunOrTimeout(&run_loop1);
+ EXPECT_EQ(expected_id1, decode_client1.id());
+ EXPECT_TRUE(task->has_run());
+
+ cache()->SetTaskToUse(nullptr);
+ base::RunLoop run_loop2;
+ DecodeClient decode_client2;
+ ImageController::ImageDecodeRequestId expected_id2 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2),
+ run_loop2.QuitClosure()));
+ RunOrTimeout(&run_loop2);
+ EXPECT_EQ(expected_id2, decode_client2.id());
+}
+
+TEST_F(ImageControllerTest, QueueImageDecodeLockedImageControllerChange) {
+ scoped_refptr<SimpleTask> task(new SimpleTask);
+ cache()->SetTaskToUse(task);
+
+ base::RunLoop run_loop1;
+ DecodeClient decode_client1;
+ ImageController::ImageDecodeRequestId expected_id1 =
+ controller()->QueueImageDecode(
+ image(),
+ base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1),
+ run_loop1.QuitClosure()));
+ RunOrTimeout(&run_loop1);
+ EXPECT_EQ(expected_id1, decode_client1.id());
+ EXPECT_TRUE(task->has_run());
+ EXPECT_EQ(1, cache()->number_of_refs());
- controller.SetImageDecodeCache(nullptr);
- EXPECT_EQ(0, cache.number_of_refs());
+ controller()->SetImageDecodeCache(nullptr);
+ EXPECT_EQ(0, cache()->number_of_refs());
}
+} // namespace
} // namespace cc
« no previous file with comments | « cc/tiles/image_controller.cc ('k') | cc/tiles/image_decode_cache.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698