1// Copyright (c) 2011 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 "base/basictypes.h"
6#include "base/environment.h"
7#include "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/process/kill.h"
10#include "base/rand_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/test/multiprocess_test.h"
13#include "base/time/time.h"
14#include "chrome/common/multi_process_lock.h"
15#include "testing/multiprocess_func_list.h"
16
17class MultiProcessLockTest : public base::MultiProcessTest {
18 public:
19  static const char kLockEnviromentVarName[];
20
21  class ScopedEnvironmentVariable {
22   public:
23    ScopedEnvironmentVariable(const std::string &name,
24                              const std::string &value)
25        : name_(name), environment_(base::Environment::Create()) {
26      environment_->SetVar(name_.c_str(), value);
27    }
28    ~ScopedEnvironmentVariable() {
29      environment_->UnSetVar(name_.c_str());
30    }
31
32   private:
33    std::string name_;
34    scoped_ptr<base::Environment> environment_;
35    DISALLOW_COPY_AND_ASSIGN(ScopedEnvironmentVariable);
36  };
37
38  std::string GenerateLockName();
39  void ExpectLockIsLocked(const std::string &name);
40  void ExpectLockIsUnlocked(const std::string &name);
41};
42
43const char MultiProcessLockTest::kLockEnviromentVarName[]
44    = "MULTI_PROCESS_TEST_LOCK_NAME";
45
46std::string MultiProcessLockTest::GenerateLockName() {
47  base::Time now = base::Time::NowFromSystemTime();
48  return base::StringPrintf("multi_process_test_lock %lf%lf",
49                            now.ToDoubleT(), base::RandDouble());
50}
51
52void MultiProcessLockTest::ExpectLockIsLocked(const std::string &name) {
53  ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
54  base::ProcessHandle handle = SpawnChild("MultiProcessLockTryFailMain");
55  ASSERT_TRUE(handle);
56  int exit_code = 0;
57  EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
58  EXPECT_EQ(exit_code, 0);
59}
60
61void MultiProcessLockTest::ExpectLockIsUnlocked(
62    const std::string &name) {
63  ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
64  base::ProcessHandle handle = SpawnChild("MultiProcessLockTrySucceedMain");
65  ASSERT_TRUE(handle);
66  int exit_code = 0;
67  EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
68  EXPECT_EQ(exit_code, 0);
69}
70
71TEST_F(MultiProcessLockTest, BasicCreationTest) {
72  // Test basic creation/destruction with no lock taken
73  std::string name = GenerateLockName();
74  scoped_ptr<MultiProcessLock> scoped(MultiProcessLock::Create(name));
75  ExpectLockIsUnlocked(name);
76  scoped.reset(NULL);
77}
78
79TEST_F(MultiProcessLockTest, LongNameTest) {
80  // Every platform has has it's own max path name size,
81  // so different checks are needed for them.
82  // POSIX: sizeof(address.sun_path) - 2
83  // Mac OS X: BOOTSTRAP_MAX_NAME_LEN
84  // Windows: MAX_PATH
85  LOG(INFO) << "Following error log due to long name is expected";
86#if defined(OS_MACOSX)
87  std::string name("This is a name that is longer than one hundred and "
88      "twenty-eight characters to make sure that we fail appropriately on "
89      "Mac OS X when we have a path that is too long for Mac OS X to handle");
90#elif defined(OS_POSIX)
91  std::string name("This is a name that is longer than one hundred and eight "
92      "characters to make sure that we fail appropriately on POSIX systems "
93      "when we have a path that is too long for the system to handle");
94#elif defined(OS_WIN)
95  std::string name("This is a name that is longer than two hundred and sixty "
96      "characters to make sure that we fail appropriately on Windows when we "
97      "have a path that is too long for Windows to handle "
98      "This limitation comes from the MAX_PATH definition which is obviously "
99      "defined to be a maximum of two hundred and sixty characters ");
100#endif
101  scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
102  EXPECT_FALSE(test_lock->TryLock());
103}
104
105TEST_F(MultiProcessLockTest, SimpleLock) {
106  std::string name = GenerateLockName();
107  scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
108  EXPECT_TRUE(test_lock->TryLock());
109  ExpectLockIsLocked(name);
110  test_lock->Unlock();
111  ExpectLockIsUnlocked(name);
112}
113
114TEST_F(MultiProcessLockTest, RecursiveLock) {
115  std::string name = GenerateLockName();
116  scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
117  EXPECT_TRUE(test_lock->TryLock());
118  ExpectLockIsLocked(name);
119  LOG(INFO) << "Following error log "
120            << "'MultiProcessLock is already locked' is expected";
121  EXPECT_TRUE(test_lock->TryLock());
122  ExpectLockIsLocked(name);
123  test_lock->Unlock();
124  ExpectLockIsUnlocked(name);
125  LOG(INFO) << "Following error log "
126            << "'Over-unlocked MultiProcessLock' is expected";
127  test_lock->Unlock();
128  ExpectLockIsUnlocked(name);
129  test_lock.reset();
130}
131
132TEST_F(MultiProcessLockTest, LockScope) {
133  // Check to see that lock is released when it goes out of scope.
134  std::string name = GenerateLockName();
135  {
136    scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
137    EXPECT_TRUE(test_lock->TryLock());
138    ExpectLockIsLocked(name);
139  }
140  ExpectLockIsUnlocked(name);
141}
142
143MULTIPROCESS_TEST_MAIN(MultiProcessLockTryFailMain) {
144  std::string name;
145  scoped_ptr<base::Environment> environment(base::Environment::Create());
146  EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
147                                  &name));
148#if defined(OS_MACOSX)
149  // OS X sends out a log if a lock fails.
150  // Hopefully this will keep people from panicking about it when they
151  // are perusing the build logs.
152  LOG(INFO) << "Following error log "
153            << "\"CFMessagePort: bootstrap_register(): failed 1100 (0x44c) "
154            << "'Permission denied'\" is expected";
155#endif  // defined(OS_MACOSX)
156  scoped_ptr<MultiProcessLock> test_lock(
157      MultiProcessLock::Create(name));
158  EXPECT_FALSE(test_lock->TryLock());
159  return 0;
160}
161
162MULTIPROCESS_TEST_MAIN(MultiProcessLockTrySucceedMain) {
163  std::string name;
164  scoped_ptr<base::Environment> environment(base::Environment::Create());
165  EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
166                                  &name));
167  scoped_ptr<MultiProcessLock> test_lock(
168      MultiProcessLock::Create(name));
169  EXPECT_TRUE(test_lock->TryLock());
170  return 0;
171}
172