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 19namespace { 20 21class 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. 69TEST_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. 79TEST_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. 90TEST_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()|. 117TEST_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