| // 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/file_util.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/files/file_path.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/test/test_suite.h" |
| #include "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" |
| |
| using namespace leveldb_env; |
| using namespace leveldb; |
| |
| #define FPL FILE_PATH_LITERAL |
| |
| TEST(ErrorEncoding, OnlyAMethod) { |
| const MethodID in_method = kSequentialFileRead; |
| const Status s = MakeIOError("Somefile.txt", "message", in_method); |
| MethodID method; |
| int error = -75; |
| EXPECT_EQ(METHOD_ONLY, |
| ParseMethodAndError(s.ToString().c_str(), &method, &error)); |
| EXPECT_EQ(in_method, method); |
| EXPECT_EQ(-75, error); |
| } |
| |
| TEST(ErrorEncoding, PlatformFileError) { |
| const MethodID in_method = kWritableFileClose; |
| const base::PlatformFileError pfe = |
| base::PLATFORM_FILE_ERROR_INVALID_OPERATION; |
| const Status s = MakeIOError("Somefile.txt", "message", in_method, pfe); |
| MethodID method; |
| int error; |
| EXPECT_EQ(METHOD_AND_PFE, |
| ParseMethodAndError(s.ToString().c_str(), &method, &error)); |
| EXPECT_EQ(in_method, method); |
| EXPECT_EQ(pfe, error); |
| } |
| |
| TEST(ErrorEncoding, Errno) { |
| const MethodID in_method = kWritableFileFlush; |
| const int some_errno = ENOENT; |
| const Status s = |
| MakeIOError("Somefile.txt", "message", in_method, some_errno); |
| MethodID method; |
| int error; |
| EXPECT_EQ(METHOD_AND_ERRNO, |
| ParseMethodAndError(s.ToString().c_str(), &method, &error)); |
| EXPECT_EQ(in_method, method); |
| EXPECT_EQ(some_errno, error); |
| } |
| |
| TEST(ErrorEncoding, NoEncodedMessage) { |
| Status s = Status::IOError("Some message", "from leveldb itself"); |
| MethodID method = kRandomAccessFileRead; |
| int error = 4; |
| EXPECT_EQ(NONE, ParseMethodAndError(s.ToString().c_str(), &method, &error)); |
| EXPECT_EQ(kRandomAccessFileRead, method); |
| EXPECT_EQ(4, error); |
| } |
| |
| class MyEnv : public ChromiumEnv { |
| public: |
| MyEnv() : directory_syncs_(0) {} |
| int directory_syncs() { return directory_syncs_; } |
| |
| protected: |
| virtual void DidSyncDir(const std::string& fname) { |
| ++directory_syncs_; |
| ChromiumEnv::DidSyncDir(fname); |
| } |
| |
| private: |
| int directory_syncs_; |
| }; |
| |
| TEST(ChromiumEnv, DirectorySyncing) { |
| MyEnv env; |
| base::ScopedTempDir dir; |
| dir.CreateUniqueTempDir(); |
| base::FilePath dir_path = dir.path(); |
| std::string some_data = "some data"; |
| Slice data = some_data; |
| |
| std::string manifest_file_name = |
| FilePathToString(dir_path.Append(FILE_PATH_LITERAL("MANIFEST-001"))); |
| WritableFile* manifest_file_ptr; |
| Status s = env.NewWritableFile(manifest_file_name, &manifest_file_ptr); |
| EXPECT_TRUE(s.ok()); |
| scoped_ptr<WritableFile> 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 = |
| FilePathToString(dir_path.Append(FILE_PATH_LITERAL("000003.sst"))); |
| WritableFile* sst_file_ptr; |
| s = env.NewWritableFile(sst_file_name, &sst_file_ptr); |
| EXPECT_TRUE(s.ok()); |
| scoped_ptr<WritableFile> 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; |
| 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")); |
| int orig_bak_files = CountFilesWithExtension(dir, FPL(".bak")); |
| 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; |
| scoped_temp_dir.CreateUniqueTempDir(); |
| base::FilePath dir = scoped_temp_dir.path(); |
| |
| Env* env = IDBEnv(); |
| std::vector<std::string> result; |
| leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result); |
| EXPECT_TRUE(status.ok()); |
| EXPECT_EQ(0, result.size()); |
| } |
| |
| TEST(ChromiumEnv, GetChildrenPriorResults) { |
| base::ScopedTempDir scoped_temp_dir; |
| 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<std::string> result; |
| leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result); |
| EXPECT_TRUE(status.ok()); |
| EXPECT_EQ(1, result.size()); |
| |
| // And a second time should also return one result |
| status = env->GetChildren(dir.AsUTF8Unsafe(), &result); |
| EXPECT_TRUE(status.ok()); |
| EXPECT_EQ(1, result.size()); |
| } |
| |
| int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); } |