| // 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 |