OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/bind.h" | |
6 #include "base/optional.h" | |
7 #include "base/run_loop.h" | |
8 #include "base/test/test_simple_task_runner.h" | |
9 #include "base/threading/sequenced_task_runner_handle.h" | |
10 #include "cc/tiles/image_controller.h" | 5 #include "cc/tiles/image_controller.h" |
11 #include "cc/tiles/image_decode_cache.h" | 6 #include "cc/tiles/image_decode_cache.h" |
12 #include "testing/gtest/include/gtest/gtest.h" | 7 #include "testing/gtest/include/gtest/gtest.h" |
13 | 8 |
14 namespace cc { | 9 namespace cc { |
15 namespace { | |
16 | 10 |
17 class TestWorkerThread : public base::SimpleThread { | |
18 public: | |
19 TestWorkerThread() | |
20 : base::SimpleThread("test_worker_thread"), condition_(&lock_) {} | |
21 | |
22 void Run() override { | |
23 for (;;) { | |
24 base::AutoLock hold(lock_); | |
25 if (shutdown_) | |
26 break; | |
27 | |
28 if (queue_.empty()) { | |
29 condition_.Wait(); | |
30 continue; | |
31 } | |
32 | |
33 queue_.front().Run(); | |
34 queue_.erase(queue_.begin()); | |
35 } | |
36 } | |
37 | |
38 void Shutdown() { | |
39 base::AutoLock hold(lock_); | |
40 shutdown_ = true; | |
41 condition_.Signal(); | |
42 } | |
43 | |
44 void PostTask(const base::Closure& task) { | |
45 base::AutoLock hold(lock_); | |
46 queue_.push_back(task); | |
47 condition_.Signal(); | |
48 } | |
49 | |
50 private: | |
51 base::Lock lock_; | |
52 base::ConditionVariable condition_; | |
53 std::vector<base::Closure> queue_; | |
54 bool shutdown_ = false; | |
55 }; | |
56 | |
57 class WorkerTaskRunner : public base::SequencedTaskRunner { | |
58 public: | |
59 WorkerTaskRunner() { thread_.Start(); } | |
60 | |
61 bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here, | |
62 const base::Closure& task, | |
63 base::TimeDelta delay) override { | |
64 return PostDelayedTask(from_here, task, delay); | |
65 } | |
66 | |
67 bool PostDelayedTask(const tracked_objects::Location& from_here, | |
68 const base::Closure& task, | |
69 base::TimeDelta delay) override { | |
70 thread_.PostTask(task); | |
71 return true; | |
72 } | |
73 | |
74 bool RunsTasksOnCurrentThread() const override { return false; } | |
75 | |
76 protected: | |
77 ~WorkerTaskRunner() override { | |
78 thread_.Shutdown(); | |
79 thread_.Join(); | |
80 } | |
81 | |
82 TestWorkerThread thread_; | |
83 }; | |
84 | |
85 // Image decode cache with introspection! | |
86 class TestableCache : public ImageDecodeCache { | 11 class TestableCache : public ImageDecodeCache { |
87 public: | 12 public: |
88 ~TestableCache() override { EXPECT_EQ(number_of_refs_, 0); } | |
89 | |
90 bool GetTaskForImageAndRef(const DrawImage& image, | 13 bool GetTaskForImageAndRef(const DrawImage& image, |
91 const TracingInfo& tracing_info, | 14 const TracingInfo& tracing_info, |
92 scoped_refptr<TileTask>* task) override { | 15 scoped_refptr<TileTask>* task) override { |
93 *task = task_to_use_; | 16 *task = nullptr; |
94 ++number_of_refs_; | 17 ++number_of_refs_; |
95 return true; | 18 return true; |
96 } | 19 } |
97 bool GetOutOfRasterDecodeTaskForImageAndRef( | |
98 const DrawImage& image, | |
99 scoped_refptr<TileTask>* task) override { | |
100 *task = task_to_use_; | |
101 ++number_of_refs_; | |
102 return true; | |
103 } | |
104 | 20 |
105 void UnrefImage(const DrawImage& image) override { | 21 void UnrefImage(const DrawImage& image) override { |
106 ASSERT_GT(number_of_refs_, 0); | 22 ASSERT_GT(number_of_refs_, 0); |
107 --number_of_refs_; | 23 --number_of_refs_; |
108 } | 24 } |
109 DecodedDrawImage GetDecodedImageForDraw(const DrawImage& image) override { | 25 DecodedDrawImage GetDecodedImageForDraw(const DrawImage& image) override { |
110 return DecodedDrawImage(nullptr, kNone_SkFilterQuality); | 26 return DecodedDrawImage(nullptr, kNone_SkFilterQuality); |
111 } | 27 } |
112 void DrawWithImageFinished(const DrawImage& image, | 28 void DrawWithImageFinished(const DrawImage& image, |
113 const DecodedDrawImage& decoded_image) override {} | 29 const DecodedDrawImage& decoded_image) override {} |
114 void ReduceCacheUsage() override {} | 30 void ReduceCacheUsage() override {} |
115 void SetShouldAggressivelyFreeResources( | 31 void SetShouldAggressivelyFreeResources( |
116 bool aggressively_free_resources) override {} | 32 bool aggressively_free_resources) override {} |
117 | 33 |
118 int number_of_refs() const { return number_of_refs_; } | 34 int number_of_refs() const { return number_of_refs_; } |
119 void SetTaskToUse(scoped_refptr<TileTask> task) { task_to_use_ = task; } | |
120 | 35 |
121 private: | 36 private: |
122 int number_of_refs_ = 0; | 37 int number_of_refs_ = 0; |
123 scoped_refptr<TileTask> task_to_use_; | |
124 }; | 38 }; |
125 | 39 |
126 // A simple class that can receive decode callbacks. | 40 TEST(ImageControllerTest, NullCacheUnrefsImages) { |
127 class DecodeClient { | 41 TestableCache cache; |
128 public: | 42 ImageController controller; |
129 DecodeClient() {} | 43 controller.SetImageDecodeCache(&cache); |
130 void Callback(const base::Closure& quit_closure, | |
131 ImageController::ImageDecodeRequestId id) { | |
132 id_ = id; | |
133 quit_closure.Run(); | |
134 } | |
135 | 44 |
136 ImageController::ImageDecodeRequestId id() { return id_; } | |
137 | |
138 private: | |
139 ImageController::ImageDecodeRequestId id_ = 0; | |
140 }; | |
141 | |
142 // A dummy task that does nothing. | |
143 class SimpleTask : public TileTask { | |
144 public: | |
145 SimpleTask() : TileTask(true /* supports_concurrent_execution */) { | |
146 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); | |
147 } | |
148 | |
149 void RunOnWorkerThread() override { | |
150 EXPECT_FALSE(HasCompleted()); | |
151 EXPECT_FALSE(thread_checker_.CalledOnValidThread()); | |
152 has_run_ = true; | |
153 } | |
154 void OnTaskCompleted() override { | |
155 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); | |
156 } | |
157 | |
158 bool has_run() { return has_run_; } | |
159 | |
160 private: | |
161 ~SimpleTask() override = default; | |
162 | |
163 base::ThreadChecker thread_checker_; | |
164 bool has_run_ = false; | |
165 | |
166 DISALLOW_COPY_AND_ASSIGN(SimpleTask); | |
167 }; | |
168 | |
169 // A task that blocks until instructed otherwise. | |
170 class BlockingTask : public TileTask { | |
171 public: | |
172 BlockingTask() | |
173 : TileTask(true /* supports_concurrent_execution */), run_cv_(&lock_) { | |
174 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); | |
175 } | |
176 | |
177 void RunOnWorkerThread() override { | |
178 EXPECT_FALSE(HasCompleted()); | |
179 EXPECT_FALSE(thread_checker_.CalledOnValidThread()); | |
180 base::AutoLock hold(lock_); | |
181 if (!can_run_) | |
182 run_cv_.Wait(); | |
183 has_run_ = true; | |
184 } | |
185 | |
186 void OnTaskCompleted() override { | |
187 EXPECT_TRUE(thread_checker_.CalledOnValidThread()); | |
188 } | |
189 | |
190 void AllowToRun() { | |
191 base::AutoLock hold(lock_); | |
192 can_run_ = true; | |
193 run_cv_.Signal(); | |
194 } | |
195 | |
196 bool has_run() { return has_run_; } | |
197 | |
198 private: | |
199 ~BlockingTask() override = default; | |
200 | |
201 base::ThreadChecker thread_checker_; | |
202 bool has_run_ = false; | |
203 base::Lock lock_; | |
204 base::ConditionVariable run_cv_; | |
205 bool can_run_ = false; | |
206 | |
207 DISALLOW_COPY_AND_ASSIGN(BlockingTask); | |
208 }; | |
209 | |
210 // For tests that exercise image controller's thread, this is the timeout value | |
211 // to | |
212 // allow the worker thread to do its work. | |
213 int kDefaultTimeoutSeconds = 10; | |
214 | |
215 class ImageControllerTest : public testing::Test { | |
216 public: | |
217 ImageControllerTest() : task_runner_(base::SequencedTaskRunnerHandle::Get()) { | |
218 bitmap_.allocN32Pixels(1, 1); | |
219 image_ = SkImage::MakeFromBitmap(bitmap_); | |
220 } | |
221 ~ImageControllerTest() override = default; | |
222 | |
223 void SetUp() override { | |
224 worker_task_runner_ = make_scoped_refptr(new WorkerTaskRunner); | |
225 controller_.reset( | |
226 new ImageController(task_runner_.get(), worker_task_runner_)); | |
227 cache_ = TestableCache(); | |
228 controller_->SetImageDecodeCache(&cache_); | |
229 } | |
230 | |
231 void TearDown() override { | |
232 controller_.reset(); | |
233 worker_task_runner_ = nullptr; | |
234 } | |
235 | |
236 base::SequencedTaskRunner* task_runner() { return task_runner_.get(); } | |
237 ImageController* controller() { return controller_.get(); } | |
238 TestableCache* cache() { return &cache_; } | |
239 sk_sp<const SkImage> image() const { return image_; } | |
240 | |
241 // Timeout callback, which errors and exits the runloop. | |
242 static void Timeout(base::RunLoop* run_loop) { | |
243 ADD_FAILURE() << "Timeout."; | |
244 run_loop->Quit(); | |
245 } | |
246 | |
247 // Convenience method to run the run loop with a timeout. | |
248 void RunOrTimeout(base::RunLoop* run_loop) { | |
249 task_runner_->PostDelayedTask( | |
250 FROM_HERE, | |
251 base::Bind(&ImageControllerTest::Timeout, base::Unretained(run_loop)), | |
252 base::TimeDelta::FromSeconds(kDefaultTimeoutSeconds)); | |
253 run_loop->Run(); | |
254 } | |
255 | |
256 private: | |
257 scoped_refptr<base::SequencedTaskRunner> task_runner_; | |
258 scoped_refptr<WorkerTaskRunner> worker_task_runner_; | |
259 TestableCache cache_; | |
260 std::unique_ptr<ImageController> controller_; | |
261 SkBitmap bitmap_; | |
262 sk_sp<const SkImage> image_; | |
263 }; | |
264 | |
265 TEST_F(ImageControllerTest, NullControllerUnrefsImages) { | |
266 std::vector<DrawImage> images(10); | 45 std::vector<DrawImage> images(10); |
267 ImageDecodeCache::TracingInfo tracing_info; | 46 ImageDecodeCache::TracingInfo tracing_info; |
268 | 47 |
269 ASSERT_EQ(10u, images.size()); | 48 ASSERT_EQ(10u, images.size()); |
270 auto tasks = | 49 auto tasks = controller.SetPredecodeImages(std::move(images), tracing_info); |
271 controller()->SetPredecodeImages(std::move(images), tracing_info); | |
272 EXPECT_EQ(0u, tasks.size()); | 50 EXPECT_EQ(0u, tasks.size()); |
273 EXPECT_EQ(10, cache()->number_of_refs()); | 51 EXPECT_EQ(10, cache.number_of_refs()); |
274 | 52 |
275 controller()->SetImageDecodeCache(nullptr); | 53 controller.SetImageDecodeCache(nullptr); |
276 EXPECT_EQ(0, cache()->number_of_refs()); | 54 EXPECT_EQ(0, cache.number_of_refs()); |
277 } | 55 } |
278 | 56 |
279 TEST_F(ImageControllerTest, QueueImageDecode) { | |
280 base::RunLoop run_loop; | |
281 DecodeClient decode_client; | |
282 EXPECT_EQ(image()->bounds().width(), 1); | |
283 ImageController::ImageDecodeRequestId expected_id = | |
284 controller()->QueueImageDecode( | |
285 image(), | |
286 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client), | |
287 run_loop.QuitClosure())); | |
288 RunOrTimeout(&run_loop); | |
289 EXPECT_EQ(expected_id, decode_client.id()); | |
290 } | |
291 | |
292 TEST_F(ImageControllerTest, QueueImageDecodeMultipleImages) { | |
293 base::RunLoop run_loop; | |
294 DecodeClient decode_client1; | |
295 ImageController::ImageDecodeRequestId expected_id1 = | |
296 controller()->QueueImageDecode( | |
297 image(), | |
298 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), | |
299 base::Bind([] {}))); | |
300 DecodeClient decode_client2; | |
301 ImageController::ImageDecodeRequestId expected_id2 = | |
302 controller()->QueueImageDecode( | |
303 image(), | |
304 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), | |
305 base::Bind([] {}))); | |
306 DecodeClient decode_client3; | |
307 ImageController::ImageDecodeRequestId expected_id3 = | |
308 controller()->QueueImageDecode( | |
309 image(), | |
310 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client3), | |
311 run_loop.QuitClosure())); | |
312 RunOrTimeout(&run_loop); | |
313 EXPECT_EQ(expected_id1, decode_client1.id()); | |
314 EXPECT_EQ(expected_id2, decode_client2.id()); | |
315 EXPECT_EQ(expected_id3, decode_client3.id()); | |
316 } | |
317 | |
318 TEST_F(ImageControllerTest, QueueImageDecodeWithTask) { | |
319 scoped_refptr<SimpleTask> task(new SimpleTask); | |
320 cache()->SetTaskToUse(task); | |
321 | |
322 base::RunLoop run_loop; | |
323 DecodeClient decode_client; | |
324 ImageController::ImageDecodeRequestId expected_id = | |
325 controller()->QueueImageDecode( | |
326 image(), | |
327 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client), | |
328 run_loop.QuitClosure())); | |
329 RunOrTimeout(&run_loop); | |
330 EXPECT_EQ(expected_id, decode_client.id()); | |
331 EXPECT_TRUE(task->has_run()); | |
332 EXPECT_TRUE(task->HasCompleted()); | |
333 } | |
334 | |
335 TEST_F(ImageControllerTest, QueueImageDecodeMultipleImagesSameTask) { | |
336 scoped_refptr<SimpleTask> task(new SimpleTask); | |
337 cache()->SetTaskToUse(task); | |
338 | |
339 base::RunLoop run_loop; | |
340 DecodeClient decode_client1; | |
341 ImageController::ImageDecodeRequestId expected_id1 = | |
342 controller()->QueueImageDecode( | |
343 image(), | |
344 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), | |
345 base::Bind([] {}))); | |
346 DecodeClient decode_client2; | |
347 ImageController::ImageDecodeRequestId expected_id2 = | |
348 controller()->QueueImageDecode( | |
349 image(), | |
350 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), | |
351 base::Bind([] {}))); | |
352 DecodeClient decode_client3; | |
353 ImageController::ImageDecodeRequestId expected_id3 = | |
354 controller()->QueueImageDecode( | |
355 image(), | |
356 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client3), | |
357 run_loop.QuitClosure())); | |
358 RunOrTimeout(&run_loop); | |
359 EXPECT_EQ(expected_id1, decode_client1.id()); | |
360 EXPECT_EQ(expected_id2, decode_client2.id()); | |
361 EXPECT_EQ(expected_id3, decode_client3.id()); | |
362 EXPECT_TRUE(task->has_run()); | |
363 EXPECT_TRUE(task->HasCompleted()); | |
364 } | |
365 | |
366 TEST_F(ImageControllerTest, QueueImageDecodeChangeControllerWithTaskQueued) { | |
367 scoped_refptr<BlockingTask> task_one(new BlockingTask); | |
368 cache()->SetTaskToUse(task_one); | |
369 | |
370 DecodeClient decode_client1; | |
371 ImageController::ImageDecodeRequestId expected_id1 = | |
372 controller()->QueueImageDecode( | |
373 image(), | |
374 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), | |
375 base::Bind([] {}))); | |
376 | |
377 scoped_refptr<BlockingTask> task_two(new BlockingTask); | |
378 cache()->SetTaskToUse(task_two); | |
379 | |
380 base::RunLoop run_loop; | |
381 DecodeClient decode_client2; | |
382 ImageController::ImageDecodeRequestId expected_id2 = | |
383 controller()->QueueImageDecode( | |
384 image(), | |
385 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), | |
386 run_loop.QuitClosure())); | |
387 | |
388 task_one->AllowToRun(); | |
389 task_two->AllowToRun(); | |
390 controller()->SetImageDecodeCache(nullptr); | |
391 | |
392 RunOrTimeout(&run_loop); | |
393 | |
394 EXPECT_TRUE(task_one->state().IsCanceled() || task_one->HasCompleted()); | |
395 EXPECT_TRUE(task_two->state().IsCanceled() || task_two->HasCompleted()); | |
396 EXPECT_EQ(expected_id1, decode_client1.id()); | |
397 EXPECT_EQ(expected_id2, decode_client2.id()); | |
398 } | |
399 | |
400 TEST_F(ImageControllerTest, QueueImageDecodeImageAlreadyLocked) { | |
401 scoped_refptr<SimpleTask> task(new SimpleTask); | |
402 cache()->SetTaskToUse(task); | |
403 | |
404 base::RunLoop run_loop1; | |
405 DecodeClient decode_client1; | |
406 ImageController::ImageDecodeRequestId expected_id1 = | |
407 controller()->QueueImageDecode( | |
408 image(), | |
409 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), | |
410 run_loop1.QuitClosure())); | |
411 RunOrTimeout(&run_loop1); | |
412 EXPECT_EQ(expected_id1, decode_client1.id()); | |
413 EXPECT_TRUE(task->has_run()); | |
414 | |
415 cache()->SetTaskToUse(nullptr); | |
416 base::RunLoop run_loop2; | |
417 DecodeClient decode_client2; | |
418 ImageController::ImageDecodeRequestId expected_id2 = | |
419 controller()->QueueImageDecode( | |
420 image(), | |
421 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client2), | |
422 run_loop2.QuitClosure())); | |
423 RunOrTimeout(&run_loop2); | |
424 EXPECT_EQ(expected_id2, decode_client2.id()); | |
425 } | |
426 | |
427 TEST_F(ImageControllerTest, QueueImageDecodeLockedImageControllerChange) { | |
428 scoped_refptr<SimpleTask> task(new SimpleTask); | |
429 cache()->SetTaskToUse(task); | |
430 | |
431 base::RunLoop run_loop1; | |
432 DecodeClient decode_client1; | |
433 ImageController::ImageDecodeRequestId expected_id1 = | |
434 controller()->QueueImageDecode( | |
435 image(), | |
436 base::Bind(&DecodeClient::Callback, base::Unretained(&decode_client1), | |
437 run_loop1.QuitClosure())); | |
438 RunOrTimeout(&run_loop1); | |
439 EXPECT_EQ(expected_id1, decode_client1.id()); | |
440 EXPECT_TRUE(task->has_run()); | |
441 EXPECT_EQ(1, cache()->number_of_refs()); | |
442 | |
443 controller()->SetImageDecodeCache(nullptr); | |
444 EXPECT_EQ(0, cache()->number_of_refs()); | |
445 } | |
446 | |
447 } // namespace | |
448 } // namespace cc | 57 } // namespace cc |
OLD | NEW |