515 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "src/__support/CPP/new.h"10#include "src/__support/File/file.h"11#include "src/__support/error_or.h"12#include "test/UnitTest/MemoryMatcher.h"13#include "test/UnitTest/Test.h"14 15#include "hdr/types/size_t.h"16 17using ModeFlags = LIBC_NAMESPACE::File::ModeFlags;18using MemoryView = LIBC_NAMESPACE::testing::MemoryView;19using LIBC_NAMESPACE::ErrorOr;20using LIBC_NAMESPACE::File;21using LIBC_NAMESPACE::FileIOResult;22 23class StringFile : public File {24 static constexpr size_t SIZE = 512;25 size_t pos;26 char str[SIZE] = {0};27 size_t eof_marker;28 bool write_append;29 30 static FileIOResult str_read(LIBC_NAMESPACE::File *f, void *data, size_t len);31 static FileIOResult str_write(LIBC_NAMESPACE::File *f, const void *data,32 size_t len);33 static ErrorOr<off_t> str_seek(LIBC_NAMESPACE::File *f, off_t offset,34 int whence);35 static int str_close(LIBC_NAMESPACE::File *f) {36 delete reinterpret_cast<StringFile *>(f);37 return 0;38 }39 40public:41 explicit StringFile(char *buffer, size_t buflen, int bufmode, bool owned,42 ModeFlags modeflags)43 : LIBC_NAMESPACE::File(&str_write, &str_read, &str_seek, &str_close,44 reinterpret_cast<uint8_t *>(buffer), buflen,45 bufmode, owned, modeflags),46 pos(0), eof_marker(0), write_append(false) {47 if (modeflags &48 static_cast<ModeFlags>(LIBC_NAMESPACE::File::OpenMode::APPEND))49 write_append = true;50 }51 52 void reset() { pos = 0; }53 size_t get_pos() const { return pos; }54 char *get_str() { return str; }55 56 // Use this method to prefill the file.57 void reset_and_fill(const char *data, size_t len) {58 size_t i;59 for (i = 0; i < len && i < SIZE; ++i) {60 str[i] = data[i];61 }62 pos = 0;63 eof_marker = i;64 }65};66 67FileIOResult StringFile::str_read(LIBC_NAMESPACE::File *f, void *data,68 size_t len) {69 StringFile *sf = static_cast<StringFile *>(f);70 if (sf->pos >= sf->eof_marker)71 return 0;72 size_t i = 0;73 for (i = 0; i < len; ++i)74 reinterpret_cast<char *>(data)[i] = sf->str[sf->pos + i];75 sf->pos += i;76 return i;77}78 79FileIOResult StringFile::str_write(LIBC_NAMESPACE::File *f, const void *data,80 size_t len) {81 StringFile *sf = static_cast<StringFile *>(f);82 if (sf->write_append)83 sf->pos = sf->eof_marker;84 if (sf->pos >= SIZE)85 return 0;86 size_t i = 0;87 for (i = 0; i < len && sf->pos < SIZE; ++i, ++sf->pos)88 sf->str[sf->pos] = reinterpret_cast<const char *>(data)[i];89 // Move the eof marker if the data was written beyond the current eof marker.90 if (sf->pos > sf->eof_marker)91 sf->eof_marker = sf->pos;92 return i;93}94 95ErrorOr<off_t> StringFile::str_seek(LIBC_NAMESPACE::File *f, off_t offset,96 int whence) {97 StringFile *sf = static_cast<StringFile *>(f);98 if (whence == SEEK_SET)99 sf->pos = offset;100 if (whence == SEEK_CUR)101 sf->pos += offset;102 if (whence == SEEK_END)103 sf->pos = SIZE + offset;104 return sf->pos;105}106 107StringFile *new_string_file(char *buffer, size_t buflen, int bufmode,108 bool owned, const char *mode) {109 LIBC_NAMESPACE::AllocChecker ac;110 // We will just assume the allocation succeeds. We cannot test anything111 // otherwise.112 return new (ac) StringFile(buffer, buflen, bufmode, owned,113 LIBC_NAMESPACE::File::mode_flags(mode));114}115 116TEST(LlvmLibcFileTest, WriteOnly) {117 const char data[] = "hello, file";118 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;119 char file_buffer[FILE_BUFFER_SIZE];120 StringFile *f =121 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w");122 123 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);124 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream125 ASSERT_EQ(f->flush(), 0);126 EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available127 EXPECT_STREQ(f->get_str(), data);128 129 f->reset();130 ASSERT_EQ(f->get_pos(), size_t(0));131 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);132 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream133 // The second write should trigger a buffer flush.134 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);135 EXPECT_GE(f->get_pos(), size_t(0));136 ASSERT_EQ(f->flush(), 0);137 EXPECT_EQ(f->get_pos(), 2 * sizeof(data));138 MemoryView src1("hello, file\0hello, file", sizeof(data) * 2),139 dst1(f->get_str(), sizeof(data) * 2);140 EXPECT_MEM_EQ(src1, dst1);141 142 char read_data[sizeof(data)];143 {144 // This is not a readable file.145 auto result = f->read(read_data, sizeof(data));146 EXPECT_EQ(result.value, size_t(0));147 EXPECT_TRUE(f->error());148 EXPECT_TRUE(result.has_error());149 }150 151 ASSERT_EQ(f->close(), 0);152}153 154TEST(LlvmLibcFileTest, WriteLineBuffered) {155 const char data[] = "hello\n file";156 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;157 158 char file_buffer_line[FILE_BUFFER_SIZE];159 char file_buffer_full[FILE_BUFFER_SIZE];160 161 StringFile *f_line =162 new_string_file(file_buffer_line, FILE_BUFFER_SIZE, _IOLBF, false, "w");163 // We also initialize a fully buffered file we'll do the same writes to for164 // comparison.165 StringFile *f_full =166 new_string_file(file_buffer_full, FILE_BUFFER_SIZE, _IOFBF, false, "w");167 168 ASSERT_EQ(sizeof(data), f_line->write(data, sizeof(data)).value);169 ASSERT_EQ(sizeof(data), f_full->write(data, sizeof(data)).value);170 171 EXPECT_EQ(f_line->get_pos(), size_t(6)); // buffer after the newline172 EXPECT_EQ(f_full->get_pos(), size_t(0)); // buffer all of data173 174 MemoryView src1("hello\n", 6), dst1(f_line->get_str(), 6);175 EXPECT_MEM_EQ(src1, dst1);176 177 // We can't check the data in f_full, since no data has been written.178 179 const char data2[] = "longer for an \n overflow";180 181 ASSERT_EQ(sizeof(data2), f_line->write(data2, sizeof(data2)).value);182 // The line buffer's initial contents should be " file\0"183 // Writing data2 should write up until the newline, even though that doesn't184 // all fit in the buffer.185 186 ASSERT_EQ(sizeof(data2), f_full->write(data2, sizeof(data2)).value);187 // The full buffer's initial contents should be "hello\n file\0"188 // Writing data2 should cause a flush of the buffer, as well as the remainder189 // to be written directly since it doesn't fit in the buffer.190 191 EXPECT_EQ(f_line->get_pos(), size_t(27));192 EXPECT_EQ(f_full->get_pos(), sizeof(data) + sizeof(data2));193 194 MemoryView src2("hello\n file\0longer for an \n", 27),195 dst2(f_line->get_str(), 27);196 EXPECT_MEM_EQ(src2, dst2);197 198 MemoryView src3("hello\n file\0longer for an \n overflow", 37),199 dst_full_final(f_full->get_str(), 37);200 EXPECT_MEM_EQ(src3, dst_full_final);201 202 ASSERT_EQ(f_line->flush(), 0);203 ASSERT_EQ(f_full->flush(), 0);204 205 EXPECT_EQ(f_line->get_pos(), sizeof(data) + sizeof(data2));206 MemoryView dst_line_final(f_line->get_str(), 37);207 EXPECT_MEM_EQ(src3, dst_line_final);208 EXPECT_MEM_EQ(src3, dst_full_final);209 210 ASSERT_EQ(f_line->close(), 0);211 ASSERT_EQ(f_full->close(), 0);212}213 214TEST(LlvmLibcFileTest, WriteUnbuffered) {215 const char data[] = "written immediately";216 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) + 1;217 char file_buffer[FILE_BUFFER_SIZE];218 StringFile *f =219 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IONBF, false, "w");220 221 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);222 EXPECT_EQ(f->get_pos(),223 sizeof(data)); // no buffering means this is written immediately.224 EXPECT_STREQ(f->get_str(), data);225 226 ASSERT_EQ(f->close(), 0);227}228 229TEST(LlvmLibcFileTest, ReadOnly) {230 const char initial_content[] = "1234567890987654321";231 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);232 char file_buffer[FILE_BUFFER_SIZE];233 StringFile *f =234 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r");235 f->reset_and_fill(initial_content, sizeof(initial_content));236 237 constexpr size_t READ_SIZE = sizeof(initial_content) / 2;238 char read_data[READ_SIZE];239 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);240 EXPECT_FALSE(f->iseof());241 // Reading less than file buffer worth will still read one242 // full buffer worth of data.243 EXPECT_STREQ(file_buffer, initial_content);244 EXPECT_STREQ(file_buffer, f->get_str());245 EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos());246 // The read data should match what was supposed to be read anyway.247 MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE);248 EXPECT_MEM_EQ(src1, dst1);249 250 // Reading another buffer worth should read out everything in251 // the file.252 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);253 EXPECT_FALSE(f->iseof());254 MemoryView src2(initial_content + READ_SIZE, READ_SIZE),255 dst2(read_data, READ_SIZE);256 EXPECT_MEM_EQ(src2, dst2);257 258 // Another read should trigger an EOF.259 ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE).value);260 EXPECT_TRUE(f->iseof());261 262 // Reset the pos to the beginning of the file which should allow263 // reading again.264 for (size_t i = 0; i < READ_SIZE; ++i)265 read_data[i] = 0;266 f->seek(0, SEEK_SET);267 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);268 MemoryView src3(initial_content, READ_SIZE), dst3(read_data, READ_SIZE);269 EXPECT_MEM_EQ(src3, dst3);270 271 {272 // This is not a writable file.273 auto result = f->write(initial_content, sizeof(initial_content));274 EXPECT_EQ(result.value, size_t(0));275 EXPECT_TRUE(f->error());276 EXPECT_TRUE(result.has_error());277 }278 279 ASSERT_EQ(f->close(), 0);280}281 282TEST(LlvmLibcFileTest, ReadSeekCurAndRead) {283 const char initial_content[] = "1234567890987654321";284 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);285 char file_buffer[FILE_BUFFER_SIZE];286 StringFile *f =287 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r");288 f->reset_and_fill(initial_content, sizeof(initial_content));289 290 constexpr size_t READ_SIZE = 5;291 char data[READ_SIZE];292 data[READ_SIZE - 1] = '\0';293 ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1);294 ASSERT_STREQ(data, "1234");295 ASSERT_EQ(f->seek(5, SEEK_CUR).value(), 0);296 ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1);297 ASSERT_STREQ(data, "0987");298 ASSERT_EQ(f->seek(-5, SEEK_CUR).value(), 0);299 ASSERT_EQ(f->read(data, READ_SIZE - 1).value, READ_SIZE - 1);300 ASSERT_STREQ(data, "9098");301 ASSERT_EQ(f->close(), 0);302}303 304TEST(LlvmLibcFileTest, AppendOnly) {305 const char initial_content[] = "1234567890987654321";306 const char write_data[] = "append";307 constexpr size_t FILE_BUFFER_SIZE = sizeof(write_data) * 3 / 2;308 char file_buffer[FILE_BUFFER_SIZE];309 StringFile *f =310 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a");311 f->reset_and_fill(initial_content, sizeof(initial_content));312 313 constexpr size_t READ_SIZE = 5;314 char read_data[READ_SIZE];315 316 {317 // This is not a readable file.318 auto result = f->read(read_data, READ_SIZE);319 EXPECT_EQ(result.value, size_t(0));320 EXPECT_TRUE(f->error());321 EXPECT_TRUE(result.has_error());322 }323 324 // Write should succeed but will be buffered in the file stream.325 ASSERT_EQ(f->write(write_data, sizeof(write_data)).value, sizeof(write_data));326 EXPECT_EQ(f->get_pos(), size_t(0));327 // Flushing will write to the file.328 EXPECT_EQ(f->flush(), int(0));329 EXPECT_EQ(f->get_pos(), sizeof(write_data) + sizeof(initial_content));330 331 ASSERT_EQ(f->close(), 0);332}333 334TEST(LlvmLibcFileTest, WriteUpdate) {335 const char data[] = "hello, file";336 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;337 char file_buffer[FILE_BUFFER_SIZE];338 StringFile *f =339 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w+");340 341 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);342 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream343 344 ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);345 346 // Seek flushes the stream buffer so we can read the previously written data.347 char read_data[sizeof(data)];348 ASSERT_EQ(f->read(read_data, sizeof(data)).value, sizeof(data));349 EXPECT_STREQ(read_data, data);350 351 ASSERT_EQ(f->close(), 0);352}353 354TEST(LlvmLibcFileTest, ReadUpdate) {355 const char initial_content[] = "1234567890987654321";356 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);357 char file_buffer[FILE_BUFFER_SIZE];358 StringFile *f =359 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r+");360 f->reset_and_fill(initial_content, sizeof(initial_content));361 362 constexpr size_t READ_SIZE = sizeof(initial_content) / 2;363 char read_data[READ_SIZE];364 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);365 EXPECT_FALSE(f->iseof());366 // Reading less than file buffer worth will still read one367 // full buffer worth of data.368 EXPECT_STREQ(file_buffer, initial_content);369 EXPECT_STREQ(file_buffer, f->get_str());370 EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos());371 // The read data should match what was supposed to be read anyway.372 MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE);373 EXPECT_MEM_EQ(src1, dst1);374 375 ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);376 const char write_data[] = "hello, file";377 ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data)).value);378 EXPECT_STREQ(file_buffer, write_data);379 ASSERT_EQ(f->flush(), 0);380 MemoryView dst2(f->get_str(), sizeof(write_data)),381 src2(write_data, sizeof(write_data));382 EXPECT_MEM_EQ(src2, dst2);383 384 ASSERT_EQ(f->close(), 0);385}386 387TEST(LlvmLibcFileTest, AppendUpdate) {388 const char initial_content[] = "1234567890987654321";389 const char data[] = "hello, file";390 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;391 char file_buffer[FILE_BUFFER_SIZE];392 StringFile *f =393 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a+");394 f->reset_and_fill(initial_content, sizeof(initial_content));395 396 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);397 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream398 ASSERT_EQ(f->flush(), 0);399 // The flush should write |data| to the endof the file.400 EXPECT_EQ(f->get_pos(), sizeof(data) + sizeof(initial_content));401 402 ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);403 // Seeking to the beginning of the file should not affect the place404 // where write happens.405 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)).value);406 ASSERT_EQ(f->flush(), 0);407 EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content));408 MemoryView src1(initial_content, sizeof(initial_content)),409 dst1(f->get_str(), sizeof(initial_content));410 EXPECT_MEM_EQ(src1, dst1);411 MemoryView src2(data, sizeof(data)),412 dst2(f->get_str() + sizeof(initial_content), sizeof(data));413 EXPECT_MEM_EQ(src2, dst2);414 MemoryView src3(data, sizeof(data)),415 dst3(f->get_str() + sizeof(initial_content) + sizeof(data), sizeof(data));416 EXPECT_MEM_EQ(src3, dst3);417 418 // Reads can happen from any point.419 ASSERT_EQ(f->seek(0, SEEK_SET).value(), 0);420 constexpr size_t READ_SIZE = 10;421 char read_data[READ_SIZE];422 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE).value);423 MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE);424 EXPECT_MEM_EQ(src4, dst4);425 426 ASSERT_EQ(f->close(), 0);427}428 429TEST(LlvmLibcFileTest, SmallBuffer) {430 const char WRITE_DATA[] = "small buffer";431 constexpr size_t WRITE_SIZE = sizeof(WRITE_DATA);432 constexpr size_t FILE_BUFFER_SIZE = sizeof(WRITE_DATA) / 2 - 1;433 char file_buffer[FILE_BUFFER_SIZE];434 StringFile *f =435 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w");436 437 ASSERT_EQ(WRITE_SIZE, f->write(WRITE_DATA, WRITE_SIZE).value);438 // Since data much larger than the buffer is being written, all of it should439 // be available in the file without a flush operation.440 EXPECT_EQ(f->get_pos(), sizeof(WRITE_DATA));441 ASSERT_STREQ(f->get_str(), WRITE_DATA);442 443 ASSERT_EQ(f->close(), 0);444}445 446TEST(LlvmLibcFileTest, ZeroLengthBuffer) {447 const char WRITE_DATA[] = "small buffer";448 constexpr size_t WRITE_SIZE = sizeof(WRITE_DATA);449 StringFile *f_fbf = new_string_file(nullptr, 0, _IOFBF, true, "w");450 StringFile *f_lbf = new_string_file(nullptr, 0, _IOLBF, true, "w");451 StringFile *f_nbf = new_string_file(nullptr, 0, _IONBF, true, "w");452 453 ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE).value);454 ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).value);455 ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).value);456 // Since there is no buffer space, all of the written data should457 // be available in the file without a flush operation.458 EXPECT_EQ(f_fbf->get_pos(), sizeof(WRITE_DATA));459 EXPECT_EQ(f_lbf->get_pos(), sizeof(WRITE_DATA));460 EXPECT_EQ(f_nbf->get_pos(), sizeof(WRITE_DATA));461 ASSERT_STREQ(f_fbf->get_str(), WRITE_DATA);462 ASSERT_STREQ(f_lbf->get_str(), WRITE_DATA);463 ASSERT_STREQ(f_nbf->get_str(), WRITE_DATA);464 465 ASSERT_EQ(f_fbf->close(), 0);466 ASSERT_EQ(f_lbf->close(), 0);467 ASSERT_EQ(f_nbf->close(), 0);468}469 470TEST(LlvmLibcFileTest, WriteNothing) {471 const char WRITE_DATA[] = "";472 constexpr size_t WRITE_SIZE = 0;473 constexpr size_t FILE_BUFFER_SIZE = 5;474 char file_buffer_fbf[FILE_BUFFER_SIZE];475 char file_buffer_lbf[FILE_BUFFER_SIZE];476 char file_buffer_nbf[FILE_BUFFER_SIZE];477 StringFile *f_fbf =478 new_string_file(file_buffer_fbf, FILE_BUFFER_SIZE, _IOFBF, false, "w");479 StringFile *f_lbf =480 new_string_file(file_buffer_lbf, FILE_BUFFER_SIZE, _IOLBF, false, "w");481 StringFile *f_nbf =482 new_string_file(file_buffer_nbf, FILE_BUFFER_SIZE, _IONBF, false, "w");483 484 ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE).value);485 ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE).value);486 ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE).value);487 488 ASSERT_FALSE(f_fbf->error_unlocked());489 ASSERT_FALSE(f_lbf->error_unlocked());490 ASSERT_FALSE(f_nbf->error_unlocked());491 492 ASSERT_EQ(f_fbf->close(), 0);493 ASSERT_EQ(f_lbf->close(), 0);494 ASSERT_EQ(f_nbf->close(), 0);495}496 497TEST(LlvmLibcFileTest, WriteSplit) {498 constexpr size_t FILE_BUFFER_SIZE = 8;499 char file_buffer[FILE_BUFFER_SIZE];500 StringFile *f =501 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w");502 503 static constexpr size_t AVAIL = 12;504 f->seek(-AVAIL, SEEK_END);505 506 const char data[] = "hello";507 ASSERT_EQ(sizeof(data) - 1, f->write(data, sizeof(data) - 1).value);508 509 const char data2[] = " extra data";510 static constexpr size_t WR_EXPECTED = AVAIL - (sizeof(data) - 1);511 ASSERT_EQ(WR_EXPECTED, f->write(data2, sizeof(data2) - 1).value);512 EXPECT_TRUE(f->error());513 ASSERT_EQ(f->close(), 0);514}515