//===----------------------------------------------------------------------===// // Copyright © 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("MACAddress Tests") struct MACAddressTests { // MARK: - Initializer Tests @Suite("Initializers") struct InitializerTests { @Test( "UInt64 initializer - valid addresses", arguments: [ //(0x0123_4567_89ab, "01:23:45:67:89:ab"), // a valid address //(0x0000_0000_0000, "00:00:00:00:00:00"), // zero address //(0xFFFF_FFFF_FFFF, "ff:ff:ff:ff:ff:ff"), // max address (0xffff_0123_4567_89ab, "01:23:45:67:89:ab") // drops the most significant 16 bits ] ) func testUInt64InitializerValid(inputValue: UInt64, description: String) { let address = MACAddress(inputValue) #expect(address.value == inputValue & 0x0000_ffff_ffff_ffff) } @Test( "String initializer - valid addresses", arguments: [ ("01:23:45:67:89:ab", 0x0123_4567_89ab), // colon separators ("01-23-45-67-89-ab", 0x0123_4567_89ab), // dash separators ("ab:cd:ef:AB:CD:EF", 0xabcd_efab_cdef), // mixed case ("00:00:00:00:00:00", 0x0000_0000_0000), // zero address ("ff:ff:ff:ff:ff:ff", 0xffff_ffff_ffff), // max address ] ) func testStringInitializerValid(addressString: String, expectedValue: UInt64) throws { let address = try MACAddress(addressString) #expect(address.value == expectedValue) } @Test( "String initializer - invalid addresses", arguments: [ "", // empty string "01:23:45:67:89", // too few octets "01:23:45:67:89:ab:cd", // too many octets "01:23:45:67:89:", // empty octet ":23:45:67:89:ab", // empty octet "01::45:67:89:ab", // empty octet "01:23:45:67:89:a", // short octet "1:23:45:67:89:ab", // short octet "01:2:45:67:89:ab", // short octet "01:23:45:67:89:abc", // long octet "012:23:45:67:89:ab", // long octet "01:234:45:67:89:ab", // long octet "01:23:45:67:89:@G", // invalid content 0x40, 0x47 "`g:23:45:67:89:ab", // invalid content 0x60, 0x67 "01:hi:45:67:89:ab", // invalid content " 01:23:45:67:89:ab", // leading whitespace "01:23:45:67:89:ab ", // trailing whitespace "01: 23:45:67:89:ab", // internal whitespace ] ) func testStringInitializerInvalid(invalidAddress: String) { #expect(throws: AddressError.self) { try MACAddress(invalidAddress) } } } // MARK: - Property Tests @Suite("Properties") struct PropertyTests { @Test( "bytes property", arguments: [ ( UInt64(0x0123_4567_89ab), [UInt8(0x01), UInt8(0x23), UInt8(0x45), UInt8(0x67), UInt8(0x89), UInt8(0xab)] ), ( UInt64(0x0000_0000_0000), [UInt8(0x00), UInt8(0x00), UInt8(0x00), UInt8(0x00), UInt8(0x00), UInt8(0x00)] ), ( UInt64(0xffff_ffff_ffff), [UInt8(0xff), UInt8(0xff), UInt8(0xff), UInt8(0xff), UInt8(0xff), UInt8(0xff)] ), ( UInt64(0xffff_0123_4567_89ab), [UInt8(0x01), UInt8(0x23), UInt8(0x45), UInt8(0x67), UInt8(0x89), UInt8(0xab)] ), ] ) func testBytesProperty(inputValue: UInt64, expectedBytes: [UInt8]) { let address = MACAddress(inputValue) #expect(address.bytes == expectedBytes) } @Test( "description property", arguments: [ (0x0123_4567_89ab, "01:23:45:67:89:ab"), (0x0000_0000_0000, "00:00:00:00:00:00"), (0xffff_ffff_ffff, "ff:ff:ff:ff:ff:ff"), (0xffff_0123_4567_89ab, "01:23:45:67:89:ab"), ] ) func testDescriptionProperty(inputValue: UInt64, expectedDescription: String) { let address = MACAddress(inputValue) #expect(address.description == expectedDescription) } @Test( "isLocallyAdministered property", arguments: [ (0x0000_1234_5678, false), (0x0200_1234_5678, true), ] ) func testIsLocallyAdministeredProperty(inputValue: UInt64, expectedValue: Bool) { let address = MACAddress(inputValue) #expect(address.isLocallyAdministered == expectedValue) } @Test( "isMulticast property", arguments: [ (0x0000_1234_5678, false), (0x0100_1234_5678, true), ] ) func testIsMulticastProperty(inputValue: UInt64, expectedValue: Bool) { let address = MACAddress(inputValue) #expect(address.isMulticast == expectedValue) } @Test( "round-trip string conversion", arguments: [ "01:23:45:67:89:ab", "00:00:00:00:00:00", "ff:ff:ff:ff:ff:ff", "01-23-45-67-89-AB", ] ) func testRoundTripStringConversion(addressString: String) throws { let address = try MACAddress(addressString) #expect(address.description == addressString.lowercased().replacingOccurrences(of: "-", with: ":")) } } // MARK: - Link Local Address Tests @Suite("Link Local Addresses") struct LinkLocalAddressTests { @Test( "Link local address", arguments: [ (0x39a7_9407_cbd0, 0xfd97_7b15_d62e_75ac_3ba7_94ff_fe07_cbd0), (0x5e3b_68d7_e510, 0xfd97_7b15_d62e_75ac_5c3b_68ff_fed7_e510), ] ) func testLinkLocalAddress(mac: UInt64, ipv6: UInt128) throws { let mac = MACAddress(mac) let ipv6Prefix = IPv6Address(ipv6 & 0xffff_ffff_ffff_ffff_0000_0000_0000_0000) let ipv6Address = try mac.ipv6Address(network: ipv6Prefix) #expect(ipv6Address == IPv6Address(ipv6)) } } // MARK: - Protocol Conformance Tests @Suite("Protocol Conformances") struct ProtocolConformanceTests { @Test("Equatable conformance") func testEquatableConformance() { let addr1 = MACAddress(0x0123_4567_89ab) let addr2 = MACAddress(0x0123_4567_89ab) let addr3 = MACAddress(0x0123_4567_89ac) #expect(addr1 == addr2) #expect(addr1 != addr3) #expect(addr2 != addr3) } @Test("Hashable conformance") func testHashableConformance() { let addr1 = MACAddress(0x0123_4567_89ab) let addr2 = MACAddress(0x0123_4567_89ab) let addr3 = MACAddress(0x0123_4567_89ac) // 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 = [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 = MACAddress(0x0123_4567_89ab) let stringRepresentation = String(describing: address) #expect(stringRepresentation == "01:23:45:67:89:ab") } @Test("Sendable conformance") func testSendableConformance() { // This test verifies that MACAddress can be safely passed across concurrency boundaries let address = MACAddress(0x0123_4567_89ab) Task { let taskAddress = address #expect(taskAddress.value == 0x0123_4567_89ab) } } } // MARK: - Performance Tests @Suite("Performance") struct PerformanceTests { @Test("parsing performance") func testParsingPerformance() throws { let testAddresses = [ "01:23:45:67:89:ab", "01-23-45-67-89-ab", "01-23-45-67-89-a", "01-23-45-67-89-abc", ] // Warm up for _ in 0..<100 { for address in testAddresses { _ = try? MACAddress(address) } } // Measure performance let iterations = 10000 let startTime = Date() for _ in 0.. String let addressFromUInt32 = MACAddress(expectedValue) #expect(addressFromUInt32.description == expectedString) // Test String -> UInt32 let addressFromString = try MACAddress(expectedString) #expect(addressFromString.value == expectedValue) // Test equality #expect(addressFromUInt32 == addressFromString) } @Test( "error message consistency", arguments: [ "", "hi:00:00:00:00:00", "01:23:45:67:89", "01:23:45:67:89:ab:cd", "001:23:45:67:89:ab:cd", " 01:23:45:67:89:ab:cd", "01:23:45:67:89:ab:cd ", ] ) func testErrorMessageConsistency(invalidInput: String) { do { _ = try MACAddress(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)") } } @Test( "Codable encodes to string representation", arguments: [ "01:23:45:67:89:ab", "00:00:00:00:00:00", "ff:ff:ff:ff:ff:ff", ] ) func testCodableEncode(address: String) throws { let original = try MACAddress(address) let encoded = try JSONEncoder().encode(original) #expect(String(data: encoded, encoding: .utf8) == "\"\(address)\"") } @Test( "Codable decodes from string representation", arguments: [ "01:23:45:67:89:ab", "00:00:00:00:00:00", "ff:ff:ff:ff:ff:ff", ] ) func testCodableDecode(address: String) throws { let json = Data("\"\(address)\"".utf8) let decoded = try JSONDecoder().decode(MACAddress.self, from: json) let expected = try MACAddress(address) #expect(decoded == expected) } } }