1 // Copyright (c) 2011 The LevelDB 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. See the AUTHORS file for names of contributors. 4 // 5 // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. 6 7 #include <memory> 8 #include <string> 9 #include <vector> 10 #include <algorithm> 11 12 #include "env/mock_env.h" 13 #include "rocksdb/env.h" 14 #include "test_util/testharness.h" 15 16 namespace ROCKSDB_NAMESPACE { 17 18 // Normalizes trivial differences across Envs such that these test cases can 19 // run on all Envs. 20 class NormalizingEnvWrapper : public EnvWrapper { 21 public: 22 explicit NormalizingEnvWrapper(Env* base) : EnvWrapper(base) {} 23 24 // Removes . and .. from directory listing 25 Status GetChildren(const std::string& dir, 26 std::vector<std::string>* result) override { 27 Status status = EnvWrapper::GetChildren(dir, result); 28 if (status.ok()) { 29 result->erase(std::remove_if(result->begin(), result->end(), 30 [](const std::string& s) { 31 return s == "." || s == ".."; 32 }), 33 result->end()); 34 } 35 return status; 36 } 37 38 // Removes . and .. from directory listing 39 Status GetChildrenFileAttributes( 40 const std::string& dir, std::vector<FileAttributes>* result) override { 41 Status status = EnvWrapper::GetChildrenFileAttributes(dir, result); 42 if (status.ok()) { 43 result->erase(std::remove_if(result->begin(), result->end(), 44 [](const FileAttributes& fa) { 45 return fa.name == "." || fa.name == ".."; 46 }), 47 result->end()); 48 } 49 return status; 50 } 51 }; 52 53 class EnvBasicTestWithParam : public testing::Test, 54 public ::testing::WithParamInterface<Env*> { 55 public: 56 Env* env_; 57 const EnvOptions soptions_; 58 std::string test_dir_; 59 60 EnvBasicTestWithParam() : env_(GetParam()) { 61 test_dir_ = test::PerThreadDBPath(env_, "env_basic_test"); 62 } 63 64 void SetUp() override { env_->CreateDirIfMissing(test_dir_); } 65 66 void TearDown() override { 67 std::vector<std::string> files; 68 env_->GetChildren(test_dir_, &files); 69 for (const auto& file : files) { 70 // don't know whether it's file or directory, try both. The tests must 71 // only create files or empty directories, so one must succeed, else the 72 // directory's corrupted. 73 Status s = env_->DeleteFile(test_dir_ + "/" + file); 74 if (!s.ok()) { 75 ASSERT_OK(env_->DeleteDir(test_dir_ + "/" + file)); 76 } 77 } 78 } 79 }; 80 81 class EnvMoreTestWithParam : public EnvBasicTestWithParam {}; 82 83 static std::unique_ptr<Env> def_env(new NormalizingEnvWrapper(Env::Default())); 84 INSTANTIATE_TEST_CASE_P(EnvDefault, EnvBasicTestWithParam, 85 ::testing::Values(def_env.get())); 86 INSTANTIATE_TEST_CASE_P(EnvDefault, EnvMoreTestWithParam, 87 ::testing::Values(def_env.get())); 88 89 static std::unique_ptr<Env> mock_env(new MockEnv(Env::Default())); 90 INSTANTIATE_TEST_CASE_P(MockEnv, EnvBasicTestWithParam, 91 ::testing::Values(mock_env.get())); 92 #ifndef ROCKSDB_LITE 93 static std::unique_ptr<Env> mem_env(NewMemEnv(Env::Default())); 94 INSTANTIATE_TEST_CASE_P(MemEnv, EnvBasicTestWithParam, 95 ::testing::Values(mem_env.get())); 96 97 namespace { 98 99 // Returns a vector of 0 or 1 Env*, depending whether an Env is registered for 100 // TEST_ENV_URI. 101 // 102 // The purpose of returning an empty vector (instead of nullptr) is that gtest 103 // ValuesIn() will skip running tests when given an empty collection. 104 std::vector<Env*> GetCustomEnvs() { 105 static Env* custom_env; 106 static bool init = false; 107 if (!init) { 108 init = true; 109 const char* uri = getenv("TEST_ENV_URI"); 110 if (uri != nullptr) { 111 Env::LoadEnv(uri, &custom_env); 112 } 113 } 114 115 std::vector<Env*> res; 116 if (custom_env != nullptr) { 117 res.emplace_back(custom_env); 118 } 119 return res; 120 } 121 122 } // anonymous namespace 123 124 INSTANTIATE_TEST_CASE_P(CustomEnv, EnvBasicTestWithParam, 125 ::testing::ValuesIn(GetCustomEnvs())); 126 127 INSTANTIATE_TEST_CASE_P(CustomEnv, EnvMoreTestWithParam, 128 ::testing::ValuesIn(GetCustomEnvs())); 129 130 #endif // ROCKSDB_LITE 131 132 TEST_P(EnvBasicTestWithParam, Basics) { 133 uint64_t file_size; 134 std::unique_ptr<WritableFile> writable_file; 135 std::vector<std::string> children; 136 137 // Check that the directory is empty. 138 ASSERT_EQ(Status::NotFound(), env_->FileExists(test_dir_ + "/non_existent")); 139 ASSERT_TRUE(!env_->GetFileSize(test_dir_ + "/non_existent", &file_size).ok()); 140 ASSERT_OK(env_->GetChildren(test_dir_, &children)); 141 ASSERT_EQ(0U, children.size()); 142 143 // Create a file. 144 ASSERT_OK(env_->NewWritableFile(test_dir_ + "/f", &writable_file, soptions_)); 145 ASSERT_OK(writable_file->Close()); 146 writable_file.reset(); 147 148 // Check that the file exists. 149 ASSERT_OK(env_->FileExists(test_dir_ + "/f")); 150 ASSERT_OK(env_->GetFileSize(test_dir_ + "/f", &file_size)); 151 ASSERT_EQ(0U, file_size); 152 ASSERT_OK(env_->GetChildren(test_dir_, &children)); 153 ASSERT_EQ(1U, children.size()); 154 ASSERT_EQ("f", children[0]); 155 ASSERT_OK(env_->DeleteFile(test_dir_ + "/f")); 156 157 // Write to the file. 158 ASSERT_OK( 159 env_->NewWritableFile(test_dir_ + "/f1", &writable_file, soptions_)); 160 ASSERT_OK(writable_file->Append("abc")); 161 ASSERT_OK(writable_file->Close()); 162 writable_file.reset(); 163 ASSERT_OK( 164 env_->NewWritableFile(test_dir_ + "/f2", &writable_file, soptions_)); 165 ASSERT_OK(writable_file->Close()); 166 writable_file.reset(); 167 168 // Check for expected size. 169 ASSERT_OK(env_->GetFileSize(test_dir_ + "/f1", &file_size)); 170 ASSERT_EQ(3U, file_size); 171 172 // Check that renaming works. 173 ASSERT_TRUE( 174 !env_->RenameFile(test_dir_ + "/non_existent", test_dir_ + "/g").ok()); 175 ASSERT_OK(env_->RenameFile(test_dir_ + "/f1", test_dir_ + "/g")); 176 ASSERT_EQ(Status::NotFound(), env_->FileExists(test_dir_ + "/f1")); 177 ASSERT_OK(env_->FileExists(test_dir_ + "/g")); 178 ASSERT_OK(env_->GetFileSize(test_dir_ + "/g", &file_size)); 179 ASSERT_EQ(3U, file_size); 180 181 // Check that renaming overwriting works 182 ASSERT_OK(env_->RenameFile(test_dir_ + "/f2", test_dir_ + "/g")); 183 ASSERT_OK(env_->GetFileSize(test_dir_ + "/g", &file_size)); 184 ASSERT_EQ(0U, file_size); 185 186 // Check that opening non-existent file fails. 187 std::unique_ptr<SequentialFile> seq_file; 188 std::unique_ptr<RandomAccessFile> rand_file; 189 ASSERT_TRUE(!env_->NewSequentialFile(test_dir_ + "/non_existent", &seq_file, 190 soptions_) 191 .ok()); 192 ASSERT_TRUE(!seq_file); 193 ASSERT_TRUE(!env_->NewRandomAccessFile(test_dir_ + "/non_existent", 194 &rand_file, soptions_) 195 .ok()); 196 ASSERT_TRUE(!rand_file); 197 198 // Check that deleting works. 199 ASSERT_TRUE(!env_->DeleteFile(test_dir_ + "/non_existent").ok()); 200 ASSERT_OK(env_->DeleteFile(test_dir_ + "/g")); 201 ASSERT_EQ(Status::NotFound(), env_->FileExists(test_dir_ + "/g")); 202 ASSERT_OK(env_->GetChildren(test_dir_, &children)); 203 ASSERT_EQ(0U, children.size()); 204 ASSERT_TRUE( 205 env_->GetChildren(test_dir_ + "/non_existent", &children).IsNotFound()); 206 } 207 208 TEST_P(EnvBasicTestWithParam, ReadWrite) { 209 std::unique_ptr<WritableFile> writable_file; 210 std::unique_ptr<SequentialFile> seq_file; 211 std::unique_ptr<RandomAccessFile> rand_file; 212 Slice result; 213 char scratch[100]; 214 215 ASSERT_OK(env_->NewWritableFile(test_dir_ + "/f", &writable_file, soptions_)); 216 ASSERT_OK(writable_file->Append("hello ")); 217 ASSERT_OK(writable_file->Append("world")); 218 ASSERT_OK(writable_file->Close()); 219 writable_file.reset(); 220 221 // Read sequentially. 222 ASSERT_OK(env_->NewSequentialFile(test_dir_ + "/f", &seq_file, soptions_)); 223 ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". 224 ASSERT_EQ(0, result.compare("hello")); 225 ASSERT_OK(seq_file->Skip(1)); 226 ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". 227 ASSERT_EQ(0, result.compare("world")); 228 ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. 229 ASSERT_EQ(0U, result.size()); 230 ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. 231 ASSERT_OK(seq_file->Read(1000, &result, scratch)); 232 ASSERT_EQ(0U, result.size()); 233 234 // Random reads. 235 ASSERT_OK(env_->NewRandomAccessFile(test_dir_ + "/f", &rand_file, soptions_)); 236 ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". 237 ASSERT_EQ(0, result.compare("world")); 238 ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". 239 ASSERT_EQ(0, result.compare("hello")); 240 ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". 241 ASSERT_EQ(0, result.compare("d")); 242 243 // Too high offset. 244 ASSERT_TRUE(rand_file->Read(1000, 5, &result, scratch).ok()); 245 } 246 247 TEST_P(EnvBasicTestWithParam, Misc) { 248 std::unique_ptr<WritableFile> writable_file; 249 ASSERT_OK(env_->NewWritableFile(test_dir_ + "/b", &writable_file, soptions_)); 250 251 // These are no-ops, but we test they return success. 252 ASSERT_OK(writable_file->Sync()); 253 ASSERT_OK(writable_file->Flush()); 254 ASSERT_OK(writable_file->Close()); 255 writable_file.reset(); 256 } 257 258 TEST_P(EnvBasicTestWithParam, LargeWrite) { 259 const size_t kWriteSize = 300 * 1024; 260 char* scratch = new char[kWriteSize * 2]; 261 262 std::string write_data; 263 for (size_t i = 0; i < kWriteSize; ++i) { 264 write_data.append(1, static_cast<char>(i)); 265 } 266 267 std::unique_ptr<WritableFile> writable_file; 268 ASSERT_OK(env_->NewWritableFile(test_dir_ + "/f", &writable_file, soptions_)); 269 ASSERT_OK(writable_file->Append("foo")); 270 ASSERT_OK(writable_file->Append(write_data)); 271 ASSERT_OK(writable_file->Close()); 272 writable_file.reset(); 273 274 std::unique_ptr<SequentialFile> seq_file; 275 Slice result; 276 ASSERT_OK(env_->NewSequentialFile(test_dir_ + "/f", &seq_file, soptions_)); 277 ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo". 278 ASSERT_EQ(0, result.compare("foo")); 279 280 size_t read = 0; 281 std::string read_data; 282 while (read < kWriteSize) { 283 ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch)); 284 read_data.append(result.data(), result.size()); 285 read += result.size(); 286 } 287 ASSERT_TRUE(write_data == read_data); 288 delete [] scratch; 289 } 290 291 TEST_P(EnvMoreTestWithParam, GetModTime) { 292 ASSERT_OK(env_->CreateDirIfMissing(test_dir_ + "/dir1")); 293 uint64_t mtime1 = 0x0; 294 ASSERT_OK(env_->GetFileModificationTime(test_dir_ + "/dir1", &mtime1)); 295 } 296 297 TEST_P(EnvMoreTestWithParam, MakeDir) { 298 ASSERT_OK(env_->CreateDir(test_dir_ + "/j")); 299 ASSERT_OK(env_->FileExists(test_dir_ + "/j")); 300 std::vector<std::string> children; 301 env_->GetChildren(test_dir_, &children); 302 ASSERT_EQ(1U, children.size()); 303 // fail because file already exists 304 ASSERT_TRUE(!env_->CreateDir(test_dir_ + "/j").ok()); 305 ASSERT_OK(env_->CreateDirIfMissing(test_dir_ + "/j")); 306 ASSERT_OK(env_->DeleteDir(test_dir_ + "/j")); 307 ASSERT_EQ(Status::NotFound(), env_->FileExists(test_dir_ + "/j")); 308 } 309 310 TEST_P(EnvMoreTestWithParam, GetChildren) { 311 // empty folder returns empty vector 312 std::vector<std::string> children; 313 std::vector<Env::FileAttributes> childAttr; 314 ASSERT_OK(env_->CreateDirIfMissing(test_dir_)); 315 ASSERT_OK(env_->GetChildren(test_dir_, &children)); 316 ASSERT_OK(env_->FileExists(test_dir_)); 317 ASSERT_OK(env_->GetChildrenFileAttributes(test_dir_, &childAttr)); 318 ASSERT_EQ(0U, children.size()); 319 ASSERT_EQ(0U, childAttr.size()); 320 321 // folder with contents returns relative path to test dir 322 ASSERT_OK(env_->CreateDirIfMissing(test_dir_ + "/niu")); 323 ASSERT_OK(env_->CreateDirIfMissing(test_dir_ + "/you")); 324 ASSERT_OK(env_->CreateDirIfMissing(test_dir_ + "/guo")); 325 ASSERT_OK(env_->GetChildren(test_dir_, &children)); 326 ASSERT_OK(env_->GetChildrenFileAttributes(test_dir_, &childAttr)); 327 ASSERT_EQ(3U, children.size()); 328 ASSERT_EQ(3U, childAttr.size()); 329 for (auto each : children) { 330 env_->DeleteDir(test_dir_ + "/" + each); 331 } // necessary for default POSIX env 332 333 // non-exist directory returns IOError 334 ASSERT_OK(env_->DeleteDir(test_dir_)); 335 ASSERT_TRUE(!env_->FileExists(test_dir_).ok()); 336 ASSERT_TRUE(!env_->GetChildren(test_dir_, &children).ok()); 337 ASSERT_TRUE(!env_->GetChildrenFileAttributes(test_dir_, &childAttr).ok()); 338 339 // if dir is a file, returns IOError 340 ASSERT_OK(env_->CreateDir(test_dir_)); 341 std::unique_ptr<WritableFile> writable_file; 342 ASSERT_OK( 343 env_->NewWritableFile(test_dir_ + "/file", &writable_file, soptions_)); 344 ASSERT_OK(writable_file->Close()); 345 writable_file.reset(); 346 ASSERT_TRUE(!env_->GetChildren(test_dir_ + "/file", &children).ok()); 347 ASSERT_EQ(0U, children.size()); 348 } 349 350 } // namespace ROCKSDB_NAMESPACE 351 int main(int argc, char** argv) { 352 ::testing::InitGoogleTest(&argc, argv); 353 return RUN_ALL_TESTS(); 354 } 355