summaryrefslogtreecommitdiff
path: root/test/unit/core
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2024-10-16 08:10:14 -0600
committerTom Rini <trini@konsulko.com>2024-10-16 08:10:14 -0600
commitf3f86fd1fe0fb288356bff78f8a6fa2edf89e3fc (patch)
treef0a99ea87d92f63895a6d053e3185838ebecf2d0 /test/unit/core
Squashed 'lib/lwip/lwip/' content from commit 0a0452b2c39b
git-subtree-dir: lib/lwip/lwip git-subtree-split: 0a0452b2c39bdd91e252aef045c115f88f6ca773
Diffstat (limited to 'test/unit/core')
-rw-r--r--test/unit/core/test_def.c84
-rw-r--r--test/unit/core/test_def.h8
-rw-r--r--test/unit/core/test_dns.c52
-rw-r--r--test/unit/core/test_dns.h8
-rw-r--r--test/unit/core/test_mem.c221
-rw-r--r--test/unit/core/test_mem.h8
-rw-r--r--test/unit/core/test_netif.c285
-rw-r--r--test/unit/core/test_netif.h8
-rw-r--r--test/unit/core/test_pbuf.c359
-rw-r--r--test/unit/core/test_pbuf.h8
-rw-r--r--test/unit/core/test_timers.c233
-rw-r--r--test/unit/core/test_timers.h8
12 files changed, 1282 insertions, 0 deletions
diff --git a/test/unit/core/test_def.c b/test/unit/core/test_def.c
new file mode 100644
index 00000000000..0ae2e9c1e49
--- /dev/null
+++ b/test/unit/core/test_def.c
@@ -0,0 +1,84 @@
+#include "test_def.h"
+
+#include "lwip/def.h"
+
+#define MAGIC_UNTOUCHED_BYTE 0x7a
+#define TEST_BUFSIZE 32
+#define GUARD_SIZE 4
+
+/* Setups/teardown functions */
+
+static void
+def_setup(void)
+{
+}
+
+static void
+def_teardown(void)
+{
+}
+
+static void
+def_check_range_untouched(const char *buf, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ fail_unless(buf[i] == (char)MAGIC_UNTOUCHED_BYTE);
+ }
+}
+
+static void test_def_itoa(int number, const char *expected)
+{
+ char buf[TEST_BUFSIZE];
+ char *test_buf = &buf[GUARD_SIZE];
+
+ size_t exp_len = strlen(expected);
+ fail_unless(exp_len + 4 < (TEST_BUFSIZE - (2 * GUARD_SIZE)));
+
+ memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
+ lwip_itoa(test_buf, exp_len + 1, number);
+ def_check_range_untouched(buf, GUARD_SIZE);
+ fail_unless(test_buf[exp_len] == 0);
+ fail_unless(!memcmp(test_buf, expected, exp_len));
+ def_check_range_untouched(&test_buf[exp_len + 1], TEST_BUFSIZE - GUARD_SIZE - exp_len - 1);
+
+ /* check with too small buffer */
+ memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
+ lwip_itoa(test_buf, exp_len, number);
+ def_check_range_untouched(buf, GUARD_SIZE);
+ def_check_range_untouched(&test_buf[exp_len + 1], TEST_BUFSIZE - GUARD_SIZE - exp_len - 1);
+
+ /* check with too large buffer */
+ memset(buf, MAGIC_UNTOUCHED_BYTE, sizeof(buf));
+ lwip_itoa(test_buf, exp_len + 4, number);
+ def_check_range_untouched(buf, GUARD_SIZE);
+ fail_unless(test_buf[exp_len] == 0);
+ fail_unless(!memcmp(test_buf, expected, exp_len));
+ def_check_range_untouched(&test_buf[exp_len + 4], TEST_BUFSIZE - GUARD_SIZE - exp_len - 4);
+}
+
+START_TEST(test_def_lwip_itoa)
+{
+ LWIP_UNUSED_ARG(_i);
+
+ test_def_itoa(0, "0");
+ test_def_itoa(1, "1");
+ test_def_itoa(-1, "-1");
+ test_def_itoa(15, "15");
+ test_def_itoa(-15, "-15");
+ test_def_itoa(156, "156");
+ test_def_itoa(1192, "1192");
+ test_def_itoa(-156, "-156");
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+def_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_def_lwip_itoa)
+ };
+ return create_suite("DEF", tests, sizeof(tests)/sizeof(testfunc), def_setup, def_teardown);
+}
diff --git a/test/unit/core/test_def.h b/test/unit/core/test_def.h
new file mode 100644
index 00000000000..7316051217e
--- /dev/null
+++ b/test/unit/core/test_def.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_DEF_H
+#define LWIP_HDR_TEST_DEF_H
+
+#include "../lwip_check.h"
+
+Suite *def_suite(void);
+
+#endif
diff --git a/test/unit/core/test_dns.c b/test/unit/core/test_dns.c
new file mode 100644
index 00000000000..6789d244109
--- /dev/null
+++ b/test/unit/core/test_dns.c
@@ -0,0 +1,52 @@
+#include "test_dns.h"
+
+#include "lwip/dns.h"
+
+/* Setups/teardown functions */
+
+static void
+dns_setup(void)
+{
+}
+
+static void
+dns_teardown(void)
+{
+}
+
+/* Test functions */
+
+START_TEST(test_dns_set_get_server)
+{
+ int n;
+ LWIP_UNUSED_ARG(_i);
+
+ for (n = 0; n < 256; n++) {
+ u8_t i = (u8_t)n;
+ ip_addr_t server;
+ /* Should return a zeroed address for any index */
+ fail_unless(dns_getserver(i));
+ fail_unless(ip_addr_isany(dns_getserver(i)));
+
+ /* Should accept setting address for any index, and ignore if out of range */
+ IP_ADDR4(&server, 10, 0, 0, i);
+ dns_setserver(i, &server);
+ fail_unless(dns_getserver(i));
+ if (i < DNS_MAX_SERVERS) {
+ fail_unless(ip_addr_eq(dns_getserver(i), &server) == 1);
+ } else {
+ fail_unless(ip_addr_isany(dns_getserver(i)));
+ }
+ }
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+dns_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_dns_set_get_server)
+ };
+ return create_suite("DNS", tests, sizeof(tests)/sizeof(testfunc), dns_setup, dns_teardown);
+}
diff --git a/test/unit/core/test_dns.h b/test/unit/core/test_dns.h
new file mode 100644
index 00000000000..eaad0ca01b7
--- /dev/null
+++ b/test/unit/core/test_dns.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_DNS_H
+#define LWIP_HDR_TEST_DNS_H
+
+#include "../lwip_check.h"
+
+Suite *dns_suite(void);
+
+#endif
diff --git a/test/unit/core/test_mem.c b/test/unit/core/test_mem.c
new file mode 100644
index 00000000000..601bfc7d485
--- /dev/null
+++ b/test/unit/core/test_mem.c
@@ -0,0 +1,221 @@
+#include "test_mem.h"
+
+#include "lwip/mem.h"
+#include "lwip/stats.h"
+
+#if !LWIP_STATS || !MEM_STATS
+#error "This tests needs MEM-statistics enabled"
+#endif
+
+/* Setups/teardown functions */
+
+static void
+mem_setup(void)
+{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+static void
+mem_teardown(void)
+{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+
+/* Test functions */
+
+/** Call mem_malloc, mem_free and mem_trim and check stats */
+START_TEST(test_mem_one)
+{
+#define SIZE1 16
+#define SIZE1_2 12
+#define SIZE2 16
+ void *p1, *p2;
+ mem_size_t s1, s2;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(lwip_stats.mem.used == 0);
+
+ p1 = mem_malloc(SIZE1);
+ fail_unless(p1 != NULL);
+ fail_unless(lwip_stats.mem.used >= SIZE1);
+ s1 = lwip_stats.mem.used;
+
+ p2 = mem_malloc(SIZE2);
+ fail_unless(p2 != NULL);
+ fail_unless(lwip_stats.mem.used >= SIZE2 + s1);
+ s2 = lwip_stats.mem.used;
+
+ mem_trim(p1, SIZE1_2);
+
+ mem_free(p2);
+ fail_unless(lwip_stats.mem.used <= s2 - SIZE2);
+
+ mem_free(p1);
+ fail_unless(lwip_stats.mem.used == 0);
+}
+END_TEST
+
+static void malloc_keep_x(int x, int num, int size, int freestep)
+{
+ int i;
+ void* p[16];
+ LWIP_ASSERT("invalid size", size >= 0 && size < (mem_size_t)-1);
+ memset(p, 0, sizeof(p));
+ for(i = 0; i < num && i < 16; i++) {
+ p[i] = mem_malloc((mem_size_t)size);
+ fail_unless(p[i] != NULL);
+ }
+ for(i = 0; i < num && i < 16; i += freestep) {
+ if (i == x) {
+ continue;
+ }
+ mem_free(p[i]);
+ p[i] = NULL;
+ }
+ for(i = 0; i < num && i < 16; i++) {
+ if (i == x) {
+ continue;
+ }
+ if (p[i] != NULL) {
+ mem_free(p[i]);
+ p[i] = NULL;
+ }
+ }
+ fail_unless(p[x] != NULL);
+ mem_free(p[x]);
+}
+
+START_TEST(test_mem_random)
+{
+ const int num = 16;
+ int x;
+ int size;
+ int freestep;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(lwip_stats.mem.used == 0);
+
+ for (x = 0; x < num; x++) {
+ for (size = 1; size < 32; size++) {
+ for (freestep = 1; freestep <= 3; freestep++) {
+ fail_unless(lwip_stats.mem.used == 0);
+ malloc_keep_x(x, num, size, freestep);
+ fail_unless(lwip_stats.mem.used == 0);
+ }
+ }
+ }
+}
+END_TEST
+
+START_TEST(test_mem_invalid_free)
+{
+ u8_t *ptr, *ptr_low, *ptr_high;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(lwip_stats.mem.used == 0);
+ fail_unless(lwip_stats.mem.illegal == 0);
+
+ ptr = (u8_t *)mem_malloc(1);
+ fail_unless(ptr != NULL);
+ fail_unless(lwip_stats.mem.used != 0);
+
+ ptr_low = ptr - 0x10;
+ mem_free(ptr_low);
+ fail_unless(lwip_stats.mem.illegal == 1);
+ lwip_stats.mem.illegal = 0;
+
+ ptr_high = ptr + (MEM_SIZE * 2);
+ mem_free(ptr_high);
+ fail_unless(lwip_stats.mem.illegal == 1);
+ lwip_stats.mem.illegal = 0;
+
+ mem_free(ptr);
+ fail_unless(lwip_stats.mem.illegal == 0);
+ fail_unless(lwip_stats.mem.used == 0);
+}
+END_TEST
+
+START_TEST(test_mem_double_free)
+{
+ u8_t *ptr1b, *ptr1, *ptr2, *ptr3;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(lwip_stats.mem.used == 0);
+ fail_unless(lwip_stats.mem.illegal == 0);
+
+ ptr1 = (u8_t *)mem_malloc(1);
+ fail_unless(ptr1 != NULL);
+ fail_unless(lwip_stats.mem.used != 0);
+
+ ptr2 = (u8_t *)mem_malloc(1);
+ fail_unless(ptr2 != NULL);
+ fail_unless(lwip_stats.mem.used != 0);
+
+ ptr3 = (u8_t *)mem_malloc(1);
+ fail_unless(ptr3 != NULL);
+ fail_unless(lwip_stats.mem.used != 0);
+
+ /* free the middle mem */
+ mem_free(ptr2);
+ fail_unless(lwip_stats.mem.illegal == 0);
+
+ /* double-free of middle mem: should fail */
+ mem_free(ptr2);
+ fail_unless(lwip_stats.mem.illegal == 1);
+ lwip_stats.mem.illegal = 0;
+
+ /* free upper memory and try again */
+ mem_free(ptr3);
+ fail_unless(lwip_stats.mem.illegal == 0);
+
+ mem_free(ptr2);
+ fail_unless(lwip_stats.mem.illegal == 1);
+ lwip_stats.mem.illegal = 0;
+
+ /* free lower memory and try again */
+ mem_free(ptr1);
+ fail_unless(lwip_stats.mem.illegal == 0);
+ fail_unless(lwip_stats.mem.used == 0);
+
+ mem_free(ptr2);
+ fail_unless(lwip_stats.mem.illegal == 1);
+ fail_unless(lwip_stats.mem.used == 0);
+ lwip_stats.mem.illegal = 0;
+
+ /* reallocate lowest memory, now overlapping already freed ptr2 */
+#ifndef MIN_SIZE
+#define MIN_SIZE 12
+#endif
+ ptr1b = (u8_t *)mem_malloc(MIN_SIZE * 2);
+ fail_unless(ptr1b != NULL);
+ fail_unless(lwip_stats.mem.used != 0);
+
+ mem_free(ptr2);
+ fail_unless(lwip_stats.mem.illegal == 1);
+ lwip_stats.mem.illegal = 0;
+
+ memset(ptr1b, 1, MIN_SIZE * 2);
+
+ mem_free(ptr2);
+ fail_unless(lwip_stats.mem.illegal == 1);
+ lwip_stats.mem.illegal = 0;
+
+ mem_free(ptr1b);
+ fail_unless(lwip_stats.mem.illegal == 0);
+ fail_unless(lwip_stats.mem.used == 0);
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+mem_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_mem_one),
+ TESTFUNC(test_mem_random),
+ TESTFUNC(test_mem_invalid_free),
+ TESTFUNC(test_mem_double_free)
+ };
+ return create_suite("MEM", tests, sizeof(tests)/sizeof(testfunc), mem_setup, mem_teardown);
+}
diff --git a/test/unit/core/test_mem.h b/test/unit/core/test_mem.h
new file mode 100644
index 00000000000..325134c30a5
--- /dev/null
+++ b/test/unit/core/test_mem.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_MEM_H
+#define LWIP_HDR_TEST_MEM_H
+
+#include "../lwip_check.h"
+
+Suite *mem_suite(void);
+
+#endif
diff --git a/test/unit/core/test_netif.c b/test/unit/core/test_netif.c
new file mode 100644
index 00000000000..a51a4792e59
--- /dev/null
+++ b/test/unit/core/test_netif.c
@@ -0,0 +1,285 @@
+#include "test_netif.h"
+
+#include "lwip/netif.h"
+#include "lwip/stats.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
+
+#if !LWIP_NETIF_EXT_STATUS_CALLBACK
+#error "This tests needs LWIP_NETIF_EXT_STATUS_CALLBACK enabled"
+#endif
+
+static struct netif net_test;
+
+
+/* Setups/teardown functions */
+
+static void
+netif_setup(void)
+{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+static void
+netif_teardown(void)
+{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+/* test helper functions */
+
+static err_t
+testif_tx_func(struct netif *netif, struct pbuf *p)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(p);
+ return ERR_OK;
+}
+
+static err_t
+testif_init(struct netif *netif)
+{
+ netif->name[0] = 'c';
+ netif->name[1] = 'h';
+ netif->output = etharp_output;
+ netif->linkoutput = testif_tx_func;
+ netif->mtu = 1500;
+ netif->hwaddr_len = 6;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6;
+
+ netif->hwaddr[0] = 0x02;
+ netif->hwaddr[1] = 0x03;
+ netif->hwaddr[2] = 0x04;
+ netif->hwaddr[3] = 0x05;
+ netif->hwaddr[4] = 0x06;
+ netif->hwaddr[5] = 0x07;
+
+ return ERR_OK;
+}
+
+#define MAX_NSC_REASON_IDX 10
+static netif_nsc_reason_t expected_reasons;
+static int callback_ctr;
+
+static int dummy_active;
+
+static void
+test_netif_ext_callback_dummy(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(reason);
+ LWIP_UNUSED_ARG(args);
+
+ fail_unless(dummy_active);
+}
+
+static void
+test_netif_ext_callback(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args)
+{
+ LWIP_UNUSED_ARG(args); /* @todo */
+ callback_ctr++;
+
+ fail_unless(netif == &net_test);
+
+ fail_unless(expected_reasons == reason);
+}
+
+/* Test functions */
+
+NETIF_DECLARE_EXT_CALLBACK(netif_callback_1)
+NETIF_DECLARE_EXT_CALLBACK(netif_callback_2)
+NETIF_DECLARE_EXT_CALLBACK(netif_callback_3)
+
+START_TEST(test_netif_extcallbacks)
+{
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
+ LWIP_UNUSED_ARG(_i);
+
+ IP4_ADDR(&addr, 0, 0, 0, 0);
+ IP4_ADDR(&netmask, 0, 0, 0, 0);
+ IP4_ADDR(&gw, 0, 0, 0, 0);
+
+ netif_add_ext_callback(&netif_callback_3, test_netif_ext_callback_dummy);
+ netif_add_ext_callback(&netif_callback_2, test_netif_ext_callback);
+ netif_add_ext_callback(&netif_callback_1, test_netif_ext_callback_dummy);
+
+ dummy_active = 1;
+
+ /* positive tests: check that single events come as expected */
+
+ expected_reasons = LWIP_NSC_NETIF_ADDED;
+ callback_ctr = 0;
+ netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ fail_unless(callback_ctr == 1);
+
+ expected_reasons = LWIP_NSC_LINK_CHANGED;
+ callback_ctr = 0;
+ netif_set_link_up(&net_test);
+ fail_unless(callback_ctr == 1);
+
+ expected_reasons = LWIP_NSC_STATUS_CHANGED;
+ callback_ctr = 0;
+ netif_set_up(&net_test);
+ fail_unless(callback_ctr == 1);
+
+ IP4_ADDR(&addr, 1, 2, 3, 4);
+ expected_reasons = LWIP_NSC_IPV4_ADDRESS_CHANGED;
+ callback_ctr = 0;
+ netif_set_ipaddr(&net_test, &addr);
+ fail_unless(callback_ctr == 1);
+
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ expected_reasons = LWIP_NSC_IPV4_NETMASK_CHANGED;
+ callback_ctr = 0;
+ netif_set_netmask(&net_test, &netmask);
+ fail_unless(callback_ctr == 1);
+
+ IP4_ADDR(&gw, 1, 2, 3, 254);
+ expected_reasons = LWIP_NSC_IPV4_GATEWAY_CHANGED;
+ callback_ctr = 0;
+ netif_set_gw(&net_test, &gw);
+ fail_unless(callback_ctr == 1);
+
+ IP4_ADDR(&addr, 0, 0, 0, 0);
+ expected_reasons = LWIP_NSC_IPV4_ADDRESS_CHANGED;
+ callback_ctr = 0;
+ netif_set_ipaddr(&net_test, &addr);
+ fail_unless(callback_ctr == 1);
+
+ IP4_ADDR(&netmask, 0, 0, 0, 0);
+ expected_reasons = LWIP_NSC_IPV4_NETMASK_CHANGED;
+ callback_ctr = 0;
+ netif_set_netmask(&net_test, &netmask);
+ fail_unless(callback_ctr == 1);
+
+ IP4_ADDR(&gw, 0, 0, 0, 0);
+ expected_reasons = LWIP_NSC_IPV4_GATEWAY_CHANGED;
+ callback_ctr = 0;
+ netif_set_gw(&net_test, &gw);
+ fail_unless(callback_ctr == 1);
+
+ /* check for multi-events (only one combined callback expected) */
+
+ IP4_ADDR(&addr, 1, 2, 3, 4);
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ IP4_ADDR(&gw, 1, 2, 3, 254);
+ expected_reasons = (netif_nsc_reason_t)(LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_NETMASK_CHANGED |
+ LWIP_NSC_IPV4_GATEWAY_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
+ LWIP_NSC_IPV4_ADDR_VALID);
+ callback_ctr = 0;
+ netif_set_addr(&net_test, &addr, &netmask, &gw);
+ fail_unless(callback_ctr == 1);
+
+ /* check that for no-change, no callback is expected */
+ expected_reasons = LWIP_NSC_NONE;
+ callback_ctr = 0;
+ netif_set_ipaddr(&net_test, &addr);
+ fail_unless(callback_ctr == 0);
+
+ netif_set_netmask(&net_test, &netmask);
+ callback_ctr = 0;
+ fail_unless(callback_ctr == 0);
+
+ callback_ctr = 0;
+ netif_set_gw(&net_test, &gw);
+ fail_unless(callback_ctr == 0);
+
+ /* netif_set_addr() always issues at least LWIP_NSC_IPV4_ADDR_VALID */
+ expected_reasons = LWIP_NSC_IPV4_ADDR_VALID;
+ callback_ctr = 0;
+ netif_set_addr(&net_test, &addr, &netmask, &gw);
+ fail_unless(callback_ctr == 1);
+
+ /* check for single-events */
+ IP4_ADDR(&addr, 1, 2, 3, 5);
+ expected_reasons = (netif_nsc_reason_t)(LWIP_NSC_IPV4_ADDRESS_CHANGED | LWIP_NSC_IPV4_SETTINGS_CHANGED |
+ LWIP_NSC_IPV4_ADDR_VALID);
+ callback_ctr = 0;
+ netif_set_addr(&net_test, &addr, &netmask, &gw);
+ fail_unless(callback_ctr == 1);
+
+ expected_reasons = LWIP_NSC_STATUS_CHANGED;
+ callback_ctr = 0;
+ netif_set_down(&net_test);
+ fail_unless(callback_ctr == 1);
+
+ expected_reasons = LWIP_NSC_NETIF_REMOVED;
+ callback_ctr = 0;
+ netif_remove(&net_test);
+ fail_unless(callback_ctr == 1);
+
+ expected_reasons = LWIP_NSC_NONE;
+
+ netif_remove_ext_callback(&netif_callback_2);
+ netif_remove_ext_callback(&netif_callback_3);
+ netif_remove_ext_callback(&netif_callback_1);
+ dummy_active = 0;
+}
+END_TEST
+
+START_TEST(test_netif_flag_set)
+{
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
+ LWIP_UNUSED_ARG(_i);
+
+ IP4_ADDR(&addr, 0, 0, 0, 0);
+ IP4_ADDR(&netmask, 0, 0, 0, 0);
+ IP4_ADDR(&gw, 0, 0, 0, 0);
+
+ netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+
+ fail_if(netif_is_flag_set(&net_test, NETIF_FLAG_UP));
+ fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_BROADCAST));
+ fail_if(netif_is_flag_set(&net_test, NETIF_FLAG_LINK_UP));
+ fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_ETHARP));
+ fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_ETHERNET));
+ fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_IGMP));
+ fail_unless(netif_is_flag_set(&net_test, NETIF_FLAG_MLD6));
+
+ netif_remove(&net_test);
+}
+END_TEST
+
+START_TEST(test_netif_find)
+{
+ struct netif net0;
+ struct netif net1;
+ LWIP_UNUSED_ARG(_i);
+
+ /* No netifs available */
+ fail_unless(netif_find("ch0") == NULL);
+
+ /* Add netifs with known names */
+ fail_unless(netif_add_noaddr(&net0, NULL, testif_init, ethernet_input) == &net0);
+ net0.num = 0;
+ fail_unless(netif_add_noaddr(&net1, NULL, testif_init, ethernet_input) == &net1);
+ net1.num = 1;
+
+ fail_unless(netif_find("ch0") == &net0);
+ fail_unless(netif_find("CH0") == NULL);
+ fail_unless(netif_find("ch1") == &net1);
+ fail_unless(netif_find("ch3") == NULL);
+ /* atoi failure is not treated as zero */
+ fail_unless(netif_find("chX") == NULL);
+ fail_unless(netif_find("ab0") == NULL);
+
+ netif_remove(&net0);
+ netif_remove(&net1);
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+netif_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_netif_extcallbacks),
+ TESTFUNC(test_netif_flag_set),
+ TESTFUNC(test_netif_find)
+ };
+ return create_suite("NETIF", tests, sizeof(tests)/sizeof(testfunc), netif_setup, netif_teardown);
+}
diff --git a/test/unit/core/test_netif.h b/test/unit/core/test_netif.h
new file mode 100644
index 00000000000..8f2b6b46852
--- /dev/null
+++ b/test/unit/core/test_netif.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_NETIF_H
+#define LWIP_HDR_TEST_NETIF_H
+
+#include "../lwip_check.h"
+
+Suite *netif_suite(void);
+
+#endif
diff --git a/test/unit/core/test_pbuf.c b/test/unit/core/test_pbuf.c
new file mode 100644
index 00000000000..6163e4fabef
--- /dev/null
+++ b/test/unit/core/test_pbuf.c
@@ -0,0 +1,359 @@
+#include "test_pbuf.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+
+#if !LWIP_STATS || !MEM_STATS ||!MEMP_STATS
+#error "This tests needs MEM- and MEMP-statistics enabled"
+#endif
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !LWIP_WND_SCALE
+#error "This test needs TCP OOSEQ queueing and window scaling enabled"
+#endif
+
+/* Setups/teardown functions */
+
+static void
+pbuf_setup(void)
+{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+static void
+pbuf_teardown(void)
+{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+
+#define TESTBUFSIZE_1 65535
+#define TESTBUFSIZE_2 65530
+#define TESTBUFSIZE_3 50050
+static u8_t testbuf_1[TESTBUFSIZE_1];
+static u8_t testbuf_1a[TESTBUFSIZE_1];
+static u8_t testbuf_2[TESTBUFSIZE_2];
+static u8_t testbuf_2a[TESTBUFSIZE_2];
+static u8_t testbuf_3[TESTBUFSIZE_3];
+static u8_t testbuf_3a[TESTBUFSIZE_3];
+
+/* Test functions */
+START_TEST(test_pbuf_alloc_zero_pbufs)
+{
+ struct pbuf *p;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_ROM);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_RAM);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+}
+END_TEST
+
+/** Call pbuf_copy on a pbuf with zero length */
+START_TEST(test_pbuf_copy_zero_pbuf)
+{
+ struct pbuf *p1, *p2, *p3;
+ err_t err;
+ LWIP_UNUSED_ARG(_i);
+
+ p1 = pbuf_alloc(PBUF_RAW, 1024, PBUF_RAM);
+ fail_unless(p1 != NULL);
+ fail_unless(p1->ref == 1);
+
+ p2 = pbuf_alloc(PBUF_RAW, 2, PBUF_POOL);
+ fail_unless(p2 != NULL);
+ fail_unless(p2->ref == 1);
+ p2->len = p2->tot_len = 0;
+
+ pbuf_cat(p1, p2);
+ fail_unless(p1->ref == 1);
+ fail_unless(p2->ref == 1);
+
+ p3 = pbuf_alloc(PBUF_RAW, p1->tot_len, PBUF_POOL);
+ err = pbuf_copy(p3, p1);
+ fail_unless(err == ERR_VAL);
+
+ pbuf_free(p1);
+ pbuf_free(p3);
+}
+END_TEST
+
+/** Call pbuf_copy on pbufs with chains of different sizes */
+START_TEST(test_pbuf_copy_unmatched_chains)
+{
+ uint16_t i, j;
+ err_t err;
+ struct pbuf *source, *dest, *p;
+ LWIP_UNUSED_ARG(_i);
+
+ source = NULL;
+ /* Build source pbuf from linked 16 byte parts,
+ * with payload bytes containing their offset */
+ for (i = 0; i < 8; i++) {
+ p = pbuf_alloc(PBUF_RAW, 16, PBUF_RAM);
+ fail_unless(p != NULL);
+ for (j = 0; j < p->len; j++) {
+ ((u8_t*)p->payload)[j] = (u8_t)((i << 4) | j);
+ }
+ if (source) {
+ pbuf_cat(source, p);
+ } else {
+ source = p;
+ }
+ }
+ for (i = 0; i < source->tot_len; i++) {
+ fail_unless(pbuf_get_at(source, i) == i);
+ }
+
+ /* Build dest pbuf from other lengths */
+ dest = pbuf_alloc(PBUF_RAW, 35, PBUF_RAM);
+ fail_unless(dest != NULL);
+ p = pbuf_alloc(PBUF_RAW, 81, PBUF_RAM);
+ fail_unless(p != NULL);
+ pbuf_cat(dest, p);
+ p = pbuf_alloc(PBUF_RAW, 27, PBUF_RAM);
+ fail_unless(p != NULL);
+ pbuf_cat(dest, p);
+
+ /* Copy contents and verify data */
+ err = pbuf_copy(dest, source);
+ fail_unless(err == ERR_OK);
+ for (i = 0; i < source->tot_len; i++) {
+ fail_unless(pbuf_get_at(dest, i) == i);
+ }
+
+ pbuf_free(source);
+ pbuf_free(dest);
+}
+END_TEST
+
+START_TEST(test_pbuf_copy_partial_pbuf)
+{
+ struct pbuf *a, *b, *dest;
+ char lwip[] = "lwip ";
+ char packet[] = "packet";
+ err_t err;
+ LWIP_UNUSED_ARG(_i);
+
+ a = pbuf_alloc(PBUF_RAW, 5, PBUF_REF);
+ fail_unless(a != NULL);
+ a->payload = lwip;
+ b = pbuf_alloc(PBUF_RAW, 7, PBUF_REF);
+ fail_unless(b != NULL);
+ b->payload = packet;
+ pbuf_cat(a, b);
+ dest = pbuf_alloc(PBUF_RAW, 14, PBUF_RAM);
+ memset(dest->payload, 0, dest->len);
+ fail_unless(dest != NULL);
+
+ /* Don't copy if data will not fit */
+ err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 4);
+ fail_unless(err == ERR_ARG);
+ /* Don't copy if length is longer than source */
+ err = pbuf_copy_partial_pbuf(dest, a, a->tot_len + 1, 0);
+ fail_unless(err == ERR_ARG);
+ /* Normal copy */
+ err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 0);
+ fail_unless(err == ERR_OK);
+ fail_unless(strcmp("lwip packet", (char*)dest->payload) == 0);
+ /* Copy at offset */
+ err = pbuf_copy_partial_pbuf(dest, a, a->tot_len, 1);
+ fail_unless(err == ERR_OK);
+ fail_unless(strcmp("llwip packet", (char*)dest->payload) == 0);
+ /* Copy at offset with shorter length */
+ err = pbuf_copy_partial_pbuf(dest, a, 6, 6);
+ fail_unless(err == ERR_OK);
+ fail_unless(strcmp("llwip lwip p", (char*)dest->payload) == 0);
+ /* Copy with shorter length */
+ err = pbuf_copy_partial_pbuf(dest, a, 5, 0);
+ fail_unless(err == ERR_OK);
+ fail_unless(strcmp("lwip lwip p", (char*)dest->payload) == 0);
+
+ pbuf_free(dest);
+ pbuf_free(a);
+}
+END_TEST
+
+START_TEST(test_pbuf_split_64k_on_small_pbufs)
+{
+ struct pbuf *p, *rest=NULL;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, 1, PBUF_POOL);
+ pbuf_split_64k(p, &rest);
+ fail_unless(p->tot_len == 1);
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(test_pbuf_queueing_bigger_than_64k)
+{
+ int i;
+ err_t err;
+ struct pbuf *p1, *p2, *p3, *rest2=NULL, *rest3=NULL;
+ LWIP_UNUSED_ARG(_i);
+
+ for(i = 0; i < TESTBUFSIZE_1; i++) {
+ testbuf_1[i] = (u8_t)rand();
+ }
+ for(i = 0; i < TESTBUFSIZE_2; i++) {
+ testbuf_2[i] = (u8_t)rand();
+ }
+ for(i = 0; i < TESTBUFSIZE_3; i++) {
+ testbuf_3[i] = (u8_t)rand();
+ }
+
+ p1 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_1, PBUF_POOL);
+ fail_unless(p1 != NULL);
+ p2 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_2, PBUF_POOL);
+ fail_unless(p2 != NULL);
+ p3 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_3, PBUF_POOL);
+ fail_unless(p3 != NULL);
+ err = pbuf_take(p1, testbuf_1, TESTBUFSIZE_1);
+ fail_unless(err == ERR_OK);
+ err = pbuf_take(p2, testbuf_2, TESTBUFSIZE_2);
+ fail_unless(err == ERR_OK);
+ err = pbuf_take(p3, testbuf_3, TESTBUFSIZE_3);
+ fail_unless(err == ERR_OK);
+
+ pbuf_cat(p1, p2);
+ pbuf_cat(p1, p3);
+
+ pbuf_split_64k(p1, &rest2);
+ fail_unless(p1->tot_len == TESTBUFSIZE_1);
+ fail_unless(rest2->tot_len == (u16_t)((TESTBUFSIZE_2+TESTBUFSIZE_3) & 0xFFFF));
+ pbuf_split_64k(rest2, &rest3);
+ fail_unless(rest2->tot_len == TESTBUFSIZE_2);
+ fail_unless(rest3->tot_len == TESTBUFSIZE_3);
+
+ pbuf_copy_partial(p1, testbuf_1a, TESTBUFSIZE_1, 0);
+ pbuf_copy_partial(rest2, testbuf_2a, TESTBUFSIZE_2, 0);
+ pbuf_copy_partial(rest3, testbuf_3a, TESTBUFSIZE_3, 0);
+ fail_if(memcmp(testbuf_1, testbuf_1a, TESTBUFSIZE_1));
+ fail_if(memcmp(testbuf_2, testbuf_2a, TESTBUFSIZE_2));
+ fail_if(memcmp(testbuf_3, testbuf_3a, TESTBUFSIZE_3));
+
+ pbuf_free(p1);
+ pbuf_free(rest2);
+ pbuf_free(rest3);
+}
+END_TEST
+
+/* Test for bug that writing with pbuf_take_at() did nothing
+ * and returned ERR_OK when writing at beginning of a pbuf
+ * in the chain.
+ */
+START_TEST(test_pbuf_take_at_edge)
+{
+ err_t res;
+ u8_t *out;
+ int i;
+ u8_t testdata[] = { 0x01, 0x08, 0x82, 0x02 };
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
+ struct pbuf *q = p->next;
+ LWIP_UNUSED_ARG(_i);
+ /* alloc big enough to get a chain of pbufs */
+ fail_if(p->tot_len == p->len);
+ memset(p->payload, 0, p->len);
+ memset(q->payload, 0, q->len);
+
+ /* copy data to the beginning of first pbuf */
+ res = pbuf_take_at(p, &testdata, sizeof(testdata), 0);
+ fail_unless(res == ERR_OK);
+
+ out = (u8_t*)p->payload;
+ for (i = 0; i < (int)sizeof(testdata); i++) {
+ fail_unless(out[i] == testdata[i],
+ "Bad data at pos %d, was %02X, expected %02X", i, out[i], testdata[i]);
+ }
+
+ /* copy data to the just before end of first pbuf */
+ res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len - 1);
+ fail_unless(res == ERR_OK);
+
+ out = (u8_t*)p->payload;
+ fail_unless(out[p->len - 1] == testdata[0],
+ "Bad data at pos %d, was %02X, expected %02X", p->len - 1, out[p->len - 1], testdata[0]);
+ out = (u8_t*)q->payload;
+ for (i = 1; i < (int)sizeof(testdata); i++) {
+ fail_unless(out[i-1] == testdata[i],
+ "Bad data at pos %d, was %02X, expected %02X", p->len - 1 + i, out[i-1], testdata[i]);
+ }
+
+ /* copy data to the beginning of second pbuf */
+ res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len);
+ fail_unless(res == ERR_OK);
+
+ out = (u8_t*)p->payload;
+ for (i = 0; i < (int)sizeof(testdata); i++) {
+ fail_unless(out[i] == testdata[i],
+ "Bad data at pos %d, was %02X, expected %02X", p->len+i, out[i], testdata[i]);
+ }
+ pbuf_free(p);
+}
+END_TEST
+
+/* Verify pbuf_put_at()/pbuf_get_at() when using
+ * offsets equal to beginning of new pbuf in chain
+ */
+START_TEST(test_pbuf_get_put_at_edge)
+{
+ u8_t *out;
+ u8_t testdata = 0x01;
+ u8_t getdata;
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
+ struct pbuf *q = p->next;
+ LWIP_UNUSED_ARG(_i);
+ /* alloc big enough to get a chain of pbufs */
+ fail_if(p->tot_len == p->len);
+ memset(p->payload, 0, p->len);
+ memset(q->payload, 0, q->len);
+
+ /* put byte at the beginning of second pbuf */
+ pbuf_put_at(p, p->len, testdata);
+
+ out = (u8_t*)q->payload;
+ fail_unless(*out == testdata,
+ "Bad data at pos %d, was %02X, expected %02X", p->len, *out, testdata);
+
+ getdata = pbuf_get_at(p, p->len);
+ fail_unless(*out == getdata,
+ "pbuf_get_at() returned bad data at pos %d, was %02X, expected %02X", p->len, getdata, *out);
+ pbuf_free(p);
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+pbuf_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_pbuf_alloc_zero_pbufs),
+ TESTFUNC(test_pbuf_copy_zero_pbuf),
+ TESTFUNC(test_pbuf_copy_unmatched_chains),
+ TESTFUNC(test_pbuf_copy_partial_pbuf),
+ TESTFUNC(test_pbuf_split_64k_on_small_pbufs),
+ TESTFUNC(test_pbuf_queueing_bigger_than_64k),
+ TESTFUNC(test_pbuf_take_at_edge),
+ TESTFUNC(test_pbuf_get_put_at_edge)
+ };
+ return create_suite("PBUF", tests, sizeof(tests)/sizeof(testfunc), pbuf_setup, pbuf_teardown);
+}
diff --git a/test/unit/core/test_pbuf.h b/test/unit/core/test_pbuf.h
new file mode 100644
index 00000000000..da7730a4c85
--- /dev/null
+++ b/test/unit/core/test_pbuf.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_PBUF_H
+#define LWIP_HDR_TEST_PBUF_H
+
+#include "../lwip_check.h"
+
+Suite *pbuf_suite(void);
+
+#endif
diff --git a/test/unit/core/test_timers.c b/test/unit/core/test_timers.c
new file mode 100644
index 00000000000..820b718924f
--- /dev/null
+++ b/test/unit/core/test_timers.c
@@ -0,0 +1,233 @@
+#include "test_timers.h"
+
+#include "lwip/def.h"
+#include "lwip/timeouts.h"
+#include "arch/sys_arch.h"
+
+/* Setups/teardown functions */
+
+static struct sys_timeo* old_list_head;
+
+static void
+timers_setup(void)
+{
+ struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
+ old_list_head = *list_head;
+ *list_head = NULL;
+}
+
+static void
+timers_teardown(void)
+{
+ struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
+ *list_head = old_list_head;
+ lwip_sys_now = 0;
+}
+
+static int fired[3];
+static void
+dummy_handler(void* arg)
+{
+ int index = LWIP_PTR_NUMERIC_CAST(int, arg);
+ fired[index] = 1;
+}
+
+#define HANDLER_EXECUTION_TIME 5
+static int cyclic_fired;
+static void
+dummy_cyclic_handler(void)
+{
+ cyclic_fired = 1;
+ lwip_sys_now += HANDLER_EXECUTION_TIME;
+}
+
+struct lwip_cyclic_timer test_cyclic = {10, dummy_cyclic_handler};
+
+static void
+do_test_cyclic_timers(u32_t offset)
+{
+ struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
+
+ /* verify normal timer expiration */
+ lwip_sys_now = offset + 0;
+ sys_timeout(test_cyclic.interval_ms, lwip_cyclic_timer, &test_cyclic);
+
+ cyclic_fired = 0;
+ sys_check_timeouts();
+ fail_unless(cyclic_fired == 0);
+
+ lwip_sys_now = offset + test_cyclic.interval_ms;
+ sys_check_timeouts();
+ fail_unless(cyclic_fired == 1);
+
+ fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + test_cyclic.interval_ms - HANDLER_EXECUTION_TIME));
+
+ sys_untimeout(lwip_cyclic_timer, &test_cyclic);
+
+
+ /* verify "overload" - next cyclic timer execution is already overdue twice */
+ lwip_sys_now = offset + 0;
+ sys_timeout(test_cyclic.interval_ms, lwip_cyclic_timer, &test_cyclic);
+
+ cyclic_fired = 0;
+ sys_check_timeouts();
+ fail_unless(cyclic_fired == 0);
+
+ lwip_sys_now = offset + 2*test_cyclic.interval_ms;
+ sys_check_timeouts();
+ fail_unless(cyclic_fired == 1);
+
+ fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + test_cyclic.interval_ms));
+}
+
+START_TEST(test_cyclic_timers)
+{
+ LWIP_UNUSED_ARG(_i);
+
+ /* check without u32_t wraparound */
+ do_test_cyclic_timers(0);
+
+ /* check with u32_t wraparound */
+ do_test_cyclic_timers(0xfffffff0);
+}
+END_TEST
+
+/* reproduce bug #52748: the bug in timeouts.c */
+START_TEST(test_bug52748)
+{
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&fired, 0, sizeof(fired));
+
+ lwip_sys_now = 50;
+ sys_timeout(20, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
+ sys_timeout( 5, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
+
+ lwip_sys_now = 55;
+ sys_check_timeouts();
+ fail_unless(fired[0] == 0);
+ fail_unless(fired[1] == 0);
+ fail_unless(fired[2] == 1);
+
+ lwip_sys_now = 60;
+ sys_timeout(10, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
+ sys_check_timeouts();
+ fail_unless(fired[0] == 0);
+ fail_unless(fired[1] == 0);
+ fail_unless(fired[2] == 1);
+
+ lwip_sys_now = 70;
+ sys_check_timeouts();
+ fail_unless(fired[0] == 1);
+ fail_unless(fired[1] == 1);
+ fail_unless(fired[2] == 1);
+}
+END_TEST
+
+static void
+do_test_timers(u32_t offset)
+{
+ struct sys_timeo** list_head = sys_timeouts_get_next_timeout();
+
+ lwip_sys_now = offset + 0;
+
+ sys_timeout(10, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
+ fail_unless(sys_timeouts_sleeptime() == 10);
+ sys_timeout(20, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
+ fail_unless(sys_timeouts_sleeptime() == 10);
+ sys_timeout( 5, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
+ fail_unless(sys_timeouts_sleeptime() == 5);
+
+ /* linked list correctly sorted? */
+ fail_unless((*list_head)->time == (u32_t)(lwip_sys_now + 5));
+ fail_unless((*list_head)->next->time == (u32_t)(lwip_sys_now + 10));
+ fail_unless((*list_head)->next->next->time == (u32_t)(lwip_sys_now + 20));
+
+ /* check timers expire in correct order */
+ memset(&fired, 0, sizeof(fired));
+
+ lwip_sys_now += 4;
+ sys_check_timeouts();
+ fail_unless(fired[2] == 0);
+
+ lwip_sys_now += 1;
+ sys_check_timeouts();
+ fail_unless(fired[2] == 1);
+
+ lwip_sys_now += 4;
+ sys_check_timeouts();
+ fail_unless(fired[0] == 0);
+
+ lwip_sys_now += 1;
+ sys_check_timeouts();
+ fail_unless(fired[0] == 1);
+
+ lwip_sys_now += 9;
+ sys_check_timeouts();
+ fail_unless(fired[1] == 0);
+
+ lwip_sys_now += 1;
+ sys_check_timeouts();
+ fail_unless(fired[1] == 1);
+
+ sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
+ sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 1));
+ sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 2));
+}
+
+START_TEST(test_timers)
+{
+ LWIP_UNUSED_ARG(_i);
+
+ /* check without u32_t wraparound */
+ do_test_timers(0);
+
+ /* check with u32_t wraparound */
+ do_test_timers(0xfffffff0);
+}
+END_TEST
+
+START_TEST(test_long_timer)
+{
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&fired, 0, sizeof(fired));
+ lwip_sys_now = 0;
+
+ sys_timeout(LWIP_UINT32_MAX / 4, dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
+ fail_unless(sys_timeouts_sleeptime() == LWIP_UINT32_MAX / 4);
+
+ sys_check_timeouts();
+ fail_unless(fired[0] == 0);
+
+ lwip_sys_now += LWIP_UINT32_MAX / 8;
+
+ sys_check_timeouts();
+ fail_unless(fired[0] == 0);
+
+ lwip_sys_now += LWIP_UINT32_MAX / 8;
+
+ sys_check_timeouts();
+ fail_unless(fired[0] == 0);
+
+ lwip_sys_now += 1;
+
+ sys_check_timeouts();
+ fail_unless(fired[0] == 1);
+
+ sys_untimeout(dummy_handler, LWIP_PTR_NUMERIC_CAST(void*, 0));
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+timers_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_bug52748),
+ TESTFUNC(test_cyclic_timers),
+ TESTFUNC(test_timers),
+ TESTFUNC(test_long_timer),
+ };
+ return create_suite("TIMERS", tests, LWIP_ARRAYSIZE(tests), timers_setup, timers_teardown);
+}
diff --git a/test/unit/core/test_timers.h b/test/unit/core/test_timers.h
new file mode 100644
index 00000000000..b16ab75bf0f
--- /dev/null
+++ b/test/unit/core/test_timers.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_TIMERS_H
+#define LWIP_HDR_TEST_TIMERS_H
+
+#include "../lwip_check.h"
+
+Suite *timers_suite(void);
+
+#endif