Switch corruption_test to use InMemEnv.

This change switches corruption_test, which previously used direct file
I/O to corrupt table files for open databases, to use InMemEnv. Using an
Env eliminates some platform dependencies thus simplifying the tests.

Also removed EnvWindowsTestHelper::RelaxFilePermissions().  This was
only added because the Windows Env opens files for exclusive access.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=239305329
This commit is contained in:
cmumford 2019-03-19 17:30:42 -07:00 committed by Victor Costan
parent ce399ac28a
commit ea49b27d06
4 changed files with 17 additions and 72 deletions

View File

@ -4,12 +4,8 @@
#include "leveldb/db.h" #include "leveldb/db.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include "leveldb/cache.h" #include "leveldb/cache.h"
#include "leveldb/env.h"
#include "leveldb/table.h" #include "leveldb/table.h"
#include "leveldb/write_batch.h" #include "leveldb/write_batch.h"
#include "db/db_impl.h" #include "db/db_impl.h"
@ -20,10 +16,6 @@
#include "util/testharness.h" #include "util/testharness.h"
#include "util/testutil.h" #include "util/testutil.h"
#if defined(LEVELDB_PLATFORM_WINDOWS)
#include "util/env_windows_test_helper.h"
#endif // defined(LEVELDB_PLATFORM_WINDOWS)
namespace leveldb { namespace leveldb {
static const int kValueSize = 1000; static const int kValueSize = 1000;
@ -36,22 +28,11 @@ class CorruptionTest {
Options options_; Options options_;
DB* db_; DB* db_;
#if defined(LEVELDB_PLATFORM_WINDOWS)
static void SetFileLimits(int mmap_limit) {
EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit);
}
// TODO(cmumford): Modify corruption_test to use MemEnv and remove.
static void RelaxFilePermissions() {
EnvWindowsTestHelper::RelaxFilePermissions();
}
#endif // defined(LEVELDB_PLATFORM_WINDOWS)
CorruptionTest() { CorruptionTest() {
tiny_cache_ = NewLRUCache(100); tiny_cache_ = NewLRUCache(100);
options_.env = &env_; options_.env = &env_;
options_.block_cache = tiny_cache_; options_.block_cache = tiny_cache_;
dbname_ = test::TmpDir() + "/corruption_test"; dbname_ = "/memenv/corruption_test";
DestroyDB(dbname_, options_); DestroyDB(dbname_, options_);
db_ = nullptr; db_ = nullptr;
@ -62,7 +43,6 @@ class CorruptionTest {
~CorruptionTest() { ~CorruptionTest() {
delete db_; delete db_;
DestroyDB(dbname_, Options());
delete tiny_cache_; delete tiny_cache_;
} }
@ -141,7 +121,7 @@ class CorruptionTest {
void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) { void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {
// Pick file to corrupt // Pick file to corrupt
std::vector<std::string> filenames; std::vector<std::string> filenames;
ASSERT_OK(env_.GetChildren(dbname_, &filenames)); ASSERT_OK(env_.target()->GetChildren(dbname_, &filenames));
uint64_t number; uint64_t number;
FileType type; FileType type;
std::string fname; std::string fname;
@ -156,35 +136,32 @@ class CorruptionTest {
} }
ASSERT_TRUE(!fname.empty()) << filetype; ASSERT_TRUE(!fname.empty()) << filetype;
struct stat sbuf; uint64_t file_size;
if (stat(fname.c_str(), &sbuf) != 0) { ASSERT_OK(env_.target()->GetFileSize(fname, &file_size));
const char* msg = strerror(errno);
ASSERT_TRUE(false) << fname << ": " << msg;
}
if (offset < 0) { if (offset < 0) {
// Relative to end of file; make it absolute // Relative to end of file; make it absolute
if (-offset > sbuf.st_size) { if (-offset > file_size) {
offset = 0; offset = 0;
} else { } else {
offset = sbuf.st_size + offset; offset = file_size + offset;
} }
} }
if (offset > sbuf.st_size) { if (offset > file_size) {
offset = sbuf.st_size; offset = file_size;
} }
if (offset + bytes_to_corrupt > sbuf.st_size) { if (offset + bytes_to_corrupt > file_size) {
bytes_to_corrupt = sbuf.st_size - offset; bytes_to_corrupt = file_size - offset;
} }
// Do it // Do it
std::string contents; std::string contents;
Status s = ReadFileToString(Env::Default(), fname, &contents); Status s = ReadFileToString(env_.target(), fname, &contents);
ASSERT_TRUE(s.ok()) << s.ToString(); ASSERT_TRUE(s.ok()) << s.ToString();
for (int i = 0; i < bytes_to_corrupt; i++) { for (int i = 0; i < bytes_to_corrupt; i++) {
contents[i + offset] ^= 0x80; contents[i + offset] ^= 0x80;
} }
s = WriteStringToFile(Env::Default(), contents, fname); s = WriteStringToFile(env_.target(), contents, fname);
ASSERT_TRUE(s.ok()) << s.ToString(); ASSERT_TRUE(s.ok()) << s.ToString();
} }
@ -385,16 +362,5 @@ TEST(CorruptionTest, UnrelatedKeys) {
} // namespace leveldb } // namespace leveldb
int main(int argc, char** argv) { int main(int argc, char** argv) {
#if defined(LEVELDB_PLATFORM_WINDOWS)
// When Windows maps the contents of a file into memory, even if read/write,
// subsequent attempts to open that file for write access will fail. Forcing
// all RandomAccessFile instances to use base file I/O (e.g. ReadFile)
// allows these tests to open files in order to corrupt their contents.
leveldb::CorruptionTest::SetFileLimits(0);
// Allow this test to write to (and corrupt) files which are normally
// open for exclusive read access.
leveldb::CorruptionTest::RelaxFilePermissions();
#endif // defined(LEVELDB_PLATFORM_WINDOWS)
return leveldb::test::RunAllTests(); return leveldb::test::RunAllTests();
} }

View File

@ -43,9 +43,6 @@ constexpr int kDefaultMmapLimit = sizeof(void*) >= 8 ? 1000 : 0;
// Modified by EnvWindowsTestHelper::SetReadOnlyMMapLimit(). // Modified by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
int g_mmap_limit = kDefaultMmapLimit; int g_mmap_limit = kDefaultMmapLimit;
// Relax some file access permissions for testing.
bool g_relax_permissions = false;
std::string GetWindowsErrorMessage(DWORD error_code) { std::string GetWindowsErrorMessage(DWORD error_code) {
std::string message; std::string message;
char* error_text = nullptr; char* error_text = nullptr;
@ -366,10 +363,6 @@ class WindowsEnv : public Env {
*result = nullptr; *result = nullptr;
DWORD desired_access = GENERIC_READ; DWORD desired_access = GENERIC_READ;
DWORD share_mode = FILE_SHARE_READ; DWORD share_mode = FILE_SHARE_READ;
if (g_relax_permissions) {
desired_access |= GENERIC_WRITE;
share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
ScopedHandle handle = ScopedHandle handle =
::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr, ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
@ -385,10 +378,6 @@ class WindowsEnv : public Env {
*result = nullptr; *result = nullptr;
DWORD desired_access = GENERIC_READ; DWORD desired_access = GENERIC_READ;
DWORD share_mode = FILE_SHARE_READ; DWORD share_mode = FILE_SHARE_READ;
if (g_relax_permissions) {
// desired_access |= GENERIC_WRITE;
share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
DWORD file_flags = FILE_ATTRIBUTE_READONLY; DWORD file_flags = FILE_ATTRIBUTE_READONLY;
ScopedHandle handle = ScopedHandle handle =
@ -433,10 +422,6 @@ class WindowsEnv : public Env {
WritableFile** result) override { WritableFile** result) override {
DWORD desired_access = GENERIC_WRITE; DWORD desired_access = GENERIC_WRITE;
DWORD share_mode = 0; DWORD share_mode = 0;
if (g_relax_permissions) {
desired_access |= GENERIC_READ;
share_mode |= FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
}
ScopedHandle handle = ScopedHandle handle =
::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr, ::CreateFileA(fname.c_str(), desired_access, share_mode, nullptr,
@ -721,11 +706,6 @@ void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) {
g_mmap_limit = limit; g_mmap_limit = limit;
} }
void EnvWindowsTestHelper::RelaxFilePermissions() {
assert(default_env == nullptr);
g_relax_permissions = true;
}
Env* Env::Default() { Env* Env::Default() {
std::call_once(once, InitDefaultEnv); std::call_once(once, InitDefaultEnv);
return default_env; return default_env;

View File

@ -18,11 +18,6 @@ class EnvWindowsTestHelper {
// Set the maximum number of read-only files that will be mapped via mmap. // Set the maximum number of read-only files that will be mapped via mmap.
// Must be called before creating an Env. // Must be called before creating an Env.
static void SetReadOnlyMMapLimit(int limit); static void SetReadOnlyMMapLimit(int limit);
// Relax file permissions for tests. This results in most files being opened
// with read-write permissions. This is helpful for corruption tests that
// need to corrupt the database files for open databases.
static void RelaxFilePermissions();
}; };
} // namespace leveldb } // namespace leveldb

View File

@ -5,6 +5,7 @@
#ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ #ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_
#define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ #define STORAGE_LEVELDB_UTIL_TESTUTIL_H_
#include "helpers/memenv/memenv.h"
#include "leveldb/env.h" #include "leveldb/env.h"
#include "leveldb/slice.h" #include "leveldb/slice.h"
#include "util/random.h" #include "util/random.h"
@ -32,9 +33,12 @@ class ErrorEnv : public EnvWrapper {
bool writable_file_error_; bool writable_file_error_;
int num_writable_file_errors_; int num_writable_file_errors_;
ErrorEnv() : EnvWrapper(Env::Default()), ErrorEnv() : EnvWrapper(NewMemEnv(Env::Default())),
writable_file_error_(false), writable_file_error_(false),
num_writable_file_errors_(0) { } num_writable_file_errors_(0) { }
~ErrorEnv() override {
delete target();
}
virtual Status NewWritableFile(const std::string& fname, virtual Status NewWritableFile(const std::string& fname,
WritableFile** result) { WritableFile** result) {