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