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