Home | History | Annotate | Download | only in leveldatabase
      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 
     21 using leveldb::DB;
     22 using leveldb::Env;
     23 using leveldb::IDBEnv;
     24 using leveldb::Options;
     25 using leveldb::ReadOptions;
     26 using leveldb::Slice;
     27 using leveldb::Status;
     28 using leveldb::WritableFile;
     29 using leveldb::WriteOptions;
     30 using leveldb_env::ChromiumEnvStdio;
     31 #if defined(OS_WIN)
     32 using leveldb_env::ChromiumEnvWin;
     33 #endif
     34 using leveldb_env::MethodID;
     35 
     36 TEST(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 
     47 TEST(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 
     59 TEST(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)
     73 TEST(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 
     87 TEST(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 
     97 template <typename T>
     98 class 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 
    113 template <typename T>
    114 class ChromiumEnvMultiPlatformTests : public ::testing::Test {
    115  public:
    116 };
    117 
    118 #if defined(OS_WIN)
    119 typedef ::testing::Types<ChromiumEnvStdio, ChromiumEnvWin>
    120     ChromiumEnvMultiPlatformTestsTypes;
    121 #else
    122 typedef ::testing::Types<ChromiumEnvStdio> ChromiumEnvMultiPlatformTestsTypes;
    123 #endif
    124 TYPED_TEST_CASE(ChromiumEnvMultiPlatformTests,
    125                 ChromiumEnvMultiPlatformTestsTypes);
    126 
    127 TYPED_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 
    162 int 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 
    175 bool 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 
    188 TEST(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 
    236 TEST(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 
    248 TEST(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 
    272 int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); }
    273