#include "ZUnitTest.hpp"

#include <SST/SST_Atomic.h>

#include <stdio.h>
#include <stdlib.h>

static const char* testAdd();
static const char* testAddPtr();
static const char* testAnd();
static const char* testOr();
static const char* testXor();
static const char* testNot();

static const char* testAddRet();
static const char* testAddPtrRet();
static const char* testAndRet();
static const char* testOrRet();
static const char* testXorRet();
static const char* testNotRet();

static const char* testXadd();
static const char* testXaddPtr();
static const char* testXchg();
static const char* testXchgPtr();
static const char* testCAS();
static const char* testCASPtr();

static const char* testLdAq();
static const char* testLdAqPtr();
static const char* testStRel();
static const char* testStRelPtr();


//List of unit tests
ZUnitTest SST_AtomicUnitTests[] =
{
	{ "SST_Atomic: AtomicAdd()", testAdd },
	{ "SST_Atomic: AtomicAddPtr()", testAddPtr },
	{ "SST_Atomic: AtomicAnd()", testAnd },
	{ "SST_Atomic: AtomicOr()", testOr },
	{ "SST_Atomic: AtomicXor()", testXor },
	{ "SST_Atomic: AtomicNot()", testNot },

	{ "SST_Atomic: AtomicAdd()", testAddRet },
	{ "SST_Atomic: AtomicAddPtr()", testAddPtrRet },
	{ "SST_Atomic: AtomicAnd()", testAndRet },
	{ "SST_Atomic: AtomicOr()", testOrRet },
	{ "SST_Atomic: AtomicXor()", testXorRet },
	{ "SST_Atomic: AtomicNot()", testNotRet },

	{ "SST_Atomic: AtomicXadd()", testXadd },
	{ "SST_Atomic: AtomicXaddPtr()", testXaddPtr },
	{ "SST_Atomic: AtomicXchg()", testXchg },
	{ "SST_Atomic: AtomicXchgPtr()", testXchgPtr },
	{ "SST_Atomic: AtomicCAS()", testCAS },
	{ "SST_Atomic: AtomicCASPtr()", testCASPtr },

	{ "SST_Atomic: AtomicLoadAcquire()", testLdAq },
	{ "SST_Atomic: AtomicLoadAcquirePtr()", testLdAqPtr },
	{ "SST_Atomic: AtomicStoreRelease()", testStRel },
	{ "SST_Atomic: AtomicStoreReleasePtr()", testStRelPtr },
};

DECLARE_ZTESTBLOCK(SST_Atomic);

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

static const char* testAdd()
{
	int v = 0x100;

	SST_Atomic_Add(&v, 0x50);

	TASSERT(v == 0x150, "Add() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testAddPtr()
{
	char data[64];
	volatile void* ptr = (void*)&data;


	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		data[i] = (char)(i&0xFF);

	SST_Atomic_AddPtr(&ptr, 32);

	TASSERT(ptr == &data[32], "AddPtr() computed incorrect value");

	//Check for corruption in the array
	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		TASSERT(data[i] == (char)(i&0xFF), "AddPtr() corrupted what was being pointed to!");

	return ZTEST_SUCCESS;
}

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

static const char* testAnd()
{
	int x = 0xAABBCCDD;

	SST_Atomic_And(&x, 0xFF00FF00);

	TASSERT(x == (int)0xAA00CC00, "And() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testOr()
{
	int x = 0xAA00CC00;

	SST_Atomic_Or(&x, 0x00220033);

	TASSERT(x == (int)0xAA22CC33, "Or() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testXor()
{
	int x = 0xAA00CC00;

	SST_Atomic_Xor(&x, 0x0022CC33);

	TASSERT(x == (int)0xAA220033, "Xor() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testNot()
{
	int x = 0xFF00FF00;

	SST_Atomic_Not(&x);

	TASSERT(x == (int)0x00FF00FF, "Not() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testAddRet()
{
	int v = 0x100;

	int retval = SST_Atomic_AddReturn(&v, 0x50);

	TASSERT(retval == 0x150, "AddReturn() returned incorrect value");
	TASSERT(v == 0x150, "AddReturn() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testAddPtrRet()
{
	char data[64];
	volatile void* ptr = (void*)&data;


	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		data[i] = (char)(i&0xFF);

	void* retval = SST_Atomic_AddPtrReturn(&ptr, 32);

	TASSERT(retval == &data[32], "AddPtrReturn() returned incorrect value");
	TASSERT(ptr == &data[32], "AddPtrReturn() computed incorrect value");

	//Check for corruption in the array
	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		TASSERT(data[i] == (char)(i&0xFF), "AddPtrReturn() corrupted what was being pointed to!");


	return ZTEST_SUCCESS;
}

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

static const char* testAndRet()
{
	int x = 0xAABBCCDD;

	int retval = SST_Atomic_AndReturn(&x, 0xFF00FF00);

	TASSERT(retval == (int)0xAA00CC00, "AndReturn() returned incorrect value");
	TASSERT(x == (int)0xAA00CC00, "AndReturn() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testOrRet()
{
	int x = 0xAA00CC00;

	int retval = SST_Atomic_OrReturn(&x, 0x00220033);

	TASSERT(retval == (int)0xAA22CC33, "OrReturn() returned incorrect value");
	TASSERT(x == (int)0xAA22CC33, "OrReturn() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testXorRet()
{
	int x = 0xAA00CC00;

	int retval = SST_Atomic_XorReturn(&x, 0x0022CC33);

	TASSERT(retval == (int)0xAA220033, "XorReturn() returned incorrect value");
	TASSERT(x == (int)0xAA220033, "XorReturn() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testNotRet()
{
	int x = 0xFF00FF00;

	int retval = SST_Atomic_NotReturn(&x);

	TASSERT(retval == (int)0x00FF00FF, "NotReturn() returned incorrect value");
	TASSERT(x == (int)0x00FF00FF, "NotReturn() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testXadd()
{
	int x = 0x100;

	int old = SST_Atomic_ExchangeAdd(&x, 0x50);

	TASSERT(old == 0x100, "ExchangeAdd() returned incorrect original value");
	TASSERT(x == (int)0x150, "ExchangeAdd() computed incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testXaddPtr()
{
	char data[64];
	volatile void* ptr = (void*)&data[0];


	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		data[i] = (char)(i&0xFF);

	void* old = SST_Atomic_ExchangeAddPtr(&ptr, 32);

	TASSERT(old == &data[0], "ExchangeAddPtr() returned incorrect original value");
	TASSERT(ptr == &data[32], "ExchangeAddPtr() computed incorrect value");

	//Check for corruption in the array
	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		TASSERT(data[i] == (char)(i&0xFF), "ExchangeAddPtr() corrupted what was being pointed to!");


	return ZTEST_SUCCESS;
}

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

static const char* testXchg()
{
	int x = 0x100;

	int old = SST_Atomic_Exchange(&x, 0x50);

	TASSERT(old == 0x100, "Exchange() returned incorrect original value");
	TASSERT(x == (int)0x50, "Exchange() stored incorrect value");

	return ZTEST_SUCCESS;
}

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

static const char* testXchgPtr()
{
	char data[64];
	volatile void* ptr = (void*)&data[0];

	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		data[i] = (char)(i&0xFF);

	void* old = SST_Atomic_ExchangePtr(&ptr, &data[32]);

	TASSERT(old == &data[0], "ExchangePtr() returned incorrect original value");
	TASSERT(ptr == &data[32], "ExchangePtr() stored incorrect value");

	//Check for corruption in the array
	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		TASSERT(data[i] == (char)(i&0xFF), "ExchangePtr() corrupted what was being pointed to!");


	return ZTEST_SUCCESS;
}

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

static const char* testCAS()
{
	int x = 0x100;
	int old;

	//Test fail compare
	old = SST_Atomic_CAS(&x, 0x50, 0xAABBCCDD);
	TASSERT(old == 0x100, "CAS() returned incorrect original value on compare fail");
	TASSERT(x == (int)0x100, "CAS() modified value on compare fail");

	//Test successful compare
	old = SST_Atomic_CAS(&x, 0x100, 0xAABBCCDD);
	TASSERT(old == 0x100, "CAS() returned incorrect original value on compare success");
	TASSERT(x == (int)0xAABBCCDD, "CAS() did not correctly store on compare success");


	return ZTEST_SUCCESS;
}

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

static const char* testCASPtr()
{
	char data[64];
	volatile void* ptr = (void*)&data[0];
	void* old;

	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		data[i] = (char)(i&0xFF);

	//Failed comparison of ptr (data[0]) vs data[32]
	old = SST_Atomic_CASPtr(&ptr, &data[32], &data[16]);
	TASSERT(old == &data[0], "CASPtr() returned incorrect original value on compare fail");
	TASSERT(ptr == &data[0], "CASPtr() stored incorrect value on compare fail");

	//Check for corruption in the array
	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		TASSERT(data[i] == (char)(i&0xFF), "CASPtr() corrupted what was being pointed to on compare fail!");

	//Successful comparison 
	old = SST_Atomic_CASPtr(&ptr, &data[0], &data[16]);
	TASSERT(old == &data[0], "CASPtr() returned incorrect original value on compare success");
	TASSERT(ptr == &data[16], "CASPtr() stored incorrect value on compare success");

	//Check for corruption in the array
	for(size_t i=0; i<sizeof(data)/sizeof(char); i++)
		TASSERT(data[i] == (char)(i&0xFF), "CASPtr() corrupted what was being pointed to on compare success!");


	return ZTEST_SUCCESS;
}

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

static const char* testLdAq()
{
	volatile int v = 50;

	TASSERT(SST_Atomic_LoadAcquire(&v) == 50, "Returned wrong value from load");

	TASSERT(v == 50, "Load modified value!");

	return ZTEST_SUCCESS;
}

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

static const char* testLdAqPtr()
{
	volatile void* value = (void*)testLdAqPtr;

	TASSERT(SST_Atomic_LoadAcquirePtr(&value) == (void*)testLdAqPtr, "Returned wrong value from load pointer");
	
	TASSERT(value == (void*)testLdAqPtr, "Load modified value!");

	return ZTEST_SUCCESS;
}

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

static const char* testStRel()
{
	volatile int v = 0xAABBCCDD;

	SST_Atomic_StoreRelease(&v, 0x11223344);

	TASSERT(v == 0x11223344, "Store did not properly store the value!");

	return ZTEST_SUCCESS;
}

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

static const char* testStRelPtr()
{
	volatile void* v = (void*)testStRelPtr;

	SST_Atomic_StoreReleasePtr(&v, (void*)testLdAqPtr);

	TASSERT(v == (void*)testLdAqPtr, "Store did not properly store the value!");

	return ZTEST_SUCCESS;

}

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