From 653ff61ca348dfb62734a476ebb66a44240e3cc9 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 28 Oct 2024 09:16:10 +0100 Subject: [PATCH] More unit tests --- src/femtotcp.c | 35 +++-- test/unit/unit.c | 400 +++++++++++++++++++++++++++++------------------ 2 files changed, 263 insertions(+), 172 deletions(-) diff --git a/src/femtotcp.c b/src/femtotcp.c index 6835a93..d6f714a 100644 --- a/src/femtotcp.c +++ b/src/femtotcp.c @@ -584,23 +584,26 @@ void ipstack_register_callback(struct ipstack *s, int sock_fd, void (*cb)(int so static struct ipstack_timer timers_binheap_pop(struct timers_binheap *heap) { uint32_t i = 0; - struct ipstack_timer tmr = heap->timers[0]; - heap->size--; - heap->timers[0] = heap->timers[heap->size]; - while (2*i+1 < heap->size) { - struct ipstack_timer tmp; - uint32_t j = 2*i+1; - if (j+1 < heap->size && heap->timers[j+1].expires < heap->timers[j].expires) { - j++; + struct ipstack_timer tmr = {0}; + do { + tmr = heap->timers[0]; + heap->size--; + heap->timers[0] = heap->timers[heap->size]; + while (2*i+1 < heap->size) { + struct ipstack_timer tmp; + uint32_t j = 2*i+1; + if (j+1 < heap->size && heap->timers[j+1].expires < heap->timers[j].expires) { + j++; + } + if (heap->timers[i].expires <= heap->timers[j].expires) { + break; + } + tmp = heap->timers[i]; + heap->timers[i] = heap->timers[j]; + heap->timers[j] = tmp; + i = j; } - if (heap->timers[i].expires <= heap->timers[j].expires) { - break; - } - tmp = heap->timers[i]; - heap->timers[i] = heap->timers[j]; - heap->timers[j] = tmp; - i = j; - } + } while (tmr.expires == 0); return tmr; } diff --git a/test/unit/unit.c b/test/unit/unit.c index b6b4077..12b8804 100644 --- a/test/unit/unit.c +++ b/test/unit/unit.c @@ -45,162 +45,10 @@ void mock_link_init(struct ipstack *s) ll->send = mock_send; } -START_TEST(test_arp_request_basic) -{ - struct ipstack s; - struct arp_packet *arp; - ipstack_init(&s); - uint32_t target_ip = 0xC0A80002; /* 192.168.0.2 */ - mock_link_init(&s); - s.last_tick = 1000; - arp_request(&s, target_ip); - ck_assert_int_eq(last_frame_sent_size, sizeof(struct arp_packet)); - arp = (struct arp_packet *)last_frame_sent; - ck_assert_mem_eq(arp->eth.dst, "\xff\xff\xff\xff\xff\xff", 6); - ck_assert_mem_eq(arp->eth.src, s.ll_dev.mac, 6); - ck_assert_int_eq(arp->eth.type, ee16(0x0806)); - ck_assert_int_eq(arp->htype, ee16(1)); - ck_assert_int_eq(arp->ptype, ee16(0x0800)); - ck_assert_int_eq(arp->hlen, 6); - ck_assert_int_eq(arp->plen, 4); - ck_assert_int_eq(arp->opcode, ee16(ARP_REQUEST)); - ck_assert_mem_eq(arp->sma, s.ll_dev.mac, 6); - ck_assert_int_eq(arp->sip, ee32(s.ipconf.ip)); - ck_assert_mem_eq(arp->tma, "\x00\x00\x00\x00\x00\x00", 6); - ck_assert_int_eq(arp->tip, ee32(target_ip)); +static struct timers_binheap heap; +static void reset_heap(void) { + heap.size = 0; } -END_TEST - -START_TEST(test_arp_request_throttle) -{ - struct ipstack s; - ipstack_init(&s); - uint32_t target_ip = 0xC0A80002; /*192.168.0.2*/ - mock_link_init(&s); - s.last_tick = 1000; - s.arp.last_arp = 880; - last_frame_sent_size = 0; - arp_request(&s, target_ip); - ck_assert_int_eq(last_frame_sent_size, 0); -} -END_TEST - -START_TEST(test_arp_request_target_ip) { - uint32_t target_ip = 0xC0A80002; - struct ipstack s; - ipstack_init(&s); - mock_link_init(&s); - s.last_tick = 1000; - arp_request(&s, target_ip); - ck_assert_int_eq(((struct arp_packet *)(last_frame_sent))->tip, ee32(target_ip)); -} -END_TEST - -START_TEST(test_arp_request_handling) { - struct arp_packet arp_req; - struct arp_packet *arp_reply; - memset(&arp_req, 0, sizeof(arp_req)); - uint32_t req_ip = 0xC0A80002; // 192.168.0.2 - uint32_t device_ip = 0xC0A80001; // 192.168.0.1 - uint8_t req_mac[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; - //uint8_t mac[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; - struct ipstack s; - ipstack_init(&s); - mock_link_init(&s); - s.ipconf.ip = device_ip; - - /* Prepare ARP request */ - arp_req.opcode = ee16(ARP_REQUEST); - arp_req.sip = ee32(req_ip); - memcpy(arp_req.sma, req_mac, 6); - arp_req.tip = ee32(device_ip); - - /* Call arp_recv with the ARP request */ - arp_recv(&s, &arp_req, sizeof(arp_req)); - ipstack_poll(&s, 1000); - ipstack_poll(&s, 1001); - ipstack_poll(&s, 1002); - - /* Check if ARP table updated with requester's MAC and IP */ - /* TODO */ - //ck_assert_int_eq(arp_lookup(&s, req_ip, mac), 0); - //ck_assert_mem_eq(mac, req_mac, 6); - - /* Check if an ARP reply was generated */ - arp_reply = (struct arp_packet *)last_frame_sent; - ck_assert_int_eq(last_frame_sent_size, sizeof(struct arp_packet)); - ck_assert_int_eq(arp_reply->opcode, ee16(ARP_REPLY)); - ck_assert_mem_eq(arp_reply->sma, s.ll_dev.mac, 6); // source MAC - ck_assert_int_eq(arp_reply->sip, ee32(device_ip)); // source IP - ck_assert_mem_eq(arp_reply->tma, req_mac, 6); // target MAC - ck_assert_int_eq(arp_reply->tip, ee32(req_ip)); // target IP -} -END_TEST - -START_TEST(test_arp_reply_handling) { - struct arp_packet arp_reply; - memset(&arp_reply, 0, sizeof(arp_reply)); - uint32_t reply_ip = 0xC0A80003; // 192.168.0.3 - uint8_t reply_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01}; - struct ipstack s; - ipstack_init(&s); - mock_link_init(&s); - - /* Prepare ARP reply */ - arp_reply.opcode = ee16(ARP_REPLY); - arp_reply.sip = ee32(reply_ip); - memcpy(arp_reply.sma, reply_mac, 6); - - /* Call arp_recv with the ARP reply */ - arp_recv(&s, &arp_reply, sizeof(arp_reply)); - - /* Check if ARP table updated with reply IP and MAC */ - ck_assert_int_eq(s.arp.neighbors[0].ip, reply_ip); - ck_assert_mem_eq(s.arp.neighbors[0].mac, reply_mac, 6); - - /* Update same IP with a different MAC address */ - uint8_t new_mac[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; - memcpy(arp_reply.sma, new_mac, 6); - arp_recv(&s, &arp_reply, sizeof(arp_reply)); - - /* Check if ARP table updates with new MAC */ - ck_assert_mem_eq(s.arp.neighbors[0].mac, new_mac, 6); -} -END_TEST - -START_TEST(test_arp_lookup_success) { - uint8_t found_mac[6]; - uint32_t ip = 0xC0A80002; - const uint8_t mock_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01}; - struct ipstack s; - ipstack_init(&s); - mock_link_init(&s); - - /* Add a known IP-MAC pair */ - s.arp.neighbors[0].ip = ip; - memcpy(s.arp.neighbors[0].mac, mock_mac, 6); - - /* Test arp_lookup */ - int result = arp_lookup(&s, ip, found_mac); - ck_assert_int_eq(result, 0); - ck_assert_mem_eq(found_mac, mock_mac, 6); -} -END_TEST - -START_TEST(test_arp_lookup_failure) { - uint8_t found_mac[6]; - uint32_t ip = 0xC0A80004; - struct ipstack s; - ipstack_init(&s); - mock_link_init(&s); - - /* Ensure arp_lookup fails for unknown IP */ - int result = arp_lookup(&s, ip, found_mac); - ck_assert_int_eq(result, -1); - uint8_t zero_mac[6] = {0, 0, 0, 0, 0, 0}; - ck_assert_mem_eq(found_mac, zero_mac, 6); -} -END_TEST START_TEST(test_fifo_init) @@ -505,19 +353,250 @@ START_TEST(test_queue_pop_wraparound) { END_TEST +/* Utils */ +START_TEST(test_insert_timer) { + reset_heap(); + + struct ipstack_timer tmr1 = { .expires = 100 }; + struct ipstack_timer tmr2 = { .expires = 50 }; + struct ipstack_timer tmr3 = { .expires = 200 }; + + int id1 = timers_binheap_insert(&heap, tmr1); + int id2 = timers_binheap_insert(&heap, tmr2); + int id3 = timers_binheap_insert(&heap, tmr3); + + ck_assert_int_eq(heap.size, 3); + ck_assert_int_lt(heap.timers[0].expires, heap.timers[1].expires); + ck_assert_int_lt(heap.timers[0].expires, heap.timers[2].expires); + ck_assert_int_ne(id1, id2); + ck_assert_int_ne(id2, id3); +} +END_TEST + +START_TEST(test_pop_timer) { + reset_heap(); + + struct ipstack_timer tmr1 = { .expires = 300 }; + struct ipstack_timer tmr2 = { .expires = 100 }; + struct ipstack_timer tmr3 = { .expires = 200 }; + + timers_binheap_insert(&heap, tmr1); + timers_binheap_insert(&heap, tmr2); + timers_binheap_insert(&heap, tmr3); + + struct ipstack_timer popped = timers_binheap_pop(&heap); + ck_assert_int_eq(popped.expires, 100); + ck_assert_int_eq(heap.size, 2); + ck_assert_int_lt(heap.timers[0].expires, heap.timers[1].expires); +} +END_TEST + +START_TEST(test_is_timer_expired) { + reset_heap(); + + struct ipstack_timer tmr = { .expires = 150 }; + timers_binheap_insert(&heap, tmr); + + ck_assert_int_eq(is_timer_expired(&heap, 100), 0); + ck_assert_int_eq(is_timer_expired(&heap, 150), 1); + ck_assert_int_eq(is_timer_expired(&heap, 200), 1); +} +END_TEST + +START_TEST(test_cancel_timer) { + reset_heap(); + + struct ipstack_timer tmr1 = { .expires = 100 }; + struct ipstack_timer tmr2 = { .expires = 200 }; + + int id1 = timers_binheap_insert(&heap, tmr1); + int id2 = timers_binheap_insert(&heap, tmr2); + (void)id2; + + timer_binheap_cancel(&heap, id1); + ck_assert_int_eq(heap.timers[0].expires, 0); // tmr1 canceled + + struct ipstack_timer popped = timers_binheap_pop(&heap); + ck_assert_int_eq(popped.expires, 200); // Only tmr2 should remain + ck_assert_int_eq(heap.size, 0); +} +END_TEST + + +/* Arp suite */ +START_TEST(test_arp_request_basic) +{ + struct ipstack s; + struct arp_packet *arp; + ipstack_init(&s); + uint32_t target_ip = 0xC0A80002; /* 192.168.0.2 */ + mock_link_init(&s); + s.last_tick = 1000; + arp_request(&s, target_ip); + ck_assert_int_eq(last_frame_sent_size, sizeof(struct arp_packet)); + arp = (struct arp_packet *)last_frame_sent; + ck_assert_mem_eq(arp->eth.dst, "\xff\xff\xff\xff\xff\xff", 6); + ck_assert_mem_eq(arp->eth.src, s.ll_dev.mac, 6); + ck_assert_int_eq(arp->eth.type, ee16(0x0806)); + ck_assert_int_eq(arp->htype, ee16(1)); + ck_assert_int_eq(arp->ptype, ee16(0x0800)); + ck_assert_int_eq(arp->hlen, 6); + ck_assert_int_eq(arp->plen, 4); + ck_assert_int_eq(arp->opcode, ee16(ARP_REQUEST)); + ck_assert_mem_eq(arp->sma, s.ll_dev.mac, 6); + ck_assert_int_eq(arp->sip, ee32(s.ipconf.ip)); + ck_assert_mem_eq(arp->tma, "\x00\x00\x00\x00\x00\x00", 6); + ck_assert_int_eq(arp->tip, ee32(target_ip)); +} +END_TEST + +START_TEST(test_arp_request_throttle) +{ + struct ipstack s; + ipstack_init(&s); + uint32_t target_ip = 0xC0A80002; /*192.168.0.2*/ + mock_link_init(&s); + s.last_tick = 1000; + s.arp.last_arp = 880; + last_frame_sent_size = 0; + arp_request(&s, target_ip); + ck_assert_int_eq(last_frame_sent_size, 0); +} +END_TEST + +START_TEST(test_arp_request_target_ip) { + uint32_t target_ip = 0xC0A80002; + struct ipstack s; + ipstack_init(&s); + mock_link_init(&s); + s.last_tick = 1000; + arp_request(&s, target_ip); + ck_assert_int_eq(((struct arp_packet *)(last_frame_sent))->tip, ee32(target_ip)); +} +END_TEST + +START_TEST(test_arp_request_handling) { + struct arp_packet arp_req; + struct arp_packet *arp_reply; + memset(&arp_req, 0, sizeof(arp_req)); + uint32_t req_ip = 0xC0A80002; // 192.168.0.2 + uint32_t device_ip = 0xC0A80001; // 192.168.0.1 + uint8_t req_mac[6] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + //uint8_t mac[6] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}; + struct ipstack s; + ipstack_init(&s); + mock_link_init(&s); + s.ipconf.ip = device_ip; + + /* Prepare ARP request */ + arp_req.opcode = ee16(ARP_REQUEST); + arp_req.sip = ee32(req_ip); + memcpy(arp_req.sma, req_mac, 6); + arp_req.tip = ee32(device_ip); + + /* Call arp_recv with the ARP request */ + arp_recv(&s, &arp_req, sizeof(arp_req)); + ipstack_poll(&s, 1000); + ipstack_poll(&s, 1001); + ipstack_poll(&s, 1002); + + /* Check if ARP table updated with requester's MAC and IP */ + /* TODO */ + //ck_assert_int_eq(arp_lookup(&s, req_ip, mac), 0); + //ck_assert_mem_eq(mac, req_mac, 6); + + /* Check if an ARP reply was generated */ + arp_reply = (struct arp_packet *)last_frame_sent; + ck_assert_int_eq(last_frame_sent_size, sizeof(struct arp_packet)); + ck_assert_int_eq(arp_reply->opcode, ee16(ARP_REPLY)); + ck_assert_mem_eq(arp_reply->sma, s.ll_dev.mac, 6); // source MAC + ck_assert_int_eq(arp_reply->sip, ee32(device_ip)); // source IP + ck_assert_mem_eq(arp_reply->tma, req_mac, 6); // target MAC + ck_assert_int_eq(arp_reply->tip, ee32(req_ip)); // target IP +} +END_TEST + +START_TEST(test_arp_reply_handling) { + struct arp_packet arp_reply; + memset(&arp_reply, 0, sizeof(arp_reply)); + uint32_t reply_ip = 0xC0A80003; // 192.168.0.3 + uint8_t reply_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01}; + struct ipstack s; + ipstack_init(&s); + mock_link_init(&s); + + /* Prepare ARP reply */ + arp_reply.opcode = ee16(ARP_REPLY); + arp_reply.sip = ee32(reply_ip); + memcpy(arp_reply.sma, reply_mac, 6); + + /* Call arp_recv with the ARP reply */ + arp_recv(&s, &arp_reply, sizeof(arp_reply)); + + /* Check if ARP table updated with reply IP and MAC */ + ck_assert_int_eq(s.arp.neighbors[0].ip, reply_ip); + ck_assert_mem_eq(s.arp.neighbors[0].mac, reply_mac, 6); + + /* Update same IP with a different MAC address */ + uint8_t new_mac[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; + memcpy(arp_reply.sma, new_mac, 6); + arp_recv(&s, &arp_reply, sizeof(arp_reply)); + + /* Check if ARP table updates with new MAC */ + ck_assert_mem_eq(s.arp.neighbors[0].mac, new_mac, 6); +} +END_TEST + +START_TEST(test_arp_lookup_success) { + uint8_t found_mac[6]; + uint32_t ip = 0xC0A80002; + const uint8_t mock_mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x01}; + struct ipstack s; + ipstack_init(&s); + mock_link_init(&s); + + /* Add a known IP-MAC pair */ + s.arp.neighbors[0].ip = ip; + memcpy(s.arp.neighbors[0].mac, mock_mac, 6); + + /* Test arp_lookup */ + int result = arp_lookup(&s, ip, found_mac); + ck_assert_int_eq(result, 0); + ck_assert_mem_eq(found_mac, mock_mac, 6); +} +END_TEST + +START_TEST(test_arp_lookup_failure) { + uint8_t found_mac[6]; + uint32_t ip = 0xC0A80004; + struct ipstack s; + ipstack_init(&s); + mock_link_init(&s); + + /* Ensure arp_lookup fails for unknown IP */ + int result = arp_lookup(&s, ip, found_mac); + ck_assert_int_eq(result, -1); + uint8_t zero_mac[6] = {0, 0, 0, 0, 0, 0}; + ck_assert_mem_eq(found_mac, zero_mac, 6); +} +END_TEST + Suite *femto_suite(void) { Suite *s; - TCase *tc_core, *tc_proto; + TCase *tc_core, *tc_proto, *tc_utils; s = suite_create("FemtoTCP"); tc_core = tcase_create("Core"); + tc_utils = tcase_create("Utils"); tc_proto = tcase_create("Protocols"); + tcase_add_test(tc_core, test_fifo_init); suite_add_tcase(s, tc_core); + suite_add_tcase(s, tc_utils); suite_add_tcase(s, tc_proto); tcase_add_test(tc_core, test_fifo_push_and_pop); @@ -560,6 +639,15 @@ Suite *femto_suite(void) tcase_add_test(tc_core, test_queue_pop_wraparound); suite_add_tcase(s, tc_core); + tcase_add_test(tc_utils, test_insert_timer); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_pop_timer); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_is_timer_expired); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_utils, test_cancel_timer); + suite_add_tcase(s, tc_utils); + tcase_add_test(tc_proto, test_arp_request_basic); suite_add_tcase(s, tc_proto); tcase_add_test(tc_proto, test_arp_request_throttle);