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 = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "w");
120 
121   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
122   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
123   ASSERT_EQ(f->flush(), 0);
124   EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available
125   EXPECT_STREQ(f->get_str(), data);
126 
127   f->reset();
128   ASSERT_EQ(f->get_pos(), size_t(0));
129   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
130   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
131   // The second write should trigger a buffer flush.
132   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
133   EXPECT_GE(f->get_pos(), size_t(0));
134   ASSERT_EQ(f->flush(), 0);
135   EXPECT_EQ(f->get_pos(), 2 * sizeof(data));
136 
137   char read_data[sizeof(data)];
138   // This is not a readable file.
139   EXPECT_EQ(f->read(read_data, sizeof(data)), size_t(0));
140   EXPECT_TRUE(f->error());
141   EXPECT_NE(errno, 0);
142   errno = 0;
143 
144   ASSERT_EQ(f->close(), 0);
145 }
146 
147 TEST(LlvmLibcFileTest, ReadOnly) {
148   const char initial_content[] = "1234567890987654321";
149   constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);
150   char file_buffer[FILE_BUFFER_SIZE];
151   StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r");
152   f->reset_and_fill(initial_content, sizeof(initial_content));
153 
154   constexpr size_t READ_SIZE = sizeof(initial_content) / 2;
155   char read_data[READ_SIZE];
156   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
157   EXPECT_FALSE(f->iseof());
158   // Reading less than file buffer worth will still read one
159   // full buffer worth of data.
160   EXPECT_STREQ(file_buffer, initial_content);
161   EXPECT_STREQ(file_buffer, f->get_str());
162   EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos());
163   // The read data should match what was supposed to be read anyway.
164   MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE);
165   EXPECT_MEM_EQ(src1, dst1);
166 
167   // Reading another buffer worth should read out everything in
168   // the file.
169   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
170   EXPECT_FALSE(f->iseof());
171   MemoryView src2(initial_content + READ_SIZE, READ_SIZE),
172       dst2(read_data, READ_SIZE);
173   EXPECT_MEM_EQ(src2, dst2);
174 
175   // Another read should trigger an EOF.
176   ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE));
177   EXPECT_TRUE(f->iseof());
178 
179   // Reset the pos to the beginning of the file which should allow
180   // reading again.
181   for (size_t i = 0; i < READ_SIZE; ++i)
182     read_data[i] = 0;
183   f->seek(0, SEEK_SET);
184   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
185   MemoryView src3(initial_content, READ_SIZE), dst3(read_data, READ_SIZE);
186   EXPECT_MEM_EQ(src3, dst3);
187 
188   // This is not a writable file.
189   EXPECT_EQ(f->write(initial_content, sizeof(initial_content)), size_t(0));
190   EXPECT_TRUE(f->error());
191   EXPECT_NE(errno, 0);
192   errno = 0;
193 
194   ASSERT_EQ(f->close(), 0);
195 }
196 
197 TEST(LlvmLibcFileTest, ReadSeekCurAndRead) {
198   const char initial_content[] = "1234567890987654321";
199   constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);
200   char file_buffer[FILE_BUFFER_SIZE];
201   StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r");
202   f->reset_and_fill(initial_content, sizeof(initial_content));
203 
204   constexpr size_t READ_SIZE = 5;
205   char data[READ_SIZE];
206   data[READ_SIZE - 1] = '\0';
207   ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1);
208   ASSERT_STREQ(data, "1234");
209   ASSERT_EQ(f->seek(5, SEEK_CUR), 0);
210   ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1);
211   ASSERT_STREQ(data, "0987");
212   ASSERT_EQ(f->seek(-5, SEEK_CUR), 0);
213   ASSERT_EQ(f->read(data, READ_SIZE - 1), READ_SIZE - 1);
214   ASSERT_STREQ(data, "9098");
215   ASSERT_EQ(f->close(), 0);
216 }
217 
218 TEST(LlvmLibcFileTest, AppendOnly) {
219   const char initial_content[] = "1234567890987654321";
220   const char write_data[] = "append";
221   constexpr size_t FILE_BUFFER_SIZE = sizeof(write_data) * 3 / 2;
222   char file_buffer[FILE_BUFFER_SIZE];
223   StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "a");
224   f->reset_and_fill(initial_content, sizeof(initial_content));
225 
226   constexpr size_t READ_SIZE = 5;
227   char read_data[READ_SIZE];
228   // This is not a readable file.
229   ASSERT_EQ(f->read(read_data, READ_SIZE), size_t(0));
230   EXPECT_TRUE(f->error());
231   EXPECT_NE(errno, 0);
232   errno = 0;
233 
234   // Write should succeed but will be buffered in the file stream.
235   ASSERT_EQ(f->write(write_data, sizeof(write_data)), sizeof(write_data));
236   EXPECT_EQ(f->get_pos(), size_t(0));
237   // Flushing will write to the file.
238   EXPECT_EQ(f->flush(), int(0));
239   EXPECT_EQ(f->get_pos(), sizeof(write_data) + sizeof(initial_content));
240 
241   ASSERT_EQ(f->close(), 0);
242 }
243 
244 TEST(LlvmLibcFileTest, WriteUpdate) {
245   const char data[] = "hello, file";
246   constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;
247   char file_buffer[FILE_BUFFER_SIZE];
248   StringFile *f =
249       new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "w+");
250 
251   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
252   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
253 
254   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
255 
256   // Seek flushes the stream buffer so we can read the previously written data.
257   char read_data[sizeof(data)];
258   ASSERT_EQ(f->read(read_data, sizeof(data)), sizeof(data));
259   EXPECT_STREQ(read_data, data);
260 
261   ASSERT_EQ(f->close(), 0);
262 }
263 
264 TEST(LlvmLibcFileTest, ReadUpdate) {
265   const char initial_content[] = "1234567890987654321";
266   constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);
267   char file_buffer[FILE_BUFFER_SIZE];
268   StringFile *f =
269       new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r+");
270   f->reset_and_fill(initial_content, sizeof(initial_content));
271 
272   constexpr size_t READ_SIZE = sizeof(initial_content) / 2;
273   char read_data[READ_SIZE];
274   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
275   EXPECT_FALSE(f->iseof());
276   // Reading less than file buffer worth will still read one
277   // full buffer worth of data.
278   EXPECT_STREQ(file_buffer, initial_content);
279   EXPECT_STREQ(file_buffer, f->get_str());
280   EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos());
281   // The read data should match what was supposed to be read anyway.
282   MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE);
283   EXPECT_MEM_EQ(src1, dst1);
284 
285   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
286   const char write_data[] = "hello, file";
287   ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data)));
288   EXPECT_STREQ(file_buffer, write_data);
289   ASSERT_EQ(f->flush(), 0);
290   MemoryView dst2(f->get_str(), sizeof(write_data)),
291       src2(write_data, sizeof(write_data));
292   EXPECT_MEM_EQ(src2, dst2);
293 
294   ASSERT_EQ(f->close(), 0);
295 }
296 
297 TEST(LlvmLibcFileTest, AppendUpdate) {
298   const char initial_content[] = "1234567890987654321";
299   const char data[] = "hello, file";
300   constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;
301   char file_buffer[FILE_BUFFER_SIZE];
302   StringFile *f =
303       new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "a+");
304   f->reset_and_fill(initial_content, sizeof(initial_content));
305 
306   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
307   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
308   ASSERT_EQ(f->flush(), 0);
309   // The flush should write |data| to the endof the file.
310   EXPECT_EQ(f->get_pos(), sizeof(data) + sizeof(initial_content));
311 
312   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
313   // Seeking to the beginning of the file should not affect the place
314   // where write happens.
315   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
316   ASSERT_EQ(f->flush(), 0);
317   EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content));
318   MemoryView src1(initial_content, sizeof(initial_content)),
319       dst1(f->get_str(), sizeof(initial_content));
320   EXPECT_MEM_EQ(src1, dst1);
321   MemoryView src2(data, sizeof(data)),
322       dst2(f->get_str() + sizeof(initial_content), sizeof(data));
323   EXPECT_MEM_EQ(src2, dst2);
324   MemoryView src3(data, sizeof(data)),
325       dst3(f->get_str() + sizeof(initial_content) + sizeof(data), sizeof(data));
326   EXPECT_MEM_EQ(src3, dst3);
327 
328   // Reads can happen from any point.
329   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
330   constexpr size_t READ_SIZE = 10;
331   char read_data[READ_SIZE];
332   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
333   MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE);
334   EXPECT_MEM_EQ(src4, dst4);
335 
336   ASSERT_EQ(f->close(), 0);
337 }
338