// 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 "base/basictypes.h"
#include "base/environment.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/process/kill.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/multiprocess_test.h"
#include "base/time/time.h"
#include "chrome/common/multi_process_lock.h"
#include "testing/multiprocess_func_list.h"
class MultiProcessLockTest : public base::MultiProcessTest {
public:
static const char kLockEnviromentVarName[];
class ScopedEnvironmentVariable {
public:
ScopedEnvironmentVariable(const std::string &name,
const std::string &value)
: name_(name), environment_(base::Environment::Create()) {
environment_->SetVar(name_.c_str(), value);
}
~ScopedEnvironmentVariable() {
environment_->UnSetVar(name_.c_str());
}
private:
std::string name_;
scoped_ptr<base::Environment> environment_;
DISALLOW_COPY_AND_ASSIGN(ScopedEnvironmentVariable);
};
std::string GenerateLockName();
void ExpectLockIsLocked(const std::string &name);
void ExpectLockIsUnlocked(const std::string &name);
};
const char MultiProcessLockTest::kLockEnviromentVarName[]
= "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) {
ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
base::ProcessHandle handle = SpawnChild("MultiProcessLockTryFailMain");
ASSERT_TRUE(handle);
int exit_code = 0;
EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
EXPECT_EQ(exit_code, 0);
}
void MultiProcessLockTest::ExpectLockIsUnlocked(
const std::string &name) {
ScopedEnvironmentVariable var(kLockEnviromentVarName, name);
base::ProcessHandle handle = SpawnChild("MultiProcessLockTrySucceedMain");
ASSERT_TRUE(handle);
int exit_code = 0;
EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
EXPECT_EQ(exit_code, 0);
}
TEST_F(MultiProcessLockTest, BasicCreationTest) {
// Test basic creation/destruction with no lock taken
std::string name = GenerateLockName();
scoped_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
scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
EXPECT_FALSE(test_lock->TryLock());
}
TEST_F(MultiProcessLockTest, SimpleLock) {
std::string name = GenerateLockName();
scoped_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();
scoped_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();
{
scoped_ptr<MultiProcessLock> test_lock(MultiProcessLock::Create(name));
EXPECT_TRUE(test_lock->TryLock());
ExpectLockIsLocked(name);
}
ExpectLockIsUnlocked(name);
}
MULTIPROCESS_TEST_MAIN(MultiProcessLockTryFailMain) {
std::string name;
scoped_ptr<base::Environment> environment(base::Environment::Create());
EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
&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)
scoped_ptr<MultiProcessLock> test_lock(
MultiProcessLock::Create(name));
EXPECT_FALSE(test_lock->TryLock());
return 0;
}
MULTIPROCESS_TEST_MAIN(MultiProcessLockTrySucceedMain) {
std::string name;
scoped_ptr<base::Environment> environment(base::Environment::Create());
EXPECT_TRUE(environment->GetVar(MultiProcessLockTest::kLockEnviromentVarName,
&name));
scoped_ptr<MultiProcessLock> test_lock(
MultiProcessLock::Create(name));
EXPECT_TRUE(test_lock->TryLock());
return 0;
}