| 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 | |