Skip to content

Commit

Permalink
Merge pull request #30 from PureSwift/gattTests
Browse files Browse the repository at this point in the history
Added GATT and ATT PDU Unit Tests
  • Loading branch information
colemancda authored Apr 9, 2018
2 parents 5c5114b + 01df316 commit a1d7660
Show file tree
Hide file tree
Showing 15 changed files with 1,388 additions and 431 deletions.
Binary file added Assets/ATTExchangeMTU.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 3 additions & 12 deletions Sources/ATT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation
// MARK: - Protocol Definitions

/// Bluetooth ATT protocol
public struct ATT {
public enum ATT {

public static let PSM: ProtocolServiceMultiplexer = .att

Expand All @@ -27,17 +27,6 @@ public struct ATT {

public static let maximumValueLength = 512

/// ATT MTU constants
public struct MaximumTransmissionUnit {

public struct LowEnergy {

public static let `default` = 23

public static let `maximum` = 517
}
}

// Namespace Typealiases

public typealias Error = ATTError
Expand All @@ -47,6 +36,8 @@ public struct ATT {
public typealias OpcodeType = ATTOpcodeType

public typealias AttributePermission = ATTAttributePermission

public typealias MaximumTransmissionUnit = ATTMaximumTransmissionUnit
}

/// Error codes for Error response PDU.
Expand Down
19 changes: 6 additions & 13 deletions Sources/ATTConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,7 @@ public final class ATTConnection {
// MARK: - Properties

/// Actual number of bytes for PDU ATT exchange.
public var maximumTransmissionUnit: Int = ATT.MaximumTransmissionUnit.LowEnergy.default {

willSet {

// enforce value range
assert(newValue >= ATT.MaximumTransmissionUnit.LowEnergy.default)
assert(newValue <= ATT.MaximumTransmissionUnit.LowEnergy.maximum)
}
}
public var maximumTransmissionUnit: ATTMaximumTransmissionUnit = .default

public let socket: L2CAPSocketProtocol

Expand Down Expand Up @@ -74,7 +66,7 @@ public final class ATTConnection {

log?("Attempt read")

let recievedData = try socket.recieve(maximumTransmissionUnit)
let recievedData = try socket.recieve(Int(maximumTransmissionUnit.rawValue))

log?("Recieved data (\(recievedData.count) bytes)")

Expand Down Expand Up @@ -122,7 +114,7 @@ public final class ATTConnection {
guard let sendOperation = pickNextSendOpcode()
else { return false }

assert(sendOperation.data.count <= maximumTransmissionUnit, "Trying to send \(sendOperation.data.count) bytes when MTU is \(maximumTransmissionUnit)")
assert(sendOperation.data.count <= Int(maximumTransmissionUnit.rawValue), "Trying to send \(sendOperation.data.count) bytes when MTU is \(maximumTransmissionUnit)")

log?("Sending data... (\(sendOperation.data.count) bytes)")

Expand Down Expand Up @@ -210,6 +202,7 @@ public final class ATTConnection {
/// Adds a PDU to the queue to send.
///
/// - Returns: Identifier of queued send operation or `nil` if the PDU cannot be sent.
@discardableResult
public func send <PDU: ATTProtocolDataUnit> (_ pdu: PDU, response: (callback: (AnyResponse) -> (), ATTProtocolDataUnit.Type)? = nil) -> UInt? {

let attributeOpcode = PDU.attributeOpcode
Expand Down Expand Up @@ -284,15 +277,15 @@ public final class ATTConnection {

// MARK: - Private Methods

private func encode<T: ATTProtocolDataUnit>(PDU: T) -> [UInt8]? {
private func encode <T: ATTProtocolDataUnit> (PDU: T) -> [UInt8]? {

let data = PDU.byteValue

// actual PDU length
let length = data.count

/// MTU must be large enough to hold PDU.
guard length <= maximumTransmissionUnit else { return nil }
guard length <= Int(maximumTransmissionUnit.rawValue) else { return nil }

// TODO: Sign (encrypt) data

Expand Down
96 changes: 96 additions & 0 deletions Sources/ATTMaximumTransmissionUnit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//
// ATTMaximumTransmissionUnit.swift
// Bluetooth
//
// Created by Alsey Coleman Miller on 4/8/18.
// Copyright © 2018 PureSwift. All rights reserved.
//

/// ATT Maximum Transmission Unit
public struct ATTMaximumTransmissionUnit: RawRepresentable {

public let rawValue: UInt16

public init?(rawValue: UInt16) {

guard rawValue <= ATTMaximumTransmissionUnit.max.rawValue,
rawValue >= ATTMaximumTransmissionUnit.min.rawValue
else { return nil }

self.rawValue = rawValue

assert(isValid)
}

fileprivate init(_ unsafe: UInt16) {

self.rawValue = unsafe
}
}

private extension ATTMaximumTransmissionUnit {

var isValid: Bool {

return (ATTMaximumTransmissionUnit.min.rawValue ... ATTMaximumTransmissionUnit.max.rawValue).contains(rawValue)
}
}

public extension ATTMaximumTransmissionUnit {

static var `default`: ATTMaximumTransmissionUnit { return ATTMaximumTransmissionUnit(23) }

static var min: ATTMaximumTransmissionUnit { return .default }

static var max: ATTMaximumTransmissionUnit { return ATTMaximumTransmissionUnit(517) }

init(server: UInt16,
client: UInt16) {

let mtu = Swift.min(Swift.max(Swift.min(client, server), ATTMaximumTransmissionUnit.default.rawValue), ATTMaximumTransmissionUnit.max.rawValue)

self.init(mtu)

assert(isValid)
}
}

// MARK: - Equatable

extension ATTMaximumTransmissionUnit: Equatable {

public static func == (lhs: ATTMaximumTransmissionUnit, rhs: ATTMaximumTransmissionUnit) -> Bool {

return lhs.rawValue == rhs.rawValue
}
}

// MARK: - Hashable

extension ATTMaximumTransmissionUnit: Hashable {

public var hashValue: Int {

return Int(rawValue)
}
}

// MARK: - CustomStringConvertible

extension ATTMaximumTransmissionUnit: CustomStringConvertible {

public var description: String {

return rawValue.description
}
}

// MARK: - Comparable

extension ATTMaximumTransmissionUnit: Comparable {

public static func < (lhs: ATTMaximumTransmissionUnit, rhs: ATTMaximumTransmissionUnit) -> Bool {

return lhs.rawValue < rhs.rawValue
}
}
56 changes: 44 additions & 12 deletions Sources/ATTProtocolDataUnit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,17 @@ public struct ATTErrorResponse: ATTProtocolDataUnit, Error {
/// The *Client Rx MTU* parameter shall be set to the maximum size of the attribute protocol PDU that the client can receive.
public struct ATTMaximumTransmissionUnitRequest: ATTProtocolDataUnit {

/// 0x02 = Exchange MTU Request
public static let attributeOpcode = ATT.Opcode.maximumTransmissionUnitRequest

public static let length = 3

/// Client Rx MTU
///
/// Client receive MTU size
public var clientMTU: UInt16

public init(clientMTU: UInt16 = 0) {
public init(clientMTU: UInt16) {

self.clientMTU = clientMTU
}
Expand All @@ -118,7 +122,9 @@ public struct ATTMaximumTransmissionUnitRequest: ATTProtocolDataUnit {
guard attributeOpcodeByte == type(of: self).attributeOpcode.rawValue
else { return nil }

self.clientMTU = UInt16(bytes: (byteValue[1], byteValue[2])).littleEndian
let clientMTU = UInt16(littleEndian: UInt16(bytes: (byteValue[1], byteValue[2])))

self.clientMTU = clientMTU
}

public var byteValue: [UInt8] {
Expand All @@ -141,13 +147,17 @@ public struct ATTMaximumTransmissionUnitRequest: ATTProtocolDataUnit {
/// The *Exchange MTU Response* is sent in reply to a received *Exchange MTU Request*.
public struct ATTMaximumTransmissionUnitResponse: ATTProtocolDataUnit {

/// 0x03 = Exchange MTU Response
public static let attributeOpcode = ATT.Opcode.maximumTransmissionUnitResponse

public static let length = 3

/// Server Rx MTU
///
/// Attribute server receive MTU size
public var serverMTU: UInt16

public init(serverMTU: UInt16 = 0) {
public init(serverMTU: UInt16) {

self.serverMTU = serverMTU
}
Expand All @@ -162,7 +172,9 @@ public struct ATTMaximumTransmissionUnitResponse: ATTProtocolDataUnit {
guard attributeOpcodeByte == type(of: self).attributeOpcode.rawValue
else { return nil }

self.serverMTU = UInt16(bytes: (byteValue[1], byteValue[2])).littleEndian
let serverMTU = UInt16(littleEndian: UInt16(bytes: (byteValue[1], byteValue[2])))

self.serverMTU = serverMTU
}

public var byteValue: [UInt8] {
Expand Down Expand Up @@ -240,10 +252,10 @@ public struct ATTFindInformationRequest: ATTProtocolDataUnit {
/// and contains information about this server.
public struct ATTFindInformationResponse: ATTProtocolDataUnit {

public static let attributeOpcode = ATT.Opcode.findInformationRequest
public static let attributeOpcode = ATTOpcode.findInformationResponse

/// Length ranges from 6, to the maximum MTU size.
public static let length = 8
public static let length = 6

/// The information data whose format is determined by the Format field.
public var data: Data
Expand Down Expand Up @@ -318,6 +330,22 @@ public struct ATTFindInformationResponse: ATTProtocolDataUnit {
}
}

/*
public static func == (lhs: Data, rhs: Data) -> Bool {
switch (lhs, rhs) {
case let (.bit16(lhsValue), .bit16(rhsValue)):
return lhsValue == rhsValue
case let (.bit128(lhsValue), .bit128(rhsValue)):
return lhsValue == rhsValue
default:
return false
}
}*/

public init?(byteValue: [UInt8], format: Format) {

let pairLength = format.length
Expand Down Expand Up @@ -861,15 +889,15 @@ public struct ATTReadResponse: ATTProtocolDataUnit {

public init?(byteValue: [UInt8]) {

guard byteValue.count >= ATTReadRequest.length
guard byteValue.count >= type(of: self).length
else { return nil }

let attributeOpcodeByte = byteValue[0]

guard attributeOpcodeByte == ATTReadResponse.attributeOpcode.rawValue
guard attributeOpcodeByte == type(of: self).attributeOpcode.rawValue
else { return nil }

if byteValue.count > ATTReadRequest.length {
if byteValue.count > type(of: self).length {

self.attributeValue = Array(byteValue.suffix(from: 1))

Expand Down Expand Up @@ -1254,6 +1282,8 @@ public struct ATTReadByGroupTypeResponse: ATTProtocolDataUnit {
}

self.data = attributeDataList

assert(length == (data[0].byteValue.count))
}

public var byteValue: [UInt8] {
Expand Down Expand Up @@ -1286,7 +1316,9 @@ public struct ATTReadByGroupTypeResponse: ATTProtocolDataUnit {
/// Attribute Value
public var value: [UInt8]

public init(attributeHandle: UInt16 = 0, endGroupHandle: UInt16 = 0, value: [UInt8] = []) {
public init(attributeHandle: UInt16 = 0,
endGroupHandle: UInt16 = 0,
value: [UInt8] = []) {

self.attributeHandle = attributeHandle
self.endGroupHandle = endGroupHandle
Expand All @@ -1301,9 +1333,9 @@ public struct ATTReadByGroupTypeResponse: ATTProtocolDataUnit {
self.attributeHandle = UInt16(bytes: (byteValue[0], byteValue[1])).littleEndian
self.endGroupHandle = UInt16(bytes: (byteValue[2], byteValue[3])).littleEndian

if byteValue.count > 4 {
if byteValue.count > type(of: self).length {

self.value = Array(byteValue.suffix(from: 4))
self.value = Array(byteValue.suffix(from: type(of: self).length))

} else {

Expand Down
Loading

0 comments on commit a1d7660

Please sign in to comment.