blob: 1467562b02d0c31ae214172800ae7c3caaa1a306 [file] [log] [blame] [edit]
// Copyright (c) 2012 The Chromium OS 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 "power_manager/common/prefs.h"
#include <memory>
#include <base/check.h>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <cros_config/fake_cros_config.h>
#include <gtest/gtest.h>
#include "power_manager/common/cros_config_prefs_source.h"
#include "power_manager/common/file_prefs_store.h"
#include "power_manager/common/prefs_observer.h"
#include "power_manager/common/test_main_loop_runner.h"
namespace {
const int kNumPrefDirectories = 3;
const int64_t kIntTestValue = 0xdeadbeef;
const double kDoubleTestValue = 0.1337;
const char kGarbageString[] = "This is garbage";
const char kIntTestFileName[] = "intfile";
const char kDoubleTestFileName[] = "doublefile";
// The test crashes after this many milliseconds if an expected preference
// change notification is never received.
const int kPrefChangeTimeoutMs = 60 * 1000;
} // namespace
namespace power_manager {
// Simple class that runs a GLib main loop until it sees a pref get changed.
// Tests should update a pref file and then call RunUntilPrefChanged().
class TestPrefsObserver : public PrefsObserver {
public:
explicit TestPrefsObserver(Prefs* prefs) : prefs_(prefs) {
prefs_->AddObserver(this);
}
TestPrefsObserver(const TestPrefsObserver&) = delete;
TestPrefsObserver& operator=(const TestPrefsObserver&) = delete;
~TestPrefsObserver() override { prefs_->RemoveObserver(this); }
// Runs |loop_| until OnPrefChanged() is called, then quits the loop
// and returns a string containing the name of the pref that was changed.
std::string RunUntilPrefChanged() {
CHECK(loop_runner_.StartLoop(
base::TimeDelta::FromMilliseconds(kPrefChangeTimeoutMs)))
<< "Pref change not received";
return pref_name_;
}
// PrefsObserver implementation:
void OnPrefChanged(const std::string& pref_name) override {
loop_runner_.StopLoop();
pref_name_ = pref_name;
}
private:
Prefs* prefs_; // weak; owned by PrefsTest
TestMainLoopRunner loop_runner_;
// Name of the last pref that was changed.
std::string pref_name_;
};
class PrefsTest : public testing::Test {
public:
PrefsTest() : test_api_(&prefs_) {}
~PrefsTest() override {}
void SetUp() override {
paths_.clear();
// Create new temp directories.
for (int i = 0; i < kNumPrefDirectories; ++i) {
temp_dir_generators_[i].reset(new base::ScopedTempDir());
ASSERT_TRUE(temp_dir_generators_[i]->CreateUniqueTempDir());
EXPECT_TRUE(temp_dir_generators_[i]->IsValid());
paths_.push_back(temp_dir_generators_[i]->GetPath());
}
// By default, don't defer writes.
test_api_.set_write_interval(base::TimeDelta());
}
protected:
PrefsSourceInterfaceVector GetSources() {
PrefsSourceInterfaceVector sources;
cros_config_ = new brillo::FakeCrosConfig();
sources.emplace_back(new CrosConfigPrefsSource(
std::unique_ptr<brillo::CrosConfigInterface>(cros_config_)));
// The first path will be used for the store; the rest become sources.
for (int i = 1; i < kNumPrefDirectories; ++i) {
sources.emplace_back(new FilePrefsStore(paths_[i]));
}
return sources;
}
void InitPrefs() {
ASSERT_TRUE(
prefs_.Init(std::make_unique<FilePrefsStore>(paths_[0]), GetSources()));
}
std::vector<base::FilePath> paths_;
std::unique_ptr<base::ScopedTempDir>
temp_dir_generators_[kNumPrefDirectories];
brillo::FakeCrosConfig* cros_config_; // weak
Prefs prefs_;
Prefs::TestApi test_api_;
};
// Test read/write with only one directory.
TEST_F(PrefsTest, TestOneDirectory) {
ASSERT_TRUE(prefs_.Init(std::make_unique<FilePrefsStore>(paths_[0]),
PrefsSourceInterfaceVector()));
// Make sure the pref files don't already exist.
EXPECT_FALSE(base::PathExists(paths_[0].Append(kIntTestFileName)));
EXPECT_FALSE(base::PathExists(paths_[0].Append(kDoubleTestFileName)));
// Write int and double values to pref files.
prefs_.SetInt64(kIntTestFileName, kIntTestValue);
prefs_.SetDouble(kDoubleTestFileName, kDoubleTestValue);
// Make sure the files were only created in the first directory.
for (int i = 0; i < kNumPrefDirectories; ++i) {
if (i == 0) {
EXPECT_TRUE(base::PathExists(paths_[i].Append(kIntTestFileName)));
EXPECT_TRUE(base::PathExists(paths_[i].Append(kDoubleTestFileName)));
continue;
}
EXPECT_FALSE(base::PathExists(paths_[i].Append(kIntTestFileName)));
EXPECT_FALSE(base::PathExists(paths_[i].Append(kDoubleTestFileName)));
}
// Now read them back and make sure they have the right values.
int64_t int_value = -1;
double double_value = -1;
EXPECT_TRUE(prefs_.GetInt64(kIntTestFileName, &int_value));
EXPECT_TRUE(prefs_.GetDouble(kDoubleTestFileName, &double_value));
EXPECT_EQ(kIntTestValue, int_value);
EXPECT_EQ(kDoubleTestValue, double_value);
}
// Test read/write with three directories.
TEST_F(PrefsTest, TestThreeDirectories) {
InitPrefs();
// Make sure the files don't already exist.
for (int i = 0; i < kNumPrefDirectories; ++i) {
EXPECT_FALSE(base::PathExists(paths_[i].Append(kIntTestFileName)));
EXPECT_FALSE(base::PathExists(paths_[i].Append(kDoubleTestFileName)));
}
// Write int and double values to pref files and make sure those files were
// created in the first directory and not in the other two.
prefs_.SetInt64(kIntTestFileName, kIntTestValue);
EXPECT_TRUE(base::PathExists(paths_[0].Append(kIntTestFileName)));
EXPECT_FALSE(base::PathExists(paths_[1].Append(kIntTestFileName)));
EXPECT_FALSE(base::PathExists(paths_[2].Append(kIntTestFileName)));
prefs_.SetDouble(kDoubleTestFileName, kDoubleTestValue);
EXPECT_TRUE(base::PathExists(paths_[0].Append(kDoubleTestFileName)));
EXPECT_FALSE(base::PathExists(paths_[1].Append(kDoubleTestFileName)));
EXPECT_FALSE(base::PathExists(paths_[2].Append(kDoubleTestFileName)));
// Now read them back and make sure they have the right values.
int64_t int_value = -1;
double double_value = -1;
EXPECT_TRUE(prefs_.GetInt64(kIntTestFileName, &int_value));
EXPECT_TRUE(prefs_.GetDouble(kDoubleTestFileName, &double_value));
EXPECT_EQ(kIntTestValue, int_value);
EXPECT_EQ(kDoubleTestValue, double_value);
}
// Test read from three directories, checking for precedence of directories.
// Prefs in |paths_[i]| take precedence over the same prefs in |paths_[j]|, for
// i < j.
TEST_F(PrefsTest, TestThreeDirectoriesStacked) {
// Run cycles from 1 to |(1 << kNumPrefDirectories) - 1|. Each cycle number's
// bits will represent the paths to populate with pref files. e.g...
// cycle 2 = 010b => write prefs to |paths_[1]|
// cycle 5 = 101b => write prefs to |paths_[0]| and |paths_[2]|
// cycle 7 = 111b => write prefs to all paths in |paths_|.
// This will test all the valid combinations of which directories have pref
// files.
for (int cycle = 1; cycle < (1 << kNumPrefDirectories); ++cycle) {
SCOPED_TRACE(
base::StringPrintf("Testing stacked directories, cycle %d", cycle));
SetUp();
Prefs prefs;
ASSERT_TRUE(
prefs.Init(std::make_unique<FilePrefsStore>(paths_[0]), GetSources()));
// Write values to the pref directories as appropriate for this cycle.
int i;
for (i = 0; i < kNumPrefDirectories; ++i) {
const base::FilePath& path = paths_[i];
// Make sure the files didn't exist already.
EXPECT_FALSE(base::PathExists(path.Append(kIntTestFileName)));
EXPECT_FALSE(base::PathExists(path.Append(kDoubleTestFileName)));
// Determine if this directory path's bit is set in the current cycle
// number.
if (!((cycle >> i) & 1))
continue;
// For path[i], write the default test values + i.
// This way, each path's pref file will have a unique value.
std::string int_string = base::NumberToString(kIntTestValue + i);
EXPECT_EQ(int_string.size(),
base::WriteFile(path.Append(kIntTestFileName),
int_string.data(), int_string.size()));
EXPECT_TRUE(base::PathExists(path.Append(kIntTestFileName)));
std::string double_string = base::NumberToString(kDoubleTestValue + i);
EXPECT_EQ(double_string.size(),
base::WriteFile(path.Append(kDoubleTestFileName),
double_string.data(), double_string.size()));
EXPECT_TRUE(base::PathExists(path.Append(kDoubleTestFileName)));
}
// Read the pref files.
int64_t int_value = -1;
double double_value = -1;
EXPECT_TRUE(prefs.GetInt64(kIntTestFileName, &int_value));
EXPECT_TRUE(prefs.GetDouble(kDoubleTestFileName, &double_value));
// Make sure the earlier paths take precedence over later paths.
bool is_first_valid_directory = true;
int num_directories_checked = 0;
for (i = 0; i < kNumPrefDirectories; ++i) {
// If the current directory was not used this cycle, disregard it.
if (!((cycle >> i) & 1))
continue;
if (is_first_valid_directory) {
// First valid directory should match.
EXPECT_EQ(kIntTestValue + i, int_value);
EXPECT_EQ(kDoubleTestValue + i, double_value);
is_first_valid_directory = false;
} else {
EXPECT_NE(kIntTestValue + i, int_value);
EXPECT_NE(kDoubleTestValue + i, double_value);
}
++num_directories_checked;
}
EXPECT_GT(num_directories_checked, 0);
}
}
// Test read from three directories, with the higher precedence directories
// containing garbage.
TEST_F(PrefsTest, TestThreeDirectoriesGarbage) {
InitPrefs();
for (int i = 0; i < kNumPrefDirectories; ++i) {
const base::FilePath& path = paths_[i];
// Make sure the files didn't exist already.
EXPECT_FALSE(base::PathExists(path.Append(kIntTestFileName)));
EXPECT_FALSE(base::PathExists(path.Append(kDoubleTestFileName)));
// Earlier directories contain garbage.
// The last one contains valid values.
std::string int_string;
std::string double_string;
if (i < kNumPrefDirectories - 1) {
int_string = kGarbageString;
double_string = kGarbageString;
} else {
int_string = base::NumberToString(kIntTestValue);
double_string = base::NumberToString(kDoubleTestValue);
}
EXPECT_EQ(int_string.size(),
base::WriteFile(path.Append(kIntTestFileName), int_string.data(),
int_string.size()));
EXPECT_TRUE(base::PathExists(path.Append(kIntTestFileName)));
EXPECT_EQ(double_string.size(),
base::WriteFile(path.Append(kDoubleTestFileName),
double_string.data(), double_string.size()));
EXPECT_TRUE(base::PathExists(path.Append(kDoubleTestFileName)));
}
// Read the pref files and make sure the right value was read.
int64_t int_value = -1;
double double_value = -1;
EXPECT_TRUE(prefs_.GetInt64(kIntTestFileName, &int_value));
EXPECT_TRUE(prefs_.GetDouble(kDoubleTestFileName, &double_value));
EXPECT_EQ(kIntTestValue, int_value);
EXPECT_EQ(kDoubleTestValue, double_value);
}
// Make sure that Prefs correctly notifies about changes to pref files.
TEST_F(PrefsTest, WatchPrefs) {
const char kPrefName[] = "foo";
const char kPrefValue[] = "1";
const base::FilePath kFilePath = paths_[0].Append(kPrefName);
TestPrefsObserver observer(&prefs_);
InitPrefs();
EXPECT_EQ(strlen(kPrefValue),
base::WriteFile(kFilePath, kPrefValue, strlen(kPrefValue)));
EXPECT_EQ(kPrefName, observer.RunUntilPrefChanged());
// Write to the file again.
EXPECT_EQ(strlen(kPrefValue),
base::WriteFile(kFilePath, kPrefValue, strlen(kPrefValue)));
EXPECT_EQ(kPrefName, observer.RunUntilPrefChanged());
// Remove the file.
EXPECT_TRUE(base::DeleteFile(kFilePath));
EXPECT_EQ(kPrefName, observer.RunUntilPrefChanged());
}
// Test that additional write requests made soon after an initial request
// are deferred.
TEST_F(PrefsTest, DeferredWrites) {
test_api_.set_write_interval(base::TimeDelta::FromSeconds(120));
InitPrefs();
// Write 1 to a pref.
const char kName[] = "foo";
prefs_.SetInt64(kName, 1);
int64_t int64_value = -1;
// Check that the value was written to disk immediately.
const base::FilePath kPath = paths_[0].Append(kName);
std::string file_contents;
EXPECT_TRUE(base::ReadFileToString(kPath, &file_contents));
EXPECT_EQ("1", file_contents);
EXPECT_TRUE(prefs_.GetInt64(kName, &int64_value));
EXPECT_EQ(1, int64_value);
// Now write 2 to the pref. Since the last write happened recently, the
// file should still contain 1, but GetInt64() should return the new value.
prefs_.SetInt64(kName, 2);
file_contents.clear();
EXPECT_TRUE(base::ReadFileToString(kPath, &file_contents));
EXPECT_EQ("1", file_contents);
EXPECT_TRUE(prefs_.GetInt64(kName, &int64_value));
EXPECT_EQ(2, int64_value);
// The new value should be written to disk after the timeout fires.
EXPECT_TRUE(test_api_.TriggerWriteTimeout());
file_contents.clear();
EXPECT_TRUE(base::ReadFileToString(kPath, &file_contents));
EXPECT_EQ("2", file_contents);
// The timeout should no longer be scheduled.
EXPECT_FALSE(test_api_.TriggerWriteTimeout());
// Write 3 and then 4. Check that the second value is written.
prefs_.SetInt64(kName, 3);
prefs_.SetInt64(kName, 4);
EXPECT_TRUE(test_api_.TriggerWriteTimeout());
file_contents.clear();
EXPECT_TRUE(base::ReadFileToString(kPath, &file_contents));
EXPECT_EQ("4", file_contents);
EXPECT_TRUE(prefs_.GetInt64(kName, &int64_value));
EXPECT_EQ(4, int64_value);
EXPECT_FALSE(test_api_.TriggerWriteTimeout());
}
// Test reads from libcros_config.
TEST_F(PrefsTest, TestLibCrosConfigPrefs) {
InitPrefs();
cros_config_->SetString("/power", "power-pref-name", "1");
cros_config_->SetString("/power", "power_pref_name", "999"); // inaccessible
int64_t value;
EXPECT_TRUE(prefs_.GetInt64("power_pref_name", &value));
EXPECT_EQ(1, value);
EXPECT_FALSE(prefs_.GetInt64("nonexistent", &value));
}
// Test that CrosConfigPrefsSource::ReadPrefString() trims trailing whitespace.
TEST_F(PrefsTest, TestLibCrosConfigPrefsTrailingWhitespace) {
InitPrefs();
cros_config_->SetString("/power", "name", "value \n");
std::string value;
EXPECT_TRUE(prefs_.GetString("name", &value));
EXPECT_EQ("value", value);
}
} // namespace power_manager