blob: ccbbf16037d1c3e968669c270ed8ede16f226c8b [file] [log] [blame]
// 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();
}