Add completion handler to opened L2CAP channel for iOS client.

PiperOrigin-RevId: 753363177
This commit is contained in:
Edwin Wu
2025-04-30 16:49:59 -07:00
committed by Copybara-Service
parent 541bffc858
commit fc0768f2c4
11 changed files with 228 additions and 49 deletions
@@ -28,6 +28,7 @@ objc_library(
"GNCBLEGATTClientTest.m",
"GNCBLEGATTServer+Testing.h",
"GNCBLEGATTServerTest.m",
"GNCBLEL2CAPClient+Testing.h",
"GNCBLEL2CAPClientTest.m",
"GNCBLEL2CAPConnectionTest.m",
"GNCBLEL2CAPFakeInputOutputStream.h",
@@ -0,0 +1,42 @@
// 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/BLEv2/GNCBLEL2CAPClient.h"
#import <Foundation/Foundation.h>
@protocol GNCPeripheral;
NS_ASSUME_NONNULL_BEGIN
@interface GNCBLEL2CAPClient (Testing)
/**
* Creates a L2CAP client with a provided peripheral.
*
* This is only exposed for testing and can be used to inject a fake peripheral.
*
* @param peripheral The peripheral instance.
* @param queue The queue to run on, this must match the queue that the peripheral's delegate is
* running on. Defaults to the main queue when @c nil.
* @param requestDisconnectionHandler Called on a private queue with @c peripheral when the
* connection to the peripheral should be cancelled.
*/
-(instancetype)initWithPeripheral:(id<GNCPeripheral>)peripheral
queue:(nullable dispatch_queue_t)queue
requestDisconnectionHandler:(GNCRequestDisconnectionHandler)requestDisconnectionHandler;
@end
NS_ASSUME_NONNULL_END
@@ -19,30 +19,75 @@
#import <XCTest/XCTest.h>
#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h"
#import "internal/platform/implementation/apple/Tests/GNCBLEL2CAPClient+Testing.h"
#import "internal/platform/implementation/apple/Tests/GNCFakePeripheral.h"
@interface GNCBLEL2CAPClientTest : XCTestCase
@property(nonatomic) GNCFakePeripheral *fakePeripheral;
@property(nonatomic) GNCBLEL2CAPClient *l2capClient;
@property(nonatomic) XCTestExpectation *requestDisconnectionHandlerExpectation;
@end
@implementation GNCBLEL2CAPClientTest
#pragma mark - Tests
- (void)testDisconnect {
GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init];
XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Disconnect."];
GNCBLEL2CAPClient *l2capClient =
[[GNCBLEL2CAPClient alloc] initWithPeripheral:fakePeripheral
- (void)setUp {
[super setUp];
self.fakePeripheral = [[GNCFakePeripheral alloc] init];
self.l2capClient =
[[GNCBLEL2CAPClient alloc] initWithPeripheral:self.fakePeripheral
queue:nil
requestDisconnectionHandler:^(id<GNCPeripheral> _Nonnull peripheral) {
XCTAssertNotNil(peripheral);
[expectation fulfill];
[self.requestDisconnectionHandlerExpectation fulfill];
}];
}
[l2capClient disconnect];
- (void)tearDown {
self.fakePeripheral = nil;
self.l2capClient = nil;
[super tearDown];
}
[self waitForExpectations:@[ expectation ] timeout:3];
- (void)testSuccessfulOpenL2CAPChannel {
XCTestExpectation *expectation =
[self expectationWithDescription:@"L2CAP channel opened successfully"];
uint16_t psm = 123;
[self.l2capClient openL2CAPChannelWithPSM:psm
completionHandler:^(GNCBLEL2CAPStream *stream, NSError *error) {
XCTAssertNil(error, @"Error should be nil");
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:1.0];
}
- (void)testOpenL2CAPChannelWithError {
XCTestExpectation *expectation =
[self expectationWithDescription:@"L2CAP channel open failed with error"];
uint16_t psm = 123;
NSError *expectedError = [NSError errorWithDomain:@"TestErrorDomain" code:1 userInfo:nil];
self.fakePeripheral.openL2CAPChannelError = expectedError;
[self.l2capClient openL2CAPChannelWithPSM:psm
completionHandler:^(GNCBLEL2CAPStream *stream, NSError *error) {
XCTAssertNotNil(error, @"Error should not be nil");
XCTAssertEqualObjects(error, expectedError);
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:1.0];
}
- (void)testDisconnect {
self.requestDisconnectionHandlerExpectation =
[self expectationWithDescription:@"Request disconnection handler should be called"];
[self.l2capClient disconnect];
[self waitForExpectations:@[ self.requestDisconnectionHandlerExpectation ] timeout:1.0];
}
@end
@@ -108,6 +108,19 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
- (void)openL2CAPChannel:(CBL2CAPPSM)PSM {
[self delayDelegateUsingBlock:^() {
CBL2CAPChannel *channel = [[CBL2CAPChannel alloc] init];
if (_openL2CAPChannelError) {
[peripheralDelegate gnc_peripheral:self
didOpenL2CAPChannel:channel
error:_openL2CAPChannelError];
} else {
[peripheralDelegate gnc_peripheral:self didOpenL2CAPChannel:channel error:nil];
}
}];
}
- (void)delayDelegateUsingBlock:(void (^)())block {
if (_delegateDelay <= 0) {
block();
@@ -119,18 +132,6 @@ NS_ASSUME_NONNULL_BEGIN
}
}
- (void)openL2CAPChannelWithPSM:(uint16_t)PSM {
[self delayDelegateUsingBlock:^() {
if (_openL2CAPChannelError) {
CBL2CAPChannel *channel = [[CBL2CAPChannel alloc] init];
[peripheralDelegate gnc_peripheral:self
didOpenL2CAPChannel:channel
error:_openL2CAPChannelError];
}
// TODO: b/399815436 - Add testing for L2CAP channels if error is nil.
}];
}
@end
NS_ASSUME_NONNULL_END