1// Copyright 2014 The Chromium 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 "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
6
7#include <string>
8
9#include "base/files/scoped_temp_dir.h"
10#include "base/logging.h"
11#include "base/strings/string_number_conversions.h"
12#include "testing/gtest/include/gtest/gtest.h"
13#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
14#include "third_party/leveldatabase/src/include/leveldb/db.h"
15#include "third_party/leveldatabase/src/include/leveldb/env.h"
16
17namespace sync_file_system {
18namespace drive_backend {
19
20struct TestData {
21  const std::string key;
22  const std::string value;
23};
24
25class LevelDBWrapperTest : public testing::Test {
26 public:
27  virtual ~LevelDBWrapperTest() {}
28
29  virtual void SetUp() OVERRIDE {
30    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
31    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
32    InitializeLevelDB();
33  }
34
35  virtual void TearDown() OVERRIDE {
36    db_.reset();
37    in_memory_env_.reset();
38  }
39
40  void CreateDefaultDatabase() {
41    leveldb::DB* db = db_->GetLevelDB();
42
43    // Expected contents are
44    // {"a": "1", "ab": "0", "bb": "3", "d": "4"}
45    const char* keys[] = {"ab", "a", "d", "bb", "d"};
46    for (size_t i = 0; i < arraysize(keys); ++i) {
47      leveldb::Status status =
48          db->Put(leveldb::WriteOptions(), keys[i], base::Int64ToString(i));
49      ASSERT_TRUE(status.ok());
50    }
51  }
52
53  LevelDBWrapper* GetDB() {
54    return db_.get();
55  }
56
57  void CheckDBContents(const TestData expects[], size_t size) {
58    DCHECK(db_);
59
60    scoped_ptr<LevelDBWrapper::Iterator> itr = db_->NewIterator();
61    itr->SeekToFirst();
62    for (size_t i = 0; i < size; ++i) {
63      ASSERT_TRUE(itr->Valid());
64      EXPECT_EQ(expects[i].key, itr->key().ToString());
65      EXPECT_EQ(expects[i].value, itr->value().ToString());
66      itr->Next();
67    }
68    EXPECT_FALSE(itr->Valid());
69  }
70
71 private:
72  void InitializeLevelDB() {
73    leveldb::DB* db = NULL;
74    leveldb::Options options;
75    options.create_if_missing = true;
76    options.max_open_files = 0;  // Use minimum.
77    options.env = in_memory_env_.get();
78    leveldb::Status status =
79        leveldb::DB::Open(options, database_dir_.path().AsUTF8Unsafe(), &db);
80    ASSERT_TRUE(status.ok());
81
82    db_.reset(new LevelDBWrapper(make_scoped_ptr(db)));
83  }
84
85  base::ScopedTempDir database_dir_;
86  scoped_ptr<leveldb::Env> in_memory_env_;
87  scoped_ptr<LevelDBWrapper> db_;
88};
89
90TEST_F(LevelDBWrapperTest, IteratorTest) {
91  CreateDefaultDatabase();
92  scoped_ptr<LevelDBWrapper::Iterator> itr = GetDB()->NewIterator();
93
94  itr->Seek("a");
95  EXPECT_TRUE(itr->Valid());
96  EXPECT_EQ("a", itr->key().ToString());
97  EXPECT_EQ("1", itr->value().ToString());
98
99  itr->Next();
100  EXPECT_TRUE(itr->Valid());
101  EXPECT_EQ("ab", itr->key().ToString());
102  EXPECT_EQ("0", itr->value().ToString());
103
104  itr->Seek("b");
105  EXPECT_TRUE(itr->Valid());
106  EXPECT_EQ("bb", itr->key().ToString());
107  EXPECT_EQ("3", itr->value().ToString());
108
109  itr->Next();
110  EXPECT_TRUE(itr->Valid());
111  EXPECT_EQ("d", itr->key().ToString());
112  EXPECT_EQ("4", itr->value().ToString());
113
114  itr->Next();
115  EXPECT_FALSE(itr->Valid());
116
117  itr->SeekToFirst();
118  EXPECT_TRUE(itr->Valid());
119  EXPECT_EQ("a", itr->key().ToString());
120  EXPECT_EQ("1", itr->value().ToString());
121
122  itr->Delete();
123  EXPECT_TRUE(itr->Valid());
124  EXPECT_EQ("ab", itr->key().ToString());
125  EXPECT_EQ("0", itr->value().ToString());
126
127  itr->SeekToFirst();
128  EXPECT_TRUE(itr->Valid());
129  EXPECT_EQ("ab", itr->key().ToString());
130  EXPECT_EQ("0", itr->value().ToString());
131
132  EXPECT_EQ(0, GetDB()->num_puts());
133  EXPECT_EQ(1, GetDB()->num_deletes());
134}
135
136TEST_F(LevelDBWrapperTest, Iterator2Test) {
137  GetDB()->Put("a", "1");
138  GetDB()->Put("b", "2");
139  GetDB()->Put("c", "3");
140  // Keep pending transanctions on memory.
141
142  scoped_ptr<LevelDBWrapper::Iterator> itr = GetDB()->NewIterator();
143
144  std::string prev_key;
145  std::string prev_value;
146  int loop_counter = 0;
147  for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
148    ASSERT_NE(prev_key, itr->key().ToString());
149    ASSERT_NE(prev_value, itr->value().ToString());
150    prev_key = itr->key().ToString();
151    prev_value = itr->value().ToString();
152    ++loop_counter;
153  }
154  EXPECT_EQ(3, loop_counter);
155  EXPECT_EQ("c", prev_key);
156  EXPECT_EQ("3", prev_value);
157
158  EXPECT_EQ(3, GetDB()->num_puts());
159  EXPECT_EQ(0, GetDB()->num_deletes());
160}
161
162TEST_F(LevelDBWrapperTest, PutTest) {
163  TestData merged_data[] = {{"a", "1"}, {"aa", "new0"}, {"ab", "0"},
164                            {"bb", "new2"}, {"c", "new1"}, {"d", "4"}};
165  TestData orig_data[] = {{"a", "1"}, {"ab", "0"}, {"bb", "3"}, {"d", "4"}};
166
167  CreateDefaultDatabase();
168
169  // Add pending transactions.
170  GetDB()->Put("aa", "new0");
171  GetDB()->Put("c", "new1");
172  GetDB()->Put("bb", "new2");  // Overwrite an entry.
173
174  SCOPED_TRACE("PutTest_Pending");
175  CheckDBContents(merged_data, arraysize(merged_data));
176
177  EXPECT_EQ(3, GetDB()->num_puts());
178  // Remove all pending transactions.
179  GetDB()->Clear();
180  EXPECT_EQ(0, GetDB()->num_puts());
181
182  SCOPED_TRACE("PutTest_Clear");
183  CheckDBContents(orig_data, arraysize(orig_data));
184
185  // Add pending transactions again, with commiting.
186  GetDB()->Put("aa", "new0");
187  GetDB()->Put("c", "new1");
188  GetDB()->Put("bb", "new2");
189  EXPECT_EQ(3, GetDB()->num_puts());
190  GetDB()->Commit();
191  EXPECT_EQ(0, GetDB()->num_puts());
192  GetDB()->Clear();  // Clear just in case.
193
194  SCOPED_TRACE("PutTest_Commit");
195  CheckDBContents(merged_data, arraysize(merged_data));
196}
197
198TEST_F(LevelDBWrapperTest, DeleteTest) {
199  TestData merged_data[] = {{"a", "1"}, {"aa", "new0"}, {"bb", "new2"},
200                            {"d", "4"}};
201  TestData orig_data[] = {{"a", "1"}, {"ab", "0"}, {"bb", "3"}, {"d", "4"}};
202
203  CreateDefaultDatabase();
204
205  // Add pending transactions.
206  GetDB()->Put("aa", "new0");
207  GetDB()->Put("c", "new1");
208  GetDB()->Put("bb", "new2");  // Overwrite an entry.
209  GetDB()->Delete("c");        // Remove a pending entry.
210  GetDB()->Delete("ab");       // Remove a committed entry.
211
212  EXPECT_EQ(3, GetDB()->num_puts());
213  EXPECT_EQ(2, GetDB()->num_deletes());
214
215  SCOPED_TRACE("DeleteTest_Pending");
216  CheckDBContents(merged_data, arraysize(merged_data));
217
218  // Remove all pending transactions.
219  GetDB()->Clear();
220
221  SCOPED_TRACE("DeleteTest_Clear");
222  CheckDBContents(orig_data, arraysize(orig_data));
223
224  // Add pending transactions again, with commiting.
225  GetDB()->Put("aa", "new0");
226  GetDB()->Put("c", "new1");
227  GetDB()->Put("bb", "new2");
228  GetDB()->Delete("c");
229  GetDB()->Delete("ab");
230  GetDB()->Commit();
231  GetDB()->Clear();
232
233  EXPECT_EQ(0, GetDB()->num_puts());
234  EXPECT_EQ(0, GetDB()->num_deletes());
235
236  SCOPED_TRACE("DeleteTest_Commit");
237  CheckDBContents(merged_data, arraysize(merged_data));
238}
239
240}  // namespace drive_backend
241}  // namespace sync_file_system
242