blob: b0818d977b2ec21292085021ac1d0ad8797e94e5 [file] [log] [blame]
[email protected]e7d25152013-05-20 21:39:311// Copyright (c) 2013 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
dskiba542a7c82017-06-29 21:58:115#include <set>
6#include <vector>
7
dskiba947f6972017-07-06 03:35:568#include "base/bind.h"
[email protected]afa0aad2014-02-19 21:09:019#include "base/files/file.h"
[email protected]27bd7f72013-10-16 18:18:0610#include "base/files/file_enumerator.h"
[email protected]4b50bd12013-06-07 01:36:3111#include "base/files/file_path.h"
thestig0c8a9bb2014-09-22 19:06:2112#include "base/files/file_util.h"
[email protected]4b50bd12013-06-07 01:36:3113#include "base/files/scoped_temp_dir.h"
cmumfordc38dbd42015-06-18 01:25:1614#include "base/lazy_instance.h"
dskiba542a7c82017-06-29 21:58:1115#include "base/macros.h"
16#include "base/memory/ptr_util.h"
Christian Dullweber3cc579a22018-09-11 10:54:4417#include "base/test/scoped_feature_list.h"
Chris Mumford8d26d10a2018-04-20 17:07:5818#include "base/test/scoped_task_environment.h"
[email protected]e7d25152013-05-20 21:39:3119#include "base/test/test_suite.h"
Lalit Magantifdc0a262017-09-21 12:30:1020#include "base/trace_event/process_memory_dump.h"
[email protected]e7d25152013-05-20 21:39:3121#include "testing/gtest/include/gtest/gtest.h"
cmumfordc38dbd42015-06-18 01:25:1622#include "third_party/leveldatabase/env_chromium.h"
Chris Mumfordc38afb62017-10-09 17:55:0823#include "third_party/leveldatabase/leveldb_chrome.h"
Christian Dullweber3cc579a22018-09-11 10:54:4424#include "third_party/leveldatabase/leveldb_features.h"
Siddhartha7c9ac4a2017-10-18 03:35:5825#include "third_party/leveldatabase/src/include/leveldb/cache.h"
[email protected]27bd7f72013-10-16 18:18:0626#include "third_party/leveldatabase/src/include/leveldb/db.h"
[email protected]e7d25152013-05-20 21:39:3127
[email protected]27bd7f72013-10-16 18:18:0628#define FPL FILE_PATH_LITERAL
29
Lalit Magantifdc0a262017-09-21 12:30:1030using base::trace_event::MemoryDumpArgs;
31using base::trace_event::MemoryDumpLevelOfDetail;
32using base::trace_event::ProcessMemoryDump;
[email protected]6550f7602014-07-25 00:42:3533using leveldb::DB;
34using leveldb::Env;
[email protected]6550f7602014-07-25 00:42:3535using leveldb::ReadOptions;
36using leveldb::Slice;
37using leveldb::Status;
38using leveldb::WritableFile;
39using leveldb::WriteOptions;
cmumfordbf823182014-11-20 16:32:5640using leveldb_env::ChromiumEnv;
dskiba542a7c82017-06-29 21:58:1141using leveldb_env::DBTracker;
[email protected]6550f7602014-07-25 00:42:3542using leveldb_env::MethodID;
Chris Mumfordb401fd82017-08-09 19:14:2543using leveldb_env::Options;
[email protected]6550f7602014-07-25 00:42:3544
Chris Mumford33b762b2017-09-14 19:45:4645namespace leveldb_env {
46
Chris Mumford203cde62017-09-13 19:48:4747static const int kReadOnlyFileLimit = 4;
48
[email protected]e7d25152013-05-20 21:39:3149TEST(ErrorEncoding, OnlyAMethod) {
[email protected]6550f7602014-07-25 00:42:3550 const MethodID in_method = leveldb_env::kSequentialFileRead;
[email protected]e7d25152013-05-20 21:39:3151 const Status s = MakeIOError("Somefile.txt", "message", in_method);
[email protected]f12abfa2013-06-18 10:58:1652 MethodID method;
jsbellab87bce2015-02-20 20:58:5453 base::File::Error error = base::File::FILE_ERROR_MAX;
cmumford5d9f5692014-12-16 00:04:5754 EXPECT_EQ(leveldb_env::METHOD_ONLY, ParseMethodAndError(s, &method, &error));
[email protected]e7d25152013-05-20 21:39:3155 EXPECT_EQ(in_method, method);
jsbellab87bce2015-02-20 20:58:5456 EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
[email protected]e7d25152013-05-20 21:39:3157}
58
[email protected]afa0aad2014-02-19 21:09:0159TEST(ErrorEncoding, FileError) {
[email protected]6550f7602014-07-25 00:42:3560 const MethodID in_method = leveldb_env::kWritableFileClose;
[email protected]afa0aad2014-02-19 21:09:0161 const base::File::Error fe = base::File::FILE_ERROR_INVALID_OPERATION;
62 const Status s = MakeIOError("Somefile.txt", "message", in_method, fe);
[email protected]f12abfa2013-06-18 10:58:1663 MethodID method;
jsbellab87bce2015-02-20 20:58:5464 base::File::Error error;
cmumford58023982015-03-24 20:01:2065 EXPECT_EQ(leveldb_env::METHOD_AND_BFE,
cmumford5d9f5692014-12-16 00:04:5766 ParseMethodAndError(s, &method, &error));
[email protected]e7d25152013-05-20 21:39:3167 EXPECT_EQ(in_method, method);
[email protected]afa0aad2014-02-19 21:09:0168 EXPECT_EQ(fe, error);
[email protected]e7d25152013-05-20 21:39:3169}
70
[email protected]e7d25152013-05-20 21:39:3171TEST(ErrorEncoding, NoEncodedMessage) {
72 Status s = Status::IOError("Some message", "from leveldb itself");
[email protected]6550f7602014-07-25 00:42:3573 MethodID method = leveldb_env::kRandomAccessFileRead;
jsbellab87bce2015-02-20 20:58:5474 base::File::Error error = base::File::FILE_ERROR_MAX;
cmumford5d9f5692014-12-16 00:04:5775 EXPECT_EQ(leveldb_env::NONE, ParseMethodAndError(s, &method, &error));
[email protected]6550f7602014-07-25 00:42:3576 EXPECT_EQ(leveldb_env::kRandomAccessFileRead, method);
jsbellab87bce2015-02-20 20:58:5477 EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
[email protected]e7d25152013-05-20 21:39:3178}
79
[email protected]7c0c90ea2014-01-27 19:34:1980template <typename T>
[email protected]7c0c90ea2014-01-27 19:34:1981class ChromiumEnvMultiPlatformTests : public ::testing::Test {
82 public:
83};
84
cmumfordbf823182014-11-20 16:32:5685typedef ::testing::Types<ChromiumEnv> ChromiumEnvMultiPlatformTestsTypes;
Victor Costanebc52732019-02-15 02:39:4786TYPED_TEST_SUITE(ChromiumEnvMultiPlatformTests,
87 ChromiumEnvMultiPlatformTestsTypes);
[email protected]7c0c90ea2014-01-27 19:34:1988
[email protected]27bd7f72013-10-16 18:18:0689int CountFilesWithExtension(const base::FilePath& dir,
90 const base::FilePath::StringType& extension) {
91 int matching_files = 0;
92 base::FileEnumerator dir_reader(
[email protected]68d00622013-10-18 12:32:2193 dir, false, base::FileEnumerator::FILES);
[email protected]27bd7f72013-10-16 18:18:0694 for (base::FilePath fname = dir_reader.Next(); !fname.empty();
95 fname = dir_reader.Next()) {
96 if (fname.MatchesExtension(extension))
97 matching_files++;
98 }
99 return matching_files;
100}
101
102bool GetFirstLDBFile(const base::FilePath& dir, base::FilePath* ldb_file) {
103 base::FileEnumerator dir_reader(
[email protected]68d00622013-10-18 12:32:21104 dir, false, base::FileEnumerator::FILES);
[email protected]27bd7f72013-10-16 18:18:06105 for (base::FilePath fname = dir_reader.Next(); !fname.empty();
106 fname = dir_reader.Next()) {
107 if (fname.MatchesExtension(FPL(".ldb"))) {
108 *ldb_file = fname;
109 return true;
110 }
111 }
112 return false;
113}
114
cmumfordc006a0ae2016-08-12 16:09:39115TEST(ChromiumEnv, DeleteBackupTables) {
[email protected]27bd7f72013-10-16 18:18:06116 Options options;
117 options.create_if_missing = true;
cmumfordc006a0ae2016-08-12 16:09:39118 options.env = Env::Default();
[email protected]27bd7f72013-10-16 18:18:06119
120 base::ScopedTempDir scoped_temp_dir;
[email protected]79af4342014-03-28 16:12:41121 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
vabra9819822016-09-28 08:44:50122 base::FilePath dir = scoped_temp_dir.GetPath();
[email protected]27bd7f72013-10-16 18:18:06123
124 DB* db;
125 Status status = DB::Open(options, dir.AsUTF8Unsafe(), &db);
126 EXPECT_TRUE(status.ok()) << status.ToString();
127 status = db->Put(WriteOptions(), "key", "value");
128 EXPECT_TRUE(status.ok()) << status.ToString();
129 Slice a = "a";
130 Slice z = "z";
cmumfordc006a0ae2016-08-12 16:09:39131 db->CompactRange(&a, &z); // Ensure manifest written out to table.
[email protected]b1011ef2013-10-22 00:15:26132 delete db;
cmumfordc006a0ae2016-08-12 16:09:39133 db = nullptr;
[email protected]27bd7f72013-10-16 18:18:06134
cmumfordc006a0ae2016-08-12 16:09:39135 // Current ChromiumEnv no longer makes backup tables - verify for sanity.
136 EXPECT_EQ(1, CountFilesWithExtension(dir, FPL(".ldb")));
137 EXPECT_EQ(0, CountFilesWithExtension(dir, FPL(".bak")));
138
139 // Manually create our own backup table to simulate opening db created by
140 // prior release.
141 base::FilePath ldb_path;
142 ASSERT_TRUE(GetFirstLDBFile(dir, &ldb_path));
143 base::FilePath bak_path = ldb_path.ReplaceExtension(FPL(".bak"));
144 ASSERT_TRUE(base::CopyFile(ldb_path, bak_path));
145 EXPECT_EQ(1, CountFilesWithExtension(dir, FPL(".bak")));
146
147 // Now reopen and close then verify the backup file was deleted.
148 status = DB::Open(options, dir.AsUTF8Unsafe(), &db);
[email protected]27bd7f72013-10-16 18:18:06149 EXPECT_TRUE(status.ok()) << status.ToString();
cmumfordc006a0ae2016-08-12 16:09:39150 EXPECT_EQ(0, CountFilesWithExtension(dir, FPL(".bak")));
[email protected]27bd7f72013-10-16 18:18:06151 delete db;
cmumfordc006a0ae2016-08-12 16:09:39152 EXPECT_EQ(1, CountFilesWithExtension(dir, FPL(".ldb")));
153 EXPECT_EQ(0, CountFilesWithExtension(dir, FPL(".bak")));
[email protected]27bd7f72013-10-16 18:18:06154}
155
[email protected]6c6862b2013-10-24 04:42:43156TEST(ChromiumEnv, GetChildrenEmptyDir) {
157 base::ScopedTempDir scoped_temp_dir;
[email protected]79af4342014-03-28 16:12:41158 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
vabra9819822016-09-28 08:44:50159 base::FilePath dir = scoped_temp_dir.GetPath();
[email protected]6c6862b2013-10-24 04:42:43160
cmumfordc38dbd42015-06-18 01:25:16161 Env* env = Env::Default();
[email protected]6c6862b2013-10-24 04:42:43162 std::vector<std::string> result;
163 leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
164 EXPECT_TRUE(status.ok());
[email protected]cd62bf62014-04-10 05:01:15165 EXPECT_EQ(0U, result.size());
[email protected]6c6862b2013-10-24 04:42:43166}
167
[email protected]21d8ff12013-12-04 21:40:55168TEST(ChromiumEnv, GetChildrenPriorResults) {
169 base::ScopedTempDir scoped_temp_dir;
[email protected]79af4342014-03-28 16:12:41170 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
vabra9819822016-09-28 08:44:50171 base::FilePath dir = scoped_temp_dir.GetPath();
[email protected]21d8ff12013-12-04 21:40:55172
173 base::FilePath new_file_dir = dir.Append(FPL("tmp_file"));
[email protected]6e937e42013-12-06 08:25:24174 FILE* f = fopen(new_file_dir.AsUTF8Unsafe().c_str(), "w");
[email protected]21d8ff12013-12-04 21:40:55175 if (f) {
176 fputs("Temp file contents", f);
177 fclose(f);
178 }
179
cmumfordc38dbd42015-06-18 01:25:16180 Env* env = Env::Default();
[email protected]21d8ff12013-12-04 21:40:55181 std::vector<std::string> result;
182 leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
183 EXPECT_TRUE(status.ok());
[email protected]1b87123d2014-04-12 10:37:19184 EXPECT_EQ(1U, result.size());
[email protected]21d8ff12013-12-04 21:40:55185
186 // And a second time should also return one result
187 status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
188 EXPECT_TRUE(status.ok());
[email protected]1b87123d2014-04-12 10:37:19189 EXPECT_EQ(1U, result.size());
[email protected]21d8ff12013-12-04 21:40:55190}
191
cmumfordedb3be62016-08-17 22:34:09192TEST(ChromiumEnv, TestWriteBufferSize) {
193 // If can't get disk size, use leveldb defaults.
194 const int64_t MB = 1024 * 1024;
195 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(-1));
196
197 // A very small disk (check lower clamp value).
198 EXPECT_EQ(size_t(1 * MB), leveldb_env::WriteBufferSize(1 * MB));
199
200 // Some value on the linear equation between min and max.
201 EXPECT_EQ(size_t(2.5 * MB), leveldb_env::WriteBufferSize(25 * MB));
202
203 // The disk size equating to the max buffer size
204 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(40 * MB));
205
206 // Make sure sizes larger than 40MB are clamped to max buffer size.
207 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(80 * MB));
208
209 // Check for very large disk size (catch overflow).
210 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(100 * MB * MB));
211}
212
Kevin Marshallbbec5c22017-08-15 01:06:17213TEST(ChromiumEnv, LockFile) {
214 base::FilePath tmp_file_path;
215 base::CreateTemporaryFile(&tmp_file_path);
216 leveldb::FileLock* lock = nullptr;
217
218 Env* env = Env::Default();
219 EXPECT_TRUE(env->LockFile(tmp_file_path.MaybeAsASCII(), &lock).ok());
220 EXPECT_NE(nullptr, lock);
221
222 leveldb::FileLock* failed_lock = nullptr;
223 EXPECT_FALSE(env->LockFile(tmp_file_path.MaybeAsASCII(), &failed_lock).ok());
224 EXPECT_EQ(nullptr, failed_lock);
225
226 EXPECT_TRUE(env->UnlockFile(lock).ok());
227 EXPECT_TRUE(env->LockFile(tmp_file_path.MaybeAsASCII(), &lock).ok());
228 EXPECT_TRUE(env->UnlockFile(lock).ok());
229}
230
Chris Mumford203cde62017-09-13 19:48:47231TEST(ChromiumEnvTest, TestOpenOnRead) {
232 // Write some test data to a single file that will be opened |n| times.
233 base::FilePath tmp_file_path;
234 ASSERT_TRUE(base::CreateTemporaryFile(&tmp_file_path));
235
236 FILE* f = fopen(tmp_file_path.AsUTF8Unsafe().c_str(), "w");
237 ASSERT_TRUE(f != NULL);
238 const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
239 fputs(kFileData, f);
240 fclose(f);
241
242 std::unique_ptr<ChromiumEnv> env(new ChromiumEnv());
243 env->SetReadOnlyFileLimitForTesting(kReadOnlyFileLimit);
244
245 // Open test file some number greater than kReadOnlyFileLimit to force the
246 // open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
247 const int kNumFiles = kReadOnlyFileLimit + 5;
248 leveldb::RandomAccessFile* files[kNumFiles] = {0};
249 for (int i = 0; i < kNumFiles; i++) {
250 ASSERT_TRUE(
251 env->NewRandomAccessFile(tmp_file_path.AsUTF8Unsafe(), &files[i]).ok());
252 }
253 char scratch;
254 Slice read_result;
255 for (int i = 0; i < kNumFiles; i++) {
256 ASSERT_TRUE(files[i]->Read(i, 1, &read_result, &scratch).ok());
257 ASSERT_EQ(kFileData[i], read_result[0]);
258 }
259 for (int i = 0; i < kNumFiles; i++) {
260 delete files[i];
261 }
262 ASSERT_TRUE(env->DeleteFile(tmp_file_path.AsUTF8Unsafe()).ok());
263}
264
dskiba542a7c82017-06-29 21:58:11265class ChromiumEnvDBTrackerTest : public ::testing::Test {
266 protected:
Chris Mumford8d26d10a2018-04-20 17:07:58267 ChromiumEnvDBTrackerTest()
268 : scoped_task_environment_(
269 base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
dskiba542a7c82017-06-29 21:58:11270 void SetUp() override {
271 testing::Test::SetUp();
272 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
273 }
274
275 const base::FilePath& temp_path() const { return scoped_temp_dir_.GetPath(); }
276
277 using VisitedDBSet = std::set<DBTracker::TrackedDB*>;
278
279 static VisitedDBSet VisitDatabases() {
280 VisitedDBSet visited;
dskiba947f6972017-07-06 03:35:56281 auto db_visitor = [](VisitedDBSet* visited, DBTracker::TrackedDB* db) {
282 ASSERT_TRUE(visited->insert(db).second)
dskiba542a7c82017-06-29 21:58:11283 << "Database " << std::hex << db << " visited for the second time";
284 };
dskiba947f6972017-07-06 03:35:56285 DBTracker::GetInstance()->VisitDatabases(
286 base::BindRepeating(db_visitor, base::Unretained(&visited)));
dskiba542a7c82017-06-29 21:58:11287 return visited;
288 }
289
290 using LiveDBSet = std::vector<std::unique_ptr<DBTracker::TrackedDB>>;
291
292 void AssertEqualSets(const LiveDBSet& live_dbs,
293 const VisitedDBSet& visited_dbs) {
294 for (const auto& live_db : live_dbs) {
295 ASSERT_EQ(1u, visited_dbs.count(live_db.get()))
296 << "Database " << std::hex << live_db.get() << " was not visited";
297 }
298 ASSERT_EQ(live_dbs.size(), visited_dbs.size())
299 << "Extra databases were visited";
300 }
301
302 private:
303 base::ScopedTempDir scoped_temp_dir_;
Chris Mumford8d26d10a2018-04-20 17:07:58304 base::test::ScopedTaskEnvironment scoped_task_environment_;
dskiba542a7c82017-06-29 21:58:11305};
306
307TEST_F(ChromiumEnvDBTrackerTest, OpenDatabase) {
308 struct KeyValue {
309 const char* key;
310 const char* value;
311 };
312 constexpr KeyValue db_data[] = {
313 {"banana", "yellow"}, {"sky", "blue"}, {"enthusiasm", ""},
314 };
315
316 // Open a new database using DBTracker::Open, write some data.
317 Options options;
318 options.create_if_missing = true;
319 std::string name = temp_path().AsUTF8Unsafe();
320 DBTracker::TrackedDB* tracked_db;
321 Status status =
322 DBTracker::GetInstance()->OpenDatabase(options, name, &tracked_db);
323 ASSERT_TRUE(status.ok()) << status.ToString();
324 for (const auto& kv : db_data) {
325 status = tracked_db->Put(WriteOptions(), kv.key, kv.value);
326 ASSERT_TRUE(status.ok()) << status.ToString();
327 }
328
329 // Close the database.
330 delete tracked_db;
331
332 // Open the database again with DB::Open, and check the data.
333 options.create_if_missing = false;
334 leveldb::DB* plain_db = nullptr;
335 status = leveldb::DB::Open(options, name, &plain_db);
336 ASSERT_TRUE(status.ok()) << status.ToString();
337 for (const auto& kv : db_data) {
338 std::string value;
339 status = plain_db->Get(ReadOptions(), kv.key, &value);
340 ASSERT_TRUE(status.ok()) << status.ToString();
341 ASSERT_EQ(value, kv.value);
342 }
343 delete plain_db;
344}
345
346TEST_F(ChromiumEnvDBTrackerTest, TrackedDBInfo) {
347 Options options;
348 options.create_if_missing = true;
349 std::string name = temp_path().AsUTF8Unsafe();
350 DBTracker::TrackedDB* db;
351 Status status = DBTracker::GetInstance()->OpenDatabase(options, name, &db);
352 ASSERT_TRUE(status.ok()) << status.ToString();
353
354 // Check that |db| reports info that was used to open it.
355 ASSERT_EQ(name, db->name());
356
357 delete db;
358}
359
360TEST_F(ChromiumEnvDBTrackerTest, VisitDatabases) {
361 LiveDBSet live_dbs;
362
363 // Open several databases.
364 for (const char* tag : {"poets", "movies", "recipes", "novels"}) {
365 Options options;
366 options.create_if_missing = true;
367 std::string name = temp_path().AppendASCII(tag).AsUTF8Unsafe();
368 DBTracker::TrackedDB* db;
369 Status status = DBTracker::GetInstance()->OpenDatabase(options, name, &db);
370 ASSERT_TRUE(status.ok()) << status.ToString();
371 live_dbs.emplace_back(db);
372 }
373
374 // Check that all live databases are visited.
375 AssertEqualSets(live_dbs, VisitDatabases());
376
377 // Close couple of a databases.
378 live_dbs.erase(live_dbs.begin());
379 live_dbs.erase(live_dbs.begin() + 1);
380
381 // Check that only remaining live databases are visited.
382 AssertEqualSets(live_dbs, VisitDatabases());
383}
384
385TEST_F(ChromiumEnvDBTrackerTest, OpenDBTracking) {
386 Options options;
387 options.create_if_missing = true;
388 std::unique_ptr<leveldb::DB> db;
389 auto status = leveldb_env::OpenDB(options, temp_path().AsUTF8Unsafe(), &db);
390 ASSERT_TRUE(status.ok()) << status.ToString();
391
392 auto visited_dbs = VisitDatabases();
393
394 // Databases returned by OpenDB() should be tracked.
395 ASSERT_EQ(1u, visited_dbs.size());
396 ASSERT_EQ(db.get(), *visited_dbs.begin());
397}
398
Chris Mumford33b762b2017-09-14 19:45:46399TEST_F(ChromiumEnvDBTrackerTest, IsTrackedDB) {
400 leveldb_env::Options options;
401 options.create_if_missing = true;
402 leveldb::DB* untracked_db;
403 base::ScopedTempDir untracked_temp_dir;
404 ASSERT_TRUE(untracked_temp_dir.CreateUniqueTempDir());
405 leveldb::Status s = leveldb::DB::Open(
406 options, untracked_temp_dir.GetPath().AsUTF8Unsafe(), &untracked_db);
407 ASSERT_TRUE(s.ok());
408 EXPECT_FALSE(DBTracker::GetInstance()->IsTrackedDB(untracked_db));
409
410 // Now a tracked db.
411 std::unique_ptr<leveldb::DB> tracked_db;
412 base::ScopedTempDir tracked_temp_dir;
413 ASSERT_TRUE(tracked_temp_dir.CreateUniqueTempDir());
414 s = leveldb_env::OpenDB(options, tracked_temp_dir.GetPath().AsUTF8Unsafe(),
415 &tracked_db);
416 ASSERT_TRUE(s.ok());
417 EXPECT_TRUE(DBTracker::GetInstance()->IsTrackedDB(tracked_db.get()));
418
419 delete untracked_db;
420}
421
Chris Mumford1dc497c02017-10-09 22:03:22422TEST_F(ChromiumEnvDBTrackerTest, CheckMemEnv) {
423 Env* env = leveldb::Env::Default();
424 ASSERT_TRUE(env != nullptr);
425 EXPECT_FALSE(leveldb_chrome::IsMemEnv(env));
426
Chris Mumford8d26d10a2018-04-20 17:07:58427 std::unique_ptr<leveldb::Env> memenv =
428 leveldb_chrome::NewMemEnv("CheckMemEnv", env);
Chris Mumford1dc497c02017-10-09 22:03:22429 EXPECT_TRUE(leveldb_chrome::IsMemEnv(memenv.get()));
Chris Mumfordc38afb62017-10-09 17:55:08430}
431
Siddhartha7c9ac4a2017-10-18 03:35:58432TEST_F(ChromiumEnvDBTrackerTest, MemoryDumpCreation) {
433 Options options;
434 options.create_if_missing = true;
435 leveldb::Cache* web_cache = leveldb_chrome::GetSharedWebBlockCache();
436 leveldb::Cache* browser_cache = leveldb_chrome::GetSharedBrowserBlockCache();
437 options.block_cache = web_cache;
438 std::unique_ptr<leveldb::DB> db1;
439 base::ScopedTempDir temp_dir1;
440 ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
441 base::ScopedTempDir temp_dir2;
442 ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
443 base::ScopedTempDir temp_dir3;
444 ASSERT_TRUE(temp_dir3.CreateUniqueTempDir());
445
446 auto status =
447 leveldb_env::OpenDB(options, temp_dir1.GetPath().AsUTF8Unsafe(), &db1);
448 ASSERT_TRUE(status.ok()) << status.ToString();
449
450 std::unique_ptr<leveldb::DB> db2;
451 status =
452 leveldb_env::OpenDB(options, temp_dir2.GetPath().AsUTF8Unsafe(), &db2);
453 ASSERT_TRUE(status.ok()) << status.ToString();
454
455 std::unique_ptr<leveldb::DB> db3;
456 options.block_cache = browser_cache;
457 status =
458 leveldb_env::OpenDB(options, temp_dir3.GetPath().AsUTF8Unsafe(), &db3);
459 ASSERT_TRUE(status.ok()) << status.ToString();
460
461 auto db_visitor = [](DBTracker::TrackedDB* db) {
462 leveldb::Cache* db_cache =
463 (db->block_cache_type() == DBTracker::SharedReadCacheUse_Browser)
464 ? leveldb_chrome::GetSharedBrowserBlockCache()
465 : leveldb_chrome::GetSharedWebBlockCache();
466 size_t initial_cache_size = db_cache->TotalCharge();
467 auto status = db->Put(WriteOptions(), "key", "value");
468 EXPECT_TRUE(status.ok()) << status.ToString();
469 db->CompactRange(nullptr, nullptr);
470 std::string value;
471 status = db->Get(ReadOptions(), "key", &value);
472 ASSERT_TRUE(status.ok()) << status.ToString();
473 EXPECT_GT(db_cache->TotalCharge(), initial_cache_size);
474 };
475 DBTracker::GetInstance()->VisitDatabases(base::BindRepeating(db_visitor));
476 ASSERT_EQ(browser_cache->TotalCharge() * 2, web_cache->TotalCharge());
477
478 MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::BACKGROUND};
erikchenf62ea042018-05-25 21:30:57479 base::trace_event::ProcessMemoryDump pmd(dump_args);
Siddhartha7c9ac4a2017-10-18 03:35:58480 auto* mad1 = DBTracker::GetOrCreateAllocatorDump(&pmd, db1.get());
481 auto* mad2 = DBTracker::GetOrCreateAllocatorDump(&pmd, db2.get());
482 auto* mad3 = DBTracker::GetOrCreateAllocatorDump(&pmd, db3.get());
483
484 // All databases should have the same size since we made the same changes.
485 size_t db_size = mad1->GetSizeInternal();
486 EXPECT_GT(db_size, 0ul);
487 EXPECT_EQ(db_size, mad2->GetSizeInternal());
488 EXPECT_EQ(db_size, mad3->GetSizeInternal());
489}
490
Chris Mumford8d26d10a2018-04-20 17:07:58491TEST_F(ChromiumEnvDBTrackerTest, MemEnvMemoryDumpCreation) {
492 std::unique_ptr<leveldb::Env> memenv = leveldb_chrome::NewMemEnv("test");
493
494 Status s;
495 WritableFile* writable_file;
496 s = memenv->NewWritableFile("first_file.txt", &writable_file);
497 ASSERT_TRUE(s.ok()) << s.ToString();
498
499 const std::string kValue(2048, 'x');
500 writable_file->Append(Slice(kValue));
501 delete writable_file;
502
503 const MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::BACKGROUND};
erikchenf62ea042018-05-25 21:30:57504 base::trace_event::ProcessMemoryDump dump1(dump_args);
Chris Mumford8d26d10a2018-04-20 17:07:58505 auto* mad = DBTracker::GetOrCreateAllocatorDump(&dump1, memenv.get());
506
507 uint64_t size_with_file = mad->GetSizeInternal();
508 EXPECT_GE(size_with_file, kValue.size());
509
510 // Now rename and size should be unchanged.
511 s = memenv->RenameFile("first_file.txt", "xxxxx_file.txt"); // same length.
512 EXPECT_TRUE(s.ok()) << s.ToString();
erikchenf62ea042018-05-25 21:30:57513 base::trace_event::ProcessMemoryDump dump2(dump_args);
Chris Mumford8d26d10a2018-04-20 17:07:58514 mad = DBTracker::GetOrCreateAllocatorDump(&dump2, memenv.get());
515 EXPECT_EQ(size_with_file, mad->GetSizeInternal());
516
517 // Now delete and size should go down.
518 s = memenv->DeleteFile("xxxxx_file.txt");
519 EXPECT_TRUE(s.ok()) << s.ToString();
520
erikchenf62ea042018-05-25 21:30:57521 base::trace_event::ProcessMemoryDump dump3(dump_args);
Chris Mumford8d26d10a2018-04-20 17:07:58522 mad = DBTracker::GetOrCreateAllocatorDump(&dump3, memenv.get());
523 EXPECT_EQ(mad->GetSizeInternal(), 0ul);
524}
525
Chris Mumfordda3afc3b2018-06-05 22:10:23526TEST(ChromiumLevelDB, PossiblyValidDB) {
527 base::ScopedTempDir scoped_temp_dir;
528 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
529
530 leveldb::Env* default_env = leveldb::Env::Default();
531 const base::FilePath& db_path = scoped_temp_dir.GetPath();
532 EXPECT_FALSE(leveldb_chrome::PossiblyValidDB(db_path, default_env));
533
534 {
535 base::File current(db_path.Append(FILE_PATH_LITERAL("CURRENT")),
536 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
537 ASSERT_TRUE(current.IsValid());
538 const char kString[] = "ManifestFile";
539 EXPECT_EQ(static_cast<int>(sizeof(kString)),
540 current.Write(0, kString, sizeof(kString)));
541 }
542
543 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, default_env));
544
545 ASSERT_TRUE(scoped_temp_dir.Delete());
546 EXPECT_FALSE(leveldb_chrome::PossiblyValidDB(db_path, default_env));
547}
548
549TEST(ChromiumLevelDB, DeleteOnDiskDB) {
550 base::ScopedTempDir scoped_temp_dir;
551 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
552
553 const base::FilePath db_path = scoped_temp_dir.GetPath().AppendASCII("db");
554 leveldb_env::Options on_disk_options;
555 on_disk_options.create_if_missing = true;
556
557 // First with no db directory.
558 EXPECT_FALSE(base::PathExists(db_path));
559 Status s = leveldb_chrome::DeleteDB(db_path, on_disk_options);
560 EXPECT_TRUE(s.ok()) << s.ToString();
561
562 // Now an empty directory.
563 EXPECT_FALSE(base::PathExists(db_path));
564 EXPECT_TRUE(base::CreateDirectory(db_path));
565 s = leveldb_chrome::DeleteDB(db_path, on_disk_options);
566 EXPECT_TRUE(s.ok()) << s.ToString();
567 EXPECT_FALSE(base::PathExists(db_path));
568
569 // Now with a valid leveldb database and an extra file.
570 std::unique_ptr<leveldb::DB> db;
571 s = OpenDB(on_disk_options, db_path.AsUTF8Unsafe(), &db);
572 ASSERT_TRUE(s.ok()) << s.ToString();
573 s = db->Put(WriteOptions(), "TheKey", "TheValue");
574 EXPECT_TRUE(s.ok()) << s.ToString();
575 db.reset();
576
577 base::File test_file(db_path.Append(FILE_PATH_LITERAL("Test file.txt")),
578 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
579 ASSERT_TRUE(test_file.IsValid());
580 const char kString[] = "Just some text.";
581 const int data_len = static_cast<int>(sizeof(kString));
582 EXPECT_EQ(data_len, test_file.Write(0, kString, data_len));
583 test_file.Close();
584
585 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, on_disk_options.env));
586 s = leveldb_chrome::DeleteDB(db_path, on_disk_options);
587 EXPECT_TRUE(s.ok()) << s.ToString();
588
589 EXPECT_FALSE(base::PathExists(db_path));
590}
591
592TEST(ChromiumLevelDB, DeleteInMemoryDB) {
593 base::ScopedTempDir scoped_temp_dir;
594 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
595
596 // First create an on-disk db with an extra file.
597 const base::FilePath db_path = scoped_temp_dir.GetPath().AppendASCII("db");
598 base::FilePath temp_path = db_path.Append(FILE_PATH_LITERAL("Test file.txt"));
599 leveldb_env::Options on_disk_options;
600 on_disk_options.create_if_missing = true;
601
602 {
603 std::unique_ptr<leveldb::DB> db;
604 Status s = OpenDB(on_disk_options, db_path.AsUTF8Unsafe(), &db);
605 ASSERT_TRUE(s.ok()) << s.ToString();
606 s = db->Put(WriteOptions(), "TheKey", "TheValue");
607 EXPECT_TRUE(s.ok()) << s.ToString();
608 db.reset();
609
610 base::File test_file(
611 temp_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
612 ASSERT_TRUE(test_file.IsValid());
613 const char kString[] = "Just some text.";
614 const int data_len = static_cast<int>(sizeof(kString));
615 EXPECT_EQ(data_len, test_file.Write(0, kString, data_len));
616 test_file.Close();
617 }
618
619 // Now create an in-memory db.
620 std::unique_ptr<leveldb::Env> mem_env = leveldb_chrome::NewMemEnv("testing");
621 leveldb_env::Options in_memory_options;
622 in_memory_options.create_if_missing = true;
623 in_memory_options.env = mem_env.get();
624
625 {
626 std::unique_ptr<leveldb::DB> db;
627 // The two DB's purposely use the same path even though the in-memory path
628 // refers to a temp directory on disk.
629 Status s = OpenDB(in_memory_options, db_path.AsUTF8Unsafe(), &db);
630 ASSERT_TRUE(s.ok()) << s.ToString();
631 s = db->Put(WriteOptions(), "TheKey", "TheValue");
632 EXPECT_TRUE(s.ok()) << s.ToString();
633 db.reset();
634
635 leveldb::WritableFile* temp_file;
636 s = mem_env->NewWritableFile(temp_path.AsUTF8Unsafe(), &temp_file);
637 ASSERT_TRUE(s.ok()) << s.ToString();
638 s = temp_file->Append("Just some text.");
639 EXPECT_TRUE(s.ok()) << s.ToString();
640 s = temp_file->Close();
641 EXPECT_TRUE(s.ok()) << s.ToString();
642 delete temp_file;
643 }
644
645 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, on_disk_options.env));
646 EXPECT_TRUE(mem_env->FileExists(temp_path.AsUTF8Unsafe()));
647 Status s = leveldb_chrome::DeleteDB(db_path, in_memory_options);
648 EXPECT_TRUE(s.ok()) << s.ToString();
649 EXPECT_FALSE(mem_env->FileExists(temp_path.AsUTF8Unsafe()));
650 // On disk should be untouched.
651 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, on_disk_options.env));
652}
653
Christian Dullweber3cc579a22018-09-11 10:54:44654class ChromiumLevelDBRebuildTest : public ::testing::Test {
655 protected:
656 ChromiumLevelDBRebuildTest() {
657 feature_list_.InitAndEnableFeature(leveldb::kLevelDBRewriteFeature);
658 }
659
660 void SetUp() override {
661 testing::Test::SetUp();
662 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
663 }
664
665 const base::FilePath& temp_path() const { return scoped_temp_dir_.GetPath(); }
666
667 private:
668 base::test::ScopedFeatureList feature_list_;
669 base::ScopedAllowBlockingForTesting allow_blocking_;
670 base::ScopedTempDir scoped_temp_dir_;
671};
672
673TEST_F(ChromiumLevelDBRebuildTest, RebuildDb) {
674 std::unique_ptr<leveldb::DB> db;
675 base::FilePath db_path = temp_path().AppendASCII("db");
676 leveldb_env::Options options;
677 options.create_if_missing = true;
678
679 auto s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
680 ASSERT_TRUE(s.ok());
681 db->Put(leveldb::WriteOptions(), "key1", "value1");
682 db->Put(leveldb::WriteOptions(), "key2", "value2");
683 db->Delete(leveldb::WriteOptions(), "key1");
684
685 leveldb::DB* old_db_ptr = db.get();
686 s = leveldb_env::RewriteDB(options, db_path.AsUTF8Unsafe(), &db);
687 EXPECT_TRUE(s.ok());
688 EXPECT_NE(old_db_ptr, db.get());
689 EXPECT_TRUE(db);
690
691 std::string value;
692 s = db->Get(leveldb::ReadOptions(), "key1", &value);
693 EXPECT_TRUE(s.IsNotFound());
694 s = db->Get(leveldb::ReadOptions(), "key2", &value);
695 EXPECT_TRUE(s.ok());
696 EXPECT_EQ("value2", value);
697}
698
699TEST_F(ChromiumLevelDBRebuildTest, RecoverMissingDB) {
700 std::unique_ptr<leveldb::DB> db;
701 base::FilePath db_path = temp_path().AppendASCII("db");
702 base::FilePath tmp_path =
703 temp_path().AppendASCII(leveldb_env::DatabaseNameForRewriteDB("db"));
704 leveldb_env::Options options;
705 options.create_if_missing = true;
706
707 // Write a temporary db to simulate a failed rewrite attempt where only the
708 // temporary db exists.
709 auto s = leveldb_env::OpenDB(options, tmp_path.AsUTF8Unsafe(), &db);
710 ASSERT_TRUE(s.ok());
711 db->Put(leveldb::WriteOptions(), "key", "value");
712 db.reset();
713
714 EXPECT_FALSE(base::DirectoryExists(db_path));
715 EXPECT_TRUE(base::DirectoryExists(tmp_path));
716
717 // Open the regular db and check if the temporary one is recovered.
718 s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
719 ASSERT_TRUE(s.ok()) << s.ToString();
720 std::string value;
721 s = db->Get(leveldb::ReadOptions(), "key", &value);
722 EXPECT_TRUE(s.ok()) << s.ToString();
723 EXPECT_EQ("value", value);
724 EXPECT_TRUE(base::DirectoryExists(db_path));
725 EXPECT_FALSE(base::DirectoryExists(tmp_path));
726}
727
728TEST_F(ChromiumLevelDBRebuildTest, RecoverCorruptDB) {
729 std::unique_ptr<leveldb::DB> db;
730 base::FilePath db_path = temp_path().AppendASCII("db");
731 base::FilePath tmp_path =
732 temp_path().AppendASCII(leveldb_env::DatabaseNameForRewriteDB("db"));
733 leveldb_env::Options options;
734 options.create_if_missing = true;
735
736 // Create a corrupt db.
737 ASSERT_TRUE(base::CreateDirectory(db_path));
738 ASSERT_TRUE(leveldb_chrome::CorruptClosedDBForTesting(db_path));
739
740 // Write a temporary db to simulate a failed rewrite attempt.
741 auto s = leveldb_env::OpenDB(options, tmp_path.AsUTF8Unsafe(), &db);
742 ASSERT_TRUE(s.ok());
743 db->Put(leveldb::WriteOptions(), "key", "value");
744 db.reset();
745
746 // Open the regular db and check if the temporary one is recovered.
747 s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
748 ASSERT_TRUE(s.ok()) << s.ToString();
749 std::string value;
750 s = db->Get(leveldb::ReadOptions(), "key", &value);
751 EXPECT_TRUE(s.ok()) << s.ToString();
752 EXPECT_EQ("value", value);
753 EXPECT_TRUE(base::DirectoryExists(db_path));
754 EXPECT_FALSE(base::DirectoryExists(tmp_path));
755}
756
757TEST_F(ChromiumLevelDBRebuildTest, FinishCleanup) {
758 std::unique_ptr<leveldb::DB> db;
759 base::FilePath db_path = temp_path().AppendASCII("db");
760 base::FilePath tmp_path =
761 temp_path().AppendASCII(leveldb_env::DatabaseNameForRewriteDB("db"));
762 leveldb_env::Options options;
763 options.create_if_missing = true;
764
765 // Write a regular and a temporary db to simulate a rewrite attempt that
766 // crashed before finishing.
767 auto s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
768 ASSERT_TRUE(s.ok());
769 db->Put(leveldb::WriteOptions(), "key", "regular");
770 db.reset();
771
772 s = leveldb_env::OpenDB(options, tmp_path.AsUTF8Unsafe(), &db);
773 ASSERT_TRUE(s.ok());
774 db->Put(leveldb::WriteOptions(), "key", "temp");
775 db.reset();
776
777 EXPECT_TRUE(base::DirectoryExists(db_path));
778 EXPECT_TRUE(base::DirectoryExists(tmp_path));
779
780 // Open the regular db and check that the temporary one was cleaned up.
781 s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
782 ASSERT_TRUE(s.ok()) << s.ToString();
783 std::string value;
784 s = db->Get(leveldb::ReadOptions(), "key", &value);
785 EXPECT_TRUE(s.ok()) << s.ToString();
786 EXPECT_EQ("regular", value);
787 EXPECT_TRUE(base::DirectoryExists(db_path));
788 EXPECT_FALSE(base::DirectoryExists(tmp_path));
789}
790
Chris Mumford33b762b2017-09-14 19:45:46791} // namespace leveldb_env
792
[email protected]e7d25152013-05-20 21:39:31793int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); }