chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,494 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
// 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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user