From a3c60158e987af569bc7376fcc1c325fee44a78d Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:14:24 +0100 Subject: [PATCH] test: add recv module unit tests --- tests/recv/test_build_reply.c | 51 +++++++++++++++++++ tests/recv/test_parse_packet.c | 74 ++++++++++++++++++++++++++++ tests/recv/test_process.c | 82 +++++++++++++++++++++++++++++++ tests/recv/test_receive_packet.c | 32 ++++++++++++ tests/recv/test_validate_params.c | 61 +++++++++++++++++++++++ 5 files changed, 300 insertions(+) create mode 100644 tests/recv/test_build_reply.c create mode 100644 tests/recv/test_parse_packet.c create mode 100644 tests/recv/test_process.c create mode 100644 tests/recv/test_receive_packet.c create mode 100644 tests/recv/test_validate_params.c diff --git a/tests/recv/test_build_reply.c b/tests/recv/test_build_reply.c new file mode 100644 index 0000000..c7c97f4 --- /dev/null +++ b/tests/recv/test_build_reply.c @@ -0,0 +1,51 @@ +#include +#include "icmp.h" +#include "internal/icmp_recv.h" +#include +#include + +Test(build_reply, basic_reply) +{ + icmp_reply_t reply; + uint8_t buffer[64]; + struct in_addr src; + const char *payload_data = "test"; + + memset(buffer, 0, sizeof(buffer)); + inet_pton(AF_INET, "192.168.1.1", &src); + recv_build_reply(&reply, 0, 0, 64, src, payload_data, 4, buffer, 20); + cr_assert_eq(reply.type, 0); + cr_assert_eq(reply.code, 0); + cr_assert_eq(reply.ttl, 64); + cr_assert_eq(reply.from.s_addr, src.s_addr); + cr_assert_eq(reply.payload, payload_data); + cr_assert_eq(reply.payload_len, 4); + cr_assert_not_null(reply.ip_payload); +} + +Test(build_reply, different_types) +{ + icmp_reply_t reply; + uint8_t buffer[64]; + struct in_addr src; + + inet_pton(AF_INET, "8.8.8.8", &src); + recv_build_reply(&reply, 3, 1, 128, src, NULL, 0, buffer, 20); + cr_assert_eq(reply.type, 3); + cr_assert_eq(reply.code, 1); + cr_assert_eq(reply.ttl, 128); + cr_assert_null(reply.payload); + cr_assert_eq(reply.payload_len, 0); +} + +Test(build_reply, timestamp_set) +{ + icmp_reply_t reply; + uint8_t buffer[64]; + struct in_addr src; + + inet_pton(AF_INET, "127.0.0.1", &src); + recv_build_reply(&reply, 0, 0, 64, src, NULL, 0, buffer, 20); + cr_assert(reply.timestamp.tv_sec > 0 || reply.timestamp.tv_nsec > 0, + "Timestamp should be set"); +} diff --git a/tests/recv/test_parse_packet.c b/tests/recv/test_parse_packet.c new file mode 100644 index 0000000..41e1d5d --- /dev/null +++ b/tests/recv/test_parse_packet.c @@ -0,0 +1,74 @@ +#include +#include "icmp.h" +#include "internal/icmp_recv.h" +#include "internal/icmp_packet.h" +#include +#include + +static void +build_test_packet(uint8_t *buffer, size_t *len) +{ + struct ip_header *ip; + struct icmp_header *icmp; + + memset(buffer, 0, 64); + ip = (struct ip_header *)buffer; + ip->version_ihl = 0x45; + ip->ttl = 64; + ip->protocol = 1; + inet_pton(AF_INET, "192.168.1.1", &ip->saddr); + icmp = (struct icmp_header *)(buffer + 20); + icmp->type = 0; + icmp->code = 0; + *len = 28; +} + +Test(parse_packet, valid_packet) +{ + uint8_t buffer[64]; + size_t buffer_len; + uint8_t type, code, ttl; + struct in_addr src_addr; + const void *payload; + size_t payload_len, ip_hdr_len; + int ret; + + build_test_packet(buffer, &buffer_len); + ret = recv_parse_packet(buffer, buffer_len, &type, &code, &ttl, + &src_addr, &payload, &payload_len, &ip_hdr_len); + cr_assert_eq(ret, 0); + cr_assert_eq(type, 0); + cr_assert_eq(code, 0); + cr_assert_eq(ttl, 64); + cr_assert_eq(ip_hdr_len, 20); +} + +Test(parse_packet, buffer_too_small) +{ + uint8_t buffer[10]; + uint8_t type, code, ttl; + struct in_addr src_addr; + const void *payload; + size_t payload_len, ip_hdr_len; + int ret; + + ret = recv_parse_packet(buffer, sizeof(buffer), &type, &code, &ttl, + &src_addr, &payload, &payload_len, &ip_hdr_len); + cr_assert_eq(ret, -1); +} + +Test(parse_packet, invalid_ip_version) +{ + uint8_t buffer[64]; + uint8_t type, code, ttl; + struct in_addr src_addr; + const void *payload; + size_t payload_len, ip_hdr_len; + int ret; + + memset(buffer, 0, sizeof(buffer)); + buffer[0] = 0x35; + ret = recv_parse_packet(buffer, 40, &type, &code, &ttl, + &src_addr, &payload, &payload_len, &ip_hdr_len); + cr_assert_eq(ret, -1); +} diff --git a/tests/recv/test_process.c b/tests/recv/test_process.c new file mode 100644 index 0000000..11430b2 --- /dev/null +++ b/tests/recv/test_process.c @@ -0,0 +1,82 @@ +#include +#include "icmp.h" +#include "test_helpers.h" +#include +#include +#include + +static void +dummy_callback(const icmp_reply_t *reply, void *userdata) +{ + (void)reply; + (void)userdata; +} + +static void +counter_callback(const icmp_reply_t *reply, void *userdata) +{ + int *count; + + (void)reply; + count = (int *)userdata; + (*count)++; +} + +Test(process, null_handle) +{ + int ret; + + ret = icmp_process(NULL, dummy_callback, NULL); + cr_assert_eq(ret, -1); +} + +Test(process, null_callback) +{ + icmp_handle_t *h; + int ret; + + if (0 == has_net_raw_capability()) + cr_skip("Test requires CAP_NET_RAW or root privileges"); + h = icmp_create(); + cr_assert_not_null(h); + ret = icmp_process(h, NULL, NULL); + cr_assert_eq(ret, -1); + cr_assert_str_eq(icmp_strerror(h), "Callback cannot be NULL"); + icmp_destroy(h); +} + +Test(process, no_packets_available) +{ + icmp_handle_t *h; + int ret; + + if (0 == has_net_raw_capability()) + cr_skip("Test requires CAP_NET_RAW or root privileges"); + h = icmp_create(); + cr_assert_not_null(h); + ret = icmp_process(h, dummy_callback, NULL); + cr_assert_eq(ret, 0); + icmp_destroy(h); +} + +Test(process, echo_loopback, .disabled = true) +{ + icmp_handle_t *h; + struct in_addr dest; + int ret; + int count; + + if (0 == has_net_raw_capability()) + cr_skip("Test requires CAP_NET_RAW or root privileges"); + h = icmp_create(); + cr_assert_not_null(h); + inet_pton(AF_INET, "127.0.0.1", &dest); + ret = icmp_send_echo(h, dest, 1234, 1, 64); + cr_assert_eq(ret, 0); + sleep(1); + count = 0; + ret = icmp_process(h, counter_callback, &count); + cr_assert_eq(ret, 0); + cr_assert_geq(count, 1, "Should receive at least one packet"); + icmp_destroy(h); +} diff --git a/tests/recv/test_receive_packet.c b/tests/recv/test_receive_packet.c new file mode 100644 index 0000000..22e6784 --- /dev/null +++ b/tests/recv/test_receive_packet.c @@ -0,0 +1,32 @@ +#include +#include "icmp.h" +#include "internal/icmp_internal.h" +#include "internal/icmp_recv.h" +#include "test_helpers.h" + +Test(receive_packet, invalid_fd) +{ + uint8_t buffer[64]; + struct sockaddr_in from; + ssize_t ret; + + ret = recv_receive_packet(-1, buffer, sizeof(buffer), &from); + cr_assert_eq(ret, -1); +} + +Test(receive_packet, no_data_available) +{ + icmp_handle_t *h; + uint8_t buffer[64]; + struct sockaddr_in from; + ssize_t ret; + + if (0 == has_net_raw_capability()) + cr_skip("Test requires CAP_NET_RAW or root privileges"); + h = icmp_create(); + cr_assert_not_null(h); + ret = recv_receive_packet(((struct icmp_handle *)h)->fd, buffer, + sizeof(buffer), &from); + cr_assert_eq(ret, 0, "Should return 0 when no data (EAGAIN)"); + icmp_destroy(h); +} diff --git a/tests/recv/test_validate_params.c b/tests/recv/test_validate_params.c new file mode 100644 index 0000000..ce1754b --- /dev/null +++ b/tests/recv/test_validate_params.c @@ -0,0 +1,61 @@ +#include +#include "icmp.h" +#include "internal/icmp_internal.h" +#include "internal/icmp_recv.h" +#include "test_helpers.h" + +static void +dummy_callback(const icmp_reply_t *reply, void *userdata) +{ + (void)reply; + (void)userdata; +} + +Test(validate_params, null_handle) +{ + int ret; + + ret = recv_validate_params(NULL, dummy_callback); + cr_assert_eq(ret, 0); +} + +Test(validate_params, null_callback) +{ + icmp_handle_t *h; + int ret; + + if (0 == has_net_raw_capability()) + cr_skip("Test requires CAP_NET_RAW or root privileges"); + h = icmp_create(); + cr_assert_not_null(h); + ret = recv_validate_params((struct icmp_handle *)h, NULL); + cr_assert_eq(ret, 0); + icmp_destroy(h); +} + +Test(validate_params, invalid_fd) +{ + struct icmp_handle h; + int ret; + + h.fd = -1; + h.last_error = ICMP_OK; + h.error_msg[0] = '\0'; + ret = recv_validate_params(&h, dummy_callback); + cr_assert_eq(ret, 0); + cr_assert_eq(h.last_error, ICMP_ERR_INVALID); +} + +Test(validate_params, valid_params) +{ + icmp_handle_t *h; + int ret; + + if (0 == has_net_raw_capability()) + cr_skip("Test requires CAP_NET_RAW or root privileges"); + h = icmp_create(); + cr_assert_not_null(h); + ret = recv_validate_params((struct icmp_handle *)h, dummy_callback); + cr_assert_eq(ret, 1); + icmp_destroy(h); +}