1#ifndef lint
2static char *rcsid = "$Id";
3#endif
4
5/*
6 * Copyright (c) 2002 Japan Network Information Center.
7 * All rights reserved.
8 *
9 * By using this file, you agree to the terms and conditions set forth bellow.
10 *
11 * 			LICENSE TERMS AND CONDITIONS
12 *
13 * The following License Terms and Conditions apply, unless a different
14 * license is obtained from Japan Network Information Center ("JPNIC"),
15 * a Japanese association, Kokusai-Kougyou-Kanda Bldg 6F, 2-3-4 Uchi-Kanda,
16 * Chiyoda-ku, Tokyo 101-0047, Japan.
17 *
18 * 1. Use, Modification and Redistribution (including distribution of any
19 *    modified or derived work) in source and/or binary forms is permitted
20 *    under this License Terms and Conditions.
21 *
22 * 2. Redistribution of source code must retain the copyright notices as they
23 *    appear in each source code file, this License Terms and Conditions.
24 *
25 * 3. Redistribution in binary form must reproduce the Copyright Notice,
26 *    this License Terms and Conditions, in the documentation and/or other
27 *    materials provided with the distribution.  For the purposes of binary
28 *    distribution the "Copyright Notice" refers to the following language:
29 *    "Copyright (c) 2000-2002 Japan Network Information Center.  All rights reserved."
30 *
31 * 4. The name of JPNIC may not be used to endorse or promote products
32 *    derived from this Software without specific prior written approval of
33 *    JPNIC.
34 *
35 * 5. Disclaimer/Limitation of Liability: THIS SOFTWARE IS PROVIDED BY JPNIC
36 *    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 *    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
38 *    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JPNIC BE LIABLE
39 *    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 *    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 *    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42 *    BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
43 *    WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
44 *    OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
45 *    ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
46 */
47
48#include <stddef.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <assert.h>
53
54#include <idn/result.h>
55#include <idn/ucs4.h>
56#include <testsuite.h>
57
58typedef struct idn_testcase *idn_testcase_t;
59
60struct idn_testcase {
61	char *title;
62	idn_testsuite_testproc_t proc;
63};
64
65struct idn_testsuite {
66	idn_testcase_t testcases;
67	int ntestcases;
68	int testcase_size;
69
70	int npassed;
71	int nfailed;
72	int nskipped;
73	idn_testcase_t current_testcase;
74	idn_teststatus_t current_status;
75
76	idn_testsuite_msgproc_t msgproc;
77	int verbose;
78};
79
80#define INITIAL_TESTCASE_SIZE	16
81#define INITIAL_SETUP_SIZE	4
82#define INITIAL_TEARDOWN_SIZE	4
83
84static void run_internal(idn_testsuite_t ctx, char *titles[]);
85static char *make_hex_string(const char *string);
86static char *make_hex_ucs4string(const unsigned long *string);
87static void put_failure_message(idn_testsuite_t ctx, const char *msg,
88				const char *file, int lineno);
89static void idn_testsuite_msgtostderr(const char *msg);
90
91int
92idn_testsuite_create(idn_testsuite_t *ctxp) {
93	idn_testsuite_t ctx = NULL;
94
95	assert(ctxp != NULL);
96
97	ctx = (idn_testsuite_t) malloc(sizeof(struct idn_testsuite));
98	if (ctx == NULL)
99		goto error;
100
101	ctx->testcases = NULL;
102	ctx->ntestcases = 0;
103	ctx->testcase_size = 0;
104	ctx->npassed = 0;
105	ctx->nfailed = 0;
106	ctx->nskipped = 0;
107	ctx->current_testcase = NULL;
108	ctx->current_status = idn_teststatus_pass;
109	ctx->msgproc = NULL;
110	ctx->verbose = 0;
111
112	ctx->testcases = (idn_testcase_t) malloc(sizeof(struct idn_testcase)
113						 * INITIAL_TESTCASE_SIZE);
114	if (ctx->testcases == NULL)
115		goto error;
116	ctx->testcase_size = INITIAL_TESTCASE_SIZE;
117
118	*ctxp = ctx;
119	return (1);
120
121error:
122	if (ctx != NULL)
123		free(ctx->testcases);
124	free(ctx);
125	return (0);
126}
127
128void
129idn_testsuite_destroy(idn_testsuite_t ctx) {
130	int i;
131
132	assert(ctx != NULL);
133
134	for (i = 0; i < ctx->ntestcases; i++)
135		free(ctx->testcases[i].title);
136
137	free(ctx->testcases);
138	free(ctx);
139}
140
141int
142idn_testsuite_addtestcase(idn_testsuite_t ctx, const char *title,
143			  idn_testsuite_testproc_t proc) {
144	char *dup_title = NULL;
145	idn_testcase_t new_buffer = NULL;
146	idn_testcase_t new_testcase;
147	int new_size;
148
149	assert(ctx != NULL && title != NULL && proc != NULL);
150
151	dup_title = (char *)malloc(strlen(title) + 1);
152	if (dup_title == NULL)
153		goto error;
154	strcpy(dup_title, title);
155
156	if (ctx->ntestcases == ctx->testcase_size) {
157		new_size = ctx->testcase_size + INITIAL_TESTCASE_SIZE;
158		new_buffer = (idn_testcase_t)
159			     realloc(ctx->testcases,
160				     sizeof(struct idn_testcase) * new_size);
161		if (new_buffer == NULL)
162			goto error;
163		ctx->testcases = new_buffer;
164		ctx->testcase_size = new_size;
165	}
166
167	new_testcase = ctx->testcases + ctx->ntestcases;
168	new_testcase->title = dup_title;
169	new_testcase->proc = proc;
170	ctx->ntestcases++;
171	return (1);
172
173error:
174	free(dup_title);
175	free(new_buffer);
176	return (0);
177}
178
179int
180idn_testsuite_ntestcases(idn_testsuite_t ctx) {
181	assert(ctx != NULL);
182	return (ctx->ntestcases);
183}
184
185void
186idn_testsuite_setverbose(idn_testsuite_t ctx) {
187	assert(ctx != NULL);
188	ctx->verbose = 1;
189}
190
191void
192idn_testsuite_unsetverbose(idn_testsuite_t ctx) {
193	assert(ctx != NULL);
194	ctx->verbose = 0;
195}
196
197static void
198run_internal(idn_testsuite_t ctx, char *titles[]) {
199	int i, j;
200	int run_testcase;
201	const char *status;
202
203	assert(ctx != NULL);
204
205	ctx->npassed = 0;
206	ctx->nfailed = 0;
207	ctx->nskipped = 0;
208
209	for (i = 0; i < ctx->ntestcases; i++) {
210		ctx->current_testcase = ctx->testcases + i;
211		ctx->current_status = idn_teststatus_pass;
212
213		if (titles == NULL)
214			run_testcase = 1;
215		else {
216			run_testcase = 0;
217			for (j = 0; titles[j] != NULL; j++) {
218				if (strcmp(ctx->current_testcase->title,
219				    titles[j]) == 0) {
220					run_testcase = 1;
221					break;
222				}
223			}
224		}
225
226		if (!run_testcase) {
227			ctx->nskipped++;
228			continue;
229		}
230		if (ctx->verbose) {
231			fprintf(stderr, "start testcase %d: %s\n", i + 1,
232				ctx->testcases[i].title);
233		}
234		(ctx->testcases[i].proc)(ctx);
235		status = idn_teststatus_tostring(ctx->current_status);
236		if (ctx->verbose) {
237			fprintf(stderr, "end testcase %d: %s\n", i + 1,
238				status);
239		}
240
241		switch (ctx->current_status) {
242		case idn_teststatus_pass:
243			ctx->npassed++;
244			break;
245		case idn_teststatus_fail:
246			ctx->nfailed++;
247			break;
248		case idn_teststatus_skip:
249			ctx->nskipped++;
250			break;
251		}
252	}
253}
254
255void
256idn_testsuite_runall(idn_testsuite_t ctx) {
257	assert(ctx != NULL);
258	run_internal(ctx, NULL);
259}
260
261void
262idn_testsuite_run(idn_testsuite_t ctx, char *titles[]) {
263	assert(ctx != NULL && titles != NULL);
264	run_internal(ctx, titles);
265}
266
267int
268idn_testsuite_npassed(idn_testsuite_t ctx) {
269	assert(ctx != NULL);
270	return (ctx->npassed);
271}
272
273int
274idn_testsuite_nfailed(idn_testsuite_t ctx) {
275	assert(ctx != NULL);
276	return (ctx->nfailed);
277}
278
279int
280idn_testsuite_nskipped(idn_testsuite_t ctx) {
281	assert(ctx != NULL);
282	return (ctx->nskipped);
283}
284
285idn_teststatus_t
286idn_testsuite_getstatus(idn_testsuite_t ctx) {
287	assert(ctx != NULL);
288	return (ctx->current_status);
289}
290
291void
292idn_testsuite_setstatus(idn_testsuite_t ctx, idn_teststatus_t status) {
293	assert(ctx != NULL);
294	assert(status == idn_teststatus_pass ||
295	       status == idn_teststatus_fail ||
296	       status == idn_teststatus_skip);
297
298	ctx->current_status = status;
299}
300
301const char *
302idn_teststatus_tostring(idn_teststatus_t status) {
303	assert(status == idn_teststatus_pass ||
304	       status == idn_teststatus_fail ||
305	       status == idn_teststatus_skip);
306
307	switch (status) {
308		case idn_teststatus_pass:
309			return "pass";
310			break;
311		case idn_teststatus_fail:
312			return "failed";
313			break;
314		case idn_teststatus_skip:
315			return "skipped";
316			break;
317	}
318
319	return "unknown";
320}
321
322void
323idn_testsuite_assert(idn_testsuite_t ctx, const char *msg,
324		     const char *file, int lineno) {
325	assert(ctx != NULL && msg != NULL && file != NULL);
326
327	if (idn_testsuite_getstatus(ctx) != idn_teststatus_pass)
328		return;
329	idn_testsuite_setstatus(ctx, idn_teststatus_fail);
330	put_failure_message(ctx, msg, file, lineno);
331}
332
333void
334idn_testsuite_assertint(idn_testsuite_t ctx, int gotten, int expected,
335			const char *file, int lineno) {
336	char msg[256]; /* large enough */
337
338	assert(ctx != NULL && file != NULL);
339
340	if (idn_testsuite_getstatus(ctx) != idn_teststatus_pass)
341		return;
342	if (expected == gotten)
343		return;
344	idn_testsuite_setstatus(ctx, idn_teststatus_fail);
345
346	sprintf(msg, "`%d' expected, but got `%d'", expected, gotten);
347	put_failure_message(ctx, msg, file, lineno);
348}
349
350void
351idn_testsuite_assertstring(idn_testsuite_t ctx,
352			   const char *gotten, const char *expected,
353			   const char *file, int lineno) {
354	char *expected_hex = NULL;
355	char *gotten_hex = NULL;
356	char *msg;
357
358	assert(ctx != NULL && gotten != NULL && expected != NULL &&
359	       file != NULL);
360
361	if (idn_testsuite_getstatus(ctx) != idn_teststatus_pass)
362		return;
363	if (strcmp(expected, gotten) == 0)
364		return;
365	idn_testsuite_setstatus(ctx, idn_teststatus_fail);
366
367	msg = (char *)malloc(strlen(expected) * 4 + strlen(gotten) * 4 + 32);
368	expected_hex = make_hex_string(expected);
369	gotten_hex = make_hex_string(gotten);
370	if (msg == NULL || expected_hex == NULL || gotten_hex == NULL) {
371		msg = "";
372	} else {
373		sprintf(msg, "`%s' expected, but got `%s'",
374			expected_hex, gotten_hex);
375	}
376
377	put_failure_message(ctx, msg, file, lineno);
378
379	free(msg);
380	free(expected_hex);
381	free(gotten_hex);
382}
383
384void
385idn_testsuite_assertptr(idn_testsuite_t ctx, const void *gotten,
386			const void *expected, const char *file, int lineno) {
387	char *msg;
388
389	assert(ctx != NULL && file != NULL);
390
391	if (idn_testsuite_getstatus(ctx) != idn_teststatus_pass)
392		return;
393	if (expected == gotten)
394		return;
395	idn_testsuite_setstatus(ctx, idn_teststatus_fail);
396
397	if (expected == NULL)
398		msg = "NULL expected, but got non-NULL";
399	else if (gotten == NULL)
400		msg = "non-NULL expected, but got NULL";
401	else
402		msg = "expected pointer != gotten pointer";
403	put_failure_message(ctx, msg, file, lineno);
404}
405
406void
407idn_testsuite_assertptrne(idn_testsuite_t ctx,
408			  const void *gotten, const void *unexpected,
409			  const char *file, int lineno) {
410	char *msg;
411
412	assert(ctx != NULL && file != NULL);
413
414	if (idn_testsuite_getstatus(ctx) != idn_teststatus_pass)
415		return;
416	if (unexpected != gotten)
417		return;
418	idn_testsuite_setstatus(ctx, idn_teststatus_fail);
419
420	if (unexpected == NULL)
421		msg = "non-NULL unexpected, but got NULL";
422	else if (gotten == NULL)
423		msg = "non-NULL expected, but got NULL";
424	else
425		msg = "expected pointer == gotten pointer";
426	put_failure_message(ctx, msg, file, lineno);
427}
428
429void
430idn_testsuite_assertresult(idn_testsuite_t ctx,
431			   idn_result_t gotten, idn_result_t expected,
432			   const char *file, int lineno) {
433	char msg[256]; /* large enough */
434
435	assert(ctx != NULL && file != NULL);
436
437	if (idn_testsuite_getstatus(ctx) != idn_teststatus_pass)
438		return;
439	if (expected == gotten)
440		return;
441	idn_testsuite_setstatus(ctx, idn_teststatus_fail);
442
443	sprintf(msg, "`%s' expected, but got `%s'",
444		idn_result_tostring(expected), idn_result_tostring(gotten));
445	put_failure_message(ctx, msg, file, lineno);
446}
447
448void
449idn_testsuite_assertucs4string(idn_testsuite_t ctx,
450			       const unsigned long *gotten,
451			       const unsigned long *expected,
452			       const char *file, int lineno) {
453	char *expected_hex = NULL;
454	char *gotten_hex = NULL;
455	char *msg;
456
457	assert(ctx != NULL && gotten != NULL && expected != NULL &&
458	       file != NULL);
459
460	if (idn_testsuite_getstatus(ctx) != idn_teststatus_pass)
461		return;
462	if (idn_ucs4_strcmp(expected, gotten) == 0)
463		return;
464	idn_testsuite_setstatus(ctx, idn_teststatus_fail);
465
466	msg = (char *)malloc(idn_ucs4_strlen(expected) * 8 +
467			     idn_ucs4_strlen(gotten) * 8 + 32);
468	expected_hex = make_hex_ucs4string(expected);
469	gotten_hex = make_hex_ucs4string(gotten);
470	if (msg == NULL || expected_hex == NULL || gotten_hex == NULL) {
471		msg = "";
472	} else {
473		sprintf(msg, "`%s' expected, but got `%s'",
474			expected_hex, gotten_hex);
475	}
476
477	put_failure_message(ctx, msg, file, lineno);
478
479	free(msg);
480	free(expected_hex);
481	free(gotten_hex);
482}
483
484static char *
485make_hex_string(const char *string) {
486	static const char hex[] = {"0123456789abcdef"};
487	char *hex_string;
488	const char *src;
489	char *dst;
490
491	hex_string = (char *)malloc((strlen(string)) * 4 + 1);
492	if (hex_string == NULL)
493		return NULL;
494
495	for (src = string, dst = hex_string; *src != '\0'; src++) {
496		if (0x20 <= *src && *src <= 0x7e && *src != '\\') {
497			*dst++ = *src;
498		} else {
499			*dst++ = '\\';
500			*dst++ = 'x';
501			*dst++ = hex[*(const unsigned char *)src >> 4];
502			*dst++ = hex[*src & 0x0f];
503		}
504	}
505	*dst = '\0';
506
507	return hex_string;
508}
509
510#define UCS4_MAX 0x10fffffUL
511
512static char *
513make_hex_ucs4string(const unsigned long *string) {
514	static const char hex[] = {"0123456789abcdef"};
515	char *hex_string;
516	const unsigned long *src;
517	char *dst;
518
519	hex_string = (char *)malloc((idn_ucs4_strlen(string)) * 8 + 1);
520	if (hex_string == NULL)
521		return NULL;
522
523	for (src = string, dst = hex_string; *src != '\0'; src++) {
524		if (0x20 <= *src && *src <= 0x7e && *src != '\\') {
525			*dst++ = *src;
526		} else if (*src <= UCS4_MAX) {
527			*dst++ = '\\';
528			*dst++ = 'u';
529			if (*src >= 0x100000) {
530				*dst++ = hex[(*src >> 20) & 0x0f];
531			}
532			if (*src >= 0x10000) {
533				*dst++ = hex[(*src >> 16) & 0x0f];
534			}
535			*dst++ = hex[(*src >> 12) & 0x0f];
536			*dst++ = hex[(*src >> 8) & 0x0f];
537			*dst++ = hex[(*src >> 4) & 0x0f];
538			*dst++ = hex[*src & 0x0f];
539		} else {
540			*dst++ = '\\';
541			*dst++ = 'u';
542			*dst++ = '?';
543			*dst++ = '?';
544			*dst++ = '?';
545			*dst++ = '?';
546		}
547	}
548	*dst = '\0';
549
550	return hex_string;
551}
552
553static void
554put_failure_message(idn_testsuite_t ctx, const char *msg, const char *file,
555		    int lineno) {
556	idn_testsuite_msgproc_t proc;
557	char buffer[256];
558	const char *title;
559
560        proc = (ctx->msgproc == NULL) ?
561               idn_testsuite_msgtostderr : ctx->msgproc;
562	title = (ctx->current_testcase != NULL &&
563		 ctx->current_testcase->title != NULL) ?
564		 ctx->current_testcase->title : "anonymous";
565
566	sprintf(buffer, "%.100s: In test `%.100s':", file, title);
567	(*proc)(buffer);
568
569	sprintf(buffer, "%.100s:%d: failed (%.100s)", file, lineno, msg);
570	(*proc)(buffer);
571}
572
573
574static void
575idn_testsuite_msgtostderr(const char *msg) {
576	fputs(msg, stderr);
577	fputc('\n', stderr);
578}
579