// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/files/file.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/test/test_suite.h" #include "third_party/leveldatabase/env_chromium.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/leveldatabase/env_idb.h" #include "third_party/leveldatabase/src/include/leveldb/db.h" #define FPL FILE_PATH_LITERAL using leveldb::DB; using leveldb::Env; using leveldb::IDBEnv; using leveldb::Options; using leveldb::ReadOptions; using leveldb::Slice; using leveldb::Status; using leveldb::WritableFile; using leveldb::WriteOptions; using leveldb_env::ChromiumEnv; using leveldb_env::MethodID; TEST(ErrorEncoding, OnlyAMethod) { const MethodID in_method = leveldb_env::kSequentialFileRead; const Status s = MakeIOError("Somefile.txt", "message", in_method); MethodID method; base::File::Error error = base::File::FILE_ERROR_MAX; EXPECT_EQ(leveldb_env::METHOD_ONLY, ParseMethodAndError(s, &method, &error)); EXPECT_EQ(in_method, method); EXPECT_EQ(base::File::FILE_ERROR_MAX, error); } TEST(ErrorEncoding, FileError) { const MethodID in_method = leveldb_env::kWritableFileClose; const base::File::Error fe = base::File::FILE_ERROR_INVALID_OPERATION; const Status s = MakeIOError("Somefile.txt", "message", in_method, fe); MethodID method; base::File::Error error; EXPECT_EQ(leveldb_env::METHOD_AND_PFE, ParseMethodAndError(s, &method, &error)); EXPECT_EQ(in_method, method); EXPECT_EQ(fe, error); } TEST(ErrorEncoding, NoEncodedMessage) { Status s = Status::IOError("Some message", "from leveldb itself"); MethodID method = leveldb_env::kRandomAccessFileRead; base::File::Error error = base::File::FILE_ERROR_MAX; EXPECT_EQ(leveldb_env::NONE, ParseMethodAndError(s, &method, &error)); EXPECT_EQ(leveldb_env::kRandomAccessFileRead, method); EXPECT_EQ(base::File::FILE_ERROR_MAX, error); } template class MyEnv : public T { public: MyEnv() : directory_syncs_(0) {} int directory_syncs() { return directory_syncs_; } protected: virtual void DidSyncDir(const std::string& fname) { ++directory_syncs_; leveldb_env::ChromiumEnv::DidSyncDir(fname); } private: int directory_syncs_; }; template class ChromiumEnvMultiPlatformTests : public ::testing::Test { public: }; typedef ::testing::Types ChromiumEnvMultiPlatformTestsTypes; TYPED_TEST_CASE(ChromiumEnvMultiPlatformTests, ChromiumEnvMultiPlatformTestsTypes); TYPED_TEST(ChromiumEnvMultiPlatformTests, DirectorySyncing) { MyEnv env; base::ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); base::FilePath dir_path = dir.path(); std::string some_data = "some data"; Slice data = some_data; std::string manifest_file_name = dir_path.Append(FILE_PATH_LITERAL("MANIFEST-001")).AsUTF8Unsafe(); WritableFile* manifest_file_ptr; Status s = env.NewWritableFile(manifest_file_name, &manifest_file_ptr); EXPECT_TRUE(s.ok()); scoped_ptr manifest_file(manifest_file_ptr); manifest_file->Append(data); EXPECT_EQ(0, env.directory_syncs()); manifest_file->Append(data); EXPECT_EQ(0, env.directory_syncs()); std::string sst_file_name = dir_path.Append(FILE_PATH_LITERAL("000003.sst")).AsUTF8Unsafe(); WritableFile* sst_file_ptr; s = env.NewWritableFile(sst_file_name, &sst_file_ptr); EXPECT_TRUE(s.ok()); scoped_ptr sst_file(sst_file_ptr); sst_file->Append(data); EXPECT_EQ(0, env.directory_syncs()); manifest_file->Append(data); EXPECT_EQ(1, env.directory_syncs()); manifest_file->Append(data); EXPECT_EQ(1, env.directory_syncs()); } int CountFilesWithExtension(const base::FilePath& dir, const base::FilePath::StringType& extension) { int matching_files = 0; base::FileEnumerator dir_reader( dir, false, base::FileEnumerator::FILES); for (base::FilePath fname = dir_reader.Next(); !fname.empty(); fname = dir_reader.Next()) { if (fname.MatchesExtension(extension)) matching_files++; } return matching_files; } bool GetFirstLDBFile(const base::FilePath& dir, base::FilePath* ldb_file) { base::FileEnumerator dir_reader( dir, false, base::FileEnumerator::FILES); for (base::FilePath fname = dir_reader.Next(); !fname.empty(); fname = dir_reader.Next()) { if (fname.MatchesExtension(FPL(".ldb"))) { *ldb_file = fname; return true; } } return false; } TEST(ChromiumEnv, BackupTables) { Options options; options.create_if_missing = true; options.env = IDBEnv(); base::ScopedTempDir scoped_temp_dir; ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath dir = scoped_temp_dir.path(); DB* db; Status status = DB::Open(options, dir.AsUTF8Unsafe(), &db); EXPECT_TRUE(status.ok()) << status.ToString(); status = db->Put(WriteOptions(), "key", "value"); EXPECT_TRUE(status.ok()) << status.ToString(); Slice a = "a"; Slice z = "z"; db->CompactRange(&a, &z); int ldb_files = CountFilesWithExtension(dir, FPL(".ldb")); int bak_files = CountFilesWithExtension(dir, FPL(".bak")); EXPECT_GT(ldb_files, 0); EXPECT_EQ(ldb_files, bak_files); base::FilePath ldb_file; EXPECT_TRUE(GetFirstLDBFile(dir, &ldb_file)); delete db; EXPECT_TRUE(base::DeleteFile(ldb_file, false)); EXPECT_EQ(ldb_files - 1, CountFilesWithExtension(dir, FPL(".ldb"))); // The ldb file deleted above should be restored in Open. status = leveldb::DB::Open(options, dir.AsUTF8Unsafe(), &db); EXPECT_TRUE(status.ok()) << status.ToString(); std::string value; status = db->Get(ReadOptions(), "key", &value); EXPECT_TRUE(status.ok()) << status.ToString(); EXPECT_EQ("value", value); delete db; // Ensure that deleting an ldb file also deletes its backup. int orig_ldb_files = CountFilesWithExtension(dir, FPL(".ldb")); EXPECT_GT(ldb_files, 0); EXPECT_EQ(ldb_files, bak_files); EXPECT_TRUE(GetFirstLDBFile(dir, &ldb_file)); options.env->DeleteFile(ldb_file.AsUTF8Unsafe()); ldb_files = CountFilesWithExtension(dir, FPL(".ldb")); bak_files = CountFilesWithExtension(dir, FPL(".bak")); EXPECT_EQ(orig_ldb_files - 1, ldb_files); EXPECT_EQ(bak_files, ldb_files); } TEST(ChromiumEnv, GetChildrenEmptyDir) { base::ScopedTempDir scoped_temp_dir; ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath dir = scoped_temp_dir.path(); Env* env = IDBEnv(); std::vector result; leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result); EXPECT_TRUE(status.ok()); EXPECT_EQ(0U, result.size()); } TEST(ChromiumEnv, GetChildrenPriorResults) { base::ScopedTempDir scoped_temp_dir; ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir()); base::FilePath dir = scoped_temp_dir.path(); base::FilePath new_file_dir = dir.Append(FPL("tmp_file")); FILE* f = fopen(new_file_dir.AsUTF8Unsafe().c_str(), "w"); if (f) { fputs("Temp file contents", f); fclose(f); } Env* env = IDBEnv(); std::vector result; leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result); EXPECT_TRUE(status.ok()); EXPECT_EQ(1U, result.size()); // And a second time should also return one result status = env->GetChildren(dir.AsUTF8Unsafe(), &result); EXPECT_TRUE(status.ok()); EXPECT_EQ(1U, result.size()); } int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); }