/**
 * @file test_copy_file_range.c
 * @brief copy_file_range 系统调用测试用例
 *
 * 测试 copy_file_range 系统调用的各种场景：
 * - 基本拷贝功能
 * - 指定偏移拷贝
 * - 部分拷贝（短读/短写）
 * - 错误处理（无效 fd、无效 flags、目录等）
 * - 同文件重叠检测
 */

#define _GNU_SOURCE
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <errno.h>

/* 系统调用号定义 */
#ifndef __NR_copy_file_range
#if defined(__x86_64__)
#define __NR_copy_file_range 326
#elif defined(__riscv) || defined(__loongarch__)
#define __NR_copy_file_range 285
#else
#error "Unsupported architecture"
#endif
#endif

/* copy_file_range 封装函数 */
static ssize_t copy_file_range_wrapper(int fd_in, off_t *off_in,
                                       int fd_out, off_t *off_out,
                                       size_t len, unsigned int flags)
{
    return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags);
}

/* 测试辅助函数 */
#define TEST_DIR "/tmp/cfr_test"
#define SRC_FILE TEST_DIR "/src.txt"
#define DST_FILE TEST_DIR "/dst.txt"

static int test_passed = 0;
static int test_failed = 0;

#define TEST_ASSERT(cond, msg) do { \
    if (!(cond)) { \
        printf("  FAILED: %s (line %d)\n", msg, __LINE__); \
        test_failed++; \
        return -1; \
    } \
} while(0)

#define TEST_START(name) printf("Testing %s...\n", name)
#define TEST_PASS() do { printf("  PASSED\n"); test_passed++; return 0; } while(0)

/* 创建测试目录 */
static int setup_test_dir(void)
{
    mkdir(TEST_DIR, 0755);
    return 0;
}

/* 清理测试文件 */
static void cleanup_test_files(void)
{
    unlink(SRC_FILE);
    unlink(DST_FILE);
}

/* 创建带有指定内容的测试文件 */
static int create_test_file(const char *path, const char *content, size_t len)
{
    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0)
        return -1;
    ssize_t written = write(fd, content, len);
    close(fd);
    return (written == (ssize_t)len) ? 0 : -1;
}

/* 读取文件内容 */
static ssize_t read_file_content(const char *path, char *buf, size_t len)
{
    int fd = open(path, O_RDONLY);
    if (fd < 0)
        return -1;
    ssize_t n = read(fd, buf, len);
    close(fd);
    return n;
}

/**
 * 测试 1: 基本拷贝功能
 * - 创建源文件，拷贝到目标文件
 * - 验证内容一致
 */
static int test_basic_copy(void)
{
    TEST_START("basic copy");
    cleanup_test_files();

    const char *test_data = "Hello, copy_file_range!";
    size_t data_len = strlen(test_data);

    TEST_ASSERT(create_test_file(SRC_FILE, test_data, data_len) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    /* 不指定偏移，使用文件当前位置 */
    ssize_t copied = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL, data_len, 0);
    TEST_ASSERT(copied == (ssize_t)data_len, "copy_file_range returned wrong count");

    close(src_fd);
    close(dst_fd);

    /* 验证目标文件内容 */
    char buf[256] = {0};
    ssize_t n = read_file_content(DST_FILE, buf, sizeof(buf));
    TEST_ASSERT(n == (ssize_t)data_len, "Dest file size mismatch");
    TEST_ASSERT(memcmp(buf, test_data, data_len) == 0, "Content mismatch");

    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 2: 指定偏移拷贝
 * - 从源文件偏移 5 开始读取
 * - 写入目标文件偏移 0
 */
static int test_with_offset(void)
{
    TEST_START("copy with offset");
    cleanup_test_files();

    const char *test_data = "0123456789ABCDEF";
    size_t data_len = strlen(test_data);

    TEST_ASSERT(create_test_file(SRC_FILE, test_data, data_len) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    off_t src_off = 5;  /* 从 '5' 开始 */
    ssize_t copied = copy_file_range_wrapper(src_fd, &src_off, dst_fd, NULL, 5, 0);
    TEST_ASSERT(copied == 5, "copy_file_range returned wrong count");
    TEST_ASSERT(src_off == 10, "Source offset not updated correctly");

    close(src_fd);
    close(dst_fd);

    /* 验证目标文件内容应该是 "56789" */
    char buf[256] = {0};
    ssize_t n = read_file_content(DST_FILE, buf, sizeof(buf));
    TEST_ASSERT(n == 5, "Dest file size mismatch");
    TEST_ASSERT(memcmp(buf, "56789", 5) == 0, "Content mismatch");

    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 3: 拷贝超过文件末尾（应只拷贝到 EOF）
 */
static int test_copy_past_eof(void)
{
    TEST_START("copy past EOF");
    cleanup_test_files();

    const char *test_data = "Short";
    size_t data_len = strlen(test_data);

    TEST_ASSERT(create_test_file(SRC_FILE, test_data, data_len) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    /* 请求拷贝 100 字节，但文件只有 5 字节 */
    ssize_t copied = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL, 100, 0);
    TEST_ASSERT(copied == (ssize_t)data_len, "Should only copy actual file size");

    close(src_fd);
    close(dst_fd);

    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 4: 无效的文件描述符
 */
static int test_invalid_fd(void)
{
    TEST_START("invalid fd");
    cleanup_test_files();

    const char *test_data = "Test";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    /* 使用无效的目标 fd */
    ssize_t ret = copy_file_range_wrapper(src_fd, NULL, 9999, NULL, 10, 0);
    TEST_ASSERT(ret == -1 && errno == EBADF, "Should return EBADF for invalid fd");

    close(src_fd);

    /* 使用无效的源 fd */
    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    ret = copy_file_range_wrapper(9999, NULL, dst_fd, NULL, 10, 0);
    TEST_ASSERT(ret == -1 && errno == EBADF, "Should return EBADF for invalid fd");

    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 5: 无效的 flags
 */
static int test_invalid_flags(void)
{
    TEST_START("invalid flags");
    cleanup_test_files();

    const char *test_data = "Test";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    /* flags 必须为 0 */
    ssize_t ret = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL, 10, 1);
    TEST_ASSERT(ret == -1 && errno == EINVAL, "Should return EINVAL for non-zero flags");

    close(src_fd);
    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 6: 目录拷贝（应失败）
 */
static int test_directory_copy(void)
{
    TEST_START("directory copy (should fail)");

    int dir_fd = open(TEST_DIR, O_RDONLY | O_DIRECTORY);
    if (dir_fd < 0) {
        printf("  SKIPPED: Cannot open directory\n");
        return 0;
    }

    const char *test_data = "Test";
    create_test_file(DST_FILE, test_data, strlen(test_data));

    int dst_fd = open(DST_FILE, O_WRONLY);
    if (dst_fd >= 0) {
        ssize_t ret = copy_file_range_wrapper(dir_fd, NULL, dst_fd, NULL, 10, 0);
        TEST_ASSERT(ret == -1 && (errno == EISDIR || errno == EINVAL),
                    "Should return EISDIR or EINVAL for directory");
        close(dst_fd);
    }

    close(dir_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 7: 源文件以只写方式打开（应失败）
 */
static int test_write_only_source(void)
{
    TEST_START("write-only source (should fail)");
    cleanup_test_files();

    const char *test_data = "Test";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_WRONLY);  /* 只写打开 */
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    ssize_t ret = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL, 10, 0);
    TEST_ASSERT(ret == -1 && errno == EBADF, "Should return EBADF for write-only source");

    close(src_fd);
    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 8: 目标文件以只读方式打开（应失败）
 */
static int test_read_only_dest(void)
{
    TEST_START("read-only dest (should fail)");
    cleanup_test_files();

    const char *test_data = "Test";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");
    TEST_ASSERT(create_test_file(DST_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create dest file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_RDONLY);  /* 只读打开 */
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    ssize_t ret = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL, 10, 0);
    TEST_ASSERT(ret == -1 && errno == EBADF, "Should return EBADF for read-only dest");

    close(src_fd);
    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 9: 目标文件以 O_APPEND 打开（应失败）
 */
static int test_append_dest(void)
{
    TEST_START("O_APPEND dest (should fail)");
    cleanup_test_files();

    const char *test_data = "Test";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_APPEND, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    ssize_t ret = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL, 10, 0);
    TEST_ASSERT(ret == -1 && errno == EBADF, "Should return EBADF for O_APPEND dest");

    close(src_fd);
    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 10: 负偏移（应失败）
 */
static int test_negative_offset(void)
{
    TEST_START("negative offset (should fail)");
    cleanup_test_files();

    const char *test_data = "Test";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    off_t neg_off = -10;
    ssize_t ret = copy_file_range_wrapper(src_fd, &neg_off, dst_fd, NULL, 10, 0);
    TEST_ASSERT(ret == -1 && errno == EINVAL, "Should return EINVAL for negative offset");

    close(src_fd);
    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 11: 长度为 0 的拷贝
 */
static int test_zero_length(void)
{
    TEST_START("zero length copy");
    cleanup_test_files();

    const char *test_data = "Test";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    ssize_t copied = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL, 0, 0);
    TEST_ASSERT(copied == 0, "Zero length copy should return 0");

    close(src_fd);
    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 12: 大文件拷贝
 */
static int test_large_copy(void)
{
    TEST_START("large file copy");
    cleanup_test_files();

    /* 创建一个 64KB 的测试文件 */
    size_t large_size = 64 * 1024;
    char *large_data = malloc(large_size);
    if (!large_data) {
        printf("  SKIPPED: Cannot allocate memory\n");
        return 0;
    }

    /* 填充测试数据 */
    for (size_t i = 0; i < large_size; i++) {
        large_data[i] = (char)(i & 0xFF);
    }

    TEST_ASSERT(create_test_file(SRC_FILE, large_data, large_size) == 0,
                "Failed to create large source file");

    int src_fd = open(SRC_FILE, O_RDONLY);
    TEST_ASSERT(src_fd >= 0, "Failed to open source file");

    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(dst_fd >= 0, "Failed to open dest file");

    ssize_t total_copied = 0;
    while ((size_t)total_copied < large_size) {
        ssize_t copied = copy_file_range_wrapper(src_fd, NULL, dst_fd, NULL,
                                                  large_size - total_copied, 0);
        if (copied <= 0) break;
        total_copied += copied;
    }

    TEST_ASSERT(total_copied == (ssize_t)large_size,
                "Large file copy size mismatch");

    close(src_fd);
    close(dst_fd);

    /* 验证内容 */
    char *verify_buf = malloc(large_size);
    if (verify_buf) {
        ssize_t n = read_file_content(DST_FILE, verify_buf, large_size);
        TEST_ASSERT(n == (ssize_t)large_size, "Read back size mismatch");
        TEST_ASSERT(memcmp(verify_buf, large_data, large_size) == 0,
                    "Large file content mismatch");
        free(verify_buf);
    }

    free(large_data);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 13: 同一文件重叠拷贝
 */
static int test_same_file_overlap(void)
{
    TEST_START("same file overlap");
    cleanup_test_files();

    const char *test_data = "0123456789";
    size_t data_len = strlen(test_data);
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, data_len) == 0,
                "Failed to create source file");

    int fd = open(SRC_FILE, O_RDWR);
    TEST_ASSERT(fd >= 0, "Failed to open file");

    /* 尝试重叠拷贝：源 [0, 5), 目标 [2, 7) - 重叠区域 [2, 5) */
    off_t off_in = 0;
    off_t off_out = 2;
    ssize_t ret = copy_file_range_wrapper(fd, &off_in, fd, &off_out, 5, 0);

    TEST_ASSERT(ret == -1 && errno == EINVAL,
                "Should fail with EINVAL for overlapping ranges");

    close(fd);
    cleanup_test_files();
    TEST_PASS();
}

/**
 * 测试 14: 目标偏移更新测试
 */
static int test_dest_offset_update(void)
{
    TEST_START("dest offset update");
    cleanup_test_files();

    const char *test_data = "Hello";
    TEST_ASSERT(create_test_file(SRC_FILE, test_data, strlen(test_data)) == 0,
                "Failed to create source file");
    
    int src_fd = open(SRC_FILE, O_RDONLY);
    int dst_fd = open(DST_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    TEST_ASSERT(src_fd >= 0 && dst_fd >= 0, "Failed to open files");

    off_t off_in = 0;
    off_t off_out = 10; /* 从目标文件偏移 10 开始写 */
    
    ssize_t ret = copy_file_range_wrapper(src_fd, &off_in, dst_fd, &off_out, 5, 0);
    
    TEST_ASSERT(ret == 5, "Should copy 5 bytes");
    TEST_ASSERT(off_in == 5, "Source offset should be updated to 5");
    TEST_ASSERT(off_out == 15, "Dest offset should be updated to 15");

    /* 验证文件大小应该是 15 (前10字节是空洞) */
    struct stat st;
    fstat(dst_fd, &st);
    TEST_ASSERT(st.st_size == 15, "Dest file size should be 15");

    close(src_fd);
    close(dst_fd);
    cleanup_test_files();
    TEST_PASS();
}

/* 主函数 */
int main(void)
{
    printf("=== copy_file_range system call tests ===\n\n");

    setup_test_dir();

    /* 运行所有测试 */
    test_basic_copy();
    test_with_offset();
    test_copy_past_eof();
    test_invalid_fd();
    test_invalid_flags();
    test_directory_copy();
    test_write_only_source();
    test_read_only_dest();
    test_append_dest();
    test_negative_offset();
    test_zero_length();
    test_large_copy();
    test_same_file_overlap();
    test_dest_offset_update();

    /* 清理 */
    cleanup_test_files();
    rmdir(TEST_DIR);

    printf("\n=== Test Summary ===\n");
    printf("Passed: %d\n", test_passed);
    printf("Failed: %d\n", test_failed);

    return test_failed > 0 ? 1 : 0;
}
