Files
wehub-resource-sync 680845cb1c
Linux build / Determine Swift version (push) Waiting to run
Linux build / Linux compile check (push) Blocked by required conditions
Build containerization / Verify commit signatures (push) Has been skipped
Build containerization / containerization (push) Successful in 0s
chore: import upstream snapshot with attribution
2026-07-13 12:25:30 +08:00

495 lines
17 KiB
Swift

//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
import Testing
@testable import ContainerizationExtras
@Suite("IPv4Address Tests")
struct IPv4AddressTests {
// MARK: - Initializer Tests
@Suite("Initializers")
struct InitializerTests {
@Test(
"UInt32 initializer",
arguments: [
(0x7F00_0001, "127.0.0.1"), // localhost
(0x0000_0000, "0.0.0.0"), // zero address
(0xFFFF_FFFF, "255.255.255.255"), // max address
(0xC0A8_0101, "192.168.1.1"), // private network
(0x0808_0808, "8.8.8.8"), // Google DNS
]
)
func testUInt32Initializer(inputValue: UInt32, description: String) {
let address = IPv4Address(inputValue)
#expect(address.value == inputValue)
}
@Test(
"String initializer - valid addresses",
arguments: [
("127.0.0.1", 0x7F00_0001), // localhost
("0.0.0.0", 0x0000_0000), // zero address
("255.255.255.255", 0xFFFF_FFFF), // broadcast
("10.0.0.1", 0x0A00_0001), // private network 10.x
("192.168.1.1", 0xC0A8_0101), // private network 192.168.x
("172.16.0.1", 0xAC10_0001), // private network 172.16.x
("1.2.3.4", 0x0102_0304), // single digits
("192.168.100.254", 0xC0A8_64FE), // mixed digits
]
)
func testStringInitializerValid(addressString: String, expectedValue: UInt32) throws {
let address = try IPv4Address(addressString)
#expect(address.value == expectedValue)
}
@Test(
"String initializer - invalid addresses",
arguments: [
"", // empty string
"1.2.3", // too short
"1.2.3.4.5", // too many octets
"192.168.1.256", // octet out of range
"192.168.001.1", // leading zeros
"01.2.3.4", // leading zero first octet
" 192.168.1.1", // leading whitespace
"192.168.1.1 ", // trailing whitespace
"192. 168.1.1", // internal whitespace
"192.168.1.a", // invalid character
"192.168.1.-1", // negative number
"192..1.1", // missing octet
".168.1.1", // missing first octet
"192.168.1.", // missing last octet
"192.168.1.1.extra", // too long
]
)
func testStringInitializerInvalid(invalidAddress: String) {
#expect(throws: AddressError.self) {
try IPv4Address(invalidAddress)
}
}
@Test(
"byte array initializer - valid addresses",
arguments: [
([UInt8(127), 0, 0, 1], UInt32(0x7F00_0001)), // localhost
([UInt8(0), 0, 0, 0], UInt32(0x0000_0000)), // zero address
([UInt8(255), 255, 255, 255], UInt32(0xFFFF_FFFF)), // broadcast
([UInt8(192), 168, 1, 1], UInt32(0xC0A8_0101)), // private network
([UInt8(0x12), 0x34, 0x56, 0x78], UInt32(0x1234_5678)), // byte order test
]
)
func testByteArrayInitializerValid(bytes: [UInt8], expectedValue: UInt32) throws {
let address = try IPv4Address(bytes)
#expect(address.value == expectedValue)
// The byte array initializer must round-trip with the bytes property.
#expect(address.bytes == bytes)
}
@Test(
"byte array initializer - invalid lengths",
arguments: [
[], // empty
[UInt8(1)], // too short
[UInt8(1), 2, 3], // too short
[UInt8(1), 2, 3, 4, 5], // too long
]
)
func testByteArrayInitializerInvalid(bytes: [UInt8]) {
#expect(throws: AddressError.self) {
try IPv4Address(bytes)
}
}
}
// MARK: - Property Tests
@Suite("Properties")
struct PropertyTests {
@Test(
"bytes property",
arguments: [
(UInt32(0x7F00_0001), [UInt8(127), UInt8(0), UInt8(0), UInt8(1)]), // localhost
(UInt32(0x0000_0000), [UInt8(0), UInt8(0), UInt8(0), UInt8(0)]), // zero
(UInt32(0xFFFF_FFFF), [UInt8(255), UInt8(255), UInt8(255), UInt8(255)]), // broadcast
(UInt32(0xC0A8_0101), [UInt8(192), UInt8(168), UInt8(1), UInt8(1)]), // private network
(UInt32(0x1234_5678), [UInt8(0x12), UInt8(0x34), UInt8(0x56), UInt8(0x78)]), // byte order test
]
)
func testBytesProperty(inputValue: UInt32, expectedBytes: [UInt8]) {
let address = IPv4Address(inputValue)
#expect(address.bytes == expectedBytes)
}
@Test(
"description property",
arguments: [
(0x7F00_0001, "127.0.0.1"), // localhost
(0x0000_0000, "0.0.0.0"), // zero
(0xFFFF_FFFF, "255.255.255.255"), // broadcast
(0xC0A8_0101, "192.168.1.1"), // private network
(0x0102_0304, "1.2.3.4"), // single digits
]
)
func testDescriptionProperty(inputValue: UInt32, expectedDescription: String) {
let address = IPv4Address(inputValue)
#expect(address.description == expectedDescription)
}
@Test(
"round-trip string conversion",
arguments: [
"0.0.0.0",
"127.0.0.1",
"192.168.1.1",
"10.0.0.1",
"172.16.0.1",
"255.255.255.255",
"1.2.3.4",
"8.8.8.8",
"1.1.1.1",
]
)
func testRoundTripStringConversion(addressString: String) throws {
let address = try IPv4Address(addressString)
#expect(address.description == addressString)
}
}
// MARK: - Protocol Conformance Tests
@Suite("Protocol Conformances")
struct ProtocolConformanceTests {
@Test("Equatable conformance")
func testEquatableConformance() {
let addr1 = IPv4Address(0x7F00_0001)
let addr2 = IPv4Address(0x7F00_0001)
let addr3 = IPv4Address(0xC0A8_0101)
#expect(addr1 == addr2)
#expect(addr1 != addr3)
#expect(addr2 != addr3)
}
@Test("Hashable conformance")
func testHashableConformance() {
let addr1 = IPv4Address(0x7F00_0001)
let addr2 = IPv4Address(0x7F00_0001)
let addr3 = IPv4Address(0xC0A8_0101)
// Equal objects should have equal hash values
#expect(addr1.hashValue == addr2.hashValue)
// Different objects should ideally have different hash values
// (though this is not guaranteed, it's very likely for these values)
#expect(addr1.hashValue != addr3.hashValue)
// Test that addresses can be used in Sets and Dictionaries
let addressSet: Set<IPv4Address> = [addr1, addr2, addr3]
#expect(addressSet.count == 2) // addr1 and addr2 are equal
let addressDict = [addr1: "localhost", addr3: "private"]
#expect(addressDict[addr2] == "localhost") // addr2 equals addr1
}
@Test("CustomStringConvertible conformance")
func testCustomStringConvertibleConformance() {
let address = IPv4Address(0x7F00_0001)
let stringRepresentation = String(describing: address)
#expect(stringRepresentation == "127.0.0.1")
}
@Test("Sendable conformance")
func testSendableConformance() {
// This test verifies that IPv4Address can be safely passed across concurrency boundaries
let address = IPv4Address(0x7F00_0001)
Task {
let taskAddress = address
#expect(taskAddress.value == 0x7F00_0001)
}
}
@Test(
"Codable encodes to string representation",
arguments: [
"127.0.0.1",
"192.168.1.1",
"0.0.0.0",
"255.255.255.255",
]
)
func testCodableEncode(address: String) throws {
let original = try IPv4Address(address)
let encoded = try JSONEncoder().encode(original)
#expect(String(data: encoded, encoding: .utf8) == "\"\(address)\"")
}
@Test(
"Codable decodes from string representation",
arguments: [
"127.0.0.1",
"192.168.1.1",
"0.0.0.0",
"255.255.255.255",
]
)
func testCodableDecode(address: String) throws {
let json = Data("\"\(address)\"".utf8)
let decoded = try JSONDecoder().decode(IPv4Address.self, from: json)
let expected = try IPv4Address(address)
#expect(decoded == expected)
}
}
// MARK: - Edge Cases and Error Conditions
@Suite("Edge Cases")
struct EdgeCaseTests {
@Test(
"boundary values",
arguments: [
("0.0.0.0", 0x0000_0000), // minimum
("255.255.255.255", 0xFFFF_FFFF), // maximum
("255.0.0.0", 0xFF00_0000), // max first octet
("0.255.0.0", 0x00FF_0000), // max second octet
("0.0.255.0", 0x0000_FF00), // max third octet
("0.0.0.255", 0x0000_00FF), // max fourth octet
]
)
func testBoundaryValues(addressString: String, expectedValue: UInt32) throws {
let address = try IPv4Address(addressString)
#expect(address.value == expectedValue)
}
@Test(
"special addresses",
arguments: [
"127.0.0.1", // loopback
"255.255.255.255", // broadcast
"0.0.0.0", // network address
"8.8.8.8", // Google DNS
"1.1.1.1", // Cloudflare DNS
]
)
func testSpecialAddresses(addressString: String) throws {
let address = try IPv4Address(addressString)
#expect(address.description == addressString)
}
@Test(
"leading zero validation - invalid",
arguments: [
"01.0.0.0",
"0.01.0.0",
"0.0.01.0",
"0.0.0.01",
"192.168.001.1",
"010.0.0.1",
"00.0.0.1",
]
)
func testLeadingZeroValidationInvalid(invalidAddress: String) {
#expect(throws: AddressError.self) {
try IPv4Address(invalidAddress)
}
}
@Test("leading zero validation - valid single zeros")
func testLeadingZeroValidationValid() {
// Single "0" should be valid
#expect(throws: Never.self) {
try IPv4Address("0.0.0.0")
}
}
@Test(
"string length validation - too short",
arguments: [
"", "1", "1.2", "1.2.3", "1.2.3.",
]
)
func testStringLengthValidationTooShort(shortString: String) {
#expect(throws: AddressError.self) {
try IPv4Address(shortString)
}
}
@Test(
"string length validation - too long",
arguments: [
"255.255.255.255.1",
"1234.168.1.1",
"192.1234.1.1",
"192.168.1234.1",
"192.168.1.1234",
]
)
func testStringLengthValidationTooLong(longString: String) {
#expect(throws: AddressError.self) {
try IPv4Address(longString)
}
}
}
// MARK: - Performance Tests
@Suite("Performance")
struct PerformanceTests {
@Test("parsing performance")
func testParsingPerformance() throws {
let testAddresses = [
"192.168.1.1",
"10.0.0.1",
"172.16.0.1",
"127.0.0.1",
"8.8.8.8",
"1.1.1.1",
"255.255.255.255",
"0.0.0.0",
]
// Warm up
for _ in 0..<100 {
for address in testAddresses {
_ = try IPv4Address(address)
}
}
// Measure performance
let iterations = 10000
let startTime = Date()
for _ in 0..<iterations {
for address in testAddresses {
_ = try IPv4Address(address)
}
}
let endTime = Date()
let totalTime = endTime.timeIntervalSince(startTime)
let averageTime = totalTime / Double(iterations * testAddresses.count)
// Should be very fast - less than 1ms per parse on average
#expect(averageTime < 0.001, "Parsing should be fast: \(averageTime)s per address")
}
@Test("bytes property performance")
func testBytesPropertyPerformance() {
let address = IPv4Address(0xC0A8_0101)
let iterations = 100000
let startTime = Date()
for _ in 0..<iterations {
_ = address.bytes
}
let endTime = Date()
let totalTime = endTime.timeIntervalSince(startTime)
let averageTime = totalTime / Double(iterations)
// Should be very fast - less than 0.1ms per call on average
#expect(averageTime < 0.0001, "Bytes property should be fast: \(averageTime)s per call")
}
@Test("description property performance")
func testDescriptionPropertyPerformance() {
let address = IPv4Address(0xC0A8_0101)
let iterations = 10000
let startTime = Date()
for _ in 0..<iterations {
_ = address.description
}
let endTime = Date()
let totalTime = endTime.timeIntervalSince(startTime)
let averageTime = totalTime / Double(iterations)
// Should be reasonably fast - less than 1ms per call on average
#expect(averageTime < 0.001, "Description property should be fast: \(averageTime)s per call")
}
}
// MARK: - Integration Tests
@Suite("Integration")
struct IntegrationTests {
@Test(
"comprehensive round-trip test",
arguments: [
(0x0000_0000, "0.0.0.0"),
(0x7F00_0001, "127.0.0.1"),
(0xC0A8_0101, "192.168.1.1"),
(0x0A00_0001, "10.0.0.1"),
(0xAC10_0001, "172.16.0.1"),
(0xFFFF_FFFF, "255.255.255.255"),
(0x0808_0808, "8.8.8.8"),
(0x0101_0101, "1.1.1.1"),
(0x1234_5678, "18.52.86.120"),
(0xDEAD_BEEF, "222.173.190.239"),
]
)
func testComprehensiveRoundTrip(expectedValue: UInt32, expectedString: String) throws {
// Test UInt32 -> String
let addressFromUInt32 = IPv4Address(expectedValue)
#expect(addressFromUInt32.description == expectedString)
// Test String -> UInt32
let addressFromString = try IPv4Address(expectedString)
#expect(addressFromString.value == expectedValue)
// Test equality
#expect(addressFromUInt32 == addressFromString)
}
@Test(
"error message consistency",
arguments: [
"",
"256.1.1.1",
"1.2.3",
"1.2.3.4.5",
"192.168.001.1",
" 192.168.1.1",
"192.168.1.1 ",
"192.168.1.a",
]
)
func testErrorMessageConsistency(invalidInput: String) {
do {
_ = try IPv4Address(invalidInput)
#expect(Bool(false), "Should have thrown for input: \(invalidInput)")
} catch let error as AddressError {
#expect(error == AddressError.unableToParse)
} catch {
#expect(Bool(false), "Should have thrown AddressError, got: \(error)")
}
}
}
}