#include "torture.h"

#include <cmocka.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

static int setup(void **state)
{
	char test_tmpdir[256];
	const char *p;

	(void) state; /* unused */

	snprintf(test_tmpdir, sizeof(test_tmpdir), "/tmp/test_socket_wrapper_XXXXXX");

	p = mkdtemp(test_tmpdir);
	assert_non_null(p);

	*state = strdup(p);
	return 0;
}

static int teardown(void **state)
{
	char remove_cmd[PATH_MAX] = {0};
	char *s = (char *)*state;
	int rc;

	if (s == NULL) {
		return -1;
	}

	snprintf(remove_cmd, sizeof(remove_cmd), "rm -rf %s", s);
	free(s);

	rc = system(remove_cmd);
	if (rc < 0) {
		fprintf(stderr, "%s failed: %s", remove_cmd, strerror(errno));
	}

	return rc;
}

static void test_fcntl_lock(void **state)
{
	char file[PATH_MAX];
	char buf[16];
	int fd, rc, len;
	char *s = (char *)*state;
	struct flock lock = {
		.l_type = F_WRLCK,
		.l_whence = SEEK_SET,
		.l_start = 0,
		.l_len = 1,
	};
	int cmd = F_SETLK;

/* Prefer OFD locks on Linux with _GNU_SOURCE set */
#ifdef F_OFD_SETLK
	if (sizeof(lock) >= 24) {
		cmd = F_OFD_SETLK;
	}
#endif

	printf("sizeof(lock)=%zu\n", sizeof(lock));
#ifdef __USE_LARGEFILE64
	printf("__USE_LARGEFILE64\n");
#endif
#ifdef __USE_FILE_OFFSET64
	printf("__USE_FILE_OFFSET64\n");
#endif

	rc = snprintf(file, sizeof(file), "%s/file", s);
	assert_in_range(rc, 0, PATH_MAX);

	fd = open(file, O_RDWR|O_CREAT, 0600);
	assert_return_code(fd, errno);

	rc = fcntl(fd, cmd, &lock);
	assert_return_code(rc, errno);

	len = snprintf(buf, sizeof(buf), "fd=%d\n", fd);
	assert_in_range(len, 0, sizeof(buf));

	rc = write(fd, buf, len);
	assert_return_code(rc, errno);

	lock.l_type = F_UNLCK;
	rc = fcntl(fd, cmd, &lock);
	assert_return_code(rc, errno);

	rc = unlink(file);
	assert_return_code(rc, errno);

	rc = close(fd);
	assert_return_code(rc, errno);
}


int main(void) {
	int rc;

	const struct CMUnitTest tcp_fcntl_lock_tests[] = {
		cmocka_unit_test(test_fcntl_lock),
	};

	rc = cmocka_run_group_tests(tcp_fcntl_lock_tests, setup, teardown);

	return rc;
}
