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);
str_close(__llvm_libc::File * f)30 static int str_close(__llvm_libc::File *f) { return 0; }
str_flush(__llvm_libc::File * f)31 static int str_flush(__llvm_libc::File *f) { return 0; }
32
33 public:
StringFile(char * buffer,size_t buflen,int bufmode,bool owned,ModeFlags modeflags)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
init(char * buffer,size_t buflen,int bufmode,bool owned,ModeFlags modeflags)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
reset()55 void reset() { pos = 0; }
get_pos() const56 size_t get_pos() const { return pos; }
get_str()57 char *get_str() { return str; }
58
59 // Use this method to prefill the file.
reset_and_fill(const char * data,size_t len)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
str_read(__llvm_libc::File * f,void * data,size_t len)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
str_write(__llvm_libc::File * f,const void * data,size_t len)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
str_seek(__llvm_libc::File * f,long offset,int whence)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
new_string_file(char * buffer,size_t buflen,int bufmode,bool owned,const char * mode)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
TEST(LlvmLibcFileTest,WriteOnly)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
TEST(LlvmLibcFileTest,WriteLineBuffered)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
TEST(LlvmLibcFileTest,WriteUnbuffered)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
TEST(LlvmLibcFileTest,ReadOnly)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
TEST(LlvmLibcFileTest,ReadSeekCurAndRead)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
TEST(LlvmLibcFileTest,AppendOnly)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
TEST(LlvmLibcFileTest,WriteUpdate)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
TEST(LlvmLibcFileTest,ReadUpdate)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
TEST(LlvmLibcFileTest,AppendUpdate)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
TEST(LlvmLibcFileTest,SmallBuffer)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
TEST(LlvmLibcFileTest,ZeroLengthBuffer)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
TEST(LlvmLibcFileTest,WriteNothing)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