/*

	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"

// Local Types

typedef struct CompareInts CompareInts;
typedef struct ComparePtrs ComparePtrs;

struct CompareInts
{
	const char *context;
	int chk_val;
	int ref_val;
};

struct ComparePtrs
{
	const char *context;
	const void *chk_val;
	const void *ref_val;
};

// Local Prototypes

// compare the run value of two states
static void chk_TestState_run_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the passed value of two states
static void chk_TestState_passed_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the failed value of two states
static void chk_TestState_failed_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the pending value of two states
static void chk_TestState_pending_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the first_log value of two states
static void chk_TestState_first_log_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the last_log value of two states
static void chk_TestState_last_log_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the ptr value of two states
static void chk_TestState_ptr_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the context value of two states
static void chk_TestState_context_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the full_context value of two states
static void chk_TestState_full_context_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the depth value of two states
static void chk_TestState_depth_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

// compare the report pointer of two states
static void chk_TestState_report_eq(
	TestState *,
	const char *,
	const char *,
	const TestState *,
	const TestState *
);

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

decl_test(chk_int_eq_test); // ensure ints are equal
decl_test(chk_ptr_eq_test); // ensure pointers are equal
decl_test(chk_ptr_ne_test); // ensure pointers are not equal
decl_test(chk_str_eq_test); // ensure strings are equal

// 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 *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	chk_TestState_run_eq(s, prefix, context, actual, expected);
	chk_TestState_passed_eq(s, prefix, context, actual, expected);
	chk_TestState_failed_eq(s, prefix, context, actual, expected);
	chk_TestState_pending_eq(s, prefix, context, actual, expected);
	chk_TestState_first_log_eq(s, prefix, context, actual, expected);
	chk_TestState_last_log_eq(s, prefix, context, actual, expected);
	chk_TestState_ptr_eq(s, prefix, context, actual, expected);
	chk_TestState_context_eq(s, prefix, context, actual, expected);
	chk_TestState_full_context_eq(s, prefix, context, actual, expected);
	chk_TestState_depth_eq(s, prefix, context, actual, expected);
	chk_TestState_report_eq(s, prefix, context, actual, expected);
}

void
chk_int_eq(
	TestState *s,
	const char *context,
	int actual,
	int expected
)
{
	void *old_ptr = s->ptr;
	CompareInts ci;
	ci.context = context;
	ci.chk_val = actual;
	ci.ref_val = expected;
	s->ptr = &ci;
	run_test(s, chk_int_eq_test);
	s->ptr = old_ptr;
}

void
chk_ptr_eq(
	TestState *s,
	const char *context,
	void *actual,
	void *expected
)
{
	void *old_ptr = s->ptr;
	ComparePtrs cp;
	cp.context = context;
	cp.chk_val = actual;
	cp.ref_val = expected;
	s->ptr = &cp;
	run_test(s, chk_ptr_eq_test);
	s->ptr = old_ptr;
}

void
chk_ptr_ne(
	TestState *s,
	const char *context,
	const void *actual,
	const void *prohibited
)
{
	void *old_ptr = s->ptr;
	ComparePtrs cp;
	cp.context = context;
	cp.chk_val = actual;
	cp.ref_val = prohibited;
	s->ptr = &cp;
	run_test(s, chk_ptr_ne_test);
	s->ptr = old_ptr;
}

void
chk_str_eq(
	TestState *s,
	const char *context,
	const char *actual,
	const char *expected
)
{
	void *old_ptr = s->ptr;
	ComparePtrs cp;
	cp.context = context;
	cp.chk_val = actual;
	cp.ref_val = expected;
	s->ptr = &cp;
	run_test(s, chk_str_eq_test);
	s->ptr = old_ptr;
}

// Local Functions

static void
chk_TestState_run_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("run\n");
	snprintf(full_context, STR_BUF_SIZE, "%s run:", context);
	chk_int_eq(s, full_context, actual->run, expected->run);
}

static void
chk_TestState_passed_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("passed\n");
	snprintf(full_context, STR_BUF_SIZE, "%s passed:", context);
	chk_int_eq(s, full_context, actual->passed, expected->passed);
}

static void
chk_TestState_failed_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("failed\n");
	snprintf(full_context, STR_BUF_SIZE, "%s failed:", context);
	chk_int_eq(s, full_context, actual->failed, expected->failed);
}

static void
chk_TestState_pending_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("pending\n");
	snprintf(full_context, STR_BUF_SIZE, "%s pending:", context);
	chk_int_eq(s, full_context, actual->pending, expected->pending);
}

static void
chk_TestState_first_log_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("first_log\n");
	snprintf(full_context, STR_BUF_SIZE, "%s first_log:", context);
	chk_ptr_eq(s, full_context, actual->first_log, expected->first_log);
}

static void
chk_TestState_last_log_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("last_log\n");
	snprintf(full_context, STR_BUF_SIZE, "%s last_log:", context);
	chk_ptr_eq(s, full_context, actual->last_log, expected->last_log);
}

static void
chk_TestState_ptr_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("ptr\n");
	snprintf(full_context, STR_BUF_SIZE, "%s ptr:", context);
	chk_ptr_eq(s, full_context, actual->ptr, expected->ptr);
}

static void
chk_TestState_context_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("context\n");
	snprintf(full_context, STR_BUF_SIZE, "%s context:", context);
	chk_str_eq(s, full_context, actual->context, expected->context);
}

static void
chk_TestState_full_context_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("full_context\n");
	snprintf(full_context, STR_BUF_SIZE, "%s full_context:", context);
	chk_str_eq(s, full_context, actual->full_context, expected->full_context);
}

static void
chk_TestState_depth_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("depth\n");
	snprintf(full_context, STR_BUF_SIZE, "%s depth:", context);
	chk_int_eq(s, full_context, actual->depth, expected->depth);
}

static void
chk_TestState_report_eq(
	TestState *s,
	const char *prefix,
	const char *context,
	const TestState *actual,
	const TestState *expected
)
{
	char full_context[STR_BUF_SIZE];
	print(prefix);
	print("report()\n");
	snprintf(full_context, STR_BUF_SIZE, "%s report():", context);
	chk_ptr_eq(s, full_context, actual->report, expected->report);
}

def_test(chk_int_eq_test, s)
{
	const CompareInts *ci = s->ptr;
	if (ci->chk_val == ci->ref_val) return test_success;
	char str[STR_BUF_SIZE];
	append_test_log(s, ci->context);
	snprintf(str, STR_BUF_SIZE, "\texpected: %d", ci->ref_val);
	append_test_log(s, str);
	snprintf(str, STR_BUF_SIZE, "\tactual: %d", ci->chk_val);
	append_test_log(s, str);
	return test_failure;
}

def_test(chk_ptr_eq_test, s)
{
	const ComparePtrs *cp = s->ptr;
	if (cp->chk_val == cp->ref_val) return test_success;
	char str[STR_BUF_SIZE];
	append_test_log(s, cp->context);
	snprintf(str, STR_BUF_SIZE, "\texpected: 0x%x", cp->ref_val);
	append_test_log(s, str);
	snprintf(str, STR_BUF_SIZE, "\tactual: 0x%x", cp->chk_val);
	append_test_log(s, str);
	return test_failure;
}

def_test(chk_ptr_ne_test, s)
{
	const ComparePtrs *cp = s->ptr;
	if (cp->chk_val != cp->ref_val) return test_success;
	char str[STR_BUF_SIZE];
	append_test_log(s, cp->context);
	snprintf(str, STR_BUF_SIZE, "\tcannot be: 0x%x", cp->ref_val);
	append_test_log(s, str);
	return test_failure;
}

def_test(chk_str_eq_test, s)
{
	const ComparePtrs *cp = s->ptr;
	if (!cp->chk_val && !cp->ref_val) return test_success;
	if (!strcmp(cp->chk_val, cp->ref_val)) return test_success;
	char str[STR_BUF_SIZE];
	append_test_log(s, cp->context);
	snprintf(str, STR_BUF_SIZE, "\texpected: %s", cp->ref_val);
	append_test_log(s, str);
	snprintf(str, STR_BUF_SIZE, "\tactual: %s", cp->ref_val);
	append_test_log(s, str);
	return test_failure;
}

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

//jl