// Copyright 2019 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 #include "base/memory/shared_memory_mapping.h" #include "base/memory/unsafe_shared_memory_region.h" #include "base/optional.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/test/bind.h" #include "base/time/time.h" #include "base/timer/elapsed_timer.h" #include "content/public/browser/service_process_host.h" #include "content/public/test/browser_test.h" #include "content/public/test/content_browser_test.h" #include "services/test/echo/public/mojom/echo.mojom.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { using ServiceProcessHostBrowserTest = ContentBrowserTest; class EchoServiceProcessObserver : public ServiceProcessHost::Observer { public: EchoServiceProcessObserver() { ServiceProcessHost::AddObserver(this); } ~EchoServiceProcessObserver() override { ServiceProcessHost::RemoveObserver(this); } void WaitForLaunch() { launch_loop_.Run(); } void WaitForDeath() { death_loop_.Run(); } void WaitForCrash() { crash_loop_.Run(); } private: // ServiceProcessHost::Observer: void OnServiceProcessLaunched(const ServiceProcessInfo& info) override { if (info.IsService()) launch_loop_.Quit(); } void OnServiceProcessTerminatedNormally( const ServiceProcessInfo& info) override { if (info.IsService()) death_loop_.Quit(); } void OnServiceProcessCrashed(const ServiceProcessInfo& info) override { if (info.IsService()) crash_loop_.Quit(); } base::RunLoop launch_loop_; base::RunLoop death_loop_; base::RunLoop crash_loop_; DISALLOW_COPY_AND_ASSIGN(EchoServiceProcessObserver); }; IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, Launch) { EchoServiceProcessObserver observer; auto echo_service = ServiceProcessHost::Launch(); observer.WaitForLaunch(); const std::string kTestString = "Aurora borealis! At this time of year? At this time of day? " "In this part of the country? Localized entirely within your kitchen?"; base::RunLoop loop; echo_service->EchoString( kTestString, base::BindLambdaForTesting([&](const std::string& echoed_input) { EXPECT_EQ(kTestString, echoed_input); loop.Quit(); })); loop.Run(); } IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, LocalDisconnectQuits) { EchoServiceProcessObserver observer; auto echo_service = ServiceProcessHost::Launch(); observer.WaitForLaunch(); echo_service.reset(); observer.WaitForDeath(); } IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, RemoteDisconnectQuits) { EchoServiceProcessObserver observer; auto echo_service = ServiceProcessHost::Launch(); observer.WaitForLaunch(); echo_service->Quit(); observer.WaitForDeath(); } IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, AllMessagesReceived) { // Verifies that messages sent right before disconnection are always received // and dispatched by the service before it self-terminates. EchoServiceProcessObserver observer; auto echo_service = ServiceProcessHost::Launch(); const size_t kBufferSize = 256; const std::string kMessages[] = { "I thought we were having steamed clams.", "D'oh, no! I said steamed hams. That's what I call hamburgers.", "You call hamburgers, \"steamed hams?\"", "Yes. It's a regional dialect."}; auto region = base::UnsafeSharedMemoryRegion::Create(kBufferSize); base::WritableSharedMemoryMapping mapping = region.Map(); memset(mapping.memory(), 0, kBufferSize); // Send several messages, since it helps to verify a lack of raciness between // service-side message dispatch and service termination. for (const auto& message : kMessages) { ASSERT_LE(message.size(), kBufferSize); echo_service->EchoStringToSharedMemory(message, region.Duplicate()); } echo_service.reset(); observer.WaitForDeath(); const std::string& kLastMessage = kMessages[base::size(kMessages) - 1]; EXPECT_EQ(0, memcmp(mapping.memory(), kLastMessage.data(), kLastMessage.size())); } IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, ObserveCrash) { EchoServiceProcessObserver observer; auto echo_service = ServiceProcessHost::Launch(); observer.WaitForLaunch(); echo_service->Crash(); observer.WaitForCrash(); } IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, IdleTimeout) { EchoServiceProcessObserver observer; auto echo_service = ServiceProcessHost::Launch(); base::RunLoop wait_for_idle_loop; constexpr auto kTimeout = base::TimeDelta::FromSeconds(1); echo_service.set_idle_handler(kTimeout, base::BindLambdaForTesting([&] { wait_for_idle_loop.Quit(); echo_service.reset(); })); // Send a message and wait for the reply. Once the message is sent we should // observe at least |kTimeout| time elapsing before the RunLoop quits, because // the service process must wait at least that long to report itself as idle. base::ElapsedTimer timer; const std::string kTestString = "Yes, and you call them steamed hams despite the fact that they are " "obviously grilled."; echo_service->EchoString( kTestString, base::BindLambdaForTesting([&](const std::string& echoed_input) { EXPECT_EQ(kTestString, echoed_input); })); wait_for_idle_loop.Run(); EXPECT_GE(timer.Elapsed(), kTimeout); // And since the idle handler resets |echo_service|, we should imminently see // normal service process termination. observer.WaitForDeath(); } } // namespace content