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.
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 "third_party/leveldatabase/env_chromium_stdio.h"
13 #include "third_party/leveldatabase/env_chromium_win.h"
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"
19 #define FPL FILE_PATH_LITERAL
23 using leveldb::IDBEnv
;
24 using leveldb::Options
;
25 using leveldb::ReadOptions
;
27 using leveldb::Status
;
28 using leveldb::WritableFile
;
29 using leveldb::WriteOptions
;
30 using leveldb_env::ChromiumEnvStdio
;
32 using leveldb_env::ChromiumEnvWin
;
34 using leveldb_env::MethodID
;
36 TEST(ErrorEncoding
, OnlyAMethod
) {
37 const MethodID in_method
= leveldb_env::kSequentialFileRead
;
38 const Status s
= MakeIOError("Somefile.txt", "message", in_method
);
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
);
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
);
53 EXPECT_EQ(leveldb_env::METHOD_AND_PFE
,
54 ParseMethodAndError(s
.ToString().c_str(), &method
, &error
));
55 EXPECT_EQ(in_method
, method
);
59 TEST(ErrorEncoding
, Errno
) {
60 const MethodID in_method
= leveldb_env::kWritableFileFlush
;
61 const int some_errno
= ENOENT
;
63 MakeIOError("Somefile.txt", "message", in_method
, some_errno
);
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
);
73 TEST(ErrorEncoding
, ErrnoWin32
) {
74 const MethodID in_method
= leveldb_env::kWritableFileFlush
;
75 const DWORD some_errno
= ERROR_FILE_NOT_FOUND
;
77 MakeIOErrorWin("Somefile.txt", "message", in_method
, some_errno
);
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
);
87 TEST(ErrorEncoding
, NoEncodedMessage
) {
88 Status s
= Status::IOError("Some message", "from leveldb itself");
89 MethodID method
= leveldb_env::kRandomAccessFileRead
;
91 EXPECT_EQ(leveldb_env::NONE
,
92 ParseMethodAndError(s
.ToString().c_str(), &method
, &error
));
93 EXPECT_EQ(leveldb_env::kRandomAccessFileRead
, method
);
98 class MyEnv
: public T
{
100 MyEnv() : directory_syncs_(0) {}
101 int directory_syncs() { return directory_syncs_
; }
104 virtual void DidSyncDir(const std::string
& fname
) {
106 leveldb_env::ChromiumEnv::DidSyncDir(fname
);
110 int directory_syncs_
;
113 template <typename T
>
114 class ChromiumEnvMultiPlatformTests
: public ::testing::Test
{
119 typedef ::testing::Types
<ChromiumEnvStdio
, ChromiumEnvWin
>
120 ChromiumEnvMultiPlatformTestsTypes
;
122 typedef ::testing::Types
<ChromiumEnvStdio
> ChromiumEnvMultiPlatformTestsTypes
;
124 TYPED_TEST_CASE(ChromiumEnvMultiPlatformTests
,
125 ChromiumEnvMultiPlatformTestsTypes
);
127 TYPED_TEST(ChromiumEnvMultiPlatformTests
, DirectorySyncing
) {
128 MyEnv
<TypeParam
> env
;
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
;
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
);
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());
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
);
152 scoped_ptr
<WritableFile
> sst_file(sst_file_ptr
);
153 sst_file
->Append(data
);
154 EXPECT_EQ(0, env
.directory_syncs());
156 manifest_file
->Append(data
);
157 EXPECT_EQ(1, env
.directory_syncs());
158 manifest_file
->Append(data
);
159 EXPECT_EQ(1, env
.directory_syncs());
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
))
172 return matching_files
;
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"))) {
188 TEST(ChromiumEnv
, BackupTables
) {
190 options
.create_if_missing
= true;
191 options
.env
= IDBEnv();
193 base::ScopedTempDir scoped_temp_dir
;
194 ASSERT_TRUE(scoped_temp_dir
.CreateUniqueTempDir());
195 base::FilePath dir
= scoped_temp_dir
.path();
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();
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
));
212 EXPECT_TRUE(base::DeleteFile(ldb_file
, false));
213 EXPECT_EQ(ldb_files
- 1, CountFilesWithExtension(dir
, FPL(".ldb")));
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();
219 status
= db
->Get(ReadOptions(), "key", &value
);
220 EXPECT_TRUE(status
.ok()) << status
.ToString();
221 EXPECT_EQ("value", value
);
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
);
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();
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());
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();
253 base::FilePath new_file_dir
= dir
.Append(FPL("tmp_file"));
254 FILE* f
= fopen(new_file_dir
.AsUTF8Unsafe().c_str(), "w");
256 fputs("Temp file contents", f
);
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());
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());
272 int main(int argc
, char** argv
) { return base::TestSuite(argc
, argv
).Run(); }