/**
 * \brief tests that all wait methods return after a shutdown
 */
TEST(Semaphore, shutdownTest){

    OS::SSemaphore uut1;
    CHECK(!uut1.isShutdown());
    uut1.shutdown();
    CHECK(uut1.isShutdown());
    uut1.wait(); //thread should not block
    CHECK(uut1.tryWait());
    CHECK(uut1.timedWait(10000));

    //Test the case where the semaphore is waiting,
    OS::SSemaphore uut2;
    SemaphoreWaiter waiter1 (&uut2);
    waiter1.start();
    SemaphoreWaiter waiter2 (&uut2);
    waiter2.start();
    SemaphoreWaiter waiter3 (&uut2);
    waiter3.start();
    uut2.shutdown();
    waiter1.join();
    waiter2.join();
    waiter3.join();

    //If the program doesn't hang, test successful!
    CHECK(uut2.isShutdown());
}
extern "C" int main(int argc, char *argv[]) {
	Callback cb("notify", &cbFunc, NULL);

	checkpointNext("Objects:");
	testNotify("  Normal", cb, 0x1337);
	testNotify("  NULL", 0, 0x1337);
	testNotify("  Invalid", 0xDEADBEEF, 0x1337);
	cb.Delete();
	testNotify("  Deleted", cb, 0x1337);

	cb.Create("notify", &cbFunc, NULL);
	checkpointNext("Values:");
	testNotify("  Zero", cb, 0);
	testNotify("  DEADBEEF", cb, 0xDEADBEEF);

	checkpointNext("Notifies:");
	int result = 0;
	for (int i = 0; i < 10000; ++i) {
		result = sceKernelNotifyCallback(cb, 1);
		if (result != 0) {
			checkpoint("  Failed at %d: %08x", i, result);
			break;
		}
	}
	if (result == 0) {
		checkpoint("  10000 notifies: OK");
	}

	checkpoint("sceKernelDelayThreadCB: %08x", sceKernelDelayThreadCB(1000));

	checkpointNext("Different thread:");
	{
		CallbackSleeper waiter1("better priority sleeping thread", 0x10);
		CallbackSleeper waiter2("worse priority sleeping thread", 0x30);
		sceKernelDelayThread(1000);
		sceKernelNotifyCallback(waiter1.callbackID(), 0x1337);
		sceKernelNotifyCallback(waiter2.callbackID(), 0x1337);
		sceKernelDelayThread(1000);
	}

	checkpointNext("Return value:");
	{
		CallbackSleeper waiter("sleeping thread");
		waiter.setReturn(0x1337);
		sceKernelDelayThread(1000);
		testNotify("  Notify #1", waiter.callbackID(), 0x1337);
		sceKernelDelayThread(1000);
		testNotify("  Notify #2", waiter.callbackID(), 0x1337);
		sceKernelDelayThread(1000);
	}

	checkpointNext("Recursion:");
	{
		SelfNotifier waiter("sleeping thread");
		sceKernelDelayThread(1000);
		testNotify("  Notify #1", waiter.callbackID(), 0x1337);
		sceKernelDelayThread(1000);
	}

	checkpointNext("Mixing types:");
	checkpoint("  scePowerRegisterCallback (causes notify): %08x", scePowerRegisterCallback(0, cb));
	testNotify("  Manual notify", cb, 0x1337);
	checkpoint("  sceKernelDelayThreadCB: %08x", sceKernelDelayThreadCB(1000));

	checkpointNext("Order:");
	Callback cb1("notify1", &cbFunc, (void *)0xABC00001);
	Callback cb2("notify2", &cbFunc, (void *)0xABC00002);
	Callback cb3("notify3", &cbFunc, (void *)0xABC00003);
	testNotify("  Notify cb #2", cb2, 0xDEF00001);
	testNotify("  Notify cb #1", cb1, 0xDEF00002);
	testNotify("  Notify cb #3", cb3, 0xDEF00003);
	checkpoint("  sceKernelCheckCallback: %08x", sceKernelCheckCallback());

	return 0;
}