| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280 |
- // Copyright (c) 2014 - 2024 Apple Inc. and the project authors
- // Licensed under Apache License v2.0 with Runtime Library Exception
- //
- // See LICENSE.txt for license information:
- // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
- //
- // -----------------------------------------------------------------------------
- import Foundation
- import FuzzCommon
- import XCTest
- struct TestOptions: SupportsFuzzOptions {
- var bool1: Bool = false {
- didSet { sets.append("bool1:\(bool1)") }
- }
- var bool2: Bool = false {
- didSet { sets.append("bool2:\(bool2)") }
- }
- var int1: Int = 100 {
- didSet { sets.append("int1:\(int1)") }
- }
- var int2: Int = 1 {
- didSet { sets.append("int2:\(int2)") }
- }
- var sets: [String] = []
- static var fuzzOptionsList: [FuzzCommon.FuzzOption<Self>] = [
- .boolean(\.bool1),
- .boolean(\.bool2),
- .byte(\.int1),
- .byte(\.int2, mod: 16),
- ]
- init() {}
- }
- struct TestOptionsLarge: SupportsFuzzOptions {
- var bool1: Bool = false {
- didSet { sets.append("bool1:\(bool1)") }
- }
- var bool2: Bool = false {
- didSet { sets.append("bool2:\(bool2)") }
- }
- var bool3: Bool = false {
- didSet { sets.append("bool3:\(bool3)") }
- }
- var bool4: Bool = false {
- didSet { sets.append("bool4:\(bool4)") }
- }
- var int1: Int = 100 {
- didSet { sets.append("int1:\(int1)") }
- }
- var bool5: Bool = false {
- didSet { sets.append("bool5:\(bool5)") }
- }
- var bool6: Bool = false {
- didSet { sets.append("bool6:\(bool6)") }
- }
- var bool7: Bool = false {
- didSet { sets.append("bool7:\(bool7)") }
- }
- var bool8: Bool = false {
- didSet { sets.append("bool8:\(bool8)") }
- }
- var int2: Int = 1 {
- didSet { sets.append("int2:\(int2)") }
- }
- var sets: [String] = []
- static var fuzzOptionsList: [FuzzCommon.FuzzOption<Self>] = [
- .boolean(\.bool1),
- .boolean(\.bool2),
- .boolean(\.bool3),
- .boolean(\.bool4),
- .byte(\.int1),
- .boolean(\.bool5),
- .boolean(\.bool6),
- .boolean(\.bool7),
- .boolean(\.bool8),
- .byte(\.int2),
- ]
- init() {}
- }
- final class Test_FuzzOptions: XCTestCase {
- func testOptionBasics_noOptionsSignal() throws {
- // Claim no bytes passed.
- let bytes: [UInt8] = []
- XCTAssertEqual(bytes.count, 0)
- try bytes.withUnsafeBytes { ptr in
- let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(opts.sets, [])
- XCTAssertEqual(bytes.count, 0)
- }
- // Try with no leading zero, so no options.
- for x: UInt8 in 1...UInt8.max {
- let bytes: [UInt8] = [x]
- XCTAssertEqual(bytes.count, 1)
- try bytes.withUnsafeBytes { ptr in
- let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(opts.sets, [])
- // The buffer comes through.
- XCTAssertEqual(bytes.count, 1)
- XCTAssertEqual(bytes.baseAddress, ptr.baseAddress)
- }
- }
- }
- func testOptionBasics_optionsSignalNoBytes() throws {
- let bytes: [UInt8] = [0] // Options signal, then nothing
- XCTAssertEqual(bytes.count, 1)
- try bytes.withUnsafeBytes { ptr in
- let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(opts.sets, [])
- // Since no following bytes, the buffer comes through.
- XCTAssertEqual(bytes.count, 1)
- XCTAssertEqual(bytes.baseAddress, ptr.baseAddress)
- }
- }
- func testOptionBasics_bool() throws {
- let testCases: [(byte: UInt8, b1: Bool, b2: Bool, sets: [String])] = [
- (0x0, false, false, ["bool1:false", "bool2:false"]),
- (0x1, true, false, ["bool1:true", "bool2:false"]),
- (0x2, false, true, ["bool1:false", "bool2:true"]),
- (0x3, true, true, ["bool1:true", "bool2:true"]),
- ]
- for test in testCases {
- let bytes: [UInt8] = [0, test.byte]
- XCTAssertEqual(bytes.count, 2)
- try bytes.withUnsafeBytes { ptr in
- let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(opts.sets, test.sets)
- XCTAssertEqual(bytes.count, 0) // No bytes, the one was the options
- XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
- XCTAssertEqual(opts.bool1, test.b1)
- XCTAssertEqual(opts.bool2, test.b2)
- }
- }
- }
- func testOptionBasics_byte() throws {
- let testCases: [(bytes: [UInt8], i1: Int, i2: Int, sets: [String])] = [
- ([0x0], 100, 1, []),
- ([0x4, 2], 2, 1, ["int1:2"]),
- ([0x8, 7], 100, 7, ["int2:7"]),
- ([0xC, 3, 20], 3, 4, ["int1:3", "int2:4"]), // int2 has a mod applied
- ]
- for test in testCases {
- let bytes: [UInt8] = [0] + test.bytes
- try bytes.withUnsafeBytes { ptr in
- let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(opts.sets, ["bool1:false", "bool2:false"] + test.sets)
- XCTAssertEqual(bytes.count, 0) // No bytes, the one was the options
- XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
- XCTAssertEqual(opts.int1, test.i1)
- XCTAssertEqual(opts.int2, test.i2)
- }
- }
- }
- func testOptionBasics_byteMissingData() {
- let testCases: [[UInt8]] = [
- [0x4], // int1, no data
- [0x8], // int2, no data
- [0xC], // int1 & int2, no data
- [0xC, 20], // int1 & int2, data for only int1
- ]
- for test in testCases {
- let bytes: [UInt8] = [0] + test
- bytes.withUnsafeBytes { ptr in
- XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count))
- }
- }
- }
- func testOptionBasics_tailingZeros() {
- // Try every value that will have at least one bit set above the valid ones
- // to ensure it causing parsing failure.
- for x: UInt8 in 0x10...UInt8.max {
- let bytes: [UInt8] = [0, x]
- bytes.withUnsafeBytes { ptr in
- XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count))
- }
- }
- }
- func testOptionBasics_tailingMoreThan7_tailingZeros() {
- // For the first byte of optionBits, just signal that there is a second, but
- // then set all the expected zero bits to ensure it fails.
- for x: UInt8 in 0x8...UInt8.max {
- let bytes: [UInt8] = [0, 0x80, x]
- bytes.withUnsafeBytes { ptr in
- XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count))
- }
- }
- }
- func testOptionBasics_bytesAfterOptsComeThrough() throws {
- let bytes: [UInt8] = [0, 0, 1, 2, 3]
- XCTAssertEqual(bytes.count, 5)
- try bytes.withUnsafeBytes { ptr in
- let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(opts.sets, ["bool1:false", "bool2:false"])
- XCTAssertEqual(bytes.count, 3)
- XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 1)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 1, as: UInt8.self), 2)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 2, as: UInt8.self), 3)
- }
- // Make sure data is right after a bytes value also
- let bytes2: [UInt8] = [0, 0x4, 20, 4, 15, 26]
- try bytes2.withUnsafeBytes { ptr in
- let result = TestOptions.extractOptions(ptr.baseAddress!, bytes2.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(opts.sets, ["bool1:false", "bool2:false", "int1:20"])
- XCTAssertEqual(bytes.count, 3)
- XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 4)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 1, as: UInt8.self), 15)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 2, as: UInt8.self), 26)
- }
- // Options that can spill to two bytes for the optionBits.
- // Only one byte of optionsBits
- let bytes3: [UInt8] = [0, 0, 1, 2, 3]
- XCTAssertEqual(bytes3.count, 5)
- try bytes3.withUnsafeBytes { ptr in
- let result = TestOptionsLarge.extractOptions(ptr.baseAddress!, bytes3.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(
- opts.sets,
- ["bool1:false", "bool2:false", "bool3:false", "bool4:false", "bool5:false", "bool6:false"]
- )
- XCTAssertEqual(bytes.count, 3)
- XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 1)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 1, as: UInt8.self), 2)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 2, as: UInt8.self), 3)
- }
- // Two bytes of optionsBits with a `byte` value
- let bytes4: [UInt8] = [0, 0x90, 123, 0x4, 20, 81, 92, 103]
- XCTAssertEqual(bytes4.count, 8)
- try bytes4.withUnsafeBytes { ptr in
- let result = TestOptionsLarge.extractOptions(ptr.baseAddress!, bytes4.count)
- let (opts, bytes) = try XCTUnwrap(result)
- XCTAssertEqual(
- opts.sets,
- [
- "bool1:false", "bool2:false", "bool3:false", "bool4:false", "int1:123", "bool5:false",
- "bool6:false", "bool7:false", "bool8:false", "int2:20",
- ]
- )
- XCTAssertEqual(bytes.count, 3)
- XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 81)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 1, as: UInt8.self), 92)
- XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 2, as: UInt8.self), 103)
- }
- }
- }
|