mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
[NC Apple coverage] Add unit tests for GNCMBleConnection.
PiperOrigin-RevId: 810761287
This commit is contained in:
committed by
Copybara-Service
parent
19dc77a78f
commit
d429a8e42d
@@ -37,6 +37,7 @@ objc_library(
|
||||
"GNCFakePeripheral.m",
|
||||
"GNCFakePeripheralManager.m",
|
||||
"GNCFakeSocket.m",
|
||||
"GNCMBleConnectionTest.m",
|
||||
"GNCMBleUtilsTest.m",
|
||||
"GNCMConnectionsTest.m",
|
||||
"GNCMFakeConnection.mm",
|
||||
|
||||
@@ -16,20 +16,40 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** A fake implementation of @c GNSSocket to inject for testing. */
|
||||
@interface GNCFakeSocket : NSObject
|
||||
|
||||
/** An expectation to fulfill when -sendData:progressHandler:completion: is called. */
|
||||
@property(nonatomic, nullable) XCTestExpectation *sendDataExpectation;
|
||||
|
||||
/** The socket's delegate. */
|
||||
@property(nonatomic, weak) id<GNSSocketDelegate> delegate;
|
||||
|
||||
/** The last data that was sent to the socket. */
|
||||
@property(nonatomic, copy, nullable) NSData *sentData;
|
||||
|
||||
/** The completion handler for the last sent data. */
|
||||
@property(nonatomic, nullable) GNSErrorHandler sentDataCompletion;
|
||||
|
||||
/** Simulates a socket connection event. */
|
||||
- (void)simulateSocketDidConnect;
|
||||
|
||||
/** Simulates a socket disconnection event. */
|
||||
- (void)simulateSocketDidDisconnectWithError:(nullable NSError *)error;
|
||||
|
||||
/** Simulates a socket receiving data event. */
|
||||
- (void)simulateSocketDidReceiveData:(NSData *)data;
|
||||
|
||||
- (void)sendData:(NSData *)data
|
||||
progressHandler:(GNSProgressHandler)progressHandler
|
||||
completion:(GNSErrorHandler)completion;
|
||||
|
||||
- (void)disconnect;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -18,7 +18,17 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation GNCFakeSocket
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
- (void)sendData:(NSData *)data
|
||||
progressHandler:(GNSProgressHandler)progressHandler
|
||||
completion:(GNSErrorHandler)completion {
|
||||
self.sentData = data;
|
||||
self.sentDataCompletion = completion;
|
||||
[self.sendDataExpectation fulfill];
|
||||
}
|
||||
|
||||
- (void)disconnect {
|
||||
// No-op for fake.
|
||||
}
|
||||
|
||||
- (void)simulateSocketDidConnect {
|
||||
[self.delegate socketDidConnect:(GNSSocket *)self];
|
||||
@@ -28,6 +38,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[self.delegate socket:(GNSSocket *)self didDisconnectWithError:error];
|
||||
}
|
||||
|
||||
- (void)simulateSocketDidReceiveData:(NSData *)data {
|
||||
[self.delegate socket:(GNSSocket *)self didReceiveData:data];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// 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 "internal/platform/implementation/apple/Mediums/BLE/GNCMBleConnection.h"
|
||||
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMBleUtils.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/GNCMConnection.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Sockets/Source/Shared/GNSSocket.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakeSocket.h"
|
||||
|
||||
static NSString *const kServiceID = @"TestServiceID";
|
||||
static const NSTimeInterval kTimeout = 1.0;
|
||||
|
||||
@interface GNCMBleConnectionTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCMBleConnectionTest {
|
||||
GNCFakeSocket *_fakeSocket;
|
||||
dispatch_queue_t _callbackQueue;
|
||||
GNCMBleConnection *_connection;
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
_fakeSocket = [[GNCFakeSocket alloc] init];
|
||||
_callbackQueue = dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
_connection = nil;
|
||||
_fakeSocket = nil;
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testCreateConnectionWithServiceID {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:kServiceID
|
||||
expectedIntroPacket:NO
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
XCTAssertNotNil(_connection);
|
||||
XCTAssertEqual(_fakeSocket.delegate, (id<GNSSocketDelegate>)_connection);
|
||||
}
|
||||
|
||||
- (void)testCreateConnectionWithoutServiceID {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:nil
|
||||
expectedIntroPacket:YES
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
XCTAssertNotNil(_connection);
|
||||
XCTAssertEqual(_fakeSocket.delegate, (id<GNSSocketDelegate>)_connection);
|
||||
}
|
||||
|
||||
- (void)testSendData {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:kServiceID
|
||||
expectedIntroPacket:NO
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
NSData *data = [@"TestData" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *serviceIDHash = GNCMServiceIDHash(kServiceID);
|
||||
NSMutableData *expectedPacket = [NSMutableData dataWithData:serviceIDHash];
|
||||
[expectedPacket appendData:data];
|
||||
|
||||
XCTestExpectation *sendDataExpectation = [self expectationWithDescription:@"Send data called"];
|
||||
_fakeSocket.sendDataExpectation = sendDataExpectation;
|
||||
|
||||
XCTestExpectation *completionExpectation = [self expectationWithDescription:@"Send completion"];
|
||||
|
||||
[_connection sendData:data
|
||||
progressHandler:^(size_t progress) {
|
||||
}
|
||||
completion:^(GNCMPayloadResult result) {
|
||||
XCTAssertEqual(result, GNCMPayloadResultSuccess);
|
||||
[completionExpectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ sendDataExpectation ] timeout:kTimeout];
|
||||
|
||||
XCTAssertEqualObjects(_fakeSocket.sentData, expectedPacket);
|
||||
if (_fakeSocket.sentDataCompletion) {
|
||||
_fakeSocket.sentDataCompletion(nil);
|
||||
}
|
||||
|
||||
[self waitForExpectations:@[ completionExpectation ] timeout:kTimeout];
|
||||
}
|
||||
|
||||
- (void)testSendDataEmpty {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:kServiceID
|
||||
expectedIntroPacket:NO
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
NSData *data = [NSData data];
|
||||
NSData *expectedPacket = GNCMGenerateBLEFramesIntroductionPacket(GNCMServiceIDHash(kServiceID));
|
||||
|
||||
XCTestExpectation *sendDataExpectation = [self expectationWithDescription:@"Send data called"];
|
||||
_fakeSocket.sendDataExpectation = sendDataExpectation;
|
||||
|
||||
XCTestExpectation *completionExpectation = [self expectationWithDescription:@"Send completion"];
|
||||
|
||||
[_connection sendData:data
|
||||
progressHandler:^(size_t progress) {
|
||||
}
|
||||
completion:^(GNCMPayloadResult result) {
|
||||
XCTAssertEqual(result, GNCMPayloadResultSuccess);
|
||||
[completionExpectation fulfill];
|
||||
}];
|
||||
|
||||
[self waitForExpectations:@[ sendDataExpectation ] timeout:kTimeout];
|
||||
|
||||
XCTAssertEqualObjects(_fakeSocket.sentData, expectedPacket);
|
||||
if (_fakeSocket.sentDataCompletion) {
|
||||
_fakeSocket.sentDataCompletion(nil);
|
||||
}
|
||||
|
||||
[self waitForExpectations:@[ completionExpectation ] timeout:kTimeout];
|
||||
}
|
||||
|
||||
- (void)testReceiveData {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:kServiceID
|
||||
expectedIntroPacket:NO
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
NSData *expectedData = [@"TestData" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *serviceIDHash = GNCMServiceIDHash(kServiceID);
|
||||
NSMutableData *packet = [NSMutableData dataWithData:serviceIDHash];
|
||||
[packet appendData:expectedData];
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Payload handler"];
|
||||
|
||||
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
|
||||
handlers.payloadHandler = ^(NSData *data) {
|
||||
XCTAssertEqualObjects(data, expectedData);
|
||||
[expectation fulfill];
|
||||
};
|
||||
_connection.connectionHandlers = handlers;
|
||||
|
||||
[_fakeSocket simulateSocketDidReceiveData:packet];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
- (void)testReceiveIntroPacketWithServiceID {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:kServiceID
|
||||
expectedIntroPacket:YES
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
NSData *introPacket = GNCMGenerateBLEFramesIntroductionPacket(GNCMServiceIDHash(kServiceID));
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Intro packet received"];
|
||||
expectation.inverted = YES; // Should not trigger payload handler
|
||||
|
||||
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
|
||||
handlers.payloadHandler = ^(NSData *data) {
|
||||
[expectation fulfill];
|
||||
};
|
||||
_connection.connectionHandlers = handlers;
|
||||
|
||||
[_fakeSocket simulateSocketDidReceiveData:introPacket];
|
||||
|
||||
// No data should be passed to the payload handler.
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
- (void)testReceiveIntroPacketWithoutServiceID {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:nil
|
||||
expectedIntroPacket:YES
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
NSData *introPacket = GNCMGenerateBLEFramesIntroductionPacket(GNCMServiceIDHash(kServiceID));
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Intro packet received"];
|
||||
expectation.inverted = YES; // Should not trigger payload handler
|
||||
|
||||
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
|
||||
handlers.payloadHandler = ^(NSData *data) {
|
||||
[expectation fulfill];
|
||||
};
|
||||
_connection.connectionHandlers = handlers;
|
||||
|
||||
[_fakeSocket simulateSocketDidReceiveData:introPacket];
|
||||
|
||||
// No data should be passed to the payload handler.
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
|
||||
// Now test receiving data after the intro packet.
|
||||
NSData *expectedData = [@"TestData" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *serviceIDHash = GNCMServiceIDHash(kServiceID);
|
||||
NSMutableData *packet = [NSMutableData dataWithData:serviceIDHash];
|
||||
[packet appendData:expectedData];
|
||||
|
||||
XCTestExpectation *dataExpectation = [self expectationWithDescription:@"Payload handler"];
|
||||
|
||||
GNCMConnectionHandlers *dataHandlers = [[GNCMConnectionHandlers alloc] init];
|
||||
dataHandlers.payloadHandler = ^(NSData *data) {
|
||||
XCTAssertEqualObjects(data, expectedData);
|
||||
[dataExpectation fulfill];
|
||||
};
|
||||
_connection.connectionHandlers = dataHandlers;
|
||||
|
||||
[_fakeSocket simulateSocketDidReceiveData:packet];
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
- (void)testDisconnect {
|
||||
_connection = [GNCMBleConnection connectionWithSocket:(GNSSocket *)_fakeSocket
|
||||
serviceID:kServiceID
|
||||
expectedIntroPacket:NO
|
||||
callbackQueue:_callbackQueue];
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Disconnected handler"];
|
||||
|
||||
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
|
||||
handlers.disconnectedHandler = ^{
|
||||
[expectation fulfill];
|
||||
};
|
||||
_connection.connectionHandlers = handlers;
|
||||
|
||||
[_fakeSocket simulateSocketDidDisconnectWithError:nil];
|
||||
|
||||
[self waitForExpectationsWithTimeout:kTimeout handler:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user