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