1 // Copyright 2016 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "include/base/cef_bind.h"
6 #include "include/cef_task.h"
7 #include "include/cef_thread.h"
8 #include "include/wrapper/cef_closure_task.h"
9 #include "tests/ceftests/test_handler.h"
10 #include "tests/gtest/include/gtest/gtest.h"
11 #include "tests/shared/browser/client_app_browser.h"
12 #include "tests/shared/renderer/client_app_renderer.h"
13
14 using client::ClientAppBrowser;
15 using client::ClientAppRenderer;
16
17 namespace {
18
19 // Base class for creating and testing threads.
20 class ThreadTest : public base::RefCountedThreadSafe<ThreadTest> {
21 public:
ThreadTest()22 ThreadTest() {}
~ThreadTest()23 virtual ~ThreadTest() {}
24
25 // Create the test thread. Should only be called one time.
CreateTestThread()26 void CreateTestThread() {
27 EXPECT_TRUE(!thread_.get());
28
29 owner_task_runner_ = CefTaskRunner::GetForCurrentThread();
30 EXPECT_TRUE(owner_task_runner_.get());
31 EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
32
33 thread_ = CefThread::CreateThread("test_thread");
34 EXPECT_TRUE(thread_.get());
35 EXPECT_TRUE(thread_->IsRunning());
36
37 thread_id_ = thread_->GetPlatformThreadId();
38 EXPECT_NE(thread_id_, kInvalidPlatformThreadId);
39
40 thread_task_runner_ = thread_->GetTaskRunner();
41 EXPECT_TRUE(thread_task_runner_.get());
42
43 AssertOwnerThread();
44 }
45
46 // Destroy the test thread. Should only be called one time.
DestroyTestThread()47 void DestroyTestThread() {
48 EXPECT_TRUE(thread_.get());
49 AssertOwnerThread();
50
51 EXPECT_TRUE(thread_->IsRunning());
52 thread_->Stop();
53 EXPECT_FALSE(thread_->IsRunning());
54
55 AssertOwnerThread();
56
57 thread_ = nullptr;
58 }
59
60 // Execute |test_task| on the test thread. After execution |callback| will be
61 // posted to |callback_task_runner|.
PostOnTestThreadAndCallback(const base::Closure & test_task,CefRefPtr<CefTaskRunner> callback_task_runner,const base::Closure & callback)62 void PostOnTestThreadAndCallback(
63 const base::Closure& test_task,
64 CefRefPtr<CefTaskRunner> callback_task_runner,
65 const base::Closure& callback) {
66 EXPECT_TRUE(thread_.get());
67 thread_task_runner_->PostTask(CefCreateClosureTask(
68 base::Bind(&ThreadTest::ExecuteOnTestThread, this, test_task,
69 callback_task_runner, callback)));
70 }
71
owner_task_runner() const72 CefRefPtr<CefTaskRunner> owner_task_runner() const {
73 return owner_task_runner_;
74 }
thread_task_runner() const75 CefRefPtr<CefTaskRunner> thread_task_runner() const {
76 return thread_task_runner_;
77 }
78
79 // Assert that we're running on the owner thread.
AssertOwnerThread()80 void AssertOwnerThread() {
81 EXPECT_TRUE(owner_task_runner_->BelongsToCurrentThread());
82 EXPECT_FALSE(thread_task_runner_->BelongsToCurrentThread());
83 EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
84 EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
85 }
86
87 // Assert that we're running on the test thread.
AssertTestThread()88 void AssertTestThread() {
89 EXPECT_FALSE(owner_task_runner_->BelongsToCurrentThread());
90 EXPECT_TRUE(thread_task_runner_->BelongsToCurrentThread());
91 EXPECT_TRUE(thread_task_runner_->IsSame(thread_->GetTaskRunner()));
92 EXPECT_EQ(thread_id_, thread_->GetPlatformThreadId());
93 }
94
95 private:
96 // Helper for PostOnTestThreadAndCallback().
ExecuteOnTestThread(const base::Closure & test_task,CefRefPtr<CefTaskRunner> callback_task_runner,const base::Closure & callback)97 void ExecuteOnTestThread(const base::Closure& test_task,
98 CefRefPtr<CefTaskRunner> callback_task_runner,
99 const base::Closure& callback) {
100 AssertTestThread();
101
102 test_task.Run();
103
104 callback_task_runner->PostTask(CefCreateClosureTask(callback));
105 }
106
107 CefRefPtr<CefTaskRunner> owner_task_runner_;
108
109 CefRefPtr<CefThread> thread_;
110 cef_platform_thread_id_t thread_id_;
111 CefRefPtr<CefTaskRunner> thread_task_runner_;
112
113 DISALLOW_COPY_AND_ASSIGN(ThreadTest);
114 };
115
116 } // namespace
117
118 // Test thread creation and destruction without any task execution.
TEST(ThreadTest,Create)119 TEST(ThreadTest, Create) {
120 scoped_refptr<ThreadTest> thread_test = new ThreadTest();
121 thread_test->CreateTestThread();
122 thread_test->DestroyTestThread();
123 thread_test = nullptr;
124 }
125
126 namespace {
127
128 // Simple implementation of ThreadTest that creates a thread, executes tasks
129 // on the thread, then destroys the thread after all tasks have completed.
130 class SimpleThreadTest : public ThreadTest {
131 public:
SimpleThreadTest(size_t expected_task_count,const base::Closure & task_callback,const base::Closure & done_callback)132 SimpleThreadTest(size_t expected_task_count,
133 const base::Closure& task_callback,
134 const base::Closure& done_callback)
135 : expected_task_count_(expected_task_count),
136 task_callback_(task_callback),
137 done_callback_(done_callback),
138 got_task_count_(0U),
139 got_done_count_(0U) {}
140
RunTest()141 void RunTest() {
142 // Create the test thread.
143 CreateTestThread();
144
145 for (size_t i = 0U; i < expected_task_count_; ++i) {
146 // Execute Task() on the test thread and then call Done() on this thread.
147 PostOnTestThreadAndCallback(base::Bind(&SimpleThreadTest::Task, this),
148 owner_task_runner(),
149 base::Bind(&SimpleThreadTest::Done, this));
150 }
151 }
152
DestroyTest()153 void DestroyTest() {
154 EXPECT_EQ(expected_task_count_, got_task_count_);
155 EXPECT_EQ(expected_task_count_, got_done_count_);
156
157 // Destroy the test thread.
158 DestroyTestThread();
159 }
160
161 private:
Task()162 void Task() {
163 AssertTestThread();
164 got_task_count_++;
165 if (!task_callback_.is_null())
166 task_callback_.Run();
167 }
168
Done()169 void Done() {
170 AssertOwnerThread();
171 if (++got_done_count_ == expected_task_count_ && !done_callback_.is_null())
172 done_callback_.Run();
173 }
174
175 const size_t expected_task_count_;
176 base::Closure task_callback_;
177 base::Closure done_callback_;
178
179 size_t got_task_count_;
180 size_t got_done_count_;
181
182 DISALLOW_COPY_AND_ASSIGN(SimpleThreadTest);
183 };
184
185 // Test creation/execution of threads in the browser process.
186
187 const char kBrowserThreadTestHtml[] = "http://test.com/browserthread.html";
188
189 // Browser side.
190 class BrowserThreadTestHandler : public TestHandler {
191 public:
BrowserThreadTestHandler(CefThreadId owner_thread_id)192 explicit BrowserThreadTestHandler(CefThreadId owner_thread_id)
193 : owner_thread_id_(owner_thread_id) {}
194
RunTest()195 void RunTest() override {
196 AddResource(kBrowserThreadTestHtml, "<html><body>Test</body></html>",
197 "text/html");
198
199 CreateBrowser(kBrowserThreadTestHtml);
200
201 // Time out the test after a reasonable period of time.
202 SetTestTimeout();
203 }
204
RunThreadTestOnOwnerThread()205 void RunThreadTestOnOwnerThread() {
206 if (!CefCurrentlyOn(owner_thread_id_)) {
207 // Run the test on the desired owner thread.
208 CefPostTask(
209 owner_thread_id_,
210 base::Bind(&BrowserThreadTestHandler::RunThreadTestOnOwnerThread,
211 this));
212 return;
213 }
214
215 EXPECT_FALSE(thread_test_.get());
216 thread_test_ = new SimpleThreadTest(
217 3, base::Closure(),
218 base::Bind(&BrowserThreadTestHandler::DoneOnOwnerThread, this));
219 thread_test_->RunTest();
220 }
221
DoneOnOwnerThread()222 void DoneOnOwnerThread() {
223 // Let the call stack unwind before destroying |thread_test_|.
224 CefPostTask(
225 owner_thread_id_,
226 base::Bind(&BrowserThreadTestHandler::DestroyTestOnOwnerThread, this));
227 }
228
DestroyTestOnOwnerThread()229 void DestroyTestOnOwnerThread() {
230 EXPECT_TRUE(CefCurrentlyOn(owner_thread_id_));
231
232 EXPECT_TRUE(thread_test_.get());
233 if (thread_test_) {
234 thread_test_->DestroyTest();
235 thread_test_ = nullptr;
236 }
237
238 got_test_done_.yes();
239
240 // Call DestroyTest() on the UI thread.
241 CefPostTask(TID_UI,
242 base::Bind(&BrowserThreadTestHandler::DestroyTest, this));
243 }
244
OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward)245 void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
246 bool isLoading,
247 bool canGoBack,
248 bool canGoForward) override {
249 if (!isLoading)
250 RunThreadTestOnOwnerThread();
251 }
252
253 private:
DestroyTest()254 void DestroyTest() override {
255 EXPECT_FALSE(thread_test_.get());
256 EXPECT_TRUE(got_test_done_);
257
258 TestHandler::DestroyTest();
259 }
260
261 const CefThreadId owner_thread_id_;
262
263 scoped_refptr<SimpleThreadTest> thread_test_;
264 TrackCallback got_test_done_;
265
266 IMPLEMENT_REFCOUNTING(BrowserThreadTestHandler);
267 DISALLOW_COPY_AND_ASSIGN(BrowserThreadTestHandler);
268 };
269
270 } // namespace
271
272 // Test creation of new threads from the browser UI thread.
TEST(ThreadTest,CreateFromBrowserUIThread)273 TEST(ThreadTest, CreateFromBrowserUIThread) {
274 CefRefPtr<BrowserThreadTestHandler> handler =
275 new BrowserThreadTestHandler(TID_UI);
276 handler->ExecuteTest();
277 ReleaseAndWaitForDestructor(handler);
278 }
279
280 // Test creation of new threads from the browser IO thread.
TEST(ThreadTest,CreateFromBrowserIOThread)281 TEST(ThreadTest, CreateFromBrowserIOThread) {
282 CefRefPtr<BrowserThreadTestHandler> handler =
283 new BrowserThreadTestHandler(TID_IO);
284 handler->ExecuteTest();
285 ReleaseAndWaitForDestructor(handler);
286 }
287
288 // Test creation of new threads from the browser FILE thread.
TEST(ThreadTest,CreateFromBrowserFILEThread)289 TEST(ThreadTest, CreateFromBrowserFILEThread) {
290 // Use a FILE thread that will run tasks relatively quickly.
291 CefRefPtr<BrowserThreadTestHandler> handler =
292 new BrowserThreadTestHandler(TID_FILE_USER_VISIBLE);
293 handler->ExecuteTest();
294 ReleaseAndWaitForDestructor(handler);
295 }
296
297 namespace {
298
299 // Test creation/execution of threads in the render process.
300
301 const char kRenderThreadTestHtml[] = "http://test.com/renderthread.html";
302 const char kRenderThreadTestMsg[] = "ThreadTest.RenderThreadTest";
303
304 // Browser side.
305 class RenderThreadTestHandler : public TestHandler {
306 public:
RenderThreadTestHandler()307 RenderThreadTestHandler() {}
308
RunTest()309 void RunTest() override {
310 AddResource(kRenderThreadTestHtml, "<html><body>Test</body></html>",
311 "text/html");
312
313 CreateBrowser(kRenderThreadTestHtml);
314
315 // Time out the test after a reasonable period of time.
316 SetTestTimeout();
317 }
318
OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward)319 void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
320 bool isLoading,
321 bool canGoBack,
322 bool canGoForward) override {
323 if (!isLoading) {
324 // Return the test in the render process.
325 CefRefPtr<CefProcessMessage> msg =
326 CefProcessMessage::Create(kRenderThreadTestMsg);
327 browser->GetMainFrame()->SendProcessMessage(PID_RENDERER, msg);
328 }
329 }
330
OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)331 bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
332 CefRefPtr<CefFrame> frame,
333 CefProcessId source_process,
334 CefRefPtr<CefProcessMessage> message) override {
335 EXPECT_TRUE(browser.get());
336 EXPECT_TRUE(frame.get());
337 EXPECT_EQ(PID_RENDERER, source_process);
338 EXPECT_TRUE(message.get());
339 EXPECT_TRUE(message->IsReadOnly());
340
341 const std::string& message_name = message->GetName();
342 EXPECT_STREQ(kRenderThreadTestMsg, message_name.c_str());
343
344 got_message_.yes();
345
346 if (message->GetArgumentList()->GetBool(0))
347 got_success_.yes();
348
349 // Test is complete.
350 DestroyTest();
351
352 return true;
353 }
354
355 protected:
DestroyTest()356 void DestroyTest() override {
357 EXPECT_TRUE(got_message_);
358 EXPECT_TRUE(got_success_);
359
360 TestHandler::DestroyTest();
361 }
362
363 TrackCallback got_message_;
364 TrackCallback got_success_;
365
366 IMPLEMENT_REFCOUNTING(RenderThreadTestHandler);
367 DISALLOW_COPY_AND_ASSIGN(RenderThreadTestHandler);
368 };
369
370 // Renderer side.
371 class RenderThreadRendererTest : public ClientAppRenderer::Delegate {
372 public:
RenderThreadRendererTest()373 RenderThreadRendererTest() {}
374
OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)375 bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
376 CefRefPtr<CefBrowser> browser,
377 CefRefPtr<CefFrame> frame,
378 CefProcessId source_process,
379 CefRefPtr<CefProcessMessage> message) override {
380 if (message->GetName().ToString() == kRenderThreadTestMsg) {
381 browser_ = browser;
382 EXPECT_FALSE(thread_test_.get());
383 thread_test_ = new SimpleThreadTest(
384 3, base::Closure(),
385 base::Bind(&RenderThreadRendererTest::Done, this));
386 thread_test_->RunTest();
387 return true;
388 }
389
390 // Message not handled.
391 return false;
392 }
393
394 private:
Done()395 void Done() {
396 // Let the call stack unwind before destroying |thread_test_|.
397 CefPostTask(TID_RENDERER,
398 base::Bind(&RenderThreadRendererTest::DestroyTest, this));
399 }
400
DestroyTest()401 void DestroyTest() {
402 EXPECT_TRUE(thread_test_.get());
403 if (thread_test_) {
404 thread_test_->DestroyTest();
405 thread_test_ = nullptr;
406 }
407
408 // Check if the test has failed.
409 bool result = !TestFailed();
410
411 // Return the result to the browser process.
412 CefRefPtr<CefProcessMessage> return_msg =
413 CefProcessMessage::Create(kRenderThreadTestMsg);
414 EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
415 browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
416
417 browser_ = nullptr;
418 }
419
420 CefRefPtr<CefBrowser> browser_;
421 scoped_refptr<SimpleThreadTest> thread_test_;
422
423 IMPLEMENT_REFCOUNTING(RenderThreadRendererTest);
424 DISALLOW_COPY_AND_ASSIGN(RenderThreadRendererTest);
425 };
426
427 } // namespace
428
TEST(ThreadTest,CreateFromRenderThread)429 TEST(ThreadTest, CreateFromRenderThread) {
430 CefRefPtr<RenderThreadTestHandler> handler = new RenderThreadTestHandler();
431 handler->ExecuteTest();
432 ReleaseAndWaitForDestructor(handler);
433 }
434
435 // Entry point for creating request handler renderer test objects.
436 // Called from client_app_delegates.cc.
CreateThreadRendererTests(ClientAppRenderer::DelegateSet & delegates)437 void CreateThreadRendererTests(ClientAppRenderer::DelegateSet& delegates) {
438 delegates.insert(new RenderThreadRendererTest);
439 }
440