From ee4e036af8ad3762b003c10ab28fc58cc41ceec7 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Wed, 19 Aug 2020 18:27:27 -0400 Subject: [PATCH] ceftests: Add shared test_request and test_server implementations To reduce text execution time and flakyness this change also introduces a global test server that is initialized when needed and torn down after all tests have completed. --- cef_paths2.gypi | 9 + tests/ceftests/cookie_unittest.cc | 136 +++---- tests/ceftests/run_all_unittests.cc | 13 +- tests/ceftests/server_unittest.cc | 7 +- tests/ceftests/test_request.cc | 109 ++++++ tests/ceftests/test_request.h | 69 ++++ tests/ceftests/test_server.cc | 492 +++++++++++++++++++++++++ tests/ceftests/test_server.h | 119 ++++++ tests/ceftests/test_server_unittest.cc | 257 +++++++++++++ tests/ceftests/urlrequest_unittest.cc | 305 +++++---------- 10 files changed, 1201 insertions(+), 315 deletions(-) create mode 100644 tests/ceftests/test_request.cc create mode 100644 tests/ceftests/test_request.h create mode 100644 tests/ceftests/test_server.cc create mode 100644 tests/ceftests/test_server.h create mode 100644 tests/ceftests/test_server_unittest.cc diff --git a/cef_paths2.gypi b/cef_paths2.gypi index 3ec16e377..1c5663d55 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -520,6 +520,11 @@ 'tests/ceftests/task_unittest.cc', 'tests/ceftests/test_handler.cc', 'tests/ceftests/test_handler.h', + 'tests/ceftests/test_request.cc', + 'tests/ceftests/test_request.h', + 'tests/ceftests/test_server.cc', + 'tests/ceftests/test_server.h', + 'tests/ceftests/test_server_unittest.cc', 'tests/ceftests/test_suite.cc', 'tests/ceftests/test_suite.h', 'tests/ceftests/test_util.cc', @@ -588,6 +593,10 @@ 'tests/ceftests/urlrequest_unittest.cc', 'tests/ceftests/test_handler.cc', 'tests/ceftests/test_handler.h', + 'tests/ceftests/test_request.cc', + 'tests/ceftests/test_request.h', + 'tests/ceftests/test_server.cc', + 'tests/ceftests/test_server.h', 'tests/ceftests/test_suite.cc', 'tests/ceftests/test_suite.h', 'tests/ceftests/test_util.cc', diff --git a/tests/ceftests/cookie_unittest.cc b/tests/ceftests/cookie_unittest.cc index e4672a1c7..5f3141f95 100644 --- a/tests/ceftests/cookie_unittest.cc +++ b/tests/ceftests/cookie_unittest.cc @@ -15,6 +15,7 @@ #include "include/wrapper/cef_closure_task.h" #include "tests/ceftests/routing_test_handler.h" #include "tests/ceftests/test_handler.h" +#include "tests/ceftests/test_server.h" #include "tests/ceftests/test_suite.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" @@ -1050,30 +1051,29 @@ namespace { const char kCookieAccessScheme[] = "http"; const char kCookieAccessDomain[] = "test-cookies.com"; -const char kCookieAccessServerIP[] = "127.0.0.1"; -const uint16 kCookieAccessServerPort = 8099; +const char* kCookieAccessServerAddress = test_server::kServerAddress; +const uint16 kCookieAccessServerPort = test_server::kServerPort; std::string GetCookieAccessOrigin(const std::string& scheme, bool server_backend) { std::stringstream ss; if (server_backend) { - ss << scheme << "://" << kCookieAccessServerIP << ":" + ss << scheme << "://" << kCookieAccessServerAddress << ":" << kCookieAccessServerPort; } else { ss << scheme << "://" << kCookieAccessDomain; } - ss << "/"; return ss.str(); } std::string GetCookieAccessUrl1(const std::string& scheme, bool server_backend) { - return GetCookieAccessOrigin(scheme, server_backend) + "cookie1.html"; + return GetCookieAccessOrigin(scheme, server_backend) + "/cookie1.html"; } std::string GetCookieAccessUrl2(const std::string& scheme, bool server_backend) { - return GetCookieAccessOrigin(scheme, server_backend) + "cookie2.html"; + return GetCookieAccessOrigin(scheme, server_backend) + "/cookie2.html"; } void TestCookieString(const std::string& cookie_str, @@ -1248,7 +1248,7 @@ class CookieAccessSchemeHandlerFactory : public CefSchemeHandlerFactory, }; // HTTP server handler. -class CookieAccessServerHandler : public CefServerHandler, +class CookieAccessServerHandler : public test_server::ObserverHelper, public CookieAccessResponseHandler { public: CookieAccessServerHandler() @@ -1288,8 +1288,7 @@ class CookieAccessServerHandler : public CefServerHandler, EXPECT_TRUE(complete_callback_.is_null()); complete_callback_ = complete_callback; - CefServer::CreateServer(kCookieAccessServerIP, kCookieAccessServerPort, 10, - this); + Initialize(); } // Results in a call to VerifyResults() and eventual execution of the @@ -1301,73 +1300,60 @@ class CookieAccessServerHandler : public CefServerHandler, EXPECT_TRUE(complete_callback_.is_null()); complete_callback_ = complete_callback; - EXPECT_TRUE(server_); - if (server_) - server_->Shutdown(); + Shutdown(); } - void OnServerCreated(CefRefPtr server) override { - EXPECT_TRUE(server); - EXPECT_TRUE(server->IsRunning()); - EXPECT_FALSE(server->HasConnection()); + void OnInitialized(const std::string& server_origin) override { + EXPECT_UI_THREAD(); + EXPECT_STREQ(server_origin.c_str(), + GetCookieAccessOrigin(kCookieAccessScheme, true).c_str()); EXPECT_FALSE(got_server_created_); got_server_created_.yes(); - EXPECT_FALSE(server_); - server_ = server; - - EXPECT_FALSE(server_runner_); - server_runner_ = server_->GetTaskRunner(); - EXPECT_TRUE(server_runner_); - EXPECT_TRUE(server_runner_->BelongsToCurrentThread()); - - CefPostTask( - TID_UI, - base::Bind(&CookieAccessServerHandler::RunCompleteCallback, this)); + RunCompleteCallback(); } - void OnServerDestroyed(CefRefPtr server) override { - EXPECT_TRUE(VerifyServer(server)); - EXPECT_FALSE(server->IsRunning()); - EXPECT_FALSE(server->HasConnection()); + void OnShutdown() override { + EXPECT_UI_THREAD(); EXPECT_FALSE(got_server_destroyed_); got_server_destroyed_.yes(); - server_ = nullptr; - VerifyResults(); + + delete this; } - void OnClientConnected(CefRefPtr server, + bool OnClientConnected(CefRefPtr server, int connection_id) override { - EXPECT_TRUE(VerifyServer(server)); - EXPECT_TRUE(server->HasConnection()); - EXPECT_TRUE(server->IsValidConnection(connection_id)); + EXPECT_UI_THREAD(); EXPECT_TRUE(connection_id_set_.find(connection_id) == connection_id_set_.end()); connection_id_set_.insert(connection_id); actual_connection_ct_++; + + return true; } - void OnClientDisconnected(CefRefPtr server, + bool OnClientDisconnected(CefRefPtr server, int connection_id) override { - EXPECT_TRUE(VerifyServer(server)); - EXPECT_FALSE(server->IsValidConnection(connection_id)); + EXPECT_UI_THREAD(); ConnectionIdSet::iterator it = connection_id_set_.find(connection_id); EXPECT_TRUE(it != connection_id_set_.end()); connection_id_set_.erase(it); + + return true; } - void OnHttpRequest(CefRefPtr server, + bool OnHttpRequest(CefRefPtr server, int connection_id, const CefString& client_address, CefRefPtr request) override { - EXPECT_TRUE(VerifyServer(server)); + EXPECT_UI_THREAD(); EXPECT_TRUE(VerifyConnection(connection_id)); EXPECT_FALSE(client_address.empty()); @@ -1378,50 +1364,16 @@ class CookieAccessServerHandler : public CefServerHandler, HandleRequest(server, connection_id, request); actual_http_request_ct_++; - } - void OnWebSocketRequest(CefRefPtr server, - int connection_id, - const CefString& client_address, - CefRefPtr request, - CefRefPtr callback) override { - NOTREACHED(); - } - - void OnWebSocketConnected(CefRefPtr server, - int connection_id) override { - NOTREACHED(); - } - - void OnWebSocketMessage(CefRefPtr server, - int connection_id, - const void* data, - size_t data_size) override { - NOTREACHED(); + return true; } private: - bool RunningOnServerThread() { - return server_runner_ && server_runner_->BelongsToCurrentThread(); - } - - bool VerifyServer(CefRefPtr server) { - V_DECLARE(); - V_EXPECT_TRUE(RunningOnServerThread()); - V_EXPECT_TRUE(server); - V_EXPECT_TRUE(server_); - V_EXPECT_TRUE(server->GetAddress().ToString() == - server_->GetAddress().ToString()); - V_RETURN(); - } - bool VerifyConnection(int connection_id) { return connection_id_set_.find(connection_id) != connection_id_set_.end(); } void VerifyResults() { - EXPECT_TRUE(RunningOnServerThread()); - EXPECT_TRUE(got_server_created_); EXPECT_TRUE(got_server_destroyed_); EXPECT_TRUE(connection_id_set_.empty()); @@ -1453,10 +1405,19 @@ class CookieAccessServerHandler : public CefServerHandler, } } - void SendResponse(CefRefPtr server, - int connection_id, - CefRefPtr response, - const std::string& response_data) { + static void SendResponse(CefRefPtr server, + int connection_id, + CefRefPtr response, + const std::string& response_data) { + // Execute on the server thread because some methods require it. + CefRefPtr task_runner = server->GetTaskRunner(); + if (!task_runner->BelongsToCurrentThread()) { + task_runner->PostTask(CefCreateClosureTask( + base::Bind(CookieAccessServerHandler::SendResponse, server, + connection_id, response, response_data))); + return; + } + int response_code = response->GetStatus(); const CefString& content_type = response->GetMimeType(); int64 content_length = static_cast(response_data.size()); @@ -1489,8 +1450,6 @@ class CookieAccessServerHandler : public CefServerHandler, typedef std::map ResponseDataMap; ResponseDataMap data_map_; - CefRefPtr server_; - CefRefPtr server_runner_; bool initialized_; // Only accessed on the UI thread. @@ -1512,7 +1471,6 @@ class CookieAccessServerHandler : public CefServerHandler, std::string request_log_; - IMPLEMENT_REFCOUNTING(CookieAccessServerHandler); DISALLOW_COPY_AND_ASSIGN(CookieAccessServerHandler); }; @@ -1833,7 +1791,7 @@ class CookieAccessTestHandler : public RoutingTestHandler, EXPECT_FALSE(server_handler_); server_handler_ = new CookieAccessServerHandler(); - AddResponses(server_handler_.get()); + AddResponses(server_handler_); server_handler_->CreateServer(complete_callback); } @@ -1911,6 +1869,7 @@ class CookieAccessTestHandler : public RoutingTestHandler, void ShutdownServer(const base::Closure& complete_callback) { EXPECT_TRUE(server_handler_); + // |server_handler_| will delete itself after shutdown. server_handler_->ShutdownServer(complete_callback); server_handler_ = nullptr; } @@ -1933,7 +1892,7 @@ class CookieAccessTestHandler : public RoutingTestHandler, CefRefPtr context_; CefRefPtr cookie_manager_; - CefRefPtr server_handler_; + CookieAccessServerHandler* server_handler_ = nullptr; CefRefPtr scheme_factory_; CookieAccessData data1_; @@ -2292,7 +2251,7 @@ class CookieRestartTestHandler : public RoutingTestHandler, EXPECT_FALSE(server_handler_); server_handler_ = new CookieAccessServerHandler(); - AddResponses(server_handler_.get()); + AddResponses(server_handler_); // 2 requests for each URL. server_handler_->SetExpectedRequestCount(4); server_handler_->CreateServer(complete_callback); @@ -2351,6 +2310,7 @@ class CookieRestartTestHandler : public RoutingTestHandler, void ShutdownServer(const base::Closure& complete_callback) { EXPECT_TRUE(server_handler_); + // |server_handler_| will delete itself after shutdown. server_handler_->ShutdownServer(complete_callback); server_handler_ = nullptr; } @@ -2360,7 +2320,7 @@ class CookieRestartTestHandler : public RoutingTestHandler, CefRefPtr context_; CefRefPtr cookie_manager_; - CefRefPtr server_handler_; + CookieAccessServerHandler* server_handler_ = nullptr; CookieAccessData data1_; CookieAccessData data2_; diff --git a/tests/ceftests/run_all_unittests.cc b/tests/ceftests/run_all_unittests.cc index 4c46abd8a..7b7f5b953 100644 --- a/tests/ceftests/run_all_unittests.cc +++ b/tests/ceftests/run_all_unittests.cc @@ -20,9 +20,11 @@ #include "include/cef_app.h" #include "include/cef_task.h" #include "include/cef_thread.h" +#include "include/cef_waitable_event.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_helpers.h" #include "tests/ceftests/test_handler.h" +#include "tests/ceftests/test_server.h" #include "tests/ceftests/test_suite.h" #include "tests/shared/browser/client_app_browser.h" #include "tests/shared/browser/main_message_loop_external_pump.h" @@ -48,6 +50,7 @@ namespace { void QuitMessageLoop() { + CEF_REQUIRE_UI_THREAD(); client::MainMessageLoop* message_loop = client::MainMessageLoop::Get(); if (message_loop) message_loop->Quit(); @@ -74,8 +77,8 @@ void RunTestsOnTestThread() { while (TestHandler::HasBrowser()) sleep(100); - // Quit the CEF message loop. - CefPostTask(TID_UI, base::Bind(&QuitMessageLoop)); + // Wait for the test server to stop, and then quit the CEF message loop. + test_server::Stop(base::Bind(QuitMessageLoop)); } // Called on the UI thread. @@ -201,6 +204,12 @@ int main(int argc, char* argv[]) { if (settings.multi_threaded_message_loop) { // Run the test suite on the main thread. retval = test_suite.Run(); + + // Wait for the test server to stop. + CefRefPtr event = + CefWaitableEvent::CreateWaitableEvent(true, false); + test_server::Stop(base::Bind(&CefWaitableEvent::Signal, event)); + event->Wait(); } else { // Create and start the test thread. CefRefPtr thread = CefThread::CreateThread("test_thread"); diff --git a/tests/ceftests/server_unittest.cc b/tests/ceftests/server_unittest.cc index b3f8c2aaf..f9cc673b6 100644 --- a/tests/ceftests/server_unittest.cc +++ b/tests/ceftests/server_unittest.cc @@ -20,6 +20,7 @@ namespace { +// Must use a different port than test_server.cc. const char kTestServerAddress[] = "127.0.0.1"; const uint16 kTestServerPort = 8099; const int kTestTimeout = 5000; @@ -27,7 +28,7 @@ const int kTestTimeout = 5000; std::string GetTestServerOrigin(bool is_websocket) { std::stringstream ss; ss << (is_websocket ? "ws://" : "http://") << kTestServerAddress << ":" - << kTestServerPort << "/"; + << kTestServerPort; return ss.str(); } @@ -793,7 +794,7 @@ CefRefPtr CreateTestServerRequest( const std::string& content_type = std::string(), const CefRequest::HeaderMap& extra_headers = CefRequest::HeaderMap()) { CefRefPtr request = CefRequest::Create(); - request->SetURL(GetTestServerOrigin(false) + path); + request->SetURL(GetTestServerOrigin(false) + "/" + path); request->SetMethod(method); CefRequest::HeaderMap header_map; @@ -1277,7 +1278,7 @@ class EchoWebSocketRequestHandler : public TestServerHandler::WsRequestHandler { explicit EchoWebSocketRequestHandler(int expected_message_ct) : expected_message_ct_(expected_message_ct), actual_message_ct_(0) {} - std::string GetWebSocketUrl() { return GetTestServerOrigin(true) + "echo"; } + std::string GetWebSocketUrl() { return GetTestServerOrigin(true) + "/echo"; } bool HandleRequest(CefRefPtr server, int connection_id, diff --git a/tests/ceftests/test_request.cc b/tests/ceftests/test_request.cc new file mode 100644 index 000000000..8de702c82 --- /dev/null +++ b/tests/ceftests/test_request.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2020 The Chromium Embedded Framework 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 "tests/ceftests/test_request.h" + +#include "include/cef_urlrequest.h" +#include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_helpers.h" + +namespace test_request { + +namespace { + +// Implementation of CefURLRequestClient that stores response information. +class RequestClient : public CefURLRequestClient, public State { + public: + RequestClient(const bool has_credentials, + const std::string& username, + const std::string& password, + const RequestDoneCallback& done_callback) + : has_credentials_(has_credentials), + username_(username), + password_(password), + done_callback_(done_callback) { + DCHECK(!done_callback_.is_null()); + } + + void OnUploadProgress(CefRefPtr request, + int64 current, + int64 total) override { + upload_progress_ct_++; + upload_total_ = total; + } + + void OnDownloadProgress(CefRefPtr request, + int64 current, + int64 total) override { + response_ = request->GetResponse(); + DCHECK(response_.get()); + DCHECK(response_->IsReadOnly()); + download_progress_ct_++; + download_total_ = total; + } + + void OnDownloadData(CefRefPtr request, + const void* data, + size_t data_length) override { + response_ = request->GetResponse(); + DCHECK(response_.get()); + DCHECK(response_->IsReadOnly()); + download_data_ct_++; + download_data_ += std::string(static_cast(data), data_length); + } + + bool GetAuthCredentials(bool isProxy, + const CefString& host, + int port, + const CefString& realm, + const CefString& scheme, + CefRefPtr callback) override { + auth_credentials_ct_++; + if (has_credentials_) { + callback->Continue(username_, password_); + return true; + } + return false; + } + + void OnRequestComplete(CefRefPtr request) override { + request_complete_ct_++; + + request_ = request->GetRequest(); + DCHECK(request_->IsReadOnly()); + status_ = request->GetRequestStatus(); + error_code_ = request->GetRequestError(); + response_was_cached_ = request->ResponseWasCached(); + response_ = request->GetResponse(); + if (response_) { + DCHECK(response_->IsReadOnly()); + } + + done_callback_.Run(*this); + } + + private: + const bool has_credentials_; + const std::string username_; + const std::string password_; + const RequestDoneCallback done_callback_; + + IMPLEMENT_REFCOUNTING(RequestClient); +}; + +} // namespace + +void Send(const SendConfig& config, const RequestDoneCallback& callback) { + DCHECK(config.request_); + CefRefPtr client = new RequestClient( + config.has_credentials_, config.username_, config.password_, callback); + if (config.frame_) { + config.frame_->CreateURLRequest(config.request_, client.get()); + } else { + CefURLRequest::Create(config.request_, client.get(), + config.request_context_); + } +} + +} // namespace test_request diff --git a/tests/ceftests/test_request.h b/tests/ceftests/test_request.h new file mode 100644 index 000000000..7aa6b7475 --- /dev/null +++ b/tests/ceftests/test_request.h @@ -0,0 +1,69 @@ +// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_TESTS_CEFTESTS_TEST_REQUEST_H_ +#define CEF_TESTS_CEFTESTS_TEST_REQUEST_H_ +#pragma once + +#include + +#include "include/base/cef_bind.h" +#include "include/cef_frame.h" +#include "include/cef_request.h" +#include "include/cef_request_context.h" +#include "include/cef_response.h" + +namespace test_request { + +// Stores all state passed to CefURLRequestClient. +struct State { + public: + // Number of times each callback is executed. + int upload_progress_ct_ = 0; + int download_progress_ct_ = 0; + int download_data_ct_ = 0; + int auth_credentials_ct_ = 0; + int request_complete_ct_ = 0; + + // From OnUploadProgress. + int64 upload_total_ = 0; + + // From OnDownloadProgress. + int64 download_total_ = 0; + + // From OnDownloadData. + std::string download_data_; + + // From OnRequestComplete. + CefRefPtr request_; + cef_urlrequest_status_t status_ = UR_UNKNOWN; + cef_errorcode_t error_code_ = ERR_NONE; + CefRefPtr response_; + bool response_was_cached_ = false; +}; + +typedef base::Callback RequestDoneCallback; + +struct SendConfig { + // Send using |frame_| or |request_context_| if non-nullptr. + // Sends using the global request context if both are nullptr. + CefRefPtr frame_; + CefRefPtr request_context_; + + // The request to send. + CefRefPtr request_; + + // Returned via GetAuthCredentials if |has_credentials_| is true. + bool has_credentials_ = false; + std::string username_; + std::string password_; +}; + +// Send a request. |callback| will be executed on the calling thread after the +// request completes. +void Send(const SendConfig& config, const RequestDoneCallback& callback); + +} // namespace test_request + +#endif // CEF_TESTS_CEFTESTS_TEST_REQUEST_H_ diff --git a/tests/ceftests/test_server.cc b/tests/ceftests/test_server.cc new file mode 100644 index 000000000..5075a0a24 --- /dev/null +++ b/tests/ceftests/test_server.cc @@ -0,0 +1,492 @@ +// Copyright (c) 2020 The Chromium Embedded Framework 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 "tests/ceftests/test_server.h" + +#include + +#include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_helpers.h" + +namespace test_server { + +// Must use a different port than server_unittest.cc. +const char kServerAddress[] = "127.0.0.1"; +const uint16 kServerPort = 8098; +const char kServerScheme[] = "http"; +const char kServerOrigin[] = "http://127.0.0.1:8098"; + +namespace { + +class ServerManager; +ServerManager* g_manager = nullptr; + +// True if Stop() has been called. +bool g_stopping = false; + +// Created on the UI thread and called on the dedicated server thread. +class ServerHandler : public CefServerHandler { + public: + ServerHandler() { + CefServer::CreateServer(kServerAddress, kServerPort, 10, this); + } + + ~ServerHandler() override { + DCHECK(!server_); + NotifyServerHandlerDeleted(); + } + + void Shutdown() { server_->Shutdown(); } + + protected: + // CefServerHandler methods: + void OnServerCreated(CefRefPtr server) override { + server_ = server; + NotifyServerCreated(kServerOrigin); + } + + void OnServerDestroyed(CefRefPtr server) override { + server_ = nullptr; + NotifyServerDestroyed(); + } + + void OnClientConnected(CefRefPtr server, + int connection_id) override { + DCHECK(server->HasConnection()); + DCHECK(server->IsValidConnection(connection_id)); + NotifyClientConnected(server, connection_id); + } + + void OnClientDisconnected(CefRefPtr server, + int connection_id) override { + DCHECK(!server->IsValidConnection(connection_id)); + NotifyClientDisconnected(server, connection_id); + } + + void OnHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) override { + NotifyHttpRequest(server, connection_id, client_address, request); + } + + void OnWebSocketRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request, + CefRefPtr callback) override {} + void OnWebSocketConnected(CefRefPtr server, + int connection_id) override {} + void OnWebSocketMessage(CefRefPtr server, + int connection_id, + const void* data, + size_t data_size) override {} + + private: + static void NotifyServerCreated(const std::string& server_origin); + static void NotifyServerDestroyed(); + static void NotifyServerHandlerDeleted(); + static void NotifyClientConnected(CefRefPtr server, + int connection_id); + static void NotifyClientDisconnected(CefRefPtr server, + int connection_id); + static void NotifyHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request); + + CefRefPtr server_; + + IMPLEMENT_REFCOUNTING(ServerHandler); + DISALLOW_COPY_AND_ASSIGN(ServerHandler); +}; + +// Only accessed on the UI thread. Deletes itself after the server is stopped. +class ServerManager { + public: + ServerManager() { + CEF_REQUIRE_UI_THREAD(); + DCHECK(!g_manager); + g_manager = this; + } + + ~ServerManager() { + CEF_REQUIRE_UI_THREAD(); + DCHECK(observer_list_.empty()); + DCHECK(start_callback_list_.empty()); + DCHECK(stop_callback_.is_null()); + + g_manager = nullptr; + } + + void Start(const StartDoneCallback& callback) { + CEF_REQUIRE_UI_THREAD(); + if (!origin_.empty()) { + // The server is already running. + callback.Run(origin_); + return; + } + + // If tests run in parallel, and the server is starting, then there may be + // multiple pending callbacks. + start_callback_list_.push_back(callback); + + // Only create the handler a single time. + if (!handler_) { + handler_ = new ServerHandler(); + } + } + + void Stop(const DoneCallback& callback) { + CEF_REQUIRE_UI_THREAD(); + if (!handler_) { + // The server is not currently running. + callback.Run(); + return; + } + + // Only 1 stop callback supported. + DCHECK(stop_callback_.is_null()); + stop_callback_ = callback; + + handler_->Shutdown(); + } + + void AddObserver(Observer* observer) { + CEF_REQUIRE_UI_THREAD(); + observer_list_.push_back(observer); + } + + void RemoveObserver(Observer* observer) { + CEF_REQUIRE_UI_THREAD(); + bool found = false; + ObserverList::iterator it = observer_list_.begin(); + for (; it != observer_list_.end(); ++it) { + if (*it == observer) { + observer_list_.erase(it); + found = true; + break; + } + } + DCHECK(found); + } + + void NotifyServerCreated(const std::string& server_origin) { + CEF_REQUIRE_UI_THREAD(); + + DCHECK(origin_.empty()); + origin_ = server_origin; + + StartDoneCallbackList::const_iterator it = start_callback_list_.begin(); + for (; it != start_callback_list_.end(); ++it) { + (*it).Run(origin_); + } + start_callback_list_.clear(); + } + + void NotifyServerDestroyed() { + CEF_REQUIRE_UI_THREAD(); + + origin_.clear(); + handler_ = nullptr; + } + + // All server-related objects have been torn down. + void NotifyServerHandlerDeleted() { + CEF_REQUIRE_UI_THREAD(); + + DCHECK(!stop_callback_.is_null()); + stop_callback_.Run(); + stop_callback_.Reset(); + + delete this; + } + + void NotifyClientConnected(CefRefPtr server, int connection_id) { + CEF_REQUIRE_UI_THREAD(); + + DCHECK(!observer_list_.empty()); + + // Use a copy in case |observer_list_| is modified during iteration. + ObserverList list = observer_list_; + + ObserverList::const_iterator it = list.begin(); + for (; it != list.end(); ++it) { + if ((*it)->OnClientConnected(server, connection_id)) { + break; + } + } + } + + void NotifyClientDisconnected(CefRefPtr server, + int connection_id) { + CEF_REQUIRE_UI_THREAD(); + + DCHECK(!observer_list_.empty()); + + // Use a copy in case |observer_list_| is modified during iteration. + ObserverList list = observer_list_; + + ObserverList::const_iterator it = list.begin(); + for (; it != list.end(); ++it) { + if ((*it)->OnClientDisconnected(server, connection_id)) { + break; + } + } + } + + void NotifyHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) { + CEF_REQUIRE_UI_THREAD(); + + DCHECK(!observer_list_.empty()); + + // Use a copy in case |observer_list_| is modified during iteration. + ObserverList list = observer_list_; + + ObserverList::const_iterator it = list.begin(); + for (; it != list.end(); ++it) { + if ((*it)->OnHttpRequest(server, connection_id, client_address, + request)) { + break; + } + } + } + + private: + CefRefPtr handler_; + std::string origin_; + + typedef std::vector StartDoneCallbackList; + StartDoneCallbackList start_callback_list_; + + DoneCallback stop_callback_; + + typedef std::vector ObserverList; + ObserverList observer_list_; + + DISALLOW_COPY_AND_ASSIGN(ServerManager); +}; + +ServerManager* GetServerManager() { + return g_manager; +} + +ServerManager* GetOrCreateServerManager() { + if (!g_manager) { + new ServerManager(); + DCHECK(g_manager); + } + return g_manager; +} + +// static +void ServerHandler::NotifyServerCreated(const std::string& server_origin) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, + base::Bind(ServerHandler::NotifyServerCreated, server_origin)); + return; + } + + GetServerManager()->NotifyServerCreated(server_origin); +} + +// static +void ServerHandler::NotifyServerDestroyed() { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyServerDestroyed)); + return; + } + + GetServerManager()->NotifyServerDestroyed(); +} + +// static +void ServerHandler::NotifyServerHandlerDeleted() { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyServerHandlerDeleted)); + return; + } + + GetServerManager()->NotifyServerHandlerDeleted(); +} + +// static +void ServerHandler::NotifyClientConnected(CefRefPtr server, + int connection_id) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyClientConnected, server, + connection_id)); + return; + } + + GetServerManager()->NotifyClientConnected(server, connection_id); +} + +// static +void ServerHandler::NotifyClientDisconnected(CefRefPtr server, + int connection_id) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyClientDisconnected, + server, connection_id)); + return; + } + + GetServerManager()->NotifyClientDisconnected(server, connection_id); +} + +// static +void ServerHandler::NotifyHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyHttpRequest, server, + connection_id, client_address, request)); + return; + } + + GetServerManager()->NotifyHttpRequest(server, connection_id, client_address, + request); +} + +// May be created on any thread but will be destroyed on the UI thread. +class ObserverRegistration : public CefRegistration { + public: + explicit ObserverRegistration(Observer* const observer) + : observer_(observer) { + DCHECK(observer_); + } + + ~ObserverRegistration() override { + CEF_REQUIRE_UI_THREAD(); + + ServerManager* manager = GetServerManager(); + if (manager) { + manager->RemoveObserver(observer_); + observer_->OnUnregistered(); + } + } + + void Initialize() { + CEF_REQUIRE_UI_THREAD(); + GetOrCreateServerManager()->AddObserver(observer_); + observer_->OnRegistered(); + } + + private: + Observer* const observer_; + + IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(ObserverRegistration); + DISALLOW_COPY_AND_ASSIGN(ObserverRegistration); +}; + +void InitializeRegistration(CefRefPtr registration, + const DoneCallback& callback) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, + base::Bind(InitializeRegistration, registration, callback)); + return; + } + + DCHECK(!g_stopping); + + registration->Initialize(); + if (!callback.is_null()) + callback.Run(); +} + +} // namespace + +void Start(const StartDoneCallback& callback) { + DCHECK(!callback.is_null()); + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(Start, callback)); + return; + } + + DCHECK(!g_stopping); + + GetOrCreateServerManager()->Start(callback); +} + +void Stop(const DoneCallback& callback) { + DCHECK(!callback.is_null()); + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, base::Bind(Stop, callback)); + return; + } + + // Stop will be called one time on test framework shutdown. + DCHECK(!g_stopping); + g_stopping = true; + + ServerManager* manager = GetServerManager(); + if (manager) { + manager->Stop(callback); + } else { + callback.Run(); + } +} + +CefRefPtr AddObserver(Observer* observer, + const DoneCallback& callback) { + DCHECK(observer); + CefRefPtr registration = + new ObserverRegistration(observer); + InitializeRegistration(registration, callback); + return registration.get(); +} + +CefRefPtr AddObserverAndStart( + Observer* observer, + const StartDoneCallback& callback) { + return AddObserver(observer, base::Bind(Start, callback)); +} + +// ObserverHelper + +ObserverHelper::ObserverHelper() : weak_ptr_factory_(this) { + CEF_REQUIRE_UI_THREAD(); +} + +ObserverHelper::~ObserverHelper() { + DCHECK(state_ == State::NONE); +} + +void ObserverHelper::Initialize() { + CEF_REQUIRE_UI_THREAD(); + DCHECK(state_ == State::NONE); + state_ = State::INITIALIZING; + registration_ = AddObserverAndStart( + this, + base::Bind(&ObserverHelper::OnStartDone, weak_ptr_factory_.GetWeakPtr())); +} + +void ObserverHelper::Shutdown() { + CEF_REQUIRE_UI_THREAD(); + DCHECK(state_ == State::INITIALIZED); + state_ = State::SHUTTINGDOWN; + registration_ = nullptr; +} + +void ObserverHelper::OnStartDone(const std::string& server_origin) { + DCHECK(state_ == State::INITIALIZING); + state_ = State::INITIALIZED; + OnInitialized(server_origin); +} + +void ObserverHelper::OnRegistered() { + DCHECK(state_ == State::INITIALIZING); +} + +void ObserverHelper::OnUnregistered() { + DCHECK(state_ == State::SHUTTINGDOWN); + state_ = State::NONE; + OnShutdown(); +} + +} // namespace test_server \ No newline at end of file diff --git a/tests/ceftests/test_server.h b/tests/ceftests/test_server.h new file mode 100644 index 000000000..958394de5 --- /dev/null +++ b/tests/ceftests/test_server.h @@ -0,0 +1,119 @@ +// Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_TESTS_CEFTESTS_TEST_SERVER_H_ +#define CEF_TESTS_CEFTESTS_TEST_SERVER_H_ +#pragma once + +#include + +#include "include/base/cef_bind.h" +#include "include/cef_registration.h" +#include "include/cef_request.h" +#include "include/cef_server.h" + +namespace test_server { + +extern const char kServerAddress[]; +extern const uint16 kServerPort; +extern const char kServerScheme[]; +extern const char kServerOrigin[]; + +typedef base::Closure DoneCallback; + +typedef base::Callback + StartDoneCallback; + +// Starts the server if it is not currently running, and executes |callback| on +// the UI thread. This method should be called by each test case that relies on +// the server. +void Start(const StartDoneCallback& callback); + +// Stops the server if it is currently running, and executes |callback| on the +// UI thread. This method will be called by the test framework on shutdown. +void Stop(const DoneCallback& callback); + +// Observer for CefServerHandler callbacks. Methods will be called on the UI +// thread. +class Observer { + public: + // Called when this Observer is registered. + virtual void OnRegistered() = 0; + + // Called when this Observer is unregistered. + virtual void OnUnregistered() = 0; + + // See CefServerHandler documentation for usage. Return true if the callback + // was handled. + virtual bool OnClientConnected(CefRefPtr server, + int connection_id) { + return false; + } + virtual bool OnClientDisconnected(CefRefPtr server, + int connection_id) { + return false; + } + virtual bool OnHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) = 0; + + protected: + virtual ~Observer() {} +}; + +// Add an observer for CefServerHandler callbacks. Remains registered until the +// returned CefRegistration object is destroyed. Registered observers will be +// executed in the order of registration until one returns true to indicate that +// it handled the callback. |callback| will be executed on the UI thread after +// registration is complete. +CefRefPtr AddObserver(Observer* observer, + const DoneCallback& callback); + +// Combination of AddObserver() followed by Start(). +CefRefPtr AddObserverAndStart( + Observer* observer, + const StartDoneCallback& callback); + +// Helper for managing Observer registration and callbacks. Only used on the UI +// thread. +class ObserverHelper : Observer { + public: + ObserverHelper(); + ~ObserverHelper() override; + + // Initialize the registration. Results in a call to OnInitialized(). + void Initialize(); + + // Shut down the registration. Results in a call to OnShutdown(). + void Shutdown(); + + // Implement this method to start sending server requests after Initialize(). + virtual void OnInitialized(const std::string& server_origin) = 0; + + // Implement this method to continue the test after Shutdown(). + virtual void OnShutdown() = 0; + + private: + void OnStartDone(const std::string& server_origin); + void OnRegistered() override; + void OnUnregistered() override; + + CefRefPtr registration_; + + enum class State { + NONE, + INITIALIZING, + INITIALIZED, + SHUTTINGDOWN, + } state_ = State::NONE; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ObserverHelper); +}; + +} // namespace test_server + +#endif // CEF_TESTS_CEFTESTS_TEST_SERVER_H_ diff --git a/tests/ceftests/test_server_unittest.cc b/tests/ceftests/test_server_unittest.cc new file mode 100644 index 000000000..54f686ce5 --- /dev/null +++ b/tests/ceftests/test_server_unittest.cc @@ -0,0 +1,257 @@ +// Copyright (c) 2020 The Chromium Embedded Framework 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 + +#include "include/cef_task.h" +#include "include/cef_waitable_event.h" +#include "include/wrapper/cef_closure_task.h" +#include "include/wrapper/cef_helpers.h" +#include "tests/ceftests/test_request.h" +#include "tests/ceftests/test_server.h" +#include "tests/ceftests/track_callback.h" +#include "tests/gtest/include/gtest/gtest.h" + +namespace { + +struct TestState { + TrackCallback got_initialized_; + TrackCallback got_connected_; + TrackCallback got_request_; + TrackCallback got_response_; + TrackCallback got_disconnected_; + TrackCallback got_shutdown_; + + bool ExpectAll() { + EXPECT_TRUE(got_initialized_); + EXPECT_TRUE(got_connected_); + EXPECT_TRUE(got_request_); + EXPECT_TRUE(got_response_); + EXPECT_TRUE(got_disconnected_); + EXPECT_TRUE(got_shutdown_); + return got_initialized_ && got_connected_ && got_request_ && + got_response_ && got_disconnected_ && got_shutdown_; + } +}; + +const char kResponseData[] = "Test data"; + +class TestServerObserver : public test_server::ObserverHelper { + public: + TestServerObserver(TestState* state, + const std::string& path, + const base::Closure& done_callback) + : state_(state), + path_(path), + done_callback_(done_callback), + weak_ptr_factory_(this) { + DCHECK(state); + DCHECK(!path.empty()); + DCHECK(!done_callback_.is_null()); + Initialize(); + } + + ~TestServerObserver() override { done_callback_.Run(); } + + void OnInitialized(const std::string& server_origin) override { + CEF_REQUIRE_UI_THREAD(); + EXPECT_FALSE(state_->got_initialized_); + EXPECT_FALSE(state_->got_connected_); + EXPECT_FALSE(state_->got_request_); + EXPECT_FALSE(state_->got_response_); + EXPECT_FALSE(state_->got_disconnected_); + EXPECT_FALSE(state_->got_shutdown_); + + state_->got_initialized_.yes(); + + url_ = server_origin + path_; + + // Send a request to the server. + test_request::SendConfig config; + config.request_ = CefRequest::Create(); + config.request_->SetURL(url_); + test_request::Send(config, + base::Bind(&TestServerObserver::OnRequestResponse, + weak_ptr_factory_.GetWeakPtr())); + } + + bool OnClientConnected(CefRefPtr server, + int connection_id) override { + CEF_REQUIRE_UI_THREAD(); + if (state_->got_connected_) { + // We already got the callback once. Let the next observer get the + // callback. + return false; + } + + EXPECT_TRUE(state_->got_initialized_); + EXPECT_FALSE(state_->got_request_); + EXPECT_FALSE(state_->got_response_); + EXPECT_FALSE(state_->got_disconnected_); + EXPECT_FALSE(state_->got_shutdown_); + + state_->got_connected_.yes(); + + // We don't know if this connection is the one that we're going to + // handle, so continue propagating the callback. + return false; + } + + bool OnHttpRequest(CefRefPtr server, + int connection_id, + const CefString& client_address, + CefRefPtr request) override { + CEF_REQUIRE_UI_THREAD(); + const std::string& url = request->GetURL(); + if (url != url_) + return false; + + EXPECT_TRUE(state_->got_initialized_); + EXPECT_TRUE(state_->got_connected_); + EXPECT_FALSE(state_->got_request_); + EXPECT_FALSE(state_->got_response_); + EXPECT_FALSE(state_->got_disconnected_); + EXPECT_FALSE(state_->got_shutdown_); + + state_->got_request_.yes(); + connection_id_ = connection_id; + + server->SendHttp200Response(connection_id, "text/plain", kResponseData, + sizeof(kResponseData) - 1); + + // Stop propagating the callback. + return true; + } + + void OnRequestResponse(const test_request::State& state) { + CEF_REQUIRE_UI_THREAD(); + // Don't test for disconnected, which may race response. + EXPECT_TRUE(state_->got_initialized_); + EXPECT_TRUE(state_->got_connected_); + EXPECT_TRUE(state_->got_request_); + EXPECT_FALSE(state_->got_response_); + EXPECT_FALSE(state_->got_shutdown_); + + state_->got_response_.yes(); + + EXPECT_STREQ(url_.c_str(), state.request_->GetURL().ToString().c_str()); + EXPECT_EQ(UR_SUCCESS, state.status_); + EXPECT_EQ(ERR_NONE, state.error_code_); + EXPECT_EQ(200, state.response_->GetStatus()); + EXPECT_STREQ(kResponseData, state.download_data_.c_str()); + + // Trigger shutdown asynchronously. + CefPostTask(TID_UI, base::Bind(&TestServerObserver::Shutdown, + weak_ptr_factory_.GetWeakPtr())); + } + + bool OnClientDisconnected(CefRefPtr server, + int connection_id) override { + CEF_REQUIRE_UI_THREAD(); + if (connection_id != connection_id_) { + // Not the connection that we handled. Let the next observer get the + // callback. + return false; + } + + // Don't test for response, which may race disconnected. + EXPECT_TRUE(state_->got_initialized_); + EXPECT_TRUE(state_->got_connected_); + EXPECT_TRUE(state_->got_request_); + EXPECT_FALSE(state_->got_disconnected_); + EXPECT_FALSE(state_->got_shutdown_); + + state_->got_disconnected_.yes(); + + // Stop propagating the callback. + return true; + } + + void OnShutdown() override { + CEF_REQUIRE_UI_THREAD(); + EXPECT_TRUE(state_->got_initialized_); + EXPECT_TRUE(state_->got_connected_); + EXPECT_TRUE(state_->got_request_); + EXPECT_TRUE(state_->got_response_); + EXPECT_TRUE(state_->got_disconnected_); + EXPECT_FALSE(state_->got_shutdown_); + + state_->got_shutdown_.yes(); + + // End the test. + delete this; + } + + private: + TestState* const state_; + const std::string path_; + const base::Closure done_callback_; + + std::string url_; + int connection_id_ = -1; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(TestServerObserver); +}; + +void CreateObserverOnUIThread(TestState* state, + const std::string& path, + const base::Closure& done_callback) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, + base::Bind(CreateObserverOnUIThread, base::Unretained(state), + path, done_callback)); + return; + } + + new TestServerObserver(state, path, done_callback); +} + +void SignalIfDone(CefRefPtr event, + size_t* count, + size_t total) { + if (++(*count) == total) { + event->Signal(); + } +} + +} // namespace + +TEST(TestServerTest, ObserverHelperSingle) { + CefRefPtr event = + CefWaitableEvent::CreateWaitableEvent(true, false); + + TestState state; + CreateObserverOnUIThread(&state, "/TestServerTest.ObserverHelperSingle", + base::Bind(&CefWaitableEvent::Signal, event)); + event->TimedWait(2000); + + EXPECT_TRUE(state.ExpectAll()); +} + +TEST(TestServerTest, ObserverHelperMultiple) { + CefRefPtr event = + CefWaitableEvent::CreateWaitableEvent(true, false); + + TestState states[3]; + size_t count = 0; + const size_t size = arraysize(states); + + const base::Closure& done_callback = + base::Bind(SignalIfDone, event, base::Unretained(&count), size); + + for (size_t i = 0; i < size; ++i) { + std::stringstream ss; + ss << "/TestServerTest.ObserverHelperMultiple" << i; + CreateObserverOnUIThread(&states[i], ss.str(), done_callback); + } + + event->TimedWait(2000); + + EXPECT_EQ(size, count); + for (size_t i = 0; i < size; ++i) { + EXPECT_TRUE(states[i].ExpectAll()) << i; + } +} diff --git a/tests/ceftests/urlrequest_unittest.cc b/tests/ceftests/urlrequest_unittest.cc index a7e8fec08..bfbee95d3 100644 --- a/tests/ceftests/urlrequest_unittest.cc +++ b/tests/ceftests/urlrequest_unittest.cc @@ -17,6 +17,8 @@ #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_scoped_temp_dir.h" #include "tests/ceftests/test_handler.h" +#include "tests/ceftests/test_request.h" +#include "tests/ceftests/test_server.h" #include "tests/ceftests/test_suite.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" @@ -45,9 +47,9 @@ const char kRequestSchemeCustom[] = "urcustom"; const char kRequestHostCustom[] = "test"; // Server backend. -const char kRequestAddressServer[] = "127.0.0.1"; -const uint16 kRequestPortServer = 8099; -const char kRequestSchemeServer[] = "http"; +const char* kRequestAddressServer = test_server::kServerAddress; +const uint16 kRequestPortServer = test_server::kServerPort; +const char* kRequestSchemeServer = test_server::kServerScheme; const char kRequestSendCookieName[] = "urcookie_send"; const char kRequestSaveCookieName[] = "urcookie_save"; @@ -1205,7 +1207,7 @@ class RequestSchemeHandlerFactory : public CefSchemeHandlerFactory { // SERVER BACKEND // HTTP server handler. -class RequestServerHandler : public CefServerHandler { +class RequestServerHandler : public test_server::ObserverHelper { public: RequestServerHandler() : initialized_(false), @@ -1244,8 +1246,7 @@ class RequestServerHandler : public CefServerHandler { EXPECT_TRUE(complete_callback_.is_null()); complete_callback_ = complete_callback; - CefServer::CreateServer(kRequestAddressServer, kRequestPortServer, 10, - this); + Initialize(); } // Results in a call to VerifyResults() and eventual execution of the @@ -1256,73 +1257,59 @@ class RequestServerHandler : public CefServerHandler { EXPECT_TRUE(complete_callback_.is_null()); complete_callback_ = complete_callback; - EXPECT_TRUE(server_); - if (server_) - server_->Shutdown(); + Shutdown(); } - void OnServerCreated(CefRefPtr server) override { - EXPECT_TRUE(server); - EXPECT_TRUE(server->IsRunning()); - EXPECT_FALSE(server->HasConnection()); + void OnInitialized(const std::string& server_origin) override { + EXPECT_UI_THREAD(); + EXPECT_STREQ(server_origin.c_str(), GetRequestOrigin(true).c_str()); + EXPECT_FALSE(got_initialized_); + got_initialized_.yes(); - EXPECT_FALSE(got_server_created_); - got_server_created_.yes(); - - EXPECT_FALSE(server_); - server_ = server; - - EXPECT_FALSE(server_runner_); - server_runner_ = server_->GetTaskRunner(); - EXPECT_TRUE(server_runner_); - EXPECT_TRUE(server_runner_->BelongsToCurrentThread()); - - CefPostTask(TID_UI, base::Bind(&RequestServerHandler::RunCompleteCallback, - this, true)); + RunCompleteCallback(true); } - void OnServerDestroyed(CefRefPtr server) override { - EXPECT_TRUE(VerifyServer(server)); - EXPECT_FALSE(server->IsRunning()); - EXPECT_FALSE(server->HasConnection()); - - EXPECT_FALSE(got_server_destroyed_); - got_server_destroyed_.yes(); + void OnShutdown() override { + EXPECT_UI_THREAD(); + EXPECT_FALSE(got_shutdown_); + got_shutdown_.yes(); data_map_.SetOwnerTaskRunner(nullptr); - server_ = nullptr; VerifyResults(); + + delete this; } - void OnClientConnected(CefRefPtr server, + bool OnClientConnected(CefRefPtr server, int connection_id) override { - EXPECT_TRUE(VerifyServer(server)); - EXPECT_TRUE(server->HasConnection()); - EXPECT_TRUE(server->IsValidConnection(connection_id)); + EXPECT_UI_THREAD(); EXPECT_TRUE(connection_id_set_.find(connection_id) == connection_id_set_.end()); connection_id_set_.insert(connection_id); actual_connection_ct_++; + + return true; } - void OnClientDisconnected(CefRefPtr server, + bool OnClientDisconnected(CefRefPtr server, int connection_id) override { - EXPECT_TRUE(VerifyServer(server)); - EXPECT_FALSE(server->IsValidConnection(connection_id)); + EXPECT_UI_THREAD(); ConnectionIdSet::iterator it = connection_id_set_.find(connection_id); EXPECT_TRUE(it != connection_id_set_.end()); connection_id_set_.erase(it); + + return true; } - void OnHttpRequest(CefRefPtr server, + bool OnHttpRequest(CefRefPtr server, int connection_id, const CefString& client_address, CefRefPtr request) override { - EXPECT_TRUE(VerifyServer(server)); + EXPECT_UI_THREAD(); EXPECT_TRUE(VerifyConnection(connection_id)); EXPECT_FALSE(client_address.empty()); @@ -1333,52 +1320,18 @@ class RequestServerHandler : public CefServerHandler { HandleRequest(server, connection_id, request); actual_http_request_ct_++; - } - void OnWebSocketRequest(CefRefPtr server, - int connection_id, - const CefString& client_address, - CefRefPtr request, - CefRefPtr callback) override { - NOTREACHED(); - } - - void OnWebSocketConnected(CefRefPtr server, - int connection_id) override { - NOTREACHED(); - } - - void OnWebSocketMessage(CefRefPtr server, - int connection_id, - const void* data, - size_t data_size) override { - NOTREACHED(); + return true; } private: - bool RunningOnServerThread() { - return server_runner_ && server_runner_->BelongsToCurrentThread(); - } - - bool VerifyServer(CefRefPtr server) { - V_DECLARE(); - V_EXPECT_TRUE(RunningOnServerThread()); - V_EXPECT_TRUE(server); - V_EXPECT_TRUE(server_); - V_EXPECT_TRUE(server->GetAddress().ToString() == - server_->GetAddress().ToString()); - V_RETURN(); - } - bool VerifyConnection(int connection_id) { return connection_id_set_.find(connection_id) != connection_id_set_.end(); } void VerifyResults() { - EXPECT_TRUE(RunningOnServerThread()); - - EXPECT_TRUE(got_server_created_); - EXPECT_TRUE(got_server_destroyed_); + EXPECT_TRUE(got_initialized_); + EXPECT_TRUE(got_shutdown_); EXPECT_TRUE(connection_id_set_.empty()); EXPECT_EQ(expected_connection_ct_, actual_connection_ct_) << request_log_; EXPECT_EQ(expected_http_request_ct_, actual_http_request_ct_) @@ -1409,18 +1362,18 @@ class RequestServerHandler : public CefServerHandler { } } - void HandleAuthRequest(CefRefPtr server, - int connection_id, - CefRefPtr request) { + static void HandleAuthRequest(CefRefPtr server, + int connection_id, + CefRefPtr request) { CefRefPtr response = CefResponse::Create(); GetAuthResponse(response); SendResponse(server, connection_id, response, std::string()); } - void HandleNormalRequest(CefRefPtr server, - int connection_id, - CefRefPtr request, - RequestRunSettings* settings) { + static void HandleNormalRequest(CefRefPtr server, + int connection_id, + CefRefPtr request, + RequestRunSettings* settings) { VerifyNormalRequest(settings, request, true); CefRefPtr response = CefResponse::Create(); @@ -1437,11 +1390,11 @@ class RequestServerHandler : public CefServerHandler { SendResponse(server, connection_id, response, response_data); } - void HandleRedirectRequest(CefRefPtr server, - int connection_id, - CefRefPtr request, - CefRefPtr redirect_request, - CefRefPtr redirect_response) { + static void HandleRedirectRequest(CefRefPtr server, + int connection_id, + CefRefPtr request, + CefRefPtr redirect_request, + CefRefPtr redirect_response) { if (redirect_response->GetStatus() == 302) { // Simulate wrong copying of POST-specific headers Content-Type and // Content-Length. A 302 redirect should end up in a GET request and @@ -1460,10 +1413,19 @@ class RequestServerHandler : public CefServerHandler { SendResponse(server, connection_id, redirect_response, std::string()); } - void SendResponse(CefRefPtr server, - int connection_id, - CefRefPtr response, - const std::string& response_data) { + static void SendResponse(CefRefPtr server, + int connection_id, + CefRefPtr response, + const std::string& response_data) { + // Execute on the server thread because some methods require it. + CefRefPtr task_runner = server->GetTaskRunner(); + if (!task_runner->BelongsToCurrentThread()) { + task_runner->PostTask(CefCreateClosureTask( + base::Bind(RequestServerHandler::SendResponse, server, connection_id, + response, response_data))); + return; + } + const int response_code = response->GetStatus(); if (response_code <= 0) { // Intentionally not responding for incomplete request tests. @@ -1498,8 +1460,8 @@ class RequestServerHandler : public CefServerHandler { EXPECT_UI_THREAD(); if (startup) { - // Transfer DataMap ownership to the server thread. - data_map_.SetOwnerTaskRunner(server_->GetTaskRunner()); + // Transfer DataMap ownership to the UI thread. + data_map_.SetOwnerTaskRunner(CefTaskRunner::GetForCurrentThread()); } EXPECT_FALSE(complete_callback_.is_null()); @@ -1509,8 +1471,6 @@ class RequestServerHandler : public CefServerHandler { RequestDataMap data_map_; - CefRefPtr server_; - CefRefPtr server_runner_; bool initialized_; // Only accessed on the UI thread. @@ -1519,8 +1479,8 @@ class RequestServerHandler : public CefServerHandler { // After initialization the below members are only accessed on the server // thread. - TrackCallback got_server_created_; - TrackCallback got_server_destroyed_; + TrackCallback got_initialized_; + TrackCallback got_shutdown_; typedef std::set ConnectionIdSet; ConnectionIdSet connection_id_set_; @@ -1531,106 +1491,6 @@ class RequestServerHandler : public CefServerHandler { int actual_http_request_ct_; std::string request_log_; - - IMPLEMENT_REFCOUNTING(RequestServerHandler); - DISALLOW_COPY_AND_ASSIGN(RequestServerHandler); -}; - -// URLREQUEST CLIENT - -// Implementation of CefURLRequestClient that stores response information. -class RequestClient : public CefURLRequestClient { - public: - typedef base::Callback)> - RequestCompleteCallback; - - explicit RequestClient(const RequestCompleteCallback& complete_callback) - : complete_callback_(complete_callback) { - EXPECT_FALSE(complete_callback_.is_null()); - } - - void OnRequestComplete(CefRefPtr request) override { - request_complete_ct_++; - - request_ = request->GetRequest(); - EXPECT_TRUE(request_->IsReadOnly()); - status_ = request->GetRequestStatus(); - error_code_ = request->GetRequestError(); - response_was_cached_ = request->ResponseWasCached(); - response_ = request->GetResponse(); - if (response_) { - EXPECT_TRUE(response_->IsReadOnly()); - } - - complete_callback_.Run(this); - } - - void OnUploadProgress(CefRefPtr request, - int64 current, - int64 total) override { - upload_progress_ct_++; - upload_total_ = total; - } - - void OnDownloadProgress(CefRefPtr request, - int64 current, - int64 total) override { - response_ = request->GetResponse(); - EXPECT_TRUE(response_.get()); - EXPECT_TRUE(response_->IsReadOnly()); - download_progress_ct_++; - download_total_ = total; - } - - void OnDownloadData(CefRefPtr request, - const void* data, - size_t data_length) override { - response_ = request->GetResponse(); - EXPECT_TRUE(response_.get()); - EXPECT_TRUE(response_->IsReadOnly()); - download_data_ct_++; - download_data_ += std::string(static_cast(data), data_length); - } - - bool GetAuthCredentials(bool isProxy, - const CefString& host, - int port, - const CefString& realm, - const CefString& scheme, - CefRefPtr callback) override { - auth_credentials_ct_++; - if (has_authentication_) { - callback->Continue(username_, password_); - return true; - } - return false; - } - - private: - const RequestCompleteCallback complete_callback_; - - public: - bool has_authentication_ = false; - std::string username_; - std::string password_; - - int request_complete_ct_ = 0; - int upload_progress_ct_ = 0; - int download_progress_ct_ = 0; - int download_data_ct_ = 0; - int auth_credentials_ct_ = 0; - - int64 upload_total_ = 0; - int64 download_total_ = 0; - std::string download_data_; - CefRefPtr request_; - CefURLRequest::Status status_ = UR_UNKNOWN; - CefURLRequest::ErrorCode error_code_ = ERR_NONE; - CefRefPtr response_; - bool response_was_cached_ = false; - - private: - IMPLEMENT_REFCOUNTING(RequestClient); }; // SHARED TEST RUNNER @@ -2501,31 +2361,31 @@ class RequestTestRunner : public base::RefCountedThreadSafe { } // Send a request. |complete_callback| will be executed on request completion. - void SendRequest( - const RequestClient::RequestCompleteCallback& complete_callback) { - CefRefPtr request; - if (settings_.redirect_request) - request = settings_.redirect_request; - else - request = settings_.request; - EXPECT_TRUE(request.get()); + void SendRequest(const test_request::RequestDoneCallback& done_callback) { + test_request::SendConfig config; - CefRefPtr client = new RequestClient(complete_callback); + if (settings_.redirect_request) + config.request_ = settings_.redirect_request; + else + config.request_ = settings_.request; + EXPECT_TRUE(config.request_.get()); // Not delegating to CefRequestHandler::GetAuthCredentials. if (!use_frame_method_ && settings_.expect_authentication) { - client->has_authentication_ = true; - client->username_ = settings_.username; - client->password_ = settings_.password; + config.has_credentials_ = true; + config.username_ = settings_.username; + config.password_ = settings_.password; } if (use_frame_method_) { EXPECT_TRUE(frame_); - frame_->CreateURLRequest(request, client.get()); + config.frame_ = frame_; } else { - CefURLRequest::Create(request, client.get(), request_context_); + config.request_context_ = request_context_; } + test_request::Send(config, done_callback); + if (settings_.incomplete_type != RequestRunSettings::INCOMPLETE_NONE) { incomplete_request_callback_.Run(); incomplete_request_callback_.Reset(); @@ -2533,7 +2393,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { } // Verify a response. - void VerifyResponse(CefRefPtr client) { + void VerifyResponse(const test_request::State* const client) { CefRefPtr expected_request; CefRefPtr expected_response; @@ -2607,8 +2467,8 @@ class RequestTestRunner : public base::RefCountedThreadSafe { } void SingleRunTestComplete(const base::Closure& complete_callback, - CefRefPtr completed_client) { - VerifyResponse(completed_client); + const test_request::State& completed_client) { + VerifyResponse(&completed_client); complete_callback.Run(); } @@ -2628,9 +2488,9 @@ class RequestTestRunner : public base::RefCountedThreadSafe { void MultipleRunTestNext(const base::Closure& complete_callback, int send_count, - CefRefPtr completed_client) { + const test_request::State& completed_client) { // Verify the completed request. - VerifyResponse(completed_client); + VerifyResponse(&completed_client); if (send_count == settings_.expected_send_count) { // All requests complete. @@ -2757,6 +2617,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { void ShutdownServer(const base::Closure& complete_callback) { EXPECT_TRUE(server_handler_); + // |server_handler_| will delete itself after shutdown. server_handler_->ShutdownServer(complete_callback); server_handler_ = nullptr; } @@ -2797,7 +2658,7 @@ class RequestTestRunner : public base::RefCountedThreadSafe { TestMap test_map_; // Server backend. - CefRefPtr server_handler_; + RequestServerHandler* server_handler_ = nullptr; // Scheme handler backend. std::string scheme_name_;