• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TEST(ErrorEncoding,OnlyAMethod)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 
TEST(ErrorEncoding,FileError)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 
TEST(ErrorEncoding,Errno)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)
TEST(ErrorEncoding,ErrnoWin32)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 
TEST(ErrorEncoding,NoEncodedMessage)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:
MyEnv()100   MyEnv() : directory_syncs_(0) {}
directory_syncs()101   int directory_syncs() { return directory_syncs_; }
102 
103  protected:
DidSyncDir(const std::string & fname)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 
TYPED_TEST(ChromiumEnvMultiPlatformTests,DirectorySyncing)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 
CountFilesWithExtension(const base::FilePath & dir,const base::FilePath::StringType & extension)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 
GetFirstLDBFile(const base::FilePath & dir,base::FilePath * ldb_file)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 
TEST(ChromiumEnv,BackupTables)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 
TEST(ChromiumEnv,GetChildrenEmptyDir)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 
TEST(ChromiumEnv,GetChildrenPriorResults)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 
main(int argc,char ** argv)272 int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); }
273