Index: chrome/common/service_process_util_unittest.cc |
diff --git a/chrome/common/service_process_util_unittest.cc b/chrome/common/service_process_util_unittest.cc |
index 03827e0260fe3d034400f6cc2e82d1c1a75b35c3..96a862150cac990642934b4f7bebf58258199e4f 100644 |
--- a/chrome/common/service_process_util_unittest.cc |
+++ b/chrome/common/service_process_util_unittest.cc |
@@ -1,12 +1,12 @@ |
-// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Copyright (c) 2011 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 "chrome/common/service_process_util.h" |
+ |
#include "base/basictypes.h" |
#if !defined(OS_MACOSX) |
-// TODO(dmaclach): Figure out tests that will work with launchd on Mac OS. |
- |
#include "base/at_exit.h" |
#include "base/command_line.h" |
#include "base/process_util.h" |
@@ -18,7 +18,6 @@ |
#include "base/utf_string_conversions.h" |
#include "chrome/common/chrome_switches.h" |
#include "chrome/common/chrome_version_info.h" |
-#include "chrome/common/service_process_util.h" |
#include "testing/multiprocess_func_list.h" |
#if defined(OS_WIN) |
@@ -89,24 +88,24 @@ void ServiceProcessStateTest::LaunchAndWait(const std::string& name) { |
} |
TEST_F(ServiceProcessStateTest, Singleton) { |
- ServiceProcessState* state = ServiceProcessState::GetInstance(); |
- ASSERT_TRUE(state->Initialize()); |
+ ServiceProcessState state; |
+ ASSERT_TRUE(state.Initialize()); |
LaunchAndWait("ServiceProcessStateTestSingleton"); |
} |
TEST_F(ServiceProcessStateTest, ReadyState) { |
ASSERT_FALSE(CheckServiceProcessReady()); |
- ServiceProcessState* state = ServiceProcessState::GetInstance(); |
- ASSERT_TRUE(state->Initialize()); |
- ASSERT_TRUE(state->SignalReady(IOMessageLoopProxy(), NULL)); |
+ ServiceProcessState state; |
+ ASSERT_TRUE(state.Initialize()); |
+ ASSERT_TRUE(state.SignalReady(IOMessageLoopProxy(), NULL)); |
LaunchAndWait("ServiceProcessStateTestReadyTrue"); |
- state->SignalStopped(); |
+ state.SignalStopped(); |
LaunchAndWait("ServiceProcessStateTestReadyFalse"); |
} |
TEST_F(ServiceProcessStateTest, AutoRun) { |
- ServiceProcessState* state = ServiceProcessState::GetInstance(); |
- ASSERT_TRUE(state->AddToAutoRun()); |
+ ServiceProcessState state; |
+ ASSERT_TRUE(state.AddToAutoRun()); |
scoped_ptr<CommandLine> autorun_command_line; |
#if defined(OS_WIN) |
std::string value_name = GetServiceProcessScopedName("_service_run"); |
@@ -139,7 +138,7 @@ TEST_F(ServiceProcessStateTest, AutoRun) { |
EXPECT_EQ(autorun_command_line->GetSwitchValueASCII(switches::kProcessType), |
std::string(switches::kServiceProcess)); |
} |
- ASSERT_TRUE(state->RemoveFromAutoRun()); |
+ ASSERT_TRUE(state.RemoveFromAutoRun()); |
#if defined(OS_WIN) |
EXPECT_FALSE(base::win::ReadCommandFromAutoRun(HKEY_CURRENT_USER, |
UTF8ToWide(value_name), |
@@ -161,8 +160,8 @@ TEST_F(ServiceProcessStateTest, SharedMem) { |
// implementation on Posix, this check will only execute on Windows. |
ASSERT_FALSE(GetServiceProcessData(&version, &pid)); |
#endif // defined(OS_WIN) |
- ServiceProcessState* state = ServiceProcessState::GetInstance(); |
- ASSERT_TRUE(state->Initialize()); |
+ ServiceProcessState state; |
+ ASSERT_TRUE(state.Initialize()); |
ASSERT_TRUE(GetServiceProcessData(&version, &pid)); |
ASSERT_EQ(base::GetCurrentProcId(), pid); |
} |
@@ -186,8 +185,8 @@ TEST_F(ServiceProcessStateTest, ForceShutdown) { |
} |
MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestSingleton) { |
- ServiceProcessState* state = ServiceProcessState::GetInstance(); |
- EXPECT_FALSE(state->Initialize()); |
+ ServiceProcessState state; |
+ EXPECT_FALSE(state.Initialize()); |
return 0; |
} |
@@ -207,11 +206,11 @@ MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) { |
base::Thread io_thread_("ServiceProcessStateTestShutdownIOThread"); |
base::Thread::Options options(MessageLoop::TYPE_IO, 0); |
EXPECT_TRUE(io_thread_.StartWithOptions(options)); |
- ServiceProcessState* state = ServiceProcessState::GetInstance(); |
- EXPECT_TRUE(state->Initialize()); |
- EXPECT_TRUE(state->SignalReady(io_thread_.message_loop_proxy(), |
- NewRunnableFunction(&ShutdownTask, |
- MessageLoop::current()))); |
+ ServiceProcessState state; |
+ EXPECT_TRUE(state.Initialize()); |
+ EXPECT_TRUE(state.SignalReady(io_thread_.message_loop_proxy(), |
+ NewRunnableFunction(&ShutdownTask, |
+ MessageLoop::current()))); |
message_loop.PostDelayedTask(FROM_HERE, |
new MessageLoop::QuitTask(), |
TestTimeouts::action_max_timeout_ms()); |
@@ -221,4 +220,312 @@ MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) { |
return 0; |
} |
+#else // !OS_MACOSX |
+ |
+#include <CoreFoundation/CoreFoundation.h> |
+ |
+#include <launch.h> |
+#include <sys/stat.h> |
+ |
+#include "base/file_path.h" |
+#include "base/file_util.h" |
+#include "base/mac/mac_util.h" |
+#include "base/mac/scoped_cftyperef.h" |
+#include "base/message_loop.h" |
+#include "base/scoped_temp_dir.h" |
+#include "base/stringprintf.h" |
+#include "base/sys_string_conversions.h" |
+#include "base/test/test_timeouts.h" |
+#include "base/threading/thread.h" |
+#include "chrome/common/launchd_mac.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+// TODO(dmaclach): Write this in terms of a real mock. |
+// http://crbug.com/76923 |
+class MockLaunchd : public Launchd { |
+ public: |
+ MockLaunchd(const FilePath& file, MessageLoop* loop) |
+ : file_(file), |
+ message_loop_(loop), |
+ restart_called_(false), |
+ remove_called_(false), |
+ checkin_called_(false), |
+ write_called_(false), |
+ delete_called_(false) |
Mark Mentovai
2011/03/22 00:48:24
Nit: { goes on this line.
dmac
2011/03/22 03:13:32
Done.
|
+ { } |
+ virtual ~MockLaunchd() { } |
+ |
+ virtual CFDictionaryRef CopyExports() OVERRIDE { |
+ ADD_FAILURE(); |
+ return NULL; |
+ } |
+ |
+ virtual CFDictionaryRef CopyJobDictionary(CFStringRef label) OVERRIDE { |
+ ADD_FAILURE(); |
+ return NULL; |
+ } |
+ |
+ virtual CFDictionaryRef CopyDictionaryByCheckingIn(CFErrorRef* error) |
+ OVERRIDE { |
+ checkin_called_ = true; |
+ CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM); |
+ CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS); |
+ const void *keys[] = { program, program_args }; |
+ base::mac::ScopedCFTypeRef<CFStringRef> path( |
+ base::SysUTF8ToCFStringRef(file_.value())); |
+ const void *array_values[] = { path.get() }; |
+ base::mac::ScopedCFTypeRef<CFArrayRef> args( |
+ CFArrayCreate(kCFAllocatorDefault, |
+ array_values, |
+ 1, |
+ &kCFTypeArrayCallBacks)); |
+ const void *values[] = { path, args }; |
+ return CFDictionaryCreate(kCFAllocatorDefault, |
+ keys, |
+ values, |
+ arraysize(keys), |
+ &kCFTypeDictionaryKeyCallBacks, |
+ &kCFTypeDictionaryValueCallBacks); |
+ } |
+ |
+ virtual bool RemoveJob(CFStringRef label, CFErrorRef* error) OVERRIDE { |
+ remove_called_ = true; |
+ message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask); |
+ return true; |
+ } |
+ |
+ virtual bool RestartJob(Domain domain, |
+ Type type, |
+ CFStringRef name, |
+ CFStringRef session_type) OVERRIDE { |
+ restart_called_ = true; |
+ message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask); |
+ return true; |
+ } |
+ |
+ virtual CFMutableDictionaryRef CreatePlistFromFile( |
+ Domain domain, |
+ Type type, |
+ CFStringRef name) OVERRIDE { |
+ base::mac::ScopedCFTypeRef<CFDictionaryRef> dict( |
+ CopyDictionaryByCheckingIn(NULL)); |
+ return CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict); |
+ } |
+ |
+ virtual bool WritePlistToFile(Domain domain, |
+ Type type, |
+ CFStringRef name, |
+ CFDictionaryRef dict) OVERRIDE { |
+ write_called_ = true; |
+ return true; |
+ } |
+ |
+ virtual bool DeletePlist(Domain domain, |
+ Type type, |
+ CFStringRef name) OVERRIDE { |
+ delete_called_ = true; |
+ return true; |
+ } |
+ |
+ FilePath file_; |
Mark Mentovai
2011/03/22 00:48:24
OK for now, but the data should really be in a pri
dmac
2011/03/22 03:13:32
Done.
|
+ MessageLoop* message_loop_; |
+ bool restart_called_; |
+ bool remove_called_; |
+ bool checkin_called_; |
+ bool write_called_; |
+ bool delete_called_; |
+}; |
+ |
+class ServiceProcessStateFileManipulationTest : public ::testing::Test { |
+ protected: |
+ ServiceProcessStateFileManipulationTest() |
+ : io_thread_("ServiceProcessStateFileManipulationTest_IO") { |
+ } |
+ virtual ~ServiceProcessStateFileManipulationTest() { } |
+ |
+ virtual void SetUp() { |
+ base::Thread::Options options; |
+ options.message_loop_type = MessageLoop::TYPE_IO; |
+ ASSERT_TRUE(io_thread_.StartWithOptions(options)); |
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
+ ASSERT_TRUE(MakeABundle(temp_dir_.path(), |
+ "Test", |
+ &bundle_path_, |
+ &executable_path_)); |
+ mock_launchd_.reset(new MockLaunchd(executable_path_, &loop_)); |
+ scoped_launchd_instance_.reset( |
+ new Launchd::ScopedInstance(mock_launchd_.get())); |
+ ASSERT_TRUE(service_process_state_.Initialize()); |
+ ASSERT_TRUE(service_process_state_.SignalReady( |
+ io_thread_.message_loop_proxy(), |
+ NULL)); |
+ loop_.PostDelayedTask(FROM_HERE, |
+ new MessageLoop::QuitTask, |
+ TestTimeouts::large_test_timeout_ms()); |
+ } |
+ |
+ bool MakeABundle(const FilePath& dst, |
+ const std::string& name, |
+ FilePath* bundle_root, |
+ FilePath* executable) { |
+ *bundle_root = dst.Append(name + std::string(".app")); |
+ FilePath contents = bundle_root->AppendASCII("Contents"); |
+ FilePath mac_os = contents.AppendASCII("MacOS"); |
+ *executable = mac_os.Append(name); |
+ FilePath info_plist = contents.Append("Info.plist"); |
+ |
+ if (!file_util::CreateDirectory(mac_os)) { |
+ return false; |
+ } |
+ const char *data = "#! testbundle"; |
Mark Mentovai
2011/03/22 00:48:24
Put a \n at the end of this string.
dmac
2011/03/22 03:13:32
Done.
|
+ int len = strlen(data); |
+ if (file_util::WriteFile(*executable, data, len) != len) { |
+ return false; |
+ } |
+ if (chmod(executable->value().c_str(), 0555) != 0) { |
+ return false; |
+ } |
+ |
+ const char* info_plist_format = |
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r" |
Mark Mentovai
2011/03/22 00:48:24
Your lines should end with \n throughout this stri
dmac
2011/03/22 03:13:32
I'm a classy kind of guy. Done.
|
+ "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\r" |
+ "<plist version=\"1.0\">\r" |
+ "<dict>\r" |
+ " <key>CFBundleDevelopmentRegion</key>\r" |
+ " <string>English</string>\r" |
+ " <key>CFBundleIdentifier</key>\r" |
+ " <string>com.test.%s</string>\r" |
+ " <key>CFBundleInfoDictionaryVersion</key>\r" |
+ " <string>6.0</string>\r" |
+ " <key>CFBundleExecutable</key>\r" |
+ " <string>%s</string>\r" |
+ " <key>CFBundleVersion</key>\r" |
+ " <string>1</string>\r" |
+ "</dict>\r" |
+ "</plist>\r"; |
+ std::string info_plist_data = base::StringPrintf(info_plist_format, |
+ name.c_str(), |
+ name.c_str()); |
+ len = info_plist_data.length(); |
+ if (file_util::WriteFile(info_plist, info_plist_data.c_str(), len) != len) { |
+ return false; |
+ } |
+ const UInt8* bundle_root_path = |
+ reinterpret_cast<const UInt8*>(bundle_root->value().c_str()); |
+ base::mac::ScopedCFTypeRef<CFURLRef> url( |
+ CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, |
+ bundle_root_path, |
+ bundle_root->value().length(), |
+ true)); |
+ base::mac::ScopedCFTypeRef<CFBundleRef> bundle( |
+ CFBundleCreate(kCFAllocatorDefault, url)); |
+ return bundle.get(); |
+ } |
+ |
+ ScopedTempDir temp_dir_; |
Mark Mentovai
2011/03/22 00:48:24
Make private when able, as in the class above.
dmac
2011/03/22 03:13:32
Done.
|
+ MessageLoopForUI loop_; |
+ base::Thread io_thread_; |
+ FilePath executable_path_, bundle_path_; |
+ scoped_ptr<MockLaunchd> mock_launchd_; |
+ scoped_ptr<Launchd::ScopedInstance> scoped_launchd_instance_; |
+ ServiceProcessState service_process_state_; |
+}; |
+ |
+void DeleteFunc(const FilePath& file) { |
+ EXPECT_TRUE(file_util::Delete(file, true)); |
+} |
+ |
+void MoveFunc(const FilePath& from, const FilePath& to) { |
+ EXPECT_TRUE(file_util::Move(from, to)); |
+} |
+ |
+void ChangeAttr(const FilePath& from, int mode) { |
+ EXPECT_EQ(chmod(from.value().c_str(), mode), 0); |
+} |
+ |
+class ScopedAttributesRestorer { |
+ public: |
+ ScopedAttributesRestorer(const FilePath& path, int mode) |
+ : path_(path), mode_(mode) { |
+ } |
+ ~ScopedAttributesRestorer() { |
+ ChangeAttr(path_, mode_); |
+ } |
+ private: |
+ FilePath path_; |
+ int mode_; |
+}; |
+ |
+void TrashFunc(const FilePath& src) { |
+ FSRef path_ref; |
+ FSRef new_path_ref; |
+ EXPECT_TRUE(base::mac::FSRefFromPath(src.value(), &path_ref)); |
+ OSStatus status = FSMoveObjectToTrashSync(&path_ref, |
+ &new_path_ref, |
+ kFSFileOperationDefaultOptions); |
+ EXPECT_EQ(status, noErr) << "FSMoveObjectToTrashSync " << status; |
+} |
+ |
+TEST_F(ServiceProcessStateFileManipulationTest, DeleteFile) { |
+ io_thread_.message_loop_proxy()->PostTask( |
+ FROM_HERE, |
+ NewRunnableFunction(&DeleteFunc, executable_path_)); |
+ loop_.Run(); |
+ ASSERT_TRUE(mock_launchd_->remove_called_); |
+ ASSERT_TRUE(mock_launchd_->delete_called_); |
+} |
+ |
+TEST_F(ServiceProcessStateFileManipulationTest, DeleteBundle) { |
+ io_thread_.message_loop_proxy()->PostTask( |
+ FROM_HERE, |
+ NewRunnableFunction(&DeleteFunc, bundle_path_)); |
+ loop_.Run(); |
+ ASSERT_TRUE(mock_launchd_->remove_called_); |
+ ASSERT_TRUE(mock_launchd_->delete_called_); |
+} |
+ |
+TEST_F(ServiceProcessStateFileManipulationTest, MoveBundle) { |
+ FilePath new_loc = temp_dir_.path().AppendASCII("MoveBundle"); |
+ io_thread_.message_loop_proxy()->PostTask( |
+ FROM_HERE, |
+ NewRunnableFunction(&MoveFunc, bundle_path_, new_loc)); |
+ loop_.Run(); |
+ ASSERT_TRUE(mock_launchd_->restart_called_); |
+ ASSERT_TRUE(mock_launchd_->write_called_); |
+} |
+ |
+TEST_F(ServiceProcessStateFileManipulationTest, MoveFile) { |
+ FilePath new_loc = temp_dir_.path().AppendASCII("MoveFile"); |
+ io_thread_.message_loop_proxy()->PostTask( |
+ FROM_HERE, |
+ NewRunnableFunction(&MoveFunc, executable_path_, new_loc)); |
+ loop_.Run(); |
+ ASSERT_TRUE(mock_launchd_->remove_called_); |
+ ASSERT_TRUE(mock_launchd_->delete_called_); |
+} |
+ |
+TEST_F(ServiceProcessStateFileManipulationTest, TrashBundle) { |
+ FSRef bundle_ref; |
+ ASSERT_TRUE(base::mac::FSRefFromPath(bundle_path_.value(), &bundle_ref)); |
+ io_thread_.message_loop_proxy()->PostTask( |
+ FROM_HERE, |
+ NewRunnableFunction(&TrashFunc, bundle_path_)); |
+ loop_.Run(); |
+ ASSERT_TRUE(mock_launchd_->remove_called_); |
+ ASSERT_TRUE(mock_launchd_->delete_called_); |
+ std::string path(base::mac::PathFromFSRef(bundle_ref)); |
+ FilePath file_path(path); |
+ ASSERT_TRUE(file_util::Delete(file_path, true)); |
+} |
+ |
+TEST_F(ServiceProcessStateFileManipulationTest, ChangeAttr) { |
+ ScopedAttributesRestorer restorer(bundle_path_, 0777); |
+ io_thread_.message_loop_proxy()->PostTask( |
+ FROM_HERE, |
+ NewRunnableFunction(&ChangeAttr, bundle_path_, 0222)); |
+ loop_.Run(); |
+ ASSERT_TRUE(mock_launchd_->remove_called_); |
+ ASSERT_TRUE(mock_launchd_->delete_called_); |
+} |
+ |
#endif // !OS_MACOSX |