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/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 17 using ModeFlags = LIBC_NAMESPACE::File::ModeFlags; 18 using MemoryView = LIBC_NAMESPACE::testing::MemoryView; 19 using LIBC_NAMESPACE::ErrorOr; 20 using LIBC_NAMESPACE::File; 21 using LIBC_NAMESPACE::FileIOResult; 22 23 class 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 40 public: 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 67 FileIOResult 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 79 FileIOResult 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 95 ErrorOr<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 107 StringFile *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 anything 111 // otherwise. 112 return new (ac) StringFile(buffer, buflen, bufmode, owned, 113 LIBC_NAMESPACE::File::mode_flags(mode)); 114 } 115 116 TEST(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 stream 125 ASSERT_EQ(f->flush(), 0); 126 EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available 127 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 stream 133 // 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 154 TEST(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 for 164 // 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 newline 172 EXPECT_EQ(f_full->get_pos(), size_t(0)); // buffer all of data 173 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't 184 // 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 remainder 189 // 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 214 TEST(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 229 TEST(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 one 242 // 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 in 251 // 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 allow 263 // 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 282 TEST(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 304 TEST(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 334 TEST(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 stream 343 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 354 TEST(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 one 367 // 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 387 TEST(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 stream 398 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 place 404 // 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 429 TEST(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 should 439 // 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 446 TEST(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 should 457 // 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 470 TEST(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