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); |
36 | static int str_close(LIBC_NAMESPACE::File *f) { |
37 | delete reinterpret_cast<StringFile *>(f); |
38 | return 0; |
39 | } |
40 | |
41 | public: |
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 | |
53 | void reset() { pos = 0; } |
54 | size_t get_pos() const { return pos; } |
55 | char *get_str() { return str; } |
56 | |
57 | // Use this method to prefill the file. |
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 | |
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 | |
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 | |
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 | |
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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "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(data: read_data, len: 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 | |
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(buffer: file_buffer_line, buflen: FILE_BUFFER_SIZE, _IOLBF, owned: false, mode: "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(buffer: file_buffer_full, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IONBF, owned: false, mode: "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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "r" ); |
236 | f->reset_and_fill(data: initial_content, len: 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(offset: 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(data: initial_content, len: 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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "r" ); |
289 | f->reset_and_fill(data: initial_content, len: 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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "a" ); |
312 | f->reset_and_fill(data: initial_content, len: 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(data: read_data, len: 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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "r+" ); |
361 | f->reset_and_fill(data: initial_content, len: 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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "a+" ); |
395 | f->reset_and_fill(data: initial_content, len: 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 | |
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(buffer: file_buffer, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "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 | |
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(buffer: nullptr, buflen: 0, _IOFBF, owned: true, mode: "w" ); |
451 | StringFile *f_lbf = new_string_file(buffer: nullptr, buflen: 0, _IOLBF, owned: true, mode: "w" ); |
452 | StringFile *f_nbf = new_string_file(buffer: nullptr, buflen: 0, _IONBF, owned: true, mode: "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 | |
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(buffer: file_buffer_fbf, buflen: FILE_BUFFER_SIZE, _IOFBF, owned: false, mode: "w" ); |
480 | StringFile *f_lbf = |
481 | new_string_file(buffer: file_buffer_lbf, buflen: FILE_BUFFER_SIZE, _IOLBF, owned: false, mode: "w" ); |
482 | StringFile *f_nbf = |
483 | new_string_file(buffer: file_buffer_nbf, buflen: FILE_BUFFER_SIZE, _IONBF, owned: false, mode: "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 | |