// 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_observer.h" #include #include "include/cef_command_line.h" #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_util.h" #include "tests/ceftests/track_callback.h" #include "tests/gtest/include/gtest/gtest.h" namespace { struct TestState { bool https_server = false; TrackCallback got_initialized_; TrackCallback got_request_; TrackCallback got_response_; TrackCallback got_shutdown_; bool ExpectAll() { EXPECT_TRUE(got_initialized_); EXPECT_TRUE(got_request_); EXPECT_TRUE(got_response_); EXPECT_TRUE(got_shutdown_); return got_initialized_ && got_request_ && got_response_ && got_shutdown_; } }; const char kResponseData[] = "Test data"; class TestServerObserver : public test_server::ObserverHelper { public: TestServerObserver(TestState* state, const std::string& path, base::OnceClosure done_callback) : state_(state), path_(path), done_callback_(std::move(done_callback)), weak_ptr_factory_(this) { DCHECK(state); DCHECK(!path.empty()); DCHECK(!done_callback_.is_null()); Initialize(state_->https_server); } ~TestServerObserver() override { std::move(done_callback_).Run(); } void OnInitialized(const std::string& server_origin) override { CEF_REQUIRE_UI_THREAD(); EXPECT_FALSE(state_->got_initialized_); EXPECT_FALSE(state_->got_request_); EXPECT_FALSE(state_->got_response_); 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::BindOnce(&TestServerObserver::OnRequestResponse, weak_ptr_factory_.GetWeakPtr())); } bool OnTestServerRequest(CefRefPtr request, const ResponseCallback& response_callback) override { CEF_REQUIRE_UI_THREAD(); const std::string& url = request->GetURL(); if (url != url_) { return false; } EXPECT_TRUE(state_->got_initialized_); EXPECT_FALSE(state_->got_request_); EXPECT_FALSE(state_->got_response_); EXPECT_FALSE(state_->got_shutdown_); state_->got_request_.yes(); auto response = CefResponse::Create(); response->SetStatus(200); response->SetMimeType("text/plain"); response_callback.Run(response, kResponseData); // 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_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::BindOnce(&TestServerObserver::Shutdown, weak_ptr_factory_.GetWeakPtr())); } void OnShutdown() override { CEF_REQUIRE_UI_THREAD(); EXPECT_TRUE(state_->got_initialized_); EXPECT_TRUE(state_->got_request_); EXPECT_TRUE(state_->got_response_); EXPECT_FALSE(state_->got_shutdown_); state_->got_shutdown_.yes(); // End the test. delete this; } private: TestState* const state_; const std::string path_; base::OnceClosure done_callback_; std::string url_; base::WeakPtrFactory weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(TestServerObserver); }; void CreateObserverOnUIThread(TestState* state, const std::string& path, base::OnceClosure done_callback) { if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, base::BindOnce(CreateObserverOnUIThread, base::Unretained(state), path, std::move(done_callback))); return; } new TestServerObserver(state, path, std::move(done_callback)); } void SignalIfDone(CefRefPtr event, size_t* count, size_t total) { if (++(*count) == total) { event->Signal(); } } void Wait(CefRefPtr event) { const auto timeout = GetConfiguredTestTimeout(/*timeout_ms=*/2000); if (!timeout) { event->Wait(); } else { event->TimedWait(*timeout); } } void RunHelperSingle(bool https_server) { CefRefPtr event = CefWaitableEvent::CreateWaitableEvent(true, false); TestState state; state.https_server = https_server; CreateObserverOnUIThread(&state, "/TestServerTest.ObserverHelperSingle", base::BindOnce(&CefWaitableEvent::Signal, event)); Wait(event); EXPECT_TRUE(state.ExpectAll()); } void RunHelperMultiple(bool https_server) { CefRefPtr event = CefWaitableEvent::CreateWaitableEvent(true, false); TestState states[3]; size_t count = 0; const size_t size = std::size(states); for (size_t i = 0; i < size; ++i) { std::stringstream ss; ss << "/TestServerTest.ObserverHelperMultiple" << i; auto done_callback = base::BindOnce(SignalIfDone, event, base::Unretained(&count), size); states[i].https_server = https_server; CreateObserverOnUIThread(&states[i], ss.str(), std::move(done_callback)); } Wait(event); EXPECT_EQ(size, count); for (size_t i = 0; i < size; ++i) { EXPECT_TRUE(states[i].ExpectAll()) << i; } } } // namespace TEST(TestServerObserverTest, HelperSingleHttp) { RunHelperSingle(/*https_server=*/false); } TEST(TestServerObserverTest, HelperMultipleHttp) { RunHelperMultiple(/*https_server=*/false); } TEST(TestServerObserverTest, HelperSingleHttps) { RunHelperSingle(/*https_server=*/true); } TEST(TestServerObserverTest, HelperMultipleHttps) { RunHelperMultiple(/*https_server=*/true); }