blob: dd4c4d44a427a6086f849d9a0a73c14a0006a07f [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "remoting/host/linux/certificate_watcher.h"
#include <cstdlib>
#include <memory>
#include <string>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
const char kCertFileName[] = "cert9.db";
const char kKeyFileName[] = "key4.db";
const char kPKCSFileName[] = "pkcs11.txt";
const char kOtherFileName[] = "testfile.txt";
const int kMessageLoopWaitMsecs = 150;
class CertificateWatcherTest : public testing::Test {
public:
CertificateWatcherTest()
: task_runner_(task_environment_.GetMainThreadTaskRunner()) {
EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
watcher_ = std::make_unique<CertificateWatcher>(
base::BindRepeating(&CertificateWatcherTest::OnRestart,
base::Unretained(this)),
task_runner_);
watcher_->SetDelayForTests(base::Seconds(0));
watcher_->SetWatchPathForTests(temp_dir_.GetPath());
}
~CertificateWatcherTest() override {
watcher_.reset();
base::RunLoop().RunUntilIdle();
}
protected:
// Call this if you expect a restart after the loop runs.
// The thread will hang if OnRestart is never called.
void RunLoop() {
base::RunLoop loop;
quit_loop_closure_ = loop.QuitClosure();
loop.Run();
}
// Call this if you expect no restart after the loop runs.
// Will quit the loop after kMessageLoopWaitMsecs.
void RunAndWait() {
base::RunLoop loop;
task_runner_->PostDelayedTask(FROM_HERE, loop.QuitClosure(), loop_wait_);
loop.Run();
}
void Start() { watcher_->Start(); }
void Connect() {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CertificateWatcher::OnClientConnected,
base::Unretained(watcher_.get()), ""));
}
void Disconnect() {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CertificateWatcher::OnClientDisconnected,
base::Unretained(watcher_.get()), ""));
}
void TouchFile(const char* filename) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&CertificateWatcherTest::TouchFileTask,
base::Unretained(this), filename));
}
void TouchFileTask(const char* filename) {
std::string testWriteString = std::to_string(rand());
base::FilePath path = temp_dir_.GetPath().AppendASCII(filename);
if (base::PathExists(path)) {
EXPECT_TRUE(base::AppendToFile(path, testWriteString));
} else {
EXPECT_EQ(static_cast<int>(testWriteString.length()),
base::WriteFile(path, testWriteString.c_str(),
testWriteString.length()));
}
}
base::test::SingleThreadTaskEnvironment task_environment_{
base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<CertificateWatcher> watcher_;
int restart_count_ = 0;
base::TimeDelta loop_wait_ = base::Milliseconds(kMessageLoopWaitMsecs);
base::RepeatingClosure quit_loop_closure_;
private:
void OnRestart() {
restart_count_++;
quit_loop_closure_.Run();
}
};
TEST_F(CertificateWatcherTest, OneTouch) {
EXPECT_EQ(0, restart_count_);
Start();
EXPECT_EQ(0, restart_count_);
TouchFile(kCertFileName);
RunLoop();
EXPECT_EQ(1, restart_count_);
}
TEST_F(CertificateWatcherTest, OneTouchAppend) {
EXPECT_EQ(0, restart_count_);
TouchFileTask(kKeyFileName);
Start();
EXPECT_EQ(0, restart_count_);
TouchFile(kKeyFileName); // Appends to existing file.
RunLoop();
EXPECT_EQ(1, restart_count_);
}
TEST_F(CertificateWatcherTest, InhibitDeferRestart) {
Start();
EXPECT_EQ(0, restart_count_);
Connect();
EXPECT_EQ(0, restart_count_);
TouchFile(kPKCSFileName);
RunAndWait();
EXPECT_EQ(0, restart_count_);
Disconnect();
RunLoop();
EXPECT_EQ(1, restart_count_);
}
TEST_F(CertificateWatcherTest, UninhibitAndRestart) {
Start();
EXPECT_EQ(0, restart_count_);
Connect();
EXPECT_EQ(0, restart_count_);
Disconnect();
RunAndWait();
EXPECT_EQ(0, restart_count_);
TouchFile(kCertFileName);
RunLoop();
EXPECT_EQ(1, restart_count_);
}
TEST_F(CertificateWatcherTest, TouchOtherFile) {
// The watcher should not trigger if changes are made that don't affect the
// NSS DB contents.
EXPECT_EQ(0, restart_count_);
Start();
EXPECT_EQ(0, restart_count_);
TouchFile(kOtherFileName);
RunAndWait();
EXPECT_EQ(0, restart_count_);
}
} // namespace remoting