1 // 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 5 #include "base/file_util.h" 6 #include "base/files/file_enumerator.h" 7 #include "base/files/file_path.h" 8 #include "base/files/scoped_temp_dir.h" 9 #include "base/test/test_suite.h" 10 #include "env_chromium.h" 11 #include "testing/gtest/include/gtest/gtest.h" 12 #include "third_party/leveldatabase/env_idb.h" 13 #include "third_party/leveldatabase/src/include/leveldb/db.h" 14 15 using namespace leveldb_env; 16 using namespace leveldb; 17 18 #define FPL FILE_PATH_LITERAL 19 20 TEST(ErrorEncoding, OnlyAMethod) { 21 const MethodID in_method = kSequentialFileRead; 22 const Status s = MakeIOError("Somefile.txt", "message", in_method); 23 MethodID method; 24 int error = -75; 25 EXPECT_EQ(METHOD_ONLY, 26 ParseMethodAndError(s.ToString().c_str(), &method, &error)); 27 EXPECT_EQ(in_method, method); 28 EXPECT_EQ(-75, error); 29 } 30 31 TEST(ErrorEncoding, PlatformFileError) { 32 const MethodID in_method = kWritableFileClose; 33 const base::PlatformFileError pfe = 34 base::PLATFORM_FILE_ERROR_INVALID_OPERATION; 35 const Status s = MakeIOError("Somefile.txt", "message", in_method, pfe); 36 MethodID method; 37 int error; 38 EXPECT_EQ(METHOD_AND_PFE, 39 ParseMethodAndError(s.ToString().c_str(), &method, &error)); 40 EXPECT_EQ(in_method, method); 41 EXPECT_EQ(pfe, error); 42 } 43 44 TEST(ErrorEncoding, Errno) { 45 const MethodID in_method = kWritableFileFlush; 46 const int some_errno = ENOENT; 47 const Status s = 48 MakeIOError("Somefile.txt", "message", in_method, some_errno); 49 MethodID method; 50 int error; 51 EXPECT_EQ(METHOD_AND_ERRNO, 52 ParseMethodAndError(s.ToString().c_str(), &method, &error)); 53 EXPECT_EQ(in_method, method); 54 EXPECT_EQ(some_errno, error); 55 } 56 57 TEST(ErrorEncoding, NoEncodedMessage) { 58 Status s = Status::IOError("Some message", "from leveldb itself"); 59 MethodID method = kRandomAccessFileRead; 60 int error = 4; 61 EXPECT_EQ(NONE, ParseMethodAndError(s.ToString().c_str(), &method, &error)); 62 EXPECT_EQ(kRandomAccessFileRead, method); 63 EXPECT_EQ(4, error); 64 } 65 66 class MyEnv : public ChromiumEnv { 67 public: 68 MyEnv() : directory_syncs_(0) {} 69 int directory_syncs() { return directory_syncs_; } 70 71 protected: 72 virtual void DidSyncDir(const std::string& fname) { 73 ++directory_syncs_; 74 ChromiumEnv::DidSyncDir(fname); 75 } 76 77 private: 78 int directory_syncs_; 79 }; 80 81 TEST(ChromiumEnv, DirectorySyncing) { 82 MyEnv env; 83 base::ScopedTempDir dir; 84 dir.CreateUniqueTempDir(); 85 base::FilePath dir_path = dir.path(); 86 std::string some_data = "some data"; 87 Slice data = some_data; 88 89 std::string manifest_file_name = 90 FilePathToString(dir_path.Append(FILE_PATH_LITERAL("MANIFEST-001"))); 91 WritableFile* manifest_file_ptr; 92 Status s = env.NewWritableFile(manifest_file_name, &manifest_file_ptr); 93 EXPECT_TRUE(s.ok()); 94 scoped_ptr<WritableFile> manifest_file(manifest_file_ptr); 95 manifest_file->Append(data); 96 EXPECT_EQ(0, env.directory_syncs()); 97 manifest_file->Append(data); 98 EXPECT_EQ(0, env.directory_syncs()); 99 100 std::string sst_file_name = 101 FilePathToString(dir_path.Append(FILE_PATH_LITERAL("000003.sst"))); 102 WritableFile* sst_file_ptr; 103 s = env.NewWritableFile(sst_file_name, &sst_file_ptr); 104 EXPECT_TRUE(s.ok()); 105 scoped_ptr<WritableFile> sst_file(sst_file_ptr); 106 sst_file->Append(data); 107 EXPECT_EQ(0, env.directory_syncs()); 108 109 manifest_file->Append(data); 110 EXPECT_EQ(1, env.directory_syncs()); 111 manifest_file->Append(data); 112 EXPECT_EQ(1, env.directory_syncs()); 113 } 114 115 int CountFilesWithExtension(const base::FilePath& dir, 116 const base::FilePath::StringType& extension) { 117 int matching_files = 0; 118 base::FileEnumerator dir_reader( 119 dir, false, base::FileEnumerator::FILES); 120 for (base::FilePath fname = dir_reader.Next(); !fname.empty(); 121 fname = dir_reader.Next()) { 122 if (fname.MatchesExtension(extension)) 123 matching_files++; 124 } 125 return matching_files; 126 } 127 128 bool GetFirstLDBFile(const base::FilePath& dir, base::FilePath* ldb_file) { 129 base::FileEnumerator dir_reader( 130 dir, false, base::FileEnumerator::FILES); 131 for (base::FilePath fname = dir_reader.Next(); !fname.empty(); 132 fname = dir_reader.Next()) { 133 if (fname.MatchesExtension(FPL(".ldb"))) { 134 *ldb_file = fname; 135 return true; 136 } 137 } 138 return false; 139 } 140 141 TEST(ChromiumEnv, BackupTables) { 142 Options options; 143 options.create_if_missing = true; 144 options.env = IDBEnv(); 145 146 base::ScopedTempDir scoped_temp_dir; 147 scoped_temp_dir.CreateUniqueTempDir(); 148 base::FilePath dir = scoped_temp_dir.path(); 149 150 DB* db; 151 Status status = DB::Open(options, dir.AsUTF8Unsafe(), &db); 152 EXPECT_TRUE(status.ok()) << status.ToString(); 153 status = db->Put(WriteOptions(), "key", "value"); 154 EXPECT_TRUE(status.ok()) << status.ToString(); 155 Slice a = "a"; 156 Slice z = "z"; 157 db->CompactRange(&a, &z); 158 int ldb_files = CountFilesWithExtension(dir, FPL(".ldb")); 159 int bak_files = CountFilesWithExtension(dir, FPL(".bak")); 160 EXPECT_GT(ldb_files, 0); 161 EXPECT_EQ(ldb_files, bak_files); 162 base::FilePath ldb_file; 163 EXPECT_TRUE(GetFirstLDBFile(dir, &ldb_file)); 164 delete db; 165 EXPECT_TRUE(base::DeleteFile(ldb_file, false)); 166 EXPECT_EQ(ldb_files - 1, CountFilesWithExtension(dir, FPL(".ldb"))); 167 168 // The ldb file deleted above should be restored in Open. 169 status = leveldb::DB::Open(options, dir.AsUTF8Unsafe(), &db); 170 EXPECT_TRUE(status.ok()) << status.ToString(); 171 std::string value; 172 status = db->Get(ReadOptions(), "key", &value); 173 EXPECT_TRUE(status.ok()) << status.ToString(); 174 EXPECT_EQ("value", value); 175 delete db; 176 177 // Ensure that deleting an ldb file also deletes its backup. 178 int orig_ldb_files = CountFilesWithExtension(dir, FPL(".ldb")); 179 int orig_bak_files = CountFilesWithExtension(dir, FPL(".bak")); 180 EXPECT_GT(ldb_files, 0); 181 EXPECT_EQ(ldb_files, bak_files); 182 EXPECT_TRUE(GetFirstLDBFile(dir, &ldb_file)); 183 options.env->DeleteFile(ldb_file.AsUTF8Unsafe()); 184 ldb_files = CountFilesWithExtension(dir, FPL(".ldb")); 185 bak_files = CountFilesWithExtension(dir, FPL(".bak")); 186 EXPECT_EQ(orig_ldb_files - 1, ldb_files); 187 EXPECT_EQ(bak_files, ldb_files); 188 } 189 190 TEST(ChromiumEnv, GetChildrenEmptyDir) { 191 base::ScopedTempDir scoped_temp_dir; 192 scoped_temp_dir.CreateUniqueTempDir(); 193 base::FilePath dir = scoped_temp_dir.path(); 194 195 Env* env = IDBEnv(); 196 std::vector<std::string> result; 197 leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result); 198 EXPECT_TRUE(status.ok()); 199 EXPECT_EQ(0, result.size()); 200 } 201 202 TEST(ChromiumEnv, GetChildrenPriorResults) { 203 base::ScopedTempDir scoped_temp_dir; 204 scoped_temp_dir.CreateUniqueTempDir(); 205 base::FilePath dir = scoped_temp_dir.path(); 206 207 base::FilePath new_file_dir = dir.Append(FPL("tmp_file")); 208 FILE* f = fopen(new_file_dir.AsUTF8Unsafe().c_str(), "w"); 209 if (f) { 210 fputs("Temp file contents", f); 211 fclose(f); 212 } 213 214 Env* env = IDBEnv(); 215 std::vector<std::string> result; 216 leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result); 217 EXPECT_TRUE(status.ok()); 218 EXPECT_EQ(1, result.size()); 219 220 // And a second time should also return one result 221 status = env->GetChildren(dir.AsUTF8Unsafe(), &result); 222 EXPECT_TRUE(status.ok()); 223 EXPECT_EQ(1, result.size()); 224 } 225 226 int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); } 227