1/*-
2 * Copyright (c) 2006 Michael Bushkov <bushman@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/queue.h>
27
28#define DECLARE_TEST_DATA(ent)						\
29struct ent##_entry {							\
30	struct ent data;						\
31	STAILQ_ENTRY(ent##_entry) entries;				\
32};									\
33									\
34struct ent##_test_data {						\
35	void (*clone_func)(struct ent *, struct ent const *);		\
36	void (*free_func)(struct ent *);				\
37									\
38	STAILQ_HEAD(ent_head, ent##_entry) snapshot_data;		\
39};									\
40									\
41void __##ent##_test_data_init(struct ent##_test_data *, 		\
42	void (*)(struct ent *, struct ent const *),			\
43	void (*freef)(struct ent *));		 			\
44void __##ent##_test_data_destroy(struct ent##_test_data *);		\
45									\
46void __##ent##_test_data_append(struct ent##_test_data *, struct ent *data);\
47int __##ent##_test_data_foreach(struct ent##_test_data *,		\
48	int (*)(struct ent *, void *), void *);				\
49int __##ent##_test_data_compare(struct ent##_test_data *,		\
50	struct ent##_test_data *, int (*)(struct ent *, struct ent *,	\
51	void *), void *);						\
52struct ent *__##ent##_test_data_find(struct ent##_test_data *, struct ent *,\
53	int (*)(struct ent *, struct ent *, void *), void *);		\
54void __##ent##_test_data_clear(struct ent##_test_data *);
55
56#define TEST_DATA_INIT(ent, td, clonef, freef)\
57	__##ent##_test_data_init(td, clonef, freef)
58#define TEST_DATA_DESTROY(ent, td) __##ent##_test_data_destroy(td)
59#define TEST_DATA_APPEND(ent, td, d) __##ent##_test_data_append(td, d)
60#define TEST_DATA_FOREACH(ent, td, f, mdata)\
61	__##ent##_test_data_foreach(td, f, mdata)
62#define TEST_DATA_COMPARE(ent, td1, td2, fcmp, mdata)\
63	__##ent##_test_data_compare(td1, td2, fcmp, mdata);
64#define TEST_DATA_FIND(ent, td, d, fcmp, mdata)\
65	__##ent##_test_data_find(td, d, fcmp, mdata)
66#define TEST_DATA_CLEAR(ent, td) __##ent##_test_data_clear(td)
67
68#define IMPLEMENT_TEST_DATA(ent)					\
69void									\
70__##ent##_test_data_init(struct ent##_test_data *td,			\
71	void (*clonef)(struct ent *, struct ent const *),		\
72	void (*freef)(struct ent *))					\
73{									\
74	ATF_REQUIRE(td != NULL);					\
75	ATF_REQUIRE(clonef != NULL);					\
76	ATF_REQUIRE(freef != NULL);					\
77									\
78	memset(td, 0, sizeof(*td));					\
79	td->clone_func = clonef;					\
80	td->free_func = freef;						\
81	STAILQ_INIT(&td->snapshot_data);				\
82}									\
83									\
84void 									\
85__##ent##_test_data_destroy(struct ent##_test_data *td)			\
86{									\
87	__##ent##_test_data_clear(td);					\
88}									\
89									\
90void 									\
91__##ent##_test_data_append(struct ent##_test_data *td, struct ent *app_data)\
92{									\
93	struct ent##_entry *e;						\
94									\
95	ATF_REQUIRE(td != NULL);					\
96	ATF_REQUIRE(app_data != NULL);					\
97									\
98	e = (struct ent##_entry *)malloc(sizeof(struct ent##_entry));	\
99	ATF_REQUIRE(e != NULL);						\
100	memset(e, 0, sizeof(struct ent##_entry));			\
101									\
102	td->clone_func(&e->data, app_data);				\
103	STAILQ_INSERT_TAIL(&td->snapshot_data, e, entries);		\
104}									\
105									\
106int									\
107__##ent##_test_data_foreach(struct ent##_test_data *td,			\
108	int (*forf)(struct ent *, void *), void *mdata)			\
109{									\
110	struct ent##_entry *e;						\
111	int rv;								\
112									\
113	ATF_REQUIRE(td != NULL);					\
114	ATF_REQUIRE(forf != NULL);					\
115									\
116	rv = 0;								\
117	STAILQ_FOREACH(e, &td->snapshot_data, entries) {		\
118		rv = forf(&e->data, mdata);				\
119		if (rv != 0)						\
120			break;						\
121	}								\
122									\
123	return (rv);							\
124}									\
125									\
126int									\
127__##ent##_test_data_compare(struct ent##_test_data *td1, struct ent##_test_data *td2,\
128	int (*cmp_func)(struct ent *, struct ent *, void *), void *mdata)\
129{									\
130	struct ent##_entry *e1, *e2;					\
131	int rv;								\
132									\
133	ATF_REQUIRE(td1 != NULL);					\
134	ATF_REQUIRE(td2 != NULL);					\
135	ATF_REQUIRE(cmp_func != NULL);					\
136									\
137	e1 = STAILQ_FIRST(&td1->snapshot_data);				\
138	e2 = STAILQ_FIRST(&td2->snapshot_data);				\
139									\
140	rv = 0;								\
141	do {								\
142		if ((e1 == NULL) || (e2 == NULL)) {			\
143			if (e1 == e2)					\
144				return (0);				\
145			else						\
146				return (-1);				\
147		}							\
148									\
149		rv = cmp_func(&e1->data, &e2->data, mdata);		\
150		e1 = STAILQ_NEXT(e1, entries);				\
151		e2 = STAILQ_NEXT(e2, entries);				\
152	} while (rv == 0);						\
153									\
154	return (rv);							\
155}									\
156									\
157struct ent *								\
158__##ent##_test_data_find(struct ent##_test_data *td, struct ent *data,	\
159	int (*cmp)(struct ent *, struct ent *, void *), void *mdata)	\
160{									\
161	struct ent##_entry *e;						\
162	struct ent *result;						\
163									\
164	ATF_REQUIRE(td != NULL);					\
165	ATF_REQUIRE(cmp != NULL);					\
166									\
167	result = NULL;							\
168	STAILQ_FOREACH(e, &td->snapshot_data, entries) {		\
169		if (cmp(&e->data, data, mdata) == 0) {			\
170			result = &e->data;				\
171			break;						\
172		}							\
173	}								\
174									\
175	return (result);						\
176}									\
177									\
178									\
179void									\
180__##ent##_test_data_clear(struct ent##_test_data *td)			\
181{									\
182	struct ent##_entry *e;						\
183	ATF_REQUIRE(td != NULL);					\
184									\
185	while (!STAILQ_EMPTY(&td->snapshot_data)) {			\
186		e = STAILQ_FIRST(&td->snapshot_data);			\
187		STAILQ_REMOVE_HEAD(&td->snapshot_data, entries);	\
188									\
189		td->free_func(&e->data);				\
190		free(e);						\
191		e = NULL;						\
192	}								\
193}
194
195#define DECLARE_TEST_FILE_SNAPSHOT(ent)					\
196struct ent##_snp_param {						\
197	FILE *fp;							\
198	void (*sdump_func)(struct ent *, char *, size_t);		\
199};									\
200									\
201int __##ent##_snapshot_write_func(struct ent *, void *);		\
202int __##ent##_snapshot_write(char const *, struct ent##_test_data *,	\
203	void (*)(struct ent *, char *, size_t));			\
204int __##ent##_snapshot_read(char const *, struct ent##_test_data *,	\
205	int (*)(struct ent *, char *));
206
207#define TEST_SNAPSHOT_FILE_WRITE(ent, fname, td, f)			\
208	__##ent##_snapshot_write(fname, td, f)
209#define TEST_SNAPSHOT_FILE_READ(ent, fname, td, f)			\
210	__##ent##_snapshot_read(fname, td, f)
211
212#define IMPLEMENT_TEST_FILE_SNAPSHOT(ent)				\
213int									\
214__##ent##_snapshot_write_func(struct ent *data, void *mdata)		\
215{									\
216	char buffer[1024];						\
217	struct ent##_snp_param *param;					\
218									\
219	ATF_REQUIRE(data != NULL);					\
220									\
221	param = (struct ent##_snp_param *)mdata;			\
222	param->sdump_func(data, buffer, sizeof(buffer));		\
223	fputs(buffer, param->fp);					\
224	fputc('\n', param->fp);						\
225									\
226	return (0);							\
227}									\
228									\
229int									\
230__##ent##_snapshot_write(char const *fname, struct ent##_test_data *td,	\
231	void (*sdump_func)(struct ent *, char *, size_t))		\
232{									\
233	struct ent##_snp_param	param;					\
234									\
235	ATF_REQUIRE(fname != NULL);					\
236	ATF_REQUIRE(td != NULL);					\
237									\
238	param.fp = fopen(fname, "w");					\
239	if (param.fp == NULL)						\
240		return (-1);						\
241									\
242	param.sdump_func = sdump_func;					\
243	__##ent##_test_data_foreach(td, __##ent##_snapshot_write_func, &param);\
244	fclose(param.fp);						\
245									\
246	return (0);							\
247}									\
248									\
249int									\
250__##ent##_snapshot_read(char const *fname, struct ent##_test_data *td,	\
251	int (*read_func)(struct ent *, char *))				\
252{									\
253	struct ent data;						\
254	FILE *fi;							\
255	size_t len;							\
256	int rv;								\
257									\
258	ATF_REQUIRE(fname != NULL);					\
259	ATF_REQUIRE(td != NULL);					\
260									\
261	fi = fopen(fname, "r");						\
262	if (fi == NULL)							\
263		return (-1);						\
264									\
265	rv = 0;								\
266	while (!feof(fi)) {						\
267		char *buf = fgetln(fi, &len);				\
268		if (buf == NULL || len <= 1)				\
269			continue;					\
270		if (buf[len - 1] == '\n')				\
271			buf[len - 1] = '\0';				\
272		else							\
273			buf[len] = '\0';				\
274		if (buf[0] == '#')					\
275			continue;					\
276		rv = read_func(&data, buf);				\
277		if (rv == 0) {						\
278			__##ent##_test_data_append(td, &data);		\
279			td->free_func(&data);				\
280		} else 							\
281			goto fin;					\
282	}								\
283									\
284fin:									\
285	fclose(fi);							\
286	return (rv);							\
287}
288
289#define DECLARE_1PASS_TEST(ent)						\
290int __##ent##_1pass_test(struct ent##_test_data *, 			\
291	int (*)(struct ent *, void *),					\
292	void *);
293
294#define DO_1PASS_TEST(ent, td, f, mdata)				\
295	__##ent##_1pass_test(td, f, mdata)
296
297#define IMPLEMENT_1PASS_TEST(ent)					\
298int									\
299__##ent##_1pass_test(struct ent##_test_data *td, 			\
300	int (*tf)(struct ent *, void *),				\
301	void *mdata)							\
302{									\
303	int rv;								\
304	rv = __##ent##_test_data_foreach(td, tf, mdata);		\
305									\
306	return (rv);							\
307}
308
309#define DECLARE_2PASS_TEST(ent)						\
310int __##ent##_2pass_test(struct ent##_test_data *, 			\
311	struct ent##_test_data *, 					\
312	int (*)(struct ent *, struct ent *, void *), void *);
313
314#define DO_2PASS_TEST(ent, td1, td2, f, mdata)				\
315	__##ent##_2pass_test(td1, td2, f, mdata)
316
317#define IMPLEMENT_2PASS_TEST(ent)					\
318int									\
319__##ent##_2pass_test(struct ent##_test_data *td1,			\
320	struct ent##_test_data *td2,					\
321	int (*cmp_func)(struct ent *, struct ent *, void *),		\
322	void *cmp_mdata)						\
323{									\
324	int rv;								\
325									\
326	rv = __##ent##_test_data_compare(td1, td2, cmp_func, cmp_mdata);\
327	return (rv);							\
328}
329