blob: 4d59c5ad4713303d6e2da59ea214011fd9930e53 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/multi_process_lock.h"
#include <memory>
#include "base/environment.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/process/kill.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/multiprocess_test.h"
#include "base/test/scoped_environment_variable_override.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/multiprocess_func_list.h"
class MultiProcessLockTest : public base::MultiProcessTest {
public:
static const char kLockEnvironmentVarName[];
std::string GenerateLockName();
void ExpectLockIsLocked(const std::string &name);
void ExpectLockIsUnlocked(const std::string &name);
};
const char MultiProcessLockTest::kLockEnvironmentVarName[] =
"MULTI_PROCESS_TEST_LOCK_NAME";
std::string MultiProcessLockTest::GenerateLockName() {
base::Time now = base::Time::NowFromSystemTime();
return base::StringPrintf("multi_process_test_lock %lf%lf",
now.ToDoubleT(), base::RandDouble());
}
void MultiProcessLockTest::ExpectLockIsLocked(const std::string &name) {
base::test::ScopedEnvironmentVariableOverride var(kLockEnvironmentVarName,
name);
EXPECT_FALSE(var.WasSet());
base::Process process = SpawnChild("MultiProcessLockTryFailMain");
ASSERT_TRUE(process.IsValid());
int exit_code = -1;
EXPECT_TRUE(process.WaitForExit(&exit_code));
EXPECT_EQ(0, exit_code);
}
void MultiProcessLockTest::ExpectLockIsUnlocked(
const std::string &name) {
base::test::ScopedEnvironmentVariableOverride var(kLockEnvironmentVarName,
name);
EXPECT_FALSE(var.WasSet());
base::Process process = SpawnChild("MultiProcessLockTrySucceedMain");
ASSERT_TRUE(process.IsValid());
int exit_code = -1;
EXPECT_TRUE(process.WaitForExit(&exit_code));
EXPECT_EQ(0, exit_code);
}
TEST_F(MultiProcessLockTest, BasicCreationTest) {
// Test basic creation/destruction with no lock taken
std::string name = GenerateLockName();
std::unique_ptr<MultiProcessLock> scoped = MultiProcessLock::Create(name);
ExpectLockIsUnlocked(name);
scoped.reset(NULL);
}
TEST_F(MultiProcessLockTest, LongNameTest) {
// Every platform has has it's own max path name size,
// so different checks are needed for them.
// POSIX: sizeof(address.sun_path) - 2
// Mac OS X: BOOTSTRAP_MAX_NAME_LEN
// Windows: MAX_PATH
LOG(INFO) << "Following error log due to long name is expected";
#if defined(OS_MACOSX)
std::string name("This is a name that is longer than one hundred and "
"twenty-eight characters to make sure that we fail appropriately on "
"Mac OS X when we have a path that is too long for Mac OS X to handle");
#elif defined(OS_POSIX)
std::string name("This is a name that is longer than one hundred and eight "
"characters to make sure that we fail appropriately on POSIX systems "
"when we have a path that is too long for the system to handle");
#elif defined(OS_WIN)
std::string name("This is a name that is longer than two hundred and sixty "
"characters to make sure that we fail appropriately on Windows when we "
"have a path that is too long for Windows to handle "
"This limitation comes from the MAX_PATH definition which is obviously "
"defined to be a maximum of two hundred and sixty characters ");
#endif
std::unique_ptr<MultiProcessLock> test_lock = MultiProcessLock::Create(name);
EXPECT_FALSE(test_lock->TryLock());
}
TEST_F(MultiProcessLockTest, SimpleLock) {
std::string name = GenerateLockName();
std::unique_ptr<MultiProcessLock> test_lock = MultiProcessLock::Create(name);
EXPECT_TRUE(test_lock->TryLock());
ExpectLockIsLocked(name);
test_lock->Unlock();
ExpectLockIsUnlocked(name);
}
TEST_F(MultiProcessLockTest, RecursiveLock) {
std::string name = GenerateLockName();
std::unique_ptr<MultiProcessLock> test_lock = MultiProcessLock::Create(name);
EXPECT_TRUE(test_lock->TryLock());
ExpectLockIsLocked(name);
LOG(INFO) << "Following error log "
<< "'MultiProcessLock is already locked' is expected";
EXPECT_TRUE(test_lock->TryLock());
ExpectLockIsLocked(name);
test_lock->Unlock();
ExpectLockIsUnlocked(name);
LOG(INFO) << "Following error log "
<< "'Over-unlocked MultiProcessLock' is expected";
test_lock->Unlock();
ExpectLockIsUnlocked(name);
test_lock.reset();
}
TEST_F(MultiProcessLockTest, LockScope) {
// Check to see that lock is released when it goes out of scope.
std::string name = GenerateLockName();
{
std::unique_ptr<MultiProcessLock> test_lock =
MultiProcessLock::Create(name);
EXPECT_TRUE(test_lock->TryLock());
ExpectLockIsLocked(name);
}
ExpectLockIsUnlocked(name);
}
MULTIPROCESS_TEST_MAIN(MultiProcessLockTryFailMain) {
std::string name;
std::unique_ptr<base::Environment> environment(base::Environment::Create());
EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnvironmentVarName,
&name));
#if defined(OS_MACOSX)
// OS X sends out a log if a lock fails.
// Hopefully this will keep people from panicking about it when they
// are perusing the build logs.
LOG(INFO) << "Following error log "
<< "\"CFMessagePort: bootstrap_register(): failed 1100 (0x44c) "
<< "'Permission denied'\" is expected";
#endif // defined(OS_MACOSX)
std::unique_ptr<MultiProcessLock> test_lock = MultiProcessLock::Create(name);
// Expect locking to fail because it is claimed by another process.
bool locked_successfully = test_lock->TryLock();
EXPECT_FALSE(locked_successfully);
return locked_successfully;
}
MULTIPROCESS_TEST_MAIN(MultiProcessLockTrySucceedMain) {
std::string name;
std::unique_ptr<base::Environment> environment(base::Environment::Create());
EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnvironmentVarName,
&name));
std::unique_ptr<MultiProcessLock> test_lock = MultiProcessLock::Create(name);
// Expect locking to succeed because it is not claimed yet.
bool locked_successfully = test_lock->TryLock();
EXPECT_TRUE(locked_successfully);
return !locked_successfully;
}