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   // TODO: Add a proper locking system and tests which exercise that.
34   static void str_lock(__llvm_libc::File *f) {}
35   static void str_unlock(__llvm_libc::File *f) {}
36 
37 public:
38   explicit StringFile(char *buffer, size_t buflen, int bufmode, bool owned,
39                       ModeFlags modeflags)
40       : __llvm_libc::File(&str_write, &str_read, &str_seek, &str_close,
41                           &str_flush, &str_lock, &str_unlock, buffer, buflen,
42                           bufmode, owned, modeflags),
43         pos(0), eof_marker(0), write_append(false) {
44     if (modeflags & static_cast<ModeFlags>(__llvm_libc::File::OpenMode::APPEND))
45       write_append = true;
46   }
47 
48   void init(char *buffer, size_t buflen, int bufmode, bool owned,
49             ModeFlags modeflags) {
50     File::init(this, &str_write, &str_read, &str_seek, &str_close, &str_flush,
51                &str_lock, &str_unlock, buffer, buflen, bufmode, owned,
52                modeflags);
53     pos = eof_marker = 0;
54     if (modeflags & static_cast<ModeFlags>(__llvm_libc::File::OpenMode::APPEND))
55       write_append = true;
56     else
57       write_append = false;
58   }
59 
60   void reset() { pos = 0; }
61   size_t get_pos() const { return pos; }
62   char *get_str() { return str; }
63 
64   // Use this method to prefill the file.
65   void reset_and_fill(const char *data, size_t len) {
66     size_t i;
67     for (i = 0; i < len && i < SIZE; ++i) {
68       str[i] = data[i];
69     }
70     pos = 0;
71     eof_marker = i;
72   }
73 };
74 
75 size_t StringFile::str_read(__llvm_libc::File *f, void *data, size_t len) {
76   StringFile *sf = static_cast<StringFile *>(f);
77   if (sf->pos >= sf->eof_marker)
78     return 0;
79   size_t i = 0;
80   for (i = 0; i < len; ++i)
81     reinterpret_cast<char *>(data)[i] = sf->str[sf->pos + i];
82   sf->pos += i;
83   return i;
84 }
85 
86 size_t StringFile::str_write(__llvm_libc::File *f, const void *data,
87                              size_t len) {
88   StringFile *sf = static_cast<StringFile *>(f);
89   if (sf->write_append)
90     sf->pos = sf->eof_marker;
91   if (sf->pos >= SIZE)
92     return 0;
93   size_t i = 0;
94   for (i = 0; i < len && sf->pos < SIZE; ++i, ++sf->pos)
95     sf->str[sf->pos] = reinterpret_cast<const char *>(data)[i];
96   // Move the eof marker if the data was written beyond the current eof marker.
97   if (sf->pos > sf->eof_marker)
98     sf->eof_marker = sf->pos;
99   return i;
100 }
101 
102 int StringFile::str_seek(__llvm_libc::File *f, long offset, int whence) {
103   StringFile *sf = static_cast<StringFile *>(f);
104   if (whence == SEEK_SET)
105     sf->pos = offset;
106   if (whence == SEEK_CUR)
107     sf->pos += offset;
108   if (whence == SEEK_END)
109     sf->pos = SIZE + offset;
110   return 0;
111 }
112 
113 StringFile *new_string_file(char *buffer, size_t buflen, int bufmode,
114                             bool owned, const char *mode) {
115   StringFile *f = reinterpret_cast<StringFile *>(malloc(sizeof(StringFile)));
116   f->init(buffer, buflen, bufmode, owned, __llvm_libc::File::mode_flags(mode));
117   return f;
118 }
119 
120 TEST(LlvmLibcFileTest, WriteOnly) {
121   const char data[] = "hello, file";
122   constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;
123   char file_buffer[FILE_BUFFER_SIZE];
124   StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "w");
125 
126   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
127   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
128   ASSERT_EQ(f->flush(), 0);
129   EXPECT_EQ(f->get_pos(), sizeof(data)); // Data should now be available
130   EXPECT_STREQ(f->get_str(), data);
131 
132   f->reset();
133   ASSERT_EQ(f->get_pos(), size_t(0));
134   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
135   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
136   // The second write should trigger a buffer flush.
137   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
138   EXPECT_GE(f->get_pos(), size_t(0));
139   ASSERT_EQ(f->flush(), 0);
140   EXPECT_EQ(f->get_pos(), 2 * sizeof(data));
141 
142   char read_data[sizeof(data)];
143   // This is not a readable file.
144   EXPECT_EQ(f->read(read_data, sizeof(data)), size_t(0));
145   EXPECT_TRUE(f->error());
146   EXPECT_NE(errno, 0);
147   errno = 0;
148 
149   ASSERT_EQ(f->close(), 0);
150 }
151 
152 TEST(LlvmLibcFileTest, ReadOnly) {
153   const char initial_content[] = "1234567890987654321";
154   constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);
155   char file_buffer[FILE_BUFFER_SIZE];
156   StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r");
157   f->reset_and_fill(initial_content, sizeof(initial_content));
158 
159   constexpr size_t READ_SIZE = sizeof(initial_content) / 2;
160   char read_data[READ_SIZE];
161   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
162   EXPECT_FALSE(f->iseof());
163   // Reading less than file buffer worth will still read one
164   // full buffer worth of data.
165   EXPECT_STREQ(file_buffer, initial_content);
166   EXPECT_STREQ(file_buffer, f->get_str());
167   EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos());
168   // The read data should match what was supposed to be read anyway.
169   MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE);
170   EXPECT_MEM_EQ(src1, dst1);
171 
172   // Reading another buffer worth should read out everything in
173   // the file.
174   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
175   EXPECT_FALSE(f->iseof());
176   MemoryView src2(initial_content + READ_SIZE, READ_SIZE),
177       dst2(read_data, READ_SIZE);
178   EXPECT_MEM_EQ(src2, dst2);
179 
180   // Another read should trigger an EOF.
181   ASSERT_GT(READ_SIZE, f->read(read_data, READ_SIZE));
182   EXPECT_TRUE(f->iseof());
183 
184   // Reset the pos to the beginning of the file which should allow
185   // reading again.
186   for (size_t i = 0; i < READ_SIZE; ++i)
187     read_data[i] = 0;
188   f->seek(0, SEEK_SET);
189   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
190   MemoryView src3(initial_content, READ_SIZE), dst3(read_data, READ_SIZE);
191   EXPECT_MEM_EQ(src3, dst3);
192 
193   // This is not a writable file.
194   EXPECT_EQ(f->write(initial_content, sizeof(initial_content)), size_t(0));
195   EXPECT_TRUE(f->error());
196   EXPECT_NE(errno, 0);
197   errno = 0;
198 
199   ASSERT_EQ(f->close(), 0);
200 }
201 
202 TEST(LlvmLibcFileTest, AppendOnly) {
203   const char initial_content[] = "1234567890987654321";
204   const char write_data[] = "append";
205   constexpr size_t FILE_BUFFER_SIZE = sizeof(write_data) * 3 / 2;
206   char file_buffer[FILE_BUFFER_SIZE];
207   StringFile *f = new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "a");
208   f->reset_and_fill(initial_content, sizeof(initial_content));
209 
210   constexpr size_t READ_SIZE = 5;
211   char read_data[READ_SIZE];
212   // This is not a readable file.
213   ASSERT_EQ(f->read(read_data, READ_SIZE), size_t(0));
214   EXPECT_TRUE(f->error());
215   EXPECT_NE(errno, 0);
216   errno = 0;
217 
218   // Write should succeed but will be buffered in the file stream.
219   ASSERT_EQ(f->write(write_data, sizeof(write_data)), sizeof(write_data));
220   EXPECT_EQ(f->get_pos(), size_t(0));
221   // Flushing will write to the file.
222   EXPECT_EQ(f->flush(), int(0));
223   EXPECT_EQ(f->get_pos(), sizeof(write_data) + sizeof(initial_content));
224 
225   ASSERT_EQ(f->close(), 0);
226 }
227 
228 TEST(LlvmLibcFileTest, WriteUpdate) {
229   const char data[] = "hello, file";
230   constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;
231   char file_buffer[FILE_BUFFER_SIZE];
232   StringFile *f =
233       new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "w+");
234 
235   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
236   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
237 
238   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
239 
240   // Seek flushes the stream buffer so we can read the previously written data.
241   char read_data[sizeof(data)];
242   ASSERT_EQ(f->read(read_data, sizeof(data)), sizeof(data));
243   EXPECT_STREQ(read_data, data);
244 
245   ASSERT_EQ(f->close(), 0);
246 }
247 
248 TEST(LlvmLibcFileTest, ReadUpdate) {
249   const char initial_content[] = "1234567890987654321";
250   constexpr size_t FILE_BUFFER_SIZE = sizeof(initial_content);
251   char file_buffer[FILE_BUFFER_SIZE];
252   StringFile *f =
253       new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "r+");
254   f->reset_and_fill(initial_content, sizeof(initial_content));
255 
256   constexpr size_t READ_SIZE = sizeof(initial_content) / 2;
257   char read_data[READ_SIZE];
258   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
259   EXPECT_FALSE(f->iseof());
260   // Reading less than file buffer worth will still read one
261   // full buffer worth of data.
262   EXPECT_STREQ(file_buffer, initial_content);
263   EXPECT_STREQ(file_buffer, f->get_str());
264   EXPECT_EQ(FILE_BUFFER_SIZE, f->get_pos());
265   // The read data should match what was supposed to be read anyway.
266   MemoryView src1(initial_content, READ_SIZE), dst1(read_data, READ_SIZE);
267   EXPECT_MEM_EQ(src1, dst1);
268 
269   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
270   const char write_data[] = "hello, file";
271   ASSERT_EQ(sizeof(write_data), f->write(write_data, sizeof(write_data)));
272   EXPECT_STREQ(file_buffer, write_data);
273   ASSERT_EQ(f->flush(), 0);
274   MemoryView dst2(f->get_str(), sizeof(write_data)),
275       src2(write_data, sizeof(write_data));
276   EXPECT_MEM_EQ(src2, dst2);
277 
278   ASSERT_EQ(f->close(), 0);
279 }
280 
281 TEST(LlvmLibcFileTest, AppendUpdate) {
282   const char initial_content[] = "1234567890987654321";
283   const char data[] = "hello, file";
284   constexpr size_t FILE_BUFFER_SIZE = sizeof(data) * 3 / 2;
285   char file_buffer[FILE_BUFFER_SIZE];
286   StringFile *f =
287       new_string_file(file_buffer, FILE_BUFFER_SIZE, 0, false, "a+");
288   f->reset_and_fill(initial_content, sizeof(initial_content));
289 
290   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
291   EXPECT_EQ(f->get_pos(), size_t(0)); // Data is buffered in the file stream
292   ASSERT_EQ(f->flush(), 0);
293   // The flush should write |data| to the endof the file.
294   EXPECT_EQ(f->get_pos(), sizeof(data) + sizeof(initial_content));
295 
296   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
297   // Seeking to the beginning of the file should not affect the place
298   // where write happens.
299   ASSERT_EQ(sizeof(data), f->write(data, sizeof(data)));
300   ASSERT_EQ(f->flush(), 0);
301   EXPECT_EQ(f->get_pos(), sizeof(data) * 2 + sizeof(initial_content));
302   MemoryView src1(initial_content, sizeof(initial_content)),
303       dst1(f->get_str(), sizeof(initial_content));
304   EXPECT_MEM_EQ(src1, dst1);
305   MemoryView src2(data, sizeof(data)),
306       dst2(f->get_str() + sizeof(initial_content), sizeof(data));
307   EXPECT_MEM_EQ(src2, dst2);
308   MemoryView src3(data, sizeof(data)),
309       dst3(f->get_str() + sizeof(initial_content) + sizeof(data), sizeof(data));
310   EXPECT_MEM_EQ(src3, dst3);
311 
312   // Reads can happen from any point.
313   ASSERT_EQ(f->seek(0, SEEK_SET), 0);
314   constexpr size_t READ_SIZE = 10;
315   char read_data[READ_SIZE];
316   ASSERT_EQ(READ_SIZE, f->read(read_data, READ_SIZE));
317   MemoryView src4(initial_content, READ_SIZE), dst4(read_data, READ_SIZE);
318   EXPECT_MEM_EQ(src4, dst4);
319 
320   ASSERT_EQ(f->close(), 0);
321 }
322