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