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