You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.2 KiB
244 lines
7.2 KiB
// Copyright (c) 2010 The Chromium OS 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 <brillo/key_value_store.h>
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <base/files/file_util.h>
|
|
#include <base/files/scoped_temp_dir.h>
|
|
#include <base/logging.h>
|
|
#include <base/strings/string_util.h>
|
|
#include <brillo/map_utils.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
using base::FilePath;
|
|
using base::ReadFileToString;
|
|
using std::map;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
namespace brillo {
|
|
|
|
class KeyValueStoreTest : public ::testing::Test {
|
|
protected:
|
|
// Returns the value from |store_| corresponding to |key|, or an empty string
|
|
// if the key is not present. Crashes if the store returns an empty value.
|
|
string GetNonemptyStringValue(const string& key) {
|
|
string value;
|
|
if (store_.GetString(key, &value))
|
|
CHECK(!value.empty());
|
|
return value;
|
|
}
|
|
|
|
KeyValueStore store_; // KeyValueStore under test.
|
|
};
|
|
|
|
TEST_F(KeyValueStoreTest, MoveConstructor) {
|
|
store_.SetBoolean("a_boolean", true);
|
|
store_.SetString("a_string", "a_value");
|
|
|
|
KeyValueStore moved_to(std::move(store_));
|
|
bool b_value = false;
|
|
EXPECT_TRUE(moved_to.GetBoolean("a_boolean", &b_value));
|
|
EXPECT_TRUE(b_value);
|
|
|
|
std::string s_value;
|
|
EXPECT_TRUE(moved_to.GetString("a_string", &s_value));
|
|
EXPECT_EQ(s_value, "a_value");
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, MoveAssignmentOperator) {
|
|
store_.SetBoolean("a_boolean", true);
|
|
store_.SetString("a_string", "a_value");
|
|
|
|
KeyValueStore moved_to;
|
|
moved_to = std::move(store_);
|
|
bool b_value = false;
|
|
EXPECT_TRUE(moved_to.GetBoolean("a_boolean", &b_value));
|
|
EXPECT_TRUE(b_value);
|
|
|
|
std::string s_value;
|
|
EXPECT_TRUE(moved_to.GetString("a_string", &s_value));
|
|
EXPECT_EQ(s_value, "a_value");
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, LoadAndSaveFromFile) {
|
|
base::ScopedTempDir temp_dir_;
|
|
CHECK(temp_dir_.CreateUniqueTempDir());
|
|
base::FilePath temp_file_ = temp_dir_.GetPath().Append("temp.conf");
|
|
base::FilePath saved_temp_file_ =
|
|
temp_dir_.GetPath().Append("saved_temp.conf");
|
|
|
|
string blob = "A=B\n# Comment\n";
|
|
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
|
|
ASSERT_TRUE(store_.Load(temp_file_));
|
|
|
|
string value;
|
|
EXPECT_TRUE(store_.GetString("A", &value));
|
|
EXPECT_EQ("B", value);
|
|
|
|
ASSERT_TRUE(store_.Save(saved_temp_file_));
|
|
string read_blob;
|
|
ASSERT_TRUE(ReadFileToString(FilePath(saved_temp_file_), &read_blob));
|
|
EXPECT_EQ("A=B\n", read_blob);
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, CommentsAreIgnored) {
|
|
EXPECT_TRUE(store_.LoadFromString(
|
|
"# comment\nA=B\n\n\n#another=comment\n # leading spaces\n"));
|
|
EXPECT_EQ("A=B\n", store_.SaveToString());
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, EmptyTest) {
|
|
EXPECT_TRUE(store_.LoadFromString(""));
|
|
EXPECT_EQ("", store_.SaveToString());
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, LoadAndReloadTest) {
|
|
EXPECT_TRUE(store_.LoadFromString(
|
|
"A=B\nC=\nFOO=BAR=BAZ\nBAR=BAX\nMISSING=NEWLINE"));
|
|
|
|
map<string, string> expected = {{"A", "B"},
|
|
{"C", ""},
|
|
{"FOO", "BAR=BAZ"},
|
|
{"BAR", "BAX"},
|
|
{"MISSING", "NEWLINE"}};
|
|
|
|
// Test expected values.
|
|
string value;
|
|
for (const auto& it : expected) {
|
|
EXPECT_TRUE(store_.GetString(it.first, &value));
|
|
EXPECT_EQ(it.second, value) << "Testing key: " << it.first;
|
|
}
|
|
|
|
// Save, load and test again.
|
|
KeyValueStore new_store;
|
|
ASSERT_TRUE(new_store.LoadFromString(store_.SaveToString()));
|
|
|
|
for (const auto& it : expected) {
|
|
EXPECT_TRUE(new_store.GetString(it.first, &value)) << "key: " << it.first;
|
|
EXPECT_EQ(it.second, value) << "key: " << it.first;
|
|
}
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, MultipleLoads) {
|
|
// The internal state is not cleared before loading.
|
|
EXPECT_TRUE(store_.LoadFromString("A=B\n"));
|
|
EXPECT_TRUE(store_.LoadFromString("B=C\n"));
|
|
EXPECT_EQ(2, store_.GetKeys().size());
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, PartialLoad) {
|
|
// The 2nd line is broken, but the pair from the first line should be kept.
|
|
EXPECT_FALSE(store_.LoadFromString("A=B\n=\n"));
|
|
EXPECT_EQ(1, store_.GetKeys().size());
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, Clear) {
|
|
EXPECT_TRUE(store_.LoadFromString("A=B\n"));
|
|
EXPECT_EQ(1, store_.GetKeys().size());
|
|
store_.Clear();
|
|
EXPECT_EQ(0, store_.GetKeys().size());
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, SimpleBooleanTest) {
|
|
bool result;
|
|
EXPECT_FALSE(store_.GetBoolean("A", &result));
|
|
|
|
store_.SetBoolean("A", true);
|
|
EXPECT_TRUE(store_.GetBoolean("A", &result));
|
|
EXPECT_TRUE(result);
|
|
|
|
store_.SetBoolean("A", false);
|
|
EXPECT_TRUE(store_.GetBoolean("A", &result));
|
|
EXPECT_FALSE(result);
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, BooleanParsingTest) {
|
|
string blob = "TRUE=true\nfalse=false\nvar=false\nDONT_SHOUT=TRUE\n";
|
|
EXPECT_TRUE(store_.LoadFromString(blob));
|
|
|
|
map<string, bool> expected = {
|
|
{"TRUE", true}, {"false", false}, {"var", false}};
|
|
bool value;
|
|
EXPECT_FALSE(store_.GetBoolean("DONT_SHOUT", &value));
|
|
string str_value;
|
|
EXPECT_TRUE(store_.GetString("DONT_SHOUT", &str_value));
|
|
|
|
// Test expected values.
|
|
for (const auto& it : expected) {
|
|
EXPECT_TRUE(store_.GetBoolean(it.first, &value)) << "key: " << it.first;
|
|
EXPECT_EQ(it.second, value) << "key: " << it.first;
|
|
}
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, TrimWhitespaceAroundKey) {
|
|
EXPECT_TRUE(store_.LoadFromString(" a=1\nb =2\n c =3\n"));
|
|
|
|
EXPECT_EQ("1", GetNonemptyStringValue("a"));
|
|
EXPECT_EQ("2", GetNonemptyStringValue("b"));
|
|
EXPECT_EQ("3", GetNonemptyStringValue("c"));
|
|
|
|
// Keys should also be trimmed when setting new values.
|
|
store_.SetString(" foo ", "4");
|
|
EXPECT_EQ("4", GetNonemptyStringValue("foo"));
|
|
|
|
store_.SetBoolean(" bar ", true);
|
|
bool value = false;
|
|
ASSERT_TRUE(store_.GetBoolean("bar", &value));
|
|
EXPECT_TRUE(value);
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, IgnoreWhitespaceLine) {
|
|
EXPECT_TRUE(store_.LoadFromString("a=1\n \t \nb=2"));
|
|
|
|
EXPECT_EQ("1", GetNonemptyStringValue("a"));
|
|
EXPECT_EQ("2", GetNonemptyStringValue("b"));
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, RejectEmptyKeys) {
|
|
EXPECT_FALSE(store_.LoadFromString("=1"));
|
|
EXPECT_FALSE(store_.LoadFromString(" =2"));
|
|
|
|
// Trying to set an empty (after trimming) key should fail an assert.
|
|
EXPECT_DEATH(store_.SetString(" ", "3"), "");
|
|
EXPECT_DEATH(store_.SetBoolean(" ", "4"), "");
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, RejectBogusLines) {
|
|
EXPECT_FALSE(store_.LoadFromString("a=1\nbogus\nb=2"));
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, MultilineValue) {
|
|
EXPECT_TRUE(store_.LoadFromString("a=foo\nb=bar\\\n baz \\ \nc=3\n"));
|
|
|
|
EXPECT_EQ("foo", GetNonemptyStringValue("a"));
|
|
EXPECT_EQ("bar baz \\ ", GetNonemptyStringValue("b"));
|
|
EXPECT_EQ("3", GetNonemptyStringValue("c"));
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, UnterminatedMultilineValue) {
|
|
EXPECT_FALSE(store_.LoadFromString("a=foo\\"));
|
|
EXPECT_FALSE(store_.LoadFromString("a=foo\\\n"));
|
|
EXPECT_FALSE(store_.LoadFromString("a=foo\\\n\n# blah\n"));
|
|
}
|
|
|
|
TEST_F(KeyValueStoreTest, GetKeys) {
|
|
map<string, string> entries = {
|
|
{"1", "apple"}, {"2", "banana"}, {"3", "cherry"}
|
|
};
|
|
for (const auto& it : entries) {
|
|
store_.SetString(it.first, it.second);
|
|
}
|
|
|
|
vector<string> keys = GetMapKeysAsVector(entries);
|
|
EXPECT_EQ(keys, store_.GetKeys());
|
|
}
|
|
|
|
} // namespace brillo
|