| // Copyright 2026 The IREE Authors | |
| // | |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| #include "iree/async/address.h" | |
| #include <string> | |
| #include "iree/base/api.h" | |
| #include "iree/testing/gtest.h" | |
| #include "iree/testing/status_matchers.h" | |
| namespace { | |
| // Helper: format an address to a std::string for easy comparison. | |
| static std::string FormatAddress(const iree_async_address_t& address) { | |
| char buffer[IREE_ASYNC_ADDRESS_MAX_FORMAT_LENGTH]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| iree_status_t status = | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value); | |
| if (!iree_status_is_ok(status)) { | |
| iree_status_ignore(status); | |
| return "<format failed>"; | |
| } | |
| return std::string(value.data, value.size); | |
| } | |
| // Helper: parse IPv4 and return the address (or fail the test). | |
| static iree_async_address_t ParseIPv4(const char* host, uint16_t port) { | |
| iree_async_address_t address; | |
| iree_string_view_t host_view = | |
| host ? iree_make_cstring_view(host) : iree_string_view_empty(); | |
| iree_status_t status = | |
| iree_async_address_from_ipv4(host_view, port, &address); | |
| IREE_EXPECT_OK(status); | |
| return address; | |
| } | |
| // Helper: parse IPv6 and return the address (or fail the test). | |
| static iree_async_address_t ParseIPv6(const char* host, uint16_t port) { | |
| iree_async_address_t address; | |
| iree_string_view_t host_view = | |
| host ? iree_make_cstring_view(host) : iree_string_view_empty(); | |
| iree_status_t status = | |
| iree_async_address_from_ipv6(host_view, port, &address); | |
| IREE_EXPECT_OK(status); | |
| return address; | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IPv4 Parsing — Valid Cases | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFromIpv4, EmptyHostIsAny) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK( | |
| iree_async_address_from_ipv4(iree_string_view_empty(), 8080, &address)); | |
| EXPECT_EQ(FormatAddress(address), "0.0.0.0:8080"); | |
| } | |
| TEST(AddressFromIpv4, Loopback) { | |
| auto address = ParseIPv4("127.0.0.1", 80); | |
| EXPECT_EQ(FormatAddress(address), "127.0.0.1:80"); | |
| } | |
| TEST(AddressFromIpv4, AllZeros) { | |
| auto address = ParseIPv4("0.0.0.0", 0); | |
| EXPECT_EQ(FormatAddress(address), "0.0.0.0:0"); | |
| } | |
| TEST(AddressFromIpv4, MaxOctets) { | |
| auto address = ParseIPv4("255.255.255.255", 65535); | |
| EXPECT_EQ(FormatAddress(address), "255.255.255.255:65535"); | |
| } | |
| TEST(AddressFromIpv4, EphemeralPort) { | |
| auto address = ParseIPv4("192.168.1.1", 0); | |
| EXPECT_EQ(FormatAddress(address), "192.168.1.1:0"); | |
| } | |
| TEST(AddressFromIpv4, TypicalServerPort) { | |
| auto address = ParseIPv4("10.0.0.1", 443); | |
| EXPECT_EQ(FormatAddress(address), "10.0.0.1:443"); | |
| } | |
| TEST(AddressFromIpv4, SingleDigitOctets) { | |
| auto address = ParseIPv4("1.2.3.4", 5); | |
| EXPECT_EQ(FormatAddress(address), "1.2.3.4:5"); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IPv4 Parsing — Error Cases | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFromIpv4, OctetOverflow) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("256.0.0.1"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, OctetOverflowLarge) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("999.0.0.1"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, TooFewDots) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("1.2.3"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, TooManyDots) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("1.2.3.4.5"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, LeadingZeros) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4(iree_make_cstring_view("01.02.03.04"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv4, LeadingZeroSingle) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("1.02.3.4"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, TrailingGarbage) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4(iree_make_cstring_view("1.2.3.4xyz"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv4, WhitespaceLeading) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view(" 1.2.3.4"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, WhitespaceTrailing) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("1.2.3.4 "), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, EmptyOctet) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("1..3.4"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, HexRejected) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4(iree_make_cstring_view("0x7f.0.0.1"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv4, JustDots) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("..."), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, NegativeRejected) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4( | |
| iree_make_cstring_view("-1.0.0.0"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, VeryLongDigitString) { | |
| // Digits that overflow a uint32 accumulator (exceeds 255 check). | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4(iree_make_cstring_view("9999999999.0.0.1"), | |
| 80, &address)); | |
| } | |
| TEST(AddressFromIpv4, EmbeddedNul) { | |
| // string_view with embedded NUL byte — not a valid digit. | |
| iree_string_view_t host = {("1.2\0.3.4"), 8}; | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv4(host, 80, &address)); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IPv6 Parsing — Valid Cases | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFromIpv6, EmptyHostIsAny) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK( | |
| iree_async_address_from_ipv6(iree_string_view_empty(), 80, &address)); | |
| EXPECT_EQ(FormatAddress(address), "[::]:80"); | |
| } | |
| TEST(AddressFromIpv6, Loopback) { | |
| auto address = ParseIPv6("::1", 80); | |
| EXPECT_EQ(FormatAddress(address), "[::1]:80"); | |
| } | |
| TEST(AddressFromIpv6, FullForm) { | |
| auto address = ParseIPv6("2001:0db8:0000:0000:0000:0000:0000:0001", 443); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::1]:443"); | |
| } | |
| TEST(AddressFromIpv6, CompressionMiddle) { | |
| auto address = ParseIPv6("2001:db8::1", 80); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::1]:80"); | |
| } | |
| TEST(AddressFromIpv6, CompressionStart) { | |
| auto address = ParseIPv6("::1", 80); | |
| EXPECT_EQ(FormatAddress(address), "[::1]:80"); | |
| } | |
| TEST(AddressFromIpv6, CompressionEnd) { | |
| auto address = ParseIPv6("2001:db8::", 80); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::]:80"); | |
| } | |
| TEST(AddressFromIpv6, AllZeros) { | |
| auto address = ParseIPv6("::", 0); | |
| EXPECT_EQ(FormatAddress(address), "[::]:0"); | |
| } | |
| TEST(AddressFromIpv6, AllOnes) { | |
| auto address = ParseIPv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 1); | |
| EXPECT_EQ(FormatAddress(address), | |
| "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:1"); | |
| } | |
| TEST(AddressFromIpv6, LinkLocal) { | |
| auto address = ParseIPv6("fe80::1", 0); | |
| EXPECT_EQ(FormatAddress(address), "[fe80::1]:0"); | |
| } | |
| TEST(AddressFromIpv6, MixedCase) { | |
| auto address = ParseIPv6("2001:DB8::ABCD", 80); | |
| // Output is always lowercase per RFC 5952. | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::abcd]:80"); | |
| } | |
| TEST(AddressFromIpv6, SingleZeroGroupNotCompressed) { | |
| // RFC 5952: single zero group is NOT compressed. | |
| auto address = ParseIPv6("2001:db8:0:1:0:0:0:1", 80); | |
| // The run of 3 zeros at positions 4-6 gets compressed, not the single at 2. | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8:0:1::1]:80"); | |
| } | |
| TEST(AddressFromIpv6, LongestRunWins) { | |
| // Two runs of zeros: positions 2-3 (length 2) and 5-7 (length 3). | |
| auto address = ParseIPv6("1:1:0:0:1:0:0:0", 80); | |
| EXPECT_EQ(FormatAddress(address), "[1:1:0:0:1::]:80"); | |
| } | |
| TEST(AddressFromIpv6, LeftmostTiebreak) { | |
| // Two runs of equal length: positions 1-2 and 5-6. | |
| auto address = ParseIPv6("1:0:0:1:1:0:0:1", 80); | |
| EXPECT_EQ(FormatAddress(address), "[1::1:1:0:0:1]:80"); | |
| } | |
| TEST(AddressFromIpv6, Ipv4Mapped) { | |
| auto address = ParseIPv6("::ffff:192.168.1.1", 8080); | |
| EXPECT_EQ(FormatAddress(address), "[::ffff:192.168.1.1]:8080"); | |
| } | |
| TEST(AddressFromIpv6, Ipv4MappedLoopback) { | |
| auto address = ParseIPv6("::ffff:127.0.0.1", 80); | |
| EXPECT_EQ(FormatAddress(address), "[::ffff:127.0.0.1]:80"); | |
| } | |
| TEST(AddressFromIpv6, LeadingZerosInGroup) { | |
| // Leading zeros in groups are valid input. | |
| auto address = ParseIPv6("2001:0db8:0000:0000:0000:0000:0000:0001", 80); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::1]:80"); | |
| } | |
| TEST(AddressFromIpv6, MaxPort) { | |
| auto address = ParseIPv6("::1", 65535); | |
| EXPECT_EQ(FormatAddress(address), "[::1]:65535"); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // IPv6 Parsing — Error Cases | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFromIpv6, DoubleCompressionRejected) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("2001::db8::1"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv6, TooManyGroups) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("1:2:3:4:5:6:7:8:9"), | |
| 80, &address)); | |
| } | |
| TEST(AddressFromIpv6, TooFewGroupsNoCompression) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("1:2:3:4:5:6:7"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv6, GroupTooLong) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("2001:dbbbb::1"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv6, InvalidHexChar) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("2001:xyzw::1"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv6, TrailingColon) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("2001:db8:1:"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv6, LeadingSingleColon) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view(":1"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv6, TooManyGroupsWithCompression) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("1:2:3:4:5::6:7:8"), | |
| 80, &address)); | |
| } | |
| TEST(AddressFromIpv6, Ipv4MappedOctetOverflow) { | |
| // IPv4-mapped form with an octet exceeding 255. | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("::ffff:256.1.1.1"), | |
| 80, &address)); | |
| } | |
| TEST(AddressFromIpv6, Ipv4MappedTooFewOctets) { | |
| // IPv4-mapped form with fewer than 4 octets. | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("::ffff:1.2.3"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv6, SixGroupsPlusIpv4Suffix) { | |
| // 6 hex groups + IPv4 suffix = 8 total 16-bit groups (valid). | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_ipv6( | |
| iree_make_cstring_view("1:2:3:4:5:6:1.2.3.4"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv6, CompressionPlusIpv4TooManyGroups) { | |
| // Compression + hex groups + IPv4 suffix would exceed 8 total groups. | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6( | |
| iree_make_cstring_view("::1:2:3:4:5:6:1.2.3.4"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv6, ZoneIdNumeric) { | |
| // Numeric zone ID maps directly to sin6_scope_id. | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_ipv6( | |
| iree_make_cstring_view("fe80::1%3"), 80, &address)); | |
| EXPECT_EQ(FormatAddress(address), "[fe80::1%3]:80"); | |
| } | |
| TEST(AddressFromIpv6, ZoneIdZeroNotFormatted) { | |
| // No zone suffix → scope_id is 0 → no % in output. | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_ipv6(iree_make_cstring_view("fe80::1"), | |
| 80, &address)); | |
| EXPECT_EQ(FormatAddress(address), "[fe80::1]:80"); | |
| } | |
| TEST(AddressFromIpv6, ZoneIdEmpty) { | |
| // Trailing % with no zone ID is invalid. | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6( | |
| iree_make_cstring_view("fe80::1%"), 80, &address)); | |
| } | |
| TEST(AddressFromIpv6, ZoneIdLargeNumeric) { | |
| // Large numeric scope_id. | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_ipv6( | |
| iree_make_cstring_view("fe80::1%42"), 80, &address)); | |
| EXPECT_EQ(FormatAddress(address), "[fe80::1%42]:80"); | |
| } | |
| #if defined(IREE_PLATFORM_LINUX) | |
| TEST(AddressFromIpv6, ZoneIdInterfaceName) { | |
| // "lo" exists on all Linux systems — resolves to a non-zero interface index. | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_ipv6( | |
| iree_make_cstring_view("fe80::1%lo"), 80, &address)); | |
| // The formatted output uses the numeric index, not the name. | |
| std::string formatted = FormatAddress(address); | |
| // Should start with [fe80::1% and end with ]:80. | |
| EXPECT_TRUE(formatted.find("[fe80::1%") == 0); | |
| EXPECT_TRUE(formatted.find("]:80") != std::string::npos); | |
| } | |
| TEST(AddressFromIpv6, ZoneIdUnknownInterface) { | |
| // Non-existent interface name (must be < IF_NAMESIZE = 16 chars). | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_NOT_FOUND, | |
| iree_async_address_from_ipv6(iree_make_cstring_view("fe80::1%noif0"), 80, | |
| &address)); | |
| } | |
| TEST(AddressFromIpv6, ZoneIdInterfaceNameTooLong) { | |
| // Interface name exceeding IF_NAMESIZE (16 bytes on Linux). | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_ipv6( | |
| iree_make_cstring_view("fe80::1%this_name_is_way_too_long"), 80, | |
| &address)); | |
| } | |
| #endif // IREE_PLATFORM_LINUX | |
| //===----------------------------------------------------------------------===// | |
| // Unix Socket Parsing | |
| //===----------------------------------------------------------------------===// | |
| #if !defined(IREE_PLATFORM_GENERIC) && !defined(IREE_PLATFORM_WASM) | |
| TEST(AddressFromUnix, SimplePath) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_unix( | |
| iree_make_cstring_view("/tmp/socket.sock"), &address)); | |
| EXPECT_EQ(FormatAddress(address), "unix:/tmp/socket.sock"); | |
| } | |
| TEST(AddressFromUnix, RelativePath) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_unix( | |
| iree_make_cstring_view("./relative.sock"), &address)); | |
| EXPECT_EQ(FormatAddress(address), "unix:./relative.sock"); | |
| } | |
| TEST(AddressFromUnix, EmptyPathRejected) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_unix(iree_string_view_empty(), &address)); | |
| } | |
| TEST(AddressFromUnix, PathTooLong) { | |
| // sun_path is typically 104 (macOS) or 108 (Linux) bytes. | |
| // A 200-byte path should exceed the limit on all platforms. | |
| std::string long_path(200, 'x'); | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_unix( | |
| iree_make_string_view(long_path.data(), long_path.size()), &address)); | |
| } | |
| TEST(AddressFromUnix, MaxLengthPath) { | |
| // Create a path that is exactly the maximum length (sizeof(sun_path) - 1). | |
| // We test with 103 bytes which fits on both Linux (107 max) and macOS (103 | |
| // max). | |
| std::string max_path(103, 'a'); | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_unix( | |
| iree_make_string_view(max_path.data(), max_path.size()), &address)); | |
| EXPECT_EQ(FormatAddress(address), "unix:" + max_path); | |
| } | |
| #if defined(IREE_PLATFORM_LINUX) || defined(IREE_PLATFORM_ANDROID) | |
| TEST(AddressFromUnix, AbstractNamespace) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_unix( | |
| iree_make_cstring_view("@my-abstract-socket"), &address)); | |
| EXPECT_EQ(FormatAddress(address), "unix:@my-abstract-socket"); | |
| } | |
| TEST(AddressFromUnix, AbstractNamespaceEmpty) { | |
| // "@" alone means abstract namespace with an empty name (just the NUL byte). | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK( | |
| iree_async_address_from_unix(iree_make_cstring_view("@"), &address)); | |
| EXPECT_EQ(FormatAddress(address), "unix:@"); | |
| } | |
| #else // !IREE_PLATFORM_LINUX && !IREE_PLATFORM_ANDROID | |
| TEST(AddressFromUnix, AbstractNamespaceUnavailable) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_UNAVAILABLE, | |
| iree_async_address_from_unix( | |
| iree_make_cstring_view("@abstract"), &address)); | |
| } | |
| #endif // IREE_PLATFORM_LINUX || IREE_PLATFORM_ANDROID | |
| TEST(AddressFormat, UnixPathWithoutTrailingNulInLength) { | |
| // Construct a valid Unix address via the normal API, then simulate what | |
| // getsockname() returns: the socklen_t may not include the trailing NUL byte. | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_unix( | |
| iree_make_cstring_view("/tmp/test.sock"), &address)); | |
| // Subtract 1 from the stored length (drop the NUL from the count). | |
| address.length -= 1; | |
| // Format should still produce the correct path. | |
| EXPECT_EQ(FormatAddress(address), "unix:/tmp/test.sock"); | |
| } | |
| #endif // !IREE_PLATFORM_GENERIC && !IREE_PLATFORM_WASM | |
| //===----------------------------------------------------------------------===// | |
| // Format — Buffer Capacity | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFormat, BufferExactSize) { | |
| auto address = ParseIPv4("1.2.3.4", 80); | |
| // "1.2.3.4:80" = 10 chars. | |
| char buffer[10]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_OK( | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value)); | |
| EXPECT_EQ(std::string(value.data, value.size), "1.2.3.4:80"); | |
| } | |
| TEST(AddressFormat, BufferTooSmall) { | |
| auto address = ParseIPv4("127.0.0.1", 8080); | |
| // "127.0.0.1:8080" = 14 chars. Provide only 5. | |
| char buffer[5]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_OUT_OF_RANGE, | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value)); | |
| } | |
| TEST(AddressFormat, BufferCapacityZero) { | |
| auto address = ParseIPv4("1.2.3.4", 80); | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_OUT_OF_RANGE, | |
| iree_async_address_format(&address, 0, NULL, &value)); | |
| } | |
| TEST(AddressFormat, OversizedBuffer) { | |
| auto address = ParseIPv4("10.0.0.1", 443); | |
| char buffer[256]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_OK( | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value)); | |
| EXPECT_EQ(std::string(value.data, value.size), "10.0.0.1:443"); | |
| // Value points into the buffer. | |
| EXPECT_EQ(value.data, buffer); | |
| } | |
| TEST(AddressFormat, OutValueNull) { | |
| auto address = ParseIPv4("1.2.3.4", 80); | |
| char buffer[64]; | |
| // out_value is NULL — should still succeed without writing the view. | |
| IREE_EXPECT_OK(iree_async_address_format(&address, sizeof(buffer), buffer, | |
| /*out_value=*/NULL)); | |
| } | |
| TEST(AddressFormat, EmptyAddress) { | |
| iree_async_address_t address; | |
| memset(&address, 0, sizeof(address)); | |
| char buffer[64]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value)); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Format — IPv6 Specific | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFormat, Ipv6Loopback) { | |
| auto address = ParseIPv6("::1", 80); | |
| char buffer[64]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_OK( | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value)); | |
| EXPECT_EQ(std::string(value.data, value.size), "[::1]:80"); | |
| } | |
| TEST(AddressFormat, Ipv6Any) { | |
| auto address = ParseIPv6("::", 0); | |
| char buffer[64]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_OK( | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value)); | |
| EXPECT_EQ(std::string(value.data, value.size), "[::]:0"); | |
| } | |
| TEST(AddressFormat, Ipv6Mapped) { | |
| auto address = ParseIPv6("::ffff:10.0.0.1", 9090); | |
| char buffer[64]; | |
| iree_string_view_t value = iree_string_view_empty(); | |
| IREE_EXPECT_OK( | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &value)); | |
| EXPECT_EQ(std::string(value.data, value.size), "[::ffff:10.0.0.1]:9090"); | |
| } | |
| TEST(AddressFormat, TruncatedLengthTooShort) { | |
| // Address with length=1 is too short for any valid sockaddr family header. | |
| iree_async_address_t address; | |
| memset(&address, 0, sizeof(address)); | |
| address.length = 1; | |
| char buffer[IREE_ASYNC_ADDRESS_MAX_FORMAT_LENGTH]; | |
| iree_string_view_t formatted = iree_string_view_empty(); | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_format(&address, sizeof(buffer), buffer, &formatted)); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // Round-Trip Tests | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressRoundTrip, Ipv4Loopback) { | |
| auto address = ParseIPv4("127.0.0.1", 8080); | |
| EXPECT_EQ(FormatAddress(address), "127.0.0.1:8080"); | |
| } | |
| TEST(AddressRoundTrip, Ipv4Any) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK( | |
| iree_async_address_from_ipv4(iree_string_view_empty(), 0, &address)); | |
| EXPECT_EQ(FormatAddress(address), "0.0.0.0:0"); | |
| } | |
| TEST(AddressRoundTrip, Ipv6Compressed) { | |
| auto address = ParseIPv6("2001:db8::1", 443); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::1]:443"); | |
| } | |
| TEST(AddressRoundTrip, Ipv6Full) { | |
| auto address = ParseIPv6("2001:db8:85a3:0:0:8a2e:370:7334", 80); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8:85a3::8a2e:370:7334]:80"); | |
| } | |
| TEST(AddressRoundTrip, Ipv6AllOnes) { | |
| auto address = ParseIPv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 1); | |
| EXPECT_EQ(FormatAddress(address), | |
| "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:1"); | |
| } | |
| #if !defined(IREE_PLATFORM_GENERIC) && !defined(IREE_PLATFORM_WASM) | |
| TEST(AddressRoundTrip, UnixFilesystem) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_unix( | |
| iree_make_cstring_view("/var/run/daemon.sock"), &address)); | |
| EXPECT_EQ(FormatAddress(address), "unix:/var/run/daemon.sock"); | |
| } | |
| #endif // !IREE_PLATFORM_GENERIC && !IREE_PLATFORM_WASM | |
| //===----------------------------------------------------------------------===// | |
| // AddressFromString — Valid IPv4 | |
| //===----------------------------------------------------------------------===// | |
| // Helper: parse from string and return the address (or fail the test). | |
| static iree_async_address_t ParseFromString(const char* address_str) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_OK(iree_async_address_from_string( | |
| iree_make_cstring_view(address_str), &address)); | |
| return address; | |
| } | |
| TEST(AddressFromString, Ipv4Loopback) { | |
| auto address = ParseFromString("127.0.0.1:80"); | |
| EXPECT_EQ(FormatAddress(address), "127.0.0.1:80"); | |
| } | |
| TEST(AddressFromString, Ipv4AllZeros) { | |
| auto address = ParseFromString("0.0.0.0:0"); | |
| EXPECT_EQ(FormatAddress(address), "0.0.0.0:0"); | |
| } | |
| TEST(AddressFromString, Ipv4MaxValues) { | |
| auto address = ParseFromString("255.255.255.255:65535"); | |
| EXPECT_EQ(FormatAddress(address), "255.255.255.255:65535"); | |
| } | |
| TEST(AddressFromString, Ipv4EmptyHostIsAny) { | |
| auto address = ParseFromString(":8080"); | |
| EXPECT_EQ(FormatAddress(address), "0.0.0.0:8080"); | |
| } | |
| TEST(AddressFromString, Ipv4TypicalServer) { | |
| auto address = ParseFromString("10.0.0.1:443"); | |
| EXPECT_EQ(FormatAddress(address), "10.0.0.1:443"); | |
| } | |
| TEST(AddressFromString, Ipv4EphemeralPort) { | |
| auto address = ParseFromString("192.168.1.1:0"); | |
| EXPECT_EQ(FormatAddress(address), "192.168.1.1:0"); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AddressFromString — Valid IPv6 | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFromString, Ipv6Loopback) { | |
| auto address = ParseFromString("[::1]:80"); | |
| EXPECT_EQ(FormatAddress(address), "[::1]:80"); | |
| } | |
| TEST(AddressFromString, Ipv6Any) { | |
| auto address = ParseFromString("[::]:0"); | |
| EXPECT_EQ(FormatAddress(address), "[::]:0"); | |
| } | |
| TEST(AddressFromString, Ipv6Compressed) { | |
| auto address = ParseFromString("[2001:db8::1]:443"); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::1]:443"); | |
| } | |
| TEST(AddressFromString, Ipv6Ipv4Mapped) { | |
| auto address = ParseFromString("[::ffff:192.168.1.1]:8080"); | |
| EXPECT_EQ(FormatAddress(address), "[::ffff:192.168.1.1]:8080"); | |
| } | |
| TEST(AddressFromString, Ipv6ZoneId) { | |
| auto address = ParseFromString("[fe80::1%3]:80"); | |
| EXPECT_EQ(FormatAddress(address), "[fe80::1%3]:80"); | |
| } | |
| TEST(AddressFromString, Ipv6NonCanonicalInput) { | |
| // Leading zeros in groups are accepted but not preserved in output. | |
| auto address = ParseFromString("[2001:0db8::1]:80"); | |
| EXPECT_EQ(FormatAddress(address), "[2001:db8::1]:80"); | |
| } | |
| TEST(AddressFromString, Ipv6Full) { | |
| auto address = ParseFromString("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:1"); | |
| EXPECT_EQ(FormatAddress(address), | |
| "[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:1"); | |
| } | |
| TEST(AddressFromString, Ipv6MaxPort) { | |
| auto address = ParseFromString("[::1]:65535"); | |
| EXPECT_EQ(FormatAddress(address), "[::1]:65535"); | |
| } | |
| TEST(AddressFromString, Ipv6EmptyBrackets) { | |
| // Empty brackets with port — from_ipv6 treats empty host as in6addr_any. | |
| auto address = ParseFromString("[]:0"); | |
| EXPECT_EQ(FormatAddress(address), "[::]:0"); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AddressFromString — Valid Unix | |
| //===----------------------------------------------------------------------===// | |
| #if !defined(IREE_PLATFORM_GENERIC) && !defined(IREE_PLATFORM_WASM) | |
| TEST(AddressFromString, UnixFilesystem) { | |
| auto address = ParseFromString("unix:/tmp/socket.sock"); | |
| EXPECT_EQ(FormatAddress(address), "unix:/tmp/socket.sock"); | |
| } | |
| TEST(AddressFromString, UnixRelativePath) { | |
| auto address = ParseFromString("unix:./relative.sock"); | |
| EXPECT_EQ(FormatAddress(address), "unix:./relative.sock"); | |
| } | |
| #if defined(IREE_PLATFORM_LINUX) || defined(IREE_PLATFORM_ANDROID) | |
| TEST(AddressFromString, UnixAbstract) { | |
| auto address = ParseFromString("unix:@my-socket"); | |
| EXPECT_EQ(FormatAddress(address), "unix:@my-socket"); | |
| } | |
| #endif // IREE_PLATFORM_LINUX || IREE_PLATFORM_ANDROID | |
| #endif // !IREE_PLATFORM_GENERIC && !IREE_PLATFORM_WASM | |
| //===----------------------------------------------------------------------===// | |
| // AddressFromString — Error Cases | |
| //===----------------------------------------------------------------------===// | |
| TEST(AddressFromString, Empty) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string(iree_string_view_empty(), &address)); | |
| } | |
| TEST(AddressFromString, Ipv4MissingPort) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("127.0.0.1"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv4EmptyPort) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("127.0.0.1:"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv4NonNumericPort) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("127.0.0.1:abc"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv4PortOverflow) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string(iree_make_cstring_view("127.0.0.1:99999"), | |
| &address)); | |
| } | |
| TEST(AddressFromString, Ipv4PortLeadingZeros) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("127.0.0.1:080"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv4PortLeadingZeroDouble) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("127.0.0.1:00"), &address)); | |
| } | |
| TEST(AddressFromString, NoColonAtAll) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("just_a_word"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6MissingCloseBracket) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string(iree_make_cstring_view("[::1"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6NoPortAfterBracket) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("[::1]"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6MissingColonAfterBracket) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("[::1]80"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6EmptyPort) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("[::1]:"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6NonNumericPort) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("[::1]:abc"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6PortOverflow) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("[::1]:99999"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6PortLeadingZeros) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("[::1]:080"), &address)); | |
| } | |
| #if !defined(IREE_PLATFORM_GENERIC) && !defined(IREE_PLATFORM_WASM) | |
| TEST(AddressFromString, UnixEmptyPath) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("unix:"), &address)); | |
| } | |
| #endif // !IREE_PLATFORM_GENERIC && !IREE_PLATFORM_WASM | |
| TEST(AddressFromString, Ipv4BadHost) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS(IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("1.2.3.4.5:80"), &address)); | |
| } | |
| TEST(AddressFromString, Ipv6BadHost) { | |
| iree_async_address_t address; | |
| IREE_EXPECT_STATUS_IS( | |
| IREE_STATUS_INVALID_ARGUMENT, | |
| iree_async_address_from_string( | |
| iree_make_cstring_view("[2001::db8::1]:80"), &address)); | |
| } | |
| //===----------------------------------------------------------------------===// | |
| // AddressFromString — Round-Trip Tests | |
| //===----------------------------------------------------------------------===// | |
| // Helper: parse → format → parse → format, verify the two formatted strings | |
| // match. This validates that the canonical form is stable. | |
| static void VerifyRoundTrip(const char* input) { | |
| iree_async_address_t address1; | |
| IREE_ASSERT_OK( | |
| iree_async_address_from_string(iree_make_cstring_view(input), &address1)); | |
| std::string formatted1 = FormatAddress(address1); | |
| ASSERT_NE(formatted1, "<format failed>") << "First format failed"; | |
| iree_async_address_t address2; | |
| IREE_ASSERT_OK(iree_async_address_from_string( | |
| iree_make_string_view(formatted1.data(), formatted1.size()), &address2)); | |
| std::string formatted2 = FormatAddress(address2); | |
| EXPECT_EQ(formatted1, formatted2) | |
| << "Round-trip failed for input '" << input << "'"; | |
| } | |
| TEST(AddressFromStringRoundTrip, Ipv4Loopback) { | |
| VerifyRoundTrip("127.0.0.1:8080"); | |
| } | |
| TEST(AddressFromStringRoundTrip, Ipv4Any) { VerifyRoundTrip(":0"); } | |
| TEST(AddressFromStringRoundTrip, Ipv4Max) { | |
| VerifyRoundTrip("255.255.255.255:65535"); | |
| } | |
| TEST(AddressFromStringRoundTrip, Ipv6Loopback) { VerifyRoundTrip("[::1]:80"); } | |
| TEST(AddressFromStringRoundTrip, Ipv6Compressed) { | |
| VerifyRoundTrip("[2001:db8::1]:443"); | |
| } | |
| TEST(AddressFromStringRoundTrip, Ipv6Full) { | |
| VerifyRoundTrip("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:1"); | |
| } | |
| TEST(AddressFromStringRoundTrip, Ipv6Mapped) { | |
| VerifyRoundTrip("[::ffff:10.0.0.1]:9090"); | |
| } | |
| TEST(AddressFromStringRoundTrip, Ipv6ZoneId) { | |
| VerifyRoundTrip("[fe80::1%42]:80"); | |
| } | |
| #if !defined(IREE_PLATFORM_GENERIC) && !defined(IREE_PLATFORM_WASM) | |
| TEST(AddressFromStringRoundTrip, UnixPath) { | |
| VerifyRoundTrip("unix:/var/run/daemon.sock"); | |
| } | |
| #if defined(IREE_PLATFORM_LINUX) || defined(IREE_PLATFORM_ANDROID) | |
| TEST(AddressFromStringRoundTrip, UnixAbstract) { | |
| VerifyRoundTrip("unix:@my-abstract-socket"); | |
| } | |
| #endif // IREE_PLATFORM_LINUX || IREE_PLATFORM_ANDROID | |
| #endif // !IREE_PLATFORM_GENERIC && !IREE_PLATFORM_WASM | |
| } // namespace |