1 // Copyright (c) 2010 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/eintr_wrapper.h" 12 #include "base/file_util.h" 13 #include "base/path_service.h" 14 #include "chrome/common/chrome_constants.h" 15 #include "chrome/common/chrome_paths.h" 16 #include "chrome/test/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 ScopedTempDir temp_dir_; 65 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 EXPECT_FALSE(IsLocked()); 72 EXPECT_TRUE(ps.Create()); 73 EXPECT_TRUE(IsLocked()); 74 ps.Cleanup(); 75 EXPECT_FALSE(IsLocked()); 76 } 77 78 // The destructor should release the lock. 79 TEST_F(ProcessSingletonMacTest, DestructorReleases) { 80 EXPECT_FALSE(IsLocked()); 81 { 82 ProcessSingleton ps(temp_dir_.path()); 83 EXPECT_TRUE(ps.Create()); 84 EXPECT_TRUE(IsLocked()); 85 } 86 EXPECT_FALSE(IsLocked()); 87 } 88 89 // Multiple singletons should interlock appropriately. 90 TEST_F(ProcessSingletonMacTest, Interlock) { 91 ProcessSingleton ps1(temp_dir_.path()); 92 ProcessSingleton ps2(temp_dir_.path()); 93 94 // Windows and Linux use a command-line flag to suppress this, but 95 // it is on a sub-process so the scope is contained. Rather than 96 // add additional API to process_singleton.h in an #ifdef, just tell 97 // the reader what to expect and move on. 98 LOG(ERROR) << "Expect two failures to obtain the lock."; 99 100 // When |ps1| has the lock, |ps2| cannot get it. 101 EXPECT_FALSE(IsLocked()); 102 EXPECT_TRUE(ps1.Create()); 103 EXPECT_TRUE(IsLocked()); 104 EXPECT_FALSE(ps2.Create()); 105 ps1.Cleanup(); 106 107 // And when |ps2| has the lock, |ps1| cannot get it. 108 EXPECT_FALSE(IsLocked()); 109 EXPECT_TRUE(ps2.Create()); 110 EXPECT_TRUE(IsLocked()); 111 EXPECT_FALSE(ps1.Create()); 112 ps2.Cleanup(); 113 EXPECT_FALSE(IsLocked()); 114 } 115 116 // Like |Interlock| test, but via |NotifyOtherProcessOrCreate()|. 117 TEST_F(ProcessSingletonMacTest, NotifyOtherProcessOrCreate) { 118 ProcessSingleton ps1(temp_dir_.path()); 119 ProcessSingleton ps2(temp_dir_.path()); 120 121 // Windows and Linux use a command-line flag to suppress this, but 122 // it is on a sub-process so the scope is contained. Rather than 123 // add additional API to process_singleton.h in an #ifdef, just tell 124 // the reader what to expect and move on. 125 LOG(ERROR) << "Expect two failures to obtain the lock."; 126 127 // When |ps1| has the lock, |ps2| cannot get it. 128 EXPECT_FALSE(IsLocked()); 129 EXPECT_EQ(ProcessSingleton::PROCESS_NONE, ps1.NotifyOtherProcessOrCreate()); 130 EXPECT_TRUE(IsLocked()); 131 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, ps2.NotifyOtherProcessOrCreate()); 132 ps1.Cleanup(); 133 134 // And when |ps2| has the lock, |ps1| cannot get it. 135 EXPECT_FALSE(IsLocked()); 136 EXPECT_EQ(ProcessSingleton::PROCESS_NONE, ps2.NotifyOtherProcessOrCreate()); 137 EXPECT_TRUE(IsLocked()); 138 EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, ps1.NotifyOtherProcessOrCreate()); 139 ps2.Cleanup(); 140 EXPECT_FALSE(IsLocked()); 141 } 142 143 // TODO(shess): Test that the lock is released when the process dies. 144 // DEATH_TEST? I don't know. If the code to communicate between 145 // browser processes is ever written, this all would need to be tested 146 // more like the other platforms, in which case it would be easy. 147 148 } // namespace 149