#pragma once

#include <string>
#include <iostream>

namespace testlib {

struct TestFailure {
	TestFailure(const char *msg, const char *file, int line):
		message(msg), filename(file), linenum(line) {}

	const char *message;
	const char *filename;
	int linenum;
};

struct TestSpec;
void addTestCase(TestSpec *testcase);

struct TestSpec {
	TestSpec(void (*f)(), const char *desc, const char *file, int line, int idx):
			func(f), description(desc), filename(file), linenum(line), index(idx) {
		addTestCase(this);
	}

	void (*func)();
	const char *description;
	const char *filename;
	int linenum;
	int index;
};

}

#define test3(name, id) name ## id
#define test2(uniqid, desc) \
	static void test3(_test_func_, uniqid)(); \
	static __attribute__((unused)) testlib::TestSpec test3(_test_register_, uniqid)( \
		&test3(_test_func_, uniqid), desc, __FILE__, __LINE__, uniqid); \
	static void test3(_test_func_, uniqid)()
#define test(desc) test2(__COUNTER__, desc)

#define expect(expr) do { \
	if (!(expr)) { \
		throw testlib::TestFailure( \
			"Expected '" #expr "' to be true.", __FILE__, __LINE__); \
	} \
} while (0)

#define expecteq(a, b) do { \
	if ((a) != (b)) { \
		throw testlib::TestFailure( \
			"Expected '" #a "' to equal '" #b "'.", __FILE__, __LINE__); \
	} \
} while (0)

#define expectneq(a, b) do { \
	if ((a) == (b)) { \
		throw testlib::TestFailure( \
			"Expected '" #a "' to not equal '" #b "'.", __FILE__, __LINE__); \
	} \
} while (0)
