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.
5 #include "extensions/browser/value_store/value_store_unittest.h"
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"
21 ValueStore
* Param(const base::FilePath
& file_path
) {
22 return new LeveldbValueStore(file_path
);
27 INSTANTIATE_TEST_CASE_P(
30 testing::Values(&Param
));
32 class LeveldbValueStoreUnitTest
: public testing::Test
{
34 LeveldbValueStoreUnitTest() {}
35 ~LeveldbValueStoreUnitTest() override
{}
38 void SetUp() override
{
39 ASSERT_TRUE(database_dir_
.CreateUniqueTempDir());
41 ASSERT_FALSE(store_
->Get()->HasError());
44 void TearDown() override
{
49 void CloseStore() { store_
.reset(); }
51 void OpenStore() { store_
.reset(new LeveldbValueStore(database_path())); }
53 LeveldbValueStore
* store() { return store_
.get(); }
54 const base::FilePath
& database_path() { return database_dir_
.path(); }
57 scoped_ptr
<LeveldbValueStore
> store_
;
58 base::ScopedTempDir database_dir_
;
60 content::TestBrowserThreadBundle thread_bundle_
;
63 // Check that we can restore a single corrupted key in the LeveldbValueStore.
64 TEST_F(LeveldbValueStoreUnitTest
, RestoreKeyTest
) {
65 const char kNotCorruptKey
[] = "not-corrupt";
66 const char kValue
[] = "value";
68 // Insert a valid pair.
69 scoped_ptr
<base::Value
> value(new base::StringValue(kValue
));
71 store()->Set(ValueStore::DEFAULTS
, kNotCorruptKey
, *value
)->HasError());
73 // Insert a corrupt pair.
74 const char kCorruptKey
[] = "corrupt";
75 leveldb::WriteBatch batch
;
76 batch
.Put(kCorruptKey
, "[{(.*+\"\'\\");
77 ASSERT_TRUE(store()->WriteToDbForTest(&batch
));
80 ValueStore::ReadResult result
= store()->Get(kCorruptKey
);
81 ASSERT_TRUE(result
->HasError());
82 ASSERT_EQ(ValueStore::CORRUPTION
, result
->error().code
);
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());
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
);
99 // Test that the Restore() method does not just delete the entire database
100 // (unless absolutely necessary), and instead only removes corrupted keys.
101 TEST_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
[] = "[{(.*+\"\'\\";
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
) {
113 ->Set(ValueStore::DEFAULTS
, kNotCorruptKeys
[i
], *value
)
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
));
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
);
128 ASSERT_TRUE(store()->Restore());
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
]));
137 result
->settings().GetString(kNotCorruptKeys
[i
], &value_string
));
138 EXPECT_EQ(kValue
, value_string
);
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
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.
148 TEST_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";
154 // Generate a database.
155 scoped_ptr
<base::Value
> value(new base::StringValue(kValue
));
156 for (size_t i
= 0; i
< kNotCorruptKeysSize
; ++i
) {
158 ->Set(ValueStore::DEFAULTS
, kNotCorruptKeys
[i
], *value
)
162 // Close it (so we remove the lock), and replace all files with LolCats.
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()),
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
);
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());