8da5f2306d
- Bindable is generally useful, so it makes sense to move it out of the netlink library. Removed Equatable conformance from Bindable as this isn't generally necessary (it's only used for unit tests in netlink). - Fix some offset checks in a couple data binding functions and improve the unit tests.
457 lines
15 KiB
Swift
457 lines
15 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 Testing
|
|
|
|
@testable import ContainerizationExtras
|
|
|
|
struct BufferTest {
|
|
// MARK: - hexEncodedString Tests
|
|
|
|
@Test func testArrayHexEncodedStringEmpty() {
|
|
let buffer: [UInt8] = []
|
|
#expect(buffer.hexEncodedString() == "")
|
|
}
|
|
|
|
@Test func testArrayHexEncodedStringSingleByte() {
|
|
let buffer: [UInt8] = [0xFF]
|
|
#expect(buffer.hexEncodedString() == "ff")
|
|
}
|
|
|
|
@Test func testArrayHexEncodedStringMultipleBytes() {
|
|
let buffer: [UInt8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]
|
|
#expect(buffer.hexEncodedString() == "0123456789abcdef")
|
|
}
|
|
|
|
@Test func testArrayHexEncodedStringZeroes() {
|
|
let buffer: [UInt8] = [0x00, 0x00, 0x00]
|
|
#expect(buffer.hexEncodedString() == "000000")
|
|
}
|
|
|
|
@Test func testArraySliceHexEncodedStringEmpty() {
|
|
let buffer: [UInt8] = [0x01, 0x02, 0x03]
|
|
let slice = buffer[0..<0]
|
|
#expect(slice.hexEncodedString() == "")
|
|
}
|
|
|
|
@Test func testArraySliceHexEncodedStringSingleByte() {
|
|
let buffer: [UInt8] = [0x01, 0x02, 0x03]
|
|
let slice = buffer[1..<2]
|
|
#expect(slice.hexEncodedString() == "02")
|
|
}
|
|
|
|
@Test func testArraySliceHexEncodedStringMultipleBytes() {
|
|
let buffer: [UInt8] = [0x00, 0xAA, 0xBB, 0xCC, 0x00]
|
|
let slice = buffer[1..<4]
|
|
#expect(slice.hexEncodedString() == "aabbcc")
|
|
}
|
|
|
|
// MARK: - bind<T> Tests
|
|
|
|
@Test func testBufferBind() throws {
|
|
let expectedValue: UInt64 = 0x0102_0304_0506_0708
|
|
let expectedBuffer: [UInt8] = [
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
|
|
]
|
|
var buffer = [UInt8](repeating: 0, count: 3 * MemoryLayout<UInt64>.size)
|
|
guard let ptr = buffer.bind(as: UInt64.self, offset: 2 * MemoryLayout<UInt64>.size) else {
|
|
#expect(Bool(false), "could not bind value to buffer")
|
|
return
|
|
}
|
|
|
|
ptr.pointee = expectedValue
|
|
#expect(buffer == expectedBuffer)
|
|
}
|
|
|
|
@Test func testBufferBindZeroOffset() {
|
|
let expectedValue: UInt32 = 0x1234_5678
|
|
var buffer = [UInt8](repeating: 0, count: 8)
|
|
guard let ptr = buffer.bind(as: UInt32.self, offset: 0) else {
|
|
#expect(Bool(false), "could not bind value to buffer at offset 0")
|
|
return
|
|
}
|
|
|
|
ptr.pointee = expectedValue
|
|
#expect(buffer[0] == 0x78)
|
|
#expect(buffer[1] == 0x56)
|
|
#expect(buffer[2] == 0x34)
|
|
#expect(buffer[3] == 0x12)
|
|
}
|
|
|
|
@Test func testBufferBindRangeError() throws {
|
|
var buffer = [UInt8](repeating: 0, count: 3 * MemoryLayout<UInt64>.size)
|
|
#expect(buffer.bind(as: UInt64.self, offset: 2 * MemoryLayout<UInt64>.size + 1) == nil)
|
|
}
|
|
|
|
@Test func testBufferBindRangeErrorExactBoundary() {
|
|
var buffer = [UInt8](repeating: 0, count: 8)
|
|
// Trying to bind UInt64 at offset 1 requires 9 bytes total
|
|
#expect(buffer.bind(as: UInt64.self, offset: 1) == nil)
|
|
}
|
|
|
|
@Test func testBufferBindWithCustomSize() {
|
|
var buffer = [UInt8](repeating: 0, count: 16)
|
|
// Request a size larger than the type
|
|
guard let ptr = buffer.bind(as: UInt32.self, offset: 4, size: 8) else {
|
|
#expect(Bool(false), "could not bind with custom size")
|
|
return
|
|
}
|
|
|
|
ptr.pointee = 0xAABB_CCDD
|
|
#expect(buffer[4] == 0xDD)
|
|
#expect(buffer[5] == 0xCC)
|
|
#expect(buffer[6] == 0xBB)
|
|
#expect(buffer[7] == 0xAA)
|
|
}
|
|
|
|
@Test func testBufferBindWithCustomSizeRangeError() {
|
|
var buffer = [UInt8](repeating: 0, count: 10)
|
|
// Request size 8 at offset 4 would require 12 bytes total
|
|
#expect(buffer.bind(as: UInt32.self, offset: 4, size: 8) == nil)
|
|
}
|
|
|
|
// MARK: - copyIn<T> Tests
|
|
|
|
@Test func testCopyInUInt8() {
|
|
var buffer = [UInt8](repeating: 0, count: 4)
|
|
let value: UInt8 = 0x42
|
|
|
|
guard let offset = buffer.copyIn(as: UInt8.self, value: value, offset: 2) else {
|
|
#expect(Bool(false), "could not copy UInt8 to buffer")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 3)
|
|
#expect(buffer[2] == 0x42)
|
|
}
|
|
|
|
@Test func testCopyInUInt16() {
|
|
var buffer = [UInt8](repeating: 0, count: 8)
|
|
let value: UInt16 = 0x1234
|
|
|
|
guard let offset = buffer.copyIn(as: UInt16.self, value: value, offset: 3) else {
|
|
#expect(Bool(false), "could not copy UInt16 to buffer")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 5)
|
|
#expect(buffer[3] == 0x34)
|
|
#expect(buffer[4] == 0x12)
|
|
}
|
|
|
|
@Test func testCopyInUInt32() {
|
|
var buffer = [UInt8](repeating: 0, count: 8)
|
|
let value: UInt32 = 0x1234_5678
|
|
|
|
guard let offset = buffer.copyIn(as: UInt32.self, value: value, offset: 0) else {
|
|
#expect(Bool(false), "could not copy UInt32 to buffer")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 4)
|
|
#expect(buffer[0] == 0x78)
|
|
#expect(buffer[1] == 0x56)
|
|
#expect(buffer[2] == 0x34)
|
|
#expect(buffer[3] == 0x12)
|
|
}
|
|
|
|
@Test func testCopyInUInt64() {
|
|
var buffer = [UInt8](repeating: 0, count: 16)
|
|
let value: UInt64 = 0x0102_0304_0506_0708
|
|
|
|
guard let offset = buffer.copyIn(as: UInt64.self, value: value, offset: 4) else {
|
|
#expect(Bool(false), "could not copy UInt64 to buffer")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 12)
|
|
#expect(buffer[4] == 0x08)
|
|
#expect(buffer[5] == 0x07)
|
|
#expect(buffer[6] == 0x06)
|
|
#expect(buffer[7] == 0x05)
|
|
#expect(buffer[8] == 0x04)
|
|
#expect(buffer[9] == 0x03)
|
|
#expect(buffer[10] == 0x02)
|
|
#expect(buffer[11] == 0x01)
|
|
}
|
|
|
|
@Test func testCopyInRangeError() {
|
|
var buffer = [UInt8](repeating: 0, count: 8)
|
|
let value: UInt64 = 0x1234_5678_90AB_CDEF
|
|
|
|
// Offset 4 + size 8 = 12, but buffer only has 8 bytes
|
|
#expect(buffer.copyIn(as: UInt64.self, value: value, offset: 4) == nil)
|
|
}
|
|
|
|
@Test func testCopyInExactBoundary() {
|
|
var buffer = [UInt8](repeating: 0, count: 8)
|
|
let value: UInt64 = 0xFEDC_BA98_7654_3210
|
|
|
|
guard let offset = buffer.copyIn(as: UInt64.self, value: value, offset: 0) else {
|
|
#expect(Bool(false), "could not copy UInt64 at exact boundary")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 8)
|
|
}
|
|
|
|
@Test func testCopyInWithCustomSize() {
|
|
var buffer = [UInt8](repeating: 0, count: 16)
|
|
let value: UInt32 = 0xAABB_CCDD
|
|
|
|
// Copy with custom size of 8 (larger than UInt32's 4 bytes)
|
|
guard let offset = buffer.copyIn(as: UInt32.self, value: value, offset: 2, size: 8) else {
|
|
#expect(Bool(false), "could not copy with custom size")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 6) // offset + MemoryLayout<UInt32>.size
|
|
#expect(buffer[2] == 0xDD)
|
|
#expect(buffer[3] == 0xCC)
|
|
#expect(buffer[4] == 0xBB)
|
|
#expect(buffer[5] == 0xAA)
|
|
}
|
|
|
|
@Test func testCopyInWithCustomSizeRangeError() {
|
|
var buffer = [UInt8](repeating: 0, count: 8)
|
|
let value: UInt32 = 0x1234_5678
|
|
|
|
// Request size 8 at offset 2 would require 10 bytes total
|
|
#expect(buffer.copyIn(as: UInt32.self, value: value, offset: 2, size: 8) == nil)
|
|
}
|
|
|
|
// MARK: - copyOut<T> Tests
|
|
|
|
@Test func testCopyOutUInt8() {
|
|
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33]
|
|
|
|
guard let (offset, value) = buffer.copyOut(as: UInt8.self, offset: 2) else {
|
|
#expect(Bool(false), "could not copy out UInt8")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 3)
|
|
#expect(value == 0x22)
|
|
}
|
|
|
|
@Test func testCopyOutUInt16() {
|
|
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
|
|
|
|
guard let (offset, value) = buffer.copyOut(as: UInt16.self, offset: 2) else {
|
|
#expect(Bool(false), "could not copy out UInt16")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 4)
|
|
#expect(value == 0x3322)
|
|
}
|
|
|
|
@Test func testCopyOutUInt32() {
|
|
let buffer: [UInt8] = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]
|
|
|
|
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: 0) else {
|
|
#expect(Bool(false), "could not copy out UInt32")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 4)
|
|
#expect(value == 0x7856_3412)
|
|
}
|
|
|
|
@Test func testCopyOutUInt64() {
|
|
let buffer: [UInt8] = [
|
|
0x00, 0x00, 0x00, 0x00,
|
|
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
|
|
0xFF, 0xFF,
|
|
]
|
|
|
|
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: 4) else {
|
|
#expect(Bool(false), "could not copy out UInt64")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 12)
|
|
#expect(value == 0x8877_6655_4433_2211)
|
|
}
|
|
|
|
@Test func testCopyOutRangeError() {
|
|
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33]
|
|
|
|
// Trying to read UInt64 from offset 0 with only 4 bytes
|
|
#expect(buffer.copyOut(as: UInt64.self, offset: 0) == nil)
|
|
}
|
|
|
|
@Test func testCopyOutExactBoundary() {
|
|
let buffer: [UInt8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
|
|
|
|
guard let (offset, value) = buffer.copyOut(as: UInt64.self, offset: 0) else {
|
|
#expect(Bool(false), "could not copy out at exact boundary")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 8)
|
|
#expect(value == 0x0807_0605_0403_0201)
|
|
}
|
|
|
|
@Test func testCopyOutWithCustomSize() {
|
|
let buffer: [UInt8] = [0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0xFF, 0xFF, 0xFF, 0xFF]
|
|
|
|
guard let (offset, value) = buffer.copyOut(as: UInt32.self, offset: 2, size: 8) else {
|
|
#expect(Bool(false), "could not copy out with custom size")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 6) // offset + MemoryLayout<UInt32>.size
|
|
#expect(value == 0x4433_2211)
|
|
}
|
|
|
|
@Test func testCopyOutWithCustomSizeRangeError() {
|
|
let buffer: [UInt8] = [0x00, 0x11, 0x22, 0x33, 0x44, 0x55]
|
|
|
|
// Request size 8 at offset 2 would require 10 bytes total, but buffer only has 6
|
|
#expect(buffer.copyOut(as: UInt32.self, offset: 2, size: 8) == nil)
|
|
}
|
|
|
|
// MARK: - copyIn(buffer:) and copyOut(buffer:) Tests
|
|
|
|
@Test func testBufferCopy() throws {
|
|
let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]
|
|
var buffer = [UInt8](repeating: 0, count: 9)
|
|
|
|
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 4) else {
|
|
#expect(Bool(false), "could not copy to buffer")
|
|
return
|
|
}
|
|
#expect(offset == 7)
|
|
|
|
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 6) else {
|
|
#expect(Bool(false), "could not copy to buffer")
|
|
return
|
|
}
|
|
#expect(offset == 9)
|
|
|
|
let expectedBuffer: [UInt8] = [
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x01, 0x02, 0x03,
|
|
]
|
|
#expect(expectedBuffer == buffer)
|
|
|
|
var outputBuffer = [UInt8](repeating: 0, count: 3)
|
|
guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 6) else {
|
|
#expect(Bool(false), "could not copy to buffer")
|
|
return
|
|
}
|
|
#expect(offset == 9)
|
|
|
|
let expectedOutputBuffer: [UInt8] = [
|
|
0x01, 0x02, 0x03,
|
|
]
|
|
#expect(expectedOutputBuffer == outputBuffer)
|
|
}
|
|
|
|
@Test func testBufferCopyZeroOffset() {
|
|
let inputBuffer: [UInt8] = [0xAA, 0xBB, 0xCC]
|
|
var buffer = [UInt8](repeating: 0, count: 5)
|
|
|
|
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 0) else {
|
|
#expect(Bool(false), "could not copy to buffer at offset 0")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 3)
|
|
#expect(buffer[0] == 0xAA)
|
|
#expect(buffer[1] == 0xBB)
|
|
#expect(buffer[2] == 0xCC)
|
|
}
|
|
|
|
@Test func testBufferCopyEmptyBuffer() {
|
|
let inputBuffer: [UInt8] = []
|
|
var buffer = [UInt8](repeating: 0, count: 5)
|
|
|
|
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 2) else {
|
|
#expect(Bool(false), "could not copy empty buffer")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 2)
|
|
}
|
|
|
|
@Test func testBufferCopyExactFit() {
|
|
let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]
|
|
var buffer = [UInt8](repeating: 0, count: 6)
|
|
|
|
guard let offset = buffer.copyIn(buffer: inputBuffer, offset: 3) else {
|
|
#expect(Bool(false), "could not copy to exact fit")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 6)
|
|
}
|
|
|
|
@Test func testBufferCopyRangeError() throws {
|
|
let inputBuffer: [UInt8] = [0x01, 0x02, 0x03]
|
|
var buffer = [UInt8](repeating: 0, count: 9)
|
|
|
|
#expect(buffer.copyIn(buffer: inputBuffer, offset: 7) == nil)
|
|
|
|
var outputBuffer = [UInt8](repeating: 0, count: 3)
|
|
#expect(buffer.copyOut(buffer: &outputBuffer, offset: 7) == nil)
|
|
}
|
|
|
|
@Test func testBufferCopyOutZeroOffset() {
|
|
let buffer: [UInt8] = [0x11, 0x22, 0x33, 0x44, 0x55]
|
|
var outputBuffer = [UInt8](repeating: 0, count: 3)
|
|
|
|
guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 0) else {
|
|
#expect(Bool(false), "could not copy out at offset 0")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 3)
|
|
#expect(outputBuffer[0] == 0x11)
|
|
#expect(outputBuffer[1] == 0x22)
|
|
#expect(outputBuffer[2] == 0x33)
|
|
}
|
|
|
|
@Test func testBufferCopyOutEmptyBuffer() {
|
|
let buffer: [UInt8] = [0x11, 0x22, 0x33]
|
|
var outputBuffer: [UInt8] = []
|
|
|
|
guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 1) else {
|
|
#expect(Bool(false), "could not copy out to empty buffer")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 1)
|
|
}
|
|
|
|
@Test func testBufferCopyOutExactFit() {
|
|
let buffer: [UInt8] = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]
|
|
var outputBuffer = [UInt8](repeating: 0, count: 3)
|
|
|
|
guard let offset = buffer.copyOut(buffer: &outputBuffer, offset: 3) else {
|
|
#expect(Bool(false), "could not copy out exact fit")
|
|
return
|
|
}
|
|
|
|
#expect(offset == 6)
|
|
#expect(outputBuffer[0] == 0xDD)
|
|
#expect(outputBuffer[1] == 0xEE)
|
|
#expect(outputBuffer[2] == 0xFF)
|
|
}
|
|
}
|