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

source code of libc/test/src/__support/File/file_test.cpp