1/*-
2 * Copyright (c) 2017 Enji Cooper <ngie@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 THE 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 THE 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#include <sys/cdefs.h>
27__FBSDID("$FreeBSD$");
28
29#include <sys/param.h>
30#include <sys/sbuf.h>
31#include <errno.h>
32#include <stdarg.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37
38#include <atf-c.h>
39
40#include "sbuf_test_common.h"
41
42static char	test_string[] = "this is a test string";
43#define	TEST_STRING_CHOP_COUNT	5
44_Static_assert(nitems(test_string) > TEST_STRING_CHOP_COUNT,
45    "test_string is too short");
46
47ATF_TC_WITHOUT_HEAD(sbuf_clear_test);
48ATF_TC_BODY(sbuf_clear_test, tc)
49{
50	struct sbuf *sb;
51	ssize_t buf_len;
52	pid_t child_proc;
53
54	sb = sbuf_new_auto();
55	ATF_REQUIRE_MSG(sb != NULL, "sbuf_new_auto failed: %s",
56	    strerror(errno));
57
58	ATF_REQUIRE_MSG(sbuf_cat(sb, test_string) == 0, "sbuf_cat failed");
59
60	/*
61	 * Cheat so we can get the contents of the buffer before calling
62	 * sbuf_finish(3) below, making additional sbuf changes impossible.
63	 */
64	child_proc = atf_utils_fork();
65	if (child_proc == 0) {
66		ATF_REQUIRE_EQ_MSG(0, sbuf_finish(sb), "sbuf_finish failed: %s",
67		    strerror(errno));
68
69		sbuf_putbuf(sb);
70		exit(0);
71	}
72	atf_utils_wait(child_proc, 0, test_string, "");
73
74	sbuf_clear(sb);
75
76	ATF_REQUIRE_MSG(sbuf_finish(sb) == 0, "sbuf_finish failed: %s",
77	    strerror(errno));
78
79	buf_len = sbuf_len(sb);
80	ATF_REQUIRE_MSG(buf_len == 0, "sbuf_len (%zd) != 0", buf_len);
81	ATF_REQUIRE_STREQ_MSG(sbuf_data(sb), "",
82	    "sbuf (\"%s\") was not empty", sbuf_data(sb));
83
84	sbuf_delete(sb);
85}
86
87ATF_TC_WITHOUT_HEAD(sbuf_done_and_sbuf_finish_test);
88ATF_TC_BODY(sbuf_done_and_sbuf_finish_test, tc)
89{
90	struct sbuf *sb;
91
92	sb = sbuf_new_auto();
93	ATF_REQUIRE_MSG(sb != NULL, "sbuf_new_auto failed: %s",
94	    strerror(errno));
95
96	ATF_CHECK(sbuf_done(sb) == 0);
97
98	ATF_REQUIRE_MSG(sbuf_finish(sb) == 0, "sbuf_finish failed: %s",
99	    strerror(errno));
100
101	ATF_CHECK(sbuf_done(sb) != 0);
102
103	sbuf_delete(sb);
104}
105
106static int
107drain_ret0(void *arg, const char *data, int len)
108{
109
110	(void)arg;
111	(void)data;
112	(void)len;
113
114	return (0);
115}
116
117ATF_TC_WITHOUT_HEAD(sbuf_drain_ret0_test);
118ATF_TC_BODY(sbuf_drain_ret0_test, tc)
119{
120	struct sbuf *sb;
121
122	sb = sbuf_new_auto();
123
124	sbuf_set_drain(sb, drain_ret0, NULL);
125
126	sbuf_cat(sb, test_string);
127
128	ATF_CHECK_EQ_MSG(-1, sbuf_finish(sb),
129	    "required to return error when drain func returns 0");
130	ATF_CHECK_EQ_MSG(EDEADLK, errno,
131	    "errno required to be EDEADLK when drain func returns 0");
132}
133
134ATF_TC_WITHOUT_HEAD(sbuf_len_test);
135ATF_TC_BODY(sbuf_len_test, tc)
136{
137	struct sbuf *sb;
138	ssize_t buf_len, test_string_len;
139	int i;
140
141	sb = sbuf_new_auto();
142	ATF_REQUIRE_MSG(sb != NULL, "sbuf_new_auto failed: %s",
143	    strerror(errno));
144
145	test_string_len = strlen(test_string);
146	for (i = 0; i < 20; i++) {
147		buf_len = sbuf_len(sb);
148		ATF_REQUIRE_MSG(buf_len == (ssize_t)(i * test_string_len),
149		    "sbuf_len (%zd) != %zu", buf_len, i * test_string_len);
150		ATF_REQUIRE_MSG(sbuf_cat(sb, test_string) == 0, "sbuf_cat failed");
151	}
152
153#ifdef	HAVE_SBUF_SET_FLAGS
154	sbuf_set_flags(sb, SBUF_INCLUDENUL);
155	ATF_REQUIRE_MSG((ssize_t)(i * test_string_len + 1) == sbuf_len(sb),
156	    "sbuf_len(..) didn't report the NUL char");
157#endif
158
159	ATF_REQUIRE_MSG(sbuf_finish(sb) == 0, "sbuf_finish failed: %s",
160	    strerror(errno));
161
162	sbuf_delete(sb);
163}
164
165ATF_TC_WITHOUT_HEAD(sbuf_new_fixedlen);
166ATF_TC_BODY(sbuf_new_fixedlen, tc)
167{
168	char buf[strlen(test_string) + 1];
169	struct sbuf sb;
170	pid_t child_proc;
171
172	sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
173
174	sbuf_cat(&sb, test_string);
175
176	child_proc = atf_utils_fork();
177	if (child_proc == 0) {
178		ATF_REQUIRE_EQ_MSG(0, sbuf_finish(&sb), "sbuf_finish failed: %s",
179		    strerror(errno));
180
181		sbuf_putbuf(&sb);
182		exit(0);
183	}
184	atf_utils_wait(child_proc, 0, test_string, "");
185
186	sbuf_putc(&sb, ' ');
187
188	ATF_CHECK_EQ_MSG(-1, sbuf_finish(&sb), "failed to return error on overflow");
189
190	sbuf_delete(&sb);
191}
192
193ATF_TC_WITHOUT_HEAD(sbuf_setpos_test);
194ATF_TC_BODY(sbuf_setpos_test, tc)
195{
196	struct sbuf *sb;
197	size_t test_string_chopped_len, test_string_len;
198	ssize_t buf_len;
199
200	sb = sbuf_new_auto();
201	ATF_REQUIRE_MSG(sb != NULL, "sbuf_new_auto failed: %s",
202	    strerror(errno));
203
204	/*
205	 * An obvious sanity check -- if sbuf_len(..) lies, these invariants
206	 * are impossible to test.
207	 */
208	ATF_REQUIRE(sbuf_len(sb) == 0);
209
210	ATF_CHECK(sbuf_setpos(sb, -1) == -1);
211	ATF_CHECK(sbuf_setpos(sb, 0) == 0);
212	ATF_CHECK(sbuf_setpos(sb, 1) == -1);
213
214	ATF_REQUIRE_MSG(sbuf_cat(sb, test_string) == 0, "sbuf_cat failed");
215
216	buf_len = sbuf_len(sb);
217	test_string_len = strlen(test_string);
218	test_string_chopped_len = test_string_len - TEST_STRING_CHOP_COUNT;
219	ATF_REQUIRE_MSG(buf_len == (ssize_t)test_string_len,
220	    "sbuf length (%zd) != test_string length (%zu)", buf_len,
221	    test_string_len);
222
223	/* Out of bounds (under length) */
224	ATF_CHECK(sbuf_setpos(sb, -1) == -1);
225	/*
226	 * Out of bounds (over length)
227	 *
228	 * Note: SBUF_INCLUDENUL not set, so take '\0' into account.
229	 */
230	ATF_CHECK(sbuf_setpos(sb, test_string_len + 2) == -1);
231	/* Within bounds */
232	ATF_CHECK(sbuf_setpos(sb, test_string_chopped_len) == 0);
233
234	ATF_REQUIRE_MSG(sbuf_finish(sb) == 0, "sbuf_finish failed: %s",
235	    strerror(errno));
236
237	buf_len = sbuf_len(sb);
238	ATF_REQUIRE_MSG(buf_len == (ssize_t)test_string_chopped_len,
239	    "sbuf_setpos didn't truncate string as expected");
240	ATF_REQUIRE_MSG(strncmp(sbuf_data(sb), test_string, buf_len) == 0,
241	    "sbuf (\"%s\") != test string (\"%s\") for [0,%zd]", sbuf_data(sb),
242	    test_string, buf_len);
243
244	sbuf_delete(sb);
245}
246
247ATF_TP_ADD_TCS(tp)
248{
249
250	ATF_TP_ADD_TC(tp, sbuf_clear_test);
251	ATF_TP_ADD_TC(tp, sbuf_done_and_sbuf_finish_test);
252	ATF_TP_ADD_TC(tp, sbuf_drain_ret0_test);
253	ATF_TP_ADD_TC(tp, sbuf_len_test);
254	ATF_TP_ADD_TC(tp, sbuf_new_fixedlen);
255#if 0
256	/* TODO */
257#ifdef	HAVE_SBUF_CLEAR_FLAGS
258	ATF_TP_ADD_TC(tp, sbuf_clear_flags_test);
259#endif
260#ifdef	HAVE_SBUF_GET_FLAGS
261	ATF_TP_ADD_TC(tp, sbuf_get_flags_test);
262#endif
263	ATF_TP_ADD_TC(tp, sbuf_new_positive_test);
264	ATF_TP_ADD_TC(tp, sbuf_new_negative_test);
265#ifdef	HAVE_SBUF_SET_FLAGS
266	ATF_TP_ADD_TC(tp, sbuf_set_flags_test);
267#endif
268#endif
269	ATF_TP_ADD_TC(tp, sbuf_setpos_test);
270
271	return (atf_no_error());
272}
273