Index: base/prefs/json_pref_store_unittest.cc |
diff --git a/base/prefs/json_pref_store_unittest.cc b/base/prefs/json_pref_store_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..41cad8985cf8386a9e59d881cc2d41b128db096e |
--- /dev/null |
+++ b/base/prefs/json_pref_store_unittest.cc |
@@ -0,0 +1,965 @@ |
+// Copyright (c) 2012 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/prefs/json_pref_store.h" |
+ |
+#include <stdint.h> |
+ |
+#include <utility> |
+ |
+#include "base/bind.h" |
+#include "base/files/file_util.h" |
+#include "base/files/scoped_temp_dir.h" |
+#include "base/location.h" |
+#include "base/macros.h" |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/metrics/histogram_samples.h" |
+#include "base/metrics/statistics_recorder.h" |
+#include "base/path_service.h" |
+#include "base/prefs/pref_filter.h" |
+#include "base/run_loop.h" |
+#include "base/single_thread_task_runner.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_util.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "base/test/simple_test_clock.h" |
+#include "base/threading/sequenced_worker_pool.h" |
+#include "base/threading/thread.h" |
+#include "base/values.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace base { |
+namespace { |
+ |
+const char kHomePage[] = "homepage"; |
+ |
+// Set the time on the given SimpleTestClock to the given time in minutes. |
+void SetCurrentTimeInMinutes(double minutes, base::SimpleTestClock* clock) { |
+ const int32_t kBaseTimeMins = 100; |
+ clock->SetNow(base::Time::FromDoubleT((kBaseTimeMins + minutes) * 60)); |
+} |
+ |
+// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on |
+// to the |prefs| until explicitly asked to release them. |
+class InterceptingPrefFilter : public PrefFilter { |
+ public: |
+ InterceptingPrefFilter(); |
+ ~InterceptingPrefFilter() override; |
+ |
+ // PrefFilter implementation: |
+ void FilterOnLoad( |
+ const PostFilterOnLoadCallback& post_filter_on_load_callback, |
+ scoped_ptr<base::DictionaryValue> pref_store_contents) override; |
+ void FilterUpdate(const std::string& path) override {} |
+ void FilterSerializeData( |
+ base::DictionaryValue* pref_store_contents) override {} |
+ |
+ bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; } |
+ |
+ // Finalize an intercepted read, handing |intercepted_prefs_| back to its |
+ // JsonPrefStore. |
+ void ReleasePrefs(); |
+ |
+ private: |
+ PostFilterOnLoadCallback post_filter_on_load_callback_; |
+ scoped_ptr<base::DictionaryValue> intercepted_prefs_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter); |
+}; |
+ |
+InterceptingPrefFilter::InterceptingPrefFilter() {} |
+InterceptingPrefFilter::~InterceptingPrefFilter() {} |
+ |
+void InterceptingPrefFilter::FilterOnLoad( |
+ const PostFilterOnLoadCallback& post_filter_on_load_callback, |
+ scoped_ptr<base::DictionaryValue> pref_store_contents) { |
+ post_filter_on_load_callback_ = post_filter_on_load_callback; |
+ intercepted_prefs_ = std::move(pref_store_contents); |
+} |
+ |
+void InterceptingPrefFilter::ReleasePrefs() { |
+ EXPECT_FALSE(post_filter_on_load_callback_.is_null()); |
+ post_filter_on_load_callback_.Run(std::move(intercepted_prefs_), false); |
+ post_filter_on_load_callback_.Reset(); |
+} |
+ |
+class MockPrefStoreObserver : public PrefStore::Observer { |
+ public: |
+ MOCK_METHOD1(OnPrefValueChanged, void (const std::string&)); |
+ MOCK_METHOD1(OnInitializationCompleted, void (bool)); |
+}; |
+ |
+class MockReadErrorDelegate : public PersistentPrefStore::ReadErrorDelegate { |
+ public: |
+ MOCK_METHOD1(OnError, void(PersistentPrefStore::PrefReadError)); |
+}; |
+ |
+} // namespace |
+ |
+class JsonPrefStoreTest : public testing::Test { |
+ protected: |
+ void SetUp() override { |
+ ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
+ |
+ ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir_)); |
+ data_dir_ = data_dir_.AppendASCII("prefs"); |
+ ASSERT_TRUE(PathExists(data_dir_)); |
+ } |
+ |
+ void TearDown() override { |
+ // Make sure all pending tasks have been processed (e.g., deleting the |
+ // JsonPrefStore may post write tasks). |
+ RunLoop().RunUntilIdle(); |
+ } |
+ |
+ // The path to temporary directory used to contain the test operations. |
+ base::ScopedTempDir temp_dir_; |
+ // The path to the directory where the test data is stored. |
+ base::FilePath data_dir_; |
+ // A message loop that we can use as the file thread message loop. |
+ MessageLoop message_loop_; |
+ |
+ private: |
+ // Ensure histograms are reset for each test. |
+ StatisticsRecorder statistics_recorder_; |
+}; |
+ |
+// Test fallback behavior for a nonexistent file. |
+TEST_F(JsonPrefStoreTest, NonExistentFile) { |
+ base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); |
+ ASSERT_FALSE(PathExists(bogus_input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( |
+ bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, |
+ pref_store->ReadPrefs()); |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+} |
+ |
+// Test fallback behavior for a nonexistent file and alternate file. |
+TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) { |
+ base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); |
+ base::FilePath bogus_alternate_input_file = |
+ temp_dir_.path().AppendASCII("read_alternate.txt"); |
+ ASSERT_FALSE(PathExists(bogus_input_file)); |
+ ASSERT_FALSE(PathExists(bogus_alternate_input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = |
+ new JsonPrefStore(bogus_input_file, bogus_alternate_input_file, |
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE, |
+ pref_store->ReadPrefs()); |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+} |
+ |
+// Test fallback behavior for an invalid file. |
+TEST_F(JsonPrefStoreTest, InvalidFile) { |
+ base::FilePath invalid_file_original = data_dir_.AppendASCII("invalid.json"); |
+ base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json"); |
+ ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( |
+ invalid_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE, |
+ pref_store->ReadPrefs()); |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ |
+ // The file should have been moved aside. |
+ EXPECT_FALSE(PathExists(invalid_file)); |
+ base::FilePath moved_aside = temp_dir_.path().AppendASCII("invalid.bad"); |
+ EXPECT_TRUE(PathExists(moved_aside)); |
+ EXPECT_TRUE(TextContentsEqual(invalid_file_original, moved_aside)); |
+} |
+ |
+// This function is used to avoid code duplication while testing synchronous and |
+// asynchronous version of the JsonPrefStore loading. |
+void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store, |
+ const base::FilePath& output_file, |
+ const base::FilePath& golden_output_file) { |
+ const char kNewWindowsInTabs[] = "tabs.new_windows_in_tabs"; |
+ const char kMaxTabs[] = "tabs.max_tabs"; |
+ const char kLongIntPref[] = "long_int.pref"; |
+ |
+ std::string cnn("http://www.cnn.com"); |
+ |
+ const Value* actual; |
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, &actual)); |
+ std::string string_value; |
+ EXPECT_TRUE(actual->GetAsString(&string_value)); |
+ EXPECT_EQ(cnn, string_value); |
+ |
+ const char kSomeDirectory[] = "some_directory"; |
+ |
+ EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual)); |
+ base::FilePath::StringType path; |
+ EXPECT_TRUE(actual->GetAsString(&path)); |
+ EXPECT_EQ(base::FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), path); |
+ base::FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/")); |
+ |
+ pref_store->SetValue(kSomeDirectory, |
+ make_scoped_ptr(new StringValue(some_path.value())), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ EXPECT_TRUE(pref_store->GetValue(kSomeDirectory, &actual)); |
+ EXPECT_TRUE(actual->GetAsString(&path)); |
+ EXPECT_EQ(some_path.value(), path); |
+ |
+ // Test reading some other data types from sub-dictionaries. |
+ EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual)); |
+ bool boolean = false; |
+ EXPECT_TRUE(actual->GetAsBoolean(&boolean)); |
+ EXPECT_TRUE(boolean); |
+ |
+ pref_store->SetValue(kNewWindowsInTabs, |
+ make_scoped_ptr(new FundamentalValue(false)), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ EXPECT_TRUE(pref_store->GetValue(kNewWindowsInTabs, &actual)); |
+ EXPECT_TRUE(actual->GetAsBoolean(&boolean)); |
+ EXPECT_FALSE(boolean); |
+ |
+ EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual)); |
+ int integer = 0; |
+ EXPECT_TRUE(actual->GetAsInteger(&integer)); |
+ EXPECT_EQ(20, integer); |
+ pref_store->SetValue(kMaxTabs, make_scoped_ptr(new FundamentalValue(10)), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ EXPECT_TRUE(pref_store->GetValue(kMaxTabs, &actual)); |
+ EXPECT_TRUE(actual->GetAsInteger(&integer)); |
+ EXPECT_EQ(10, integer); |
+ |
+ pref_store->SetValue( |
+ kLongIntPref, |
+ make_scoped_ptr(new StringValue(base::Int64ToString(214748364842LL))), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ EXPECT_TRUE(pref_store->GetValue(kLongIntPref, &actual)); |
+ EXPECT_TRUE(actual->GetAsString(&string_value)); |
+ int64_t value; |
+ base::StringToInt64(string_value, &value); |
+ EXPECT_EQ(214748364842LL, value); |
+ |
+ // Serialize and compare to expected output. |
+ ASSERT_TRUE(PathExists(golden_output_file)); |
+ pref_store->CommitPendingWrite(); |
+ RunLoop().RunUntilIdle(); |
+ EXPECT_TRUE(TextContentsEqual(golden_output_file, output_file)); |
+ ASSERT_TRUE(base::DeleteFile(output_file, false)); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, Basic) { |
+ ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("write.json"))); |
+ |
+ // Test that the persistent value can be loaded. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ ASSERT_TRUE(PathExists(input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( |
+ input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, BasicAsync) { |
+ ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("write.json"))); |
+ |
+ // Test that the persistent value can be loaded. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ ASSERT_TRUE(PathExists(input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( |
+ input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ |
+ { |
+ MockPrefStoreObserver mock_observer; |
+ pref_store->AddObserver(&mock_observer); |
+ |
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; |
+ pref_store->ReadPrefsAsync(mock_error_delegate); |
+ |
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); |
+ EXPECT_CALL(*mock_error_delegate, |
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); |
+ RunLoop().RunUntilIdle(); |
+ pref_store->RemoveObserver(&mock_observer); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ } |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, PreserveEmptyValues) { |
+ FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json"); |
+ |
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( |
+ pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ |
+ // Set some keys with empty values. |
+ pref_store->SetValue("list", make_scoped_ptr(new base::ListValue), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ pref_store->SetValue("dict", make_scoped_ptr(new base::DictionaryValue), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ |
+ // Write to file. |
+ pref_store->CommitPendingWrite(); |
+ RunLoop().RunUntilIdle(); |
+ |
+ // Reload. |
+ pref_store = new JsonPrefStore(pref_file, message_loop_.task_runner(), |
+ scoped_ptr<PrefFilter>()); |
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); |
+ ASSERT_FALSE(pref_store->ReadOnly()); |
+ |
+ // Check values. |
+ const Value* result = NULL; |
+ EXPECT_TRUE(pref_store->GetValue("list", &result)); |
+ EXPECT_TRUE(ListValue().Equals(result)); |
+ EXPECT_TRUE(pref_store->GetValue("dict", &result)); |
+ EXPECT_TRUE(DictionaryValue().Equals(result)); |
+} |
+ |
+// This test is just documenting some potentially non-obvious behavior. It |
+// shouldn't be taken as normative. |
+TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) { |
+ FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json"); |
+ |
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( |
+ pref_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ |
+ scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); |
+ dict->SetString("key", "value"); |
+ pref_store->SetValue("dict", std::move(dict), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ |
+ pref_store->RemoveValue("dict.key", |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ |
+ const base::Value* retrieved_dict = NULL; |
+ bool has_dict = pref_store->GetValue("dict", &retrieved_dict); |
+ EXPECT_FALSE(has_dict); |
+} |
+ |
+// Tests asynchronous reading of the file when there is no file. |
+TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) { |
+ base::FilePath bogus_input_file = temp_dir_.path().AppendASCII("read.txt"); |
+ ASSERT_FALSE(PathExists(bogus_input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore( |
+ bogus_input_file, message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ MockPrefStoreObserver mock_observer; |
+ pref_store->AddObserver(&mock_observer); |
+ |
+ MockReadErrorDelegate *mock_error_delegate = new MockReadErrorDelegate; |
+ pref_store->ReadPrefsAsync(mock_error_delegate); |
+ |
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); |
+ EXPECT_CALL(*mock_error_delegate, |
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NO_FILE)).Times(1); |
+ RunLoop().RunUntilIdle(); |
+ pref_store->RemoveObserver(&mock_observer); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, ReadWithInterceptor) { |
+ ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("write.json"))); |
+ |
+ // Test that the persistent value can be loaded. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ ASSERT_TRUE(PathExists(input_file)); |
+ |
+ scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter( |
+ new InterceptingPrefFilter()); |
+ InterceptingPrefFilter* raw_intercepting_pref_filter_ = |
+ intercepting_pref_filter.get(); |
+ scoped_refptr<JsonPrefStore> pref_store = |
+ new JsonPrefStore(input_file, message_loop_.task_runner(), |
+ std::move(intercepting_pref_filter)); |
+ |
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE, |
+ pref_store->ReadPrefs()); |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ |
+ // The store shouldn't be considered initialized until the interceptor |
+ // returns. |
+ EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); |
+ EXPECT_FALSE(pref_store->IsInitializationComplete()); |
+ EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL)); |
+ |
+ raw_intercepting_pref_filter_->ReleasePrefs(); |
+ |
+ EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL)); |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) { |
+ ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("write.json"))); |
+ |
+ // Test that the persistent value can be loaded. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ ASSERT_TRUE(PathExists(input_file)); |
+ |
+ scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter( |
+ new InterceptingPrefFilter()); |
+ InterceptingPrefFilter* raw_intercepting_pref_filter_ = |
+ intercepting_pref_filter.get(); |
+ scoped_refptr<JsonPrefStore> pref_store = |
+ new JsonPrefStore(input_file, message_loop_.task_runner(), |
+ std::move(intercepting_pref_filter)); |
+ |
+ MockPrefStoreObserver mock_observer; |
+ pref_store->AddObserver(&mock_observer); |
+ |
+ // Ownership of the |mock_error_delegate| is handed to the |pref_store| below. |
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; |
+ |
+ { |
+ pref_store->ReadPrefsAsync(mock_error_delegate); |
+ |
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0); |
+ // EXPECT_CALL(*mock_error_delegate, |
+ // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); |
+ RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs()); |
+ EXPECT_FALSE(pref_store->IsInitializationComplete()); |
+ EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL)); |
+ } |
+ |
+ { |
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); |
+ // EXPECT_CALL(*mock_error_delegate, |
+ // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); |
+ |
+ raw_intercepting_pref_filter_->ReleasePrefs(); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL)); |
+ } |
+ |
+ pref_store->RemoveObserver(&mock_observer); |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, AlternateFile) { |
+ ASSERT_TRUE( |
+ base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("alternate.json"))); |
+ |
+ // Test that the alternate file is moved to the main file and read as-is from |
+ // there. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ base::FilePath alternate_input_file = |
+ temp_dir_.path().AppendASCII("alternate.json"); |
+ ASSERT_FALSE(PathExists(input_file)); |
+ ASSERT_TRUE(PathExists(alternate_input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = |
+ new JsonPrefStore(input_file, alternate_input_file, |
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ |
+ ASSERT_FALSE(PathExists(input_file)); |
+ ASSERT_TRUE(PathExists(alternate_input_file)); |
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); |
+ |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_FALSE(PathExists(alternate_input_file)); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) { |
+ ASSERT_TRUE( |
+ base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("write.json"))); |
+ ASSERT_TRUE( |
+ base::CopyFile(data_dir_.AppendASCII("invalid.json"), |
+ temp_dir_.path().AppendASCII("alternate.json"))); |
+ |
+ // Test that the alternate file is ignored and that the read occurs from the |
+ // existing main file. There is no attempt at even deleting the alternate |
+ // file as this scenario should never happen in normal user-data-dirs. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ base::FilePath alternate_input_file = |
+ temp_dir_.path().AppendASCII("alternate.json"); |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_TRUE(PathExists(alternate_input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = |
+ new JsonPrefStore(input_file, alternate_input_file, |
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_TRUE(PathExists(alternate_input_file)); |
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); |
+ |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_TRUE(PathExists(alternate_input_file)); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, AlternateFileDNE) { |
+ ASSERT_TRUE( |
+ base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("write.json"))); |
+ |
+ // Test that the basic read works fine when an alternate file is specified but |
+ // does not exist. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ base::FilePath alternate_input_file = |
+ temp_dir_.path().AppendASCII("alternate.json"); |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_FALSE(PathExists(alternate_input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = |
+ new JsonPrefStore(input_file, alternate_input_file, |
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_FALSE(PathExists(alternate_input_file)); |
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs()); |
+ |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_FALSE(PathExists(alternate_input_file)); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) { |
+ ASSERT_TRUE( |
+ base::CopyFile(data_dir_.AppendASCII("read.json"), |
+ temp_dir_.path().AppendASCII("alternate.json"))); |
+ |
+ // Test that the alternate file is moved to the main file and read as-is from |
+ // there even when the read is made asynchronously. |
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json"); |
+ base::FilePath alternate_input_file = |
+ temp_dir_.path().AppendASCII("alternate.json"); |
+ ASSERT_FALSE(PathExists(input_file)); |
+ ASSERT_TRUE(PathExists(alternate_input_file)); |
+ scoped_refptr<JsonPrefStore> pref_store = |
+ new JsonPrefStore(input_file, alternate_input_file, |
+ message_loop_.task_runner(), scoped_ptr<PrefFilter>()); |
+ |
+ ASSERT_FALSE(PathExists(input_file)); |
+ ASSERT_TRUE(PathExists(alternate_input_file)); |
+ |
+ { |
+ MockPrefStoreObserver mock_observer; |
+ pref_store->AddObserver(&mock_observer); |
+ |
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate; |
+ pref_store->ReadPrefsAsync(mock_error_delegate); |
+ |
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1); |
+ EXPECT_CALL(*mock_error_delegate, |
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0); |
+ RunLoop().RunUntilIdle(); |
+ pref_store->RemoveObserver(&mock_observer); |
+ |
+ EXPECT_FALSE(pref_store->ReadOnly()); |
+ EXPECT_TRUE(pref_store->IsInitializationComplete()); |
+ } |
+ |
+ ASSERT_TRUE(PathExists(input_file)); |
+ ASSERT_FALSE(PathExists(alternate_input_file)); |
+ |
+ // The JSON file looks like this: |
+ // { |
+ // "homepage": "http://www.cnn.com", |
+ // "some_directory": "/usr/local/", |
+ // "tabs": { |
+ // "new_windows_in_tabs": true, |
+ // "max_tabs": 20 |
+ // } |
+ // } |
+ |
+ RunBasicJsonPrefStoreTest( |
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json")); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestBasic) { |
+ SimpleTestClock* test_clock = new SimpleTestClock; |
+ SetCurrentTimeInMinutes(0, test_clock); |
+ JsonPrefStore::WriteCountHistogram histogram( |
+ base::TimeDelta::FromSeconds(10), |
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), |
+ scoped_ptr<base::Clock>(test_clock)); |
+ int32_t report_interval = |
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; |
+ |
+ histogram.RecordWriteOccured(); |
+ |
+ SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); |
+ histogram.ReportOutstandingWrites(); |
+ scoped_ptr<HistogramSamples> samples = |
+ histogram.GetHistogram()->SnapshotSamples(); |
+ ASSERT_EQ(1, samples->GetCount(1)); |
+ ASSERT_EQ(1, samples->TotalCount()); |
+ |
+ ASSERT_EQ("Settings.JsonDataWriteCount.Local_State", |
+ histogram.GetHistogram()->histogram_name()); |
+ ASSERT_TRUE(histogram.GetHistogram()->HasConstructionArguments(1, 30, 31)); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestSinglePeriod) { |
+ SimpleTestClock* test_clock = new SimpleTestClock; |
+ SetCurrentTimeInMinutes(0, test_clock); |
+ JsonPrefStore::WriteCountHistogram histogram( |
+ base::TimeDelta::FromSeconds(10), |
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), |
+ scoped_ptr<base::Clock>(test_clock)); |
+ int32_t report_interval = |
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; |
+ |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(0.5 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(0.7 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ |
+ // Nothing should be recorded until the report period has elapsed. |
+ scoped_ptr<HistogramSamples> samples = |
+ histogram.GetHistogram()->SnapshotSamples(); |
+ ASSERT_EQ(0, samples->TotalCount()); |
+ |
+ SetCurrentTimeInMinutes(1.3 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ |
+ // Now the report period has elapsed. |
+ samples = histogram.GetHistogram()->SnapshotSamples(); |
+ ASSERT_EQ(1, samples->GetCount(3)); |
+ ASSERT_EQ(1, samples->TotalCount()); |
+ |
+ // The last write won't be recorded because the second count period hasn't |
+ // fully elapsed. |
+ SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); |
+ histogram.ReportOutstandingWrites(); |
+ |
+ samples = histogram.GetHistogram()->SnapshotSamples(); |
+ ASSERT_EQ(1, samples->GetCount(3)); |
+ ASSERT_EQ(1, samples->TotalCount()); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestMultiplePeriods) { |
+ SimpleTestClock* test_clock = new SimpleTestClock; |
+ SetCurrentTimeInMinutes(0, test_clock); |
+ JsonPrefStore::WriteCountHistogram histogram( |
+ base::TimeDelta::FromSeconds(10), |
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), |
+ scoped_ptr<base::Clock>(test_clock)); |
+ int32_t report_interval = |
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; |
+ |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(0.5 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(0.7 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(1.3 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(1.5 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(2.1 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(2.5 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(2.7 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(3.3 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ |
+ // The last write won't be recorded because the second count period hasn't |
+ // fully elapsed |
+ SetCurrentTimeInMinutes(3.5 * report_interval, test_clock); |
+ histogram.ReportOutstandingWrites(); |
+ scoped_ptr<HistogramSamples> samples = |
+ histogram.GetHistogram()->SnapshotSamples(); |
+ ASSERT_EQ(2, samples->GetCount(3)); |
+ ASSERT_EQ(1, samples->GetCount(2)); |
+ ASSERT_EQ(3, samples->TotalCount()); |
+} |
+ |
+TEST_F(JsonPrefStoreTest, WriteCountHistogramTestPeriodWithGaps) { |
+ SimpleTestClock* test_clock = new SimpleTestClock; |
+ SetCurrentTimeInMinutes(0, test_clock); |
+ JsonPrefStore::WriteCountHistogram histogram( |
+ base::TimeDelta::FromSeconds(10), |
+ base::FilePath(FILE_PATH_LITERAL("/tmp/Local State")), |
+ scoped_ptr<base::Clock>(test_clock)); |
+ int32_t report_interval = |
+ JsonPrefStore::WriteCountHistogram::kHistogramWriteReportIntervalMins; |
+ |
+ // 1 write in the first period. |
+ histogram.RecordWriteOccured(); |
+ |
+ // No writes in the second and third periods. |
+ |
+ // 2 writes in the fourth period. |
+ SetCurrentTimeInMinutes(3.1 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(3.3 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ |
+ // No writes in the fifth period. |
+ |
+ // 3 writes in the sixth period. |
+ SetCurrentTimeInMinutes(5.1 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(5.3 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ SetCurrentTimeInMinutes(5.5 * report_interval, test_clock); |
+ histogram.RecordWriteOccured(); |
+ |
+ SetCurrentTimeInMinutes(6.1 * report_interval, test_clock); |
+ histogram.ReportOutstandingWrites(); |
+ scoped_ptr<HistogramSamples> samples = |
+ histogram.GetHistogram()->SnapshotSamples(); |
+ ASSERT_EQ(3, samples->GetCount(0)); |
+ ASSERT_EQ(1, samples->GetCount(1)); |
+ ASSERT_EQ(1, samples->GetCount(2)); |
+ ASSERT_EQ(1, samples->GetCount(3)); |
+ ASSERT_EQ(6, samples->TotalCount()); |
+} |
+ |
+class JsonPrefStoreLossyWriteTest : public JsonPrefStoreTest { |
+ protected: |
+ void SetUp() override { |
+ JsonPrefStoreTest::SetUp(); |
+ test_file_ = temp_dir_.path().AppendASCII("test.json"); |
+ } |
+ |
+ // Creates a JsonPrefStore with the given |file_writer|. |
+ scoped_refptr<JsonPrefStore> CreatePrefStore() { |
+ return new JsonPrefStore(test_file_, message_loop_.task_runner(), |
+ scoped_ptr<PrefFilter>()); |
+ } |
+ |
+ // Return the ImportantFileWriter for a given JsonPrefStore. |
+ ImportantFileWriter* GetImportantFileWriter( |
+ scoped_refptr<JsonPrefStore> pref_store) { |
+ return &(pref_store->writer_); |
+ } |
+ |
+ // Get the contents of kTestFile. Pumps the message loop before returning the |
+ // result. |
+ std::string GetTestFileContents() { |
+ RunLoop().RunUntilIdle(); |
+ std::string file_contents; |
+ ReadFileToString(test_file_, &file_contents); |
+ return file_contents; |
+ } |
+ |
+ private: |
+ base::FilePath test_file_; |
+}; |
+ |
+TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteBasic) { |
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); |
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); |
+ |
+ // Set a normal pref and check that it gets scheduled to be written. |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ pref_store->SetValue("normal", |
+ make_scoped_ptr(new base::StringValue("normal")), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ ASSERT_TRUE(file_writer->HasPendingWrite()); |
+ file_writer->DoScheduledWrite(); |
+ ASSERT_EQ("{\"normal\":\"normal\"}", GetTestFileContents()); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ |
+ // Set a lossy pref and check that it is not scheduled to be written. |
+ // SetValue/RemoveValue. |
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ pref_store->RemoveValue("lossy", WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ |
+ // SetValueSilently/RemoveValueSilently. |
+ pref_store->SetValueSilently("lossy", |
+ make_scoped_ptr(new base::StringValue("lossy")), |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ pref_store->RemoveValueSilently("lossy", |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ |
+ // ReportValueChanged. |
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ pref_store->ReportValueChanged("lossy", |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ |
+ // Call CommitPendingWrite and check that the lossy pref and the normal pref |
+ // are there with the last values set above. |
+ pref_store->CommitPendingWrite(); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", |
+ GetTestFileContents()); |
+} |
+ |
+TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossyFirst) { |
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); |
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); |
+ |
+ // Set a lossy pref and check that it is not scheduled to be written. |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ |
+ // Set a normal pref and check that it is scheduled to be written. |
+ pref_store->SetValue("normal", |
+ make_scoped_ptr(new base::StringValue("normal")), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ ASSERT_TRUE(file_writer->HasPendingWrite()); |
+ |
+ // Call DoScheduledWrite and check both prefs get written. |
+ file_writer->DoScheduledWrite(); |
+ ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", |
+ GetTestFileContents()); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+} |
+ |
+TEST_F(JsonPrefStoreLossyWriteTest, LossyWriteMixedLossySecond) { |
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); |
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); |
+ |
+ // Set a normal pref and check that it is scheduled to be written. |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ pref_store->SetValue("normal", |
+ make_scoped_ptr(new base::StringValue("normal")), |
+ WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); |
+ ASSERT_TRUE(file_writer->HasPendingWrite()); |
+ |
+ // Set a lossy pref and check that the write is still scheduled. |
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_TRUE(file_writer->HasPendingWrite()); |
+ |
+ // Call DoScheduledWrite and check both prefs get written. |
+ file_writer->DoScheduledWrite(); |
+ ASSERT_EQ("{\"lossy\":\"lossy\",\"normal\":\"normal\"}", |
+ GetTestFileContents()); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+} |
+ |
+TEST_F(JsonPrefStoreLossyWriteTest, ScheduleLossyWrite) { |
+ scoped_refptr<JsonPrefStore> pref_store = CreatePrefStore(); |
+ ImportantFileWriter* file_writer = GetImportantFileWriter(pref_store); |
+ |
+ // Set a lossy pref and check that it is not scheduled to be written. |
+ pref_store->SetValue("lossy", make_scoped_ptr(new base::StringValue("lossy")), |
+ WriteablePrefStore::LOSSY_PREF_WRITE_FLAG); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ |
+ // Schedule pending lossy writes and check that it is scheduled. |
+ pref_store->SchedulePendingLossyWrites(); |
+ ASSERT_TRUE(file_writer->HasPendingWrite()); |
+ |
+ // Call CommitPendingWrite and check that the lossy pref is there with the |
+ // last value set above. |
+ pref_store->CommitPendingWrite(); |
+ ASSERT_FALSE(file_writer->HasPendingWrite()); |
+ ASSERT_EQ("{\"lossy\":\"lossy\"}", GetTestFileContents()); |
+} |
+ |
+} // namespace base |