xref: /freebsd/lib/libc/tests/stdio/fmemopen2_test.c (revision 325151a3)
1 /*-
2 Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org>
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions
6 are met:
7 1. Redistributions of source code must retain the above copyright
8    notice, this list of conditions and the following disclaimer.
9 2. Redistributions in binary form must reproduce the above copyright
10    notice, this list of conditions and the following disclaimer in the
11    documentation and/or other materials provided with the distribution.
12 
13 THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 SUCH DAMAGE.
24 */
25 
26 /*
27  * Test basic FILE * functions (fread, fwrite, fseek, fclose) against
28  * a FILE * retrieved using fmemopen()
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <errno.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <atf-c.h>
39 
40 ATF_TC_WITHOUT_HEAD(test_preexisting);
41 ATF_TC_BODY(test_preexisting, tc)
42 {
43 	/*
44 	 * Use a pre-existing buffer.
45 	 */
46 
47 	char buf[512];
48 	char buf2[512];
49 	char str[]  = "Test writing some stuff";
50 	char str2[] = "AAAAAAAAA";
51 	char str3[] = "AAAA writing some stuff";
52 	FILE *fp;
53 	size_t nofw, nofr;
54 	int rc;
55 
56 	/* Open a FILE * using fmemopen. */
57 	fp = fmemopen(buf, sizeof(buf), "w");
58 	ATF_REQUIRE(fp != NULL);
59 
60 	/* Write to the buffer. */
61 	nofw = fwrite(str, 1, sizeof(str), fp);
62 	ATF_REQUIRE(nofw == sizeof(str));
63 
64 	/* Close the FILE *. */
65 	rc = fclose(fp);
66 	ATF_REQUIRE(rc == 0);
67 
68 	/* Re-open the FILE * to read back the data. */
69 	fp = fmemopen(buf, sizeof(buf), "r");
70 	ATF_REQUIRE(fp != NULL);
71 
72 	/* Read from the buffer. */
73 	bzero(buf2, sizeof(buf2));
74 	nofr = fread(buf2, 1, sizeof(buf2), fp);
75 	ATF_REQUIRE(nofr == sizeof(buf2));
76 
77 	/*
78 	 * Since a write on a FILE * retrieved by fmemopen
79 	 * will add a '\0' (if there's space), we can check
80 	 * the strings for equality.
81 	 */
82 	ATF_REQUIRE(strcmp(str, buf2) == 0);
83 
84 	/* Close the FILE *. */
85 	rc = fclose(fp);
86 	ATF_REQUIRE(rc == 0);
87 
88 	/* Now open a FILE * on the first 4 bytes of the string. */
89 	fp = fmemopen(str, 4, "w");
90 	ATF_REQUIRE(fp != NULL);
91 
92 	/*
93 	 * Try to write more bytes than we shoud, we'll get a short count (4).
94 	 */
95 	nofw = fwrite(str2, 1, sizeof(str2), fp);
96 	ATF_REQUIRE(nofw == 4);
97 
98 	/* Close the FILE *. */
99 	rc = fclose(fp);
100 	ATF_REQUIRE(rc == 0);
101 
102 	/* Check that the string was not modified after the first 4 bytes. */
103 	ATF_REQUIRE(strcmp(str, str3) == 0);
104 }
105 
106 ATF_TC_WITHOUT_HEAD(test_autoalloc);
107 ATF_TC_BODY(test_autoalloc, tc)
108 {
109 	/*
110 	 * Let fmemopen allocate the buffer.
111 	 */
112 
113 	char str[] = "A quick test";
114 	FILE *fp;
115 	long pos;
116 	size_t nofw, nofr, i;
117 	int rc;
118 
119 	/* Open a FILE * using fmemopen. */
120 	fp = fmemopen(NULL, 512, "w+");
121 	ATF_REQUIRE(fp != NULL);
122 
123 	/* fill the buffer */
124 	for (i = 0; i < 512; i++) {
125 		nofw = fwrite("a", 1, 1, fp);
126 		ATF_REQUIRE(nofw == 1);
127 	}
128 
129 	/* Get the current position into the stream. */
130 	pos = ftell(fp);
131 	ATF_REQUIRE(pos == 512);
132 
133 	/*
134 	 * Try to write past the end, we should get a short object count (0)
135 	 */
136 	nofw = fwrite("a", 1, 1, fp);
137 	ATF_REQUIRE(nofw == 0);
138 
139 	/* Close the FILE *. */
140 	rc = fclose(fp);
141 	ATF_REQUIRE(rc == 0);
142 
143 	/* Open a FILE * using a wrong mode */
144 	fp = fmemopen(NULL, 512, "r");
145 	ATF_REQUIRE(fp == NULL);
146 
147 	fp = fmemopen(NULL, 512, "w");
148 	ATF_REQUIRE(fp == NULL);
149 }
150 
151 ATF_TC_WITHOUT_HEAD(test_data_length);
152 ATF_TC_BODY(test_data_length, tc)
153 {
154 	/*
155 	 * Here we test that a read operation doesn't go past the end of the
156 	 * data actually written, and that a SEEK_END seeks from the end of the
157 	 * data, not of the whole buffer.
158 	 */
159 	FILE *fp;
160 	char buf[512] = {'\0'};
161 	char str[]  = "Test data length. ";
162 	char str2[] = "Do we have two sentences?";
163 	char str3[sizeof(str) + sizeof(str2) -1];
164 	long pos;
165 	size_t nofw, nofr;
166 	int rc;
167 
168 	/* Open a FILE * for updating our buffer. */
169 	fp = fmemopen(buf, sizeof(buf), "w+");
170 	ATF_REQUIRE(fp != NULL);
171 
172 	/* Write our string into the buffer. */
173 	nofw = fwrite(str, 1, sizeof(str), fp);
174 	ATF_REQUIRE(nofw == sizeof(str));
175 
176 	/*
177 	 * Now seek to the end and check that ftell
178 	 * gives us sizeof(str).
179 	 */
180 	rc = fseek(fp, 0, SEEK_END);
181 	ATF_REQUIRE(rc == 0);
182 	pos = ftell(fp);
183 	ATF_REQUIRE(pos == sizeof(str));
184 
185 	/* Close the FILE *. */
186 	rc = fclose(fp);
187 	ATF_REQUIRE(rc == 0);
188 
189 	/* Reopen the buffer for appending. */
190 	fp = fmemopen(buf, sizeof(buf), "a+");
191 	ATF_REQUIRE(fp != NULL);
192 
193 	/* We should now be writing after the first string. */
194 	nofw = fwrite(str2, 1, sizeof(str2), fp);
195 	ATF_REQUIRE(nofw == sizeof(str2));
196 
197 	/* Rewind the FILE *. */
198 	rc = fseek(fp, 0, SEEK_SET);
199 	ATF_REQUIRE(rc == 0);
200 
201 	/* Make sure we're at the beginning. */
202 	pos = ftell(fp);
203 	ATF_REQUIRE(pos == 0);
204 
205 	/* Read the whole buffer. */
206 	nofr = fread(str3, 1, sizeof(buf), fp);
207 	ATF_REQUIRE(nofr == sizeof(str3));
208 
209 	/* Make sure the two strings are there. */
210 	ATF_REQUIRE(strncmp(str3, str, sizeof(str) - 1) == 0);
211 	ATF_REQUIRE(strncmp(str3 + sizeof(str) - 1, str2, sizeof(str2)) == 0);
212 
213 	/* Close the FILE *. */
214 	rc = fclose(fp);
215 	ATF_REQUIRE(rc == 0);
216 }
217 
218 ATF_TC_WITHOUT_HEAD(test_binary);
219 ATF_TC_BODY(test_binary, tc)
220 {
221 	/*
222 	 * Make sure that NULL bytes are never appended when opening a buffer
223 	 * in binary mode.
224 	 */
225 
226 	FILE *fp;
227 	char buf[20];
228 	char str[] = "Test";
229 	size_t nofw;
230 	int rc, i;
231 
232 	/* Pre-fill the buffer. */
233 	memset(buf, 'A', sizeof(buf));
234 
235 	/* Open a FILE * in binary mode. */
236 	fp = fmemopen(buf, sizeof(buf), "w+b");
237 	ATF_REQUIRE(fp != NULL);
238 
239 	/* Write some data into it. */
240 	nofw = fwrite(str, 1, strlen(str), fp);
241 	ATF_REQUIRE(nofw == strlen(str));
242 
243 	/* Make sure that the buffer doesn't contain any NULL bytes. */
244 	for (i = 0; i < sizeof(buf); i++)
245 		ATF_REQUIRE(buf[i] != '\0');
246 
247 	/* Close the FILE *. */
248 	rc = fclose(fp);
249 	ATF_REQUIRE(rc == 0);
250 }
251 
252 ATF_TC_WITHOUT_HEAD(test_append_binary_pos);
253 ATF_TC_BODY(test_append_binary_pos, tc)
254 {
255 	/*
256 	 * For compatibility with other implementations (glibc), we set the
257 	 * position to 0 when opening an automatically allocated binary stream
258 	 * for appending.
259 	 */
260 
261 	FILE *fp;
262 
263 	fp = fmemopen(NULL, 16, "ab+");
264 	ATF_REQUIRE(ftell(fp) == 0L);
265 	fclose(fp);
266 
267 	/*
268 	 * Make sure that a pre-allocated buffer behaves correctly.
269 	 */
270 	char buf[] = "Hello";
271 	fp = fmemopen(buf, sizeof(buf), "ab+");
272 	ATF_REQUIRE(ftell(fp) == strlen(buf));
273 	fclose(fp);
274 }
275 
276 ATF_TC_WITHOUT_HEAD(test_size_0);
277 ATF_TC_BODY(test_size_0, tc)
278 {
279 	/*
280 	 * POSIX mandates that we return EINVAL if size is 0.
281 	 */
282 
283 	FILE *fp;
284 
285 	fp = fmemopen(NULL, 0, "r+");
286 	ATF_REQUIRE(fp == NULL);
287 	ATF_REQUIRE(errno == EINVAL);
288 }
289 
290 ATF_TP_ADD_TCS(tp)
291 {
292 
293 	ATF_TP_ADD_TC(tp, test_autoalloc);
294 	ATF_TP_ADD_TC(tp, test_preexisting);
295 	ATF_TP_ADD_TC(tp, test_data_length);
296 	ATF_TP_ADD_TC(tp, test_binary);
297 	ATF_TP_ADD_TC(tp, test_append_binary_pos);
298 	ATF_TP_ADD_TC(tp, test_size_0);
299 
300 	return (atf_no_error());
301 }
302