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/rand_util.h"
10#include "base/stringprintf.h"
11#include "base/test/multiprocess_test.h"
12#include "base/time.h"
13#include "chrome/common/multi_process_lock.h"
14#include "testing/multiprocess_func_list.h"
15
16class MultiProcessLockTest : public base::MultiProcessTest {
17 public:
18  static const char kLockEnviromentVarName[];
19
20  class ScopedEnvironmentVariable {
21   public:
22    ScopedEnvironmentVariable(const std::string &name,
23                              const std::string &value)
24        : name_(name), environment_(base::Environment::Create()) {
25      environment_->SetVar(name_.c_str(), value);
26    }
27    ~ScopedEnvironmentVariable() {
28      environment_->UnSetVar(name_.c_str());
29    }
30
31   private:
32    std::string name_;
33    scoped_ptr<base::Environment> environment_;
34    DISALLOW_COPY_AND_ASSIGN(ScopedEnvironmentVariable);
35  };
36
37  std::string GenerateLockName();
38  void ExpectLockIsLocked(const std::string &name);
39  void ExpectLockIsUnlocked(const std::string &name);
40};
41
42const char MultiProcessLockTest::kLockEnviromentVarName[]
43    = "MULTI_PROCESS_TEST_LOCK_NAME";
44
45std::string MultiProcessLockTest::GenerateLockName() {
46  base::Time now = base::Time::NowFromSystemTime();
47  return base::StringPrintf("multi_process_test_lock %lf%lf",
48                            now.ToDoubleT(), base::RandDouble());
49}
50
51void MultiProcessLockTest::ExpectLockIsLocked(const std::string &name) {
52  ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
53  base::ProcessHandle handle = SpawnChild("MultiProcessLockTryFailMain", false);
54  ASSERT_TRUE(handle);
55  int exit_code = 0;
56  EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
57  EXPECT_EQ(exit_code, 0);
58}
59
60void MultiProcessLockTest::ExpectLockIsUnlocked(
61    const std::string &name) {
62  ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
63  base::ProcessHandle handle = SpawnChild("MultiProcessLockTrySucceedMain",
64                                          false);
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  // Linux has a max path name of 108 characters.
81  // http://lxr.linux.no/linux+v2.6.36/include/linux/un.h
82  // This is enforced on all platforms.
83  LOG(INFO) << "Following error log due to long name is expected";
84  std::string name("This is a name that is longer than one hundred and eight "
85      "characters to make sure that we fail appropriately on linux when we "
86      "have a path that is to long for linux to handle");
87  scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
88  EXPECT_FALSE(test_lock->TryLock());
89}
90
91TEST_F(MultiProcessLockTest, SimpleLock) {
92  std::string name = GenerateLockName();
93  scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
94  EXPECT_TRUE(test_lock->TryLock());
95  ExpectLockIsLocked(name);
96  test_lock->Unlock();
97  ExpectLockIsUnlocked(name);
98}
99
100TEST_F(MultiProcessLockTest, RecursiveLock) {
101  std::string name = GenerateLockName();
102  scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
103  EXPECT_TRUE(test_lock->TryLock());
104  ExpectLockIsLocked(name);
105  LOG(INFO) << "Following error log "
106            << "'MultiProcessLock is already locked' is expected";
107  EXPECT_TRUE(test_lock->TryLock());
108  ExpectLockIsLocked(name);
109  test_lock->Unlock();
110  ExpectLockIsUnlocked(name);
111  LOG(INFO) << "Following error log "
112            << "'Over-unlocked MultiProcessLock' is expected";
113  test_lock->Unlock();
114  ExpectLockIsUnlocked(name);
115  test_lock.reset();
116}
117
118TEST_F(MultiProcessLockTest, LockScope) {
119  // Check to see that lock is released when it goes out of scope.
120  std::string name = GenerateLockName();
121  {
122    scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
123    EXPECT_TRUE(test_lock->TryLock());
124    ExpectLockIsLocked(name);
125  }
126  ExpectLockIsUnlocked(name);
127}
128
129MULTIPROCESS_TEST_MAIN(MultiProcessLockTryFailMain) {
130  std::string name;
131  scoped_ptr<base::Environment> environment(base::Environment::Create());
132  EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
133                                  &name));
134#if defined(OS_MACOSX)
135  // OS X sends out a log if a lock fails.
136  // Hopefully this will keep people from panicking about it when they
137  // are perusing the build logs.
138  LOG(INFO) << "Following error log "
139            << "\"CFMessagePort: bootstrap_register(): failed 1100 (0x44c) "
140            << "'Permission denied'\" is expected";
141#endif  // defined(OS_MACOSX)
142  scoped_ptr<MultiProcessLock> test_lock(
143      MultiProcessLock::Create(name));
144  EXPECT_FALSE(test_lock->TryLock());
145  return 0;
146}
147
148MULTIPROCESS_TEST_MAIN(MultiProcessLockTrySucceedMain) {
149  std::string name;
150  scoped_ptr<base::Environment> environment(base::Environment::Create());
151  EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
152                                  &name));
153  scoped_ptr<MultiProcessLock> test_lock(
154      MultiProcessLock::Create(name));
155  EXPECT_TRUE(test_lock->TryLock());
156  return 0;
157}
158