#pragma once

#include <exception>
#include <iostream>

class TestFailedException : public std::exception {
};

// Asserts the comparison specified by CMP is true, or fails the current unit test
#define UASSERTCMP(CMP, actual, expected) do {                                        \
	const auto &a = (actual);                                                     \
	const auto &e = (expected);                                                   \
	if (!CMP(a, e)) {                                                             \
		std::cout                                                             \
			<< "Test assertion failed: " << #actual << " " << #CMP << " " \
			<< #expected << std::endl                                     \
			<< "    at " << __FILE__ << ":" << __LINE__ << std::endl      \
			<< "    actual:   " << a << std::endl << "    expected: "     \
			<< e << std::endl;                                            \
		throw TestFailedException();                                          \
	}                                                                             \
} while (0)

#define CMPEQ(a, e)    (a == e)
#define CMPTRUE(a, e)  (a)
#define CMPNE(a, e)    (a != e)

#define UASSERTEQ(actual, expected)  UASSERTCMP(CMPEQ, actual, expected)
#define UASSERTNE(actual, nexpected) UASSERTCMP(CMPNE, actual, nexpected)
#define UASSERT(actual)              UASSERTCMP(CMPTRUE, actual, true)