/*

	9unit
	Copyright (C) Jonathan Lamothe <jonathan@jlamothe.net>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this program.  If not, see
	<http://www.gnu.org/licenses/>.

*/

#include <u.h>
#include <libc.h>
#include <stdio.h>

#include "../9unit.h"
#include "util.h"

// Internal Prototypes

static void chk_TestState_eq_test(TestState *, void *, void *);
static TestResult chk_int_eq_test(TestState *, void *, void *);
static TestResult chk_ptr_eq_test(TestState *, void *, void *);
static TestResult chk_ptr_ne_test(TestState *, void *, void *);
static TestResult chk_str_eq_test(TestState *, void *, void *);

// discard report data
static void report(const char *);

// Public Functions

void
mk_sample_state(TestState *s)
{
	memset(s, 0, sizeof(TestState));
	s->passed = 1;
	s->failed = 2;
	s->pending = 3;
	s->run = 6;
	s->report = report;
}

void
chk_TestState_eq(
	TestState *s,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	test_context_compare(
		s,
		context,
		chk_TestState_eq_test,
		actual,
		expected
	);
}

void
chk_int_eq(
	TestState *s,
	const char *context,
	int actual,
	int expected
)
{
	single_test_context_compare(
		s,
		context,
		chk_int_eq_test,
		&actual,
		&expected
	);
}

void
chk_ptr_eq(
	TestState *s,
	const char *context,
	const void *actual,
	const void *expected
)
{
	single_test_context_compare(
		s,
		context,
		chk_ptr_eq_test,
		actual,
		expected
	);
}

void
chk_ptr_ne(
	TestState *s,
	const char *context,
	const void *actual,
	const void *prohibited
)
{
	single_test_context_compare(
		s,
		context,
		chk_ptr_ne_test,
		actual,
		prohibited
	);
}

void
chk_str_eq(
	TestState *s,
	const char *context,
	const char *actual,
	const char *expected
)
{
	single_test_context_compare(
		s,
		context,
		chk_str_eq_test,
		actual,
		expected
	);
}

// Local Functions

static void
chk_TestState_eq_test(
	TestState *s,
	void *aptr,
	void *eptr
)
{
	if (!eptr)
	{
		chk_ptr_eq(s, "is null", aptr, 0);
		return;
	}

	TestState
		*actual = aptr,
		*expected = eptr;

	chk_int_eq(s, "run", actual->run, expected->run);
	chk_int_eq(s, "passed", actual->passed, expected->passed);
	chk_int_eq(s, "failed", actual->failed, expected->failed);
	chk_int_eq(s, "pending", actual->pending, expected->pending);
	chk_ptr_eq(s, "first_log", actual->first_log, expected->first_log);
	chk_ptr_eq(s, "last_log", actual->last_log, expected->last_log);
	chk_ptr_eq(s, "ptr", actual->ptr, expected->ptr);
	chk_str_eq(s, "context", actual->context, expected->context);
	chk_str_eq(s, "full_context", actual->full_context, expected->full_context);
	chk_int_eq(s, "depth", actual->depth, expected->depth);
	chk_ptr_eq(s, "report()", actual->report, expected->report);
}

static TestResult
chk_int_eq_test(
	TestState *s,
	void *aptr,
	void *eptr
)
{
	int
		*actual = aptr,
		*expected = eptr;

	if (*actual == *expected)
		return test_success;

	// log the error
	char str[STR_BUF_SIZE];
	log_test_context(s);
	append_test_log(s, "ERROR:");
	snprintf(str, STR_BUF_SIZE, "\texpected: %d", *expected);
	append_test_log(s, str);
	snprintf(str, STR_BUF_SIZE, "\tactual: %d", *actual);
	append_test_log(s, str);

	return test_failure;
}

static TestResult
chk_ptr_eq_test(
	TestState *s,
	void *actual,
	void *expected
)
{
	if (actual == expected)
		return test_success;

	// log the error
	char str[STR_BUF_SIZE];
	log_test_context(s);
	append_test_log(s, "ERROR:");
	snprintf(str, STR_BUF_SIZE, "\texpected: 0x%x", expected);
	append_test_log(s, str);
	snprintf(str, STR_BUF_SIZE, "\tactual: 0x%x", actual);
	append_test_log(s, str);

	return test_failure;
}

static TestResult
chk_ptr_ne_test(
	TestState *s,
	void *actual,
	void *prohibited
)
{
	if (actual != prohibited)
		return test_success;

	// log the error
	char str[STR_BUF_SIZE];
	log_test_context(s);
	snprintf(
		str,
		STR_BUF_SIZE,
		"ERROR: prohibited value: 0x%x",
		actual
	);
	append_test_log(s, str);

	return test_failure;
}

static TestResult
chk_str_eq_test(
	TestState *s,
	void *aptr,
	void *eptr
)
{
	if (!eptr)
	{
		if (!aptr) return test_success;
		char str[STR_BUF_SIZE];
		log_test_context(s);
		append_test_log(s, "ERROR:");
		append_test_log(s, "\texpected: 0x0");
		snprintf(str, STR_BUF_SIZE, "\tactual: 0x%x", aptr);
		append_test_log(s, str);
		return test_failure;
	}

	const char
		*actual = aptr,
		*expected = eptr;

	if (!strcmp(actual, expected)) return;

	// log the error
	char str[STR_BUF_SIZE];
	log_test_context(s);
	append_test_log(s, "ERROR:");
	snprintf(str, STR_BUF_SIZE, "\texpected: %s", expected);
	append_test_log(s, str);
	snprintf(str, STR_BUF_SIZE, "\tactual: %s", actual);
	append_test_log(s, str);

	return test_failure;
}

static void
report(const char *)
{
	return;
}

//jl