1731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Use of this source code is governed by a BSD-style license that can be
3731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// found in the LICENSE file.
4731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
5731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include <errno.h>
6731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include <fcntl.h>
7731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include <sys/file.h>
8731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
9731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/browser/process_singleton.h"
10731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
11731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/eintr_wrapper.h"
12731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/file_util.h"
13731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/path_service.h"
14731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/common/chrome_constants.h"
15731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/common/chrome_paths.h"
16731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "chrome/test/testing_profile.h"
17731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "testing/platform_test.h"
18731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
19731df977c0511bca2206b5f333555b1205ff1f43Iain Merricknamespace {
20731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
21731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickclass ProcessSingletonMacTest : public PlatformTest {
22731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick public:
23731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  virtual void SetUp() {
24731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    PlatformTest::SetUp();
25731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
26731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Put the lock in a temporary directory.  Doesn't need to be a
27731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // full profile to test this code.
2872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
29731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    lock_path_ = temp_dir_.path().Append(chrome::kSingletonLockFilename);
30731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
31731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
32731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  virtual void TearDown() {
33731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    PlatformTest::TearDown();
34731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
35731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Verify that the lock was released.
36731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    EXPECT_FALSE(IsLocked());
37731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
38731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
39731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Return |true| if the file exists and is locked.  Forces a failure
40731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // in the containing test in case of error condition.
41731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  bool IsLocked() {
42731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    int fd = HANDLE_EINTR(open(lock_path_.value().c_str(), O_RDONLY));
43731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (fd == -1) {
44731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      EXPECT_EQ(ENOENT, errno) << "Unexpected error opening lockfile.";
45731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return false;
46731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    }
47731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
48731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    file_util::ScopedFD auto_close(&fd);
49731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
50731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    int rc = HANDLE_EINTR(flock(fd, LOCK_EX|LOCK_NB));
51731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
52731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Got the lock, so it wasn't already locked.  Close releases.
53731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (rc != -1)
54731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return false;
55731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
56731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Someone else has the lock.
57731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (errno == EWOULDBLOCK)
58731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      return true;
59731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
60731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    EXPECT_EQ(EWOULDBLOCK, errno) << "Unexpected error acquiring lock.";
61731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    return false;
62731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
63731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
64731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ScopedTempDir temp_dir_;
65731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  FilePath lock_path_;
66731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick};
67731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
68731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Test that the base case doesn't blow up.
69731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickTEST_F(ProcessSingletonMacTest, Basic) {
70731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ProcessSingleton ps(temp_dir_.path());
71731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
72731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(ps.Create());
73731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(IsLocked());
74731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ps.Cleanup();
75731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
76731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
77731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
78731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// The destructor should release the lock.
79731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickTEST_F(ProcessSingletonMacTest, DestructorReleases) {
80731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
81731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  {
82731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    ProcessSingleton ps(temp_dir_.path());
83731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    EXPECT_TRUE(ps.Create());
84731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    EXPECT_TRUE(IsLocked());
85731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  }
86731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
87731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
88731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
89731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Multiple singletons should interlock appropriately.
90731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickTEST_F(ProcessSingletonMacTest, Interlock) {
91731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ProcessSingleton ps1(temp_dir_.path());
92731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ProcessSingleton ps2(temp_dir_.path());
93731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
94731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Windows and Linux use a command-line flag to suppress this, but
95731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // it is on a sub-process so the scope is contained.  Rather than
96731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // add additional API to process_singleton.h in an #ifdef, just tell
97731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // the reader what to expect and move on.
98731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  LOG(ERROR) << "Expect two failures to obtain the lock.";
99731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
100731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // When |ps1| has the lock, |ps2| cannot get it.
101731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
102731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(ps1.Create());
103731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(IsLocked());
104731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(ps2.Create());
105731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ps1.Cleanup();
106731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
107731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // And when |ps2| has the lock, |ps1| cannot get it.
108731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
109731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(ps2.Create());
110731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(IsLocked());
111731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(ps1.Create());
112731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ps2.Cleanup();
113731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
114731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
115731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
116731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// Like |Interlock| test, but via |NotifyOtherProcessOrCreate()|.
117731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickTEST_F(ProcessSingletonMacTest, NotifyOtherProcessOrCreate) {
118731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ProcessSingleton ps1(temp_dir_.path());
119731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ProcessSingleton ps2(temp_dir_.path());
120731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
121731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // Windows and Linux use a command-line flag to suppress this, but
122731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // it is on a sub-process so the scope is contained.  Rather than
123731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // add additional API to process_singleton.h in an #ifdef, just tell
124731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // the reader what to expect and move on.
125731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  LOG(ERROR) << "Expect two failures to obtain the lock.";
126731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
127731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // When |ps1| has the lock, |ps2| cannot get it.
128731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
129731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_EQ(ProcessSingleton::PROCESS_NONE, ps1.NotifyOtherProcessOrCreate());
130731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(IsLocked());
131731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, ps2.NotifyOtherProcessOrCreate());
132731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ps1.Cleanup();
133731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
134731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  // And when |ps2| has the lock, |ps1| cannot get it.
135731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
136731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_EQ(ProcessSingleton::PROCESS_NONE, ps2.NotifyOtherProcessOrCreate());
137731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_TRUE(IsLocked());
138731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_EQ(ProcessSingleton::PROFILE_IN_USE, ps1.NotifyOtherProcessOrCreate());
139731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  ps2.Cleanup();
140731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  EXPECT_FALSE(IsLocked());
141731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
142731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
143731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// TODO(shess): Test that the lock is released when the process dies.
144731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// DEATH_TEST?  I don't know.  If the code to communicate between
145731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// browser processes is ever written, this all would need to be tested
146731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick// more like the other platforms, in which case it would be easy.
147731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
148731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}  // namespace
149