/*
	Test-SST_SafeArithmetic.cpp
	Author: Chris Ertel <crertel@762studios.com>
	Created: 2/5/2012

	Purpose: 

	Tests to ensure that the safe arithmetic package for libsst is, in fact, safe.
*/
#include "ZUnitTest.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <climits>
#include <SST/SST_SafeArithmetic.h>

static const char* testSafeAddI8();
static const char* testSafeAddI16();
static const char* testSafeAddI32();
static const char* testSafeAddI64();

static const char* testSafeAddU8();
static const char* testSafeAddU16();
static const char* testSafeAddU32();
static const char* testSafeAddU64();

static const char* testSafeMultiplyU8();
static const char* testSafeMultiplyU16();
static const char* testSafeMultiplyU32();
static const char* testSafeMultiplyU64();

static const char* testSafeAddI8ToSizeT();
static const char* testSafeAddI16ToSizeT();
static const char* testSafeAddI32ToSizeT();
static const char* testSafeAddI64ToSizeT();

static const char* testSafeAddU8ToSizeT();
static const char* testSafeAddU16ToSizeT();
static const char* testSafeAddU32ToSizeT();
static const char* testSafeAddU64ToSizeT();

static const char* testSafeAddSizeTToSizeT();

//List of unit tests
ZUnitTest SST_SafeArithmeticUnitTests[] =
{
	{
		"Safe add signed 8-bit",
		testSafeAddI8
	},
	{
		"Safe add signed 16-bit",
		testSafeAddI16
	},
	{
		"Safe add signed 32-bit",
		testSafeAddI32
	},
	{
		"Safe add signed 64-bit",
		testSafeAddI64
	},
	{
		"Safe add unsigned 8-bit",
		testSafeAddU8
	},
	{
		"Safe add unsigned 16-bit",
		testSafeAddU16
	},
	{
		"Safe add unsigned 32-bit",
		testSafeAddU32
	},
	{
		"Safe add unsigned 64-bit",
		testSafeAddU64
	},

	{
		"Safe multiply unsigned 8-bit",
		testSafeMultiplyU8
	},
	{
		"Safe multiply unsigned 16-bit",
		testSafeMultiplyU16
	},
	{
		"Safe multiply unsigned 32-bit",
		testSafeMultiplyU32
	},
	{
		"Safe multiply unsigned 64-bit",
		testSafeMultiplyU64
	},
	
	{
		"Safe add signed 8-bit to size_t",
		testSafeAddI8ToSizeT
	},
	{
		"Safe add signed 16-bit to size_t",
		testSafeAddI16ToSizeT
	},
	{
		"Safe add signed 32-bit to size_t",
		testSafeAddI32ToSizeT
	},
	{
		"Safe add signed 64-bit to size_t",
		testSafeAddI64ToSizeT
	},
	{
		"Safe add unsigned 8-bit to size_t",
		testSafeAddU8ToSizeT
	},
	{
		"Safe add unsigned 16-bit to size_t",
		testSafeAddU16ToSizeT
	},
	{
		"Safe add unsigned 32-bit to size_t",
		testSafeAddU32ToSizeT
	},
	{
		"Safe add unsigned 64-bit to size_t",
		testSafeAddU64ToSizeT
	},
	{
		"Safe add size_t to size_t",
		testSafeAddSizeTToSizeT
	}
};

DECLARE_ZTESTBLOCK(SST_SafeArithmetic);

/*************************************************************************/

static const char* testSafeAddI8()
{
	int8_t out;
	TASSERT( (SST_OS_SafeAddI8(INT8_MAX-1, 1, &out) == 1), "SafeAddI8 returned false when no overflow!\n");
	TASSERT( (out == INT8_MAX), "SafeAddI8 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI8(INT8_MAX-1, 2, &out) == 0), "SafeAddI8 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddI16()
{
	int16_t out;
	TASSERT( (SST_OS_SafeAddI16(INT16_MAX-1, 1, &out) == 1), "SafeAddI16 returned false when no overflow!\n");
	TASSERT( (out == INT16_MAX), "SafeAddI16 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI16(INT16_MAX-1, 2, &out) == 0), "SafeAddI16 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddI32()
{
	int32_t out;
	TASSERT( (SST_OS_SafeAddI32(INT32_MAX-1, 1, &out) == 1), "SafeAddI32 returned false when no overflow!\n");
	TASSERT( (out == INT32_MAX), "SafeAddI32 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI32(INT32_MAX-1, 2, &out) == 0), "SafeAddI32 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddI64()
{
	int64_t out;
	TASSERT( (SST_OS_SafeAddI64(INT64_MAX-1, 1, &out) == 1), "SafeAddI64 returned false when no overflow!\n");
	TASSERT( (out == INT64_MAX), "SafeAddI64 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI64(INT64_MAX-1, 2, &out) == 0), "SafeAddI64 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU8()
{
	uint8_t out;
	TASSERT( (SST_OS_SafeAddU8(UINT8_MAX-1, 1, &out) == 1), "SafeAddU8 returned false when no overflow!\n");
	TASSERT( (out == UINT8_MAX), "SafeAddU8 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU8(UINT8_MAX-1, 2, &out) == 0), "SafeAddU8 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU16()
{
	uint16_t out;
	TASSERT( (SST_OS_SafeAddU16(UINT16_MAX-1, 1, &out) == 1), "SafeAddU16 returned false when no overflow!\n");
	TASSERT( (out == UINT16_MAX), "SafeAddU16 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU16(UINT16_MAX-1, 2, &out) == 0), "SafeAddU16 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU32()
{
	uint32_t out;
	TASSERT( (SST_OS_SafeAddU32(UINT32_MAX-1, 1, &out) == 1), "SafeAddU32 returned false when no overflow!\n");
	TASSERT( (out == UINT32_MAX), "SafeAddU32 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU32(UINT32_MAX, 2, &out) == 0), "SafeAddU32 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU64()
{
	uint64_t out;
	TASSERT( (SST_OS_SafeAddU64(UINT64_MAX-1, 1, &out) == 1), "SafeAddU64 returned false when no overflow!\n");
	TASSERT( (out == UINT64_MAX), "SafeAddU64 can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU64(UINT64_MAX-1, 2, &out) == 0), "SafeAddU64 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeMultiplyU8()
{
	uint8_t a = UINT8_MAX - 1;
	uint8_t b = 1;
	uint8_t out;
	TASSERT( (SST_OS_SafeMultiplyU8(a,b,&out) == 1), "SafeMultiplyU8 returned false when no overflow!\n");
	TASSERT( (out == UINT8_MAX - 1), "SafeMultiplyU8 can't multiply without overflow!\n");
	TASSERT( (SST_OS_SafeMultiplyU8(a,a,&out) == 0), "SafeMultiplyU8 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeMultiplyU16()
{
	uint16_t a = UINT16_MAX - 1;
	uint16_t b = 1;
	uint16_t out;
	TASSERT( (SST_OS_SafeMultiplyU16(a,b,&out) == 1), "SafeMultiplyU16 returned false when no overflow!\n");
	TASSERT( (out == UINT16_MAX - 1), "SafeMultiplyU16 can't multiply without overflow!\n");
	TASSERT( (SST_OS_SafeMultiplyU16(a,a,&out) == 0), "SafeMultiplyU16 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeMultiplyU32()
{
	uint32_t a = UINT32_MAX - 1;
	uint32_t b = 1;
	uint32_t out;
	TASSERT( (SST_OS_SafeMultiplyU32(a,b,&out) == 1), "SafeMultiplyU32 returned false when no overflow!\n");
	TASSERT( (out == UINT32_MAX - 1), "SafeMultiplyU32 can't multiply without overflow!\n");
	TASSERT( (SST_OS_SafeMultiplyU32(a,a,&out) == 0), "SafeMultiplyU32 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeMultiplyU64()
{
	uint64_t a = UINT64_MAX;
	uint64_t b = 1;
	uint64_t out;
	TASSERT( (SST_OS_SafeMultiplyU64(a,b,&out) == 1), "SafeMultiplyU64 returned false when no overflow!\n");
	TASSERT( (out == UINT64_MAX), "SafeMultiplyU64 can't multiply without overflow!\n");
	TASSERT( (SST_OS_SafeMultiplyU64(a,2,&out) == 0), "SafeMultiplyU64 returned true when overflow!\n");
	TASSERT( (SST_OS_SafeMultiplyU64(a,a,&out) == 0), "SafeMultiplyU64 returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddI8ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddI8ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddI8ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddI8ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI8ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddI8ToSizeT returned true when overflow!\n");
	TASSERT( (SST_OS_SafeAddI8ToSizeT(-1, 0, &out) == 0), "SafeAddI8ToSizeT returned true when underflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddI16ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddI16ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddI16ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddI16ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI16ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddI16ToSizeT returned true when overflow!\n");
	TASSERT( (SST_OS_SafeAddI16ToSizeT(-1, 0, &out) == 0), "SafeAddI16ToSizeT returned true when underflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddI32ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddI32ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddI32ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddI32ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI32ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddI32ToSizeT returned true when overflow!\n");
	TASSERT( (SST_OS_SafeAddI32ToSizeT(-1, 0, &out) == 0), "SafeAddI32ToSizeT returned true when underflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddI64ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddI64ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddI64ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddI64ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddI64ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddI64ToSizeT returned true when overflow!\n");
	TASSERT( (SST_OS_SafeAddI64ToSizeT(-1, 0, &out) == 0), "SafeAddI64ToSizeT returned true when underflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU8ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddU8ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddU8ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddU8ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU8ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddU8ToSizeT returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU16ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddU16ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddU16ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddU16ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU16ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddU16ToSizeT returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU32ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddU32ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddU32ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddU32ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU32ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddU32ToSizeT returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddU64ToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddU64ToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddU64ToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddU64ToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddU64ToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddU64ToSizeT returned true when overflow!\n");
	return ZTEST_SUCCESS;
}

/*************************************************************************/

static const char* testSafeAddSizeTToSizeT()
{
	size_t out;

	TASSERT( (SST_OS_SafeAddSizeTToSizeT(1, SIZE_MAX-1, &out) == 1), "SafeAddSizeTToSizeT returned false when no overflow!\n");
	TASSERT( (out == SIZE_MAX), "SafeAddSizeTToSizeT can't add without overflow!\n");
	TASSERT( (SST_OS_SafeAddSizeTToSizeT(2, SIZE_MAX-1, &out) == 0), "SafeAddSizeTToSizeT returned true when overflow!\n");
	return ZTEST_SUCCESS;
}