1// Copyright (c) 2012 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 "chrome/browser/service_process/service_process_control.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/command_line.h"
10#include "base/path_service.h"
11#include "base/process/kill.h"
12#include "base/process/process_handle.h"
13#include "base/process/process_iterator.h"
14#include "base/test/test_timeouts.h"
15#include "chrome/browser/ui/browser.h"
16#include "chrome/common/chrome_constants.h"
17#include "chrome/common/chrome_version_info.h"
18#include "chrome/common/service_process_util.h"
19#include "chrome/test/base/in_process_browser_test.h"
20#include "chrome/test/base/ui_test_utils.h"
21#include "content/public/common/content_paths.h"
22#include "content/public/common/content_switches.h"
23#include "testing/gmock/include/gmock/gmock.h"
24
25class ServiceProcessControlBrowserTest
26    : public InProcessBrowserTest {
27 public:
28  ServiceProcessControlBrowserTest()
29      : service_process_handle_(base::kNullProcessHandle) {
30  }
31  virtual ~ServiceProcessControlBrowserTest() {
32    base::CloseProcessHandle(service_process_handle_);
33    service_process_handle_ = base::kNullProcessHandle;
34  }
35
36  void HistogramsCallback() {
37    MockHistogramsCallback();
38    QuitMessageLoop();
39  }
40
41  MOCK_METHOD0(MockHistogramsCallback, void());
42
43 protected:
44  void LaunchServiceProcessControl() {
45    // Launch the process asynchronously.
46    ServiceProcessControl::GetInstance()->Launch(
47        base::Bind(&ServiceProcessControlBrowserTest::ProcessControlLaunched,
48                   this),
49        base::Bind(
50            &ServiceProcessControlBrowserTest::ProcessControlLaunchFailed,
51            this));
52
53    // Then run the message loop to keep things running.
54    content::RunMessageLoop();
55  }
56
57  static void QuitMessageLoop() {
58    base::MessageLoop::current()->Quit();
59  }
60
61  static void CloudPrintInfoCallback(
62      const cloud_print::CloudPrintProxyInfo& proxy_info) {
63    QuitMessageLoop();
64  }
65
66  void Disconnect() {
67    // This will close the IPC connection.
68    ServiceProcessControl::GetInstance()->Disconnect();
69  }
70
71  virtual void SetUp() OVERRIDE {
72    service_process_handle_ = base::kNullProcessHandle;
73  }
74
75  virtual void TearDown() OVERRIDE {
76    if (ServiceProcessControl::GetInstance()->IsConnected())
77      EXPECT_TRUE(ServiceProcessControl::GetInstance()->Shutdown());
78#if defined(OS_MACOSX)
79    // ForceServiceProcessShutdown removes the process from launched on Mac.
80    ForceServiceProcessShutdown("", 0);
81#endif  // OS_MACOSX
82    if (service_process_handle_ != base::kNullProcessHandle) {
83      EXPECT_TRUE(base::WaitForSingleProcess(
84          service_process_handle_,
85          TestTimeouts::action_max_timeout()));
86      service_process_handle_ = base::kNullProcessHandle;
87    }
88  }
89
90  void ProcessControlLaunched() {
91    base::ProcessId service_pid;
92    EXPECT_TRUE(GetServiceProcessData(NULL, &service_pid));
93    EXPECT_NE(static_cast<base::ProcessId>(0), service_pid);
94    EXPECT_TRUE(base::OpenProcessHandleWithAccess(
95        service_pid,
96        base::kProcessAccessWaitForTermination |
97        // we need query permission to get exit code
98        base::kProcessAccessQueryInformation,
99        &service_process_handle_));
100    // Quit the current message. Post a QuitTask instead of just calling Quit()
101    // because this can get invoked in the context of a Launch() call and we
102    // may not be in Run() yet.
103    base::MessageLoop::current()->PostTask(FROM_HERE,
104                                           base::MessageLoop::QuitClosure());
105  }
106
107  void ProcessControlLaunchFailed() {
108    ADD_FAILURE();
109    // Quit the current message.
110    base::MessageLoop::current()->PostTask(FROM_HERE,
111                                           base::MessageLoop::QuitClosure());
112  }
113
114 private:
115  base::ProcessHandle service_process_handle_;
116};
117
118class RealServiceProcessControlBrowserTest
119      : public ServiceProcessControlBrowserTest {
120 public:
121  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
122    ServiceProcessControlBrowserTest::SetUpCommandLine(command_line);
123    base::FilePath exe;
124    PathService::Get(base::DIR_EXE, &exe);
125#if defined(OS_MACOSX)
126    exe = exe.DirName().DirName().DirName();
127#endif
128    exe = exe.Append(chrome::kHelperProcessExecutablePath);
129    // Run chrome instead of browser_tests.exe.
130    EXPECT_TRUE(base::PathExists(exe));
131    command_line->AppendSwitchPath(switches::kBrowserSubprocessPath, exe);
132  }
133};
134
135// TODO(vitalybuka): Fix crbug.com/340563
136IN_PROC_BROWSER_TEST_F(RealServiceProcessControlBrowserTest,
137                       DISABLED_LaunchAndIPC) {
138  LaunchServiceProcessControl();
139
140  // Make sure we are connected to the service process.
141  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
142  ServiceProcessControl::GetInstance()->GetCloudPrintProxyInfo(
143        base::Bind(&ServiceProcessControlBrowserTest::CloudPrintInfoCallback));
144  content::RunMessageLoop();
145
146  // And then shutdown the service process.
147  EXPECT_TRUE(ServiceProcessControl::GetInstance()->Shutdown());
148}
149
150IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, LaunchAndIPC) {
151  LaunchServiceProcessControl();
152
153  // Make sure we are connected to the service process.
154  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
155  ServiceProcessControl::GetInstance()->GetCloudPrintProxyInfo(
156        base::Bind(&ServiceProcessControlBrowserTest::CloudPrintInfoCallback));
157  content::RunMessageLoop();
158
159  // And then shutdown the service process.
160  EXPECT_TRUE(ServiceProcessControl::GetInstance()->Shutdown());
161}
162
163// This tests the case when a service process is launched when the browser
164// starts but we try to launch it again while setting up Cloud Print.
165IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, LaunchTwice) {
166  // Launch the service process the first time.
167  LaunchServiceProcessControl();
168
169  // Make sure we are connected to the service process.
170  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
171  EXPECT_TRUE(ServiceProcessControl::GetInstance()->GetCloudPrintProxyInfo(
172        base::Bind(&ServiceProcessControlBrowserTest::CloudPrintInfoCallback)));
173  content::RunMessageLoop();
174
175  // Launch the service process again.
176  LaunchServiceProcessControl();
177  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
178  EXPECT_TRUE(ServiceProcessControl::GetInstance()->GetCloudPrintProxyInfo(
179        base::Bind(&ServiceProcessControlBrowserTest::CloudPrintInfoCallback)));
180  content::RunMessageLoop();
181}
182
183static void DecrementUntilZero(int* count) {
184  (*count)--;
185  if (!(*count))
186    base::MessageLoop::current()->PostTask(FROM_HERE,
187                                           base::MessageLoop::QuitClosure());
188}
189
190// Invoke multiple Launch calls in succession and ensure that all the tasks
191// get invoked.
192IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest,
193                       MultipleLaunchTasks) {
194  ServiceProcessControl* process = ServiceProcessControl::GetInstance();
195  int launch_count = 5;
196  for (int i = 0; i < launch_count; i++) {
197    // Launch the process asynchronously.
198    process->Launch(base::Bind(&DecrementUntilZero, &launch_count),
199                    base::MessageLoop::QuitClosure());
200  }
201  // Then run the message loop to keep things running.
202  content::RunMessageLoop();
203  EXPECT_EQ(0, launch_count);
204}
205
206// Make sure using the same task for success and failure tasks works.
207IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, SameLaunchTask) {
208  ServiceProcessControl* process = ServiceProcessControl::GetInstance();
209  int launch_count = 5;
210  for (int i = 0; i < launch_count; i++) {
211    // Launch the process asynchronously.
212    base::Closure task = base::Bind(&DecrementUntilZero, &launch_count);
213    process->Launch(task, task);
214  }
215  // Then run the message loop to keep things running.
216  content::RunMessageLoop();
217  EXPECT_EQ(0, launch_count);
218}
219
220// Tests whether disconnecting from the service IPC causes the service process
221// to die.
222IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, DieOnDisconnect) {
223  // Launch the service process.
224  LaunchServiceProcessControl();
225  // Make sure we are connected to the service process.
226  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
227  Disconnect();
228}
229
230IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, ForceShutdown) {
231  // Launch the service process.
232  LaunchServiceProcessControl();
233  // Make sure we are connected to the service process.
234  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
235  base::ProcessId service_pid;
236  EXPECT_TRUE(GetServiceProcessData(NULL, &service_pid));
237  EXPECT_NE(static_cast<base::ProcessId>(0), service_pid);
238  chrome::VersionInfo version_info;
239  ForceServiceProcessShutdown(version_info.Version(), service_pid);
240}
241
242IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, CheckPid) {
243  base::ProcessId service_pid;
244  EXPECT_FALSE(GetServiceProcessData(NULL, &service_pid));
245  // Launch the service process.
246  LaunchServiceProcessControl();
247  EXPECT_TRUE(GetServiceProcessData(NULL, &service_pid));
248  EXPECT_NE(static_cast<base::ProcessId>(0), service_pid);
249  // Disconnect from service process.
250  Disconnect();
251}
252
253IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, HistogramsNoService) {
254  ASSERT_FALSE(ServiceProcessControl::GetInstance()->IsConnected());
255  EXPECT_CALL(*this, MockHistogramsCallback()).Times(0);
256  EXPECT_FALSE(ServiceProcessControl::GetInstance()->GetHistograms(
257      base::Bind(&ServiceProcessControlBrowserTest::HistogramsCallback,
258                 base::Unretained(this)),
259      base::TimeDelta()));
260}
261
262IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, HistogramsTimeout) {
263  LaunchServiceProcessControl();
264  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
265  // Callback should not be called during GetHistograms call.
266  EXPECT_CALL(*this, MockHistogramsCallback()).Times(0);
267  EXPECT_TRUE(ServiceProcessControl::GetInstance()->GetHistograms(
268      base::Bind(&ServiceProcessControlBrowserTest::HistogramsCallback,
269                 base::Unretained(this)),
270      base::TimeDelta::FromMilliseconds(100)));
271  EXPECT_CALL(*this, MockHistogramsCallback()).Times(1);
272  EXPECT_TRUE(ServiceProcessControl::GetInstance()->Shutdown());
273  content::RunMessageLoop();
274}
275
276IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, Histograms) {
277  LaunchServiceProcessControl();
278  ASSERT_TRUE(ServiceProcessControl::GetInstance()->IsConnected());
279  // Callback should not be called during GetHistograms call.
280  EXPECT_CALL(*this, MockHistogramsCallback()).Times(0);
281  // Wait for real callback by providing large timeout value.
282  EXPECT_TRUE(ServiceProcessControl::GetInstance()->GetHistograms(
283      base::Bind(&ServiceProcessControlBrowserTest::HistogramsCallback,
284                base::Unretained(this)),
285      base::TimeDelta::FromHours(1)));
286  EXPECT_CALL(*this, MockHistogramsCallback()).Times(1);
287  content::RunMessageLoop();
288}
289