/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "pcode_test.h"

#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)

NOINLINE i4 breakOnPass(void);
NOINLINE i4 breakOnError(void);
void nosprintf5() { }			/* sprintf5 function was removed, but java is still looking for it. */

FunctionInfo mainFunctionInfoTable[] = {
#ifdef HAS_FLOAT
	{"assertF4", (testFuncPtr) assertF4, 0},
#endif
#ifdef HAS_DOUBLE
	{"assertF8", (testFuncPtr) assertF8, 0},
#endif
	{"assertI1", (testFuncPtr) assertI1, 0},
	{"assertI2", (testFuncPtr) assertI2, 0},
	{"assertI4", (testFuncPtr) assertI4, 0},
#ifdef HAS_LONGLONG
	{"assertI8", (testFuncPtr) assertI8, 0},
#endif
	{"assertU1", (testFuncPtr) assertU1, 0},
	{"assertU2", (testFuncPtr) assertU2, 0},
	{"assertU4", (testFuncPtr) assertU4, 0},
#ifdef HAS_LONGLONG
	{"assertU8", (testFuncPtr) assertU8, 0},
#endif
	{"breakOnDone", (testFuncPtr) breakOnDone, 0},
	{"breakOnError", (testFuncPtr) breakOnError, 0},
	{"breakOnPass", (testFuncPtr) breakOnPass, 0},
	{"breakOnSubDone", (testFuncPtr) breakOnSubDone, 0},
	{"noteTestMain", (testFuncPtr) noteTestMain, 0},
	{0, 0, 0},
};

static TestInfo MainInfo = {
	{'A', 'b', 'C', 'd', 'E', 'F', 'g', 'H'},
	sizeof(i1 *),			/* ptrSz */
	0x01020304,			/* byteOrder */
	breakOnPass,			/* onPass function ptr */
	breakOnError,			/* onError function ptr */
	breakOnDone,			/* onDone function ptr */
	0,				/* numpass */
	0,				/* numfail  */
	0,				/* lastTestPos */
	0,				/* lastErrorLine */
	"none",				/* lastErrorFile */
	"none",				/* lasFunc */
	nosprintf5,			/* sprintf5 function ptr */
	0,				/* sprintf5 buffer */
	0,				/* sprintf5 enabled flag */
	__VERSION__,			/* compiler version */
	TOSTRING(NAME),			/* value of name symbol */
	TOSTRING(THECCFLAGS),		/* value of THECCFLAGS symbol */
	"BUILDDATE: " __DATE__,		/* build date */
	mainFunctionInfoTable,		/* function table */
};

NOINLINE void TestInfo_reset(void)
{
	MainInfo.numpass = 0;
	MainInfo.numfail = 0;
	MainInfo.lastTestPos = 0;
	MainInfo.lastErrorFile = "none";
	MainInfo.lastFunc = "none";
}

/* Injected call when a test is done */

NOINLINE i4 breakOnDone(const char *file, int line, const char *func)
{
	return 0;
}

/* Called from assert when a test passes */

NOINLINE i4 breakOnPass(void)
{
	MainInfo.numpass++;
	return 0;
}

/* Called from assert when a test fails */

NOINLINE i4 breakOnError(void)
{
	MainInfo.numfail++;
	return 0;
}

/* Injected call when a subtest is done */

NOINLINE i4 breakOnSubDone(const char *file, int line, const char *func)
{
	return 0;
}

/* Injected at start of a test to record file position */

void noteTestMain(const char *file, int line, const char *func)
{
	MainInfo.lastFunc = (char *) func;
	MainInfo.lastTestPos = line;
}

#if defined(BUILD_EXE)
#define DO_PRINT_INT(ok)	print_int(file, line, MainInfo.lastFunc, expected, val, (ok) ? "OK" : "ERROR");
#define DO_PRINT_LONG(ok)	print_long(file, line, MainInfo.lastFunc, expected, val, (ok) ? "OK" : "ERROR");
/* for ARM platform, and possibly others, printf does not properly handle long long args */
#define DO_PRINT_LONGLONG(ok)	print_long(file, line, MainInfo.lastFunc, (long) expected, (long) val, (ok) ? "OK" : "ERROR");
#define DO_PRINT_UINT(ok)	print_uint(file, line, MainInfo.lastFunc, expected, val, (ok) ? "OK" : "ERROR");
#define DO_PRINT_ULONG(ok)	print_ulong(file, line, MainInfo.lastFunc, expected, val, (ok) ? "OK" : "ERROR");
#define DO_PRINT_ULONGLONG(ok)	print_ulong(file, line, MainInfo.lastFunc, (long) expected, (long) val, (ok) ? "OK" : "ERROR");
#define DO_PRINT_FLOAT(ok)	print_float(file, line, MainInfo.lastFunc, expected, val, (ok) ? "OK" : "ERROR");
#else
#define DO_PRINT_INT(ok)
#define DO_PRINT_LONG(ok)
#define DO_PRINT_LONGLONG(ok)
#define DO_PRINT_UINT(ok)
#define DO_PRINT_ULONG(ok)
#define DO_PRINT_ULONGLONG(ok)
#define DO_PRINT_FLOAT(ok)
#endif

/* The remaining functions are asserts. Assert functions perform
 * comparison of expected and actual values, record location of
 * errors, and call breakOnPass or breakOnError.
 */

void assertI1(const char *file, int line, const char *func, i1 val, i1 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_INT(val == expected);
}

void assertI2(const char *file, int line, const char *func, i2 val, i2 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_INT(val == expected);
}

void assertI4(const char *file, int line, const char *func, i4 val, i4 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_INT(val == expected);
}

#ifdef HAS_LONGLONG
void assertI8(const char *file, int line, const char *func, i8 val, i8 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_LONGLONG(val == expected);
}
#endif

void assertU1(const char *file, int line, const char *func, u1 val, u1 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_UINT(val == expected);
}

void assertU2(const char *file, int line, const char *func, u2 val, u2 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_UINT(val == expected);
}

void assertU4(const char *file, int line, const char *func, u4 val, u4 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
		MainInfo.numfail++;
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_UINT(val == expected);
}

#ifdef HAS_LONGLONG
void assertU8(const char *file, int line, const char *func, u8 val, u8 expected)
{
	if (val == expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_ULONGLONG(val == expected);
}
#endif

#ifdef HAS_FLOAT
void assertF4(const char *file, int line, const char *func, f4 val, f4 expected)
{
	u4 u4Val = *(u4 *) & val;
	u4 u4Expected = *(u4 *) & expected;

	/* Mask off last byte from value and expected */
	u4Val &= 0xFFFFFF00;
	u4Expected &= 0xFFFFFF00;

	/* Should fail if diff in sign/exponent/or more than (0xFF * eplison) */
	if (u4Val == u4Expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_FLOAT(u4Val == u4Expected);
}
#endif

#ifdef HAS_DOUBLE
void assertF8(const char *file, int line, const char *func, f8 val, f8 expected)
{
	u8 u8Val = *(u8 *) & val;
	u8 u8Expected = *(u8 *) & expected;

	/* Mask off last 2 bytes from value and expected */
	u8Val &= 0xFFFFFFFFFFFF0000ULL;
	u8Expected &= 0xFFFFFFFFFFFF0000ULL;

	/* Should fail if diff in sign/exponent/or more than (0xFFFF * eplison) */
	if (u8Val == u8Expected) {
		breakOnPass();
	} else {
		MainInfo.lastErrorLine = line;
		MainInfo.lastErrorFile = (char *) file;
		breakOnError();
	}
	MainInfo.lastTestPos = line;
	DO_PRINT_FLOAT(u8Val == u8Expected);
}
#endif

