Skip to content

Commit

Permalink
Added more tests to the BLEPeripheralManager (#52)
Browse files Browse the repository at this point in the history
  • Loading branch information
Henryforce authored Aug 7, 2024
1 parent 68de8fd commit f1e34d4
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 9 deletions.
34 changes: 34 additions & 0 deletions Sources/BLECombineKit/Peripheral Manager/BLEATTRequest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// BLEATTRequest.swift
// BLECombineKit
//
// Created by Henry Javier Serrano Echeverria on 06/08/24.
// Copyright © 2024 Henry Serrano. All rights reserved.
//

import CoreBluetooth

public protocol BLEATTRequest {
/// Reference to the actual request. Use this getter to obtain the CBATTRequest if needed. Note that CBATTRequest conforms to BLEATTRequest.
var associatedRequest: CBATTRequest? { get }

/// The wrapper of the central that originated the request.
var centralWrapper: BLECentral { get }

/// The characteristic whose value will be read or written.
var characteristic: CBCharacteristic { get }

/// The zero-based index of the first byte for the read or write.
var offset: Int { get }

/// The data being read or written. For read requests, <i>value</i> will be nil and should be set before responding via @link respondToRequest:withResult: @/link. For write requests, <i>value</i> will contain the data to be written.
var value: Data? { get set }
}

extension CBATTRequest: BLEATTRequest {
public var associatedRequest: CBATTRequest? { self }

public var centralWrapper: BLECentral {
central
}
}
24 changes: 24 additions & 0 deletions Sources/BLECombineKit/Peripheral Manager/BLECentral.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// BLECentral.swift
// BLECombineKit
//
// Created by Henry Javier Serrano Echeverria on 06/08/24.
// Copyright © 2024 Henry Serrano. All rights reserved.
//

import CoreBluetooth

public protocol BLECentral {
/// Reference to the actual central. Use this getter to obtain the CBCentral if needed. Note that CBCentral conforms to BLECentral.
var associatedCentral: CBCentral? { get }

/// The UUID associated with the peer.
var identifier: UUID { get }

/// The maximum amount of data, in bytes, that can be received by the central in a single notification or indication.
var maximumUpdateValueLength: Int { get }
}

extension CBCentral: BLECentral {
public var associatedCentral: CBCentral? { self }
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public protocol BLEPeripheralManager {
/// * `BLEError.bluetoothPoweredOff`
/// * `BLEError.bluetoothInUnknownState`
/// * `BLEError.bluetoothResetting`
func observeDidReceiveRead() -> AnyPublisher<CBATTRequest, Never>
func observeDidReceiveRead() -> AnyPublisher<BLEATTRequest, Never>

/// Continuous observer for `CBPeripheralManagerDelegate.peripheralManager(_:didReceiveWrite:)` results
/// - Returns: Observable that emits `next` event whenever didReceiveWrite occurs.
Expand All @@ -110,10 +110,10 @@ public protocol BLEPeripheralManager {
/// * `BLEError.bluetoothPoweredOff`
/// * `BLEError.bluetoothInUnknownState`
/// * `BLEError.bluetoothResetting`
func observeDidReceiveWrite() -> AnyPublisher<[CBATTRequest], Never>
func observeDidReceiveWrite() -> AnyPublisher<[BLEATTRequest], Never>

/// Wrapper for `CBPeripheralManager.respond(to:withResult:)` method
func respond(to request: CBATTRequest, withResult result: CBATTError.Code)
func respond(to request: BLEATTRequest, withResult result: CBATTError.Code)

/// Wrapper for `CBPeripheralManager.updateValue(_:for:onSubscribedCentrals:)` method
func updateValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ final class BLEPeripheralManagerDelegate: NSObject, CBPeripheralManagerDelegate
let didUpdateState = PassthroughSubject<CBManagerState, Never>()
let isReady = PassthroughSubject<Void, Never>()
let didStartAdvertising = PassthroughSubject<Error?, Never>()
let didReceiveRead = PassthroughSubject<CBATTRequest, Never>()
let didReceiveRead = PassthroughSubject<BLEATTRequest, Never>()
let willRestoreState = CurrentValueSubject<[String: Any], Never>([:])
let didAddService = PassthroughSubject<(CBService, Error?), Never>()
let didReceiveWrite = PassthroughSubject<[CBATTRequest], Never>()
let didReceiveWrite = PassthroughSubject<[BLEATTRequest], Never>()
let didSubscribeTo = PassthroughSubject<(CBCentral, CBCharacteristic), Never>()
let didUnsubscribeFrom = PassthroughSubject<(CBCentral, CBCharacteristic), Never>()
let didPublishL2CAPChannel = PassthroughSubject<(CBL2CAPPSM, Error?), Never>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,17 @@ final class StandardBLEPeripheralManager: BLEPeripheralManager {

// MARK: Read & Write

func observeDidReceiveRead() -> AnyPublisher<CBATTRequest, Never> {
func observeDidReceiveRead() -> AnyPublisher<BLEATTRequest, Never> {
delegate.didReceiveRead.ensure(.poweredOn, manager: self)
}

func observeDidReceiveWrite() -> AnyPublisher<[CBATTRequest], Never> {
func observeDidReceiveWrite() -> AnyPublisher<[BLEATTRequest], Never> {
delegate.didReceiveWrite.ensure(.poweredOn, manager: self)
}

func respond(to request: CBATTRequest, withResult result: CBATTError.Code) {
manager.respond(to: request, withResult: result)
func respond(to request: BLEATTRequest, withResult result: CBATTError.Code) {
guard let validRequest = request.associatedRequest else { return }
manager.respond(to: validRequest, withResult: result)
}

// MARK: Updating value
Expand Down
21 changes: 21 additions & 0 deletions Tests/BLECombineKitTests/BLEPeripheralManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,25 @@ final class BLEPeripheralManagerTests: XCTestCase {
XCTAssertEqual(managerWrapper.removeAllServicesCount, 1)
}

func testObserveDidReceiveRead() throws {
// Given.
let expectation = XCTestExpectation(description: #function)
let mockRequest = MockBLEATTRequest()
var expectedRequest: BLEATTRequest?
managerWrapper.mutableState = .poweredOn

// When.
manager.observeDidReceiveRead()
.sink { request in
expectedRequest = request
expectation.fulfill()
}.store(in: &cancellables)
delegate.didReceiveRead.send(mockRequest)

// Then.
wait(for: [expectation], timeout: 0.01)
let validRequest = try XCTUnwrap(expectedRequest)
XCTAssertEqual(validRequest.centralWrapper.identifier, mockRequest.centralWrapper.identifier)
}

}
30 changes: 30 additions & 0 deletions Tests/BLECombineKitTests/Mocks/MockPeripheralManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright © 2024 Henry Serrano. All rights reserved.
//

import BLECombineKit
import CoreBluetooth

final class MockCBPeripheralManager: CBPeripheralManager {
Expand All @@ -29,3 +30,32 @@ final class MockCBPeripheralManager: CBPeripheralManager {
removeAllServicesCount += 1
}
}

final class MockBLECentral: BLECentral {
var associatedCentral: CBCentral?

var identifier = UUID()

var maximumUpdateValueLength: Int = 0
}

final class MockBLEATTRequest: BLEATTRequest {
var associatedRequest: CBATTRequest?

var centralWrapper: BLECentral = MockBLECentral()

var mutableCharacteristic = CBMutableCharacteristic(
type: CBUUID(string: "0x00FF"),
properties: .read,
value: nil,
permissions: .readable
)
var characteristic: CBCharacteristic {
mutableCharacteristic
}

var offset: Int = 0

var value: Data?

}

0 comments on commit f1e34d4

Please sign in to comment.