#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include "cmockery.h"

#include "c.h"
#include "../checkpointer.c"
#include "postgres.h"

#define MAX_BGW_REQUESTS 5

static void
init_request_queue(void)
{
	size_t size = sizeof(CheckpointerShmemStruct) + sizeof(CheckpointerRequest)*MAX_BGW_REQUESTS;
	CheckpointerShmem = (CheckpointerShmemStruct *) malloc(size);
	memset(CheckpointerShmem, 0, size);
	CheckpointerShmem->checkpointer_pid = 1234;
	CheckpointerShmem->max_requests = MAX_BGW_REQUESTS;
	IsUnderPostmaster = true;
	ProcGlobal = (PROC_HDR *) malloc(sizeof(PROC_HDR));
	ProcGlobal->checkpointerLatch = NULL;
}

/*
 * Basic enqueue tests, including compaction upon enqueuing into a
 * full queue.
 */
static void
test__ForwardSyncRequest_enqueue(void **state)
{
	bool ret;
	int i;
	FileTag dummy_tag = {1, MAIN_FORKNUM, {1, 1, 1}, 1};
	init_request_queue();
	ProcGlobal->checkpointerLatch = NULL;
	expect_value(LWLockAcquire, lock, CheckpointerCommLock);
	expect_value(LWLockAcquire, mode, LW_EXCLUSIVE);
	will_return(LWLockAcquire, true);
	expect_value(LWLockRelease, lock, CheckpointerCommLock);
	will_be_called(LWLockRelease);
	/* basic enqueue */
	ret = ForwardSyncRequest(&dummy_tag, SYNC_REQUEST);
	assert_true(ret);
	assert_true(CheckpointerShmem->num_requests == 1);
	/* fill up the queue */
	for (i=2; i<=MAX_BGW_REQUESTS; i++)
	{
		expect_value(LWLockAcquire, lock, CheckpointerCommLock);
		expect_value(LWLockAcquire, mode, LW_EXCLUSIVE);
		will_return(LWLockAcquire, true);
		expect_value(LWLockRelease, lock, CheckpointerCommLock);
		will_be_called(LWLockRelease);
		dummy_tag.segno = i;
		ret = ForwardSyncRequest(&dummy_tag, SYNC_REQUEST);
		assert_true(ret);
	}
	expect_value(LWLockAcquire, lock, CheckpointerCommLock);
	expect_value(LWLockAcquire, mode, LW_EXCLUSIVE);
	will_return(LWLockAcquire, true);
	expect_value(LWLockRelease, lock, CheckpointerCommLock);
	will_be_called(LWLockRelease);
#ifdef USE_ASSERT_CHECKING
	expect_value(LWLockHeldByMe, l, CheckpointerCommLock);
	will_return(LWLockHeldByMe, true);
#endif
	/*
	 * This enqueue request should trigger compaction but no
	 * duplicates are in the queue.  So the queue should remain
	 * full.
	 */
	ret = ForwardSyncRequest(&dummy_tag, SYNC_REQUEST);
	assert_false(ret);
	assert_true(CheckpointerShmem->num_requests == CheckpointerShmem->max_requests);
	free(CheckpointerShmem);
}

int
main(int argc, char* argv[]) {
	cmockery_parse_arguments(argc, argv);
	MemoryContextInit();
	const UnitTest tests[] = {
		unit_test(test__ForwardSyncRequest_enqueue)
	};
	return run_tests(tests);
}
