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
TEST(ErrorEncoding,OnlyAMethod)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
TEST(ErrorEncoding,FileError)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
TEST(ErrorEncoding,Errno)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)
TEST(ErrorEncoding,ErrnoWin32)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
TEST(ErrorEncoding,NoEncodedMessage)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:
MyEnv()87 MyEnv() : directory_syncs_(0) {}
directory_syncs()88 int directory_syncs() { return directory_syncs_; }
89
90 protected:
DidSyncDir(const std::string & fname)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
TYPED_TEST(ChromiumEnvMultiPlatformTests,DirectorySyncing)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
CountFilesWithExtension(const base::FilePath & dir,const base::FilePath::StringType & extension)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
GetFirstLDBFile(const base::FilePath & dir,base::FilePath * ldb_file)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
TEST(ChromiumEnv,BackupTables)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
TEST(ChromiumEnv,GetChildrenEmptyDir)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
TEST(ChromiumEnv,GetChildrenPriorResults)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
main(int argc,char ** argv)257 int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); }
258