root/extensions/browser/value_store/value_store_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GetJSON
  2. ValuesEqual
  3. SettingsEq
  4. ChangesEq
  5. file_thread_
  6. SetUp
  7. TearDown
  8. TEST_P
  9. TEST_P
  10. TEST_P
  11. TEST_P
  12. TEST_P
  13. TEST_P
  14. TEST_P
  15. TEST_P
  16. TEST_P
  17. TEST_P
  18. TEST_P
  19. TEST_P

// Copyright 2014 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 "extensions/browser/value_store/value_store_unittest.h"

#include "base/json/json_writer.h"
#include "base/memory/linked_ptr.h"
#include "base/values.h"

using content::BrowserThread;

namespace {

// To save typing ValueStore::DEFAULTS everywhere.
const ValueStore::WriteOptions DEFAULTS = ValueStore::DEFAULTS;

// Gets the pretty-printed JSON for a value.
std::string GetJSON(const base::Value& value) {
  std::string json;
  base::JSONWriter::WriteWithOptions(&value,
                                     base::JSONWriter::OPTIONS_PRETTY_PRINT,
                                     &json);
  return json;
}

}  // namespace

// Compares two possibly NULL values for equality, filling |error| with an
// appropriate error message if they're different.
bool ValuesEqual(const base::Value* expected,
                 const base::Value* actual,
                 std::string* error) {
  if (expected == actual) {
    return true;
  }
  if (expected && !actual) {
    *error = "Expected: " + GetJSON(*expected) + ", actual: NULL";
    return false;
  }
  if (actual && !expected) {
    *error = "Expected: NULL, actual: " + GetJSON(*actual);
    return false;
  }
  if (!expected->Equals(actual)) {
    *error =
        "Expected: " + GetJSON(*expected) + ", actual: " + GetJSON(*actual);
    return false;
  }
  return true;
}

// Returns whether the read result of a storage operation has the expected
// settings.
testing::AssertionResult SettingsEq(
    const char* _1, const char* _2,
    const base::DictionaryValue& expected,
    ValueStore::ReadResult actual_result) {
  if (actual_result->HasError()) {
    return testing::AssertionFailure() <<
        "Result has error: " << actual_result->error().message;
  }

  std::string error;
  if (!ValuesEqual(&expected, &actual_result->settings(), &error)) {
    return testing::AssertionFailure() << error;
  }

  return testing::AssertionSuccess();
}

// Returns whether the write result of a storage operation has the expected
// changes.
testing::AssertionResult ChangesEq(
    const char* _1, const char* _2,
    const ValueStoreChangeList& expected,
    ValueStore::WriteResult actual_result) {
  if (actual_result->HasError()) {
    return testing::AssertionFailure() <<
        "Result has error: " << actual_result->error().message;
  }

  const ValueStoreChangeList& actual = actual_result->changes();
  if (expected.size() != actual.size()) {
    return testing::AssertionFailure() <<
        "Actual has wrong size, expecting " << expected.size() <<
        " but was " << actual.size();
  }

  std::map<std::string, linked_ptr<ValueStoreChange> > expected_as_map;
  for (ValueStoreChangeList::const_iterator it = expected.begin();
      it != expected.end(); ++it) {
    expected_as_map[it->key()] =
        linked_ptr<ValueStoreChange>(new ValueStoreChange(*it));
  }

  std::set<std::string> keys_seen;

  for (ValueStoreChangeList::const_iterator it = actual.begin();
      it != actual.end(); ++it) {
    if (keys_seen.count(it->key())) {
      return testing::AssertionFailure() <<
          "Multiple changes seen for key: " << it->key();
    }
    keys_seen.insert(it->key());

    if (!expected_as_map.count(it->key())) {
      return testing::AssertionFailure() <<
          "Actual has unexpected change for key: " << it->key();
    }

    ValueStoreChange expected_change = *expected_as_map[it->key()];
    std::string error;
    if (!ValuesEqual(expected_change.new_value(), it->new_value(), &error)) {
      return testing::AssertionFailure() <<
          "New value for " << it->key() << " was unexpected: " << error;
    }
    if (!ValuesEqual(expected_change.old_value(), it->old_value(), &error)) {
      return testing::AssertionFailure() <<
          "Old value for " << it->key() << " was unexpected: " << error;
    }
  }

  return testing::AssertionSuccess();
}

ValueStoreTest::ValueStoreTest()
    : key1_("foo"),
      key2_("bar"),
      key3_("baz"),
      empty_dict_(new base::DictionaryValue()),
      dict1_(new base::DictionaryValue()),
      dict3_(new base::DictionaryValue()),
      dict12_(new base::DictionaryValue()),
      dict123_(new base::DictionaryValue()),
      ui_thread_(BrowserThread::UI, base::MessageLoop::current()),
      file_thread_(BrowserThread::FILE, base::MessageLoop::current()) {
  val1_.reset(base::Value::CreateStringValue(key1_ + "Value"));
  val2_.reset(base::Value::CreateStringValue(key2_ + "Value"));
  val3_.reset(base::Value::CreateStringValue(key3_ + "Value"));

  list1_.push_back(key1_);
  list2_.push_back(key2_);
  list3_.push_back(key3_);
  list12_.push_back(key1_);
  list12_.push_back(key2_);
  list13_.push_back(key1_);
  list13_.push_back(key3_);
  list123_.push_back(key1_);
  list123_.push_back(key2_);
  list123_.push_back(key3_);

  set1_.insert(list1_.begin(), list1_.end());
  set2_.insert(list2_.begin(), list2_.end());
  set3_.insert(list3_.begin(), list3_.end());
  set12_.insert(list12_.begin(), list12_.end());
  set13_.insert(list13_.begin(), list13_.end());
  set123_.insert(list123_.begin(), list123_.end());

  dict1_->Set(key1_, val1_->DeepCopy());
  dict3_->Set(key3_, val3_->DeepCopy());
  dict12_->Set(key1_, val1_->DeepCopy());
  dict12_->Set(key2_, val2_->DeepCopy());
  dict123_->Set(key1_, val1_->DeepCopy());
  dict123_->Set(key2_, val2_->DeepCopy());
  dict123_->Set(key3_, val3_->DeepCopy());
}

ValueStoreTest::~ValueStoreTest() {}

void ValueStoreTest::SetUp() {
  ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
  storage_.reset((GetParam())(temp_dir_.path().AppendASCII("dbName")));
  ASSERT_TRUE(storage_.get());
}

void ValueStoreTest::TearDown() {
  storage_.reset();
}

TEST_P(ValueStoreTest, GetWhenEmpty) {
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get());
}

TEST_P(ValueStoreTest, GetWithSingleValue) {
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, NULL, val1_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq,
        changes, storage_->Set(DEFAULTS, key1_, *val1_));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key2_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key3_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get());
}

TEST_P(ValueStoreTest, GetWithMultipleValues) {
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, NULL, val1_->DeepCopy()));
    changes.push_back(ValueStoreChange(key2_, NULL, val2_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Set(DEFAULTS, *dict12_));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key3_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get());
}

TEST_P(ValueStoreTest, RemoveWhenEmpty) {
  EXPECT_PRED_FORMAT2(ChangesEq, ValueStoreChangeList(),
                      storage_->Remove(key1_));

  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get());
}

TEST_P(ValueStoreTest, RemoveWithSingleValue) {
  storage_->Set(DEFAULTS, *dict1_);
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, val1_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(key1_));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key2_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list12_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get());
}

TEST_P(ValueStoreTest, RemoveWithMultipleValues) {
  storage_->Set(DEFAULTS, *dict123_);
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key3_, val3_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(key3_));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key3_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(list1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get(list12_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(list13_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get());

  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, val1_->DeepCopy(), NULL));
    changes.push_back(ValueStoreChange(key2_, val2_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(list12_));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key3_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list12_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list13_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get());
}

TEST_P(ValueStoreTest, SetWhenOverwriting) {
  storage_->Set(DEFAULTS, key1_, *val2_);
  {
    ValueStoreChangeList changes;
    changes.push_back(
        ValueStoreChange(key1_, val2_->DeepCopy(), val1_->DeepCopy()));
    changes.push_back(ValueStoreChange(key2_, NULL, val2_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Set(DEFAULTS, *dict12_));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key3_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(list1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get(list12_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict1_, storage_->Get(list13_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *dict12_, storage_->Get());
}

TEST_P(ValueStoreTest, ClearWhenEmpty) {
  EXPECT_PRED_FORMAT2(ChangesEq, ValueStoreChangeList(), storage_->Clear());

  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get());
}

TEST_P(ValueStoreTest, ClearWhenNotEmpty) {
  storage_->Set(DEFAULTS, *dict12_);
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, val1_->DeepCopy(), NULL));
    changes.push_back(ValueStoreChange(key2_, val2_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Clear());
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(key1_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(empty_list_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(list123_));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get());
}

// Dots should be allowed in key names; they shouldn't be interpreted as
// indexing into a dictionary.
TEST_P(ValueStoreTest, DotsInKeyNames) {
  std::string dot_key("foo.bar");
  base::StringValue dot_value("baz.qux");
  std::vector<std::string> dot_list;
  dot_list.push_back(dot_key);
  base::DictionaryValue dot_dict;
  dot_dict.SetWithoutPathExpansion(dot_key, dot_value.DeepCopy());

  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(dot_key));

  {
    ValueStoreChangeList changes;
    changes.push_back(
        ValueStoreChange(dot_key, NULL, dot_value.DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq,
        changes, storage_->Set(DEFAULTS, dot_key, dot_value));
  }
  EXPECT_PRED_FORMAT2(ChangesEq,
      ValueStoreChangeList(), storage_->Set(DEFAULTS, dot_key, dot_value));

  EXPECT_PRED_FORMAT2(SettingsEq, dot_dict, storage_->Get(dot_key));

  {
    ValueStoreChangeList changes;
    changes.push_back(
        ValueStoreChange(dot_key, dot_value.DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(dot_key));
  }
  EXPECT_PRED_FORMAT2(ChangesEq,
      ValueStoreChangeList(), storage_->Remove(dot_key));
  {
    ValueStoreChangeList changes;
    changes.push_back(
        ValueStoreChange(dot_key, NULL, dot_value.DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Set(DEFAULTS, dot_dict));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, dot_dict, storage_->Get(dot_list));
  EXPECT_PRED_FORMAT2(SettingsEq, dot_dict, storage_->Get());

  {
    ValueStoreChangeList changes;
    changes.push_back(
        ValueStoreChange(dot_key, dot_value.DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(dot_list));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get(dot_key));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get());
}

TEST_P(ValueStoreTest, DotsInKeyNamesWithDicts) {
  base::DictionaryValue outer_dict;
  base::DictionaryValue* inner_dict = new base::DictionaryValue();
  outer_dict.Set("foo", inner_dict);
  inner_dict->SetString("bar", "baz");

  {
    ValueStoreChangeList changes;
    changes.push_back(
        ValueStoreChange("foo", NULL, inner_dict->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq,
        changes, storage_->Set(DEFAULTS, outer_dict));
  }

  EXPECT_PRED_FORMAT2(SettingsEq, outer_dict, storage_->Get("foo"));
  EXPECT_PRED_FORMAT2(SettingsEq, *empty_dict_, storage_->Get("foo.bar"));
}

TEST_P(ValueStoreTest, ComplexChangedKeysScenarios) {
  // Test:
  //   - Setting over missing/changed/same keys, combinations.
  //   - Removing over missing and present keys, combinations.
  //   - Clearing.
  std::vector<std::string> complex_list;
  base::DictionaryValue complex_changed_dict;

  storage_->Set(DEFAULTS, key1_, *val1_);
  EXPECT_PRED_FORMAT2(ChangesEq,
      ValueStoreChangeList(), storage_->Set(DEFAULTS, key1_, *val1_));
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(
        key1_, val1_->DeepCopy(), val2_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq,
        changes, storage_->Set(DEFAULTS, key1_, *val2_));
  }
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, val2_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(key1_));
    EXPECT_PRED_FORMAT2(ChangesEq,
        ValueStoreChangeList(), storage_->Remove(key1_));
  }
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, NULL, val1_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq,
        changes, storage_->Set(DEFAULTS, key1_, *val1_));
  }
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, val1_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Clear());
    EXPECT_PRED_FORMAT2(ChangesEq, ValueStoreChangeList(), storage_->Clear());
  }

  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, NULL, val1_->DeepCopy()));
    changes.push_back(ValueStoreChange(key2_, NULL, val2_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Set(DEFAULTS, *dict12_));
    EXPECT_PRED_FORMAT2(ChangesEq,
        ValueStoreChangeList(), storage_->Set(DEFAULTS, *dict12_));
  }
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key3_, NULL, val3_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Set(DEFAULTS, *dict123_));
  }
  {
    base::DictionaryValue to_set;
    to_set.Set(key1_, val2_->DeepCopy());
    to_set.Set(key2_, val2_->DeepCopy());
    to_set.Set("asdf", val1_->DeepCopy());
    to_set.Set("qwerty", val3_->DeepCopy());

    ValueStoreChangeList changes;
    changes.push_back(
        ValueStoreChange(key1_, val1_->DeepCopy(), val2_->DeepCopy()));
    changes.push_back(ValueStoreChange("asdf", NULL, val1_->DeepCopy()));
    changes.push_back(
        ValueStoreChange("qwerty", NULL, val3_->DeepCopy()));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Set(DEFAULTS, to_set));
  }
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key1_, val2_->DeepCopy(), NULL));
    changes.push_back(ValueStoreChange(key2_, val2_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(list12_));
  }
  {
    std::vector<std::string> to_remove;
    to_remove.push_back(key1_);
    to_remove.push_back("asdf");

    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange("asdf", val1_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Remove(to_remove));
  }
  {
    ValueStoreChangeList changes;
    changes.push_back(ValueStoreChange(key3_, val3_->DeepCopy(), NULL));
    changes.push_back(
        ValueStoreChange("qwerty", val3_->DeepCopy(), NULL));
    EXPECT_PRED_FORMAT2(ChangesEq, changes, storage_->Clear());
    EXPECT_PRED_FORMAT2(ChangesEq, ValueStoreChangeList(), storage_->Clear());
  }
}

/* [<][>][^][v][top][bottom][index][help] */