blob: 070e893f6613c5ba27b537cdea9712fd6b1ec6a9 [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/lazy_instance.h"
#include "base/test/test_suite.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/leveldatabase/env_chromium.h"
#include "third_party/leveldatabase/src/include/leveldb/db.h"
#define FPL FILE_PATH_LITERAL
using leveldb::DB;
using leveldb::Env;
using leveldb::Options;
using leveldb::ReadOptions;
using leveldb::Slice;
using leveldb::Status;
using leveldb::WritableFile;
using leveldb::WriteOptions;
using leveldb_env::ChromiumEnv;
using leveldb_env::MethodID;
TEST(ErrorEncoding, OnlyAMethod) {
const MethodID in_method = leveldb_env::kSequentialFileRead;
const Status s = MakeIOError("Somefile.txt", "message", in_method);
MethodID method;
base::File::Error error = base::File::FILE_ERROR_MAX;
EXPECT_EQ(leveldb_env::METHOD_ONLY, ParseMethodAndError(s, &method, &error));
EXPECT_EQ(in_method, method);
EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
}
TEST(ErrorEncoding, FileError) {
const MethodID in_method = leveldb_env::kWritableFileClose;
const base::File::Error fe = base::File::FILE_ERROR_INVALID_OPERATION;
const Status s = MakeIOError("Somefile.txt", "message", in_method, fe);
MethodID method;
base::File::Error error;
EXPECT_EQ(leveldb_env::METHOD_AND_BFE,
ParseMethodAndError(s, &method, &error));
EXPECT_EQ(in_method, method);
EXPECT_EQ(fe, error);
}
TEST(ErrorEncoding, NoEncodedMessage) {
Status s = Status::IOError("Some message", "from leveldb itself");
MethodID method = leveldb_env::kRandomAccessFileRead;
base::File::Error error = base::File::FILE_ERROR_MAX;
EXPECT_EQ(leveldb_env::NONE, ParseMethodAndError(s, &method, &error));
EXPECT_EQ(leveldb_env::kRandomAccessFileRead, method);
EXPECT_EQ(base::File::FILE_ERROR_MAX, error);
}
template <typename T>
class ChromiumEnvMultiPlatformTests : public ::testing::Test {
public:
};
typedef ::testing::Types<ChromiumEnv> ChromiumEnvMultiPlatformTestsTypes;
TYPED_TEST_CASE(ChromiumEnvMultiPlatformTests,
ChromiumEnvMultiPlatformTestsTypes);
int CountFilesWithExtension(const base::FilePath& dir,
const base::FilePath::StringType& extension) {
int matching_files = 0;
base::FileEnumerator dir_reader(
dir, false, base::FileEnumerator::FILES);
for (base::FilePath fname = dir_reader.Next(); !fname.empty();
fname = dir_reader.Next()) {
if (fname.MatchesExtension(extension))
matching_files++;
}
return matching_files;
}
bool GetFirstLDBFile(const base::FilePath& dir, base::FilePath* ldb_file) {
base::FileEnumerator dir_reader(
dir, false, base::FileEnumerator::FILES);
for (base::FilePath fname = dir_reader.Next(); !fname.empty();
fname = dir_reader.Next()) {
if (fname.MatchesExtension(FPL(".ldb"))) {
*ldb_file = fname;
return true;
}
}
return false;
}
class BackupEnv : public ChromiumEnv {
public:
BackupEnv() : ChromiumEnv("BackupEnv", true /* backup tables */) {}
};
base::LazyInstance<BackupEnv>::Leaky backup_env = LAZY_INSTANCE_INITIALIZER;
TEST(ChromiumEnv, BackupTables) {
Options options;
options.create_if_missing = true;
options.env = backup_env.Pointer();
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
base::FilePath dir = scoped_temp_dir.path();
DB* db;
Status status = DB::Open(options, dir.AsUTF8Unsafe(), &db);
EXPECT_TRUE(status.ok()) << status.ToString();
status = db->Put(WriteOptions(), "key", "value");
EXPECT_TRUE(status.ok()) << status.ToString();
Slice a = "a";
Slice z = "z";
db->CompactRange(&a, &z);
int ldb_files = CountFilesWithExtension(dir, FPL(".ldb"));
int bak_files = CountFilesWithExtension(dir, FPL(".bak"));
EXPECT_GT(ldb_files, 0);
EXPECT_EQ(ldb_files, bak_files);
base::FilePath ldb_file;
EXPECT_TRUE(GetFirstLDBFile(dir, &ldb_file));
delete db;
EXPECT_TRUE(base::DeleteFile(ldb_file, false));
EXPECT_EQ(ldb_files - 1, CountFilesWithExtension(dir, FPL(".ldb")));
// The ldb file deleted above should be restored in Open.
status = leveldb::DB::Open(options, dir.AsUTF8Unsafe(), &db);
EXPECT_TRUE(status.ok()) << status.ToString();
std::string value;
status = db->Get(ReadOptions(), "key", &value);
EXPECT_TRUE(status.ok()) << status.ToString();
EXPECT_EQ("value", value);
delete db;
// Ensure that deleting an ldb file also deletes its backup.
int orig_ldb_files = CountFilesWithExtension(dir, FPL(".ldb"));
EXPECT_GT(ldb_files, 0);
EXPECT_EQ(ldb_files, bak_files);
EXPECT_TRUE(GetFirstLDBFile(dir, &ldb_file));
options.env->DeleteFile(ldb_file.AsUTF8Unsafe());
ldb_files = CountFilesWithExtension(dir, FPL(".ldb"));
bak_files = CountFilesWithExtension(dir, FPL(".bak"));
EXPECT_EQ(orig_ldb_files - 1, ldb_files);
EXPECT_EQ(bak_files, ldb_files);
}
TEST(ChromiumEnv, GetChildrenEmptyDir) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
base::FilePath dir = scoped_temp_dir.path();
Env* env = Env::Default();
std::vector<std::string> result;
leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
EXPECT_TRUE(status.ok());
EXPECT_EQ(0U, result.size());
}
TEST(ChromiumEnv, GetChildrenPriorResults) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
base::FilePath dir = scoped_temp_dir.path();
base::FilePath new_file_dir = dir.Append(FPL("tmp_file"));
FILE* f = fopen(new_file_dir.AsUTF8Unsafe().c_str(), "w");
if (f) {
fputs("Temp file contents", f);
fclose(f);
}
Env* env = Env::Default();
std::vector<std::string> result;
leveldb::Status status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
EXPECT_TRUE(status.ok());
EXPECT_EQ(1U, result.size());
// And a second time should also return one result
status = env->GetChildren(dir.AsUTF8Unsafe(), &result);
EXPECT_TRUE(status.ok());
EXPECT_EQ(1U, result.size());
}
int main(int argc, char** argv) { return base::TestSuite(argc, argv).Run(); }