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 = 120 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w"); 121 122 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 123 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 124 ASSERT_EQ(f->flush(), 0); 125 EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available 126 EXPECT_STREQ(f->get_str(), data); 127 128 f->reset(); 129 ASSERT_EQ(f->get_pos(), size_t(0)); 130 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 131 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 132 // The second write should trigger a buffer flush. 133 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 134 EXPECT_GE(f->get_pos(), size_t(0)); 135 ASSERT_EQ(f->flush(), 0); 136 EXPECT_EQ(f->get_pos(), 2 * sizeof(data)); 137 MemoryView src1("hello, file\0hello, file", sizeof(data) * 2), 138 dst1(f->get_str(), sizeof(data) * 2); 139 EXPECT_MEM_EQ(src1, dst1); 140 141 char read_data[sizeof(data)]; 142 // This is not a readable file. 143 EXPECT_EQ(f->read(read_data, sizeof(data)), size_t(0)); 144 EXPECT_TRUE(f->error()); 145 EXPECT_NE(errno, 0); 146 errno = 0; 147 148 ASSERT_EQ(f->close(), 0); 149 } 150 151 TEST(LlvmLibcFileTest, WriteLineBuffered) { 152 const char data[] = "hello\n file"; 153 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2; 154 155 char file_buffer_line[FILE_BUFFER_SIZE]; 156 char file_buffer_full[FILE_BUFFER_SIZE]; 157 158 StringFile *f_line = 159 new_string_file(file_buffer_line, FILE_BUFFER_SIZE, _IOLBF, false, "w"); 160 // We also initialize a fully buffered file we'll do the same writes to for 161 // comparison. 162 StringFile *f_full = 163 new_string_file(file_buffer_full, FILE_BUFFER_SIZE, _IOFBF, false, "w"); 164 165 ASSERT_EQ(sizeof(data), f_line->write(data, sizeof(data))); 166 ASSERT_EQ(sizeof(data), f_full->write(data, sizeof(data))); 167 168 EXPECT_EQ(f_line->get_pos(), size_t(6)); // buffer after the newline 169 EXPECT_EQ(f_full->get_pos(), size_t(0)); // buffer all of data 170 171 MemoryView src1("hello\n", 6), dst1(f_line->get_str(), 6); 172 EXPECT_MEM_EQ(src1, dst1); 173 174 // We can't check the data in f_full, since no data has been written. 175 176 const char data2[] = "longer for an \n overflow"; 177 178 ASSERT_EQ(sizeof(data2), f_line->write(data2, sizeof(data2))); 179 // The line buffer's initial contents should be " file\0" 180 // Writing data2 should write up until the newline, even though that doesn't 181 // all fit in the buffer. 182 183 ASSERT_EQ(sizeof(data2), f_full->write(data2, sizeof(data2))); 184 // The full buffer's initial contents should be "hello\n file\0" 185 // Writing data2 should cause a flush of the buffer, as well as the remainder 186 // to be written directly since it doesn't fit in the buffer. 187 188 EXPECT_EQ(f_line->get_pos(), size_t(27)); 189 EXPECT_EQ(f_full->get_pos(), sizeof(data) + sizeof(data2)); 190 191 MemoryView src2("hello\n file\0longer for an \n", 27), 192 dst2(f_line->get_str(), 27); 193 EXPECT_MEM_EQ(src2, dst2); 194 195 MemoryView src3("hello\n file\0longer for an \n overflow", 37), 196 dst_full_final(f_full->get_str(), 37); 197 EXPECT_MEM_EQ(src3, dst_full_final); 198 199 ASSERT_EQ(f_line->flush(), 0); 200 ASSERT_EQ(f_full->flush(), 0); 201 202 EXPECT_EQ(f_line->get_pos(), sizeof(data) + sizeof(data2)); 203 MemoryView dst_line_final(f_line->get_str(), 37); 204 EXPECT_MEM_EQ(src3, dst_line_final); 205 EXPECT_MEM_EQ(src3, dst_full_final); 206 207 ASSERT_EQ(f_line->close(), 0); 208 ASSERT_EQ(f_full->close(), 0); 209 } 210 211 TEST(LlvmLibcFileTest, WriteUnbuffered) { 212 const char data[] = "written immediately"; 213 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) + 1; 214 char file_buffer[FILE_BUFFER_SIZE]; 215 StringFile *f = 216 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IONBF, false, "w"); 217 218 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 219 EXPECT_EQ(f->get_pos(), 220 sizeof(data)); // no buffering means this is written immediately. 221 EXPECT_STREQ(f->get_str(), data); 222 223 ASSERT_EQ(f->close(), 0); 224 } 225 226 TEST(LlvmLibcFileTest, ReadOnly) { 227 const char initial_content[] = "1234567890987654321"; 228 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content); 229 char file_buffer[FILE_BUFFER_SIZE]; 230 StringFile *f = 231 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r"); 232 f->reset_and_fill(initial_content, sizeof(initial_content)); 233 234 constexpr size_t READ_SIZE = sizeof(initial_content) / 2; 235 char read_data[READ_SIZE]; 236 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 237 EXPECT_FALSE(f->iseof()); 238 // Reading less than file buffer worth will still read one 239 // full buffer worth of data. 240 EXPECT_STREQ(file_buffer, initial_content); 241 EXPECT_STREQ(file_buffer, f->get_str()); 242 EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos()); 243 // The read data should match what was supposed to be read anyway. 244 MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE); 245 EXPECT_MEM_EQ(src1, dst1); 246 247 // Reading another buffer worth should read out everything in 248 // the file. 249 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 250 EXPECT_FALSE(f->iseof()); 251 MemoryView src2(initial_content + READ_SIZE, READ_SIZE), 252 dst2(read_data, READ_SIZE); 253 EXPECT_MEM_EQ(src2, dst2); 254 255 // Another read should trigger an EOF. 256 ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE)); 257 EXPECT_TRUE(f->iseof()); 258 259 // Reset the pos to the beginning of the file which should allow 260 // reading again. 261 for (size_t i = 0; i < READ_SIZE; ++i) 262 read_data[i] = 0; 263 f->seek(0, SEEK_SET); 264 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 265 MemoryView src3(initial_content, READ_SIZE), dst3(read_data, READ_SIZE); 266 EXPECT_MEM_EQ(src3, dst3); 267 268 // This is not a writable file. 269 EXPECT_EQ(f->write(initial_content, sizeof(initial_content)), size_t(0)); 270 EXPECT_TRUE(f->error()); 271 EXPECT_NE(errno, 0); 272 errno = 0; 273 274 ASSERT_EQ(f->close(), 0); 275 } 276 277 TEST(LlvmLibcFileTest, ReadSeekCurAndRead) { 278 const char initial_content[] = "1234567890987654321"; 279 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content); 280 char file_buffer[FILE_BUFFER_SIZE]; 281 StringFile *f = 282 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r"); 283 f->reset_and_fill(initial_content, sizeof(initial_content)); 284 285 constexpr size_t READ_SIZE = 5; 286 char data[READ_SIZE]; 287 data[READ_SIZE - 1] = '\0'; 288 ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); 289 ASSERT_STREQ(data, "1234"); 290 ASSERT_EQ(f->seek(5, SEEK_CUR), 0); 291 ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); 292 ASSERT_STREQ(data, "0987"); 293 ASSERT_EQ(f->seek(-5, SEEK_CUR), 0); 294 ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1); 295 ASSERT_STREQ(data, "9098"); 296 ASSERT_EQ(f->close(), 0); 297 } 298 299 TEST(LlvmLibcFileTest, AppendOnly) { 300 const char initial_content[] = "1234567890987654321"; 301 const char write_data[] = "append"; 302 constexpr size_t FILE_BUFFER_SIZE = sizeof(write_data) * 3 / 2; 303 char file_buffer[FILE_BUFFER_SIZE]; 304 StringFile *f = 305 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a"); 306 f->reset_and_fill(initial_content, sizeof(initial_content)); 307 308 constexpr size_t READ_SIZE = 5; 309 char read_data[READ_SIZE]; 310 // This is not a readable file. 311 ASSERT_EQ(f->read(read_data, READ_SIZE), size_t(0)); 312 EXPECT_TRUE(f->error()); 313 EXPECT_NE(errno, 0); 314 errno = 0; 315 316 // Write should succeed but will be buffered in the file stream. 317 ASSERT_EQ(f->write(write_data, sizeof(write_data)), sizeof(write_data)); 318 EXPECT_EQ(f->get_pos(), size_t(0)); 319 // Flushing will write to the file. 320 EXPECT_EQ(f->flush(), int(0)); 321 EXPECT_EQ(f->get_pos(), sizeof(write_data) + sizeof(initial_content)); 322 323 ASSERT_EQ(f->close(), 0); 324 } 325 326 TEST(LlvmLibcFileTest, WriteUpdate) { 327 const char data[] = "hello, file"; 328 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2; 329 char file_buffer[FILE_BUFFER_SIZE]; 330 StringFile *f = 331 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w+"); 332 333 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 334 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 335 336 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 337 338 // Seek flushes the stream buffer so we can read the previously written data. 339 char read_data[sizeof(data)]; 340 ASSERT_EQ(f->read(read_data, sizeof(data)), sizeof(data)); 341 EXPECT_STREQ(read_data, data); 342 343 ASSERT_EQ(f->close(), 0); 344 } 345 346 TEST(LlvmLibcFileTest, ReadUpdate) { 347 const char initial_content[] = "1234567890987654321"; 348 constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content); 349 char file_buffer[FILE_BUFFER_SIZE]; 350 StringFile *f = 351 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r+"); 352 f->reset_and_fill(initial_content, sizeof(initial_content)); 353 354 constexpr size_t READ_SIZE = sizeof(initial_content) / 2; 355 char read_data[READ_SIZE]; 356 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 357 EXPECT_FALSE(f->iseof()); 358 // Reading less than file buffer worth will still read one 359 // full buffer worth of data. 360 EXPECT_STREQ(file_buffer, initial_content); 361 EXPECT_STREQ(file_buffer, f->get_str()); 362 EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos()); 363 // The read data should match what was supposed to be read anyway. 364 MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE); 365 EXPECT_MEM_EQ(src1, dst1); 366 367 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 368 const char write_data[] = "hello, file"; 369 ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data))); 370 EXPECT_STREQ(file_buffer, write_data); 371 ASSERT_EQ(f->flush(), 0); 372 MemoryView dst2(f->get_str(), sizeof(write_data)), 373 src2(write_data, sizeof(write_data)); 374 EXPECT_MEM_EQ(src2, dst2); 375 376 ASSERT_EQ(f->close(), 0); 377 } 378 379 TEST(LlvmLibcFileTest, AppendUpdate) { 380 const char initial_content[] = "1234567890987654321"; 381 const char data[] = "hello, file"; 382 constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2; 383 char file_buffer[FILE_BUFFER_SIZE]; 384 StringFile *f = 385 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a+"); 386 f->reset_and_fill(initial_content, sizeof(initial_content)); 387 388 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 389 EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream 390 ASSERT_EQ(f->flush(), 0); 391 // The flush should write |data| to the endof the file. 392 EXPECT_EQ(f->get_pos(), sizeof(data) + sizeof(initial_content)); 393 394 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 395 // Seeking to the beginning of the file should not affect the place 396 // where write happens. 397 ASSERT_EQ(sizeof(data), f->write(data, sizeof(data))); 398 ASSERT_EQ(f->flush(), 0); 399 EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content)); 400 MemoryView src1(initial_content, sizeof(initial_content)), 401 dst1(f->get_str(), sizeof(initial_content)); 402 EXPECT_MEM_EQ(src1, dst1); 403 MemoryView src2(data, sizeof(data)), 404 dst2(f->get_str() + sizeof(initial_content), sizeof(data)); 405 EXPECT_MEM_EQ(src2, dst2); 406 MemoryView src3(data, sizeof(data)), 407 dst3(f->get_str() + sizeof(initial_content) + sizeof(data), sizeof(data)); 408 EXPECT_MEM_EQ(src3, dst3); 409 410 // Reads can happen from any point. 411 ASSERT_EQ(f->seek(0, SEEK_SET), 0); 412 constexpr size_t READ_SIZE = 10; 413 char read_data[READ_SIZE]; 414 ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE)); 415 MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE); 416 EXPECT_MEM_EQ(src4, dst4); 417 418 ASSERT_EQ(f->close(), 0); 419 } 420 421 TEST(LlvmLibcFileTest, SmallBuffer) { 422 const char WRITE_DATA[] = "small buffer"; 423 constexpr size_t WRITE_SIZE = sizeof(WRITE_DATA); 424 constexpr size_t FILE_BUFFER_SIZE = sizeof(WRITE_DATA) / 2 - 1; 425 char file_buffer[FILE_BUFFER_SIZE]; 426 StringFile *f = 427 new_string_file(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "w"); 428 429 ASSERT_EQ(WRITE_SIZE, f->write(WRITE_DATA, WRITE_SIZE)); 430 // Since data much larger than the buffer is being written, all of it should 431 // be available in the file without a flush operation. 432 EXPECT_EQ(f->get_pos(), sizeof(WRITE_DATA)); 433 ASSERT_STREQ(f->get_str(), WRITE_DATA); 434 435 ASSERT_EQ(f->close(), 0); 436 } 437 438 TEST(LlvmLibcFileTest, ZeroLengthBuffer) { 439 const char WRITE_DATA[] = "small buffer"; 440 constexpr size_t WRITE_SIZE = sizeof(WRITE_DATA); 441 StringFile *f_fbf = new_string_file(nullptr, 0, _IOFBF, true, "w"); 442 StringFile *f_lbf = new_string_file(nullptr, 0, _IOLBF, true, "w"); 443 StringFile *f_nbf = new_string_file(nullptr, 0, _IONBF, true, "w"); 444 445 ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE)); 446 ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE)); 447 ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE)); 448 // Since there is no buffer space, all of the written data should 449 // be available in the file without a flush operation. 450 EXPECT_EQ(f_fbf->get_pos(), sizeof(WRITE_DATA)); 451 EXPECT_EQ(f_lbf->get_pos(), sizeof(WRITE_DATA)); 452 EXPECT_EQ(f_nbf->get_pos(), sizeof(WRITE_DATA)); 453 ASSERT_STREQ(f_fbf->get_str(), WRITE_DATA); 454 ASSERT_STREQ(f_lbf->get_str(), WRITE_DATA); 455 ASSERT_STREQ(f_nbf->get_str(), WRITE_DATA); 456 457 ASSERT_EQ(f_fbf->close(), 0); 458 ASSERT_EQ(f_lbf->close(), 0); 459 ASSERT_EQ(f_nbf->close(), 0); 460 } 461 462 TEST(LlvmLibcFileTest, WriteNothing) { 463 const char WRITE_DATA[] = ""; 464 constexpr size_t WRITE_SIZE = 0; 465 constexpr size_t FILE_BUFFER_SIZE = 5; 466 char file_buffer_fbf[FILE_BUFFER_SIZE]; 467 char file_buffer_lbf[FILE_BUFFER_SIZE]; 468 char file_buffer_nbf[FILE_BUFFER_SIZE]; 469 StringFile *f_fbf = 470 new_string_file(file_buffer_fbf, FILE_BUFFER_SIZE, _IOFBF, false, "w"); 471 StringFile *f_lbf = 472 new_string_file(file_buffer_lbf, FILE_BUFFER_SIZE, _IOLBF, false, "w"); 473 StringFile *f_nbf = 474 new_string_file(file_buffer_nbf, FILE_BUFFER_SIZE, _IONBF, false, "w"); 475 476 ASSERT_EQ(WRITE_SIZE, f_fbf->write(WRITE_DATA, WRITE_SIZE)); 477 ASSERT_EQ(WRITE_SIZE, f_lbf->write(WRITE_DATA, WRITE_SIZE)); 478 ASSERT_EQ(WRITE_SIZE, f_nbf->write(WRITE_DATA, WRITE_SIZE)); 479 480 ASSERT_FALSE(f_fbf->error_unlocked()); 481 ASSERT_FALSE(f_lbf->error_unlocked()); 482 ASSERT_FALSE(f_nbf->error_unlocked()); 483 484 ASSERT_EQ(f_fbf->close(), 0); 485 ASSERT_EQ(f_lbf->close(), 0); 486 ASSERT_EQ(f_nbf->close(), 0); 487 } 488