diff --git a/.github/utest/netdev/netdev.cfg b/.github/utest/netdev/netdev.cfg new file mode 100644 index 00000000000..d09e790ac41 --- /dev/null +++ b/.github/utest/netdev/netdev.cfg @@ -0,0 +1,11 @@ +# dependencies +CONFIG_RT_CONSOLEBUF_SIZE=1024 +CONFIG_RT_NAME_MAX=24 +CONFIG_RT_USING_CI_ACTION=y +# CONFIG_RT_USING_LWIP203=y +CONFIG_RT_USING_LWIP212=y + +CONFIG_RT_UTEST_TC_USING_NETDEV=y +CONFIG_RT_UTEST_DEFAULT_NETDEV_NAME="e0" + +CONFIG_BSP_DRV_EMAC=y \ No newline at end of file diff --git a/.github/workflows/utest_auto_run.yml b/.github/workflows/utest_auto_run.yml index 2a102e4bbe6..6fa2fce9f6e 100644 --- a/.github/workflows/utest_auto_run.yml +++ b/.github/workflows/utest_auto_run.yml @@ -61,6 +61,9 @@ jobs: - platform: { UTEST: "A9", RTT_BSP: "bsp/qemu-vexpress-a9", QEMU_ARCH: "arm", QEMU_MACHINE: "vexpress-a9", SD_FILE: "sd.bin", KERNEL: "standard", "SMP_RUN":"" } config_file: "lwip/lwip.cfg" + - platform: { UTEST: "A9", RTT_BSP: "bsp/qemu-vexpress-a9", QEMU_ARCH: "arm", QEMU_MACHINE: "vexpress-a9", SD_FILE: "sd.bin", KERNEL: "standard", "SMP_RUN":"" } + config_file: "netdev/netdev.cfg" + env: TEST_QEMU_ARCH: ${{ matrix.platform.QEMU_ARCH }} TEST_QEMU_MACHINE: ${{ matrix.platform.QEMU_MACHINE }} diff --git a/components/net/utest/Kconfig b/components/net/utest/Kconfig index 7d79b5dfd6f..55dbcff906a 100644 --- a/components/net/utest/Kconfig +++ b/components/net/utest/Kconfig @@ -105,3 +105,33 @@ if RT_USING_LWIP endif endmenu endif + +if RT_USING_NETDEV + menu "Netdev Network Unit Testcase" + + config RT_UTEST_TC_USING_NETDEV + bool "netdev api test" + help + Enable netdev network device framework unit tests. + + Test coverage includes: + * Network connectivity (ping operations) + * DHCP functionality (enable/disable/restore) + * DNS configuration and hostname resolution + * Interface configuration (IP/gateway/netmask) + * IP address conversion functions + * Device retrieval and management + * Status control and callback mechanisms + + if RT_UTEST_TC_USING_NETDEV + + config RT_UTEST_DEFAULT_NETDEV_NAME + string "Default netdev name" + default "e0" + help + Network interface name for tests. Common values: + "e0" (Ethernet), "w0" (Wireless). + Ensure the device exists in your environment. + endif + endmenu +endif diff --git a/components/net/utest/SConscript b/components/net/utest/SConscript index e941348afcb..010ccb4e5f4 100644 --- a/components/net/utest/SConscript +++ b/components/net/utest/SConscript @@ -5,11 +5,17 @@ cwd = GetCurrentDir() src = [] CPPPATH = [cwd] -if GetDepend('RT_UTEST_TC_USING_LWIP'): - # Add lwIP test source if enabled - src += ['tc_lwip.c'] +if GetDepend('RT_UTEST_USING_ALL_CASES') or GetDepend('RT_UTEST_TC_USING_LWIP') or GetDepend('RT_UTEST_TC_USING_NETDEV'): + + if GetDepend('RT_UTEST_TC_USING_LWIP'): + # Add lwIP test source if enabled + src += ['tc_lwip.c'] + + if GetDepend('RT_UTEST_TC_USING_NETDEV'): + # Add netdev test source if enabled + src += ['tc_netdev.c'] # Define the test group with proper dependencies -group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_LWIP'], CPPPATH = CPPPATH) +group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH) Return('group') diff --git a/components/net/utest/tc_netdev.c b/components/net/utest/tc_netdev.c new file mode 100644 index 00000000000..e416e75f5fd --- /dev/null +++ b/components/net/utest/tc_netdev.c @@ -0,0 +1,1144 @@ +/* + * Copyright (c) 2006-2025, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2025-09-12 Rbb666 the first version + */ + +#include + +#include "utest.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Callback event synchronization timeout (milliseconds) */ +#define CALLBACK_EVENT_TIMEOUT 1000 + +/** @brief Save original default network device pointer for restoration after testing */ +static struct netdev *netdev_default_old = RT_NULL; + +/* Save initial network configuration for network state restoration after testing */ +static ip_addr_t initial_ip_addr; /**< Initial IP address */ +static ip_addr_t initial_netmask; /**< Initial subnet mask */ +static ip_addr_t initial_gw; /**< Initial gateway address */ +static ip_addr_t initial_dns0; /**< Initial primary DNS server */ +static ip_addr_t initial_dns1; /**< Initial secondary DNS server */ +static rt_bool_t initial_dhcp_enabled; /**< Initial DHCP enable status */ + +/** + * @brief Execute multiple ping tests and calculate success rate + * + * This function performs multiple ping operations to a specified host and determines + * test results based on success rate. The test passes if success rate exceeds 80%. + * + * @param netdev Network device pointer + * @param host Target host address (IP address or domain name) + * @param n Number of ping test attempts + * @return rt_err_t RT_EOK-test passed (success rate>=80%), -RT_ERROR-test failed + * + * @note This function uses 32-byte data packets with a 5-second timeout + */ +rt_err_t multiple_ping_test(struct netdev *netdev, const char *host, rt_uint32_t n) +{ +#define UTEST_PING_DATA_LEN 32 /* Ping data packet size */ +#define UTEST_PING_TIMEOUT (5 * RT_TICK_PER_SECOND) /* Ping timeout: 5 seconds */ + + rt_uint32_t success_num = 0, i; + rt_err_t res = RT_EOK; + struct netdev_ping_resp ping_resp; + + /* Execute ping operations n times */ + for (i = 0; i < n; i++) + { + res = netdev->ops->ping(netdev, host, UTEST_PING_DATA_LEN, UTEST_PING_TIMEOUT, &ping_resp, RT_FALSE); + if (res == RT_EOK) + { + success_num++; + } + } + + /* Test passes if success rate is more than 80 percent */ + if (success_num >= ceil(0.8 * n)) + return RT_EOK; + else + return -RT_ERROR; +} + +/** + * @brief Test network connectivity using ping operations + * + * This function tests network connectivity by pinging various targets: + * - Gateway address (should succeed) + * - Invalid internal IP address (should fail) + * - External IP address (should succeed) + * - Invalid external IP address (should fail) + * - External URL (should succeed) + * - Invalid external URL (should fail) + * + * @note This test verifies both successful and failed ping scenarios + */ +static void test_netdev_ping(void) +{ +#define UTEST_INTRANET_WRONG_IP_ADDR "192.256.0.321" /* Invalid IP format */ +#define UTEST_EXTERNAL_IP_ADDR "8.8.8.8" /* Valid external IP */ +#define UTEST_EXTERNAL_WRONG_IP_ADDR "123.456.789.012" /* Invalid IP format */ +#define UTEST_EXTERNAL_URL "www.baidu.com" /* Valid external URL */ +#define UTEST_EXTERNAL_WRONG_URL "www.abcsdd.com" /* Non-existent URL */ + + rt_err_t res = RT_EOK; + + /* Test ping to gateway address - should succeed */ + res = multiple_ping_test(netdev_default, inet_ntoa(netdev_default->gw), 10); + uassert_true(res == RT_EOK); + + /* Test ping to invalid internal IP - should fail */ + res = multiple_ping_test(netdev_default, UTEST_INTRANET_WRONG_IP_ADDR, 1); + uassert_false(res == RT_EOK); + + /* Test ping to external IP address - should succeed */ + res = multiple_ping_test(netdev_default, UTEST_EXTERNAL_IP_ADDR, 10); + uassert_true(res == RT_EOK); + + /* Test ping to invalid external IP - should fail */ + res = multiple_ping_test(netdev_default, UTEST_EXTERNAL_WRONG_IP_ADDR, 1); + uassert_false(res == RT_EOK); + + /* Test ping to external URL - should succeed */ + res = multiple_ping_test(netdev_default, UTEST_EXTERNAL_URL, 10); + uassert_true(res == RT_EOK); + + /* Test ping to invalid external URL - should fail */ + res = multiple_ping_test(netdev_default, UTEST_EXTERNAL_WRONG_URL, 1); + uassert_false(res == RT_EOK); +} + +/** + * @brief Test network interface configuration and status + * + * This function tests: + * - Network interface status (up/link up) + * - IP address configuration validity + * - Gateway configuration validity + * - Subnet mask configuration validity + * - DNS server configuration validity + * + * @note Validates that the network interface is properly configured and operational + */ +static void test_netdev_ifconfig(void) +{ + /* Perform network interface status validation */ + uassert_true(netdev_is_up(netdev_default) && netdev_is_link_up(netdev_default)); /* Interface should be up and linked */ + uassert_false(ip_addr_isany(&netdev_default->ip_addr)); /* IP address should be assigned */ + uassert_false(ip_addr_isany(&netdev_default->gw)); /* Gateway should be configured */ + uassert_false(ip_addr_isany(&netdev_default->netmask)); /* Subnet mask should be configured */ + uassert_false(ip_addr_isany(&netdev_default->dns_servers[0])); /* Primary DNS should be configured */ +} + +/** + * @brief Test network statistics functionality + * + * This function tests the netstat operation capability of the network device. + * It verifies that the netstat function can be called without crashing. + * + * @note This is a basic functionality test to ensure netstat operation is available + */ +static void test_netdev_netstat(void) +{ + /* Simply test that netstat function can be called without crashing */ + if (netdev_default->ops->netstat) + { + netdev_default->ops->netstat(netdev_default); + uassert_true(RT_TRUE); /* If we reach here, the function call succeeded */ + } + else + { + uassert_true(RT_FALSE); /* netstat function not available */ + } +} + +/** + * @brief Test DNS server configuration and hostname resolution + * + * This function tests: + * - Setting incorrect DNS server and verifying hostname resolution fails + * - Setting correct DNS server and verifying hostname resolution succeeds + * - DNS server configuration operations + * + * @note DNS configuration will be restored in utest_tc_cleanup + */ +static void test_netdev_dns(void) +{ +#define UTEST_DNS "114.114.114.114" /* Valid DNS server */ +#define UTEST_WRONG_DNS "13.19.123.143" /* Invalid DNS server */ +#define UTEST_WRONG_HOST_NAME "www.abcde.com" /* Test hostname */ +#define UTEST_HOST_ADDR "www.rt-thread.org" /* RT-Thread official website */ + + ip_addr_t ipaddr = {0}; + struct hostent *res = RT_NULL; + struct sal_proto_family *netdev_inet = RT_NULL; + + /* Get SAL protocol family for DNS operations */ + netdev_inet = netdev_default->sal_user_data; + if (netdev_inet == RT_NULL) + { + rt_kprintf("get sal proto family fail!"); + uassert_true(RT_FALSE); + return; + } + + /* Test with wrong DNS server - hostname resolution should fail */ + inet_aton(UTEST_WRONG_DNS, &ipaddr); + netdev_default->ops->set_dns_server(netdev_default, 0, &ipaddr); + netdev_default->ops->set_dns_server(netdev_default, 1, &ipaddr); + res = netdev_inet->netdb_ops->gethostbyname(UTEST_WRONG_HOST_NAME); + if (res == RT_NULL) + { + uassert_true(RT_TRUE); /* Expected failure */ + } + else + { + uassert_true(RT_FALSE); /* Unexpected success */ + } + + /* Test with correct DNS server - hostname resolution should succeed */ + inet_aton(UTEST_DNS, &ipaddr); + netdev_default->ops->set_dns_server(netdev_default, 0, &ipaddr); + netdev_default->ops->set_dns_server(netdev_default, 1, &ipaddr); + res = netdev_inet->netdb_ops->gethostbyname(UTEST_HOST_ADDR); + uassert_true(res != RT_NULL); + + /* DNS will be restored in utest_tc_cleanup */ +} + +/** + * @brief Test DHCP functionality + * + * This function tests: + * - DHCP disable operation + * - DHCP state verification + * - DHCP state restoration + * - Network configuration stability after DHCP operations + * + * @note Includes retry mechanism to wait for network configuration restoration + * to prevent interference with subsequent ping tests + */ +static void test_netdev_dhcp(void) +{ + rt_err_t res; + rt_bool_t initial_state; + ip_addr_t initial_ip, initial_gw; + int retry_count = 0; + const int max_retries = 10; + + /* Save initial network configuration for restoration */ + initial_state = netdev_is_dhcp_enabled(netdev_default); + initial_ip = netdev_default->ip_addr; + initial_gw = netdev_default->gw; + rt_kprintf("Initial DHCP state: %s\n", initial_state ? "enabled" : "disabled"); + rt_kprintf("Initial IP: %s, Gateway: %s\n", inet_ntoa(initial_ip), inet_ntoa(initial_gw)); + + /* Test DHCP disable operation */ + res = netdev_dhcp_enabled(netdev_default, RT_FALSE); + if (res == RT_EOK) + { + uassert_false(netdev_is_dhcp_enabled(netdev_default)); + rt_kprintf("DHCP successfully disabled\n"); + } + else + { + rt_kprintf("Failed to disable DHCP: error code %d\n", res); + uassert_true(RT_FALSE); + return; + } + + /* Restore initial DHCP state */ + res = netdev_dhcp_enabled(netdev_default, initial_state); + uassert_true(res == RT_EOK); + + /* Wait for network configuration to stabilize after DHCP restoration */ + if (initial_state) + { + rt_kprintf("Waiting for DHCP to restore network configuration...\n"); + while (retry_count < max_retries) + { + rt_thread_mdelay(1000); /* Wait 1000ms between checks */ + + /* Check if IP and gateway are properly restored */ + if (!ip_addr_isany(&netdev_default->ip_addr) && + !ip_addr_isany(&netdev_default->gw)) + { + rt_kprintf("Network configuration restored after %d retries\n", retry_count); + rt_kprintf("Current IP: %s, Gateway: %s\n", + inet_ntoa(netdev_default->ip_addr), + inet_ntoa(netdev_default->gw)); + break; + } + retry_count++; + } + + /* Fail the test if network configuration is not restored within timeout */ + if (retry_count >= max_retries) + { + rt_kprintf("Failed: Network configuration not fully restored within timeout\n"); + uassert_true(RT_FALSE); + } + } +} + +/** + * @brief Test setting network interface configuration parameters + * + * This function tests: + * - IP address setting and verification + * - Gateway address setting and verification + * - Network configuration restoration + * - DHCP disable/enable during configuration changes + * + * @note DHCP will be restored in utest_tc_cleanup + */ +static void test_netdev_ifconfig_set(void) +{ +#define UTEST_WRONG_IP_ADDR "192.1.4.125" /* Test IP address */ +#define UTEST_HOST_ADDR "www.rt-thread.org" /* Test hostname */ +#define UTEST_WRONG_GW_IP_ADDR "192.168.99.1" /* Test gateway address */ + + ip_addr_t ipaddr, true_ipaddr; + rt_err_t res = RT_EOK; + + /* Save original IP address for restoration */ + true_ipaddr = netdev_default->ip_addr; + + /* Disable DHCP before manual configuration */ + res = netdev_dhcp_enabled(netdev_default, RT_FALSE); + if (res != RT_EOK) + { + rt_kprintf("Failed to disable DHCP: %d\n", res); + uassert_true(RT_FALSE); + } + + /* Test setting a different valid IP address */ + inet_aton(UTEST_WRONG_IP_ADDR, &ipaddr); + res = netdev_set_ipaddr(netdev_default, &ipaddr); + if (res != RT_EOK) + { + rt_kprintf("Failed to set IP address: %d\n", res); + uassert_true(RT_FALSE); + } + + /* Verify IP address was set correctly */ + uassert_true(ip_addr_cmp(&netdev_default->ip_addr, &ipaddr)); + + /* Test ping with new IP configuration (may fail due to network setup) */ + res = multiple_ping_test(netdev_default, UTEST_HOST_ADDR, 1); + /* Don't assert on ping result as it depends on network setup */ + + /* Restore original IP address */ + res = netdev_set_ipaddr(netdev_default, &true_ipaddr); + if (res != RT_EOK) + { + rt_kprintf("Failed to restore IP address: %d\n", res); + uassert_true(RT_FALSE); + } + + /* Verify IP address was restored */ + uassert_true(ip_addr_cmp(&netdev_default->ip_addr, &true_ipaddr)); + + /* Test gateway setting */ + ip_addr_t original_gw = netdev_default->gw; + inet_aton(UTEST_WRONG_GW_IP_ADDR, &ipaddr); + res = netdev_set_gw(netdev_default, &ipaddr); + if (res != RT_EOK) + { + rt_kprintf("Failed to set gateway: %d\n", res); + uassert_true(RT_FALSE); + } + + /* Verify gateway was set correctly */ + uassert_true(ip_addr_cmp(&netdev_default->gw, &ipaddr)); + + /* Restore original gateway */ + res = netdev_set_gw(netdev_default, &original_gw); + if (res != RT_EOK) + { + rt_kprintf("Failed to restore gateway: %d\n", res); + uassert_true(RT_FALSE); + } + + /* Verify gateway was restored */ + uassert_true(ip_addr_cmp(&netdev_default->gw, &original_gw)); + + /* DHCP will be restored in utest_tc_cleanup */ +} + +/** + * @brief Test setting the default network device + * + * This function tests the ability to set a network device as the default + * network interface for the system. + */ +static void test_netdev_set_default_netdev(void) +{ + netdev_set_default(netdev_get_by_name(RT_UTEST_DEFAULT_NETDEV_NAME)); + uassert_true(RT_TRUE); +} + +/** + * @brief Test IP address conversion functions + * + * This function tests various IP address conversion utilities: + * - IPv4 string to binary conversion (aton) + * - IPv4 binary to string conversion (ntoa) + * - IPv4 binary to string conversion with buffer (ntoa_r) + * - IPv4 address parsing and validation + * - inet_pton and inet_ntop functions + * + * @note Tests both valid and invalid IP address formats + */ +static void test_netdev_ipaddr_conversion(void) +{ +#define UTEST_IP4_ADDR_STR "192.168.1.1" /* Valid IPv4 address */ +#define UTEST_IP4_ADDR_STR2 "10.0.0.1" /* Another valid IPv4 address */ +#define UTEST_INVALID_IP_STR "999.999.999.999" /* Invalid IPv4 address */ + + ip4_addr_t ip4_addr; + char buf[16]; /* Maximum length for IPv4 string representation */ + int res; + + /* Test IPv4 string to binary conversion (aton) */ + res = netdev_ip4addr_aton(UTEST_IP4_ADDR_STR, &ip4_addr); + uassert_true(res == 1); /* Should succeed */ + uassert_true(ip4_addr_get_u32(&ip4_addr) == inet_addr(UTEST_IP4_ADDR_STR)); + + /* Test invalid IP address conversion */ + res = netdev_ip4addr_aton(UTEST_INVALID_IP_STR, &ip4_addr); + uassert_true(res == 0); /* Should fail */ + + /* Test IPv4 binary to string conversion with buffer (ntoa_r) */ + ip4_addr_set_u32(&ip4_addr, inet_addr(UTEST_IP4_ADDR_STR)); + netdev_ip4addr_ntoa_r(&ip4_addr, buf, sizeof(buf)); + uassert_str_equal(buf, UTEST_IP4_ADDR_STR); + + /* Test IPv4 binary to string conversion (ntoa) */ + const char *ntoa_result = netdev_ip4addr_ntoa(&ip4_addr); + uassert_str_equal(ntoa_result, UTEST_IP4_ADDR_STR); + + /* Test IP address string to binary conversion (ipaddr_addr) */ + in_addr_t addr = netdev_ipaddr_addr(UTEST_IP4_ADDR_STR); + uassert_true(addr == inet_addr(UTEST_IP4_ADDR_STR)); + + /* Test invalid IP address should return INADDR_NONE */ + addr = netdev_ipaddr_addr(UTEST_INVALID_IP_STR); + uassert_true(addr == INADDR_NONE); + + /* Test inet_pton and inet_ntop for IPv4 */ + struct in_addr in_addr; + res = netdev_inet_pton(AF_INET, UTEST_IP4_ADDR_STR, &in_addr); + uassert_true(res == 1); /* Should succeed */ + uassert_true(in_addr.s_addr == inet_addr(UTEST_IP4_ADDR_STR)); + + const char *ntop_result = netdev_inet_ntop(AF_INET, &in_addr, buf, sizeof(buf)); + uassert_true(ntop_result != NULL); + uassert_str_equal(buf, UTEST_IP4_ADDR_STR); + + /* Test invalid IP format should fail */ + res = netdev_inet_pton(AF_INET, UTEST_INVALID_IP_STR, &in_addr); + uassert_true(res == 0); /* Should fail */ +} + +/** + * @brief Test network device retrieval functions + * + * This function tests various methods to retrieve network devices: + * - Get device by name (valid and invalid names) + * - Get device by IP address (valid and invalid addresses) + * - Get device by interface index (valid and invalid indices) + * - Get first device by flags + * - Get device by protocol family (if SAL is enabled) + * + * @note Tests both successful and failed retrieval scenarios + */ +static void test_netdev_get_functions(void) +{ + struct netdev *netdev_found; + + /* Test get_by_name */ + netdev_found = netdev_get_by_name(netdev_default->name); + uassert_true(netdev_found == netdev_default); + + netdev_found = netdev_get_by_name("nonexistent"); + uassert_true(netdev_found == RT_NULL); + + /* Test get_by_ipaddr */ + netdev_found = netdev_get_by_ipaddr(&netdev_default->ip_addr); + uassert_true(netdev_found == netdev_default); + + ip_addr_t invalid_ip; + ip_addr_set_zero(&invalid_ip); + netdev_found = netdev_get_by_ipaddr(&invalid_ip); + uassert_true(netdev_found == RT_NULL); + + /* Test get_by_ifindex */ + netdev_found = netdev_get_by_ifindex(netdev_default->ifindex); + uassert_true(netdev_found == netdev_default); + + netdev_found = netdev_get_by_ifindex(-1); + uassert_true(netdev_found == RT_NULL); + + /* Test get_first_by_flags */ + netdev_found = netdev_get_first_by_flags(NETDEV_FLAG_UP); + uassert_true(netdev_found != RT_NULL); + + netdev_found = netdev_get_first_by_flags(NETDEV_FLAG_UP | NETDEV_FLAG_LINK_UP); + uassert_true(netdev_found != RT_NULL); + +#ifdef RT_USING_SAL + /* Test get_by_family */ + netdev_found = netdev_get_by_family(AF_INET); + uassert_true(netdev_found != RT_NULL); + + /* Test family_get */ + int family = netdev_family_get(netdev_default); + uassert_true(family == AF_INET); +#endif +} + +/** + * @brief Test network device status setting operations + * + * This function tests: + * - Network interface up/down operations + * - Link status setting and verification + * - Status operation error handling + * - Status restoration + * + * @note Carefully avoids disrupting network connectivity during testing + */ +static void test_netdev_status_set(void) +{ + rt_err_t res; + rt_bool_t original_link_up; + + /* Test set_up and set_down - be careful not to disrupt network */ + rt_bool_t was_up = netdev_is_up(netdev_default); + + if (!was_up) + { + res = netdev_set_up(netdev_default); + uassert_true(res == RT_EOK); + uassert_true(netdev_is_up(netdev_default)); + } + + uassert_true(netdev_is_up(netdev_default)); + uassert_true(netdev_default->ops->set_up != RT_NULL); + uassert_true(netdev_default->ops->set_down != RT_NULL); + + /* Test set_down and set_up cycle */ + res = netdev_set_down(netdev_default); + if (res == RT_EOK) + { + uassert_false(netdev_is_up(netdev_default)); + res = netdev_set_up(netdev_default); + uassert_true(res == RT_EOK); + uassert_true(netdev_is_up(netdev_default)); + } + else + { + uassert_true(netdev_is_up(netdev_default)); + } + + /* Test link status */ + original_link_up = netdev_is_link_up(netdev_default); + netdev_low_level_set_link_status(netdev_default, RT_FALSE); + uassert_false(netdev_is_link_up(netdev_default)); + netdev_low_level_set_link_status(netdev_default, RT_TRUE); + uassert_true(netdev_is_link_up(netdev_default)); + + /* Restore original link status */ + netdev_low_level_set_link_status(netdev_default, original_link_up); +} + +/** + * @brief Test network device configuration setting operations + * + * This function tests: + * - Subnet mask setting and verification + * - DNS server setting and verification + * - Network interface parameter setting (set_if) + * - Configuration restoration + * - DHCP interaction with manual configuration + * + * @note Includes proper configuration backup and restoration + */ +static void test_netdev_config_set(void) +{ + ip_addr_t original_netmask = netdev_default->netmask; + ip_addr_t original_dns0 = netdev_default->dns_servers[0]; + ip_addr_t original_dns1 = netdev_default->dns_servers[1]; + ip_addr_t test_netmask = {0}, test_dns = {0}; + rt_err_t res; + + /* Test set_netmask */ + if (netdev_is_dhcp_enabled(netdev_default)) + { + res = netdev_dhcp_enabled(netdev_default, RT_FALSE); + if (res != RT_EOK) + { + goto skip_netmask_test; + } + } + + inet_aton("255.255.255.0", &test_netmask); + res = netdev_set_netmask(netdev_default, &test_netmask); + if (res == RT_EOK) + { + uassert_true(ip_addr_cmp(&netdev_default->netmask, &test_netmask)); + } + + /* Restore netmask */ + res = netdev_set_netmask(netdev_default, &original_netmask); + uassert_true(res == RT_EOK); + +skip_netmask_test: + /* Test set_dns_server */ + inet_aton("8.8.8.8", &test_dns); + res = netdev_set_dns_server(netdev_default, 0, &test_dns); + uassert_true(res == RT_EOK); + uassert_true(ip_addr_cmp(&netdev_default->dns_servers[0], &test_dns)); + + inet_aton("8.8.4.4", &test_dns); + res = netdev_set_dns_server(netdev_default, 1, &test_dns); + uassert_true(res == RT_EOK); + uassert_true(ip_addr_cmp(&netdev_default->dns_servers[1], &test_dns)); + + /* Restore DNS servers */ + res = netdev_set_dns_server(netdev_default, 0, &original_dns0); + uassert_true(res == RT_EOK); + res = netdev_set_dns_server(netdev_default, 1, &original_dns1); + uassert_true(res == RT_EOK); + + /* Test set_if */ + ip_addr_t test_ip, test_gw, test_nm; + ip_addr_t original_ip, original_gw, original_nm; + + original_ip = netdev_default->ip_addr; + original_gw = netdev_default->gw; + original_nm = netdev_default->netmask; + + inet_aton("192.168.2.100", &test_ip); + inet_aton("192.168.2.1", &test_gw); + inet_aton("255.255.255.0", &test_nm); + + netdev_set_if(netdev_default->name, inet_ntoa(test_ip), inet_ntoa(test_gw), inet_ntoa(test_nm)); + uassert_true(RT_TRUE); + + /* Restore original network configuration */ + netdev_set_ipaddr(netdev_default, &original_ip); + netdev_set_gw(netdev_default, &original_gw); + netdev_set_netmask(netdev_default, &original_nm); +} + +/* Network device callback test variables */ +static rt_bool_t callback_called = RT_FALSE; /**< General callback called flag */ +static enum netdev_cb_type last_callback_type = NETDEV_CB_ADDR_IP; /**< Last callback type received */ +static rt_bool_t ip_callback_called = RT_FALSE; /**< IP address callback flag */ +static rt_bool_t gw_callback_called = RT_FALSE; /**< Gateway callback flag */ +static rt_bool_t dhcp_callback_called = RT_FALSE; /**< DHCP callback flag */ + +/* Event for synchronizing callback tests */ +static rt_event_t callback_event; + +/** + * @brief Helper function for testing callback operations + * + * This function performs a network operation and waits for the corresponding + * callback to be triggered within a specified timeout. + * + * @param operation Function pointer to the operation to perform + * @param param Parameter to pass to the operation + * @param event_mask Event mask to wait for + * @param callback_flag Pointer to callback flag to check + * @param test_name Name of the test for logging purposes + */ +static void test_callback_operation(rt_err_t (*operation)(struct netdev *, void *), + void *param, + rt_uint32_t event_mask, + rt_bool_t *callback_flag, + const char *test_name) +{ + rt_err_t res; + rt_uint32_t recved; + + /* Reset callback flag */ + *callback_flag = RT_FALSE; + + /* Perform operation */ + res = operation(netdev_default, param); + if (res == RT_EOK) + { + /* Wait for callback event */ + rt_err_t event_res = rt_event_recv(callback_event, event_mask, + RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR, + CALLBACK_EVENT_TIMEOUT, &recved); + if (event_res == RT_EOK && *callback_flag) + { + rt_kprintf("%s test passed\n", test_name); + uassert_true(RT_TRUE); + } + else + { + rt_kprintf("%s test failed - event_res=%d, callback_called=%d\n", + test_name, event_res, *callback_flag); + uassert_true(RT_FALSE); + } + } + else + { + rt_kprintf("Failed to perform %s: %d\n", test_name, res); + uassert_true(RT_FALSE); + } +} + +/* Wrapper functions for callback testing */ +static rt_err_t set_ipaddr_wrapper(struct netdev *netdev, void *param) +{ + return netdev_set_ipaddr(netdev, (ip_addr_t *)param); +} + +static rt_err_t set_gw_wrapper(struct netdev *netdev, void *param) +{ + return netdev_set_gw(netdev, (ip_addr_t *)param); +} + +static rt_err_t dhcp_enable_wrapper(struct netdev *netdev, void *param) +{ + return netdev_dhcp_enabled(netdev, *(rt_bool_t *)param); +} + +static void test_callback(struct netdev *netdev, enum netdev_cb_type type) +{ + callback_called = RT_TRUE; + last_callback_type = type; + rt_kprintf("Callback called: type=%d, netdev=%s\n", type, netdev->name); + + /* Track specific callback types */ + switch (type) + { + case NETDEV_CB_ADDR_IP: + ip_callback_called = RT_TRUE; + break; + case NETDEV_CB_ADDR_GATEWAY: + gw_callback_called = RT_TRUE; + break; + case NETDEV_CB_STATUS_DHCP_ENABLE: + dhcp_callback_called = RT_TRUE; + break; + default: + break; + } + + /* Send event to synchronize */ + if (callback_event) + { + rt_event_send(callback_event, 1 << type); + } +} + +static void empty_callback(struct netdev *netdev, enum netdev_cb_type type) +{ + /* Empty callback function for clearing */ +} + +/** + * @brief Test network device callback mechanism + * + * This function tests the callback functionality for network events: + * - Setting status and address callbacks + * - IP address change callbacks + * - Gateway change callbacks + * - DHCP enable callbacks + * - Callback synchronization using events + * - Callback cleanup + * + * @note Uses event synchronization to verify callbacks are properly triggered + */ +static void test_netdev_callbacks(void) +{ + ip_addr_t test_ip, test_gw; + rt_bool_t dhcp_enable = RT_TRUE; + + /* Reset callback test variables */ + callback_called = RT_FALSE; + last_callback_type = NETDEV_CB_ADDR_IP; + ip_callback_called = RT_FALSE; + gw_callback_called = RT_FALSE; + dhcp_callback_called = RT_FALSE; + + /* Set status callback */ + netdev_set_status_callback(netdev_default, test_callback); + /* Set address callback */ + netdev_set_addr_callback(netdev_default, test_callback); + + /* Test IP address change callback */ + inet_aton("192.168.1.100", &test_ip); + test_callback_operation(set_ipaddr_wrapper, &test_ip, + 1 << NETDEV_CB_ADDR_IP, &ip_callback_called, "IP address"); + + /* Reset callback flags */ + ip_callback_called = RT_FALSE; + gw_callback_called = RT_FALSE; + + /* Test gateway change callback */ + inet_aton("192.168.1.1", &test_gw); + test_callback_operation(set_gw_wrapper, &test_gw, + 1 << NETDEV_CB_ADDR_GATEWAY, &gw_callback_called, "Gateway"); + + /* Reset callback flags */ + dhcp_callback_called = RT_FALSE; + + /* Test DHCP enable callback */ + test_callback_operation(dhcp_enable_wrapper, &dhcp_enable, + 1 << NETDEV_CB_STATUS_DHCP_ENABLE, &dhcp_callback_called, "DHCP enable"); + + /* Clear callbacks */ + netdev_set_status_callback(netdev_default, empty_callback); + netdev_set_addr_callback(netdev_default, empty_callback); +} + +/** + * @brief Test multiple network interfaces functionality + * + * This function tests: + * - Creating and registering a new network device + * - Network device search and verification + * - Default network device switching + * - Interface index assignment and lookup + * - Network device list management + * - Network device unregistration and cleanup + * + * @note Creates a temporary test network device for comprehensive testing + */ +static void test_netdev_multiple_interfaces(void) +{ + /* Test network device name */ +#define TEST_NETDEV_NAME "fake-e0" + + struct netdev *test_netdev = RT_NULL; + struct netdev *found_netdev = RT_NULL; + struct netdev *original_default = netdev_default; + ip_addr_t test_ip; + int result; + + rt_slist_t *node = RT_NULL; + struct netdev *current_netdev; + int netdev_count = 0; + rt_bool_t found_original = RT_FALSE; + rt_bool_t found_test = RT_FALSE; + + /* Allocate memory for test netdev */ + test_netdev = rt_malloc(sizeof(struct netdev)); + if (test_netdev == RT_NULL) + { + rt_kprintf("Failed to allocate memory for test netdev\n"); + uassert_true(RT_FALSE); + return; + } + + /* Initialize test netdev structure */ + rt_memset(test_netdev, 0, sizeof(struct netdev)); + + /* Set basic properties */ + test_netdev->hwaddr_len = 6; + test_netdev->hwaddr[0] = 0x01; + test_netdev->hwaddr[1] = 0x02; + test_netdev->hwaddr[2] = 0x03; + test_netdev->hwaddr[3] = 0x04; + test_netdev->hwaddr[4] = 0x05; + test_netdev->hwaddr[5] = 0x06; + + test_netdev->mtu = 1500; + test_netdev->flags = NETDEV_FLAG_UP | NETDEV_FLAG_LINK_UP; + + /* Set IP address */ + inet_aton("192.168.2.100", &test_ip); + ip_addr_copy(test_netdev->ip_addr, test_ip); + inet_aton("255.255.255.0", &test_ip); + ip_addr_copy(test_netdev->netmask, test_ip); + inet_aton("192.168.2.1", &test_ip); + ip_addr_copy(test_netdev->gw, test_ip); + + /* Register the test netdev */ + result = netdev_register(test_netdev, TEST_NETDEV_NAME, RT_NULL); + if (result != RT_EOK) + { + rt_kprintf("Failed to register test netdev: %d\n", result); + rt_free(test_netdev); + uassert_true(RT_FALSE); + return; + } + + rt_kprintf("Successfully registered test netdev '%s'\n", TEST_NETDEV_NAME); + + /* Verify the test netdev can be found by name */ + found_netdev = netdev_get_by_name(TEST_NETDEV_NAME); + uassert_true(found_netdev == test_netdev); + + /* Verify the test netdev can be found by IP address */ + found_netdev = netdev_get_by_ipaddr(&test_netdev->ip_addr); + uassert_true(found_netdev == test_netdev); + + /* Test default netdev switching */ + netdev_set_default(test_netdev); + uassert_true(netdev_default == test_netdev); + + /* Switch back to original default */ + netdev_set_default(original_default); + uassert_true(netdev_default == original_default); + + /* Test netdev ifindex */ + uassert_true(test_netdev->ifindex > 0); + found_netdev = netdev_get_by_ifindex(test_netdev->ifindex); + uassert_true(found_netdev == test_netdev); + + if (netdev_list == RT_NULL) + { + rt_kprintf("Netdev list is empty\n"); + uassert_true(RT_FALSE); + return; + } + + for (node = &(netdev_list->list); node; node = rt_slist_next(node)) + { + current_netdev = rt_list_entry(node, struct netdev, list); + netdev_count++; + + if (rt_strcmp(current_netdev->name, original_default->name) == 0) + { + found_original = RT_TRUE; + } + if (rt_strcmp(current_netdev->name, TEST_NETDEV_NAME) == 0) + { + found_test = RT_TRUE; + } + } + + uassert_true(netdev_count >= 2); /* At least original + test */ + uassert_true(found_original); + uassert_true(found_test); + + /* Cleanup: Unregister the test netdev */ + result = netdev_unregister(test_netdev); + if (result == RT_EOK) + { + test_netdev = RT_NULL; + rt_free(test_netdev); + } + else + { + rt_kprintf("Failed to unregister test netdev: %d\n", result); + /* Manual cleanup if unregister failed */ + if (test_netdev != RT_NULL) + { + rt_free(test_netdev); + test_netdev = RT_NULL; + } + } + + /* Verify test netdev is no longer accessible */ + found_netdev = netdev_get_by_name(TEST_NETDEV_NAME); + uassert_true(found_netdev == RT_NULL); +} + +/** + * @brief Unit test initialization function + * + * This function performs setup operations before running tests: + * - Creates callback synchronization event + * - Locates and configures the test network device + * - Saves initial network configuration for restoration + * - Waits for network connectivity to be established + * - Sets the test network device as default + * + * @return rt_err_t RT_EOK on success, -RT_ERROR on failure + */ +static rt_err_t utest_tc_init(void) +{ + struct netdev *netdev_lwip = RT_NULL; + + /* Create event for callback synchronization */ + callback_event = rt_event_create("callback_event", RT_IPC_FLAG_FIFO); + if (callback_event == RT_NULL) + { + rt_kprintf("Failed to create callback event\n"); + return -RT_ERROR; + } + + netdev_lwip = netdev_get_by_name(RT_UTEST_DEFAULT_NETDEV_NAME); + if (netdev_lwip == RT_NULL) + { + rt_kprintf("Network interface device not found!\n"); + return -RT_ERROR; + } + + /* Save initial network configuration */ + initial_ip_addr = netdev_lwip->ip_addr; + initial_netmask = netdev_lwip->netmask; + initial_gw = netdev_lwip->gw; + initial_dns0 = netdev_lwip->dns_servers[0]; + initial_dns1 = netdev_lwip->dns_servers[1]; + initial_dhcp_enabled = netdev_is_dhcp_enabled(netdev_lwip); + + rt_kprintf("Saved initial network config - IP: %s, DHCP: %s\n", + inet_ntoa(initial_ip_addr), + initial_dhcp_enabled ? "enabled" : "disabled"); + + /* Wait for network connect successful */ + while (1) + { + if (!ip_addr_isany(&netdev_lwip->ip_addr)) + { + rt_kprintf("IP address assigned: %s\n", inet_ntoa(netdev_lwip->ip_addr)); + break; + } + rt_thread_mdelay(500); + } + + /* Save the old netdev */ + netdev_default_old = netdev_default; + netdev_set_default(netdev_lwip); + + if (netdev_default == RT_NULL) + { + rt_kprintf("No default network interface!\n"); + return -RT_ERROR; + } + + return RT_EOK; +} + +/** + * @brief Unit test cleanup function + * + * This function performs cleanup operations after running tests: + * - Deletes callback synchronization event + * - Restores original network configuration + * - Restores original DHCP settings + * - Restores original default network device + * + * @return rt_err_t RT_EOK on success + */ +static rt_err_t utest_tc_cleanup(void) +{ + rt_err_t res; + + /* Delete callback event */ + if (callback_event) + { + rt_event_delete(callback_event); + callback_event = RT_NULL; + } + + /* Restore initial network configuration */ + if (netdev_default != RT_NULL) + { + rt_kprintf("Restoring initial network configuration...\n"); + + /* First disable DHCP if it's enabled */ + if (netdev_is_dhcp_enabled(netdev_default)) + { + res = netdev_dhcp_enabled(netdev_default, RT_FALSE); + if (res != RT_EOK) + { + rt_kprintf("Warning: Failed to disable DHCP: %d\n", res); + } + } + + /* Restore network configuration */ + res = netdev_set_ipaddr(netdev_default, &initial_ip_addr); + if (res != RT_EOK) rt_kprintf("Warning: Failed to restore IP address: %d\n", res); + + res = netdev_set_netmask(netdev_default, &initial_netmask); + if (res != RT_EOK) rt_kprintf("Warning: Failed to restore netmask: %d\n", res); + + res = netdev_set_gw(netdev_default, &initial_gw); + if (res != RT_EOK) rt_kprintf("Warning: Failed to restore gateway: %d\n", res); + + res = netdev_set_dns_server(netdev_default, 0, &initial_dns0); + if (res != RT_EOK) rt_kprintf("Warning: Failed to restore DNS0: %d\n", res); + + res = netdev_set_dns_server(netdev_default, 1, &initial_dns1); + if (res != RT_EOK) rt_kprintf("Warning: Failed to restore DNS1: %d\n", res); + + /* Restore DHCP state */ + if (initial_dhcp_enabled) + { + rt_kprintf("Restoring DHCP...\n"); + res = netdev_dhcp_enabled(netdev_default, RT_TRUE); + if (res != RT_EOK) + { + rt_kprintf("Warning: Failed to restore DHCP: %d\n", res); + } + else + { + rt_kprintf("DHCP restored, waiting for IP address...\n"); + } + } + + rt_kprintf("Network configuration restoration completed\n"); + } + + netdev_set_default(netdev_default_old); + return RT_EOK; +} + +static void testcase(void) +{ + /* Test DHCP functionality */ + UTEST_UNIT_RUN(test_netdev_dhcp); + + /* Test network connectivity using ping operations */ + UTEST_UNIT_RUN(test_netdev_ping); + + /* Test network interface configuration and status */ + UTEST_UNIT_RUN(test_netdev_ifconfig); + + /* Test network statistics functionality */ + UTEST_UNIT_RUN(test_netdev_netstat); + + /* Test DNS server configuration and hostname resolution */ + UTEST_UNIT_RUN(test_netdev_dns); + + /* Test setting network interface configuration parameters */ + UTEST_UNIT_RUN(test_netdev_ifconfig_set); + + /* Test setting the default network device */ + UTEST_UNIT_RUN(test_netdev_set_default_netdev); + + /* Test IP address conversion functions */ + UTEST_UNIT_RUN(test_netdev_ipaddr_conversion); + + /* Test network device retrieval functions */ + UTEST_UNIT_RUN(test_netdev_get_functions); + + /* Test network device status setting operations */ + UTEST_UNIT_RUN(test_netdev_status_set); + + /* Test network device configuration setting operations */ + UTEST_UNIT_RUN(test_netdev_config_set); + + /* Test network device callback mechanisms */ + UTEST_UNIT_RUN(test_netdev_callbacks); + + /* Test multiple network interfaces functionality */ + UTEST_UNIT_RUN(test_netdev_multiple_interfaces); +} +UTEST_TC_EXPORT(testcase, "components.net.tc_netdev", utest_tc_init, utest_tc_cleanup, 1000);