blob: 2bf72895cded5990e98f93c16116ce2af0ffae2c [file] [log] [blame]
Avi Drissman505076bc2022-10-06 21:15:301// Copyright 2013 The Chromium Authors
[email protected]e7d25152013-05-20 21:39:312// 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
[email protected]afa0aad2014-02-19 21:09:018#include "base/files/file.h"
[email protected]27bd7f72013-10-16 18:18:069#include "base/files/file_enumerator.h"
[email protected]4b50bd12013-06-07 01:36:3110#include "base/files/file_path.h"
thestig0c8a9bb2014-09-22 19:06:2111#include "base/files/file_util.h"
[email protected]4b50bd12013-06-07 01:36:3112#include "base/files/scoped_temp_dir.h"
Avi Drissmane44e4562023-01-13 03:55:0813#include "base/functional/bind.h"
cmumfordc38dbd42015-06-18 01:25:1614#include "base/lazy_instance.h"
dskiba542a7c82017-06-29 21:58:1115#include "base/memory/ptr_util.h"
Reilly Grantd6c56532023-04-06 03:13:0316#include "base/test/launcher/unit_test_launcher.h"
Gabriel Charettec7108742019-08-23 03:31:4017#include "base/test/task_environment.h"
[email protected]e7d25152013-05-20 21:39:3118#include "base/test/test_suite.h"
Lalit Magantifdc0a262017-09-21 12:30:1019#include "base/trace_event/process_memory_dump.h"
Chris Mumford6b4373b2019-03-20 20:02:0720#include "build/build_config.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"
Siddhartha7c9ac4a2017-10-18 03:35:5824#include "third_party/leveldatabase/src/include/leveldb/cache.h"
[email protected]27bd7f72013-10-16 18:18:0625#include "third_party/leveldatabase/src/include/leveldb/db.h"
[email protected]e7d25152013-05-20 21:39:3126
[email protected]27bd7f72013-10-16 18:18:0627#define FPL FILE_PATH_LITERAL
28
Lalit Magantifdc0a262017-09-21 12:30:1029using base::trace_event::MemoryDumpArgs;
30using base::trace_event::MemoryDumpLevelOfDetail;
31using base::trace_event::ProcessMemoryDump;
[email protected]6550f7602014-07-25 00:42:3532using leveldb::DB;
33using leveldb::Env;
[email protected]6550f7602014-07-25 00:42:3534using leveldb::ReadOptions;
35using leveldb::Slice;
36using leveldb::Status;
37using leveldb::WritableFile;
38using leveldb::WriteOptions;
cmumfordbf823182014-11-20 16:32:5639using leveldb_env::ChromiumEnv;
dskiba542a7c82017-06-29 21:58:1140using leveldb_env::DBTracker;
[email protected]6550f7602014-07-25 00:42:3541using leveldb_env::MethodID;
Chris Mumfordb401fd82017-08-09 19:14:2542using leveldb_env::Options;
[email protected]6550f7602014-07-25 00:42:3543
Chris Mumford33b762b2017-09-14 19:45:4644namespace leveldb_env {
45
Chris Mumford203cde62017-09-13 19:48:4746static const int kReadOnlyFileLimit = 4;
47
[email protected]e7d25152013-05-20 21:39:3148TEST(ErrorEncoding, OnlyAMethod) {
[email protected]6550f7602014-07-25 00:42:3549 const MethodID in_method = leveldb_env::kSequentialFileRead;
[email protected]e7d25152013-05-20 21:39:3150 const Status s = MakeIOError("Somefile.txt", "message", in_method);
[email protected]f12abfa2013-06-18 10:58:1651 MethodID method;
jsbellab87bce2015-02-20 20:58:5452 base::File::Error error = base::File::FILE_ERROR_MAX;
cmumford5d9f5692014-12-16 00:04:5753 EXPECT_EQ(leveldb_env::METHOD_ONLY, ParseMethodAndError(s, &method, &error));
[email protected]e7d25152013-05-20 21:39:3154 EXPECT_EQ(in_method, method);
jsbellab87bce2015-02-20 20:58:5455 EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
[email protected]e7d25152013-05-20 21:39:3156}
57
[email protected]afa0aad2014-02-19 21:09:0158TEST(ErrorEncoding, FileError) {
[email protected]6550f7602014-07-25 00:42:3559 const MethodID in_method = leveldb_env::kWritableFileClose;
[email protected]afa0aad2014-02-19 21:09:0160 const base::File::Error fe = base::File::FILE_ERROR_INVALID_OPERATION;
61 const Status s = MakeIOError("Somefile.txt", "message", in_method, fe);
[email protected]f12abfa2013-06-18 10:58:1662 MethodID method;
jsbellab87bce2015-02-20 20:58:5463 base::File::Error error;
cmumford58023982015-03-24 20:01:2064 EXPECT_EQ(leveldb_env::METHOD_AND_BFE,
cmumford5d9f5692014-12-16 00:04:5765 ParseMethodAndError(s, &method, &error));
[email protected]e7d25152013-05-20 21:39:3166 EXPECT_EQ(in_method, method);
[email protected]afa0aad2014-02-19 21:09:0167 EXPECT_EQ(fe, error);
[email protected]e7d25152013-05-20 21:39:3168}
69
[email protected]e7d25152013-05-20 21:39:3170TEST(ErrorEncoding, NoEncodedMessage) {
71 Status s = Status::IOError("Some message", "from leveldb itself");
[email protected]6550f7602014-07-25 00:42:3572 MethodID method = leveldb_env::kRandomAccessFileRead;
jsbellab87bce2015-02-20 20:58:5473 base::File::Error error = base::File::FILE_ERROR_MAX;
cmumford5d9f5692014-12-16 00:04:5774 EXPECT_EQ(leveldb_env::NONE, ParseMethodAndError(s, &method, &error));
[email protected]6550f7602014-07-25 00:42:3575 EXPECT_EQ(leveldb_env::kRandomAccessFileRead, method);
jsbellab87bce2015-02-20 20:58:5476 EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
[email protected]e7d25152013-05-20 21:39:3177}
78
[email protected]7c0c90ea2014-01-27 19:34:1979template <typename T>
[email protected]7c0c90ea2014-01-27 19:34:1980class ChromiumEnvMultiPlatformTests : public ::testing::Test {
81 public:
82};
83
cmumfordbf823182014-11-20 16:32:5684typedef ::testing::Types<ChromiumEnv> ChromiumEnvMultiPlatformTestsTypes;
Victor Costanebc52732019-02-15 02:39:4785TYPED_TEST_SUITE(ChromiumEnvMultiPlatformTests,
86 ChromiumEnvMultiPlatformTestsTypes);
[email protected]7c0c90ea2014-01-27 19:34:1987
[email protected]27bd7f72013-10-16 18:18:0688int CountFilesWithExtension(const base::FilePath& dir,
89 const base::FilePath::StringType& extension) {
90 int matching_files = 0;
91 base::FileEnumerator dir_reader(
[email protected]68d00622013-10-18 12:32:2192 dir, false, base::FileEnumerator::FILES);
[email protected]27bd7f72013-10-16 18:18:0693 for (base::FilePath fname = dir_reader.Next(); !fname.empty();
94 fname = dir_reader.Next()) {
95 if (fname.MatchesExtension(extension))
96 matching_files++;
97 }
98 return matching_files;
99}
100
101bool GetFirstLDBFile(const base::FilePath& dir, base::FilePath* ldb_file) {
102 base::FileEnumerator dir_reader(
[email protected]68d00622013-10-18 12:32:21103 dir, false, base::FileEnumerator::FILES);
[email protected]27bd7f72013-10-16 18:18:06104 for (base::FilePath fname = dir_reader.Next(); !fname.empty();
105 fname = dir_reader.Next()) {
106 if (fname.MatchesExtension(FPL(".ldb"))) {
107 *ldb_file = fname;
108 return true;
109 }
110 }
111 return false;
112}
113
Victor Costanc0c7c5e2020-01-18 06:30:25114TEST(ChromiumEnv, RemoveBackupTables) {
Michal Zajaczkowski6be67b42021-02-20 10:12:07115 base::test::TaskEnvironment env(
116 base::test::TaskEnvironment::MainThreadType::UI);
[email protected]27bd7f72013-10-16 18:18:06117 Options options;
118 options.create_if_missing = true;
cmumfordc006a0ae2016-08-12 16:09:39119 options.env = Env::Default();
[email protected]27bd7f72013-10-16 18:18:06120
121 base::ScopedTempDir scoped_temp_dir;
[email protected]79af4342014-03-28 16:12:41122 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
vabra9819822016-09-28 08:44:50123 base::FilePath dir = scoped_temp_dir.GetPath();
[email protected]27bd7f72013-10-16 18:18:06124
125 DB* db;
126 Status status = DB::Open(options, dir.AsUTF8Unsafe(), &db);
127 EXPECT_TRUE(status.ok()) << status.ToString();
128 status = db->Put(WriteOptions(), "key", "value");
129 EXPECT_TRUE(status.ok()) << status.ToString();
130 Slice a = "a";
131 Slice z = "z";
cmumfordc006a0ae2016-08-12 16:09:39132 db->CompactRange(&a, &z); // Ensure manifest written out to table.
[email protected]b1011ef2013-10-22 00:15:26133 delete db;
cmumfordc006a0ae2016-08-12 16:09:39134 db = nullptr;
[email protected]27bd7f72013-10-16 18:18:06135
cmumfordc006a0ae2016-08-12 16:09:39136 // Current ChromiumEnv no longer makes backup tables - verify for sanity.
137 EXPECT_EQ(1, CountFilesWithExtension(dir, FPL(".ldb")));
138 EXPECT_EQ(0, CountFilesWithExtension(dir, FPL(".bak")));
139
140 // Manually create our own backup table to simulate opening db created by
141 // prior release.
142 base::FilePath ldb_path;
143 ASSERT_TRUE(GetFirstLDBFile(dir, &ldb_path));
144 base::FilePath bak_path = ldb_path.ReplaceExtension(FPL(".bak"));
145 ASSERT_TRUE(base::CopyFile(ldb_path, bak_path));
146 EXPECT_EQ(1, CountFilesWithExtension(dir, FPL(".bak")));
147
148 // Now reopen and close then verify the backup file was deleted.
149 status = DB::Open(options, dir.AsUTF8Unsafe(), &db);
[email protected]27bd7f72013-10-16 18:18:06150 EXPECT_TRUE(status.ok()) << status.ToString();
cmumfordc006a0ae2016-08-12 16:09:39151 EXPECT_EQ(0, CountFilesWithExtension(dir, FPL(".bak")));
[email protected]27bd7f72013-10-16 18:18:06152 delete db;
cmumfordc006a0ae2016-08-12 16:09:39153 EXPECT_EQ(1, CountFilesWithExtension(dir, FPL(".ldb")));
154 EXPECT_EQ(0, CountFilesWithExtension(dir, FPL(".bak")));
[email protected]27bd7f72013-10-16 18:18:06155}
156
[email protected]6c6862b2013-10-24 04:42:43157TEST(ChromiumEnv, GetChildrenEmptyDir) {
158 base::ScopedTempDir scoped_temp_dir;
[email protected]79af4342014-03-28 16:12:41159 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
vabra9819822016-09-28 08:44:50160 base::FilePath dir = scoped_temp_dir.GetPath();
[email protected]6c6862b2013-10-24 04:42:43161
cmumfordc38dbd42015-06-18 01:25:16162 Env* env = Env::Default();
[email protected]6c6862b2013-10-24 04:42:43163 std::vector<std::string> result;
164 leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
165 EXPECT_TRUE(status.ok());
[email protected]cd62bf62014-04-10 05:01:15166 EXPECT_EQ(0U, result.size());
[email protected]6c6862b2013-10-24 04:42:43167}
168
[email protected]21d8ff12013-12-04 21:40:55169TEST(ChromiumEnv, GetChildrenPriorResults) {
170 base::ScopedTempDir scoped_temp_dir;
[email protected]79af4342014-03-28 16:12:41171 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
vabra9819822016-09-28 08:44:50172 base::FilePath dir = scoped_temp_dir.GetPath();
[email protected]21d8ff12013-12-04 21:40:55173
174 base::FilePath new_file_dir = dir.Append(FPL("tmp_file"));
[email protected]6e937e42013-12-06 08:25:24175 FILE* f = fopen(new_file_dir.AsUTF8Unsafe().c_str(), "w");
[email protected]21d8ff12013-12-04 21:40:55176 if (f) {
177 fputs("Temp file contents", f);
178 fclose(f);
179 }
180
cmumfordc38dbd42015-06-18 01:25:16181 Env* env = Env::Default();
[email protected]21d8ff12013-12-04 21:40:55182 std::vector<std::string> result;
183 leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
184 EXPECT_TRUE(status.ok());
[email protected]1b87123d2014-04-12 10:37:19185 EXPECT_EQ(1U, result.size());
[email protected]21d8ff12013-12-04 21:40:55186
187 // And a second time should also return one result
188 status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
189 EXPECT_TRUE(status.ok());
[email protected]1b87123d2014-04-12 10:37:19190 EXPECT_EQ(1U, result.size());
[email protected]21d8ff12013-12-04 21:40:55191}
192
cmumfordedb3be62016-08-17 22:34:09193TEST(ChromiumEnv, TestWriteBufferSize) {
194 // If can't get disk size, use leveldb defaults.
195 const int64_t MB = 1024 * 1024;
196 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(-1));
197
198 // A very small disk (check lower clamp value).
199 EXPECT_EQ(size_t(1 * MB), leveldb_env::WriteBufferSize(1 * MB));
200
201 // Some value on the linear equation between min and max.
202 EXPECT_EQ(size_t(2.5 * MB), leveldb_env::WriteBufferSize(25 * MB));
203
204 // The disk size equating to the max buffer size
205 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(40 * MB));
206
207 // Make sure sizes larger than 40MB are clamped to max buffer size.
208 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(80 * MB));
209
210 // Check for very large disk size (catch overflow).
211 EXPECT_EQ(size_t(4 * MB), leveldb_env::WriteBufferSize(100 * MB * MB));
212}
213
Kevin Marshallbbec5c22017-08-15 01:06:17214TEST(ChromiumEnv, LockFile) {
215 base::FilePath tmp_file_path;
216 base::CreateTemporaryFile(&tmp_file_path);
217 leveldb::FileLock* lock = nullptr;
218
219 Env* env = Env::Default();
220 EXPECT_TRUE(env->LockFile(tmp_file_path.MaybeAsASCII(), &lock).ok());
221 EXPECT_NE(nullptr, lock);
222
223 leveldb::FileLock* failed_lock = nullptr;
224 EXPECT_FALSE(env->LockFile(tmp_file_path.MaybeAsASCII(), &failed_lock).ok());
225 EXPECT_EQ(nullptr, failed_lock);
226
227 EXPECT_TRUE(env->UnlockFile(lock).ok());
228 EXPECT_TRUE(env->LockFile(tmp_file_path.MaybeAsASCII(), &lock).ok());
229 EXPECT_TRUE(env->UnlockFile(lock).ok());
230}
231
Chris Mumford203cde62017-09-13 19:48:47232TEST(ChromiumEnvTest, TestOpenOnRead) {
233 // Write some test data to a single file that will be opened |n| times.
234 base::FilePath tmp_file_path;
235 ASSERT_TRUE(base::CreateTemporaryFile(&tmp_file_path));
236
237 FILE* f = fopen(tmp_file_path.AsUTF8Unsafe().c_str(), "w");
238 ASSERT_TRUE(f != NULL);
239 const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
240 fputs(kFileData, f);
241 fclose(f);
242
243 std::unique_ptr<ChromiumEnv> env(new ChromiumEnv());
244 env->SetReadOnlyFileLimitForTesting(kReadOnlyFileLimit);
245
246 // Open test file some number greater than kReadOnlyFileLimit to force the
247 // open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
248 const int kNumFiles = kReadOnlyFileLimit + 5;
249 leveldb::RandomAccessFile* files[kNumFiles] = {0};
250 for (int i = 0; i < kNumFiles; i++) {
251 ASSERT_TRUE(
252 env->NewRandomAccessFile(tmp_file_path.AsUTF8Unsafe(), &files[i]).ok());
253 }
254 char scratch;
255 Slice read_result;
256 for (int i = 0; i < kNumFiles; i++) {
257 ASSERT_TRUE(files[i]->Read(i, 1, &read_result, &scratch).ok());
258 ASSERT_EQ(kFileData[i], read_result[0]);
259 }
260 for (int i = 0; i < kNumFiles; i++) {
261 delete files[i];
262 }
Victor Costanc0c7c5e2020-01-18 06:30:25263 ASSERT_TRUE(env->RemoveFile(tmp_file_path.AsUTF8Unsafe()).ok());
Chris Mumford203cde62017-09-13 19:48:47264}
265
dskiba542a7c82017-06-29 21:58:11266class ChromiumEnvDBTrackerTest : public ::testing::Test {
267 protected:
Chris Mumford8d26d10a2018-04-20 17:07:58268 ChromiumEnvDBTrackerTest()
Gabriel Charette2dfa2ba42019-09-10 00:28:54269 : task_environment_(base::test::TaskEnvironment::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_;
Gabriel Charette2dfa2ba42019-09-10 00:28:54304 base::test::TaskEnvironment 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
Alex Mitra280bdfb2023-09-14 12:20:37432// TODO(crbug.com/1482738): Fix and re-enable this test.
Austin Sullivan24b4a4f2023-09-14 17:07:31433#if BUILDFLAG(IS_ANDROID)
434#define MAYBE_MemoryDumpCreation DISABLED_MemoryDumpCreation
435#else
436#define MAYBE_MemoryDumpCreation MemoryDumpCreation
437#endif // BUILDFLAG(IS_ANDROID)
438TEST_F(ChromiumEnvDBTrackerTest, MAYBE_MemoryDumpCreation) {
Siddhartha7c9ac4a2017-10-18 03:35:58439 Options options;
440 options.create_if_missing = true;
441 leveldb::Cache* web_cache = leveldb_chrome::GetSharedWebBlockCache();
442 leveldb::Cache* browser_cache = leveldb_chrome::GetSharedBrowserBlockCache();
443 options.block_cache = web_cache;
444 std::unique_ptr<leveldb::DB> db1;
445 base::ScopedTempDir temp_dir1;
446 ASSERT_TRUE(temp_dir1.CreateUniqueTempDir());
447 base::ScopedTempDir temp_dir2;
448 ASSERT_TRUE(temp_dir2.CreateUniqueTempDir());
449 base::ScopedTempDir temp_dir3;
450 ASSERT_TRUE(temp_dir3.CreateUniqueTempDir());
451
452 auto status =
453 leveldb_env::OpenDB(options, temp_dir1.GetPath().AsUTF8Unsafe(), &db1);
454 ASSERT_TRUE(status.ok()) << status.ToString();
455
456 std::unique_ptr<leveldb::DB> db2;
457 status =
458 leveldb_env::OpenDB(options, temp_dir2.GetPath().AsUTF8Unsafe(), &db2);
459 ASSERT_TRUE(status.ok()) << status.ToString();
460
461 std::unique_ptr<leveldb::DB> db3;
462 options.block_cache = browser_cache;
463 status =
464 leveldb_env::OpenDB(options, temp_dir3.GetPath().AsUTF8Unsafe(), &db3);
465 ASSERT_TRUE(status.ok()) << status.ToString();
466
467 auto db_visitor = [](DBTracker::TrackedDB* db) {
468 leveldb::Cache* db_cache =
469 (db->block_cache_type() == DBTracker::SharedReadCacheUse_Browser)
470 ? leveldb_chrome::GetSharedBrowserBlockCache()
471 : leveldb_chrome::GetSharedWebBlockCache();
472 size_t initial_cache_size = db_cache->TotalCharge();
473 auto status = db->Put(WriteOptions(), "key", "value");
474 EXPECT_TRUE(status.ok()) << status.ToString();
475 db->CompactRange(nullptr, nullptr);
476 std::string value;
477 status = db->Get(ReadOptions(), "key", &value);
478 ASSERT_TRUE(status.ok()) << status.ToString();
479 EXPECT_GT(db_cache->TotalCharge(), initial_cache_size);
480 };
481 DBTracker::GetInstance()->VisitDatabases(base::BindRepeating(db_visitor));
482 ASSERT_EQ(browser_cache->TotalCharge() * 2, web_cache->TotalCharge());
483
Ho Cheungadbf3fb2023-09-08 02:01:11484 MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::kBackground};
erikchenf62ea042018-05-25 21:30:57485 base::trace_event::ProcessMemoryDump pmd(dump_args);
Siddhartha7c9ac4a2017-10-18 03:35:58486 auto* mad1 = DBTracker::GetOrCreateAllocatorDump(&pmd, db1.get());
487 auto* mad2 = DBTracker::GetOrCreateAllocatorDump(&pmd, db2.get());
488 auto* mad3 = DBTracker::GetOrCreateAllocatorDump(&pmd, db3.get());
489
490 // All databases should have the same size since we made the same changes.
491 size_t db_size = mad1->GetSizeInternal();
492 EXPECT_GT(db_size, 0ul);
493 EXPECT_EQ(db_size, mad2->GetSizeInternal());
494 EXPECT_EQ(db_size, mad3->GetSizeInternal());
495}
496
Chris Mumford8d26d10a2018-04-20 17:07:58497TEST_F(ChromiumEnvDBTrackerTest, MemEnvMemoryDumpCreation) {
498 std::unique_ptr<leveldb::Env> memenv = leveldb_chrome::NewMemEnv("test");
499
500 Status s;
501 WritableFile* writable_file;
502 s = memenv->NewWritableFile("first_file.txt", &writable_file);
503 ASSERT_TRUE(s.ok()) << s.ToString();
504
505 const std::string kValue(2048, 'x');
506 writable_file->Append(Slice(kValue));
507 delete writable_file;
508
Ho Cheungadbf3fb2023-09-08 02:01:11509 const MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::kBackground};
erikchenf62ea042018-05-25 21:30:57510 base::trace_event::ProcessMemoryDump dump1(dump_args);
Chris Mumford8d26d10a2018-04-20 17:07:58511 auto* mad = DBTracker::GetOrCreateAllocatorDump(&dump1, memenv.get());
512
513 uint64_t size_with_file = mad->GetSizeInternal();
514 EXPECT_GE(size_with_file, kValue.size());
515
516 // Now rename and size should be unchanged.
517 s = memenv->RenameFile("first_file.txt", "xxxxx_file.txt"); // same length.
518 EXPECT_TRUE(s.ok()) << s.ToString();
erikchenf62ea042018-05-25 21:30:57519 base::trace_event::ProcessMemoryDump dump2(dump_args);
Chris Mumford8d26d10a2018-04-20 17:07:58520 mad = DBTracker::GetOrCreateAllocatorDump(&dump2, memenv.get());
521 EXPECT_EQ(size_with_file, mad->GetSizeInternal());
522
523 // Now delete and size should go down.
Victor Costanc0c7c5e2020-01-18 06:30:25524 s = memenv->RemoveFile("xxxxx_file.txt");
Chris Mumford8d26d10a2018-04-20 17:07:58525 EXPECT_TRUE(s.ok()) << s.ToString();
526
erikchenf62ea042018-05-25 21:30:57527 base::trace_event::ProcessMemoryDump dump3(dump_args);
Chris Mumford8d26d10a2018-04-20 17:07:58528 mad = DBTracker::GetOrCreateAllocatorDump(&dump3, memenv.get());
529 EXPECT_EQ(mad->GetSizeInternal(), 0ul);
530}
531
Chris Mumfordda3afc3b2018-06-05 22:10:23532TEST(ChromiumLevelDB, PossiblyValidDB) {
533 base::ScopedTempDir scoped_temp_dir;
534 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
535
536 leveldb::Env* default_env = leveldb::Env::Default();
537 const base::FilePath& db_path = scoped_temp_dir.GetPath();
538 EXPECT_FALSE(leveldb_chrome::PossiblyValidDB(db_path, default_env));
539
540 {
541 base::File current(db_path.Append(FILE_PATH_LITERAL("CURRENT")),
542 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
543 ASSERT_TRUE(current.IsValid());
544 const char kString[] = "ManifestFile";
545 EXPECT_EQ(static_cast<int>(sizeof(kString)),
546 current.Write(0, kString, sizeof(kString)));
547 }
548
549 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, default_env));
550
551 ASSERT_TRUE(scoped_temp_dir.Delete());
552 EXPECT_FALSE(leveldb_chrome::PossiblyValidDB(db_path, default_env));
553}
554
555TEST(ChromiumLevelDB, DeleteOnDiskDB) {
556 base::ScopedTempDir scoped_temp_dir;
557 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
558
559 const base::FilePath db_path = scoped_temp_dir.GetPath().AppendASCII("db");
560 leveldb_env::Options on_disk_options;
561 on_disk_options.create_if_missing = true;
562
563 // First with no db directory.
564 EXPECT_FALSE(base::PathExists(db_path));
565 Status s = leveldb_chrome::DeleteDB(db_path, on_disk_options);
566 EXPECT_TRUE(s.ok()) << s.ToString();
567
568 // Now an empty directory.
569 EXPECT_FALSE(base::PathExists(db_path));
570 EXPECT_TRUE(base::CreateDirectory(db_path));
571 s = leveldb_chrome::DeleteDB(db_path, on_disk_options);
572 EXPECT_TRUE(s.ok()) << s.ToString();
573 EXPECT_FALSE(base::PathExists(db_path));
574
575 // Now with a valid leveldb database and an extra file.
576 std::unique_ptr<leveldb::DB> db;
577 s = OpenDB(on_disk_options, db_path.AsUTF8Unsafe(), &db);
578 ASSERT_TRUE(s.ok()) << s.ToString();
579 s = db->Put(WriteOptions(), "TheKey", "TheValue");
580 EXPECT_TRUE(s.ok()) << s.ToString();
581 db.reset();
582
583 base::File test_file(db_path.Append(FILE_PATH_LITERAL("Test file.txt")),
584 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
585 ASSERT_TRUE(test_file.IsValid());
586 const char kString[] = "Just some text.";
587 const int data_len = static_cast<int>(sizeof(kString));
588 EXPECT_EQ(data_len, test_file.Write(0, kString, data_len));
589 test_file.Close();
590
591 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, on_disk_options.env));
592 s = leveldb_chrome::DeleteDB(db_path, on_disk_options);
593 EXPECT_TRUE(s.ok()) << s.ToString();
594
595 EXPECT_FALSE(base::PathExists(db_path));
596}
597
598TEST(ChromiumLevelDB, DeleteInMemoryDB) {
599 base::ScopedTempDir scoped_temp_dir;
600 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
601
602 // First create an on-disk db with an extra file.
Michal Zajaczkowski6be67b42021-02-20 10:12:07603 const base::FilePath db_path =
604 scoped_temp_dir.GetPath().AppendASCII("db").NormalizePathSeparatorsTo(
605 '/');
606 base::FilePath temp_path = db_path.Append(FILE_PATH_LITERAL("Test file.txt"))
607 .NormalizePathSeparatorsTo('/');
Chris Mumfordda3afc3b2018-06-05 22:10:23608 leveldb_env::Options on_disk_options;
609 on_disk_options.create_if_missing = true;
610
611 {
612 std::unique_ptr<leveldb::DB> db;
613 Status s = OpenDB(on_disk_options, db_path.AsUTF8Unsafe(), &db);
614 ASSERT_TRUE(s.ok()) << s.ToString();
615 s = db->Put(WriteOptions(), "TheKey", "TheValue");
616 EXPECT_TRUE(s.ok()) << s.ToString();
617 db.reset();
618
619 base::File test_file(
620 temp_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
621 ASSERT_TRUE(test_file.IsValid());
622 const char kString[] = "Just some text.";
623 const int data_len = static_cast<int>(sizeof(kString));
624 EXPECT_EQ(data_len, test_file.Write(0, kString, data_len));
625 test_file.Close();
626 }
627
628 // Now create an in-memory db.
629 std::unique_ptr<leveldb::Env> mem_env = leveldb_chrome::NewMemEnv("testing");
630 leveldb_env::Options in_memory_options;
631 in_memory_options.create_if_missing = true;
632 in_memory_options.env = mem_env.get();
633
634 {
635 std::unique_ptr<leveldb::DB> db;
636 // The two DB's purposely use the same path even though the in-memory path
637 // refers to a temp directory on disk.
638 Status s = OpenDB(in_memory_options, db_path.AsUTF8Unsafe(), &db);
639 ASSERT_TRUE(s.ok()) << s.ToString();
640 s = db->Put(WriteOptions(), "TheKey", "TheValue");
641 EXPECT_TRUE(s.ok()) << s.ToString();
642 db.reset();
643
644 leveldb::WritableFile* temp_file;
645 s = mem_env->NewWritableFile(temp_path.AsUTF8Unsafe(), &temp_file);
646 ASSERT_TRUE(s.ok()) << s.ToString();
647 s = temp_file->Append("Just some text.");
648 EXPECT_TRUE(s.ok()) << s.ToString();
649 s = temp_file->Close();
650 EXPECT_TRUE(s.ok()) << s.ToString();
651 delete temp_file;
652 }
653
654 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, on_disk_options.env));
655 EXPECT_TRUE(mem_env->FileExists(temp_path.AsUTF8Unsafe()));
656 Status s = leveldb_chrome::DeleteDB(db_path, in_memory_options);
657 EXPECT_TRUE(s.ok()) << s.ToString();
658 EXPECT_FALSE(mem_env->FileExists(temp_path.AsUTF8Unsafe()));
659 // On disk should be untouched.
660 EXPECT_TRUE(leveldb_chrome::PossiblyValidDB(db_path, on_disk_options.env));
661}
662
Christian Dullweber3cc579a22018-09-11 10:54:44663class ChromiumLevelDBRebuildTest : public ::testing::Test {
664 protected:
Elly Fong-Jones592ec902021-05-03 18:19:04665 ChromiumLevelDBRebuildTest() = default;
Christian Dullweber3cc579a22018-09-11 10:54:44666
667 void SetUp() override {
668 testing::Test::SetUp();
669 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
670 }
671
672 const base::FilePath& temp_path() const { return scoped_temp_dir_.GetPath(); }
673
674 private:
Peter Kastingcedbaf12023-03-14 17:22:31675 // TODO(https://github.com/llvm/llvm-project/issues/61334): Explicit
676 // [[maybe_unused]] attribute shouuld not be necessary here.
677 [[maybe_unused]] base::ScopedAllowBlockingForTesting allow_blocking_;
Christian Dullweber3cc579a22018-09-11 10:54:44678 base::ScopedTempDir scoped_temp_dir_;
679};
680
681TEST_F(ChromiumLevelDBRebuildTest, RebuildDb) {
682 std::unique_ptr<leveldb::DB> db;
683 base::FilePath db_path = temp_path().AppendASCII("db");
684 leveldb_env::Options options;
685 options.create_if_missing = true;
686
687 auto s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
Reilly Grantd6c56532023-04-06 03:13:03688 EXPECT_TRUE(s.ok());
689 ASSERT_TRUE(db);
Christian Dullweber3cc579a22018-09-11 10:54:44690 db->Put(leveldb::WriteOptions(), "key1", "value1");
691 db->Put(leveldb::WriteOptions(), "key2", "value2");
692 db->Delete(leveldb::WriteOptions(), "key1");
693
Christian Dullweber3cc579a22018-09-11 10:54:44694 s = leveldb_env::RewriteDB(options, db_path.AsUTF8Unsafe(), &db);
695 EXPECT_TRUE(s.ok());
Reilly Grantd6c56532023-04-06 03:13:03696 ASSERT_TRUE(db);
Christian Dullweber3cc579a22018-09-11 10:54:44697
698 std::string value;
699 s = db->Get(leveldb::ReadOptions(), "key1", &value);
700 EXPECT_TRUE(s.IsNotFound());
701 s = db->Get(leveldb::ReadOptions(), "key2", &value);
702 EXPECT_TRUE(s.ok());
703 EXPECT_EQ("value2", value);
704}
705
706TEST_F(ChromiumLevelDBRebuildTest, RecoverMissingDB) {
707 std::unique_ptr<leveldb::DB> db;
708 base::FilePath db_path = temp_path().AppendASCII("db");
709 base::FilePath tmp_path =
710 temp_path().AppendASCII(leveldb_env::DatabaseNameForRewriteDB("db"));
711 leveldb_env::Options options;
712 options.create_if_missing = true;
713
714 // Write a temporary db to simulate a failed rewrite attempt where only the
715 // temporary db exists.
716 auto s = leveldb_env::OpenDB(options, tmp_path.AsUTF8Unsafe(), &db);
717 ASSERT_TRUE(s.ok());
718 db->Put(leveldb::WriteOptions(), "key", "value");
719 db.reset();
720
721 EXPECT_FALSE(base::DirectoryExists(db_path));
722 EXPECT_TRUE(base::DirectoryExists(tmp_path));
723
724 // Open the regular db and check if the temporary one is recovered.
725 s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
726 ASSERT_TRUE(s.ok()) << s.ToString();
727 std::string value;
728 s = db->Get(leveldb::ReadOptions(), "key", &value);
729 EXPECT_TRUE(s.ok()) << s.ToString();
730 EXPECT_EQ("value", value);
731 EXPECT_TRUE(base::DirectoryExists(db_path));
732 EXPECT_FALSE(base::DirectoryExists(tmp_path));
733}
734
735TEST_F(ChromiumLevelDBRebuildTest, RecoverCorruptDB) {
736 std::unique_ptr<leveldb::DB> db;
737 base::FilePath db_path = temp_path().AppendASCII("db");
738 base::FilePath tmp_path =
739 temp_path().AppendASCII(leveldb_env::DatabaseNameForRewriteDB("db"));
740 leveldb_env::Options options;
741 options.create_if_missing = true;
742
743 // Create a corrupt db.
744 ASSERT_TRUE(base::CreateDirectory(db_path));
745 ASSERT_TRUE(leveldb_chrome::CorruptClosedDBForTesting(db_path));
746
747 // Write a temporary db to simulate a failed rewrite attempt.
748 auto s = leveldb_env::OpenDB(options, tmp_path.AsUTF8Unsafe(), &db);
749 ASSERT_TRUE(s.ok());
750 db->Put(leveldb::WriteOptions(), "key", "value");
751 db.reset();
752
753 // Open the regular db and check if the temporary one is recovered.
754 s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
755 ASSERT_TRUE(s.ok()) << s.ToString();
756 std::string value;
757 s = db->Get(leveldb::ReadOptions(), "key", &value);
758 EXPECT_TRUE(s.ok()) << s.ToString();
759 EXPECT_EQ("value", value);
760 EXPECT_TRUE(base::DirectoryExists(db_path));
761 EXPECT_FALSE(base::DirectoryExists(tmp_path));
762}
763
764TEST_F(ChromiumLevelDBRebuildTest, FinishCleanup) {
765 std::unique_ptr<leveldb::DB> db;
766 base::FilePath db_path = temp_path().AppendASCII("db");
767 base::FilePath tmp_path =
768 temp_path().AppendASCII(leveldb_env::DatabaseNameForRewriteDB("db"));
769 leveldb_env::Options options;
770 options.create_if_missing = true;
771
772 // Write a regular and a temporary db to simulate a rewrite attempt that
773 // crashed before finishing.
774 auto s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
775 ASSERT_TRUE(s.ok());
776 db->Put(leveldb::WriteOptions(), "key", "regular");
777 db.reset();
778
779 s = leveldb_env::OpenDB(options, tmp_path.AsUTF8Unsafe(), &db);
780 ASSERT_TRUE(s.ok());
781 db->Put(leveldb::WriteOptions(), "key", "temp");
782 db.reset();
783
784 EXPECT_TRUE(base::DirectoryExists(db_path));
785 EXPECT_TRUE(base::DirectoryExists(tmp_path));
786
787 // Open the regular db and check that the temporary one was cleaned up.
788 s = leveldb_env::OpenDB(options, db_path.AsUTF8Unsafe(), &db);
789 ASSERT_TRUE(s.ok()) << s.ToString();
790 std::string value;
791 s = db->Get(leveldb::ReadOptions(), "key", &value);
792 EXPECT_TRUE(s.ok()) << s.ToString();
793 EXPECT_EQ("regular", value);
794 EXPECT_TRUE(base::DirectoryExists(db_path));
795 EXPECT_FALSE(base::DirectoryExists(tmp_path));
796}
797
Chris Mumford33b762b2017-09-14 19:45:46798} // namespace leveldb_env
799
Reilly Grantd6c56532023-04-06 03:13:03800int main(int argc, char** argv) {
801 base::TestSuite test_suite(argc, argv);
802
803 // Many tests reuse the same database path and so must run serially.
804 return base::LaunchUnitTestsSerially(
805 argc, argv, BindOnce(&base::TestSuite::Run, Unretained(&test_suite)));
806}