| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 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/common/service_process_util.h" |
| 6 |
| 7 #import <Foundation/Foundation.h> |
| 8 |
| 9 #include "base/bind.h" |
| 10 #include "base/command_line.h" |
| 11 #include "base/files/file_path.h" |
| 12 #include "base/files/file_util.h" |
| 13 #include "base/files/scoped_temp_dir.h" |
| 14 #include "base/mac/mac_util.h" |
| 15 #include "base/mac/scoped_nsobject.h" |
| 16 #include "base/process/launch.h" |
| 17 #include "base/run_loop.h" |
| 18 #include "base/strings/sys_string_conversions.h" |
| 19 #include "base/test/test_timeouts.h" |
| 20 #include "base/threading/thread.h" |
| 21 #include "chrome/common/mac/launchd.h" |
| 22 #include "chrome/common/mac/mock_launchd.h" |
| 23 #include "testing/gtest/include/gtest/gtest.h" |
| 24 |
| 25 // Once Chrome no longer supports OSX 10.7, everything within this |
| 26 // preprocessor block can be removed. |
| 27 #if !defined(MAC_OS_X_VERSION_10_8) || \ |
| 28 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8 |
| 29 @interface NSFileManager (Chrome) |
| 30 - (BOOL)trashItemAtURL:(NSURL*)url |
| 31 resultingItemURL:(NSURL* __nullable* __nullable)outResultingURL |
| 32 error:(NSError**)error; |
| 33 @end |
| 34 #endif |
| 35 |
| 36 class ServiceProcessStateFileManipulationTest : public ::testing::Test { |
| 37 public: |
| 38 void TrashFunc(const base::FilePath& src) { |
| 39 NSURL* url = [NSURL fileURLWithPath:base::SysUTF8ToNSString(src.value())]; |
| 40 ASSERT_TRUE(url); |
| 41 NSURL* resultingItemURL = nil; |
| 42 BOOL success = |
| 43 [[NSFileManager defaultManager] trashItemAtURL:url |
| 44 resultingItemURL:&resultingItemURL |
| 45 error:nil]; |
| 46 ASSERT_TRUE(success); |
| 47 trashed_url_.reset([resultingItemURL retain]); |
| 48 } |
| 49 |
| 50 protected: |
| 51 ServiceProcessStateFileManipulationTest() |
| 52 : io_thread_("ServiceProcessStateFileManipulationTest_IO") {} |
| 53 |
| 54 void SetUp() override { |
| 55 base::Thread::Options options; |
| 56 options.message_loop_type = base::MessageLoop::TYPE_IO; |
| 57 ASSERT_TRUE(io_thread_.StartWithOptions(options)); |
| 58 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| 59 ASSERT_TRUE(MockLaunchd::MakeABundle(GetTempDirPath(), "Test", |
| 60 &bundle_path_, &executable_path_)); |
| 61 mock_launchd_.reset( |
| 62 new MockLaunchd(executable_path_, &loop_, false, false)); |
| 63 scoped_launchd_instance_.reset( |
| 64 new Launchd::ScopedInstance(mock_launchd_.get())); |
| 65 ASSERT_TRUE(service_process_state_.Initialize()); |
| 66 ASSERT_TRUE(service_process_state_.SignalReady( |
| 67 io_thread_.task_runner().get(), base::Closure())); |
| 68 loop_.task_runner()->PostDelayedTask(FROM_HERE, |
| 69 run_loop_.QuitWhenIdleClosure(), |
| 70 TestTimeouts::action_max_timeout()); |
| 71 } |
| 72 |
| 73 const MockLaunchd* mock_launchd() const { return mock_launchd_.get(); } |
| 74 const base::FilePath& executable_path() const { return executable_path_; } |
| 75 const base::FilePath& bundle_path() const { return bundle_path_; } |
| 76 const base::FilePath& GetTempDirPath() const { return temp_dir_.GetPath(); } |
| 77 |
| 78 base::SingleThreadTaskRunner* GetIOTaskRunner() { |
| 79 return io_thread_.task_runner().get(); |
| 80 } |
| 81 void Run() { run_loop_.Run(); } |
| 82 |
| 83 base::scoped_nsobject<NSURL> trashed_url_; |
| 84 |
| 85 private: |
| 86 base::ScopedTempDir temp_dir_; |
| 87 base::MessageLoopForUI loop_; |
| 88 base::RunLoop run_loop_; |
| 89 base::Thread io_thread_; |
| 90 base::FilePath executable_path_, bundle_path_; |
| 91 std::unique_ptr<MockLaunchd> mock_launchd_; |
| 92 std::unique_ptr<Launchd::ScopedInstance> scoped_launchd_instance_; |
| 93 ServiceProcessState service_process_state_; |
| 94 }; |
| 95 |
| 96 void DeleteFunc(const base::FilePath& file) { |
| 97 EXPECT_TRUE(base::DeleteFile(file, true)); |
| 98 } |
| 99 |
| 100 void MoveFunc(const base::FilePath& from, const base::FilePath& to) { |
| 101 EXPECT_TRUE(base::Move(from, to)); |
| 102 } |
| 103 |
| 104 void ChangeAttr(const base::FilePath& from, int mode) { |
| 105 EXPECT_EQ(chmod(from.value().c_str(), mode), 0); |
| 106 } |
| 107 |
| 108 class ScopedAttributesRestorer { |
| 109 public: |
| 110 ScopedAttributesRestorer(const base::FilePath& path, int mode) |
| 111 : path_(path), mode_(mode) {} |
| 112 ~ScopedAttributesRestorer() { ChangeAttr(path_, mode_); } |
| 113 |
| 114 private: |
| 115 base::FilePath path_; |
| 116 int mode_; |
| 117 }; |
| 118 |
| 119 TEST_F(ServiceProcessStateFileManipulationTest, VerifyLaunchD) { |
| 120 // There have been problems where launchd has gotten into a bad state, usually |
| 121 // because something had deleted all the files in /tmp. launchd depends on |
| 122 // a Unix Domain Socket that it creates at /tmp/launchd*/sock. |
| 123 // The symptom of this problem is that the service process connect fails |
| 124 // on Mac and "launch_msg(): Socket is not connected" appears. |
| 125 // This test is designed to make sure that launchd is working. |
| 126 // http://crbug/75518 |
| 127 // Note: This particular problem no longer affects launchd in 10.10+, since |
| 128 // there is no user owned launchd process and sockets are no longer made at |
| 129 // /tmp/launchd*/sock. This test is still useful as a sanity check to make |
| 130 // sure that launchd appears to be working. |
| 131 |
| 132 base::CommandLine cl(base::FilePath("/bin/launchctl")); |
| 133 cl.AppendArg("limit"); |
| 134 |
| 135 std::string output; |
| 136 int exit_code = -1; |
| 137 ASSERT_TRUE(base::GetAppOutputWithExitCode(cl, &output, &exit_code) && |
| 138 exit_code == 0) |
| 139 << " exit_code:" << exit_code << " " << output; |
| 140 } |
| 141 |
| 142 TEST_F(ServiceProcessStateFileManipulationTest, DeleteFile) { |
| 143 GetIOTaskRunner()->PostTask(FROM_HERE, |
| 144 base::Bind(&DeleteFunc, executable_path())); |
| 145 Run(); |
| 146 ASSERT_TRUE(mock_launchd()->remove_called()); |
| 147 ASSERT_TRUE(mock_launchd()->delete_called()); |
| 148 } |
| 149 |
| 150 TEST_F(ServiceProcessStateFileManipulationTest, DeleteBundle) { |
| 151 GetIOTaskRunner()->PostTask(FROM_HERE, |
| 152 base::Bind(&DeleteFunc, bundle_path())); |
| 153 Run(); |
| 154 ASSERT_TRUE(mock_launchd()->remove_called()); |
| 155 ASSERT_TRUE(mock_launchd()->delete_called()); |
| 156 } |
| 157 |
| 158 TEST_F(ServiceProcessStateFileManipulationTest, MoveBundle) { |
| 159 base::FilePath new_loc = GetTempDirPath().AppendASCII("MoveBundle"); |
| 160 GetIOTaskRunner()->PostTask(FROM_HERE, |
| 161 base::Bind(&MoveFunc, bundle_path(), new_loc)); |
| 162 Run(); |
| 163 ASSERT_TRUE(mock_launchd()->restart_called()); |
| 164 ASSERT_TRUE(mock_launchd()->write_called()); |
| 165 } |
| 166 |
| 167 TEST_F(ServiceProcessStateFileManipulationTest, MoveFile) { |
| 168 base::FilePath new_loc = GetTempDirPath().AppendASCII("MoveFile"); |
| 169 GetIOTaskRunner()->PostTask( |
| 170 FROM_HERE, base::Bind(&MoveFunc, executable_path(), new_loc)); |
| 171 Run(); |
| 172 ASSERT_TRUE(mock_launchd()->remove_called()); |
| 173 ASSERT_TRUE(mock_launchd()->delete_called()); |
| 174 } |
| 175 |
| 176 TEST_F(ServiceProcessStateFileManipulationTest, TrashBundle) { |
| 177 GetIOTaskRunner()->PostTask( |
| 178 FROM_HERE, base::Bind(&ServiceProcessStateFileManipulationTest::TrashFunc, |
| 179 base::Unretained(this), bundle_path())); |
| 180 Run(); |
| 181 ASSERT_TRUE(mock_launchd()->remove_called()); |
| 182 ASSERT_TRUE(mock_launchd()->delete_called()); |
| 183 std::string path(base::SysNSStringToUTF8([trashed_url_ path])); |
| 184 base::FilePath file_path(path); |
| 185 ASSERT_TRUE(base::DeleteFile(file_path, true)); |
| 186 } |
| 187 |
| 188 TEST_F(ServiceProcessStateFileManipulationTest, ChangeAttr) { |
| 189 ScopedAttributesRestorer restorer(bundle_path(), 0777); |
| 190 GetIOTaskRunner()->PostTask(FROM_HERE, |
| 191 base::Bind(&ChangeAttr, bundle_path(), 0222)); |
| 192 Run(); |
| 193 ASSERT_TRUE(mock_launchd()->remove_called()); |
| 194 ASSERT_TRUE(mock_launchd()->delete_called()); |
| 195 } |
| OLD | NEW |