fix: refactoring and portability — issues #61, #51, #52

This commit is contained in:
2026-07-20 18:01:23 +02:00
parent 504a3a4d4f
commit f256e468a1
28 changed files with 877 additions and 70 deletions
+9
View File
@@ -23,6 +23,14 @@ static void test_data_create_empty() {
data_destroy(d);
}
static void test_data_create_empty_zero() {
Data* d = data_create_empty(0);
EXPECT_NOT_NULL(d);
EXPECT_NOT_NULL(d->data);
EXPECT_EQ_INT((int)d->size, 0);
data_destroy(d);
}
static void test_data_create_reserve() {
Data* d = data_create_reserve(1024);
EXPECT_NOT_NULL(d);
@@ -44,6 +52,7 @@ static void test_data_destroy_normal() {
void test_data() {
test_data_create();
test_data_create_empty();
test_data_create_empty_zero();
test_data_create_reserve();
test_data_destroy_null();
test_data_destroy_normal();
+235
View File
@@ -0,0 +1,235 @@
#include "test_file_sendfile.h"
#include "file.h"
#include "data.h"
#include "config.h"
#include "protocol.h"
#include "utils.h"
#include "test_utils.h"
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
static void test_sendfile_basic() {
const char* content = "Hello sendfile test content!";
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);
file->data->size = len;
file->data->data = malloc(len);
EXPECT_NOT_NULL(file->data->data);
memcpy(file->data->data, content, 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]);
// Receive path: read size then data
unsigned long long recv_size;
EXPECT_TRUE(receive_n_data(p[0], &recv_size, sizeof(recv_size)));
EXPECT_EQ_INT((int)recv_size, (int)len);
char* buf = malloc(recv_size + 1);
EXPECT_NOT_NULL(buf);
EXPECT_TRUE(receive_n_data(p[0], buf, recv_size));
buf[recv_size] = '\0';
EXPECT_EQ_INT(memcmp(buf, content, len), 0);
free(buf);
close(p[0]);
_exit(0);
} 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_basic.txt");
EXPECT_TRUE(sent);
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
}
static void test_sendfile_with_path() {
const char* content = "Sendfile with path test";
size_t len = strlen(content);
EXPECT_TRUE(to_disk("test_sendfile_path.txt", content, len));
File* file = file_create("test_sendfile_path.txt");
EXPECT_NOT_NULL(file);
file->data->size = len;
file->data->data = malloc(len);
EXPECT_NOT_NULL(file->data->data);
memcpy(file->data->data, content, 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]);
char* recv_path = receive_str(p[0]);
EXPECT_NOT_NULL(recv_path);
EXPECT_EQ_STR(recv_path, "test_sendfile_path.txt");
free(recv_path);
unsigned long long recv_size;
EXPECT_TRUE(receive_n_data(p[0], &recv_size, sizeof(recv_size)));
char* buf = malloc(recv_size + 1);
EXPECT_NOT_NULL(buf);
EXPECT_TRUE(receive_n_data(p[0], buf, recv_size));
buf[recv_size] = '\0';
EXPECT_EQ_INT(memcmp(buf, content, len), 0);
free(buf);
close(p[0]);
_exit(0);
} 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);
unlink("test_sendfile_path.txt");
EXPECT_TRUE(sent);
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
}
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 = len;
file->data->data = NULL;
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]);
unsigned long long recv_size;
EXPECT_TRUE(receive_n_data(p[0], &recv_size, sizeof(recv_size)));
EXPECT_EQ_INT((int)recv_size, 0);
close(p[0]);
_exit(0);
} 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_empty.txt");
EXPECT_TRUE(sent);
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
}
}
static void test_sendfile_with_compression_fallback() {
// When compression_level > 0, sendfile falls back to file_send_single_calls
const char* content = "Sendfile compression fallback test data here.";
size_t len = strlen(content);
EXPECT_TRUE(to_disk("test_sendfile_comp.txt", content, len));
File* file = file_create("test_sendfile_comp.txt");
EXPECT_NOT_NULL(file);
file->data->size = len;
file->data->data = malloc(len);
EXPECT_NOT_NULL(file->data->data);
memcpy(file->data->data, content, len);
Config* cfg = config_create(str_dup(PROTOCOL_VERSION), str_dup("/tmp"), str_dup("/tmp"), false,
false, false, false, false, 0, false, 0);
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 || 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]);
// Send with compression_level=1, should fall back to file_send_single_calls
bool sent = file_send_sendfile(file, p[1], false, 1, 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);
}
}
static void test_sendfile_nonexistent_file() {
File* file = file_create("nonexistent_file_xyz_sendfile_test.txt");
EXPECT_NOT_NULL(file);
file->data->size = 100;
file->data->data = malloc(100);
EXPECT_NOT_NULL(file->data->data);
memset(file->data->data, 0, 100);
int p[2];
EXPECT_EQ_INT(pipe(p), 0);
io_set_fds(p[0], p[1]);
io_set_bwlimit(0);
close(p[0]);
bool sent = file_send_sendfile(file, p[1], false, 0, false);
close(p[1]);
file_destroy(file);
// File doesn't exist on disk, sendfile should fail
EXPECT_FALSE(sent);
}
void test_file_sendfile() {
test_sendfile_basic();
test_sendfile_with_path();
test_sendfile_empty_file();
test_sendfile_with_compression_fallback();
test_sendfile_nonexistent_file();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_FILE_SENDFILE_H
#define TEST_FILE_SENDFILE_H
void test_file_sendfile();
#endif
+78
View File
@@ -0,0 +1,78 @@
#include "test_log.h"
#include "log.h"
#include "test_utils.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
// Helper to redirect stderr temporarily
static int stderr_pipe[2];
static void capture_stderr_start() {
fflush(stderr);
EXPECT_EQ_INT(pipe(stderr_pipe), 0);
EXPECT_EQ_INT(dup2(stderr_pipe[1], STDERR_FILENO), STDERR_FILENO);
close(stderr_pipe[1]);
}
static void capture_stderr_end() {
fflush(stderr);
close(stderr_pipe[0]);
}
static void test_log_message_debug() {
set_log_level(LOG_LEVEL_DEBUG);
// Should output at DEBUG level
log_message(LOG_LEVEL_DEBUG, "Debug message test: %d", 42);
log_message(LOG_LEVEL_INFO, "Info message test");
log_message(LOG_LEVEL_WARNING, "Warning message test");
log_message(LOG_LEVEL_ERROR, "Error message test");
// If we get here without crashing, the test passes
EXPECT_TRUE(true);
}
static void test_log_message_level_filtering() {
set_log_level(LOG_LEVEL_WARNING);
capture_stderr_start();
log_message(LOG_LEVEL_DEBUG, "Should NOT appear");
log_message(LOG_LEVEL_INFO, "Should NOT appear");
log_message(LOG_LEVEL_WARNING, "Should appear");
log_message(LOG_LEVEL_ERROR, "Should appear");
capture_stderr_end();
// We can't easily check the content, but we verified it doesn't crash
EXPECT_TRUE(true);
}
static void test_log_message_error() {
set_log_level(LOG_LEVEL_ERROR);
log_message(LOG_LEVEL_ERROR, "Error only: %s", "critical");
EXPECT_TRUE(true);
}
static void test_set_log_level() {
set_log_level(LOG_LEVEL_DEBUG);
log_message(LOG_LEVEL_DEBUG, "debug visible");
set_log_level(LOG_LEVEL_WARNING);
log_message(LOG_LEVEL_DEBUG, "debug hidden (no crash)");
log_message(LOG_LEVEL_WARNING, "warning visible");
set_log_level(LOG_LEVEL_INFO);
log_message(LOG_LEVEL_INFO, "info visible");
EXPECT_TRUE(true);
}
static void test_log_message_various_args() {
set_log_level(LOG_LEVEL_DEBUG);
log_message(LOG_LEVEL_INFO, "String: %s, Int: %d, Hex: %x", "test", 123, 0xFF);
log_message(LOG_LEVEL_WARNING, "Warning with number %d", 42);
log_message(LOG_LEVEL_DEBUG, "Debug with pointer %p", (void*)0x1234);
EXPECT_TRUE(true);
}
void test_log() {
test_log_message_debug();
test_log_message_level_filtering();
test_log_message_error();
test_set_log_level();
test_log_message_various_args();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_LOG_H
#define TEST_LOG_H
void test_log();
#endif
+91
View File
@@ -0,0 +1,91 @@
#include "test_multiprocessing.h"
#include "multiprocessing.h"
#include "config.h"
#include "queue.h"
#include "utils.h"
#include "test_utils.h"
#include <stdlib.h>
#include <string.h>
static void test_pipeline_context_sender_create_destroy() {
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);
Queue* q_scanner = queue_create(10, free);
EXPECT_NOT_NULL(q_scanner);
Queue* q_loader = queue_create(10, free);
EXPECT_NOT_NULL(q_loader);
PipelineContextSender* ctx = pipeline_context_sender_create(cfg, q_scanner, q_loader);
EXPECT_NOT_NULL(ctx);
EXPECT_EQ_INT(ctx->scanner_done, false);
EXPECT_EQ_INT(ctx->loader_done, false);
EXPECT_NULL(ctx->manifest);
pipeline_context_sender_destroy(ctx);
// cfg, q_scanner, q_loader are destroyed by pipeline_context_sender_destroy
}
static void test_pipeline_context_sender_create_null_config() {
// Passing NULL config to pipeline_context_sender_create - it will be used
// and destroyed, so this should be tested carefully.
Queue* q_scanner = queue_create(10, free);
EXPECT_NOT_NULL(q_scanner);
Queue* q_loader = queue_create(10, free);
EXPECT_NOT_NULL(q_loader);
PipelineContextSender* ctx = pipeline_context_sender_create(NULL, q_scanner, q_loader);
EXPECT_NOT_NULL(ctx);
EXPECT_NULL(ctx->config);
// Destroy without config - config_delete(NULL) should be safe
pipeline_context_sender_destroy(ctx);
}
static void test_pipeline_context_sender_destroy_null() {
pipeline_context_sender_destroy(NULL);
}
static void test_pipeline_context_receiver_create_destroy() {
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);
Queue* q = queue_create(10, free);
EXPECT_NOT_NULL(q);
PipelineContextReceiver* ctx = pipeline_context_receiver_create(cfg, q, 42);
EXPECT_NOT_NULL(ctx);
EXPECT_EQ_INT(ctx->file_descriptor, 42);
EXPECT_EQ_INT(ctx->receiver_done, false);
pipeline_context_receiver_destroy(ctx);
// cfg, q are destroyed by pipeline_context_receiver_destroy
}
static void test_pipeline_context_receiver_create_null_config() {
Queue* q = queue_create(10, free);
EXPECT_NOT_NULL(q);
PipelineContextReceiver* ctx = pipeline_context_receiver_create(NULL, q, -1);
EXPECT_NOT_NULL(ctx);
EXPECT_NULL(ctx->config);
EXPECT_EQ_INT(ctx->file_descriptor, -1);
pipeline_context_receiver_destroy(ctx);
}
static void test_pipeline_context_receiver_destroy_null() {
pipeline_context_receiver_destroy(NULL);
}
void test_multiprocessing() {
test_pipeline_context_sender_create_destroy();
test_pipeline_context_sender_create_null_config();
test_pipeline_context_sender_destroy_null();
test_pipeline_context_receiver_create_destroy();
test_pipeline_context_receiver_create_null_config();
test_pipeline_context_receiver_destroy_null();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_MULTIPROCESSING_H
#define TEST_MULTIPROCESSING_H
void test_multiprocessing();
#endif
+34
View File
@@ -0,0 +1,34 @@
#include "test_transport_ssh.h"
#include "transport_ssh.h"
#include "transport_tcp.h"
#include "test_utils.h"
#include <stdlib.h>
#include <string.h>
static void test_ssh_connect_bad_destination() {
Client* client = client_connect_ssh("nosuchhost.local", 22);
// SSH connection to a non-existent host should fail (return NULL)
EXPECT_NULL(client);
}
static void test_ssh_connect_null_destination() {
Client* client = client_connect_ssh(NULL, 22);
EXPECT_NULL(client);
}
static void test_ssh_connect_bad_port() {
Client* client = client_connect_ssh("localhost", -1);
EXPECT_NULL(client);
}
static void test_ssh_connect_zero_port() {
Client* client = client_connect_ssh("localhost", 0);
EXPECT_NULL(client);
}
void test_transport_ssh() {
test_ssh_connect_bad_destination();
test_ssh_connect_null_destination();
test_ssh_connect_bad_port();
test_ssh_connect_zero_port();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_TRANSPORT_SSH_H
#define TEST_TRANSPORT_SSH_H
void test_transport_ssh();
#endif
+90
View File
@@ -0,0 +1,90 @@
#include "test_transport_tcp.h"
#include "transport_tcp.h"
#include "test_utils.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
static void test_client_create_delete() {
Client* client = client_create();
EXPECT_NOT_NULL(client);
EXPECT_EQ_INT(client->file_descriptor, -1);
EXPECT_EQ_INT(client->ssh_child_pid, -1);
client_delete(client);
}
static void test_client_delete_null() {
client_delete(NULL);
}
static void test_client_disconnect_null() {
client_disconnect(NULL);
}
static void test_client_connect_bad_host() {
Client* client = client_create();
EXPECT_NOT_NULL(client);
// Connecting to a non-existent host should fail
bool connected = client_connect(client, "192.0.2.999", 12345);
EXPECT_FALSE(connected);
client_disconnect(client);
client_delete(client);
}
static void test_client_connect_bad_port() {
Client* client = client_create();
EXPECT_NOT_NULL(client);
// Connecting to port 0 should fail
bool connected = client_connect(client, "127.0.0.1", 0);
EXPECT_FALSE(connected);
client_disconnect(client);
client_delete(client);
}
static void test_client_connect_null_host() {
Client* client = client_create();
EXPECT_NOT_NULL(client);
bool connected = client_connect(client, NULL, 8080);
EXPECT_FALSE(connected);
client_disconnect(client);
client_delete(client);
}
static void test_server_create_delete() {
Server* server = server_create(0);
// server_create may return NULL if it fails to bind, but we need to check
// if it succeeds. Port 0 should bind to an ephemeral port.
if (server != NULL) {
EXPECT_NOT_NULL(server);
server_delete(&server);
EXPECT_NULL(server);
} else {
// On some systems port 0 may fail; that's okay
EXPECT_TRUE(true);
}
}
static void test_server_delete_null() {
Server* server = NULL;
server_delete(&server);
EXPECT_NULL(server);
}
void test_transport_tcp() {
test_client_create_delete();
test_client_delete_null();
test_client_disconnect_null();
test_client_connect_bad_host();
test_client_connect_bad_port();
test_client_connect_null_host();
test_server_create_delete();
test_server_delete_null();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_TRANSPORT_TCP_H
#define TEST_TRANSPORT_TCP_H
void test_transport_tcp();
#endif
+69
View File
@@ -0,0 +1,69 @@
#include "test_transport_tls.h"
#include "transport_tls.h"
#include "transport_tcp.h"
#include "test_utils.h"
#include <stdlib.h>
#include <string.h>
static void test_tls_global_init() {
bool ok = tls_global_init();
// Should succeed in normal environments with OpenSSL
// May fail in minimal environments, but we just test it doesn't crash
EXPECT_TRUE(true);
}
static void test_client_connect_tls_bad_host() {
// Ensure TLS is initialized
tls_global_init();
Client* client = client_create();
EXPECT_NOT_NULL(client);
// Connecting without TLS setup should fail
bool connected = client_connect_tls(client, "192.0.2.1", 443, NULL, NULL, NULL);
EXPECT_FALSE(connected);
client_disconnect(client);
client_delete(client);
}
static void test_client_connect_tls_null_params() {
tls_global_init();
Client* client = client_create();
EXPECT_NOT_NULL(client);
bool connected = client_connect_tls(client, NULL, 0, NULL, NULL, NULL);
EXPECT_FALSE(connected);
client_disconnect(client);
client_delete(client);
}
static void test_server_create_tls_no_cert() {
Server* server = server_create(0);
if (server != NULL) {
// Trying to set up TLS without cert/key files should fail
bool ok = server_create_tls(server, "/nonexistent/cert.pem", "/nonexistent/key.pem",
"/nonexistent/ca.pem");
EXPECT_FALSE(ok);
server_delete(&server);
}
}
static void test_server_create_tls_null_files() {
Server* server = server_create(0);
if (server != NULL) {
bool ok = server_create_tls(server, NULL, NULL, NULL);
EXPECT_FALSE(ok);
server_delete(&server);
}
}
void test_transport_tls() {
test_tls_global_init();
test_client_connect_tls_bad_host();
test_client_connect_tls_null_params();
test_server_create_tls_no_cert();
test_server_create_tls_null_files();
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef TEST_TRANSPORT_TLS_H
#define TEST_TRANSPORT_TLS_H
void test_transport_tls();
#endif