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/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"
21 class ProcessSingletonMacTest
: public PlatformTest
{
23 virtual void SetUp() {
24 PlatformTest::SetUp();
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
);
32 virtual void TearDown() {
33 PlatformTest::TearDown();
35 // Verify that the lock was released.
36 EXPECT_FALSE(IsLocked());
39 // Return |true| if the file exists and is locked. Forces a failure
40 // in the containing test in case of error condition.
42 int fd
= HANDLE_EINTR(open(lock_path_
.value().c_str(), O_RDONLY
));
44 EXPECT_EQ(ENOENT
, errno
) << "Unexpected error opening lockfile.";
48 file_util::ScopedFD
auto_close(&fd
);
50 int rc
= HANDLE_EINTR(flock(fd
, LOCK_EX
|LOCK_NB
));
52 // Got the lock, so it wasn't already locked. Close releases.
56 // Someone else has the lock.
57 if (errno
== EWOULDBLOCK
)
60 EXPECT_EQ(EWOULDBLOCK
, errno
) << "Unexpected error acquiring lock.";
64 ScopedTempDir temp_dir_
;
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(ProcessSingleton::NotificationCallback()));
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 EXPECT_TRUE(ps
.Create(ProcessSingleton::NotificationCallback()));
84 EXPECT_TRUE(IsLocked());
86 EXPECT_FALSE(IsLocked());
89 // Multiple singletons should interlock appropriately.
90 TEST_F(ProcessSingletonMacTest
, Interlock
) {
91 ProcessSingleton
ps1(temp_dir_
.path());
92 ProcessSingleton
ps2(temp_dir_
.path());
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.";
100 // When |ps1| has the lock, |ps2| cannot get it.
101 EXPECT_FALSE(IsLocked());
102 EXPECT_TRUE(ps1
.Create(ProcessSingleton::NotificationCallback()));
103 EXPECT_TRUE(IsLocked());
104 EXPECT_FALSE(ps2
.Create(ProcessSingleton::NotificationCallback()));
107 // And when |ps2| has the lock, |ps1| cannot get it.
108 EXPECT_FALSE(IsLocked());
109 EXPECT_TRUE(ps2
.Create(ProcessSingleton::NotificationCallback()));
110 EXPECT_TRUE(IsLocked());
111 EXPECT_FALSE(ps1
.Create(ProcessSingleton::NotificationCallback()));
113 EXPECT_FALSE(IsLocked());
116 // Like |Interlock| test, but via |NotifyOtherProcessOrCreate()|.
117 TEST_F(ProcessSingletonMacTest
, NotifyOtherProcessOrCreate
) {
118 ProcessSingleton
ps1(temp_dir_
.path());
119 ProcessSingleton
ps2(temp_dir_
.path());
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.";
127 // When |ps1| has the lock, |ps2| cannot get it.
128 EXPECT_FALSE(IsLocked());
130 ProcessSingleton::PROCESS_NONE
,
131 ps1
.NotifyOtherProcessOrCreate(ProcessSingleton::NotificationCallback()));
132 EXPECT_TRUE(IsLocked());
134 ProcessSingleton::PROFILE_IN_USE
,
135 ps2
.NotifyOtherProcessOrCreate(ProcessSingleton::NotificationCallback()));
138 // And when |ps2| has the lock, |ps1| cannot get it.
139 EXPECT_FALSE(IsLocked());
141 ProcessSingleton::PROCESS_NONE
,
142 ps2
.NotifyOtherProcessOrCreate(ProcessSingleton::NotificationCallback()));
143 EXPECT_TRUE(IsLocked());
145 ProcessSingleton::PROFILE_IN_USE
,
146 ps1
.NotifyOtherProcessOrCreate(ProcessSingleton::NotificationCallback()));
148 EXPECT_FALSE(IsLocked());
151 // TODO(shess): Test that the lock is released when the process dies.
152 // DEATH_TEST? I don't know. If the code to communicate between
153 // browser processes is ever written, this all would need to be tested
154 // more like the other platforms, in which case it would be easy.