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