| // Copyright (c) 2011 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 "chrome/browser/extensions/extension_settings_storage_unittest.h" |
| |
| #include "base/bind.h" |
| #include "base/json/json_writer.h" |
| #include "base/file_util.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/values.h" |
| #include "chrome/browser/extensions/extension_settings.h" |
| #include "content/browser/browser_thread.h" |
| |
| // Define macro to get the __LINE__ expansion. |
| #define NEW_CALLBACK(expected) \ |
| (new AssertEqualsCallback((expected), __LINE__)) |
| |
| namespace { |
| |
| // Callback from storage methods which performs the test assertions. |
| class AssertEqualsCallback : public ExtensionSettingsStorage::Callback { |
| public: |
| AssertEqualsCallback(DictionaryValue* expected, int line) |
| : expected_(expected), line_(line), called_(false) { |
| } |
| |
| ~AssertEqualsCallback() { |
| // Need to DCHECK since ASSERT_* can't be used from destructors. |
| DCHECK(called_); |
| } |
| |
| virtual void OnSuccess(DictionaryValue* actual) OVERRIDE { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ASSERT_FALSE(called_) << "Callback has already been called"; |
| called_ = true; |
| if (expected_ == NULL) { |
| ASSERT_TRUE(actual == NULL) << "Values are different:\n" << |
| "Line: " << line_ << "\n" << |
| "Expected: NULL\n" << |
| "Got: " << GetJson(actual); |
| } else { |
| ASSERT_TRUE(expected_->Equals(actual)) << "Values are different:\n" << |
| "Line: " << line_ << "\n" << |
| "Expected: " << GetJson(expected_) << |
| "Got: " << GetJson(actual); |
| delete actual; |
| } |
| } |
| |
| virtual void OnFailure(const std::string& message) OVERRIDE { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| ASSERT_FALSE(called_) << "Callback has already been called"; |
| called_ = true; |
| // No tests allow failure (yet). |
| ASSERT_TRUE(false) << "Callback failed on line " << line_; |
| } |
| |
| private: |
| std::string GetJson(Value* value) { |
| std::string json; |
| base::JSONWriter::Write(value, true, &json); |
| return json; |
| } |
| |
| DictionaryValue* expected_; |
| int line_; |
| bool called_; |
| }; |
| |
| } // namespace |
| |
| ExtensionSettingsStorageTest::ExtensionSettingsStorageTest() |
| : key1_("foo"), key2_("bar"), key3_("baz") { |
| val1_.reset(Value::CreateStringValue(key1_ + "Value")); |
| val2_.reset(Value::CreateStringValue(key2_ + "Value")); |
| val3_.reset(Value::CreateStringValue(key3_ + "Value")); |
| |
| emptyList_.reset(new ListValue()); |
| |
| list1_.reset(new ListValue()); |
| list1_->Append(Value::CreateStringValue(key1_)); |
| |
| list2_.reset(new ListValue()); |
| list2_->Append(Value::CreateStringValue(key2_)); |
| |
| list12_.reset(new ListValue()); |
| list12_->Append(Value::CreateStringValue(key1_)); |
| list12_->Append(Value::CreateStringValue(key2_)); |
| |
| list13_.reset(new ListValue()); |
| list13_->Append(Value::CreateStringValue(key1_)); |
| list13_->Append(Value::CreateStringValue(key3_)); |
| |
| list123_.reset(new ListValue()); |
| list123_->Append(Value::CreateStringValue(key1_)); |
| list123_->Append(Value::CreateStringValue(key2_)); |
| list123_->Append(Value::CreateStringValue(key3_)); |
| |
| emptyDict_.reset(new DictionaryValue()); |
| |
| dict1_.reset(new DictionaryValue()); |
| dict1_->Set(key1_, val1_->DeepCopy()); |
| |
| dict12_.reset(new DictionaryValue()); |
| dict12_->Set(key1_, val1_->DeepCopy()); |
| dict12_->Set(key2_, val2_->DeepCopy()); |
| |
| dict123_.reset(new DictionaryValue()); |
| dict123_->Set(key1_, val1_->DeepCopy()); |
| dict123_->Set(key2_, val2_->DeepCopy()); |
| dict123_->Set(key3_, val3_->DeepCopy()); |
| } |
| |
| ExtensionSettingsStorageTest::~ExtensionSettingsStorageTest() { |
| } |
| |
| void ExtensionSettingsStorageTest::SetUp() { |
| ui_message_loop_ = new MessageLoopForUI(); |
| // Use the same message loop for the UI and FILE threads, giving a test |
| // pattern where storage API calls get posted to the same message loop (the |
| // current one), then all run with MessageLoop::current()->RunAllPending(). |
| ui_thread_ = new BrowserThread(BrowserThread::UI, MessageLoop::current()); |
| file_thread_ = new BrowserThread(BrowserThread::FILE, MessageLoop::current()); |
| |
| FilePath temp_dir; |
| file_util::CreateNewTempDirectory(FilePath::StringType(), &temp_dir); |
| settings_ = new ExtensionSettings(temp_dir); |
| |
| storage_ = NULL; |
| (GetParam())( |
| settings_, |
| "fakeExtension", |
| base::Bind( |
| &ExtensionSettingsStorageTest::SetStorage, |
| base::Unretained(this))); |
| MessageLoop::current()->RunAllPending(); |
| DCHECK(storage_ != NULL); |
| } |
| |
| void ExtensionSettingsStorageTest::TearDown() { |
| settings_ = NULL; |
| delete file_thread_; |
| delete ui_thread_; |
| delete ui_message_loop_; |
| } |
| |
| void ExtensionSettingsStorageTest::SetStorage( |
| ExtensionSettingsStorage* storage) { |
| storage_ = storage; |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, GetWhenEmpty) { |
| storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list123_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(NEW_CALLBACK(emptyDict_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, GetWithSingleValue) { |
| storage_->Set(key1_, *val1_, NEW_CALLBACK(dict1_.get())); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list2_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list123_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(NEW_CALLBACK(dict1_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, GetWithMultipleValues) { |
| storage_->Set(*dict12_, NEW_CALLBACK(dict12_.get())); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list13_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list12_, NEW_CALLBACK(dict12_.get())); |
| storage_->Get(*list123_, NEW_CALLBACK(dict12_.get())); |
| storage_->Get(NEW_CALLBACK(dict12_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, RemoveWhenEmpty) { |
| storage_->Remove(key1_, NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(NEW_CALLBACK(emptyDict_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, RemoveWithSingleValue) { |
| storage_->Set(key1_, *val1_, NEW_CALLBACK(dict1_.get())); |
| MessageLoop::current()->RunAllPending(); |
| storage_->Remove(key1_, NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list12_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(NEW_CALLBACK(emptyDict_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, RemoveWithMultipleValues) { |
| storage_->Set(*dict123_, NEW_CALLBACK(dict123_.get())); |
| MessageLoop::current()->RunAllPending(); |
| storage_->Remove(key3_, NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list13_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list12_, NEW_CALLBACK(dict12_.get())); |
| storage_->Get(*list123_, NEW_CALLBACK(dict12_.get())); |
| storage_->Get(NEW_CALLBACK(dict12_.get())); |
| |
| storage_->Remove(*list2_, NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(key2_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list2_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list123_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(NEW_CALLBACK(dict1_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, SetWhenOverwriting) { |
| DictionaryValue key1Val2; |
| key1Val2.Set(key1_, val2_->DeepCopy()); |
| storage_->Set(key1_, *val2_, NEW_CALLBACK(&key1Val2)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Set(*dict12_, NEW_CALLBACK(dict12_.get())); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(key3_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*emptyList_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list13_, NEW_CALLBACK(dict1_.get())); |
| storage_->Get(*list12_, NEW_CALLBACK(dict12_.get())); |
| storage_->Get(*list123_, NEW_CALLBACK(dict12_.get())); |
| storage_->Get(NEW_CALLBACK(dict12_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, ClearWhenEmpty) { |
| storage_->Clear(NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(NEW_CALLBACK(emptyDict_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, ClearWhenNotEmpty) { |
| storage_->Set(*dict12_, NEW_CALLBACK(dict12_.get())); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Clear(NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(key1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(*list1_, NEW_CALLBACK(emptyDict_.get())); |
| storage_->Get(NEW_CALLBACK(emptyDict_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| // Dots should be allowed in key names; they shouldn't be interpreted as |
| // indexing into a dictionary. |
| TEST_P(ExtensionSettingsStorageTest, DotsInKeyNames) { |
| std::string dot_key("foo.bar"); |
| StringValue dot_value("baz.qux"); |
| ListValue dot_list; |
| dot_list.Append(Value::CreateStringValue(dot_key)); |
| DictionaryValue dot_dict; |
| dot_dict.SetWithoutPathExpansion(dot_key, dot_value.DeepCopy()); |
| |
| storage_->Set(dot_key, dot_value, NEW_CALLBACK(&dot_dict)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(dot_key, NEW_CALLBACK(&dot_dict)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Remove(dot_key, NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Set(dot_dict, NEW_CALLBACK(&dot_dict)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(dot_list, NEW_CALLBACK(&dot_dict)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Remove(dot_list, NEW_CALLBACK(NULL)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get(NEW_CALLBACK(emptyDict_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |
| |
| TEST_P(ExtensionSettingsStorageTest, DotsInKeyNamesWithDicts) { |
| DictionaryValue outer_dict; |
| DictionaryValue* inner_dict = new DictionaryValue(); |
| outer_dict.Set("foo", inner_dict); |
| inner_dict->Set("bar", Value::CreateStringValue("baz")); |
| |
| storage_->Set(outer_dict, NEW_CALLBACK(&outer_dict)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get("foo", NEW_CALLBACK(&outer_dict)); |
| MessageLoop::current()->RunAllPending(); |
| |
| storage_->Get("foo.bar", NEW_CALLBACK(emptyDict_.get())); |
| MessageLoop::current()->RunAllPending(); |
| } |