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 <errno.h> 6 #include <fcntl.h> 7 #include <sys/file.h> 8 9 #include "chrome/browser/process_singleton.h" 10 11 #include "base/file_util.h" 12 #include "base/path_service.h" 13 #include "base/posix/eintr_wrapper.h" 14 #include "chrome/common/chrome_constants.h" 15 #include "chrome/common/chrome_paths.h" 16 #include "chrome/test/base/testing_profile.h" 17 #include "testing/platform_test.h" 18 19 namespace { 20 21 class ProcessSingletonMacTest : public PlatformTest { 22 public: 23 virtual void SetUp() { 24 PlatformTest::SetUp(); 25 26 // Put the lock in a temporary directory. Doesn't need to be a 27 // full profile to test this code. 28 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 29 lock_path_ = temp_dir_.path().Append(chrome::kSingletonLockFilename); 30 } 31 32 virtual void TearDown() { 33 PlatformTest::TearDown(); 34 35 // Verify that the lock was released. 36 EXPECT_FALSE(IsLocked()); 37 } 38 39 // Return |true| if the file exists and is locked. Forces a failure 40 // in the containing test in case of error condition. 41 bool IsLocked() { 42 int fd = HANDLE_EINTR(open(lock_path_.value().c_str(), O_RDONLY)); 43 if (fd == -1) { 44 EXPECT_EQ(ENOENT, errno) << "Unexpected error opening lockfile."; 45 return false; 46 } 47 48 file_util::ScopedFD auto_close(&fd); 49 50 int rc = HANDLE_EINTR(flock(fd, LOCK_EX|LOCK_NB)); 51 52 // Got the lock, so it wasn't already locked. Close releases. 53 if (rc != -1) 54 return false; 55 56 // Someone else has the lock. 57 if (errno == EWOULDBLOCK) 58 return true; 59 60 EXPECT_EQ(EWOULDBLOCK, errno) << "Unexpected error acquiring lock."; 61 return false; 62 } 63 64 base::ScopedTempDir temp_dir_; 65 base::FilePath lock_path_; 66 }; 67 68 // Test that the base case doesn't blow up. 69 TEST_F(ProcessSingletonMacTest, Basic) { 70 ProcessSingleton ps(temp_dir_.path(), 71 ProcessSingleton::NotificationCallback()); 72 EXPECT_FALSE(IsLocked()); 73 EXPECT_TRUE(ps.Create()); 74 EXPECT_TRUE(IsLocked()); 75 ps.Cleanup(); 76 EXPECT_FALSE(IsLocked()); 77 } 78 79 // The destructor should release the lock. 80 TEST_F(ProcessSingletonMacTest, DestructorReleases) { 81 EXPECT_FALSE(IsLocked()); 82 { 83 ProcessSingleton ps(temp_dir_.path(), 84 ProcessSingleton::NotificationCallback()); 85 EXPECT_TRUE(ps.Create()); 86 EXPECT_TRUE(IsLocked()); 87 } 88 EXPECT_FALSE(IsLocked()); 89 } 90 91 // Multiple singletons should interlock appropriately. 92 TEST_F(ProcessSingletonMacTest, Interlock) { 93 ProcessSingleton ps1(temp_dir_.path(), 94 ProcessSingleton::NotificationCallback()); 95 ProcessSingleton ps2(temp_dir_.path(), 96 ProcessSingleton::NotificationCallback()); 97 98 // Windows and Linux use a command-line flag to suppress this, but 99 // it is on a sub-process so the scope is contained. Rather than 100 // add additional API to process_singleton.h in an #ifdef, just tell 101 // the reader what to expect and move on. 102 LOG(ERROR) << "Expect two failures to obtain the lock."; 103 104 // When |ps1| has the lock, |ps2| cannot get it. 105 EXPECT_FALSE(IsLocked()); 106 EXPECT_TRUE(ps1.Create()); 107 EXPECT_TRUE(IsLocked()); 108 EXPECT_FALSE(ps2.Create()); 109 ps1.Cleanup(); 110 111 // And when |ps2| has the lock, |ps1| cannot get it. 112 EXPECT_FALSE(IsLocked()); 113 EXPECT_TRUE(ps2.Create()); 114 EXPECT_TRUE(IsLocked()); 115 EXPECT_FALSE(ps1.Create()); 116 ps2.Cleanup(); 117 EXPECT_FALSE(IsLocked()); 118 } 119 120 // Like |Interlock| test, but via |NotifyOtherProcessOrCreate()|. 121 TEST_F(ProcessSingletonMacTest, NotifyOtherProcessOrCreate) { 122 ProcessSingleton ps1(temp_dir_.path(), 123 ProcessSingleton::NotificationCallback()); 124 ProcessSingleton ps2(temp_dir_.path(), 125 ProcessSingleton::NotificationCallback()); 126 127 // Windows and Linux use a command-line flag to suppress this, but 128 // it is on a sub-process so the scope is contained. Rather than 129 // add additional API to process_singleton.h in an #ifdef, just tell 130 // the reader what to expect and move on. 131 LOG(ERROR) << "Expect two failures to obtain the lock."; 132 133 // When |ps1| has the lock, |ps2| cannot get it. 134 EXPECT_FALSE(IsLocked()); 135 EXPECT_EQ( 136 ProcessSingleton::PROCESS_NONE, 137 ps1.NotifyOtherProcessOrCreate()); 138 EXPECT_TRUE(IsLocked()); 139 EXPECT_EQ( 140 ProcessSingleton::PROFILE_IN_USE, 141 ps2.NotifyOtherProcessOrCreate()); 142 ps1.Cleanup(); 143 144 // And when |ps2| has the lock, |ps1| cannot get it. 145 EXPECT_FALSE(IsLocked()); 146 EXPECT_EQ( 147 ProcessSingleton::PROCESS_NONE, 148 ps2.NotifyOtherProcessOrCreate()); 149 EXPECT_TRUE(IsLocked()); 150 EXPECT_EQ( 151 ProcessSingleton::PROFILE_IN_USE, 152 ps1.NotifyOtherProcessOrCreate()); 153 ps2.Cleanup(); 154 EXPECT_FALSE(IsLocked()); 155 } 156 157 // TODO(shess): Test that the lock is released when the process dies. 158 // DEATH_TEST? I don't know. If the code to communicate between 159 // browser processes is ever written, this all would need to be tested 160 // more like the other platforms, in which case it would be easy. 161 162 } // namespace 163