1 //===-- Unittests for platform independent file class ---------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "src/__support/File/file.h" 10 #include "utils/UnitTest/MemoryMatcher.h" 11 #include "utils/UnitTest/Test.h" 12 13 #include <errno.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 17 using ModeFlags = __llvm_libc::File::ModeFlags; 18 using MemoryView = __llvm_libc::memory::testing::MemoryView; 19 20 class StringFile : public __llvm_libc::File { 21 static constexpr size_t SIZE = 512; 22 size_t pos; 23 char str[SIZE] = {0}; 24 size_t eof_marker; 25 bool write_append; 26 27 static size_t str_read(__llvm_libc::File *f, void *data, size_t len); 28 static size_t str_write(__llvm_libc::File *f, const void *data, size_t len); 29 static int str_seek(__llvm_libc::File *f, long offset, int whence); 30 static int str_close(__llvm_libc::File *f) { return 0; } 31 static int str_flush(__llvm_libc::File *f) { return 0; } 32 33 public: 34 explicit StringFile(char *buffer, size_t buflen, int bufmode, bool owned, 35 ModeFlags modeflags) 36 : __llvm_libc::File(&str_write, &str_read, &str_seek, &str_close, 37 &str_flush, buffer, buflen, bufmode, owned, 38 modeflags), 39 pos(0), eof_marker(0), write_append(false) { 40 if (modeflags & static_cast<ModeFlags>(__llvm_libc::File::OpenMode::APPEND)) 41 write_append = true; 42 } 43 44 void init(char *buffer, size_t buflen, int bufmode, bool owned, 45 ModeFlags modeflags) { 46 File::init(this, &str_write, &str_read, &str_seek, &str_close, &str_flush, 47 buffer, buflen, bufmode, owned, modeflags); 48 pos = eof_marker = 0; 49 if (modeflags & static_cast<ModeFlags>(__llvm_libc::File::OpenMode::APPEND)) 50 write_append = true; 51 else 52 write_append = false; 53 } 54 55 void reset() { pos = 0; } 56 size_t get_pos() const { return pos; } 57 char *get_str() { return str; } 58 59 // Use this method to prefill the file. 60 void reset_and_fill(const char *data, size_t len) { 61 size_t i; 62 for (i = 0; i < len && i < SIZE; ++i) { 63 str[i] = data[i]; 64 } 65 pos = 0; 66 eof_marker = i; 67 } 68 }; 69 70 size_t StringFile::str_read(__llvm_libc::File *f, void *data, size_t len) { 71 StringFile *sf = static_cast<StringFile *>(f); 72 if (sf->pos >= sf->eof_marker) 73 return 0; 74 size_t i = 0; 75 for (i = 0; i < len; ++i) 76 reinterpret_cast<char *>(data)[i] = sf->str[sf->pos + i]; 77 sf->pos += i; 78 return i; 79 } 80 81 size_t StringFile::str_write(__llvm_libc::File *f, const void *data, 82 size_t len) { 83 StringFile *sf = static_cast<StringFile *>(f); 84 if (sf->write_append) 85 sf->pos = sf->eof_marker; 86 if (sf->pos >= SIZE) 87 return 0; 88 size_t i = 0; 89 for (i = 0; i < len && sf->pos < SIZE; ++i, ++sf->pos) 90 sf->str[sf->pos] = reinterpret_cast<const char *>(data)[i]; 91 // Move the eof marker if the data was written beyond the current eof marker. 92 if (sf->pos > sf->eof_marker) 93 sf->eof_marker = sf->pos; 94 return i; 95 } 96 97 int StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) { 98 StringFile *sf = static_cast<StringFile *>(f); 99 if (whence == SEEK_SET) 100 sf->pos = offset; 101 if (whence == SEEK_CUR) 102 sf->pos += offset; 103 if (whence == SEEK_END) 104 sf->pos = SIZE + offset; 105 return 0; 106 } 107 108 StringFile *new_string_file(char *buffer, size_t buflen, int bufmode, 109 bool owned, const char *mode) { 110 StringFile *f = reinterpret_cast<StringFile *>(malloc(sizeof(StringFile))); 111 f->init(buffer, buflen, bufmode, owned, __llvm_libc::File::mode_flags(mode)); 112 return f; 113 } 114 115 TEST(LlvmLibcFileTest, WriteOnly) { 116 const char data[] = "hello, file"; 117 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2; 118 char file_buffer[FILE_BUFFER_SIZE]; 119 StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "w"); 120 121 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 122 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 123 ASSERT_EQ(f->flush(), 0); 124 EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available 125 EXPECT_STREQ(f->get_str(), data); 126 127 f->reset(); 128 ASSERT_EQ(f->get_pos(), size_t(0)); 129 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 130 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 131 // The second write should trigger a buffer flush. 132 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 133 EXPECT_GE(f->get_pos(), size_t(0)); 134 ASSERT_EQ(f->flush(), 0); 135 EXPECT_EQ(f->get_pos(), 2 * sizeof(data)); 136 137 char read_data[sizeof(data)]; 138 // This is not a readable file. 139 EXPECT_EQ(f->read(read_data, sizeof(data)), size_t(0)); 140 EXPECT_TRUE(f->error()); 141 EXPECT_NE(errno, 0); 142 errno = 0; 143 144 ASSERT_EQ(f->close(), 0); 145 } 146 147 TEST(LlvmLibcFileTest, ReadOnly) { 148 const char initial_content[] = "1234567890987654321"; 149 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content); 150 char file_buffer[FILE_BUFFER_SIZE]; 151 StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r"); 152 f->reset_and_fill(initial_content, sizeof(initial_content)); 153 154 constexpr size_t READ_SIZE = sizeof(initial_content) / 2; 155 char read_data[READ_SIZE]; 156 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 157 EXPECT_FALSE(f->iseof()); 158 // Reading less than file buffer worth will still read one 159 // full buffer worth of data. 160 EXPECT_STREQ(file_buffer, initial_content); 161 EXPECT_STREQ(file_buffer, f->get_str()); 162 EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos()); 163 // The read data should match what was supposed to be read anyway. 164 MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE); 165 EXPECT_MEM_EQ(src1, dst1); 166 167 // Reading another buffer worth should read out everything in 168 // the file. 169 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 170 EXPECT_FALSE(f->iseof()); 171 MemoryView src2(initial_content + READ_SIZE, READ_SIZE), 172 dst2(read_data, READ_SIZE); 173 EXPECT_MEM_EQ(src2, dst2); 174 175 // Another read should trigger an EOF. 176 ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE)); 177 EXPECT_TRUE(f->iseof()); 178 179 // Reset the pos to the beginning of the file which should allow 180 // reading again. 181 for (size_t i = 0; i < READ_SIZE; ++i) 182 read_data[i] = 0; 183 f->seek(0, SEEK_SET); 184 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 185 MemoryView src3(initial_content, READ_SIZE), dst3(read_data, READ_SIZE); 186 EXPECT_MEM_EQ(src3, dst3); 187 188 // This is not a writable file. 189 EXPECT_EQ(f->write(initial_content, sizeof(initial_content)), size_t(0)); 190 EXPECT_TRUE(f->error()); 191 EXPECT_NE(errno, 0); 192 errno = 0; 193 194 ASSERT_EQ(f->close(), 0); 195 } 196 197 TEST(LlvmLibcFileTest, ReadSeekCurAndRead) { 198 const char initial_content[] = "1234567890987654321"; 199 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content); 200 char file_buffer[FILE_BUFFER_SIZE]; 201 StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r"); 202 f->reset_and_fill(initial_content, sizeof(initial_content)); 203 204 constexpr size_t READ_SIZE = 5; 205 char data[READ_SIZE]; 206 data[READ_SIZE - 1] = '\0'; 207 ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); 208 ASSERT_STREQ(data, "1234"); 209 ASSERT_EQ(f->seek(5, SEEK_CUR), 0); 210 ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); 211 ASSERT_STREQ(data, "0987"); 212 ASSERT_EQ(f->seek(-5, SEEK_CUR), 0); 213 ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); 214 ASSERT_STREQ(data, "9098"); 215 ASSERT_EQ(f->close(), 0); 216 } 217 218 TEST(LlvmLibcFileTest, AppendOnly) { 219 const char initial_content[] = "1234567890987654321"; 220 const char write_data[] = "append"; 221 constexpr size_t FILE_BUFFER_SIZE = sizeof(write_data) * 3 / 2; 222 char file_buffer[FILE_BUFFER_SIZE]; 223 StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "a"); 224 f->reset_and_fill(initial_content, sizeof(initial_content)); 225 226 constexpr size_t READ_SIZE = 5; 227 char read_data[READ_SIZE]; 228 // This is not a readable file. 229 ASSERT_EQ(f->read(read_data, READ_SIZE), size_t(0)); 230 EXPECT_TRUE(f->error()); 231 EXPECT_NE(errno, 0); 232 errno = 0; 233 234 // Write should succeed but will be buffered in the file stream. 235 ASSERT_EQ(f->write(write_data, sizeof(write_data)), sizeof(write_data)); 236 EXPECT_EQ(f->get_pos(), size_t(0)); 237 // Flushing will write to the file. 238 EXPECT_EQ(f->flush(), int(0)); 239 EXPECT_EQ(f->get_pos(), sizeof(write_data) + sizeof(initial_content)); 240 241 ASSERT_EQ(f->close(), 0); 242 } 243 244 TEST(LlvmLibcFileTest, WriteUpdate) { 245 const char data[] = "hello, file"; 246 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2; 247 char file_buffer[FILE_BUFFER_SIZE]; 248 StringFile *f = 249 new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "w+"); 250 251 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 252 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 253 254 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 255 256 // Seek flushes the stream buffer so we can read the previously written data. 257 char read_data[sizeof(data)]; 258 ASSERT_EQ(f->read(read_data, sizeof(data)), sizeof(data)); 259 EXPECT_STREQ(read_data, data); 260 261 ASSERT_EQ(f->close(), 0); 262 } 263 264 TEST(LlvmLibcFileTest, ReadUpdate) { 265 const char initial_content[] = "1234567890987654321"; 266 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content); 267 char file_buffer[FILE_BUFFER_SIZE]; 268 StringFile *f = 269 new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r+"); 270 f->reset_and_fill(initial_content, sizeof(initial_content)); 271 272 constexpr size_t READ_SIZE = sizeof(initial_content) / 2; 273 char read_data[READ_SIZE]; 274 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 275 EXPECT_FALSE(f->iseof()); 276 // Reading less than file buffer worth will still read one 277 // full buffer worth of data. 278 EXPECT_STREQ(file_buffer, initial_content); 279 EXPECT_STREQ(file_buffer, f->get_str()); 280 EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos()); 281 // The read data should match what was supposed to be read anyway. 282 MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE); 283 EXPECT_MEM_EQ(src1, dst1); 284 285 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 286 const char write_data[] = "hello, file"; 287 ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data))); 288 EXPECT_STREQ(file_buffer, write_data); 289 ASSERT_EQ(f->flush(), 0); 290 MemoryView dst2(f->get_str(), sizeof(write_data)), 291 src2(write_data, sizeof(write_data)); 292 EXPECT_MEM_EQ(src2, dst2); 293 294 ASSERT_EQ(f->close(), 0); 295 } 296 297 TEST(LlvmLibcFileTest, AppendUpdate) { 298 const char initial_content[] = "1234567890987654321"; 299 const char data[] = "hello, file"; 300 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2; 301 char file_buffer[FILE_BUFFER_SIZE]; 302 StringFile *f = 303 new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "a+"); 304 f->reset_and_fill(initial_content, sizeof(initial_content)); 305 306 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 307 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 308 ASSERT_EQ(f->flush(), 0); 309 // The flush should write |data| to the endof the file. 310 EXPECT_EQ(f->get_pos(), sizeof(data) + sizeof(initial_content)); 311 312 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 313 // Seeking to the beginning of the file should not affect the place 314 // where write happens. 315 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 316 ASSERT_EQ(f->flush(), 0); 317 EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content)); 318 MemoryView src1(initial_content, sizeof(initial_content)), 319 dst1(f->get_str(), sizeof(initial_content)); 320 EXPECT_MEM_EQ(src1, dst1); 321 MemoryView src2(data, sizeof(data)), 322 dst2(f->get_str() + sizeof(initial_content), sizeof(data)); 323 EXPECT_MEM_EQ(src2, dst2); 324 MemoryView src3(data, sizeof(data)), 325 dst3(f->get_str() + sizeof(initial_content) + sizeof(data), sizeof(data)); 326 EXPECT_MEM_EQ(src3, dst3); 327 328 // Reads can happen from any point. 329 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 330 constexpr size_t READ_SIZE = 10; 331 char read_data[READ_SIZE]; 332 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 333 MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE); 334 EXPECT_MEM_EQ(src4, dst4); 335 336 ASSERT_EQ(f->close(), 0); 337 } 338