#include <u.h>
#include <libc.h>
#include <stdarg.h>
#ifdef PLAN9
#include "p9limits.h"
#else
#include <limits.h>
#endif

/*
 * $Id: tdp.c,v 1.7 1998/11/10 22:35:28 mhw Exp $
 *
 * Test harness for the print family. This is a bit messy,
 * basically because I use the preprocessor to paste the
 * test cases together. This does allow us to run the
 * same test cases through sprint() and snprint() though.
 *
 * It's probably a bad idea to use fprint() to format and
 * report the error messages, as that is part of the code
 * being tested. It does let me run this test suite on
 * Plan 9 to check that my results match theirs.
 */

static int tests = 0;
static int failed = 0;

static char buf[8*1024];
static int length;

static void
start_test_sprint(void)
{
}

static void
check_test_sprint(char *expect)
{
	++tests;
	if (length != strlen(buf)) {
		++failed;
		fprint(2, "length %d != strlen %d when expecting %s\n",
			length, strlen(buf), expect);
	}
	++tests;
	if (strcmp(buf, expect) != 0) {
		++failed;
		fprint(2, "expected %s; got %s\n", expect, buf);
	}
}

#ifndef LIMIT
#define LIMIT 10
#endif

static void
start_test_snprint(void)
{
	memset(buf, '#', 10+LIMIT+10);
}

static void
check_test_snprint(char *expect)
{
	int i;

	++tests;
	if (length > LIMIT) {
		++failed;
		fprint(2, "length %d > %d when expecting %s\n",
			length, LIMIT, expect);
	}
	++tests;
	for (i = 0; i < 10; ++i)
		if (buf[i] != '#') {
			++failed;
			fprint(2, "hit lower fencepost when expecting %s\n",
				expect);
			break;
		}
	++tests;
	for (i = 10; i < 10+LIMIT; ) {
		Rune r;
		int b;
		if (buf[i] == 0)
			break;
		if (!fullrune(buf+i, 10+LIMIT-i))
			break;
		b = chartorune(&r, buf+i);
		if (i+b >= 10+LIMIT || (r == Runeerror && b == 1))
			break;
		i += b;
	}
	if (strncmp(buf+10, expect, i-10) != 0) {
		++failed;
		fprint(2, "expected %.*s; got %.*s\n", i-10, expect, length, buf+10);
	}
	++tests;
	for (i = 10+length; i < 10+LIMIT+10; ++i)
		if (buf[i] != '#') {
			++failed;
			fprint(2, "hit upper fencepost when expecting %s\n",
				expect);
			break;
		}
}

/*
 * Pull in some Runes.
 */
#include "../libXg/latin1.c"

typedef
struct Point {
	int x;
	int y;
} Point;

typedef
struct Rectangle {
	Point min;
	Point max;
} Rectangle;

int
Pconv(void *v, Fconv *fp)
{
	char str[50];
	va_list ap = v;
	Point *p = va_arg(ap, Point *);

	sprint(str, "(%d,%d)", p->x, p->y);
	strconv(str, fp);
	return ap-(va_list)v;
}

int
Rconv(void *v, Fconv *fp)
{
	char str[50];
	va_list ap = v;
	Rectangle *r = va_arg(ap, Rectangle *);

	sprint(str, "(%P,%P)", &r->min, &r->max);
	strconv(str, fp);
	return ap-(va_list)v;
}

int
main(void)
{
	Point p;
	Rectangle r;
	char expect[8*1024];

	++tests;
	if (fmtinstall('P', Pconv) != 0) {
		++failed;
		fprint(2, "couldn't install Pconv\n");
	}
	++tests;
	if (fmtinstall('R', Rconv) != 0) {
		++failed;
		fprint(2, "couldn't install Rconv\n");
	}
	++tests;
	if (fmtinstall(-1, Rconv) != -1) {
		++failed;
		fprint(2, "managed to install conversion routine for bad character\n");
	}
	++tests;
	if (fmtinstall(256, Rconv) != -1) {
		++failed;
		fprint(2, "managed to install conversion routine for bad character\n");
	}

#define A1 buf,
#define T(ARGS, EXPECT) 		\
	start_test_sprint();		\
	length = sprint ARGS;		\
	check_test_sprint(EXPECT);
#include "tdp-tests.h"
#undef A1
#undef T

#define A1 buf+10, LIMIT,
#define T(ARGS, EXPECT) 		\
	start_test_snprint();		\
	length = snprint ARGS;		\
	check_test_snprint(EXPECT);
#include "tdp-tests.h"
#undef A1
#undef T

	if (failed) {
		fprint(2, "tdp: failed %d of %d tests.\n", failed, tests);
		return 1;
	} else {
		fprint(2, "tdp: passed all %d tests.\n", tests);
		return 0;
	}
}
