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