| 1 | #include "catch.hpp" | 
|---|
| 2 | #include "duckdb/common/file_buffer.hpp" | 
|---|
| 3 | #include "duckdb/common/file_system.hpp" | 
|---|
| 4 | #include "duckdb/common/fstream.hpp" | 
|---|
| 5 | #include "test_helpers.hpp" | 
|---|
| 6 |  | 
|---|
| 7 | using namespace duckdb; | 
|---|
| 8 | using namespace std; | 
|---|
| 9 |  | 
|---|
| 10 | static void create_dummy_file(string fname) { | 
|---|
| 11 | ofstream outfile(fname); | 
|---|
| 12 | outfile << "I_AM_A_DUMMY"<< endl; | 
|---|
| 13 | outfile.close(); | 
|---|
| 14 | } | 
|---|
| 15 |  | 
|---|
| 16 | TEST_CASE( "Make sure file system operators work as advertised", "[file_system]") { | 
|---|
| 17 | FileSystem fs; | 
|---|
| 18 | auto dname = TestCreatePath( "TEST_DIR"); | 
|---|
| 19 | string fname = "TEST_FILE"; | 
|---|
| 20 | string fname2 = "TEST_FILE_TWO"; | 
|---|
| 21 |  | 
|---|
| 22 | if (fs.DirectoryExists(dname)) { | 
|---|
| 23 | fs.RemoveDirectory(dname); | 
|---|
| 24 | } | 
|---|
| 25 |  | 
|---|
| 26 | fs.CreateDirectory(dname); | 
|---|
| 27 | REQUIRE(fs.DirectoryExists(dname)); | 
|---|
| 28 | REQUIRE(!fs.FileExists(dname)); | 
|---|
| 29 |  | 
|---|
| 30 | // we can call this again and nothing happens | 
|---|
| 31 | fs.CreateDirectory(dname); | 
|---|
| 32 |  | 
|---|
| 33 | auto fname_in_dir = fs.JoinPath(dname, fname); | 
|---|
| 34 | auto fname_in_dir2 = fs.JoinPath(dname, fname2); | 
|---|
| 35 |  | 
|---|
| 36 | create_dummy_file(fname_in_dir); | 
|---|
| 37 | REQUIRE(fs.FileExists(fname_in_dir)); | 
|---|
| 38 | REQUIRE(!fs.DirectoryExists(fname_in_dir)); | 
|---|
| 39 |  | 
|---|
| 40 | size_t n_files = 0; | 
|---|
| 41 | REQUIRE(fs.ListFiles(dname, [&n_files](const string &path) { n_files++; })); | 
|---|
| 42 |  | 
|---|
| 43 | REQUIRE(n_files == 1); | 
|---|
| 44 |  | 
|---|
| 45 | REQUIRE(fs.FileExists(fname_in_dir)); | 
|---|
| 46 | REQUIRE(!fs.FileExists(fname_in_dir2)); | 
|---|
| 47 |  | 
|---|
| 48 | fs.MoveFile(fname_in_dir, fname_in_dir2); | 
|---|
| 49 |  | 
|---|
| 50 | REQUIRE(!fs.FileExists(fname_in_dir)); | 
|---|
| 51 | REQUIRE(fs.FileExists(fname_in_dir2)); | 
|---|
| 52 |  | 
|---|
| 53 | fs.RemoveDirectory(dname); | 
|---|
| 54 |  | 
|---|
| 55 | REQUIRE(!fs.DirectoryExists(dname)); | 
|---|
| 56 | REQUIRE(!fs.FileExists(fname_in_dir)); | 
|---|
| 57 | REQUIRE(!fs.FileExists(fname_in_dir2)); | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | // note: the integer count is chosen as 512 so that we write 512*8=4096 bytes to the file | 
|---|
| 61 | // this is required for the Direct-IO as on Windows Direct-IO can only write multiples of sector sizes | 
|---|
| 62 | // sector sizes are typically one of [512/1024/2048/4096] bytes, hence a 4096 bytes write succeeds. | 
|---|
| 63 | #define INTEGER_COUNT 512 | 
|---|
| 64 |  | 
|---|
| 65 | TEST_CASE( "Test file operations", "[file_system]") { | 
|---|
| 66 | FileSystem fs; | 
|---|
| 67 | unique_ptr<FileHandle> handle, handle2; | 
|---|
| 68 | int64_t test_data[INTEGER_COUNT]; | 
|---|
| 69 | for (int i = 0; i < INTEGER_COUNT; i++) { | 
|---|
| 70 | test_data[i] = i; | 
|---|
| 71 | } | 
|---|
| 72 |  | 
|---|
| 73 | auto fname = TestCreatePath( "test_file"); | 
|---|
| 74 |  | 
|---|
| 75 | // standard reading/writing test | 
|---|
| 76 |  | 
|---|
| 77 | // open file for writing | 
|---|
| 78 | REQUIRE_NOTHROW(handle = fs.OpenFile(fname, FileFlags::WRITE | FileFlags::CREATE, FileLockType::NO_LOCK)); | 
|---|
| 79 | // write 10 integers | 
|---|
| 80 | REQUIRE_NOTHROW(handle->Write((void *)test_data, sizeof(int64_t) * INTEGER_COUNT, 0)); | 
|---|
| 81 | // close the file | 
|---|
| 82 | handle.reset(); | 
|---|
| 83 |  | 
|---|
| 84 | for (int i = 0; i < INTEGER_COUNT; i++) { | 
|---|
| 85 | test_data[i] = 0; | 
|---|
| 86 | } | 
|---|
| 87 | // now open the file for reading | 
|---|
| 88 | REQUIRE_NOTHROW(handle = fs.OpenFile(fname, FileFlags::READ, FileLockType::NO_LOCK)); | 
|---|
| 89 | // read the 10 integers back | 
|---|
| 90 | REQUIRE_NOTHROW(handle->Read((void *)test_data, sizeof(int64_t) * INTEGER_COUNT, 0)); | 
|---|
| 91 | // check the values of the integers | 
|---|
| 92 | for (int i = 0; i < 10; i++) { | 
|---|
| 93 | REQUIRE(test_data[i] == i); | 
|---|
| 94 | } | 
|---|
| 95 | handle.reset(); | 
|---|
| 96 | fs.RemoveFile(fname); | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | TEST_CASE( "Test file buffers for reading/writing to file", "[file_system]") { | 
|---|
| 100 | FileSystem fs; | 
|---|
| 101 | unique_ptr<FileHandle> handle; | 
|---|
| 102 |  | 
|---|
| 103 | auto fname = TestCreatePath( "test_file"); | 
|---|
| 104 |  | 
|---|
| 105 | // create the buffer and fill it with data | 
|---|
| 106 | auto buf = make_unique<FileBuffer>(FileBufferType::MANAGED_BUFFER, 4096); | 
|---|
| 107 | int64_t *ptr = (int64_t *)buf->buffer; | 
|---|
| 108 | for (size_t i = 0; i < 10; i++) { | 
|---|
| 109 | ptr[i] = i; | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | // open file for writing | 
|---|
| 113 | REQUIRE_NOTHROW(handle = fs.OpenFile(fname, FileFlags::WRITE | FileFlags::CREATE | FileFlags::DIRECT_IO, | 
|---|
| 114 | FileLockType::WRITE_LOCK)); | 
|---|
| 115 | // write the buffer | 
|---|
| 116 | REQUIRE_NOTHROW(buf->Write(*handle, 0)); | 
|---|
| 117 | // clear the buffer | 
|---|
| 118 | buf->Clear(); | 
|---|
| 119 | // now read data back into the buffer | 
|---|
| 120 | REQUIRE_NOTHROW(buf->Read(*handle, 0)); | 
|---|
| 121 | for (size_t i = 0; i < 10; i++) { | 
|---|
| 122 | REQUIRE(ptr[i] == i); | 
|---|
| 123 | } | 
|---|
| 124 | // close the file | 
|---|
| 125 | handle.reset(); | 
|---|
| 126 |  | 
|---|
| 127 | fs.RemoveFile(fname); | 
|---|
| 128 | } | 
|---|
| 129 |  | 
|---|