1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Use of this source code is governed by a BSD-style license that can be
3731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// found in the LICENSE file.
4731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/service_process_util.h"
6ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
7dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/basictypes.h"
8dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
9dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if !defined(OS_MACOSX)
1021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/at_exit.h"
11dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/command_line.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_ptr.h"
13731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/process_util.h"
14731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/string_util.h"
1572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/test/multiprocess_test.h"
1672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/test/test_timeouts.h"
1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "base/threading/thread.h"
18dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/utf_string_conversions.h"
19dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/common/chrome_switches.h"
20731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/common/chrome_version_info.h"
2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "testing/multiprocess_func_list.h"
22731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
23dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if defined(OS_WIN)
24dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "base/win/win_util.h"
25dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif
26dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if defined(OS_LINUX)
28dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include <glib.h>
29dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "chrome/common/auto_start_linux.h"
30dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif
31dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
3272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsennamespace {
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool g_good_shutdown = false;
3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ShutdownTask(MessageLoop* loop) {
3772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Quit the main message loop.
3872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_FALSE(g_good_shutdown);
3972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  g_good_shutdown = true;
4072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
4172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
4272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
4372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}  // namespace
44731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
45731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickTEST(ServiceProcessUtilTest, ScopedVersionedName) {
46731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string test_str = "test";
47731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  std::string scoped_name = GetServiceProcessScopedVersionedName(test_str);
48731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  chrome::VersionInfo version_info;
49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(version_info.is_valid());
50731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(EndsWith(scoped_name, test_str, true));
51731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_NE(std::string::npos, scoped_name.find(version_info.Version()));
52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
53731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenclass ServiceProcessStateTest : public base::MultiProcessTest {
5572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen public:
5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ServiceProcessStateTest();
5772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ~ServiceProcessStateTest();
5872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  virtual void SetUp();
5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::MessageLoopProxy* IOMessageLoopProxy() {
6072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return io_thread_.message_loop_proxy();
6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
6272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  void LaunchAndWait(const std::string& name);
6372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
6421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen private:
6521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // This is used to release the ServiceProcessState singleton after each test.
6621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  base::ShadowingAtExitManager at_exit_manager_;
6772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Thread io_thread_;
6821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen};
6921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
7072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenServiceProcessStateTest::ServiceProcessStateTest()
7172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : io_thread_("ServiceProcessStateTestThread") {
7272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
7372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
7472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenServiceProcessStateTest::~ServiceProcessStateTest() {
7572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
7672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
7772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ServiceProcessStateTest::SetUp() {
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Thread::Options options(MessageLoop::TYPE_IO, 0);
7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(io_thread_.StartWithOptions(options));
8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
8172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
8272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ServiceProcessStateTest::LaunchAndWait(const std::string& name) {
8372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ProcessHandle handle = SpawnChild(name, false);
8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(handle);
8572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  int exit_code = 0;
8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(base::WaitForExitCode(handle, &exit_code));
8772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_EQ(exit_code, 0);
8872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
8972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
9021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenTEST_F(ServiceProcessStateTest, Singleton) {
91ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessState state;
92ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(state.Initialize());
9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LaunchAndWait("ServiceProcessStateTestSingleton");
94731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
95731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
9621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenTEST_F(ServiceProcessStateTest, ReadyState) {
9772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_FALSE(CheckServiceProcessReady());
98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessState state;
99ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(state.Initialize());
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(state.SignalReady(IOMessageLoopProxy(), NULL));
10172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LaunchAndWait("ServiceProcessStateTestReadyTrue");
102ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  state.SignalStopped();
10372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LaunchAndWait("ServiceProcessStateTestReadyFalse");
104731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
105731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
106dc0f95d653279beabeb9817299e2902918ba123eKristian MonsenTEST_F(ServiceProcessStateTest, AutoRun) {
107ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessState state;
108ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(state.AddToAutoRun());
109dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  scoped_ptr<CommandLine> autorun_command_line;
110dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if defined(OS_WIN)
111dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  std::string value_name = GetServiceProcessScopedName("_service_run");
112dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  string16 value;
113dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER,
114dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                                UTF8ToWide(value_name),
115dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                                &value));
116dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  autorun_command_line.reset(new CommandLine(CommandLine::FromString(value)));
117dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#elif defined(OS_LINUX)
118dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if defined(GOOGLE_CHROME_BUILD)
119dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  std::string base_desktop_name = "google-chrome-service.desktop";
120dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#else  // CHROMIUM_BUILD
121dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  std::string base_desktop_name = "chromium-service.desktop";
122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif
123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  std::string exec_value;
124dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_TRUE(AutoStart::GetAutostartFileValue(
125dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      GetServiceProcessScopedName(base_desktop_name), "Exec", &exec_value));
126dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  GError *error = NULL;
127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  gchar **argv = NULL;
128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  gint argc = 0;
129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (g_shell_parse_argv(exec_value.c_str(), &argc, &argv, &error)) {
130dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    autorun_command_line.reset(new CommandLine(argc, argv));
131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    g_strfreev(argv);
132dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  } else {
133dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    ADD_FAILURE();
134dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    g_error_free(error);
135dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
136dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif  // defined(OS_WIN)
137dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (autorun_command_line.get()) {
138dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    EXPECT_EQ(autorun_command_line->GetSwitchValueASCII(switches::kProcessType),
139dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen              std::string(switches::kServiceProcess));
140dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  }
141ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(state.RemoveFromAutoRun());
142dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#if defined(OS_WIN)
143dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER,
144dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                                 UTF8ToWide(value_name),
145dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen                                                 &value));
146dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#elif defined(OS_LINUX)
147dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  EXPECT_FALSE(AutoStart::GetAutostartFileValue(
148dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      GetServiceProcessScopedName(base_desktop_name), "Exec", &exec_value));
149dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif  // defined(OS_WIN)
150dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen}
151dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen
15221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenTEST_F(ServiceProcessStateTest, SharedMem) {
15372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string version;
15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ProcessId pid;
155731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#if defined(OS_WIN)
156731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // On Posix, named shared memory uses a file on disk. This file
157731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // could be lying around from previous crashes which could cause
158731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // GetServiceProcessPid to lie. On Windows, we use a named event so we
159731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // don't have this issue. Until we have a more stable shared memory
160731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // implementation on Posix, this check will only execute on Windows.
161dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  ASSERT_FALSE(GetServiceProcessData(&version, &pid));
162731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#endif  // defined(OS_WIN)
163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessState state;
164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(state.Initialize());
165dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  ASSERT_TRUE(GetServiceProcessData(&version, &pid));
16672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_EQ(base::GetCurrentProcId(), pid);
16772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
16872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
16972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenTEST_F(ServiceProcessStateTest, ForceShutdown) {
17072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ProcessHandle handle = SpawnChild("ServiceProcessStateTestShutdown",
17172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                          true);
17272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(handle);
17372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  for (int i = 0; !CheckServiceProcessReady() && i < 10; ++i) {
17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    base::PlatformThread::Sleep(TestTimeouts::tiny_timeout_ms());
17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(CheckServiceProcessReady());
17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string version;
17872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ProcessId pid;
179dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  ASSERT_TRUE(GetServiceProcessData(&version, &pid));
18072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(ForceServiceProcessShutdown(version, pid));
18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  int exit_code = 0;
18272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_TRUE(base::WaitForExitCodeWithTimeout(handle,
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      &exit_code, TestTimeouts::action_max_timeout_ms()));
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::CloseProcessHandle(handle);
18572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ASSERT_EQ(exit_code, 0);
18672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
18872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenMULTIPROCESS_TEST_MAIN(ServiceProcessStateTestSingleton) {
189ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessState state;
190ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_FALSE(state.Initialize());
19172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return 0;
19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenMULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyTrue) {
19572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_TRUE(CheckServiceProcessReady());
19672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return 0;
19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
19872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
19972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenMULTIPROCESS_TEST_MAIN(ServiceProcessStateTestReadyFalse) {
20072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_FALSE(CheckServiceProcessReady());
20172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return 0;
20272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
20372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
20472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenMULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) {
20572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  MessageLoop message_loop;
20672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  message_loop.set_thread_name("ServiceProcessStateTestShutdownMainThread");
20772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Thread io_thread_("ServiceProcessStateTestShutdownIOThread");
20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::Thread::Options options(MessageLoop::TYPE_IO, 0);
20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_TRUE(io_thread_.StartWithOptions(options));
210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessState state;
211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(state.Initialize());
212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(state.SignalReady(io_thread_.message_loop_proxy(),
213ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                NewRunnableFunction(&ShutdownTask,
214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                    MessageLoop::current())));
21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  message_loop.PostDelayedTask(FROM_HERE,
21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                               new MessageLoop::QuitTask(),
21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                               TestTimeouts::action_max_timeout_ms());
21872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_FALSE(g_good_shutdown);
21972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  message_loop.Run();
22072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  EXPECT_TRUE(g_good_shutdown);
22172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return 0;
222731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
223731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
224ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#else  // !OS_MACOSX
225ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
226ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <CoreFoundation/CoreFoundation.h>
227ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
228ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <launch.h>
229ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include <sys/stat.h>
230ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
231ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/file_path.h"
232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/file_util.h"
233ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/mac/mac_util.h"
234ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/mac/scoped_cftyperef.h"
235ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/memory/scoped_temp_dir.h"
236ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/message_loop.h"
237ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/stringprintf.h"
238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/sys_string_conversions.h"
239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/test/test_timeouts.h"
240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/threading/thread.h"
241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/launchd_mac.h"
242ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "testing/gtest/include/gtest/gtest.h"
243ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
244ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TODO(dmaclach): Write this in terms of a real mock.
245ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// http://crbug.com/76923
246ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenclass MockLaunchd : public Launchd {
247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen public:
248ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  MockLaunchd(const FilePath& file, MessageLoop* loop)
249ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      : file_(file),
250ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        message_loop_(loop),
251ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        restart_called_(false),
252ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        remove_called_(false),
253ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        checkin_called_(false),
254ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        write_called_(false),
255ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        delete_called_(false) {
256ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
257ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual ~MockLaunchd() { }
258ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
259ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual CFDictionaryRef CopyExports() OVERRIDE {
260ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ADD_FAILURE();
261ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return NULL;
262ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
263ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
264ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual CFDictionaryRef CopyJobDictionary(CFStringRef label) OVERRIDE {
265ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ADD_FAILURE();
266ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return NULL;
267ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
268ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
269ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual CFDictionaryRef CopyDictionaryByCheckingIn(CFErrorRef* error)
270ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      OVERRIDE {
271ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    checkin_called_ = true;
272ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM);
273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS);
274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const void *keys[] = { program, program_args };
275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::mac::ScopedCFTypeRef<CFStringRef> path(
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        base::SysUTF8ToCFStringRef(file_.value()));
277ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const void *array_values[] = { path.get() };
278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::mac::ScopedCFTypeRef<CFArrayRef> args(
279ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        CFArrayCreate(kCFAllocatorDefault,
280ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                      array_values,
281ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                      1,
282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                      &kCFTypeArrayCallBacks));
283ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const void *values[] = { path, args };
284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return CFDictionaryCreate(kCFAllocatorDefault,
285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              keys,
286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              values,
287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              arraysize(keys),
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              &kCFTypeDictionaryKeyCallBacks,
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              &kCFTypeDictionaryValueCallBacks);
290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual bool RemoveJob(CFStringRef label, CFErrorRef* error) OVERRIDE {
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    remove_called_ = true;
294ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask);
295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return true;
296ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
297ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
298ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual bool RestartJob(Domain domain,
299ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                          Type type,
300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                          CFStringRef name,
301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                          CFStringRef session_type) OVERRIDE {
302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    restart_called_ = true;
303ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask);
304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return true;
305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual CFMutableDictionaryRef CreatePlistFromFile(
308ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Domain domain,
309ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Type type,
310ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      CFStringRef name) OVERRIDE {
311ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::mac::ScopedCFTypeRef<CFDictionaryRef> dict(
312ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        CopyDictionaryByCheckingIn(NULL));
313ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict);
314ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual bool WritePlistToFile(Domain domain,
317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                Type type,
318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                CFStringRef name,
319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                CFDictionaryRef dict) OVERRIDE {
320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    write_called_ = true;
321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return true;
322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
323ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
324ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual bool DeletePlist(Domain domain,
325ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           Type type,
326ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                           CFStringRef name) OVERRIDE {
327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    delete_called_ = true;
328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return true;
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool restart_called() const { return restart_called_; }
332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool remove_called() const { return remove_called_; }
333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool checkin_called() const { return checkin_called_; }
334ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool write_called() const { return write_called_; }
335ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool delete_called() const { return delete_called_; }
336ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
337ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen private:
338ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath file_;
339ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  MessageLoop* message_loop_;
340ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool restart_called_;
341ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool remove_called_;
342ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool checkin_called_;
343ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool write_called_;
344ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool delete_called_;
345ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenclass ServiceProcessStateFileManipulationTest : public ::testing::Test {
348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen protected:
349ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessStateFileManipulationTest()
350ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      : io_thread_("ServiceProcessStateFileManipulationTest_IO") {
351ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
352ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual ~ServiceProcessStateFileManipulationTest() { }
353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
354ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  virtual void SetUp() {
355ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::Thread::Options options;
356ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    options.message_loop_type = MessageLoop::TYPE_IO;
357ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ASSERT_TRUE(io_thread_.StartWithOptions(options));
358ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
359ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ASSERT_TRUE(MakeABundle(GetTempDirPath(),
360ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            "Test",
361ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            &bundle_path_,
362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                            &executable_path_));
363ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    mock_launchd_.reset(new MockLaunchd(executable_path_, &loop_));
364ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    scoped_launchd_instance_.reset(
365ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        new Launchd::ScopedInstance(mock_launchd_.get()));
366ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ASSERT_TRUE(service_process_state_.Initialize());
367ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ASSERT_TRUE(service_process_state_.SignalReady(
368ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        io_thread_.message_loop_proxy(),
369ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        NULL));
370ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    loop_.PostDelayedTask(FROM_HERE,
371ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                          new MessageLoop::QuitTask,
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                          TestTimeouts::action_max_timeout_ms());
373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool MakeABundle(const FilePath& dst,
376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                   const std::string& name,
377ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                   FilePath* bundle_root,
378ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                   FilePath* executable) {
379ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    *bundle_root = dst.Append(name + std::string(".app"));
380ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    FilePath contents = bundle_root->AppendASCII("Contents");
381ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    FilePath mac_os = contents.AppendASCII("MacOS");
382ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    *executable = mac_os.Append(name);
383ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    FilePath info_plist = contents.Append("Info.plist");
384ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
385ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!file_util::CreateDirectory(mac_os)) {
386ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return false;
387ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
388ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const char *data = "#! testbundle\n";
389ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    int len = strlen(data);
390ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (file_util::WriteFile(*executable, data, len) != len) {
391ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return false;
392ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
393ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (chmod(executable->value().c_str(), 0555) != 0) {
394ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return false;
395ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
396ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
397ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const char* info_plist_format =
398ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
399ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
400ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "<plist version=\"1.0\">\n"
401ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "<dict>\n"
402ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <key>CFBundleDevelopmentRegion</key>\n"
403ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <string>English</string>\n"
404ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <key>CFBundleIdentifier</key>\n"
405ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <string>com.test.%s</string>\n"
406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <key>CFBundleInfoDictionaryVersion</key>\n"
407ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <string>6.0</string>\n"
408ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <key>CFBundleExecutable</key>\n"
409ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <string>%s</string>\n"
410ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <key>CFBundleVersion</key>\n"
411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "  <string>1</string>\n"
412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "</dict>\n"
413ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      "</plist>\n";
414ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    std::string info_plist_data = base::StringPrintf(info_plist_format,
415ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                     name.c_str(),
416ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                                     name.c_str());
417ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    len = info_plist_data.length();
418ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (file_util::WriteFile(info_plist, info_plist_data.c_str(), len) != len) {
419ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return false;
420ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
421ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const UInt8* bundle_root_path =
422ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        reinterpret_cast<const UInt8*>(bundle_root->value().c_str());
423ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::mac::ScopedCFTypeRef<CFURLRef> url(
424ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
425ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                              bundle_root_path,
426ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                              bundle_root->value().length(),
427ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                              true));
428ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    base::mac::ScopedCFTypeRef<CFBundleRef> bundle(
429ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        CFBundleCreate(kCFAllocatorDefault, url));
430ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return bundle.get();
431ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
433ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const MockLaunchd* mock_launchd() const { return mock_launchd_.get(); }
434ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const FilePath& executable_path() const { return executable_path_; }
435ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const FilePath& bundle_path() const { return bundle_path_; }
436ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const FilePath& GetTempDirPath() const { return temp_dir_.path(); }
437ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
438ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::MessageLoopProxy* GetIOMessageLoopProxy() {
439ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return io_thread_.message_loop_proxy().get();
440ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
441ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void Run() { loop_.Run(); }
442ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
443ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen private:
444ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ScopedTempDir temp_dir_;
445ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  MessageLoopForUI loop_;
446ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::Thread io_thread_;
447ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath executable_path_, bundle_path_;
448ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_ptr<MockLaunchd> mock_launchd_;
449ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_ptr<Launchd::ScopedInstance> scoped_launchd_instance_;
450ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ServiceProcessState service_process_state_;
451ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
452ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
453ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid DeleteFunc(const FilePath& file) {
454ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(file_util::Delete(file, true));
455ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
456ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
457ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid MoveFunc(const FilePath& from, const FilePath& to) {
458ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(file_util::Move(from, to));
459ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
460ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
461ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ChangeAttr(const FilePath& from, int mode) {
462ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_EQ(chmod(from.value().c_str(), mode), 0);
463ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
464ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
465ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenclass ScopedAttributesRestorer {
466ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen public:
467ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ScopedAttributesRestorer(const FilePath& path, int mode)
468ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      : path_(path), mode_(mode) {
469ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
470ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ~ScopedAttributesRestorer() {
471ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ChangeAttr(path_, mode_);
472ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
473ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen private:
474ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath path_;
475ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int mode_;
476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen};
477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
478ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TrashFunc(const FilePath& src) {
479ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FSRef path_ref;
480ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FSRef new_path_ref;
481ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_TRUE(base::mac::FSRefFromPath(src.value(), &path_ref));
482ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  OSStatus status = FSMoveObjectToTrashSync(&path_ref,
483ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                            &new_path_ref,
484ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                            kFSFileOperationDefaultOptions);
485ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  EXPECT_EQ(status, noErr)  << "FSMoveObjectToTrashSync " << status;
486ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
487ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
488ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(ServiceProcessStateFileManipulationTest, DeleteFile) {
489ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  GetIOMessageLoopProxy()->PostTask(
490ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      FROM_HERE,
491ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableFunction(&DeleteFunc, executable_path()));
492ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Run();
493ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->remove_called());
494ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->delete_called());
495ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
496ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
497ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(ServiceProcessStateFileManipulationTest, DeleteBundle) {
498ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  GetIOMessageLoopProxy()->PostTask(
499ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      FROM_HERE,
500ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableFunction(&DeleteFunc, bundle_path()));
501ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Run();
502ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->remove_called());
503ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->delete_called());
504ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
505ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
506ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(ServiceProcessStateFileManipulationTest, MoveBundle) {
507ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath new_loc = GetTempDirPath().AppendASCII("MoveBundle");
508ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  GetIOMessageLoopProxy()->PostTask(
509ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      FROM_HERE,
510ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableFunction(&MoveFunc, bundle_path(), new_loc));
511ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Run();
512ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->restart_called());
513ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->write_called());
514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
516ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(ServiceProcessStateFileManipulationTest, MoveFile) {
517ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath new_loc = GetTempDirPath().AppendASCII("MoveFile");
518ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  GetIOMessageLoopProxy()->PostTask(
519ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      FROM_HERE,
520ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableFunction(&MoveFunc, executable_path(), new_loc));
521ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Run();
522ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->remove_called());
523ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->delete_called());
524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(ServiceProcessStateFileManipulationTest, TrashBundle) {
527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FSRef bundle_ref;
528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(base::mac::FSRefFromPath(bundle_path().value(), &bundle_ref));
529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  GetIOMessageLoopProxy()->PostTask(
530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      FROM_HERE,
531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableFunction(&TrashFunc, bundle_path()));
532ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Run();
533ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->remove_called());
534ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->delete_called());
535ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::string path(base::mac::PathFromFSRef(bundle_ref));
536ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  FilePath file_path(path);
537ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(file_util::Delete(file_path, true));
538ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
539ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
540ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenTEST_F(ServiceProcessStateFileManipulationTest, ChangeAttr) {
541ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ScopedAttributesRestorer restorer(bundle_path(), 0777);
542ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  GetIOMessageLoopProxy()->PostTask(
543ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      FROM_HERE,
544ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NewRunnableFunction(&ChangeAttr, bundle_path(), 0222));
545ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Run();
546ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->remove_called());
547ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ASSERT_TRUE(mock_launchd()->delete_called());
548ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
549ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
550dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#endif  // !OS_MACOSX
551