| // Copyright 2017 The Chromium 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 <string> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/macros.h" |
| #include "base/process/launch.h" |
| #include "base/run_loop.h" |
| #include "base/test/launcher/test_launcher.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/test_launcher.h" |
| #include "content/shell/browser/shell_content_browser_client.h" |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/service_manager/public/mojom/constants.mojom.h" |
| #include "services/service_manager/public/mojom/service_manager.mojom.h" |
| #include "services/test/echo/public/mojom/echo.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using testing::HasSubstr; |
| using testing::Not; |
| |
| namespace content { |
| namespace { |
| |
| bool ShouldTerminateOnServiceQuit(const service_manager::Identity& id) { |
| return id.name() == echo::mojom::kServiceName; |
| } |
| |
| class ServiceInstanceListener |
| : public service_manager::mojom::ServiceManagerListener { |
| public: |
| explicit ServiceInstanceListener( |
| service_manager::mojom::ServiceManagerListenerRequest request) |
| : binding_(this, std::move(request)) {} |
| ~ServiceInstanceListener() override = default; |
| |
| void WaitForInit() { |
| base::RunLoop loop; |
| init_wait_loop_ = &loop; |
| loop.Run(); |
| init_wait_loop_ = nullptr; |
| } |
| |
| uint32_t WaitForServicePID(const std::string& service_name) { |
| base::RunLoop loop; |
| pid_wait_loop_ = &loop; |
| service_expecting_pid_ = service_name; |
| loop.Run(); |
| pid_wait_loop_ = nullptr; |
| return pid_received_; |
| } |
| |
| private: |
| // service_manager::mojom::ServiceManagerListener: |
| void OnInit(std::vector<service_manager::mojom::RunningServiceInfoPtr> |
| instances) override { |
| if (init_wait_loop_) |
| init_wait_loop_->Quit(); |
| } |
| |
| void OnServiceCreated( |
| service_manager::mojom::RunningServiceInfoPtr instance) override {} |
| void OnServiceStarted(const service_manager::Identity&, |
| uint32_t pid) override {} |
| void OnServiceFailedToStart(const service_manager::Identity&) override {} |
| void OnServiceStopped(const service_manager::Identity&) override {} |
| |
| void OnServicePIDReceived(const service_manager::Identity& identity, |
| uint32_t pid) override { |
| if (identity.name() == service_expecting_pid_ && pid_wait_loop_) { |
| pid_received_ = pid; |
| pid_wait_loop_->Quit(); |
| } |
| } |
| |
| base::RunLoop* init_wait_loop_ = nullptr; |
| base::RunLoop* pid_wait_loop_ = nullptr; |
| std::string service_expecting_pid_; |
| uint32_t pid_received_ = 0; |
| mojo::Binding<service_manager::mojom::ServiceManagerListener> binding_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ServiceInstanceListener); |
| }; |
| |
| } // namespace |
| |
| using ServiceManagerContextBrowserTest = ContentBrowserTest; |
| |
| // "MANUAL" tests only run when kRunManualTestsFlag is set. |
| IN_PROC_BROWSER_TEST_F(ServiceManagerContextBrowserTest, |
| MANUAL_TerminateOnServiceQuit) { |
| ShellContentBrowserClient::Get() |
| ->set_should_terminate_on_service_quit_callback( |
| base::Bind(&ShouldTerminateOnServiceQuit)); |
| |
| // Launch a test service. |
| echo::mojom::EchoPtr echo_ptr; |
| content::ServiceManagerConnection::GetForProcess() |
| ->GetConnector() |
| ->BindInterface(echo::mojom::kServiceName, &echo_ptr); |
| |
| base::RunLoop loop; |
| // Terminate the service. Browser should exit in response with an error. |
| echo_ptr->Quit(); |
| loop.Run(); |
| } |
| |
| // Flaky timeout on Linux and Chrome OS ASAN: http://crbug.com/803814, |
| // crbug.com/804113. |
| #if (defined(OS_CHROMEOS) || defined(OS_LINUX)) && defined(ADDRESS_SANITIZER) |
| #define MAYBE_TerminateOnServiceQuit DISABLED_TerminateOnServiceQuit |
| #elif defined(OS_WIN) |
| // crbug.com/804937. Causes failures when test times out even if retry passes. |
| #define MAYBE_TerminateOnServiceQuit DISABLED_TerminateOnServiceQuit |
| #else |
| #define MAYBE_TerminateOnServiceQuit TerminateOnServiceQuit |
| #endif |
| TEST(ServiceManagerContextTest, MAYBE_TerminateOnServiceQuit) { |
| // Run the above test and collect the test output. |
| base::CommandLine new_test = |
| base::CommandLine(base::CommandLine::ForCurrentProcess()->GetProgram()); |
| new_test.AppendSwitchASCII( |
| base::kGTestFilterFlag, |
| "ServiceManagerContextBrowserTest.MANUAL_TerminateOnServiceQuit"); |
| new_test.AppendSwitch(kRunManualTestsFlag); |
| new_test.AppendSwitch(kSingleProcessTestsFlag); |
| |
| base::ScopedAllowBlockingForTesting allow; |
| std::string output; |
| base::GetAppOutputAndError(new_test, &output); |
| |
| #if !defined(OS_ANDROID) |
| // The test output contains the failure message. |
| // TODO(jamescook): The |output| is always empty on Android. I suspect the |
| // test runner does logs collection after the program has exited. |
| EXPECT_THAT(output, HasSubstr("Terminating because service 'echo' quit")); |
| #endif |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ServiceManagerContextBrowserTest, |
| ServiceProcessReportsPID) { |
| service_manager::mojom::ServiceManagerListenerPtr listener_proxy; |
| ServiceInstanceListener listener(mojo::MakeRequest(&listener_proxy)); |
| |
| auto* connector = ServiceManagerConnection::GetForProcess()->GetConnector(); |
| |
| service_manager::mojom::ServiceManagerPtr service_manager; |
| connector->BindInterface(service_manager::mojom::kServiceName, |
| &service_manager); |
| service_manager->AddListener(std::move(listener_proxy)); |
| listener.WaitForInit(); |
| |
| echo::mojom::EchoPtr echo_ptr; |
| connector->BindInterface(echo::mojom::kServiceName, &echo_ptr); |
| |
| // PID should be non-zero, confirming that it was indeed properly reported to |
| // the Service Manager. If not reported at all, this will hang. |
| EXPECT_GT(listener.WaitForServicePID(echo::mojom::kServiceName), 0u); |
| } |
| |
| } // namespace content |