1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <string.h>
6 
7 #include "base/memory/shared_memory_mapping.h"
8 #include "base/memory/unsafe_shared_memory_region.h"
9 #include "base/optional.h"
10 #include "base/run_loop.h"
11 #include "base/stl_util.h"
12 #include "base/test/bind.h"
13 #include "base/time/time.h"
14 #include "base/timer/elapsed_timer.h"
15 #include "content/public/browser/service_process_host.h"
16 #include "content/public/test/browser_test.h"
17 #include "content/public/test/content_browser_test.h"
18 #include "services/test/echo/public/mojom/echo.mojom.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20 
21 namespace content {
22 
23 using ServiceProcessHostBrowserTest = ContentBrowserTest;
24 
25 class EchoServiceProcessObserver : public ServiceProcessHost::Observer {
26  public:
EchoServiceProcessObserver()27   EchoServiceProcessObserver() { ServiceProcessHost::AddObserver(this); }
28 
~EchoServiceProcessObserver()29   ~EchoServiceProcessObserver() override {
30     ServiceProcessHost::RemoveObserver(this);
31   }
32 
WaitForLaunch()33   void WaitForLaunch() { launch_loop_.Run(); }
WaitForDeath()34   void WaitForDeath() { death_loop_.Run(); }
WaitForCrash()35   void WaitForCrash() { crash_loop_.Run(); }
36 
37  private:
38   // ServiceProcessHost::Observer:
OnServiceProcessLaunched(const ServiceProcessInfo & info)39   void OnServiceProcessLaunched(const ServiceProcessInfo& info) override {
40     if (info.IsService<echo::mojom::EchoService>())
41       launch_loop_.Quit();
42   }
43 
OnServiceProcessTerminatedNormally(const ServiceProcessInfo & info)44   void OnServiceProcessTerminatedNormally(
45       const ServiceProcessInfo& info) override {
46     if (info.IsService<echo::mojom::EchoService>())
47       death_loop_.Quit();
48   }
49 
OnServiceProcessCrashed(const ServiceProcessInfo & info)50   void OnServiceProcessCrashed(const ServiceProcessInfo& info) override {
51     if (info.IsService<echo::mojom::EchoService>())
52       crash_loop_.Quit();
53   }
54 
55   base::RunLoop launch_loop_;
56   base::RunLoop death_loop_;
57   base::RunLoop crash_loop_;
58 
59   DISALLOW_COPY_AND_ASSIGN(EchoServiceProcessObserver);
60 };
61 
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest,Launch)62 IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, Launch) {
63   EchoServiceProcessObserver observer;
64   auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
65   observer.WaitForLaunch();
66 
67   const std::string kTestString =
68       "Aurora borealis! At this time of year? At this time of day? "
69       "In this part of the country? Localized entirely within your kitchen?";
70   base::RunLoop loop;
71   echo_service->EchoString(
72       kTestString,
73       base::BindLambdaForTesting([&](const std::string& echoed_input) {
74         EXPECT_EQ(kTestString, echoed_input);
75         loop.Quit();
76       }));
77   loop.Run();
78 }
79 
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest,LocalDisconnectQuits)80 IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, LocalDisconnectQuits) {
81   EchoServiceProcessObserver observer;
82   auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
83   observer.WaitForLaunch();
84   echo_service.reset();
85   observer.WaitForDeath();
86 }
87 
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest,RemoteDisconnectQuits)88 IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, RemoteDisconnectQuits) {
89   EchoServiceProcessObserver observer;
90   auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
91   observer.WaitForLaunch();
92   echo_service->Quit();
93   observer.WaitForDeath();
94 }
95 
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest,AllMessagesReceived)96 IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, AllMessagesReceived) {
97   // Verifies that messages sent right before disconnection are always received
98   // and dispatched by the service before it self-terminates.
99   EchoServiceProcessObserver observer;
100   auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
101 
102   const size_t kBufferSize = 256;
103   const std::string kMessages[] = {
104       "I thought we were having steamed clams.",
105       "D'oh, no! I said steamed hams. That's what I call hamburgers.",
106       "You call hamburgers, \"steamed hams?\"",
107       "Yes. It's a regional dialect."};
108   auto region = base::UnsafeSharedMemoryRegion::Create(kBufferSize);
109   base::WritableSharedMemoryMapping mapping = region.Map();
110   memset(mapping.memory(), 0, kBufferSize);
111 
112   // Send several messages, since it helps to verify a lack of raciness between
113   // service-side message dispatch and service termination.
114   for (const auto& message : kMessages) {
115     ASSERT_LE(message.size(), kBufferSize);
116     echo_service->EchoStringToSharedMemory(message, region.Duplicate());
117   }
118   echo_service.reset();
119   observer.WaitForDeath();
120 
121   const std::string& kLastMessage = kMessages[base::size(kMessages) - 1];
122   EXPECT_EQ(0,
123             memcmp(mapping.memory(), kLastMessage.data(), kLastMessage.size()));
124 }
125 
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest,ObserveCrash)126 IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, ObserveCrash) {
127   EchoServiceProcessObserver observer;
128   auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
129   observer.WaitForLaunch();
130   echo_service->Crash();
131   observer.WaitForCrash();
132 }
133 
IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest,IdleTimeout)134 IN_PROC_BROWSER_TEST_F(ServiceProcessHostBrowserTest, IdleTimeout) {
135   EchoServiceProcessObserver observer;
136   auto echo_service = ServiceProcessHost::Launch<echo::mojom::EchoService>();
137 
138   base::RunLoop wait_for_idle_loop;
139   constexpr auto kTimeout = base::TimeDelta::FromSeconds(1);
140   echo_service.set_idle_handler(kTimeout, base::BindLambdaForTesting([&] {
141                                   wait_for_idle_loop.Quit();
142                                   echo_service.reset();
143                                 }));
144 
145   // Send a message and wait for the reply. Once the message is sent we should
146   // observe at least |kTimeout| time elapsing before the RunLoop quits, because
147   // the service process must wait at least that long to report itself as idle.
148   base::ElapsedTimer timer;
149   const std::string kTestString =
150       "Yes, and you call them steamed hams despite the fact that they are "
151       "obviously grilled.";
152   echo_service->EchoString(
153       kTestString,
154       base::BindLambdaForTesting([&](const std::string& echoed_input) {
155         EXPECT_EQ(kTestString, echoed_input);
156       }));
157   wait_for_idle_loop.Run();
158   EXPECT_GE(timer.Elapsed(), kTimeout);
159 
160   // And since the idle handler resets |echo_service|, we should imminently see
161   // normal service process termination.
162   observer.WaitForDeath();
163 }
164 
165 }  // namespace content
166