| 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..12baf413a0f157cf538629c5fce777a682b3cc98 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,330 @@ 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) {
|
| + }
|
| + 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;
|
| + }
|
| +
|
| + bool restart_called() const { return restart_called_; }
|
| + bool remove_called() const { return remove_called_; }
|
| + bool checkin_called() const { return checkin_called_; }
|
| + bool write_called() const { return write_called_; }
|
| + bool delete_called() const { return delete_called_; }
|
| +
|
| + private:
|
| + FilePath file_;
|
| + 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(GetTempDirPath(),
|
| + "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\n";
|
| + 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\"?>\n"
|
| + "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
| + "<plist version=\"1.0\">\n"
|
| + "<dict>\n"
|
| + " <key>CFBundleDevelopmentRegion</key>\n"
|
| + " <string>English</string>\n"
|
| + " <key>CFBundleIdentifier</key>\n"
|
| + " <string>com.test.%s</string>\n"
|
| + " <key>CFBundleInfoDictionaryVersion</key>\n"
|
| + " <string>6.0</string>\n"
|
| + " <key>CFBundleExecutable</key>\n"
|
| + " <string>%s</string>\n"
|
| + " <key>CFBundleVersion</key>\n"
|
| + " <string>1</string>\n"
|
| + "</dict>\n"
|
| + "</plist>\n";
|
| + 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();
|
| + }
|
| +
|
| + const MockLaunchd* mock_launchd() const { return mock_launchd_.get(); }
|
| + const FilePath& executable_path() const { return executable_path_; }
|
| + const FilePath& bundle_path() const { return bundle_path_; }
|
| + const FilePath& GetTempDirPath() const { return temp_dir_.path(); }
|
| +
|
| + base::MessageLoopProxy* GetIOMessageLoopProxy() {
|
| + return io_thread_.message_loop_proxy().get();
|
| + }
|
| + void Run() { loop_.Run(); }
|
| +
|
| + private:
|
| + ScopedTempDir temp_dir_;
|
| + 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, DISABLED_DeleteFile) {
|
| + GetIOMessageLoopProxy()->PostTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&DeleteFunc, executable_path()));
|
| + Run();
|
| + ASSERT_TRUE(mock_launchd()->remove_called());
|
| + ASSERT_TRUE(mock_launchd()->delete_called());
|
| +}
|
| +
|
| +TEST_F(ServiceProcessStateFileManipulationTest, DISABLED_DeleteBundle) {
|
| + GetIOMessageLoopProxy()->PostTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&DeleteFunc, bundle_path()));
|
| + Run();
|
| + ASSERT_TRUE(mock_launchd()->remove_called());
|
| + ASSERT_TRUE(mock_launchd()->delete_called());
|
| +}
|
| +
|
| +TEST_F(ServiceProcessStateFileManipulationTest, DISABLED_MoveBundle) {
|
| + FilePath new_loc = GetTempDirPath().AppendASCII("MoveBundle");
|
| + GetIOMessageLoopProxy()->PostTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&MoveFunc, bundle_path(), new_loc));
|
| + Run();
|
| + ASSERT_TRUE(mock_launchd()->restart_called());
|
| + ASSERT_TRUE(mock_launchd()->write_called());
|
| +}
|
| +
|
| +TEST_F(ServiceProcessStateFileManipulationTest, DISABLED_MoveFile) {
|
| + FilePath new_loc = GetTempDirPath().AppendASCII("MoveFile");
|
| + GetIOMessageLoopProxy()->PostTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&MoveFunc, executable_path(), new_loc));
|
| + Run();
|
| + ASSERT_TRUE(mock_launchd()->remove_called());
|
| + ASSERT_TRUE(mock_launchd()->delete_called());
|
| +}
|
| +
|
| +TEST_F(ServiceProcessStateFileManipulationTest, DISABLED_TrashBundle) {
|
| + FSRef bundle_ref;
|
| + ASSERT_TRUE(base::mac::FSRefFromPath(bundle_path().value(), &bundle_ref));
|
| + GetIOMessageLoopProxy()->PostTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&TrashFunc, bundle_path()));
|
| + 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, DISABLED_ChangeAttr) {
|
| + ScopedAttributesRestorer restorer(bundle_path(), 0777);
|
| + GetIOMessageLoopProxy()->PostTask(
|
| + FROM_HERE,
|
| + NewRunnableFunction(&ChangeAttr, bundle_path(), 0222));
|
| + Run();
|
| + ASSERT_TRUE(mock_launchd()->remove_called());
|
| + ASSERT_TRUE(mock_launchd()->delete_called());
|
| +}
|
| +
|
| #endif // !OS_MACOSX
|
|
|