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