1// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <brillo/key_value_store.h>
6
7#include <map>
8#include <string>
9#include <vector>
10
11#include <base/files/file_util.h>
12#include <base/files/scoped_temp_dir.h>
13#include <base/logging.h>
14#include <base/strings/string_util.h>
15#include <brillo/map_utils.h>
16#include <gtest/gtest.h>
17
18using base::FilePath;
19using base::ReadFileToString;
20using std::map;
21using std::string;
22using std::vector;
23
24namespace brillo {
25
26class KeyValueStoreTest : public ::testing::Test {
27 protected:
28  // Returns the value from |store_| corresponding to |key|, or an empty string
29  // if the key is not present. Crashes if the store returns an empty value.
30  string GetNonemptyStringValue(const string& key) {
31    string value;
32    if (store_.GetString(key, &value))
33      CHECK(!value.empty());
34    return value;
35  }
36
37  KeyValueStore store_;  // KeyValueStore under test.
38};
39
40TEST_F(KeyValueStoreTest, LoadAndSaveFromFile) {
41  base::ScopedTempDir temp_dir_;
42  CHECK(temp_dir_.CreateUniqueTempDir());
43  base::FilePath temp_file_ = temp_dir_.path().Append("temp.conf");
44  base::FilePath saved_temp_file_ = temp_dir_.path().Append("saved_temp.conf");
45
46  string blob = "A=B\n# Comment\n";
47  ASSERT_EQ(blob.size(), base::WriteFile(temp_file_, blob.data(), blob.size()));
48  ASSERT_TRUE(store_.Load(temp_file_));
49
50  string value;
51  EXPECT_TRUE(store_.GetString("A", &value));
52  EXPECT_EQ("B", value);
53
54  ASSERT_TRUE(store_.Save(saved_temp_file_));
55  string read_blob;
56  ASSERT_TRUE(ReadFileToString(FilePath(saved_temp_file_), &read_blob));
57  EXPECT_EQ("A=B\n", read_blob);
58}
59
60TEST_F(KeyValueStoreTest, CommentsAreIgnored) {
61  EXPECT_TRUE(store_.LoadFromString(
62      "# comment\nA=B\n\n\n#another=comment\n  # leading spaces\n"));
63  EXPECT_EQ("A=B\n", store_.SaveToString());
64}
65
66TEST_F(KeyValueStoreTest, EmptyTest) {
67  EXPECT_TRUE(store_.LoadFromString(""));
68  EXPECT_EQ("", store_.SaveToString());
69}
70
71TEST_F(KeyValueStoreTest, LoadAndReloadTest) {
72  EXPECT_TRUE(store_.LoadFromString(
73      "A=B\nC=\nFOO=BAR=BAZ\nBAR=BAX\nMISSING=NEWLINE"));
74
75  map<string, string> expected = {{"A", "B"},
76                                  {"C", ""},
77                                  {"FOO", "BAR=BAZ"},
78                                  {"BAR", "BAX"},
79                                  {"MISSING", "NEWLINE"}};
80
81  // Test expected values.
82  string value;
83  for (const auto& it : expected) {
84    EXPECT_TRUE(store_.GetString(it.first, &value));
85    EXPECT_EQ(it.second, value) << "Testing key: " << it.first;
86  }
87
88  // Save, load and test again.
89  KeyValueStore new_store;
90  ASSERT_TRUE(new_store.LoadFromString(store_.SaveToString()));
91
92  for (const auto& it : expected) {
93    EXPECT_TRUE(new_store.GetString(it.first, &value)) << "key: " << it.first;
94    EXPECT_EQ(it.second, value) << "key: " << it.first;
95  }
96}
97
98TEST_F(KeyValueStoreTest, SimpleBooleanTest) {
99  bool result;
100  EXPECT_FALSE(store_.GetBoolean("A", &result));
101
102  store_.SetBoolean("A", true);
103  EXPECT_TRUE(store_.GetBoolean("A", &result));
104  EXPECT_TRUE(result);
105
106  store_.SetBoolean("A", false);
107  EXPECT_TRUE(store_.GetBoolean("A", &result));
108  EXPECT_FALSE(result);
109}
110
111TEST_F(KeyValueStoreTest, BooleanParsingTest) {
112  string blob = "TRUE=true\nfalse=false\nvar=false\nDONT_SHOUT=TRUE\n";
113  EXPECT_TRUE(store_.LoadFromString(blob));
114
115  map<string, bool> expected = {
116      {"TRUE", true}, {"false", false}, {"var", false}};
117  bool value;
118  EXPECT_FALSE(store_.GetBoolean("DONT_SHOUT", &value));
119  string str_value;
120  EXPECT_TRUE(store_.GetString("DONT_SHOUT", &str_value));
121
122  // Test expected values.
123  for (const auto& it : expected) {
124    EXPECT_TRUE(store_.GetBoolean(it.first, &value)) << "key: " << it.first;
125    EXPECT_EQ(it.second, value) << "key: " << it.first;
126  }
127}
128
129TEST_F(KeyValueStoreTest, TrimWhitespaceAroundKey) {
130  EXPECT_TRUE(store_.LoadFromString("  a=1\nb  =2\n c =3\n"));
131
132  EXPECT_EQ("1", GetNonemptyStringValue("a"));
133  EXPECT_EQ("2", GetNonemptyStringValue("b"));
134  EXPECT_EQ("3", GetNonemptyStringValue("c"));
135
136  // Keys should also be trimmed when setting new values.
137  store_.SetString(" foo ", "4");
138  EXPECT_EQ("4", GetNonemptyStringValue("foo"));
139
140  store_.SetBoolean(" bar ", true);
141  bool value = false;
142  ASSERT_TRUE(store_.GetBoolean("bar", &value));
143  EXPECT_TRUE(value);
144}
145
146TEST_F(KeyValueStoreTest, IgnoreWhitespaceLine) {
147  EXPECT_TRUE(store_.LoadFromString("a=1\n \t \nb=2"));
148
149  EXPECT_EQ("1", GetNonemptyStringValue("a"));
150  EXPECT_EQ("2", GetNonemptyStringValue("b"));
151}
152
153TEST_F(KeyValueStoreTest, RejectEmptyKeys) {
154  EXPECT_FALSE(store_.LoadFromString("=1"));
155  EXPECT_FALSE(store_.LoadFromString(" =2"));
156
157  // Trying to set an empty (after trimming) key should fail an assert.
158  EXPECT_DEATH(store_.SetString(" ", "3"), "");
159  EXPECT_DEATH(store_.SetBoolean(" ", "4"), "");
160}
161
162TEST_F(KeyValueStoreTest, RejectBogusLines) {
163  EXPECT_FALSE(store_.LoadFromString("a=1\nbogus\nb=2"));
164}
165
166TEST_F(KeyValueStoreTest, MultilineValue) {
167  EXPECT_TRUE(store_.LoadFromString("a=foo\nb=bar\\\n  baz \\ \nc=3\n"));
168
169  EXPECT_EQ("foo", GetNonemptyStringValue("a"));
170  EXPECT_EQ("bar  baz \\ ", GetNonemptyStringValue("b"));
171  EXPECT_EQ("3", GetNonemptyStringValue("c"));
172}
173
174TEST_F(KeyValueStoreTest, UnterminatedMultilineValue) {
175  EXPECT_FALSE(store_.LoadFromString("a=foo\\"));
176  EXPECT_FALSE(store_.LoadFromString("a=foo\\\n"));
177  EXPECT_FALSE(store_.LoadFromString("a=foo\\\n\n# blah\n"));
178}
179
180TEST_F(KeyValueStoreTest, GetKeys) {
181  map<string, string> entries = {
182    {"1", "apple"}, {"2", "banana"}, {"3", "cherry"}
183  };
184  for (const auto& it : entries) {
185    store_.SetString(it.first, it.second);
186  }
187
188  vector<string> keys = GetMapKeysAsVector(entries);
189  EXPECT_EQ(keys, store_.GetKeys());
190}
191
192}  // namespace brillo
193