Skip to content

Commit

Permalink
Merge pull request #24 from NordicSemiconductor/develop
Browse files Browse the repository at this point in the history
Version 0.11.1
  • Loading branch information
philips77 authored Aug 7, 2020
2 parents b9baa3a + 05ee6a7 commit 1562143
Show file tree
Hide file tree
Showing 16 changed files with 267 additions and 44 deletions.
2 changes: 1 addition & 1 deletion CoreBluetoothMock.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'CoreBluetoothMock'
s.version = '0.11.0'
s.version = '0.11.1'
s.summary = 'Mocking library for CoreBluetooth.'

s.description = <<-DESC
Expand Down
57 changes: 51 additions & 6 deletions CoreBluetoothMock/Classes/CBMCentralManagerMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ public class CBMCentralManagerMock: NSObject, CBMCentralManager {
if proximity == .outOfRange {
self.peripheral(peripheral,
didDisconnectWithError: CBMError(.connectionTimeout))
} else {
self.peripheralBecameAvailable(peripheral)
}
}

Expand Down Expand Up @@ -320,6 +322,32 @@ public class CBMCentralManagerMock: NSObject, CBMCentralManager {
// TODO: notify a user registered for connection events
}

/// Method called when a peripheral becomes available (in range).
/// If there is a pending connection request, it will connect.
/// - Parameter peripheral: The peripheral that came in range.
internal static func peripheralBecameAvailable(_ peripheral: CBMPeripheralSpec) {
// Is the peripheral simulated?
guard peripherals.contains(peripheral) else {
return
}
managers
.compactMap { $0.ref }
.forEach { manager in
if let target = manager.peripherals[peripheral.identifier],
target.state == .connecting {
target.connect() { result in
switch result {
case .success:
manager.delegate?.centralManager(manager, didConnect: target)
case .failure(let error):
manager.delegate?.centralManager(manager, didFailToConnect: target,
error: error)
}
}
}
}
}

/// Simulates the peripheral to disconnect from the device.
/// All connected mock central managers will receive
/// `peripheral(:didDisconnected:error)` callback.
Expand Down Expand Up @@ -477,7 +505,7 @@ public class CBMCentralManagerMock: NSObject, CBMCentralManager {
guard peripherals.values.contains(mock) else {
return
}
mock.connect { result in
mock.connect() { result in
switch result {
case .success:
self.delegate?.centralManager(self, didConnect: mock)
Expand Down Expand Up @@ -667,30 +695,47 @@ public class CBMPeripheralMock: CBMPeer, CBMPeripheral {
// MARK: Connection

fileprivate func connect(completion: @escaping (Result<Void, Error>) -> ()) {
// Ensure the device is connectable and disconnected.
// Ensure the device is disconnected.
guard state == .disconnected || state == .connecting else {
return
}
// Connection is pending.
state = .connecting
// Ensure the device is connectable and in range.
guard let delegate = mock.connectionDelegate,
let interval = mock.connectionInterval,
state == .disconnected else {
mock.proximity != .outOfRange else {
// There's no timeout on iOS. The device will connect when brought back
// into range. To cancel pending connection, call disconnect().
return
}
state = .connecting
let result = delegate.peripheralDidReceiveConnectionRequest(mock)
queue.asyncAfter(deadline: .now() + interval) { [weak self] in
if let self = self, self.state == .connecting {
if case .success = result {
self.state = .connected
self._canSendWriteWithoutResponse = true
self.mock.virtualConnections += 1
} else {
self.state = .disconnected
}
completion(result)
}
}
}

fileprivate func disconnect(completion: @escaping () -> ()) {
// Ensure the device is connected.
// Cancel pending connection.
guard state != .connecting else {
state = .disconnected
queue.async {
completion()
}
return
}
// Ensure the device is connectable and connected.
guard let interval = mock.connectionInterval,
state == .connected || state == .connecting else {
state == .connected else {
return
}
if #available(iOS 9.0, *), case .connected = state {
Expand Down
4 changes: 2 additions & 2 deletions CoreBluetoothMock/Classes/CBMPeripheralSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ public class CBMPeripheralSpec {
///
/// All connected mock central managers will receive
/// `peripheral(:didDisconnected:error)` callback.
/// - Parameter error: The disconnection reason. Use `CBError` or
/// `CBATTError` errors.
/// - Parameter error: The disconnection reason. Use `CBMError` or
/// `CBMATTError` errors.
public func simulateDisconnection(withError error: Error = CBMError(.peripheralDisconnected)) {
CBMCentralManagerMock.peripheral(self, didDisconnectWithError: error)
}
Expand Down
4 changes: 2 additions & 2 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
PODS:
- CoreBluetoothMock (0.11.0)
- CoreBluetoothMock (0.11.1)

DEPENDENCIES:
- CoreBluetoothMock (from `../`)
Expand All @@ -9,7 +9,7 @@ EXTERNAL SOURCES:
:path: "../"

SPEC CHECKSUMS:
CoreBluetoothMock: 158792f5f41671d5c61b882c38e804f35e0b9339
CoreBluetoothMock: f201b575c3eb3f0753124d088595c6fe2d6dceec

PODFILE CHECKSUM: bfd9fc7193b211c18bbe632884c30bc5d6a4807c

Expand Down
4 changes: 2 additions & 2 deletions Example/Pods/Local Podspecs/CoreBluetoothMock.podspec.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Example/Pods/Manifest.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
105 changes: 105 additions & 0 deletions Example/Tests/FailedConnectionTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2020, Nordic Semiconductor
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


import XCTest
@testable import nRF_Blinky
@testable import CoreBluetoothMock

/// This test simulates a scenario when a device failed to connect for some reason.
///
/// It is using the app and testing it by sending notifications that trigger different
/// actions.
class FailedConnectionTest: XCTestCase {

override func setUp() {
// This method is called AFTER ScannerTableViewController.viewDidLoad()
// where the BlinkyManager is instantiated. A separate mock manager
// is not created in this test.
// Initially mock Bluetooth adapter is powered Off.
CBMCentralManagerMock.simulatePeripherals([blinky, hrm, thingy])
CBMCentralManagerMock.simulateInitialState(.poweredOn)
}

override func tearDown() {
// We can't call CBMCentralManagerMock.tearDownSimulation() here.
// That would invalidate the BlinkyManager in ScannerTableViewController.
// The central manager must be reused, so let's just power mock off,
// which will allow us to set different set of peripherals in another test.
CBMCentralManagerMock.simulatePowerOff()
}

func testScanningBlinky() {
// Set up the devices in range.
blinky.simulateProximityChange(.immediate)
hrm.simulateProximityChange(.near)
thingy.simulateProximityChange(.far)
// Reset the blinky.
blinky.simulateReset()

// Wait until the blinky is found.
var target: BlinkyPeripheral?
let found = XCTestExpectation(description: "Device found")
Sim.onBlinkyDiscovery { blinky in
XCTAssertEqual(blinky.advertisedName, "nRF Blinky")
XCTAssert(blinky.isConnectable == true)
XCTAssert(blinky.isConnected == false)
XCTAssertGreaterThanOrEqual(blinky.RSSI.intValue, -70 - 15)
XCTAssertLessThanOrEqual(blinky.RSSI.intValue, -70 + 15)
target = blinky
found.fulfill()
}
wait(for: [found], timeout: 3)
XCTAssertNotNil(target, "nRF Blinky not found. Make sure you run the test on a simulator.")
if target == nil {
// Going further would cause a crash.
return
}

// Let's move Blinky out of range.
blinky.simulateProximityChange(.outOfRange)

// Select found device.
Sim.post(.selectPeripheral(at: 0))

// Wait until blinky is connected and ready.
let connected = XCTestExpectation(description: "Connected")
connected.isInverted = true
target!.onConnected {
connected.fulfill()
}
wait(for: [connected], timeout: 3)

let appDelegate = UIApplication.shared.delegate as! AppDelegate
let navigationController = appDelegate.window!.rootViewController as! UINavigationController
navigationController.popViewController(animated: true)
}

}
3 changes: 2 additions & 1 deletion Example/Tests/NormalBehaviorTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ class NormalBehaviorTest: XCTestCase {

// Simulate graceful disconnect.
let disconnection = XCTestExpectation(description: "Disconnection")
target!.onDisconnected {
target!.onDisconnected { error in
XCTAssertEqual((error as? CBMError)?.code, CBError.peripheralDisconnected)
disconnection.fulfill()
}
blinky.simulateDisconnection()
Expand Down
3 changes: 2 additions & 1 deletion Example/Tests/ResetTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ class ResetTest: XCTestCase {

// Simulate reset and button press afterwards.
let reset = XCTestExpectation(description: "Reset")
target!.onDisconnected {
target!.onDisconnected { error in
XCTAssertEqual((error as? CBMError)?.code, CBError.connectionTimeout)
reset.fulfill()
}
blinky.simulateReset()
Expand Down
22 changes: 13 additions & 9 deletions Example/nRFBlinky.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

/* Begin PBXBuildFile section */
2DBB24A841D22CDDB14787B6 /* BlinkyPeripheralEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB2D0A6C475A3BA45D6FAA /* BlinkyPeripheralEvents.swift */; };
2DBB258568F7E07E0DFA3B4E /* EventInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB2F5ACA6DCE530B0DDE90 /* EventInterceptor.swift */; };
2DBB258568F7E07E0DFA3B4E /* EventHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB2F5ACA6DCE530B0DDE90 /* EventHelper.swift */; };
2DBB2A694C3D00999D8B3E29 /* BlinkyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB27B357E95965525A70A5 /* BlinkyManager.swift */; };
2DBB2D9D87AA009D95C10DEE /* ResetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB22CFA82E35CBA3BB326F /* ResetTest.swift */; };
2DBB2F44478EB7F4C0DF3AA4 /* BlinkyManagerEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DBB27ABD18F3DAECC075CF2 /* BlinkyManagerEvents.swift */; };
Expand All @@ -28,6 +28,7 @@
52B537AF23FBE84900372948 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52B5379223FBE84900372948 /* LaunchScreen.storyboard */; };
52B537B023FBE84900372948 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52B5379423FBE84900372948 /* Main.storyboard */; };
52B537B223FBE85100372948 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B537B123FBE85100372948 /* AppDelegate.swift */; };
52E905CE24DD35A100BB5740 /* FailedConnectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52E905CD24DD35A100BB5740 /* FailedConnectionTest.swift */; };
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
7B2BC15014F59F63362D632E /* Pods_nRFBlinky.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D451FCAEA31E307D873AF9E /* Pods_nRFBlinky.framework */; };
9C3DAF3BDCE839F729AB18D4 /* Pods_nRFBlinky_nRFBlinky_UITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FAE5CCCE10D0C6B6720ACAB6 /* Pods_nRFBlinky_nRFBlinky_UITests.framework */; };
Expand Down Expand Up @@ -56,7 +57,7 @@
2DBB27ABD18F3DAECC075CF2 /* BlinkyManagerEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlinkyManagerEvents.swift; sourceTree = "<group>"; };
2DBB27B357E95965525A70A5 /* BlinkyManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlinkyManager.swift; sourceTree = "<group>"; };
2DBB2D0A6C475A3BA45D6FAA /* BlinkyPeripheralEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlinkyPeripheralEvents.swift; sourceTree = "<group>"; };
2DBB2F5ACA6DCE530B0DDE90 /* EventInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventInterceptor.swift; sourceTree = "<group>"; };
2DBB2F5ACA6DCE530B0DDE90 /* EventHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventHelper.swift; sourceTree = "<group>"; };
453BB604F04AB8D6B8030CE1 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
526EA53A240D2F8100BF70B2 /* nRFBlinky_UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = nRFBlinky_UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
526EA53C240D2F8100BF70B2 /* UITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -109,6 +110,7 @@
52B537A323FBE84900372948 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = "<group>"; };
52B537B123FBE85100372948 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
52B537D723FC129400372948 /* MockPeripherals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPeripherals.swift; sourceTree = "<group>"; };
52E905CD24DD35A100BB5740 /* FailedConnectionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FailedConnectionTest.swift; sourceTree = "<group>"; };
593E9FFC1917DE0521FBC97F /* Pods-nRFBlinky_UITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-nRFBlinky_UITests.debug.xcconfig"; path = "Target Support Files/Pods-nRFBlinky_UITests/Pods-nRFBlinky_UITests.debug.xcconfig"; sourceTree = "<group>"; };
607FACD01AFB9204008FA782 /* nRF Blinky.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "nRF Blinky.app"; sourceTree = BUILT_PRODUCTS_DIR; };
607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -176,9 +178,10 @@
isa = PBXGroup;
children = (
52A648F0240E83F000817F2F /* NormalBehaviorTest.swift */,
52A648F8240E844700817F2F /* Supporting Files */,
2DBB2F5ACA6DCE530B0DDE90 /* EventInterceptor.swift */,
2DBB22CFA82E35CBA3BB326F /* ResetTest.swift */,
52E905CD24DD35A100BB5740 /* FailedConnectionTest.swift */,
2DBB2F5ACA6DCE530B0DDE90 /* EventHelper.swift */,
52A648F8240E844700817F2F /* Supporting Files */,
);
path = Tests;
sourceTree = "<group>";
Expand Down Expand Up @@ -606,7 +609,8 @@
buildActionMask = 2147483647;
files = (
52A648F1240E83F000817F2F /* NormalBehaviorTest.swift in Sources */,
2DBB258568F7E07E0DFA3B4E /* EventInterceptor.swift in Sources */,
2DBB258568F7E07E0DFA3B4E /* EventHelper.swift in Sources */,
52E905CE24DD35A100BB5740 /* FailedConnectionTest.swift in Sources */,
2DBB2D9D87AA009D95C10DEE /* ResetTest.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -939,13 +943,13 @@
baseConfigurationReference = FDD05DB6B9B51CA381BE6886 /* Pods-nRFBlinky.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 2;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = P3R8YQEV4L;
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
INFOPLIST_FILE = nRFBlinky/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.3.0;
MARKETING_VERSION = 1.3.1;
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "com.nordicsemi.nrf-blinky";
PRODUCT_NAME = "nRF Blinky";
Expand All @@ -960,12 +964,12 @@
baseConfigurationReference = E20CFB7929AACA783363B9F4 /* Pods-nRFBlinky.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = 2;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = P3R8YQEV4L;
INFOPLIST_FILE = nRFBlinky/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.3.0;
MARKETING_VERSION = 1.3.1;
MODULE_NAME = ExampleApp;
PRODUCT_BUNDLE_IDENTIFIER = "com.nordicsemi.nrf-blinky";
PRODUCT_NAME = "nRF Blinky";
Expand Down
Loading

0 comments on commit 1562143

Please sign in to comment.