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