1 //===-- Unittests for the fopencookie function ----------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "src/stdio/clearerr.h"
10 #include "src/stdio/fclose.h"
11 #include "src/stdio/feof.h"
12 #include "src/stdio/ferror.h"
13 #include "src/stdio/fflush.h"
14 #include "src/stdio/fopencookie.h"
15 #include "src/stdio/fread.h"
16 #include "src/stdio/fseek.h"
17 #include "src/stdio/fwrite.h"
18 #include "test/UnitTest/MemoryMatcher.h"
19 #include "test/UnitTest/Test.h"
21 #include "hdr/stdio_macros.h"
22 #include "hdr/types/size_t.h"
23 #include "src/errno/libc_errno.h"
25 using MemoryView
= LIBC_NAMESPACE::testing::MemoryView
;
29 size_t bufsize
; // Size of buf
30 size_t endpos
; // 1 more than current fill size
31 size_t offset
; // Current read/write location
34 ssize_t
write_ss(void *cookie
, const char *buf
, size_t size
) {
35 auto *ss
= reinterpret_cast<StringStream
*>(cookie
);
36 if (ss
->offset
+ size
> ss
->bufsize
)
38 reinterpret_cast<char *>(realloc(ss
->buf
, (ss
->offset
+ size
) * 2));
39 for (size_t i
= 0; i
< size
; ++i
, ss
->offset
+= 1)
40 ss
->buf
[ss
->offset
] = buf
[i
];
41 if (ss
->offset
> ss
->endpos
)
42 ss
->endpos
= ss
->offset
;
46 ssize_t
read_ss(void *cookie
, char *buf
, size_t size
) {
47 auto *ss
= reinterpret_cast<StringStream
*>(cookie
);
48 ssize_t copysize
= size
;
49 if (ss
->offset
+ size
> ss
->endpos
) {
50 // You cannot copy more than what you have available.
51 copysize
= ss
->endpos
- ss
->offset
;
53 copysize
= 0; // A seek could have moved offset past the endpos
55 for (size_t i
= 0; i
< size_t(copysize
); ++i
, ++ss
->offset
)
56 buf
[i
] = ss
->buf
[ss
->offset
];
60 int seek_ss(void *cookie
, off64_t
*offset
, int whence
) {
61 auto *ss
= reinterpret_cast<StringStream
*>(cookie
);
63 if (whence
== SEEK_SET
) {
65 } else if (whence
== SEEK_CUR
) {
66 new_offset
= *offset
+ ss
->offset
;
67 } else if (whence
== SEEK_END
) {
68 new_offset
= *offset
+ ss
->endpos
;
70 LIBC_NAMESPACE::libc_errno
= EINVAL
;
73 if (new_offset
< 0 || size_t(new_offset
) > ss
->bufsize
)
75 ss
->offset
= new_offset
;
80 int close_ss(void *cookie
) {
81 auto *ss
= reinterpret_cast<StringStream
*>(cookie
);
84 ss
->bufsize
= ss
->endpos
= ss
->offset
= 0;
88 constexpr cookie_io_functions_t STRING_STREAM_FUNCS
= {&read_ss
, &write_ss
,
91 TEST(LlvmLibcFOpenCookie
, ReadOnlyCookieTest
) {
92 constexpr char CONTENT
[] = "Hello,readonly!";
93 auto *ss
= reinterpret_cast<StringStream
*>(malloc(sizeof(StringStream
)));
94 ss
->buf
= reinterpret_cast<char *>(malloc(sizeof(CONTENT
)));
95 ss
->bufsize
= sizeof(CONTENT
);
97 ss
->endpos
= ss
->bufsize
;
98 for (size_t i
= 0; i
< sizeof(CONTENT
); ++i
)
99 ss
->buf
[i
] = CONTENT
[i
];
101 ::FILE *f
= LIBC_NAMESPACE::fopencookie(ss
, "r", STRING_STREAM_FUNCS
);
102 ASSERT_TRUE(f
!= nullptr);
103 char read_data
[sizeof(CONTENT
)];
104 ASSERT_EQ(sizeof(CONTENT
),
105 LIBC_NAMESPACE::fread(read_data
, 1, sizeof(CONTENT
), f
));
106 ASSERT_STREQ(read_data
, CONTENT
);
108 // Reading another time should trigger eof.
109 ASSERT_NE(sizeof(CONTENT
),
110 LIBC_NAMESPACE::fread(read_data
, 1, sizeof(CONTENT
), f
));
111 ASSERT_NE(LIBC_NAMESPACE::feof(f
), 0);
113 ASSERT_EQ(0, LIBC_NAMESPACE::fseek(f
, 0, SEEK_SET
));
114 // Should be an error to write.
115 ASSERT_EQ(size_t(0), LIBC_NAMESPACE::fwrite(CONTENT
, 1, sizeof(CONTENT
), f
));
116 ASSERT_NE(LIBC_NAMESPACE::ferror(f
), 0);
117 ASSERT_ERRNO_FAILURE();
118 LIBC_NAMESPACE::libc_errno
= 0;
120 LIBC_NAMESPACE::clearerr(f
);
121 ASSERT_EQ(LIBC_NAMESPACE::ferror(f
), 0);
123 ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f
));
127 TEST(LlvmLibcFOpenCookie
, WriteOnlyCookieTest
) {
128 size_t INIT_BUFSIZE
= 32;
129 auto *ss
= reinterpret_cast<StringStream
*>(malloc(sizeof(StringStream
)));
130 ss
->buf
= reinterpret_cast<char *>(malloc(INIT_BUFSIZE
));
131 ss
->bufsize
= INIT_BUFSIZE
;
135 ::FILE *f
= LIBC_NAMESPACE::fopencookie(ss
, "w", STRING_STREAM_FUNCS
);
136 ASSERT_TRUE(f
!= nullptr);
138 constexpr char WRITE_DATA
[] = "Hello,writeonly!";
139 ASSERT_EQ(sizeof(WRITE_DATA
),
140 LIBC_NAMESPACE::fwrite(WRITE_DATA
, 1, sizeof(WRITE_DATA
), f
));
141 // Flushing will ensure the data to be written to the string stream.
142 ASSERT_EQ(0, LIBC_NAMESPACE::fflush(f
));
143 ASSERT_STREQ(WRITE_DATA
, ss
->buf
);
145 ASSERT_EQ(0, LIBC_NAMESPACE::fseek(f
, 0, SEEK_SET
));
146 char read_data
[sizeof(WRITE_DATA
)];
147 // Should be an error to read.
149 LIBC_NAMESPACE::fread(read_data
, 1, sizeof(WRITE_DATA
), f
));
150 ASSERT_NE(LIBC_NAMESPACE::ferror(f
), 0);
151 ASSERT_ERRNO_EQ(EBADF
);
152 LIBC_NAMESPACE::libc_errno
= 0;
154 LIBC_NAMESPACE::clearerr(f
);
155 ASSERT_EQ(LIBC_NAMESPACE::ferror(f
), 0);
157 ASSERT_EQ(0, LIBC_NAMESPACE::fclose(f
));
161 TEST(LlvmLibcFOpenCookie
, AppendOnlyCookieTest
) {
162 constexpr char INITIAL_CONTENT
[] = "1234567890987654321";
163 constexpr char WRITE_DATA
[] = "append";
164 auto *ss
= reinterpret_cast<StringStream
*>(malloc(sizeof(StringStream
)));
165 ss
->buf
= reinterpret_cast<char *>(malloc(sizeof(INITIAL_CONTENT
)));
166 ss
->bufsize
= sizeof(INITIAL_CONTENT
);
167 ss
->offset
= ss
->bufsize
; // We want to open the file in append mode.
168 ss
->endpos
= ss
->bufsize
;
169 for (size_t i
= 0; i
< sizeof(INITIAL_CONTENT
); ++i
)
170 ss
->buf
[i
] = INITIAL_CONTENT
[i
];
172 ::FILE *f
= LIBC_NAMESPACE::fopencookie(ss
, "a", STRING_STREAM_FUNCS
);
173 ASSERT_TRUE(f
!= nullptr);
175 constexpr size_t READ_SIZE
= 5;
176 char read_data
[READ_SIZE
];
177 // This is not a readable file.
178 ASSERT_EQ(LIBC_NAMESPACE::fread(read_data
, 1, READ_SIZE
, f
), size_t(0));
179 ASSERT_NE(LIBC_NAMESPACE::ferror(f
), 0);
180 ASSERT_ERRNO_FAILURE();
181 LIBC_NAMESPACE::libc_errno
= 0;
183 LIBC_NAMESPACE::clearerr(f
);
184 ASSERT_EQ(LIBC_NAMESPACE::ferror(f
), 0);
186 ASSERT_EQ(LIBC_NAMESPACE::fwrite(WRITE_DATA
, 1, sizeof(WRITE_DATA
), f
),
188 EXPECT_EQ(LIBC_NAMESPACE::fflush(f
), 0);
189 EXPECT_EQ(ss
->endpos
, sizeof(WRITE_DATA
) + sizeof(INITIAL_CONTENT
));
191 ASSERT_EQ(LIBC_NAMESPACE::fclose(f
), 0);
195 TEST(LlvmLibcFOpenCookie
, ReadUpdateCookieTest
) {
196 const char INITIAL_CONTENT
[] = "1234567890987654321";
197 auto *ss
= reinterpret_cast<StringStream
*>(malloc(sizeof(StringStream
)));
198 ss
->buf
= reinterpret_cast<char *>(malloc(sizeof(INITIAL_CONTENT
)));
199 ss
->bufsize
= sizeof(INITIAL_CONTENT
);
201 ss
->endpos
= ss
->bufsize
;
202 for (size_t i
= 0; i
< sizeof(INITIAL_CONTENT
); ++i
)
203 ss
->buf
[i
] = INITIAL_CONTENT
[i
];
205 ::FILE *f
= LIBC_NAMESPACE::fopencookie(ss
, "r+", STRING_STREAM_FUNCS
);
206 ASSERT_TRUE(f
!= nullptr);
208 constexpr size_t READ_SIZE
= sizeof(INITIAL_CONTENT
) / 2;
209 char read_data
[READ_SIZE
];
210 ASSERT_EQ(READ_SIZE
, LIBC_NAMESPACE::fread(read_data
, 1, READ_SIZE
, f
));
212 MemoryView
src1(INITIAL_CONTENT
, READ_SIZE
), dst1(read_data
, READ_SIZE
);
213 EXPECT_MEM_EQ(src1
, dst1
);
215 ASSERT_EQ(LIBC_NAMESPACE::fseek(f
, 0, SEEK_SET
), 0);
216 constexpr char WRITE_DATA
[] = "hello, file";
217 ASSERT_EQ(sizeof(WRITE_DATA
),
218 LIBC_NAMESPACE::fwrite(WRITE_DATA
, 1, sizeof(WRITE_DATA
), f
));
219 ASSERT_EQ(LIBC_NAMESPACE::fflush(f
), 0);
220 EXPECT_STREQ(ss
->buf
, WRITE_DATA
);
222 ASSERT_EQ(LIBC_NAMESPACE::fclose(f
), 0);
226 TEST(LlvmLibcFOpenCookie
, WriteUpdateCookieTest
) {
227 constexpr char WRITE_DATA
[] = "hello, file";
228 auto *ss
= reinterpret_cast<StringStream
*>(malloc(sizeof(StringStream
)));
229 ss
->buf
= reinterpret_cast<char *>(malloc(sizeof(WRITE_DATA
)));
230 ss
->bufsize
= sizeof(WRITE_DATA
);
234 ::FILE *f
= LIBC_NAMESPACE::fopencookie(ss
, "w+", STRING_STREAM_FUNCS
);
235 ASSERT_TRUE(f
!= nullptr);
237 ASSERT_EQ(sizeof(WRITE_DATA
),
238 LIBC_NAMESPACE::fwrite(WRITE_DATA
, 1, sizeof(WRITE_DATA
), f
));
240 ASSERT_EQ(LIBC_NAMESPACE::fseek(f
, 0, SEEK_SET
), 0);
242 char read_data
[sizeof(WRITE_DATA
)];
243 ASSERT_EQ(LIBC_NAMESPACE::fread(read_data
, 1, sizeof(read_data
), f
),
245 EXPECT_STREQ(read_data
, WRITE_DATA
);
247 ASSERT_EQ(LIBC_NAMESPACE::fclose(f
), 0);