• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 using ModeFlags = LIBC_NAMESPACE::File::ModeFlags;
19 using MemoryView = LIBC_NAMESPACE::testing::MemoryView;
20 using LIBC_NAMESPACE::ErrorOr;
21 using LIBC_NAMESPACE::File;
22 using LIBC_NAMESPACE::FileIOResult;
23 
24 class 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);
str_close(LIBC_NAMESPACE::File * f)36   static int str_close(LIBC_NAMESPACE::File *f) {
37     delete reinterpret_cast<StringFile *>(f);
38     return 0;
39   }
40 
41 public:
StringFile(char * buffer,size_t buflen,int bufmode,bool owned,ModeFlags modeflags)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 
reset()53   void reset() { pos = 0; }
get_pos() const54   size_t get_pos() const { return pos; }
get_str()55   char *get_str() { return str; }
56 
57   // Use this method to prefill the file.
reset_and_fill(const char * data,size_t len)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 
str_read(LIBC_NAMESPACE::File * f,void * data,size_t len)68 FileIOResult 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 
str_write(LIBC_NAMESPACE::File * f,const void * data,size_t len)80 FileIOResult 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 
str_seek(LIBC_NAMESPACE::File * f,long offset,int whence)96 ErrorOr<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 
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   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 
TEST(LlvmLibcFileTest,WriteOnly)117 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "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(read_data, 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 
TEST(LlvmLibcFileTest,WriteLineBuffered)155 TEST(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(file_buffer_line, FILE_BUFFER_SIZE, _IOLBF, false, "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(file_buffer_full, FILE_BUFFER_SIZE, _IOFBF, false, "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 
TEST(LlvmLibcFileTest,WriteUnbuffered)215 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IONBF, false, "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 
TEST(LlvmLibcFileTest,ReadOnly)230 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r");
236   f->reset_and_fill(initial_content, 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(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(initial_content, 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 
TEST(LlvmLibcFileTest,ReadSeekCurAndRead)283 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r");
289   f->reset_and_fill(initial_content, 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 
TEST(LlvmLibcFileTest,AppendOnly)305 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a");
312   f->reset_and_fill(initial_content, 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(read_data, 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 
TEST(LlvmLibcFileTest,WriteUpdate)335 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "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 
TEST(LlvmLibcFileTest,ReadUpdate)355 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "r+");
361   f->reset_and_fill(initial_content, 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 
TEST(LlvmLibcFileTest,AppendUpdate)388 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "a+");
395   f->reset_and_fill(initial_content, 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 
TEST(LlvmLibcFileTest,SmallBuffer)430 TEST(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(file_buffer, FILE_BUFFER_SIZE, _IOFBF, false, "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 
TEST(LlvmLibcFileTest,ZeroLengthBuffer)447 TEST(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(nullptr, 0, _IOFBF, true, "w");
451   StringFile *f_lbf = new_string_file(nullptr, 0, _IOLBF, true, "w");
452   StringFile *f_nbf = new_string_file(nullptr, 0, _IONBF, true, "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 
TEST(LlvmLibcFileTest,WriteNothing)471 TEST(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(file_buffer_fbf, FILE_BUFFER_SIZE, _IOFBF, false, "w");
480   StringFile *f_lbf =
481       new_string_file(file_buffer_lbf, FILE_BUFFER_SIZE, _IOLBF, false, "w");
482   StringFile *f_nbf =
483       new_string_file(file_buffer_nbf, FILE_BUFFER_SIZE, _IONBF, false, "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