#include "test_file_sendfile.h" #include "file.h" #include "config.h" #include "protocol.h" #include "utils.h" #include "test_utils.h" #include #include #include #include #include /* Test basic sendfile transfer: create a file, send it via file_send_sendfile, * receive via file_receive, and verify contents. */ static void test_sendfile_basic() { const char* content = "Hello from sendfile test!"; size_t len = strlen(content); EXPECT_TRUE(to_disk("test_sendfile_basic.txt", content, len)); File* file = file_create("test_sendfile_basic.txt"); EXPECT_NOT_NULL(file); /* Set the size so file_send_sendfile can report it */ file->data->size = len; Config* cfg = config_create(str_dup(PROTOCOL_VERSION), str_dup("/tmp"), str_dup("/tmp"), false, false, false, false, false, 0, false, 0); EXPECT_NOT_NULL(cfg); int p[2]; EXPECT_EQ_INT(pipe(p), 0); io_set_fds(p[0], p[1]); io_set_bwlimit(0); pid_t pid = fork(); if (pid == 0) { /* Child: receive */ close(p[1]); File* received = file_receive(cfg, p[0]); close(p[0]); bool ok = true; if (!received) ok = false; else { if (!received->path || strcmp(received->path, "test_sendfile_basic.txt") != 0) ok = false; if (!received->data || received->data->size != len) ok = false; else if (memcmp(received->data->data, content, len) != 0) ok = false; } file_destroy(received); config_delete(cfg); _exit(ok ? 0 : 1); } else { /* Parent: send via sendfile */ close(p[0]); bool sent = file_send_sendfile(file, p[1], false, 0, true); close(p[1]); int status; waitpid(pid, &status, 0); file_destroy(file); config_delete(cfg); unlink("test_sendfile_basic.txt"); EXPECT_TRUE(sent); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } } /* Test sendfile with an empty file */ static void test_sendfile_empty_file() { const char* content = ""; size_t len = 0; EXPECT_TRUE(to_disk("test_sendfile_empty.txt", content, len)); File* file = file_create("test_sendfile_empty.txt"); EXPECT_NOT_NULL(file); file->data->size = 0; Config* cfg = config_create(str_dup(PROTOCOL_VERSION), str_dup("/tmp"), str_dup("/tmp"), false, false, false, false, false, 0, false, 0); EXPECT_NOT_NULL(cfg); int p[2]; EXPECT_EQ_INT(pipe(p), 0); io_set_fds(p[0], p[1]); io_set_bwlimit(0); pid_t pid = fork(); if (pid == 0) { close(p[1]); File* received = file_receive(cfg, p[0]); close(p[0]); bool ok = true; if (!received) ok = false; else { if (strcmp(received->path, "test_sendfile_empty.txt") != 0) ok = false; if (received->data->size != 0) ok = false; } file_destroy(received); config_delete(cfg); _exit(ok ? 0 : 1); } else { close(p[0]); bool sent = file_send_sendfile(file, p[1], false, 0, true); close(p[1]); int status; waitpid(pid, &status, 0); file_destroy(file); config_delete(cfg); unlink("test_sendfile_empty.txt"); EXPECT_TRUE(sent); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } } /* Test error path: file does not exist on disk */ static void test_sendfile_missing_file() { File* file = file_create("nonexistent_sendfile_test_file.txt"); EXPECT_NOT_NULL(file); file->data->size = 100; /* fake size */ /* Use a pipe that we can write to but sendfile should fail */ int p[2]; EXPECT_EQ_INT(pipe(p), 0); io_set_fds(p[0], p[1]); io_set_bwlimit(0); /* file_send_sendfile will try to open the nonexistent file -> should return false */ bool sent = file_send_sendfile(file, p[1], false, 0, true); close(p[0]); close(p[1]); file_destroy(file); EXPECT_FALSE(sent); } /* Test compression level > 0 falls back to file_send_single_calls */ static void test_sendfile_compression_fallback() { const char* content = "Compression fallback content"; size_t len = strlen(content); EXPECT_TRUE(to_disk("test_sendfile_comp.txt", content, len)); struct stat st; EXPECT_EQ_INT(stat("test_sendfile_comp.txt", &st), 0); File* file = file_create("test_sendfile_comp.txt"); EXPECT_NOT_NULL(file); /* Load the file data into memory (required by file_send_single_calls fallback) */ file->data->size = (size_t)st.st_size; EXPECT_TRUE(file_load_data(file)); Config* cfg = config_create(str_dup(PROTOCOL_VERSION), str_dup("/tmp"), str_dup("/tmp"), false, false, false, true, false, 3, false, 0); EXPECT_NOT_NULL(cfg); int p[2]; EXPECT_EQ_INT(pipe(p), 0); io_set_fds(p[0], p[1]); io_set_bwlimit(0); pid_t pid = fork(); if (pid == 0) { close(p[1]); File* received = file_receive(cfg, p[0]); close(p[0]); bool ok = true; if (!received) ok = false; else { if (received->data->size != len) ok = false; else if (memcmp(received->data->data, content, len) != 0) ok = false; } file_destroy(received); config_delete(cfg); _exit(ok ? 0 : 1); } else { close(p[0]); /* compression_level = 3 triggers fallback to file_send_single_calls */ bool sent = file_send_sendfile(file, p[1], false, 3, true); close(p[1]); int status; waitpid(pid, &status, 0); file_destroy(file); config_delete(cfg); unlink("test_sendfile_comp.txt"); EXPECT_TRUE(sent); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } } /* Test sendfile without path (send_path = false) */ static void test_sendfile_no_path() { const char* content = "No path sendfile test"; size_t len = strlen(content); EXPECT_TRUE(to_disk("test_sendfile_nopath.txt", content, len)); File* file = file_create("test_sendfile_nopath.txt"); EXPECT_NOT_NULL(file); file->data->size = len; int p[2]; EXPECT_EQ_INT(pipe(p), 0); io_set_fds(p[0], p[1]); io_set_bwlimit(0); pid_t pid = fork(); if (pid == 0) { close(p[1]); /* When send_path is false, the sender still sends file_type + data */ int file_type; receive_int(p[0], &file_type); Data* received = receive_data(p[0]); close(p[0]); bool ok = true; if (!received) ok = false; else if (received->size != len) ok = false; else if (memcmp(received->data, content, len) != 0) ok = false; data_destroy(received); _exit(ok ? 0 : 1); } else { close(p[0]); bool sent = file_send_sendfile(file, p[1], false, 0, false); close(p[1]); int status; waitpid(pid, &status, 0); file_destroy(file); unlink("test_sendfile_nopath.txt"); EXPECT_TRUE(sent); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); } } void test_file_sendfile() { test_sendfile_basic(); test_sendfile_empty_file(); test_sendfile_missing_file(); test_sendfile_compression_fallback(); test_sendfile_no_path(); }