#include "test_util/testutil.h"
#include "util/coding.h"
#include "util/mutexlock.h"
+#include "util/random.h"
#include "util/string_util.h"
+#include "utilities/fault_injection_env.h"
+#include "utilities/fault_injection_fs.h"
namespace ROCKSDB_NAMESPACE {
if (::stat(filename.c_str(), &sb) == 0) {
ASSERT_EQ(sb.st_mode & 0777, 0644);
}
- env_->DeleteFile(filename);
+ ASSERT_OK(env_->DeleteFile(filename));
}
env_->SetAllowNonOwnerAccess(false);
if (::stat(filename.c_str(), &sb) == 0) {
ASSERT_EQ(sb.st_mode & 0777, 0600);
}
- env_->DeleteFile(filename);
+ ASSERT_OK(env_->DeleteFile(filename));
}
}
}
+
+TEST_F(EnvPosixTest, LowerThreadPoolCpuPriority) {
+ std::atomic<CpuPriority> from_priority(CpuPriority::kNormal);
+ std::atomic<CpuPriority> to_priority(CpuPriority::kNormal);
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
+ "ThreadPoolImpl::BGThread::BeforeSetCpuPriority", [&](void* pri) {
+ from_priority.store(*reinterpret_cast<CpuPriority*>(pri));
+ });
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
+ "ThreadPoolImpl::BGThread::AfterSetCpuPriority", [&](void* pri) {
+ to_priority.store(*reinterpret_cast<CpuPriority*>(pri));
+ });
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
+
+ env_->SetBackgroundThreads(1, Env::BOTTOM);
+ env_->SetBackgroundThreads(1, Env::HIGH);
+
+ auto RunTask = [&](Env::Priority pool) {
+ std::atomic<bool> called(false);
+ env_->Schedule(&SetBool, &called, pool);
+ for (int i = 0; i < kDelayMicros; i++) {
+ if (called.load()) {
+ break;
+ }
+ Env::Default()->SleepForMicroseconds(1);
+ }
+ ASSERT_TRUE(called.load());
+ };
+
+ {
+ // Same priority, no-op.
+ env_->LowerThreadPoolCPUPriority(Env::Priority::BOTTOM,
+ CpuPriority::kNormal)
+ .PermitUncheckedError();
+ RunTask(Env::Priority::BOTTOM);
+ ASSERT_EQ(from_priority, CpuPriority::kNormal);
+ ASSERT_EQ(to_priority, CpuPriority::kNormal);
+ }
+
+ {
+ // Higher priority, no-op.
+ env_->LowerThreadPoolCPUPriority(Env::Priority::BOTTOM, CpuPriority::kHigh)
+ .PermitUncheckedError();
+ RunTask(Env::Priority::BOTTOM);
+ ASSERT_EQ(from_priority, CpuPriority::kNormal);
+ ASSERT_EQ(to_priority, CpuPriority::kNormal);
+ }
+
+ {
+ // Lower priority from kNormal -> kLow.
+ env_->LowerThreadPoolCPUPriority(Env::Priority::BOTTOM, CpuPriority::kLow)
+ .PermitUncheckedError();
+ RunTask(Env::Priority::BOTTOM);
+ ASSERT_EQ(from_priority, CpuPriority::kNormal);
+ ASSERT_EQ(to_priority, CpuPriority::kLow);
+ }
+
+ {
+ // Lower priority from kLow -> kIdle.
+ env_->LowerThreadPoolCPUPriority(Env::Priority::BOTTOM, CpuPriority::kIdle)
+ .PermitUncheckedError();
+ RunTask(Env::Priority::BOTTOM);
+ ASSERT_EQ(from_priority, CpuPriority::kLow);
+ ASSERT_EQ(to_priority, CpuPriority::kIdle);
+ }
+
+ {
+ // Lower priority from kNormal -> kIdle for another pool.
+ env_->LowerThreadPoolCPUPriority(Env::Priority::HIGH, CpuPriority::kIdle)
+ .PermitUncheckedError();
+ RunTask(Env::Priority::HIGH);
+ ASSERT_EQ(from_priority, CpuPriority::kNormal);
+ ASSERT_EQ(to_priority, CpuPriority::kIdle);
+ }
+
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
+}
#endif
TEST_F(EnvPosixTest, MemoryMappedFileBuffer) {
ASSERT_OK(env_->NewWritableFile(fname, &wfile, soptions));
Random rnd(301);
- test::RandomString(&rnd, kFileBytes, &expected_data);
+ expected_data = rnd.RandomString(kFileBytes);
ASSERT_OK(wfile->Append(expected_data));
}
// run in any order. The purpose of the test is unclear.
#ifndef OS_WIN
TEST_P(EnvPosixTestWithParam, RunMany) {
+ env_->SetBackgroundThreads(1, Env::LOW);
std::atomic<int> last_id(0);
struct CB {
ASSERT_EQ(unique_id2, unique_id3);
// Delete the file
- env_->DeleteFile(fname);
+ ASSERT_OK(env_->DeleteFile(fname));
}
}
#endif // !defined(OS_WIN)
// Collect and check whether the IDs are unique.
std::unordered_set<std::string> ids;
- for (const std::string fname : fnames) {
+ for (const std::string& fname : fnames) {
std::unique_ptr<RandomAccessFile> file;
std::string unique_id;
ASSERT_OK(env_->NewRandomAccessFile(fname, &file, soptions));
}
// Delete the files
- for (const std::string fname : fnames) {
+ for (const std::string& fname : fnames) {
ASSERT_OK(env_->DeleteFile(fname));
}
}
}
-TEST_P(EnvPosixTestWithParam, RandomAccessUniqueIDDeletes) {
+// TODO: Disable the flaky test, it's a known issue that ext4 may return same
+// key after file deletion. The issue is tracked in #7405, #7470.
+TEST_P(EnvPosixTestWithParam, DISABLED_RandomAccessUniqueIDDeletes) {
if (env_ == Env::Default()) {
EnvOptions soptions;
soptions.use_direct_reads = soptions.use_direct_writes = direct_io_;
}
}
+TEST_F(EnvPosixTest, MultiReadNonAlignedLargeNum) {
+ // In this test we don't do aligned read, wo it doesn't work for
+ // direct I/O case.
+ EnvOptions soptions;
+ soptions.use_direct_reads = soptions.use_direct_writes = false;
+ std::string fname = test::PerThreadDBPath(env_, "testfile");
+
+ const size_t kTotalSize = 81920;
+ Random rnd(301);
+ std::string expected_data = rnd.RandomString(kTotalSize);
+
+ // Create file.
+ {
+ std::unique_ptr<WritableFile> wfile;
+ ASSERT_OK(env_->NewWritableFile(fname, &wfile, soptions));
+ ASSERT_OK(wfile->Append(expected_data));
+ ASSERT_OK(wfile->Close());
+ }
+
+ // More attempts to simulate more partial result sequences.
+ for (uint32_t attempt = 0; attempt < 25; attempt++) {
+ // Right now kIoUringDepth is hard coded as 256, so we need very large
+ // number of keys to cover the case of multiple rounds of submissions.
+ // Right now the test latency is still acceptable. If it ends up with
+ // too long, we can modify the io uring depth with SyncPoint here.
+ const int num_reads = rnd.Uniform(512) + 1;
+
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
+ "PosixRandomAccessFile::MultiRead:io_uring_result", [&](void* arg) {
+ if (attempt > 5) {
+ // Improve partial result rates in second half of the run to
+ // cover the case of repeated partial results.
+ int odd = (attempt < 15) ? num_reads / 2 : 4;
+ // No failure in first several attempts.
+ size_t& bytes_read = *static_cast<size_t*>(arg);
+ if (rnd.OneIn(odd)) {
+ bytes_read = 0;
+ } else if (rnd.OneIn(odd / 2)) {
+ bytes_read = static_cast<size_t>(
+ rnd.Uniform(static_cast<int>(bytes_read)));
+ }
+ }
+ });
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
+
+ // Generate (offset, len) pairs
+ std::set<int> start_offsets;
+ for (int i = 0; i < num_reads; i++) {
+ int rnd_off;
+ // No repeat offsets.
+ while (start_offsets.find(rnd_off = rnd.Uniform(81920)) != start_offsets.end()) {}
+ start_offsets.insert(rnd_off);
+ }
+ std::vector<size_t> offsets;
+ std::vector<size_t> lens;
+ // std::set already sorted the offsets.
+ for (int so: start_offsets) {
+ offsets.push_back(so);
+ }
+ for (size_t i = 0; i + 1 < offsets.size(); i++) {
+ lens.push_back(static_cast<size_t>(rnd.Uniform(static_cast<int>(offsets[i + 1] - offsets[i])) + 1));
+ }
+ lens.push_back(static_cast<size_t>(rnd.Uniform(static_cast<int>(kTotalSize - offsets.back())) + 1));
+ ASSERT_EQ(num_reads, lens.size());
+
+ // Create requests
+ std::vector<std::string> scratches;
+ scratches.reserve(num_reads);
+ std::vector<ReadRequest> reqs(num_reads);
+ for (size_t i = 0; i < reqs.size(); ++i) {
+ reqs[i].offset = offsets[i];
+ reqs[i].len = lens[i];
+ scratches.emplace_back(reqs[i].len, ' ');
+ reqs[i].scratch = const_cast<char*>(scratches.back().data());
+ }
+
+ // Query the data
+ std::unique_ptr<RandomAccessFile> file;
+ ASSERT_OK(env_->NewRandomAccessFile(fname, &file, soptions));
+ ASSERT_OK(file->MultiRead(reqs.data(), reqs.size()));
+
+ // Validate results
+ for (int i = 0; i < num_reads; ++i) {
+ ASSERT_OK(reqs[i].status);
+ ASSERT_EQ(Slice(expected_data.data() + offsets[i], lens[i]).ToString(true),
+ reqs[i].result.ToString(true));
+ }
+
+ ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
+ }
+}
+
// Only works in linux platforms
#ifdef OS_WIN
TEST_P(EnvPosixTestWithParam, DISABLED_InvalidateCache) {
auto data = NewAligned(kStrSize, 'A');
Slice str(data.get(), kStrSize);
srcfile->PrepareWrite(srcfile->GetFileSize(), kStrSize);
- srcfile->Append(str);
+ ASSERT_OK(srcfile->Append(str));
srcfile->GetPreallocationStatus(&block_size, &last_allocated_block);
ASSERT_EQ(last_allocated_block, 1UL);
auto buf_ptr = NewAligned(block_size, ' ');
Slice buf(buf_ptr.get(), block_size);
srcfile->PrepareWrite(srcfile->GetFileSize(), block_size);
- srcfile->Append(buf);
+ ASSERT_OK(srcfile->Append(buf));
srcfile->GetPreallocationStatus(&block_size, &last_allocated_block);
ASSERT_EQ(last_allocated_block, 2UL);
}
auto buf_ptr = NewAligned(block_size * 5, ' ');
Slice buf = Slice(buf_ptr.get(), block_size * 5);
srcfile->PrepareWrite(srcfile->GetFileSize(), buf.size());
- srcfile->Append(buf);
+ ASSERT_OK(srcfile->Append(buf));
srcfile->GetPreallocationStatus(&block_size, &last_allocated_block);
ASSERT_EQ(last_allocated_block, 7UL);
}
const int kNumChildren = 10;
std::string data;
+ std::string test_base_dir = test::PerThreadDBPath(env_, "env_test_chr_attr");
+ env_->CreateDir(test_base_dir).PermitUncheckedError();
for (int i = 0; i < kNumChildren; ++i) {
- const std::string path =
- test::TmpDir(env_) + "/" + "testfile_" + std::to_string(i);
+ const std::string path = test_base_dir + "/testfile_" + std::to_string(i);
std::unique_ptr<WritableFile> file;
#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(OS_SOLARIS) && !defined(OS_AIX) && !defined(OS_OPENBSD) && !defined(OS_FREEBSD)
if (soptions.use_direct_writes) {
ASSERT_OK(env_->NewWritableFile(path, &file, soptions));
auto buf_ptr = NewAligned(data.size(), 'T');
Slice buf(buf_ptr.get(), data.size());
- file->Append(buf);
+ ASSERT_OK(file->Append(buf));
data.append(std::string(4096, 'T'));
}
std::vector<Env::FileAttributes> file_attrs;
- ASSERT_OK(env_->GetChildrenFileAttributes(test::TmpDir(env_), &file_attrs));
+ ASSERT_OK(env_->GetChildrenFileAttributes(test_base_dir, &file_attrs));
for (int i = 0; i < kNumChildren; ++i) {
const std::string name = "testfile_" + std::to_string(i);
- const std::string path = test::TmpDir(env_) + "/" + name;
+ const std::string path = test_base_dir + "/" + name;
auto file_attrs_iter = std::find_if(
file_attrs.begin(), file_attrs.end(),
{
Base b(&step);
Wrapper w(&b);
- w.Append(Slice());
- w.PositionedAppend(Slice(), 0);
- w.Truncate(0);
- w.Close();
- w.Flush();
- w.Sync();
- w.Fsync();
+ ASSERT_OK(w.Append(Slice()));
+ ASSERT_OK(w.PositionedAppend(Slice(), 0));
+ ASSERT_OK(w.Truncate(0));
+ ASSERT_OK(w.Close());
+ ASSERT_OK(w.Flush());
+ ASSERT_OK(w.Sync());
+ ASSERT_OK(w.Fsync());
w.IsSyncThreadSafe();
w.use_direct_io();
w.GetRequiredBufferAlignment();
w.SetPreallocationBlockSize(0);
w.GetPreallocationStatus(nullptr, nullptr);
w.GetUniqueId(nullptr, 0);
- w.InvalidateCache(0, 0);
- w.RangeSync(0, 0);
+ ASSERT_OK(w.InvalidateCache(0, 0));
+ ASSERT_OK(w.RangeSync(0, 0));
w.PrepareWrite(0, 0);
- w.Allocate(0, 0);
+ ASSERT_OK(w.Allocate(0, 0));
}
EXPECT_EQ(24, step);
TEST_P(EnvPosixTestWithParam, PosixRandomRWFile) {
const std::string path = test::PerThreadDBPath(env_, "random_rw_file");
- env_->DeleteFile(path);
+ env_->DeleteFile(path).PermitUncheckedError();
std::unique_ptr<RandomRWFile> file;
ASSERT_EQ(read_res.ToString(), "XXXQ");
// Close file and reopen it
- file->Close();
+ ASSERT_OK(file->Close());
ASSERT_OK(env_->NewRandomRWFile(path, &file, EnvOptions()));
ASSERT_OK(file->Read(0, 9, &read_res, buf));
ASSERT_EQ(read_res.ToString(), "ABXXTTTTTT");
// Clean up
- env_->DeleteFile(path);
+ ASSERT_OK(env_->DeleteFile(path));
}
class RandomRWFileWithMirrorString {
TEST_P(EnvPosixTestWithParam, PosixRandomRWFileRandomized) {
const std::string path = test::PerThreadDBPath(env_, "random_rw_file_rand");
- env_->DeleteFile(path);
+ env_->DeleteFile(path).PermitUncheckedError();
std::unique_ptr<RandomRWFile> file;
std::string buf;
for (int i = 0; i < 10000; i++) {
// Genrate random data
- test::RandomString(&rnd, 10, &buf);
+ buf = rnd.RandomString(10);
// Pick random offset for write
size_t write_off = rnd.Next() % 1000;
}
// clean up
- env_->DeleteFile(path);
+ ASSERT_OK(env_->DeleteFile(path));
}
class TestEnv : public EnvWrapper {
TestLogger(TestEnv* env_ptr) : Logger() { env = env_ptr; }
~TestLogger() override {
if (!closed_) {
- CloseHelper();
+ Status s = CloseHelper();
+ s.PermitUncheckedError();
}
}
void Logv(const char* /*format*/, va_list /*ap*/) override{};
int close_count;
};
-class EnvTest : public testing::Test {};
+class EnvTest : public testing::Test {
+ public:
+ EnvTest() : test_directory_(test::PerThreadDBPath("env_test")) {}
+
+ protected:
+ const std::string test_directory_;
+};
TEST_F(EnvTest, Close) {
TestEnv* env = new TestEnv();
Status s;
s = env->NewLogger("", &logger);
- ASSERT_EQ(s, Status::OK());
- logger.get()->Close();
+ ASSERT_OK(s);
+ ASSERT_OK(logger.get()->Close());
ASSERT_EQ(env->GetCloseCount(), 1);
// Call Close() again. CloseHelper() should not be called again
- logger.get()->Close();
+ ASSERT_OK(logger.get()->Close());
ASSERT_EQ(env->GetCloseCount(), 1);
logger.reset();
ASSERT_EQ(env->GetCloseCount(), 1);
s = env->NewLogger("", &logger);
- ASSERT_EQ(s, Status::OK());
+ ASSERT_OK(s);
logger.reset();
ASSERT_EQ(env->GetCloseCount(), 2);
delete env;
}
+class LogvWithInfoLogLevelLogger : public Logger {
+ public:
+ using Logger::Logv;
+ void Logv(const InfoLogLevel /* log_level */, const char* /* format */,
+ va_list /* ap */) override {}
+};
+
+TEST_F(EnvTest, LogvWithInfoLogLevel) {
+ // Verifies the log functions work on a `Logger` that only overrides the
+ // `Logv()` overload including `InfoLogLevel`.
+ const std::string kSampleMessage("sample log message");
+ LogvWithInfoLogLevelLogger logger;
+ ROCKS_LOG_HEADER(&logger, "%s", kSampleMessage.c_str());
+ ROCKS_LOG_DEBUG(&logger, "%s", kSampleMessage.c_str());
+ ROCKS_LOG_INFO(&logger, "%s", kSampleMessage.c_str());
+ ROCKS_LOG_WARN(&logger, "%s", kSampleMessage.c_str());
+ ROCKS_LOG_ERROR(&logger, "%s", kSampleMessage.c_str());
+ ROCKS_LOG_FATAL(&logger, "%s", kSampleMessage.c_str());
+}
+
INSTANTIATE_TEST_CASE_P(DefaultEnvWithoutDirectIO, EnvPosixTestWithParam,
::testing::Values(std::pair<Env*, bool>(Env::Default(),
false)));
::testing::Values(std::pair<Env*, bool>(chroot_env.get(), true)));
#endif // !defined(ROCKSDB_LITE) && !defined(OS_WIN)
+class EnvFSTestWithParam
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<std::tuple<bool, bool, bool>> {
+ public:
+ EnvFSTestWithParam() {
+ bool env_non_null = std::get<0>(GetParam());
+ bool env_default = std::get<1>(GetParam());
+ bool fs_default = std::get<2>(GetParam());
+
+ env_ = env_non_null ? (env_default ? Env::Default() : nullptr) : nullptr;
+ fs_ = fs_default
+ ? FileSystem::Default()
+ : std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
+ if (env_non_null && env_default && !fs_default) {
+ env_ptr_ = NewCompositeEnv(fs_);
+ }
+ if (env_non_null && !env_default && fs_default) {
+ env_ptr_ = std::unique_ptr<Env>(new FaultInjectionTestEnv(Env::Default()));
+ fs_.reset();
+ }
+ if (env_non_null && !env_default && !fs_default) {
+ env_ptr_.reset(new FaultInjectionTestEnv(Env::Default()));
+ composite_env_ptr_.reset(new CompositeEnvWrapper(env_ptr_.get(), fs_));
+ env_ = composite_env_ptr_.get();
+ } else {
+ env_ = env_ptr_.get();
+ }
+
+ dbname1_ = test::PerThreadDBPath("env_fs_test1");
+ dbname2_ = test::PerThreadDBPath("env_fs_test2");
+ }
+
+ ~EnvFSTestWithParam() = default;
+
+ Env* env_;
+ std::unique_ptr<Env> env_ptr_;
+ std::unique_ptr<Env> composite_env_ptr_;
+ std::shared_ptr<FileSystem> fs_;
+ std::string dbname1_;
+ std::string dbname2_;
+};
+
+TEST_P(EnvFSTestWithParam, OptionsTest) {
+ Options opts;
+ opts.env = env_;
+ opts.create_if_missing = true;
+ std::string dbname = dbname1_;
+
+ if (env_) {
+ if (fs_) {
+ ASSERT_EQ(fs_.get(), env_->GetFileSystem().get());
+ } else {
+ ASSERT_NE(FileSystem::Default().get(), env_->GetFileSystem().get());
+ }
+ }
+ for (int i = 0; i < 2; ++i) {
+ DB* db;
+ Status s = DB::Open(opts, dbname, &db);
+ ASSERT_OK(s);
+
+ WriteOptions wo;
+ ASSERT_OK(db->Put(wo, "a", "a"));
+ ASSERT_OK(db->Flush(FlushOptions()));
+ ASSERT_OK(db->Put(wo, "b", "b"));
+ ASSERT_OK(db->Flush(FlushOptions()));
+ ASSERT_OK(db->CompactRange(CompactRangeOptions(), nullptr, nullptr));
+
+ std::string val;
+ ASSERT_OK(db->Get(ReadOptions(), "a", &val));
+ ASSERT_EQ("a", val);
+ ASSERT_OK(db->Get(ReadOptions(), "b", &val));
+ ASSERT_EQ("b", val);
+
+ ASSERT_OK(db->Close());
+ delete db;
+ DestroyDB(dbname, opts);
+
+ dbname = dbname2_;
+ }
+}
+
+// The parameters are as follows -
+// 1. True means Options::env is non-null, false means null
+// 2. True means use Env::Default, false means custom
+// 3. True means use FileSystem::Default, false means custom
+INSTANTIATE_TEST_CASE_P(
+ EnvFSTest, EnvFSTestWithParam,
+ ::testing::Combine(::testing::Bool(), ::testing::Bool(),
+ ::testing::Bool()));
+// This test ensures that default Env and those allocated by
+// NewCompositeEnv() all share the same threadpool
+TEST_F(EnvTest, MultipleCompositeEnv) {
+ std::shared_ptr<FaultInjectionTestFS> fs1 =
+ std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
+ std::shared_ptr<FaultInjectionTestFS> fs2 =
+ std::make_shared<FaultInjectionTestFS>(FileSystem::Default());
+ std::unique_ptr<Env> env1 = NewCompositeEnv(fs1);
+ std::unique_ptr<Env> env2 = NewCompositeEnv(fs2);
+ Env::Default()->SetBackgroundThreads(8, Env::HIGH);
+ Env::Default()->SetBackgroundThreads(16, Env::LOW);
+ ASSERT_EQ(env1->GetBackgroundThreads(Env::LOW), 16);
+ ASSERT_EQ(env1->GetBackgroundThreads(Env::HIGH), 8);
+ ASSERT_EQ(env2->GetBackgroundThreads(Env::LOW), 16);
+ ASSERT_EQ(env2->GetBackgroundThreads(Env::HIGH), 8);
+}
+
+TEST_F(EnvTest, IsDirectory) {
+ Status s = Env::Default()->CreateDirIfMissing(test_directory_);
+ ASSERT_OK(s);
+ const std::string test_sub_dir = test_directory_ + "sub1";
+ const std::string test_file_path = test_directory_ + "file1";
+ ASSERT_OK(Env::Default()->CreateDirIfMissing(test_sub_dir));
+ bool is_dir = false;
+ ASSERT_OK(Env::Default()->IsDirectory(test_sub_dir, &is_dir));
+ ASSERT_TRUE(is_dir);
+ {
+ std::unique_ptr<FSWritableFile> wfile;
+ s = Env::Default()->GetFileSystem()->NewWritableFile(
+ test_file_path, FileOptions(), &wfile, /*dbg=*/nullptr);
+ ASSERT_OK(s);
+ std::unique_ptr<WritableFileWriter> fwriter;
+ fwriter.reset(new WritableFileWriter(std::move(wfile), test_file_path,
+ FileOptions(), Env::Default()));
+ constexpr char buf[] = "test";
+ s = fwriter->Append(buf);
+ ASSERT_OK(s);
+ }
+ ASSERT_OK(Env::Default()->IsDirectory(test_file_path, &is_dir));
+ ASSERT_FALSE(is_dir);
+}
+
} // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) {