From 2f6cff0f085fe3833a8af496642a9279c854adfa Mon Sep 17 00:00:00 2001 From: Edwin Wu Date: Wed, 11 Mar 2026 08:21:50 -0700 Subject: [PATCH] Refactor GATT/L2CAP servers to support shared peripheral manager PiperOrigin-RevId: 882024313 --- .../flags/nearby_connections_feature_flags.h | 4 + .../apple/Flags/GNCFeatureFlags.h | 3 + .../apple/Flags/GNCFeatureFlags.mm | 6 + .../apple/Flags/Tests/GNCFeatureFlagsTest.mm | 37 ++ .../apple/Mediums/BLE/GNCBLEGATTServer.h | 15 +- .../apple/Mediums/BLE/GNCBLEGATTServer.m | 51 +- .../apple/Mediums/BLE/GNCBLEL2CAPServer.h | 6 +- .../apple/Mediums/BLE/GNCBLEL2CAPServer.m | 65 ++- .../apple/Mediums/BLE/GNCBLEMedium.m | 42 +- .../apple/Mediums/BLE/Tests/BUILD | 6 +- ...TTServerTest.m => GNCBLEGATTServerTest.mm} | 466 +++++++++++++---- .../Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m | 329 ------------ .../BLE/Tests/GNCBLEL2CAPServerTest.mm | 485 ++++++++++++++++++ .../apple/Tests/ble_medium_test.mm | 4 +- 14 files changed, 1024 insertions(+), 495 deletions(-) rename internal/platform/implementation/apple/Mediums/BLE/Tests/{GNCBLEGATTServerTest.m => GNCBLEGATTServerTest.mm} (60%) delete mode 100644 internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m create mode 100644 internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.mm diff --git a/connections/implementation/flags/nearby_connections_feature_flags.h b/connections/implementation/flags/nearby_connections_feature_flags.h index 8dbe6dbc..f241106d 100644 --- a/connections/implementation/flags/nearby_connections_feature_flags.h +++ b/connections/implementation/flags/nearby_connections_feature_flags.h @@ -104,6 +104,10 @@ constexpr auto kMediumMaxAllowedReadBytes = // Disable/Enable refactor of BLE/L2CAP in Nearby Connections SDK. constexpr auto kRefactorBleL2cap = flags::Flag(kConfigPackage, "45737079", false); +// Enable/Disable usage of shared CBPeripheralManager for GATT and L2CAP +// servers. +constexpr auto kEnableSharedPeripheralManager = + flags::Flag(kConfigPackage, "45770787", false); // Set the safe-to-disconnect version. // 0. Disabled all. 1. safe-to-disconnect 2. reserved 3. auto-reconnect // 4. auto-resume 5. non-distance-constraint-recovery 6. payload_ack diff --git a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h index 7d75da5d..e31a9b94 100644 --- a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h +++ b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.h @@ -29,4 +29,7 @@ /** Checks whether BLE L2CAP refactor is enabled in the Nearby Connections SDK. */ @property(nonatomic, class, readonly) BOOL refactorBleL2capEnabled; +/** Checks whether shared peripheral manager is enabled in the Nearby Connections SDK. */ +@property(nonatomic, class, readonly) BOOL sharedPeripheralManagerEnabled; + @end diff --git a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm index 548518e8..1955a263 100644 --- a/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm +++ b/internal/platform/implementation/apple/Flags/GNCFeatureFlags.mm @@ -42,4 +42,10 @@ kRefactorBleL2cap); } ++ (BOOL)sharedPeripheralManagerEnabled { + return nearby::NearbyFlags::GetInstance().GetBoolFlag( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager); +} + @end diff --git a/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm b/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm index 767eebfb..59784339 100644 --- a/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm +++ b/internal/platform/implementation/apple/Flags/Tests/GNCFeatureFlagsTest.mm @@ -34,6 +34,13 @@ nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( nearby::connections::config_package_nearby::nearby_connections_feature::kEnableBleL2cap, false); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + false); + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + false); [super tearDown]; } @@ -78,4 +85,34 @@ XCTAssertFalse([GNCFeatureFlags bleL2capEnabled]); } +- (void)testRefactorBleL2capEnabled_WhenFlagIsTrue { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + YES); + XCTAssertTrue([GNCFeatureFlags refactorBleL2capEnabled]); +} + +- (void)testRefactorBleL2capEnabled_WhenFlagIsFalse { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature::kRefactorBleL2cap, + NO); + XCTAssertFalse([GNCFeatureFlags refactorBleL2capEnabled]); +} + +- (void)testSharedPeripheralManagerEnabled_WhenFlagIsTrue { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + YES); + XCTAssertTrue([GNCFeatureFlags sharedPeripheralManagerEnabled]); +} + +- (void)testSharedPeripheralManagerEnabled_WhenFlagIsFalse { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + NO); + XCTAssertFalse([GNCFeatureFlags sharedPeripheralManagerEnabled]); +} + @end diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h index 1b94731a..5ee0cad3 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.h @@ -15,6 +15,8 @@ #import #import +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h" + @class GNCBLEGATTCharacteristic; NS_ASSUME_NONNULL_BEGIN @@ -57,7 +59,18 @@ typedef void (^GNCStopAdvertisingCompletionHandler)(NSError *_Nullable error); * * @note The public APIs of this class are thread safe. */ -@interface GNCBLEGATTServer : NSObject +@interface GNCBLEGATTServer : NSObject + +/** + * Initializes the GATT server. + * + * @param peripheralManager The peripheral manager to use. + * @param queue The queue to use for delegate callbacks and internal operations. + */ +- (instancetype)initWithPeripheralManager:(nullable id)peripheralManager + queue:(nullable dispatch_queue_t)queue; + +- (instancetype)init NS_UNAVAILABLE; /** * Creates a characteristic and adds it to the GATT server. diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m index 6193a7ad..1b9035ad 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEGATTServer.m @@ -59,30 +59,33 @@ static const int kMaxAdvertisementLengthOnIOS = 23; NSDictionary *_advertisementData; } -- (instancetype)init { +- (instancetype)initWithPeripheralManager:(nullable id)peripheralManager + queue:(nullable dispatch_queue_t)queue { self = [super init]; if (self) { - _queue = dispatch_queue_create(kGNCBLEGATTServerQueueLabel, DISPATCH_QUEUE_SERIAL); - _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil queue:_queue]; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; - _services = [[NSMutableDictionary alloc] init]; - _pendingCharacteristics = [[NSMutableDictionary alloc] init]; - _characteristicValues = [[NSMutableDictionary alloc] init]; - _advertisementData = nil; - } - return self; -} + _queue = queue ?: dispatch_queue_create(kGNCBLEGATTServerQueueLabel, DISPATCH_QUEUE_SERIAL); + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + if (!peripheralManager) { + // In shared mode, the peripheral manager must be injected. + [NSException raise:NSInvalidArgumentException + format:@"Peripheral manager cannot be nil when shared manager is enabled."]; + } + _peripheralManager = peripheralManager; + // In shared mode, do NOT set the delegate. The Multiplexer handles callbacks. + } else { + // Legacy mode: Create a new manager if one isn't provided. + if (!peripheralManager) { + peripheralManager = [[CBPeripheralManager alloc] + initWithDelegate:self + queue:_queue + options:@{CBPeripheralManagerOptionShowPowerAlertKey : @NO}]; + } + _peripheralManager = peripheralManager; + // In legacy mode, we own the manager (or use the injected one as if we own it) and set the + // delegate. + _peripheralManager.peripheralDelegate = self; + } -// This is private and should only be used for tests. The provided peripheral manager must call -// delegate methods on the main queue. -- (instancetype)initWithPeripheralManager:(nullable id)peripheralManager { - self = [super init]; - if (self) { - _queue = dispatch_get_main_queue(); - _peripheralManager = peripheralManager; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; _services = [[NSMutableDictionary alloc] init]; _pendingCharacteristics = [[NSMutableDictionary alloc] init]; _characteristicValues = [[NSMutableDictionary alloc] init]; @@ -342,6 +345,12 @@ static const int kMaxAdvertisementLengthOnIOS = 23; - (void)gnc_peripheralManager:(id)peripheral didReceiveReadRequest:(CBATTRequest *)request { dispatch_assert_queue(_queue); + if (!_services[request.characteristic.service.UUID]) { + // This server does not own the requested service. Ignore the request to allow other listeners + // (or future listeners) to handle it. + return; + } + NSData *value = _characteristicValues[request.characteristic.service.UUID][request.characteristic.UUID]; if (!value) { diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h index be602889..285bb0ce 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.h @@ -15,6 +15,8 @@ #import #import +#import "internal/platform/implementation/apple/Mediums/BLE/GNCPeripheralManager.h" + @class GNCBLEL2CAPStream; @protocol GNCPeripheralManager; @@ -42,7 +44,7 @@ typedef void (^GNCOpenL2CAPServerChannelOpendCompletionHandler)(GNCBLEL2CAPStrea * * @note The public APIs of this class are thread safe. */ -@interface GNCBLEL2CAPServer : NSObject +@interface GNCBLEL2CAPServer : NSObject // Represents a PSM (Protocol/Service Multiplexer) value for an L2CAP channel. @property(atomic, readonly) CBL2CAPPSM PSM; @@ -57,6 +59,8 @@ typedef void (^GNCOpenL2CAPServerChannelOpendCompletionHandler)(GNCBLEL2CAPStrea - (instancetype)initWithPeripheralManager:(nullable id)peripheralManager queue:(nullable dispatch_queue_t)queue; +- (instancetype)init NS_UNAVAILABLE; + /** * Starts listening for an L2CAP channel. * diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m index e33f458a..fc454feb 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPServer.m @@ -17,6 +17,7 @@ #import #import +#import "internal/platform/implementation/apple/Flags/GNCFeatureFlags.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEError.h" #import "internal/platform/implementation/apple/Mediums/BLE/GNCBLEL2CAPStream.h" @@ -43,21 +44,25 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP BOOL _alreadyStartedWhenPeripheralPoweredOff; } -- (instancetype)init { - return [self initWithPeripheralManager:nil queue:nil]; -} - -// This is private and should only be used for tests. The provided peripheral manager must call -// delegate methods on the main queue. - (instancetype)initWithPeripheralManager:(nullable id)peripheralManager queue:(nullable dispatch_queue_t)queue { self = [super init]; if (self) { _queue = queue ?: dispatch_queue_create(kGNCBLEL2CAPServerQueueLabel, DISPATCH_QUEUE_SERIAL); - if (peripheralManager) { + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + if (!peripheralManager) { + // In shared mode, the peripheral manager must be injected. + [NSException raise:NSInvalidArgumentException + format:@"Peripheral manager cannot be nil when shared manager is enabled."]; + } _peripheralManager = peripheralManager; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; + // In shared mode, do NOT set the delegate. The Multiplexer handles callbacks. + } else { + if (peripheralManager) { + _peripheralManager = peripheralManager; + // Set for @c GNCPeripheralManager to be able to forward callbacks. + _peripheralManager.peripheralDelegate = self; + } } } return self; @@ -70,20 +75,36 @@ static char *const kGNCBLEL2CAPServerQueueLabel = "com.google.nearby.GNCBLEL2CAP channelOpenedCompletionHandler { _psmPublishedCompletionHandler = [psmPublishedCompletionHandler copy]; _channelOpenedCompletionHandler = [channelOpenedCompletionHandler copy]; - if (!_queue) { - _psmPublishedCompletionHandler(0, [NSError errorWithDomain:GNCBLEErrorDomain - code:GNCBLEErrorL2CAPListeningOnQueueNil - userInfo:nil]); - return; - } - if (!_peripheralManager) { - // Lazy initialization to avoid system dialog on app startup before pairing. - _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil - queue:_queue - options:nil]; + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + if (!_queue) { + if (_psmPublishedCompletionHandler) { + _psmPublishedCompletionHandler(0, + [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorL2CAPListeningOnQueueNil + userInfo:nil]); + } + return; + } + if (!_peripheralManager) { + GNCLoggerError(@"[NEARBY] Peripheral manager must not be nil."); + return; + } + } else { + if (!_queue) { + _psmPublishedCompletionHandler(0, [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorL2CAPListeningOnQueueNil + userInfo:nil]); + return; + } + if (!_peripheralManager) { + // Lazy initialization to avoid system dialog on app startup before pairing. + _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:nil + queue:_queue + options:nil]; - // Set for @c GNCPeripheralManager to be able to forward callbacks. - _peripheralManager.peripheralDelegate = self; + // Set for @c GNCPeripheralManager to be able to forward callbacks. + _peripheralManager.peripheralDelegate = self; + } } if (_peripheralManager.state == CBManagerStatePoweredOn) { diff --git a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m index a6276c3c..b4ab5364 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m +++ b/internal/platform/implementation/apple/Mediums/BLE/GNCBLEMedium.m @@ -40,16 +40,6 @@ static NSError *AlreadyScanningError() { return [NSError errorWithDomain:GNCBLEErrorDomain code:GNCBLEErrorAlreadyScanning userInfo:nil]; } -static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( - id _Nullable peripheralManager) { - if (!peripheralManager) { - return [[GNCBLEL2CAPServer alloc] init]; - } else { - return [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:peripheralManager - queue:dispatch_get_main_queue()]; - } -} - @interface GNCBLEMedium () @end @@ -164,7 +154,16 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler { dispatch_async(_queue, ^{ if (!_server) { - _server = [[GNCBLEGATTServer alloc] init]; + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + // TODO (edwinwu): Implement shared peripheral manager. + // For now, raise an exception. + [NSException raise:NSInvalidArgumentException + format:@"Not implemented for shared manager is enabled."]; + } else { + // In legacy mode, we pass nil (or a separate manager) and do NOT add to multiplexer. + // GNCBLEGATTServer will create its own internal manager. + _server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil]; + } } [_server startAdvertisingData:serviceData completionHandler:completionHandler]; }); @@ -238,7 +237,14 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( (nullable GNCGATTServerCompletionHandler)completionHandler { dispatch_async(_queue, ^{ if (!_server) { - _server = [[GNCBLEGATTServer alloc] init]; + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + // TODO (edwinwu): Implement shared peripheral manager. + // For now, raise an exception. + [NSException raise:NSInvalidArgumentException + format:@"Not implemented for shared manager is enabled."]; + } else { + _server = [[GNCBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil]; + } } if (completionHandler) { completionHandler(_server, nil); @@ -284,7 +290,17 @@ static GNCBLEL2CAPServer *_Nonnull CreateL2CapServer( (nullable id)peripheralManager { dispatch_async(_queue, ^{ if (!_l2capServer) { - _l2capServer = CreateL2CapServer(peripheralManager); + if (GNCFeatureFlags.sharedPeripheralManagerEnabled) { + // TODO (edwinwu): Implement shared peripheral manager. + // For now, raise an exception. + [NSException raise:NSInvalidArgumentException + format:@"Not implemented for shared manager is enabled."]; + } else { + // Legacy mode + _l2capServer = [[GNCBLEL2CAPServer alloc] + initWithPeripheralManager:peripheralManager // Likely nil, so Server creates new one + queue:peripheralManager ? dispatch_get_main_queue() : nil]; + } } [_l2capServer startListeningChannelWithPSMPublishedCompletionHandler:psmPublishedCompletionHandler diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD b/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD index 86e1be56..2b56b470 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/BUILD @@ -26,11 +26,11 @@ objc_library( srcs = [ "GNCBLEGATTCharacteristicTest.mm", "GNCBLEGATTClientTest.m", - "GNCBLEGATTServerTest.m", + "GNCBLEGATTServerTest.mm", "GNCBLEL2CAPClientTest.m", "GNCBLEL2CAPConnectionTest.m", "GNCBLEL2CAPFakeInputOutputStream.m", - "GNCBLEL2CAPServerTest.m", + "GNCBLEL2CAPServerTest.mm", "GNCBLEL2CAPStreamTest.m", "GNCBLEMediumTest.m", "GNCFakeBLEGATTServer.m", @@ -66,6 +66,8 @@ objc_library( "GNCMFakeConnection.h", ], deps = [ + "//connections/implementation/flags:connections_flags", + "//internal/flags:nearby_flags", "//internal/platform/implementation/apple", # buildcleaner: keep "//internal/platform/implementation/apple/Mediums/BLE", "//internal/platform/implementation/apple/Mediums/BLE/Sockets:Shared", diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.mm similarity index 60% rename from internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.m rename to internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.mm index f6bfac51..8e4882fd 100644 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.m +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServerTest.mm @@ -22,6 +22,9 @@ #import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEGATTServer+Testing.h" #import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" + static NSString *const kServiceUUID1 = @"0000FEF3-0000-1000-8000-00805F9B34FB"; static NSString *const kServiceUUID2 = @"0000FEF4-0000-1000-8000-00805F9B34FB"; static NSString *const kCharacteristicUUID1 = @"00000000-0000-3000-8000-000000000000"; @@ -34,11 +37,60 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 #pragma mark - Create Characteristic -- (void)testCreateCharacteristic { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; +- (void)tearDown { + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); + [super tearDown]; +} - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; +- (void)testInit_setsDelegateCorrectlyBasedOnFlag { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEGATTServer *server = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + + if (enabled.boolValue) { + // In shared mode, functionality is delegated to the multiplexer, so the server should NOT + // self-assign as delegate. + XCTAssertNil(fakePeripheralManager.peripheralDelegate); + } else { + // In legacy mode, the server owns the manager and sets itself as delegate. + XCTAssertEqual(fakePeripheralManager.peripheralDelegate, server); + } + } +} + +- (void)testInit_throwsWithNilManagerWhenFlagEnabled { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + true); + + XCTAssertThrowsSpecificNamed( + [[GNCBLEGATTServer alloc] initWithPeripheralManager:(id)nil + queue:dispatch_get_main_queue()], + NSException, NSInvalidArgumentException); +} + +- (void)testCreateCharacteristic { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -62,13 +114,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testCreateMultipleCharacteristicsForOneService { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -105,13 +167,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation1, expectation2 ] timeout:3]; XCTAssertEqual(fakePeripheralManager.services.count, 1); XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 2); + } } - (void)testCreateDuplicateCharacteristics { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -140,13 +212,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation ] timeout:3]; XCTAssertEqual(fakePeripheralManager.services.count, 1); XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 1); + } } - (void)testCreateDuplicatePendingCharacteristics { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; @@ -171,13 +253,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testCreateCharacteristicNotPoweredOn { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; @@ -198,13 +290,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testCreateCharacteristicServiceFailure { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } fakePeripheralManager.didAddServiceError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -228,15 +330,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } #pragma mark - Read Request - (void)testReadRequest { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -265,14 +377,24 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 characteristic:characteristicUUID]; [self waitForExpectations:@[ fakePeripheralManager.respondToRequestSuccessExpectation ] - timeout:0]; + timeout:3]; + } } -- (void)testReadRequestInvalidService { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; +- (void)testReadRequestInvalidCharacteristic { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -296,73 +418,106 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation ] timeout:3]; + CBUUID *invalidCharacteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID2]; + + [fakePeripheralManager + simulatePeripheralManagerDidReceiveReadRequestForService:serviceUUID + characteristic:invalidCharacteristicUUID]; + + [self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:3]; + } +} + +- (void)testReadRequestInvalidService { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + CBUUID *invalidServiceUUID = [CBUUID UUIDWithString:kServiceUUID2]; + // Expectation that we *should not* receive a response. + fakePeripheralManager.respondToRequestSuccessExpectation.inverted = YES; + fakePeripheralManager.respondToRequestErrorExpectation.inverted = YES; + [fakePeripheralManager simulatePeripheralManagerDidReceiveReadRequestForService:invalidServiceUUID characteristic:characteristicUUID]; - [self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:0]; -} - -- (void)testReadRequestInvalidCharacteristic { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"Create and update characteristic."]; - - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; - CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead - completionHandler:^(GNCBLEGATTCharacteristic *characteristic, - NSError *error) { - [gattServer updateCharacteristic:characteristic - value:[NSData data] - completionHandler:^(NSError *error) { - [expectation fulfill]; - }]; - }]; - - [self waitForExpectations:@[ expectation ] timeout:3]; - - CBUUID *invalidCharacteristicUUID = - [CBUUID UUIDWithString:kCharacteristicUUID2]; - - [fakePeripheralManager - simulatePeripheralManagerDidReceiveReadRequestForService:serviceUUID - characteristic:invalidCharacteristicUUID]; - - [self waitForExpectations:@[ fakePeripheralManager.respondToRequestErrorExpectation ] timeout:0]; + [self waitForExpectations:@[ + fakePeripheralManager.respondToRequestSuccessExpectation, + fakePeripheralManager.respondToRequestErrorExpectation + ] + timeout:3]; + } } #pragma mark - Stop - (void)testStop { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } - [gattServer stop]; + [gattServer stop]; - XCTAssertEqual(fakePeripheralManager.services.count, 0); + XCTAssertEqual(fakePeripheralManager.services.count, 0); + } } #pragma mark - Start Advertising - (void)testStartAdvertisingNoServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -377,13 +532,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingEmptyServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -402,13 +567,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingShortServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -429,13 +604,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertising20ByteServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -457,13 +642,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingLongServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -482,13 +677,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingWithEmojiServiceData { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -510,13 +715,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingMultipleServices { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -536,13 +751,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingNotPoweredOn { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } XCTestExpectation *expectation = [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; @@ -556,13 +781,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingStartFailure { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } fakePeripheralManager.didStartAdvertisingError = [NSError errorWithDomain:@"fake" code:0 @@ -581,13 +816,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartAdvertisingAlreadyAdvertising { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } fakePeripheralManager.didStartAdvertisingError = [NSError errorWithDomain:@"fake" code:0 @@ -608,13 +853,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } - (void)testStartStopStartAdvertising { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = gattServer; + } [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; @@ -648,6 +903,9 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 }]; [self waitForExpectations:@[ expectation ] timeout:3]; + } } @end + + diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m deleted file mode 100644 index 2380d287..00000000 --- a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.m +++ /dev/null @@ -1,329 +0,0 @@ -// 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/GNCBLEL2CAPServer.h" - -#import -#import -#import - -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServer+Testing.h" -#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" - -static const NSTimeInterval kTestTimeout = 1.0; - -@interface GNCBLEL2CAPServerTest : XCTestCase -@end - -@implementation GNCBLEL2CAPServerTest - -#pragma mark Tests - -- (void)testInit { - GNCBLEL2CAPServer *l2capServer = [[GNCBLEL2CAPServer alloc] init]; - XCTAssertNotNil(l2capServer); - XCTAssertNil([l2capServer valueForKey:@"_peripheralManager"]); -} - -- (void)testPublishL2CAPChannelAndOpenChannelWhenStartListeningChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *psmPublishedExpectation = - [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - [psmPublishedExpectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - XCTAssertNil(error); - XCTAssertNotNil(stream); - [channelOpenedexpectation fulfill]; - }]; - [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] - timeout:kTestTimeout]; - XCTAssertNotNil([l2capServer valueForKey:@"l2CAPChannel"]); - XCTAssertNotNil([l2capServer valueForKey:@"l2CAPStream"]); -} - -- (void)testFailedToPublishL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - fakePeripheralManager.didPublishL2CAPChannelError = [NSError errorWithDomain:@"fake" - code:0 - userInfo:nil]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *psmPublishedExpectation = - [[XCTestExpectation alloc] initWithDescription:@"PSM published with error."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, fakePeripheralManager.didPublishL2CAPChannelError); - XCTAssertEqual(PSM, 0); - [psmPublishedExpectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - [self waitForExpectations:@[ psmPublishedExpectation ] timeout:kTestTimeout]; -} - -- (void)testPoweredOffUnpublishesChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOff]; - - [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; -} - -- (void)testPeripheralManagerDidUpdateStatePoweredOffUnpublishesChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - fakePeripheralManager.state = CBManagerStatePoweredOn; - [(id)l2capServer - peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - fakePeripheralManager.state = CBManagerStatePoweredOff; - [(id)l2capServer - peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; - - [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; -} - -- (void)testStartPeripheralManagerInitiallyOff { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *psmPublishedExpectation = - [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - [psmPublishedExpectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - [channelOpenedexpectation fulfill]; - }]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] - timeout:kTestTimeout]; -} - -- (void)testClose { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - [l2capServer close]; - - XCTAssertEqual([l2capServer PSM], 0); -} - -- (void)testCloseDoesNotUnpublishesChannelIfNotConnected { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - fakePeripheralManager.unpublishExpectation.inverted = YES; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - - [l2capServer close]; - - [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; -} - -- (void)testFailedToOpenL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - fakePeripheralManager.didOpenL2CAPChannelError = [NSError errorWithDomain:@"fake" - code:0 - userInfo:nil]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(error, nil); - XCTAssertEqual(PSM, fakePeripheralManager.PSM); - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - XCTAssertNil(stream); - XCTAssertEqual(error, fakePeripheralManager.didOpenL2CAPChannelError); - [channelOpenedexpectation fulfill]; - }]; - [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; - XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); - XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); -} - -- (void)testPeripheralManagerDidUnpublishL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - - [(id)l2capServer - peripheralManager:(CBPeripheralManager *)fakePeripheralManager - didUnpublishL2CAPChannel:l2capServer.PSM - error:[NSError errorWithDomain:@"fake" code:0 userInfo:nil]]; - - XCTAssertEqual(l2capServer.PSM, 0); -} - -- (void)testCloseL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *channelOpenedexpectation = - [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error) { - [channelOpenedexpectation fulfill]; - }]; - [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; - - [l2capServer closeL2CAPChannel]; - - XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); - XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); -} - -- (void)testPeripheralManagerDidPublishL2CAPChannel { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"completion called"]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(PSM, 1); - XCTAssertNil(error); - [expectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - [(id)l2capServer - peripheralManager:(CBPeripheralManager *)fakePeripheralManager - didPublishL2CAPChannel:1 - error:nil]; - [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; - XCTAssertEqual(l2capServer.PSM, 1); -} - -- (void)testPeripheralManagerDidPublishL2CAPChannelWithError { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - GNCBLEL2CAPServer *l2capServer = - [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager - queue:dispatch_get_main_queue()]; - XCTestExpectation *expectation = - [[XCTestExpectation alloc] initWithDescription:@"completion called"]; - NSError *publishError = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; - [l2capServer - startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, - NSError *_Nullable error) { - XCTAssertEqual(PSM, 0); - XCTAssertEqualObjects(error, publishError); - [expectation fulfill]; - } - channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, - NSError *_Nullable error){ - }]; - [(id)l2capServer - peripheralManager:(CBPeripheralManager *)fakePeripheralManager - didPublishL2CAPChannel:0 - error:publishError]; - [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; - XCTAssertEqual(l2capServer.PSM, 0); -} - -@end diff --git a/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.mm b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.mm new file mode 100644 index 00000000..2d2fabda --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServerTest.mm @@ -0,0 +1,485 @@ +// 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/GNCBLEL2CAPServer.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCBLEL2CAPServer+Testing.h" +#import "internal/platform/implementation/apple/Mediums/BLE/Tests/GNCFakePeripheralManager.h" + +#include "connections/implementation/flags/nearby_connections_feature_flags.h" +#include "internal/flags/nearby_flags.h" + +static const NSTimeInterval kTestTimeout = 1.0; + +@interface GNCBLEL2CAPServerTest : XCTestCase +@end + +@implementation GNCBLEL2CAPServerTest + +#pragma mark Tests + +- (void)tearDown { + nearby::NearbyFlags::GetInstance().ResetOverridedValues(); + [super tearDown]; +} + +- (void)testInit_setsDelegateCorrectlyBasedOnFlag { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *server = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + + if (enabled.boolValue) { + // In shared mode, functionality is delegated to the multiplexer, so the server should NOT + // self-assign as delegate. + XCTAssertNil(fakePeripheralManager.peripheralDelegate); + } else { + // In legacy mode, the server owns the manager and sets itself as delegate. + XCTAssertEqual(fakePeripheralManager.peripheralDelegate, server); + } + } +} + +- (void)testInit_throwsWithNilManagerWhenFlagEnabled { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + true); + + XCTAssertThrowsSpecificNamed( + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:(id)nil + queue:dispatch_get_main_queue()], + NSException, NSInvalidArgumentException); +} + +- (void)testPublishL2CAPChannelAndOpenChannelWhenStartListeningChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *psmPublishedExpectation = + [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + [psmPublishedExpectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + XCTAssertNil(error); + XCTAssertNotNil(stream); + [channelOpenedexpectation fulfill]; + }]; + [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] + timeout:kTestTimeout]; + XCTAssertNotNil([l2capServer valueForKey:@"l2CAPChannel"]); + XCTAssertNotNil([l2capServer valueForKey:@"l2CAPStream"]); + } +} + +- (void)testFailedToPublishL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + fakePeripheralManager.didPublishL2CAPChannelError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *psmPublishedExpectation = + [[XCTestExpectation alloc] initWithDescription:@"PSM published with error."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, fakePeripheralManager.didPublishL2CAPChannelError); + XCTAssertEqual(PSM, 0); + [psmPublishedExpectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + [self waitForExpectations:@[ psmPublishedExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testPoweredOffUnpublishesChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOff]; + + [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testPeripheralManagerDidUpdateStatePoweredOffUnpublishesChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + fakePeripheralManager.state = CBManagerStatePoweredOn; + [(id)l2capServer + peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + fakePeripheralManager.state = CBManagerStatePoweredOff; + [(id)l2capServer + peripheralManagerDidUpdateState:(CBPeripheralManager *)fakePeripheralManager]; + + [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testStartPeripheralManagerInitiallyOff { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *psmPublishedExpectation = + [[XCTestExpectation alloc] initWithDescription:@"PSM published."]; + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + [psmPublishedExpectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + [channelOpenedexpectation fulfill]; + }]; + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + [self waitForExpectations:@[ psmPublishedExpectation, channelOpenedexpectation ] + timeout:kTestTimeout]; + } +} + +- (void)testClose { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + [l2capServer close]; + + XCTAssertEqual([l2capServer PSM], 0); + } +} + +- (void)testCloseDoesNotUnpublishesChannelIfNotConnected { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + fakePeripheralManager.unpublishExpectation.inverted = YES; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [l2capServer close]; + + [self waitForExpectations:@[ fakePeripheralManager.unpublishExpectation ] timeout:kTestTimeout]; + } +} + +- (void)testFailedToOpenL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + fakePeripheralManager.didOpenL2CAPChannelError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(error, nil); + XCTAssertEqual(PSM, fakePeripheralManager.PSM); + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + XCTAssertNil(stream); + XCTAssertEqual(error, fakePeripheralManager.didOpenL2CAPChannelError); + [channelOpenedexpectation fulfill]; + }]; + [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; + XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); + XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); + } +} + +- (void)testPeripheralManagerDidUnpublishL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + + [(id)l2capServer + peripheralManager:(CBPeripheralManager *)fakePeripheralManager + didUnpublishL2CAPChannel:l2capServer.PSM + error:[NSError errorWithDomain:@"fake" code:0 userInfo:nil]]; + + XCTAssertEqual(l2capServer.PSM, 0); + } +} + +- (void)testCloseL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *channelOpenedexpectation = + [[XCTestExpectation alloc] initWithDescription:@"Channel opened."]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error) { + [channelOpenedexpectation fulfill]; + }]; + [self waitForExpectations:@[ channelOpenedexpectation ] timeout:kTestTimeout]; + + [l2capServer closeL2CAPChannel]; + + XCTAssertNil([l2capServer valueForKey:@"l2CAPChannel"]); + XCTAssertNil([l2capServer valueForKey:@"l2CAPStream"]); + } +} + +- (void)testPeripheralManagerDidPublishL2CAPChannel { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"completion called"]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(PSM, 1); + XCTAssertNil(error); + [expectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + [(id)l2capServer + peripheralManager:(CBPeripheralManager *)fakePeripheralManager + didPublishL2CAPChannel:1 + error:nil]; + [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; + XCTAssertEqual(l2capServer.PSM, 1); + } +} + +- (void)testPeripheralManagerDidPublishL2CAPChannelWithError { + for (NSNumber *enabled in @[ @NO, @YES ]) { + nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue( + nearby::connections::config_package_nearby::nearby_connections_feature:: + kEnableSharedPeripheralManager, + enabled.boolValue); + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + GNCBLEL2CAPServer *l2capServer = + [[GNCBLEL2CAPServer alloc] initWithPeripheralManager:fakePeripheralManager + queue:dispatch_get_main_queue()]; + if (enabled.boolValue) { + fakePeripheralManager.peripheralDelegate = l2capServer; + } + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"completion called"]; + NSError *publishError = [NSError errorWithDomain:@"test" code:0 userInfo:nil]; + [l2capServer + startListeningChannelWithPSMPublishedCompletionHandler:^(uint16_t PSM, + NSError *_Nullable error) { + XCTAssertEqual(PSM, 0); + XCTAssertEqualObjects(error, publishError); + [expectation fulfill]; + } + channelOpenedCompletionHandler:^(GNCBLEL2CAPStream *_Nullable stream, + NSError *_Nullable error){ + }]; + [(id)l2capServer + peripheralManager:(CBPeripheralManager *)fakePeripheralManager + didPublishL2CAPChannel:0 + error:publishError]; + [self waitForExpectations:@[ expectation ] timeout:kTestTimeout]; + XCTAssertEqual(l2capServer.PSM, 0); + } +} + +@end + diff --git a/internal/platform/implementation/apple/Tests/ble_medium_test.mm b/internal/platform/implementation/apple/Tests/ble_medium_test.mm index b628c3f3..23a79dd9 100644 --- a/internal/platform/implementation/apple/Tests/ble_medium_test.mm +++ b/internal/platform/implementation/apple/Tests/ble_medium_test.mm @@ -225,8 +225,8 @@ static const char *const kTestServiceID = "TestServiceID"; #pragma mark - GATT Server Tests - (void)testStartGattServer_Success { - _fakeGNCBLEMedium.fakeGATTServer = [[GNCFakeBLEGATTServer alloc] init]; - + _fakeGNCBLEMedium.fakeGATTServer = + [[GNCFakeBLEGATTServer alloc] initWithPeripheralManager:nil queue:nil]; auto gatt_server = _medium->StartGattServer({}); XCTAssertNotEqual(gatt_server.get(), nullptr);