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