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 "extensions/browser/value_store/value_store_unittest.h"
6
7#include "base/files/file_enumerator.h"
8#include "base/files/file_util.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/memory/ref_counted.h"
11#include "base/message_loop/message_loop.h"
12#include "base/values.h"
13#include "content/public/test/test_browser_thread_bundle.h"
14#include "extensions/browser/value_store/leveldb_value_store.h"
15#include "testing/gtest/include/gtest/gtest.h"
16#include "third_party/leveldatabase/src/include/leveldb/db.h"
17#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
18
19namespace {
20
21ValueStore* Param(const base::FilePath& file_path) {
22  return new LeveldbValueStore(file_path);
23}
24
25}  // namespace
26
27INSTANTIATE_TEST_CASE_P(
28    LeveldbValueStore,
29    ValueStoreTest,
30    testing::Values(&Param));
31
32class LeveldbValueStoreUnitTest : public testing::Test {
33 public:
34  LeveldbValueStoreUnitTest() {}
35  virtual ~LeveldbValueStoreUnitTest() {}
36
37 protected:
38  virtual void SetUp() OVERRIDE {
39    ASSERT_TRUE(database_dir_.CreateUniqueTempDir());
40    OpenStore();
41    ASSERT_FALSE(store_->Get()->HasError());
42  }
43
44  virtual void TearDown() OVERRIDE {
45    store_->Clear();
46    store_.reset();
47  }
48
49  void CloseStore() { store_.reset(); }
50
51  void OpenStore() { store_.reset(new LeveldbValueStore(database_path())); }
52
53  LeveldbValueStore* store() { return store_.get(); }
54  const base::FilePath& database_path() { return database_dir_.path(); }
55
56 private:
57  scoped_ptr<LeveldbValueStore> store_;
58  base::ScopedTempDir database_dir_;
59
60  content::TestBrowserThreadBundle thread_bundle_;
61};
62
63// Check that we can restore a single corrupted key in the LeveldbValueStore.
64TEST_F(LeveldbValueStoreUnitTest, RestoreKeyTest) {
65  const char kNotCorruptKey[] = "not-corrupt";
66  const char kValue[] = "value";
67
68  // Insert a valid pair.
69  scoped_ptr<base::Value> value(new base::StringValue(kValue));
70  ASSERT_FALSE(
71      store()->Set(ValueStore::DEFAULTS, kNotCorruptKey, *value)->HasError());
72
73  // Insert a corrupt pair.
74  const char kCorruptKey[] = "corrupt";
75  leveldb::WriteBatch batch;
76  batch.Put(kCorruptKey, "[{(.*+\"\'\\");
77  ASSERT_TRUE(store()->WriteToDbForTest(&batch));
78
79  // Verify corruption.
80  ValueStore::ReadResult result = store()->Get(kCorruptKey);
81  ASSERT_TRUE(result->HasError());
82  ASSERT_EQ(ValueStore::CORRUPTION, result->error().code);
83
84  // Restore and verify.
85  ASSERT_TRUE(store()->RestoreKey(kCorruptKey));
86  result = store()->Get(kCorruptKey);
87  EXPECT_FALSE(result->HasError());
88  EXPECT_TRUE(result->settings().empty());
89
90  // Verify that the valid pair is still present.
91  result = store()->Get(kNotCorruptKey);
92  EXPECT_FALSE(result->HasError());
93  EXPECT_TRUE(result->settings().HasKey(kNotCorruptKey));
94  std::string value_string;
95  EXPECT_TRUE(result->settings().GetString(kNotCorruptKey, &value_string));
96  EXPECT_EQ(kValue, value_string);
97}
98
99// Test that the Restore() method does not just delete the entire database
100// (unless absolutely necessary), and instead only removes corrupted keys.
101TEST_F(LeveldbValueStoreUnitTest, RestoreDoesMinimumNecessary) {
102  const char* kNotCorruptKeys[] = {"a", "n", "z"};
103  const size_t kNotCorruptKeysSize = 3u;
104  const char kCorruptKey1[] = "f";
105  const char kCorruptKey2[] = "s";
106  const char kValue[] = "value";
107  const char kCorruptValue[] = "[{(.*+\"\'\\";
108
109  // Insert a collection of non-corrupted pairs.
110  scoped_ptr<base::Value> value(new base::StringValue(kValue));
111  for (size_t i = 0; i < kNotCorruptKeysSize; ++i) {
112    ASSERT_FALSE(store()
113                     ->Set(ValueStore::DEFAULTS, kNotCorruptKeys[i], *value)
114                     ->HasError());
115  }
116
117  // Insert a few corrupted pairs.
118  leveldb::WriteBatch batch;
119  batch.Put(kCorruptKey1, kCorruptValue);
120  batch.Put(kCorruptKey2, kCorruptValue);
121  ASSERT_TRUE(store()->WriteToDbForTest(&batch));
122
123  // Verify that we broke it, and then fix it.
124  ValueStore::ReadResult result = store()->Get();
125  ASSERT_TRUE(result->HasError());
126  ASSERT_EQ(ValueStore::CORRUPTION, result->error().code);
127
128  ASSERT_TRUE(store()->Restore());
129
130  // We should still have all valid pairs present in the database.
131  std::string value_string;
132  for (size_t i = 0; i < kNotCorruptKeysSize; ++i) {
133    result = store()->Get(kNotCorruptKeys[i]);
134    EXPECT_FALSE(result->HasError());
135    EXPECT_TRUE(result->settings().HasKey(kNotCorruptKeys[i]));
136    EXPECT_TRUE(
137        result->settings().GetString(kNotCorruptKeys[i], &value_string));
138    EXPECT_EQ(kValue, value_string);
139  }
140}
141
142// Test that the LeveldbValueStore can recover in the case of a CATastrophic
143// failure and we have total corruption. In this case, the database is plagued
144// by LolCats.
145// Full corruption has been known to happen occasionally in strange edge cases,
146// such as after users use Windows Restore. We can't prevent it, but we need to
147// be able to handle it smoothly.
148TEST_F(LeveldbValueStoreUnitTest, RestoreFullDatabase) {
149  const std::string kLolCats("I can haz leveldb filez?");
150  const char* kNotCorruptKeys[] = {"a", "n", "z"};
151  const size_t kNotCorruptKeysSize = 3u;
152  const char kValue[] = "value";
153
154  // Generate a database.
155  scoped_ptr<base::Value> value(new base::StringValue(kValue));
156  for (size_t i = 0; i < kNotCorruptKeysSize; ++i) {
157    ASSERT_FALSE(store()
158                     ->Set(ValueStore::DEFAULTS, kNotCorruptKeys[i], *value)
159                     ->HasError());
160  }
161
162  // Close it (so we remove the lock), and replace all files with LolCats.
163  CloseStore();
164  base::FileEnumerator enumerator(
165      database_path(), true /* recursive */, base::FileEnumerator::FILES);
166  for (base::FilePath file = enumerator.Next(); !file.empty();
167       file = enumerator.Next()) {
168    // WriteFile() failure is a result of -1.
169    ASSERT_NE(base::WriteFile(file, kLolCats.c_str(), kLolCats.length()),
170              -1);
171  }
172  OpenStore();
173
174  // We should definitely have an error.
175  ValueStore::ReadResult result = store()->Get();
176  ASSERT_TRUE(result->HasError());
177  ASSERT_EQ(ValueStore::CORRUPTION, result->error().code);
178
179  ASSERT_TRUE(store()->Restore());
180  result = store()->Get();
181  EXPECT_FALSE(result->HasError());
182  // We couldn't recover anything, but we should be in a sane state again.
183  EXPECT_EQ(0u, result->settings().size());
184}
185