summaryrefslogtreecommitdiffstats
path: root/base/prefs
diff options
context:
space:
mode:
Diffstat (limited to 'base/prefs')
-rw-r--r--base/prefs/json_pref_store.cc37
-rw-r--r--base/prefs/json_pref_store.h18
-rw-r--r--base/prefs/json_pref_store_unittest.cc204
3 files changed, 249 insertions, 10 deletions
diff --git a/base/prefs/json_pref_store.cc b/base/prefs/json_pref_store.cc
index a6c2362..9180984 100644
--- a/base/prefs/json_pref_store.cc
+++ b/base/prefs/json_pref_store.cc
@@ -37,21 +37,23 @@ class FileThreadDeserializer
origin_loop_proxy_(base::MessageLoopProxy::current()) {
}
- void Start(const base::FilePath& path) {
+ void Start(const base::FilePath& path,
+ const base::FilePath& alternate_path) {
DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
// TODO(gab): This should use PostTaskAndReplyWithResult instead of using
// the |error_| member to pass data across tasks.
sequenced_task_runner_->PostTask(
FROM_HERE,
base::Bind(&FileThreadDeserializer::ReadFileAndReport,
- this, path));
+ this, path, alternate_path));
}
// Deserializes JSON on the sequenced task runner.
- void ReadFileAndReport(const base::FilePath& path) {
+ void ReadFileAndReport(const base::FilePath& path,
+ const base::FilePath& alternate_path) {
DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
- value_.reset(DoReading(path, &error_, &no_dir_));
+ value_.reset(DoReading(path, alternate_path, &error_, &no_dir_));
origin_loop_proxy_->PostTask(
FROM_HERE,
@@ -65,8 +67,14 @@ class FileThreadDeserializer
}
static base::Value* DoReading(const base::FilePath& path,
+ const base::FilePath& alternate_path,
PersistentPrefStore::PrefReadError* error,
bool* no_dir) {
+ if (!base::PathExists(path) && !alternate_path.empty() &&
+ base::PathExists(alternate_path)) {
+ base::Move(alternate_path, path);
+ }
+
int error_code;
std::string error_msg;
JSONFileValueSerializer serializer(path);
@@ -167,6 +175,22 @@ JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
read_error_(PREF_READ_ERROR_NONE) {
}
+JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
+ const base::FilePath& alternate_filename,
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter)
+ : path_(filename),
+ alternate_path_(alternate_filename),
+ sequenced_task_runner_(sequenced_task_runner),
+ prefs_(new base::DictionaryValue()),
+ read_only_(false),
+ writer_(filename, sequenced_task_runner),
+ pref_filter_(pref_filter.Pass()),
+ initialized_(false),
+ filtering_in_progress_(false),
+ read_error_(PREF_READ_ERROR_NONE) {
+}
+
bool JsonPrefStore::GetValue(const std::string& key,
const base::Value** result) const {
base::Value* tmp = NULL;
@@ -252,7 +276,8 @@ PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
PrefReadError error;
bool no_dir;
scoped_ptr<base::Value> value(
- FileThreadDeserializer::DoReading(path_, &error, &no_dir));
+ FileThreadDeserializer::DoReading(path_, alternate_path_, &error,
+ &no_dir));
OnFileRead(value.Pass(), error, no_dir);
return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE :
error;
@@ -271,7 +296,7 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
// in the end.
scoped_refptr<FileThreadDeserializer> deserializer(
new FileThreadDeserializer(this, sequenced_task_runner_.get()));
- deserializer->Start(path_);
+ deserializer->Start(path_, alternate_path_);
}
void JsonPrefStore::CommitPendingWrite() {
diff --git a/base/prefs/json_pref_store.h b/base/prefs/json_pref_store.h
index 49e74ee..6ceea68 100644
--- a/base/prefs/json_pref_store.h
+++ b/base/prefs/json_pref_store.h
@@ -43,12 +43,23 @@ class BASE_PREFS_EXPORT JsonPrefStore
const base::FilePath& pref_filename,
base::SequencedWorkerPool* worker_pool);
- // |sequenced_task_runner| is must be a shutdown-blocking task runner, ideally
- // created by GetTaskRunnerForFile() method above.
+ // Same as the constructor below with no alternate filename.
JsonPrefStore(const base::FilePath& pref_filename,
base::SequencedTaskRunner* sequenced_task_runner,
scoped_ptr<PrefFilter> pref_filter);
+ // |sequenced_task_runner| must be a shutdown-blocking task runner, ideally
+ // created by the GetTaskRunnerForFile() method above.
+ // |pref_filename| is the path to the file to read prefs from.
+ // |pref_alternate_filename| is the path to an alternate file which the
+ // desired prefs may have previously been written to. If |pref_filename|
+ // doesn't exist and |pref_alternate_filename| does, |pref_alternate_filename|
+ // will be moved to |pref_filename| before the read occurs.
+ JsonPrefStore(const base::FilePath& pref_filename,
+ const base::FilePath& pref_alternate_filename,
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter);
+
// PrefStore overrides:
virtual bool GetValue(const std::string& key,
const base::Value** result) const OVERRIDE;
@@ -115,7 +126,8 @@ class BASE_PREFS_EXPORT JsonPrefStore
scoped_ptr<base::DictionaryValue> prefs,
bool schedule_write);
- base::FilePath path_;
+ const base::FilePath path_;
+ const base::FilePath alternate_path_;
const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
scoped_ptr<base::DictionaryValue> prefs_;
diff --git a/base/prefs/json_pref_store_unittest.cc b/base/prefs/json_pref_store_unittest.cc
index 2b4a9e8..441c229 100644
--- a/base/prefs/json_pref_store_unittest.cc
+++ b/base/prefs/json_pref_store_unittest.cc
@@ -44,7 +44,7 @@ class InterceptingPrefFilter : public PrefFilter {
bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; }
- // Finalize an intercepted read, handing |intercept_prefs_| back to its
+ // Finalize an intercepted read, handing |intercepted_prefs_| back to its
// JsonPrefStore.
void ReleasePrefs();
@@ -122,6 +122,23 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) {
EXPECT_FALSE(pref_store->ReadOnly());
}
+// Test fallback behavior for a nonexistent file and alternate file.
+TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) {
+ base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
+ base::FilePath bogus_alternate_input_file =
+ data_dir_.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_.message_loop_proxy().get(),
+ 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");
@@ -469,4 +486,189 @@ TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
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_.message_loop_proxy().get(),
+ 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_.message_loop_proxy().get(),
+ 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_.message_loop_proxy().get(),
+ 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_.message_loop_proxy().get(),
+ 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"));
+}
+
} // namespace base