blob: 702953d8f98a13bfd9eebde0bd35b93da13f2d6d [file] [log] [blame]
// 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 <chromeos/key_value_store.h>
#include <map>
#include <string>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
#include <gtest/gtest.h>
using base::FilePath;
using base::ReadFileToString;
using std::map;
using std::string;
namespace chromeos {
class KeyValueStoreTest : public ::testing::Test {
public:
void SetUp() override {
CHECK(temp_dir_.CreateUniqueTempDir());
temp_file_ = temp_dir_.path().Append("temp.conf");
}
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;
}
base::FilePath temp_file_;
base::ScopedTempDir temp_dir_;
KeyValueStore store_; // KeyValueStore under test.
};
TEST_F(KeyValueStoreTest, CommentsAreIgnored) {
string blob = "# comment\nA=B\n\n\n#another=comment\n # leading spaces\n";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
ASSERT_TRUE(store_.Load(temp_file_));
ASSERT_TRUE(store_.Save(temp_file_));
string read_blob;
ASSERT_TRUE(ReadFileToString(FilePath(temp_file_), &read_blob));
EXPECT_EQ("A=B\n", read_blob);
}
TEST_F(KeyValueStoreTest, EmptyTest) {
ASSERT_EQ(0, base::WriteFile(temp_file_, "", 0));
ASSERT_TRUE(store_.Load(temp_file_));
ASSERT_TRUE(store_.Save(temp_file_));
string read_blob;
ASSERT_TRUE(ReadFileToString(FilePath(temp_file_), &read_blob));
EXPECT_EQ("", read_blob);
}
TEST_F(KeyValueStoreTest, LoadAndReloadTest) {
string blob = "A=B\nC=\nFOO=BAR=BAZ\nBAR=BAX\nMISSING=NEWLINE";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
ASSERT_TRUE(store_.Load(temp_file_));
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.
ASSERT_TRUE(store_.Save(temp_file_));
KeyValueStore new_store;
ASSERT_TRUE(new_store.Load(temp_file_));
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, 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";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
ASSERT_TRUE(store_.Load(temp_file_));
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) {
string blob = " a=1\nb =2\n c =3\n";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
ASSERT_TRUE(store_.Load(temp_file_));
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) {
string blob = "a=1\n \t \nb=2";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
ASSERT_TRUE(store_.Load(temp_file_));
EXPECT_EQ("1", GetNonemptyStringValue("a"));
EXPECT_EQ("2", GetNonemptyStringValue("b"));
}
TEST_F(KeyValueStoreTest, RejectEmptyKeys) {
string blob = "=1";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
EXPECT_FALSE(store_.Load(temp_file_));
blob = " =2";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
EXPECT_FALSE(store_.Load(temp_file_));
// 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) {
string blob = "a=1\nbogus\nb=2";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
EXPECT_FALSE(store_.Load(temp_file_));
}
TEST_F(KeyValueStoreTest, MultilineValue) {
string blob = "a=foo\nb=bar\\\n baz \\ \nc=3\n";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
ASSERT_TRUE(store_.Load(temp_file_));
EXPECT_EQ("foo", GetNonemptyStringValue("a"));
EXPECT_EQ("bar baz \\ ", GetNonemptyStringValue("b"));
EXPECT_EQ("3", GetNonemptyStringValue("c"));
}
TEST_F(KeyValueStoreTest, UnterminatedMultilineValue) {
string blob = "a=foo\\";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
EXPECT_FALSE(store_.Load(temp_file_));
blob = "a=foo\\\n";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
EXPECT_FALSE(store_.Load(temp_file_));
blob = "a=foo\\\n\n# blah\n";
ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
EXPECT_FALSE(store_.Load(temp_file_));
}
} // namespace chromeos