/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2021-2023 Brett Sheffield <bacs@librecast.net> */

#include "test.h"
#include "testdata.h"
#include <librecast/mtree.h>
#include <librecast/sync.h>
#include <errno.h>
#include <unistd.h>

int main(void)
{
	struct stat sb_orig, sb;
	char src[] = "0000-0112.src.tmp.XXXXXX";
	char dst[] = "0000-0112.dst.tmp.XXXXXX";
	const off_t len = 1000;
	const size_t sz_s = 1024 * 10 + 13;
	const size_t extra = 999;
	size_t newlen;
	size_t off;
	int fds, rc;

	test_name("lc_syncfilelocal()");

	/* create test file */
	fds = test_data_file(src, sz_s, TEST_TMP | TEST_RND);
	test_assert(fds != -1, "test_data_file()");
	if (fds == -1) goto exit_0;
	test_assert(test_data_size(src, sz_s) == 0, "source exists and is correct size");
	test_assert(stat(src, &sb_orig) != -1, "stat source file");

	/* set destination filename */
	off = strlen(src) - 6;
	memcpy(dst + off, src + off, 6);

	/* sync files */
	test_assert(lc_syncfilelocal(dst, src, NULL, NULL, NULL, 0) == 0, "lc_syncfilelocal()");

	/* verify data matches */
	test_assert(test_data_size(dst, sz_s) == 0, "destination exists and is correct size");
	test_assert(test_file_match(dst, src) == 0, "source and destination data matches");
	test_assert(stat(src, &sb) != -1, "stat source file");
	test_assert(sb.st_mode == sb_orig.st_mode, "source file mode unchanged");
	test_assert(stat(dst, &sb) != -1, "stat destination file");
	test_assert(sb.st_mode == sb_orig.st_mode, "dest file mode set correctly");

	test_log("\nscratch the source, re-sync, check again\n");
	test_file_scratch(src, 10, 1);
	test_assert(test_file_match(dst, src) != 0, "source and destination data no longer match");
	test_assert(lc_syncfilelocal(dst, src, NULL, NULL, NULL, 0) == 0, "lc_syncfilelocal()");
	test_assert(test_data_size(dst, sz_s) == 0, "destination exists and is correct size");
	test_assert(test_file_match(dst, src) == 0, "source and destination data matches");

	test_log("\nscratch the destination, re-sync, check again\n");
	test_file_scratch(dst, 999, 42);
	test_assert(test_file_match(dst, src) != 0, "source and destination data no longer match");
	test_assert(lc_syncfilelocal(dst, src, NULL, NULL, NULL, 0) == 0, "lc_syncfilelocal()");
	test_assert(test_data_size(dst, sz_s) == 0, "destination exists and is correct size");
	test_assert(test_file_match(dst, src) == 0, "source and destination data matches");

	test_log("\ntruncate the destination, re-sync, check again\n");
	test_assert(truncate(dst, len) == 0, "truncating destination to %zu bytes", (size_t)len);
	test_assert(test_data_size(dst, len) == 0, "destination exists and is truncated");
	test_assert(test_file_match(dst, src) != 0, "source and destination data no longer match");
	test_assert(lc_syncfilelocal(dst, src, NULL, NULL, NULL, 0) == 0, "lc_syncfilelocal()");
	test_assert(test_data_size(dst, sz_s) == 0, "destination exists and is correct size");
	test_assert(test_file_match(dst, src) == 0, "source and destination data matches");

	test_log("\ntruncate the source, re-sync, check again\n");
	test_assert(truncate(src, len) == 0, "truncating source to %zu bytes", (size_t)len);
	test_assert(test_data_size(src, len) == 0, "source exists and is truncated");
	test_assert(test_file_match(dst, src) != 0, "source and destination data no longer match");
	rc = lc_syncfilelocal(dst, src, NULL, NULL, NULL, 0);
	test_assert(rc == 0, "lc_syncfilelocal() returned %i", rc);
	test_assert(test_data_size(dst, len) == 0, "destination exists and is new size");
	test_assert(test_file_match(dst, src) == 0, "source and destination data matches");

	test_log("\nextend the source (sparse), re-sync, check again\n");
	newlen = sz_s + len;
	test_sparsify_fd(fds, newlen);
	test_assert(test_data_size(src, newlen) == 0, "source exists and is new length");
	rc = lc_syncfilelocal(dst, src, NULL, NULL, NULL, 0);
	test_assert(rc == 0, "lc_syncfilelocal() returned %i", rc);
	test_assert(test_data_size(dst, newlen) == 0, "destination exists and is new size");
	test_assert(test_file_match(dst, src) == 0, "source and destination data matches");

	test_log("\nextend the source (append random data), re-sync, check again\n");
	test_file_scratch(src, newlen, extra); /* extend with random data */
	newlen += extra;
	test_assert(test_data_size(src, newlen) == 0, "source exists and is new length");
	test_assert(lc_syncfilelocal(dst, src, NULL, NULL, NULL, 0) == 0, "lc_syncfilelocal()");
	test_assert(test_data_size(dst, newlen) == 0, "destination exists and is new size");
	test_assert(test_file_match(dst, src) == 0, "source and destination data matches");

	test_assert(stat(src, &sb) != -1, "stat source file");
	test_assert(sb.st_mode == sb_orig.st_mode, "source file mode unchanged");
	test_assert(stat(dst, &sb) != -1, "stat destination file");
	test_assert(sb.st_mode == sb_orig.st_mode, "dest file mode set correctly");

	/* clean up, delete test files */
	close(fds);
	unlink(dst);
	unlink(src);
exit_0:
	return test_status;
}
