mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
[NC Apple coverage] Refactor: Wrap Network.framework C functions for testability. Part II.
PiperOrigin-RevId: 831136979
This commit is contained in:
committed by
Copybara-Service
parent
2695dd4089
commit
50de2233f5
@@ -19,6 +19,12 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GNCNWBrowseResultImpl : NSObject <GNCNWBrowseResult>
|
||||
|
||||
/**
|
||||
* Returns the shared singleton instance of this class.
|
||||
*/
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -20,6 +20,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation GNCNWBrowseResultImpl
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static GNCNWBrowseResultImpl *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[self alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (void)enumerateInterfaces:(nw_browse_result_t)browseResult
|
||||
usingBlock:(nw_browse_result_enumerate_interface_t)block {
|
||||
nw_browse_result_enumerate_interfaces(browseResult, block);
|
||||
|
||||
@@ -22,6 +22,40 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@protocol GNCNWConnection <NSObject>
|
||||
|
||||
/**
|
||||
* Creates a connection to an endpoint.
|
||||
*
|
||||
* @param endpoint The endpoint to connect to.
|
||||
* @param parameters The parameters to use for the connection.
|
||||
* @return The new connection object.
|
||||
*/
|
||||
- (nw_connection_t)createConnectionWithEndpoint:(nw_endpoint_t)endpoint
|
||||
parameters:(nw_parameters_t)parameters;
|
||||
|
||||
/**
|
||||
* Sets the dispatch queue for the connection.
|
||||
*
|
||||
* @param queue The dispatch queue to use for the connection.
|
||||
*/
|
||||
- (void)setQueue:(dispatch_queue_t)queue;
|
||||
|
||||
/**
|
||||
* Sets the state changed handler for the connection.
|
||||
*
|
||||
* @param handler The state changed handler to use for the connection.
|
||||
*/
|
||||
- (void)setStateChangedHandler:(nw_connection_state_changed_handler_t)handler;
|
||||
|
||||
/**
|
||||
* Starts the connection.
|
||||
*/
|
||||
- (void)start;
|
||||
|
||||
/**
|
||||
* Cancels the connection.
|
||||
*/
|
||||
- (void)cancel;
|
||||
|
||||
@optional
|
||||
/**
|
||||
* Initializes the connection object with a Network.framework connection object.
|
||||
@@ -34,7 +68,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* Receives a message from the connection.
|
||||
*
|
||||
* @param minIncompleteLength The minimum number of bytes to receive before the completion handler is called.
|
||||
* @param minIncompleteLength The minimum number of bytes to receive before the completion handler
|
||||
* is called.
|
||||
* @param maxLengths The maximum number of bytes to receive.
|
||||
* @param handler The completion handler to call when the receive is complete or an error occurs.
|
||||
*/
|
||||
@@ -57,11 +92,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
isComplete:(BOOL)isComplete
|
||||
completionHandler:(void (^)(nw_error_t _Nullable error))handler;
|
||||
|
||||
/**
|
||||
* Cancels the connection.
|
||||
*/
|
||||
- (void)cancel;
|
||||
|
||||
/**
|
||||
* Returns the underlying nw_connection_t object.
|
||||
*
|
||||
|
||||
@@ -25,8 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@interface GNCNWConnectionImpl : NSObject <GNCNWConnection>
|
||||
|
||||
- (instancetype)initWithNWConnection:(nw_connection_t)connection NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithNWConnection:(nw_connection_t)connection;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -31,6 +31,31 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nw_connection_t)createConnectionWithEndpoint:(nw_endpoint_t)endpoint
|
||||
parameters:(nw_parameters_t)parameters {
|
||||
if (_connection) {
|
||||
nw_connection_cancel(_connection);
|
||||
}
|
||||
_connection = nw_connection_create(endpoint, parameters);
|
||||
return _connection;
|
||||
}
|
||||
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
nw_connection_set_queue(_connection, queue);
|
||||
}
|
||||
|
||||
- (void)setStateChangedHandler:(nw_connection_state_changed_handler_t)handler {
|
||||
nw_connection_set_state_changed_handler(_connection, handler);
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
nw_connection_start(_connection);
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
nw_connection_cancel(_connection);
|
||||
}
|
||||
|
||||
- (void)receiveMessageWithMinLength:(uint32_t)minIncompleteLength
|
||||
maxLength:(uint32_t)maxLength
|
||||
completionHandler:(void (^)(dispatch_data_t _Nullable content,
|
||||
@@ -46,10 +71,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
nw_connection_send(_connection, content, context, isComplete, handler);
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
nw_connection_cancel(_connection);
|
||||
}
|
||||
|
||||
- (nullable nw_connection_t)nwConnection {
|
||||
return _connection;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
|
||||
#import "internal/platform/implementation/apple/Log/GNCLogger.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowseResult.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowseResultImpl.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowser.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowserImpl.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnection.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnectionImpl.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkServerSocket+Internal.h"
|
||||
@@ -36,31 +41,36 @@ NSTimeInterval const GNCStartDiscoveryTimeoutInSeconds = 0.5;
|
||||
|
||||
BOOL GNCHasLoopbackInterface(nw_browse_result_t browseResult) {
|
||||
__block BOOL isLoopback = NO;
|
||||
nw_browse_result_enumerate_interfaces(browseResult, ^bool(nw_interface_t interface) {
|
||||
nw_interface_type_t type = nw_interface_get_type(interface);
|
||||
if (type == nw_interface_type_loopback) {
|
||||
isLoopback = YES;
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
});
|
||||
id<GNCNWBrowseResult> wrapper = [GNCNWBrowseResultImpl sharedInstance];
|
||||
[wrapper enumerateInterfaces:browseResult
|
||||
usingBlock:^bool(nw_interface_t interface) {
|
||||
nw_interface_type_t type = [wrapper interfaceGetType:interface];
|
||||
if (type == nw_interface_type_loopback) {
|
||||
isLoopback = YES;
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
return isLoopback;
|
||||
}
|
||||
|
||||
NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_result_t browseResult) {
|
||||
nw_txt_record_t txt_record = nw_browse_result_copy_txt_record_object(browseResult);
|
||||
id<GNCNWBrowseResult> wrapper = [GNCNWBrowseResultImpl sharedInstance];
|
||||
nw_txt_record_t txt_record = [wrapper copyTXTRecordObject:browseResult];
|
||||
NSMutableDictionary<NSString *, NSString *> *txtRecords =
|
||||
[[NSMutableDictionary<NSString *, NSString *> alloc] init];
|
||||
nw_txt_record_apply(txt_record, ^bool(const char *key, const nw_txt_record_find_key_t found,
|
||||
const uint8_t *value, const size_t value_len) {
|
||||
if (found == nw_txt_record_find_key_non_empty_value) {
|
||||
NSString *valueString = [[NSString alloc] initWithBytes:value
|
||||
length:value_len
|
||||
encoding:NSUTF8StringEncoding];
|
||||
[txtRecords setValue:valueString forKey:@(key)];
|
||||
}
|
||||
return YES;
|
||||
});
|
||||
[wrapper applyTXTRecord:txt_record
|
||||
block:^bool(const char *key, const nw_txt_record_find_key_t found,
|
||||
const uint8_t *value, const size_t value_len) {
|
||||
if (found == nw_txt_record_find_key_non_empty_value) {
|
||||
NSString *valueString =
|
||||
[[NSString alloc] initWithBytes:value
|
||||
length:value_len
|
||||
encoding:NSUTF8StringEncoding];
|
||||
[txtRecords setValue:valueString forKey:@(key)];
|
||||
}
|
||||
return YES;
|
||||
}];
|
||||
return txtRecords;
|
||||
}
|
||||
|
||||
@@ -189,71 +199,90 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
|
||||
nw_browse_descriptor_t descriptor =
|
||||
nw_browse_descriptor_create_bonjour_service([serviceType UTF8String], /*domain=*/nil);
|
||||
nw_browse_descriptor_set_include_txt_record(descriptor, YES);
|
||||
nw_browser_t browser = nw_browser_create(descriptor, parameters);
|
||||
|
||||
nw_browser_set_queue(browser, _dispatchQueue);
|
||||
id<GNCNWBrowser> browserWrapper = [[GNCNWBrowserImpl alloc] init];
|
||||
nw_browser_t browser = [browserWrapper createWithDescriptor:descriptor parameters:parameters];
|
||||
|
||||
nw_browser_set_browse_results_changed_handler(browser, ^(nw_browse_result_t old_result,
|
||||
nw_browse_result_t new_result,
|
||||
bool batch_complete) {
|
||||
nw_browse_result_change_t change = nw_browse_result_get_changes(old_result, new_result);
|
||||
switch (change) {
|
||||
case nw_browse_result_change_identical:
|
||||
case nw_browse_result_change_interface_added:
|
||||
case nw_browse_result_change_interface_removed:
|
||||
break;
|
||||
case nw_browse_result_change_result_added: {
|
||||
// If the service has a loopback interface, we probably discovered our own
|
||||
// advertisement, so we ignore it.
|
||||
BOOL hasLoopback = GNCHasLoopbackInterface(new_result);
|
||||
if (hasLoopback) {
|
||||
GNCLoggerInfo(@"Ignoring a service discovered with loopback interface.");
|
||||
break;
|
||||
}
|
||||
nw_endpoint_t endpoint = nw_browse_result_copy_endpoint(new_result);
|
||||
NSString *name = @(nw_endpoint_get_bonjour_service_name(endpoint));
|
||||
NSDictionary<NSString *, NSString *> *txtRecords = GNCTXTRecordForBrowseResult(new_result);
|
||||
serviceFoundHandler(name, txtRecords);
|
||||
break;
|
||||
}
|
||||
case nw_browse_result_change_txt_record_changed: {
|
||||
// If the service has a loopback interface, we probably lost our own advertisement, so
|
||||
// we ignore it.
|
||||
BOOL oldHasLoopback = GNCHasLoopbackInterface(old_result);
|
||||
BOOL newHasLoopback = GNCHasLoopbackInterface(new_result);
|
||||
if (oldHasLoopback || newHasLoopback) {
|
||||
GNCLoggerInfo(@"Ignoring a service change with loopback interface.");
|
||||
break;
|
||||
}
|
||||
nw_endpoint_t old_endpoint = nw_browse_result_copy_endpoint(old_result);
|
||||
NSString *oldName = @(nw_endpoint_get_bonjour_service_name(old_endpoint));
|
||||
NSDictionary<NSString *, NSString *> *oldTXTRecords =
|
||||
GNCTXTRecordForBrowseResult(old_result);
|
||||
serviceLostHandler(oldName, oldTXTRecords);
|
||||
[browserWrapper setQueue:browser queue:_dispatchQueue];
|
||||
|
||||
nw_endpoint_t new_endpoint = nw_browse_result_copy_endpoint(new_result);
|
||||
NSString *newName = @(nw_endpoint_get_bonjour_service_name(new_endpoint));
|
||||
NSDictionary<NSString *, NSString *> *newTXTRecords =
|
||||
GNCTXTRecordForBrowseResult(new_result);
|
||||
serviceFoundHandler(newName, newTXTRecords);
|
||||
break;
|
||||
}
|
||||
case nw_browse_result_change_result_removed: {
|
||||
// If the service has a loopback interface, we probably lost our own advertisement, so
|
||||
// we ignore it.
|
||||
BOOL hasLoopback = GNCHasLoopbackInterface(old_result);
|
||||
if (hasLoopback) {
|
||||
GNCLoggerInfo(@"Ignoring a service lost with loopback interface.");
|
||||
break;
|
||||
}
|
||||
nw_endpoint_t endpoint = nw_browse_result_copy_endpoint(old_result);
|
||||
NSString *name = @(nw_endpoint_get_bonjour_service_name(endpoint));
|
||||
NSDictionary<NSString *, NSString *> *txtRecords = GNCTXTRecordForBrowseResult(old_result);
|
||||
serviceLostHandler(name, txtRecords);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
[browserWrapper
|
||||
setBrowseResultsChangedHandler:browser
|
||||
handler:^(nw_browse_result_t old_result, nw_browse_result_t new_result,
|
||||
bool batch_complete) {
|
||||
id<GNCNWBrowseResult> browseResultWrapper =
|
||||
[GNCNWBrowseResultImpl sharedInstance];
|
||||
nw_browse_result_change_t change =
|
||||
[browseResultWrapper getChangesFrom:old_result to:new_result];
|
||||
switch (change) {
|
||||
case nw_browse_result_change_identical:
|
||||
case nw_browse_result_change_interface_added:
|
||||
case nw_browse_result_change_interface_removed:
|
||||
break;
|
||||
case nw_browse_result_change_result_added: {
|
||||
// If the service has a loopback interface, we probably
|
||||
// discovered our own advertisement, so we ignore it.
|
||||
BOOL hasLoopback = GNCHasLoopbackInterface(new_result);
|
||||
if (hasLoopback) {
|
||||
GNCLoggerInfo(
|
||||
@"Ignoring a service discovered with loopback interface.");
|
||||
break;
|
||||
}
|
||||
nw_endpoint_t endpoint =
|
||||
[browseResultWrapper copyEndpointFromResult:new_result];
|
||||
NSString *name = [browseResultWrapper
|
||||
getBonjourServiceNameFromEndpoint:endpoint];
|
||||
NSDictionary<NSString *, NSString *> *txtRecords =
|
||||
GNCTXTRecordForBrowseResult(new_result);
|
||||
serviceFoundHandler(name, txtRecords);
|
||||
break;
|
||||
}
|
||||
case nw_browse_result_change_txt_record_changed: {
|
||||
// If the service has a loopback interface, we probably lost our
|
||||
// own advertisement, so we ignore it.
|
||||
BOOL oldHasLoopback = GNCHasLoopbackInterface(old_result);
|
||||
BOOL newHasLoopback = GNCHasLoopbackInterface(new_result);
|
||||
if (oldHasLoopback || newHasLoopback) {
|
||||
GNCLoggerInfo(
|
||||
@"Ignoring a service change with loopback interface.");
|
||||
break;
|
||||
}
|
||||
nw_endpoint_t old_endpoint =
|
||||
[browseResultWrapper copyEndpointFromResult:old_result];
|
||||
NSString *oldName = [browseResultWrapper
|
||||
getBonjourServiceNameFromEndpoint:old_endpoint];
|
||||
NSDictionary<NSString *, NSString *> *oldTXTRecords =
|
||||
GNCTXTRecordForBrowseResult(old_result);
|
||||
serviceLostHandler(oldName, oldTXTRecords);
|
||||
|
||||
nw_endpoint_t new_endpoint =
|
||||
[browseResultWrapper copyEndpointFromResult:new_result];
|
||||
NSString *newName = [browseResultWrapper
|
||||
getBonjourServiceNameFromEndpoint:new_endpoint];
|
||||
NSDictionary<NSString *, NSString *> *newTXTRecords =
|
||||
GNCTXTRecordForBrowseResult(new_result);
|
||||
serviceFoundHandler(newName, newTXTRecords);
|
||||
break;
|
||||
}
|
||||
case nw_browse_result_change_result_removed: {
|
||||
// If the service has a loopback interface, we probably lost our
|
||||
// own advertisement, so we ignore it.
|
||||
BOOL hasLoopback = GNCHasLoopbackInterface(old_result);
|
||||
if (hasLoopback) {
|
||||
GNCLoggerInfo(
|
||||
@"Ignoring a service lost with loopback interface.");
|
||||
break;
|
||||
}
|
||||
nw_endpoint_t endpoint =
|
||||
[browseResultWrapper copyEndpointFromResult:old_result];
|
||||
NSString *name = [browseResultWrapper
|
||||
getBonjourServiceNameFromEndpoint:endpoint];
|
||||
NSDictionary<NSString *, NSString *> *txtRecords =
|
||||
GNCTXTRecordForBrowseResult(old_result);
|
||||
serviceLostHandler(name, txtRecords);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
NSCondition *condition = [[NSCondition alloc] init];
|
||||
[condition lock];
|
||||
@@ -261,18 +290,20 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
|
||||
__block nw_browser_state_t blockResult = nw_browser_state_invalid;
|
||||
__block NSError *blockError = nil;
|
||||
|
||||
nw_browser_set_state_changed_handler(browser, ^(nw_browser_state_t state, nw_error_t error) {
|
||||
[condition lock];
|
||||
blockResult = state;
|
||||
if (error != nil) {
|
||||
blockError = (__bridge_transfer NSError *)nw_error_copy_cf_error(error);
|
||||
}
|
||||
[condition signal];
|
||||
[condition unlock];
|
||||
});
|
||||
[browserWrapper setStateChangedHandler:browser
|
||||
handler:^(nw_browser_state_t state, nw_error_t error) {
|
||||
[condition lock];
|
||||
blockResult = state;
|
||||
if (error != nil) {
|
||||
blockError =
|
||||
(__bridge_transfer NSError *)nw_error_copy_cf_error(error);
|
||||
}
|
||||
[condition signal];
|
||||
[condition unlock];
|
||||
}];
|
||||
|
||||
[_serviceBrowsers setObject:browser forKey:serviceType];
|
||||
nw_browser_start(browser);
|
||||
[browserWrapper start:browser];
|
||||
|
||||
BOOL didSignal = [condition
|
||||
waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:GNCStartDiscoveryTimeoutInSeconds]];
|
||||
@@ -306,8 +337,11 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
|
||||
|
||||
- (void)stopDiscoveryForServiceType:(nonnull NSString *)serviceType {
|
||||
nw_browser_t browser = [_serviceBrowsers objectForKey:serviceType];
|
||||
[_serviceBrowsers removeObjectForKey:serviceType];
|
||||
nw_browser_cancel(browser);
|
||||
if (browser) {
|
||||
[_serviceBrowsers removeObjectForKey:serviceType];
|
||||
id<GNCNWBrowser> browserWrapper = [[GNCNWBrowserImpl alloc] init];
|
||||
[browserWrapper cancel:browser];
|
||||
}
|
||||
}
|
||||
|
||||
- (nullable GNCNWFrameworkSocket *)connectToServiceName:(NSString *)serviceName
|
||||
@@ -391,10 +425,12 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
|
||||
GNCLoggerError(@"[GNCNWFramework] Failed to create NW parameters.");
|
||||
return nil;
|
||||
}
|
||||
nw_connection_t connection = nw_connection_create(endpoint, parameters);
|
||||
nw_connection_set_queue(connection, queue ? queue : dispatch_get_main_queue());
|
||||
nw_connection_set_state_changed_handler(
|
||||
connection, ^(nw_connection_state_t state, nw_error_t error) {
|
||||
|
||||
id<GNCNWConnection> connectionWrapper = [[GNCNWConnectionImpl alloc] init];
|
||||
[connectionWrapper createConnectionWithEndpoint:endpoint parameters:parameters];
|
||||
[connectionWrapper setQueue:queue ?: _dispatchQueue];
|
||||
[connectionWrapper
|
||||
setStateChangedHandler:^(nw_connection_state_t state, nw_error_t error) {
|
||||
[condition lock];
|
||||
// Ignore the preparing state, because it is not a final state.
|
||||
if (state != nw_connection_state_preparing) {
|
||||
@@ -405,24 +441,23 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
|
||||
[condition signal];
|
||||
}
|
||||
[condition unlock];
|
||||
});
|
||||
}];
|
||||
if (cancelSource) {
|
||||
dispatch_source_set_event_handler(cancelSource, ^{
|
||||
GNCLoggerInfo(
|
||||
@"[GNCNWFramework] Connection to endpoint was cancelled before it could be established.");
|
||||
nw_connection_cancel(connection);
|
||||
[connectionWrapper cancel];
|
||||
dispatch_source_cancel(cancelSource);
|
||||
});
|
||||
}
|
||||
nw_connection_start(connection);
|
||||
[connectionWrapper start];
|
||||
|
||||
BOOL didSignal =
|
||||
[condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:GNCConnectionTimeoutInSeconds]];
|
||||
[condition unlock];
|
||||
|
||||
// We timed out waiting for the connection to transition into a state.
|
||||
if (!didSignal) {
|
||||
nw_connection_cancel(connection);
|
||||
[connectionWrapper cancel];
|
||||
if (error != nil) {
|
||||
*error = [NSError errorWithDomain:GNCNWFrameworkErrorDomain
|
||||
code:GNCNWFrameworkErrorTimedOut
|
||||
@@ -443,9 +478,9 @@ NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(nw_browse_resu
|
||||
case nw_connection_state_failed:
|
||||
case nw_connection_state_cancelled:
|
||||
return nil;
|
||||
case nw_connection_state_ready:
|
||||
return [[GNCNWFrameworkSocket alloc]
|
||||
initWithConnection:[[GNCNWConnectionImpl alloc] initWithNWConnection:connection]];
|
||||
case nw_connection_state_ready: {
|
||||
return [[GNCNWFrameworkSocket alloc] initWithConnection:connectionWrapper];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ static const NSTimeInterval kConnectionWriteTimeout = 5.0; // 5 seconds timeout
|
||||
}
|
||||
|
||||
- (void)close {
|
||||
[self.connection cancel];
|
||||
[_connection cancel];
|
||||
_connection = nil;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowseResult.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowseResultImpl.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Network/Network.h>
|
||||
@@ -24,7 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property(nonatomic) nw_interface_type_t type;
|
||||
@end
|
||||
|
||||
@interface GNCFakeNWBrowseResult : NSObject <GNCNWBrowseResult>
|
||||
@interface GNCFakeNWBrowseResult : GNCNWBrowseResultImpl
|
||||
@property(nonatomic, copy) NSArray<GNCFakeNWInterface *> *interfaces;
|
||||
@property(nonatomic, nullable) NSDictionary<NSString *, NSString *> *txtRecord;
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
#import <Network/Network.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowser.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowserImpl.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface GNCFakeNWBrowser : NSObject <GNCNWBrowser>
|
||||
@interface GNCFakeNWBrowser : GNCNWBrowserImpl
|
||||
|
||||
@property(nonatomic, nullable) nw_browser_t createWithDescriptorResult;
|
||||
@property(nonatomic) BOOL simulateTimeout;
|
||||
|
||||
+9
-5
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnection.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnectionImpl.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Network/Network.h>
|
||||
@@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* A fake implementation of GNCNWConnection for testing purposes.
|
||||
*/
|
||||
@interface GNCFakeNWConnection : NSObject <GNCNWConnection>
|
||||
@interface GNCFakeNWConnection : GNCNWConnectionImpl
|
||||
|
||||
/** The data to be received by the fake connection. */
|
||||
@property(nonatomic, nullable) dispatch_data_t dataToReceive;
|
||||
@@ -30,16 +30,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/** Whether cancel has been called on the fake connection. */
|
||||
@property(nonatomic) BOOL cancelCalled;
|
||||
|
||||
/** Property to simulate start failure. */
|
||||
@property(nonatomic) BOOL simulateStartFailure;
|
||||
|
||||
/** Property to simulate send failure. */
|
||||
@property(nonatomic) BOOL simulateSendFailure;
|
||||
|
||||
/** Property to simulate receive failure. */
|
||||
@property(nonatomic) BOOL simulateReceiveFailure;
|
||||
|
||||
- (instancetype)init NS_DESIGNATED_INITIALIZER;
|
||||
/** The state changed handler for the fake connection. */
|
||||
@property(nonatomic, nullable) nw_connection_state_changed_handler_t stateChangedHandler;
|
||||
|
||||
- (instancetype)initWithNWConnection:(nw_connection_t)connection NS_UNAVAILABLE;
|
||||
- (instancetype)initWithNWConnection:(nw_connection_t)connection;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
+44
-8
@@ -19,10 +19,50 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation GNCFakeNWConnection
|
||||
@implementation GNCFakeNWConnection {
|
||||
nw_connection_t _fakeNWConnection;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
return [super init];
|
||||
- (instancetype)initWithNWConnection:(nw_connection_t)connection {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_fakeNWConnection = connection;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nw_connection_t)createConnectionWithEndpoint:(nw_endpoint_t)endpoint
|
||||
parameters:(nw_parameters_t)parameters {
|
||||
// Return a non-nil, unique-ish value to represent the connection.
|
||||
_fakeNWConnection = (nw_connection_t)[[NSObject alloc] init];
|
||||
return _fakeNWConnection;
|
||||
}
|
||||
|
||||
- (void)setQueue:(dispatch_queue_t)queue {
|
||||
// No-op for fake.
|
||||
}
|
||||
|
||||
- (void)setStateChangedHandler:(nullable nw_connection_state_changed_handler_t)handler {
|
||||
// Capture the handler passed to the method.
|
||||
_stateChangedHandler = handler;
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
// Simulate immediate transition to ready or failed.
|
||||
if (self.stateChangedHandler) {
|
||||
nw_connection_state_t state =
|
||||
self.simulateStartFailure ? nw_connection_state_failed : nw_connection_state_ready;
|
||||
dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
|
||||
self.stateChangedHandler(state, nil);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
self.cancelCalled = YES;
|
||||
if (self.stateChangedHandler) {
|
||||
self.stateChangedHandler(nw_connection_state_cancelled, nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)receiveMessageWithMinLength:(uint32_t)minIncompleteLength
|
||||
@@ -53,12 +93,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
});
|
||||
}
|
||||
|
||||
- (void)cancel {
|
||||
self.cancelCalled = YES;
|
||||
}
|
||||
|
||||
- (nullable nw_connection_t)nwConnection {
|
||||
return nil; // Fake doesn't wrap a real connection
|
||||
return _fakeNWConnection;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -31,6 +31,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property(nonatomic, nullable) NSError *simulatedError;
|
||||
|
||||
/** If set, `start` will not call the state changed handler, simulating a timeout. */
|
||||
@property(nonatomic) BOOL simulateTimeout;
|
||||
|
||||
/** State to report from `start` if `simulatedError` is nil. Defaults to `ready`. */
|
||||
@property(nonatomic) nw_listener_state_t stateForStart;
|
||||
|
||||
/** The port number to return from the `port` property. */
|
||||
@property(nonatomic) uint16_t port;
|
||||
|
||||
@@ -64,6 +70,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/** The descriptor captured from the last call to `setAdvertiseDescriptor:`. */
|
||||
@property(nonatomic, nullable) nw_advertise_descriptor_t capturedAdvertiseDescriptor;
|
||||
|
||||
/**
|
||||
* Simulates a new connection arriving, calling newConnectionHandler.
|
||||
*
|
||||
* @param connection The connection to pass to handler.
|
||||
*/
|
||||
- (void)simulateNewConnection:(nw_connection_t)connection;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
+25
-2
@@ -22,6 +22,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@synthesize port = _port;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_stateForStart = nw_listener_state_ready;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setNewConnectionHandler:(void (^)(nw_connection_t connection))handler {
|
||||
self.capturedNewConnectionHandler = handler;
|
||||
}
|
||||
@@ -40,10 +48,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)start {
|
||||
self.startCalled = YES;
|
||||
if (self.simulateTimeout) {
|
||||
return;
|
||||
}
|
||||
// Trigger the state change handler asynchronously.
|
||||
if (self.capturedStateChangedHandler) {
|
||||
nw_listener_state_t state =
|
||||
self.simulatedError ? nw_listener_state_failed : nw_listener_state_ready;
|
||||
nw_listener_state_t state = self.simulatedError ? nw_listener_state_failed : self.stateForStart;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
// We cannot create a realistic nw_error_t, so pass nil.
|
||||
self.capturedStateChangedHandler(state, nil);
|
||||
@@ -53,6 +63,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (void)cancel {
|
||||
self.cancelCalled = YES;
|
||||
if (self.capturedStateChangedHandler) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
self.capturedStateChangedHandler(nw_listener_state_cancelled, nil);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAdvertiseDescriptor:(nullable nw_advertise_descriptor_t)advertiseDescriptor {
|
||||
@@ -60,6 +75,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
self.capturedAdvertiseDescriptor = advertiseDescriptor;
|
||||
}
|
||||
|
||||
- (void)simulateNewConnection:(nw_connection_t)connection {
|
||||
if (self.capturedNewConnectionHandler) {
|
||||
dispatch_async(self.capturedQueue ?: dispatch_get_main_queue(), ^{
|
||||
self.capturedNewConnectionHandler(connection);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
+589
-26
@@ -14,19 +14,53 @@
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFramework.h"
|
||||
|
||||
#import <Network/Network.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "third_party/objective_c/ocmock/v3/Source/OCMock/OCMock.h"
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCIPv4Address.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowseResult.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowseResultImpl.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowser.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWBrowserImpl.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnection.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWConnectionImpl.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkError.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkServerSocket+Internal.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWBrowseResult.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWBrowser.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWFrameworkServerSocket.h"
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWListener.h"
|
||||
#import "third_party/objective_c/ocmock/v3/Source/OCMock/OCMock.h"
|
||||
|
||||
// Extern C functions for testing.
|
||||
extern BOOL GNCHasLoopbackInterface(nw_browse_result_t browseResult);
|
||||
extern NSDictionary<NSString *, NSString *> *GNCTXTRecordForBrowseResult(
|
||||
nw_browse_result_t browseResult);
|
||||
|
||||
static const NSInteger kPort = 1234;
|
||||
static NSString *const kServiceName = @"TestService";
|
||||
static NSString *const kServiceType = @"_test._tcp";
|
||||
static NSString *const kHostAddress = @"127.0.0.1";
|
||||
|
||||
@interface GNCNWFrameworkTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCNWFrameworkTest
|
||||
@implementation GNCNWFrameworkTest {
|
||||
id _mockBrowseResultImpl;
|
||||
id _mockBrowserImpl;
|
||||
id _mockConnectionImpl;
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
_mockBrowseResultImpl = OCMClassMock([GNCNWBrowseResultImpl class]);
|
||||
_mockBrowserImpl = OCMClassMock([GNCNWBrowserImpl class]);
|
||||
_mockConnectionImpl = OCMClassMock([GNCNWConnectionImpl class]);
|
||||
}
|
||||
|
||||
|
||||
- (void)testGNCNWFrameworkCanBeInstantiated {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
@@ -43,12 +77,12 @@
|
||||
id mockServerSocket = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
id mockServerSocketAlloc = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
OCMStub([mockServerSocketAlloc alloc]).andReturn(mockServerSocketAlloc);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:1234]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:kPort]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocket startListeningWithError:[OCMArg anyObjectRef] includePeerToPeer:NO])
|
||||
.andReturn(YES);
|
||||
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkServerSocket *serverSocket = [framework listenForServiceOnPort:1234
|
||||
GNCNWFrameworkServerSocket *serverSocket = [framework listenForServiceOnPort:kPort
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNotNil(serverSocket);
|
||||
@@ -61,7 +95,7 @@
|
||||
id mockServerSocket = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
id mockServerSocketAlloc = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
OCMStub([mockServerSocketAlloc alloc]).andReturn(mockServerSocketAlloc);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:1234]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:kPort]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocket startListeningWithError:[OCMArg anyObjectRef] includePeerToPeer:NO])
|
||||
.andDo(^(id localSelf, NSError **error, BOOL includePeerToPeer) {
|
||||
if (error) {
|
||||
@@ -71,7 +105,7 @@
|
||||
});
|
||||
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkServerSocket *serverSocket = [framework listenForServiceOnPort:1234
|
||||
GNCNWFrameworkServerSocket *serverSocket = [framework listenForServiceOnPort:kPort
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNil(serverSocket);
|
||||
@@ -87,7 +121,7 @@
|
||||
id mockServerSocket = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
id mockServerSocketAlloc = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
OCMStub([mockServerSocketAlloc alloc]).andReturn(mockServerSocketAlloc);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:1234]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:kPort]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocket startListeningWithPSKIdentity:pskIdentity
|
||||
PSKSharedSecret:pskSharedSecret
|
||||
includePeerToPeer:YES
|
||||
@@ -98,7 +132,7 @@
|
||||
GNCNWFrameworkServerSocket *serverSocket =
|
||||
[framework listenForServiceWithPSKIdentity:pskIdentity
|
||||
PSKSharedSecret:pskSharedSecret
|
||||
port:1234
|
||||
port:kPort
|
||||
includePeerToPeer:YES
|
||||
error:&error];
|
||||
XCTAssertNotNil(serverSocket);
|
||||
@@ -114,7 +148,7 @@
|
||||
id mockServerSocket = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
id mockServerSocketAlloc = OCMClassMock([GNCNWFrameworkServerSocket class]);
|
||||
OCMStub([mockServerSocketAlloc alloc]).andReturn(mockServerSocketAlloc);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:1234]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocketAlloc initWithPort:kPort]).andReturn(mockServerSocket);
|
||||
OCMStub([mockServerSocket startListeningWithPSKIdentity:pskIdentity
|
||||
PSKSharedSecret:pskSharedSecret
|
||||
includePeerToPeer:NO
|
||||
@@ -131,7 +165,7 @@
|
||||
GNCNWFrameworkServerSocket *serverSocket =
|
||||
[framework listenForServiceWithPSKIdentity:pskIdentity
|
||||
PSKSharedSecret:pskSharedSecret
|
||||
port:1234
|
||||
port:kPort
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNil(serverSocket);
|
||||
@@ -140,46 +174,42 @@
|
||||
}
|
||||
|
||||
- (void)testStartAdvertisingPort {
|
||||
NSInteger port = 1234;
|
||||
NSString *serviceName = @"TestService";
|
||||
NSString *serviceType = @"_test._tcp.";
|
||||
NSDictionary<NSString *, NSString *> *txtRecords = @{@"key" : @"value"};
|
||||
|
||||
GNCFakeNWFrameworkServerSocket *fakeServerSocket =
|
||||
[[GNCFakeNWFrameworkServerSocket alloc] initWithPort:port];
|
||||
[[GNCFakeNWFrameworkServerSocket alloc] initWithPort:kPort];
|
||||
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
NSMapTable<NSNumber *, GNCNWFrameworkServerSocket *> *serverSockets =
|
||||
[framework valueForKey:@"_serverSockets"];
|
||||
[serverSockets setObject:fakeServerSocket forKey:@(port)];
|
||||
[serverSockets setObject:fakeServerSocket forKey:@(kPort)];
|
||||
|
||||
[framework startAdvertisingPort:port
|
||||
serviceName:serviceName
|
||||
serviceType:serviceType
|
||||
[framework startAdvertisingPort:kPort
|
||||
serviceName:kServiceName
|
||||
serviceType:kServiceType
|
||||
txtRecords:txtRecords];
|
||||
|
||||
XCTAssertTrue(fakeServerSocket.startAdvertisingCalled);
|
||||
XCTAssertEqualObjects(fakeServerSocket.startAdvertisingServiceName, serviceName);
|
||||
XCTAssertEqualObjects(fakeServerSocket.startAdvertisingServiceType, serviceType);
|
||||
XCTAssertEqualObjects(fakeServerSocket.startAdvertisingServiceName, kServiceName);
|
||||
XCTAssertEqualObjects(fakeServerSocket.startAdvertisingServiceType, kServiceType);
|
||||
XCTAssertEqualObjects(fakeServerSocket.startAdvertisingTXTRecords, txtRecords);
|
||||
}
|
||||
|
||||
- (void)testStopAdvertisingPort {
|
||||
NSInteger port = 1234;
|
||||
GNCFakeNWFrameworkServerSocket *fakeServerSocket =
|
||||
[[GNCFakeNWFrameworkServerSocket alloc] initWithPort:port];
|
||||
[[GNCFakeNWFrameworkServerSocket alloc] initWithPort:kPort];
|
||||
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
NSMapTable<NSNumber *, GNCNWFrameworkServerSocket *> *serverSockets =
|
||||
[framework valueForKey:@"_serverSockets"];
|
||||
[serverSockets setObject:fakeServerSocket forKey:@(port)];
|
||||
[serverSockets setObject:fakeServerSocket forKey:@(kPort)];
|
||||
|
||||
XCTAssertTrue([[[serverSockets keyEnumerator] allObjects] containsObject:@(port)]);
|
||||
XCTAssertTrue([[[serverSockets keyEnumerator] allObjects] containsObject:@(kPort)]);
|
||||
|
||||
[framework stopAdvertisingPort:port];
|
||||
[framework stopAdvertisingPort:kPort];
|
||||
|
||||
XCTAssertTrue(fakeServerSocket.stopAdvertisingCalled);
|
||||
XCTAssertNil([serverSockets objectForKey:@(port)]);
|
||||
XCTAssertNil([serverSockets objectForKey:@(kPort)]);
|
||||
}
|
||||
|
||||
- (void)testIsDiscoveringAnyServiceWhenNoServicesAreDiscovering {
|
||||
@@ -187,4 +217,537 @@
|
||||
XCTAssertFalse([framework isDiscoveringAnyService]);
|
||||
}
|
||||
|
||||
- (void)testStartDiscoveryForServiceTypeSuccess API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
XCTestExpectation *serviceFoundExpectation = [self expectationWithDescription:@"Service found"];
|
||||
__block NSString *foundServiceName = nil;
|
||||
__block NSDictionary<NSString *, NSString *> *foundTXTRecords = nil;
|
||||
|
||||
NSError *error = nil;
|
||||
BOOL result = [framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
foundServiceName = serviceName;
|
||||
foundTXTRecords = txtRecords;
|
||||
[serviceFoundExpectation fulfill];
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
|
||||
XCTAssertTrue(result);
|
||||
XCTAssertNil(error);
|
||||
XCTAssertTrue([framework isDiscoveringAnyService]);
|
||||
XCTAssertNotNil(fakeBrowser.stateChangedHandler);
|
||||
XCTAssertNotNil(fakeBrowser.browseResultsChangedHandler);
|
||||
|
||||
// Simulate a service being found.
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowseResult.txtRecord = @{@"key" : @"value"};
|
||||
fakeBrowseResult.getChangesFromResult = nw_browse_result_change_result_added;
|
||||
nw_endpoint_t fakeEndpoint =
|
||||
nw_endpoint_create_host("localhost", [[NSString stringWithFormat:@"%ld", kPort] UTF8String]);
|
||||
fakeBrowseResult.endpointFromResultResult = fakeEndpoint;
|
||||
fakeBrowseResult.getBonjourServiceNameFromEndpointResult = kServiceName;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
if (fakeBrowser.browseResultsChangedHandler) {
|
||||
GNCFakeNWBrowseResult *oldFakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowser.browseResultsChangedHandler((nw_browse_result_t)oldFakeBrowseResult,
|
||||
(nw_browse_result_t)fakeBrowseResult, true);
|
||||
}
|
||||
|
||||
[self waitForExpectationsWithTimeout:1.0 handler:nil];
|
||||
|
||||
XCTAssertEqualObjects(foundServiceName, kServiceName);
|
||||
XCTAssertEqualObjects(foundTXTRecords, @{@"key" : @"value"});
|
||||
}
|
||||
|
||||
- (void)testStartDiscoveryForServiceTypeDuplicate API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
// Start discovery for kServiceType.
|
||||
NSError *error = nil;
|
||||
[framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNil(error);
|
||||
|
||||
// Try to start discovery again for same service type.
|
||||
BOOL result = [framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
|
||||
XCTAssertFalse(result);
|
||||
XCTAssertNotNil(error);
|
||||
XCTAssertEqual(error.domain, GNCNWFrameworkErrorDomain);
|
||||
XCTAssertEqual(error.code, GNCNWFrameworkErrorDuplicateDiscovererForServiceType);
|
||||
}
|
||||
|
||||
- (void)testStartDiscoveryForServiceTypeTimeout API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
fakeBrowser.simulateTimeout = YES;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
NSError *error = nil;
|
||||
BOOL result = [framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
|
||||
XCTAssertFalse(result);
|
||||
XCTAssertNotNil(error);
|
||||
XCTAssertEqual(error.domain, GNCNWFrameworkErrorDomain);
|
||||
XCTAssertEqual(error.code, GNCNWFrameworkErrorTimedOut);
|
||||
XCTAssertFalse([framework isDiscoveringAnyService]); // Should be removed on timeout.
|
||||
}
|
||||
|
||||
- (void)testStartDiscoveryForServiceTypeServiceLost API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
XCTestExpectation *serviceLostExpectation = [self expectationWithDescription:@"Service lost"];
|
||||
__block NSString *lostServiceName = nil;
|
||||
|
||||
NSError *error = nil;
|
||||
[framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
lostServiceName = serviceName;
|
||||
[serviceLostExpectation fulfill];
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNil(error);
|
||||
|
||||
// Simulate a service being lost.
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowseResult.txtRecord = @{@"key" : @"value"};
|
||||
fakeBrowseResult.getChangesFromResult = nw_browse_result_change_result_removed;
|
||||
nw_endpoint_t fakeEndpoint =
|
||||
nw_endpoint_create_host("localhost", [[NSString stringWithFormat:@"%ld", kPort] UTF8String]);
|
||||
fakeBrowseResult.endpointFromResultResult = fakeEndpoint;
|
||||
fakeBrowseResult.getBonjourServiceNameFromEndpointResult = kServiceName;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
if (fakeBrowser.browseResultsChangedHandler) {
|
||||
GNCFakeNWBrowseResult *newFakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowser.browseResultsChangedHandler((nw_browse_result_t)fakeBrowseResult,
|
||||
(nw_browse_result_t)newFakeBrowseResult, true);
|
||||
}
|
||||
|
||||
[self waitForExpectationsWithTimeout:1.0 handler:nil];
|
||||
XCTAssertEqualObjects(lostServiceName, kServiceName);
|
||||
}
|
||||
|
||||
- (void)testStartDiscoveryForServiceTypeTxtRecordChanged API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
XCTestExpectation *serviceLostExpectation =
|
||||
[self expectationWithDescription:@"Service lost for TXT change"];
|
||||
XCTestExpectation *serviceFoundExpectation =
|
||||
[self expectationWithDescription:@"Service found for TXT change"];
|
||||
__block NSString *lostServiceName = nil;
|
||||
__block NSString *foundServiceName = nil;
|
||||
__block NSDictionary<NSString *, NSString *> *foundTxtRecords = nil;
|
||||
|
||||
NSError *error = nil;
|
||||
[framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
foundServiceName = serviceName;
|
||||
foundTxtRecords = txtRecords;
|
||||
[serviceFoundExpectation fulfill];
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
lostServiceName = serviceName;
|
||||
[serviceLostExpectation fulfill];
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNil(error);
|
||||
|
||||
// Simulate a TXT record change.
|
||||
GNCFakeNWBrowseResult *oldResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
oldResult.txtRecord = @{@"key" : @"value1"};
|
||||
oldResult.getChangesFromResult = nw_browse_result_change_txt_record_changed;
|
||||
nw_endpoint_t fakeEndpoint1 =
|
||||
nw_endpoint_create_host("localhost", [[NSString stringWithFormat:@"%ld", kPort] UTF8String]);
|
||||
oldResult.endpointFromResultResult = fakeEndpoint1;
|
||||
oldResult.getBonjourServiceNameFromEndpointResult = kServiceName;
|
||||
|
||||
GNCFakeNWBrowseResult *newResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
newResult.txtRecord = @{@"key" : @"value2"};
|
||||
newResult.getChangesFromResult = nw_browse_result_change_txt_record_changed;
|
||||
nw_endpoint_t fakeEndpoint2 =
|
||||
nw_endpoint_create_host("localhost", [[NSString stringWithFormat:@"%ld", kPort] UTF8String]);
|
||||
newResult.endpointFromResultResult = fakeEndpoint2;
|
||||
newResult.getBonjourServiceNameFromEndpointResult = kServiceName;
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(oldResult);
|
||||
if (fakeBrowser.browseResultsChangedHandler) {
|
||||
fakeBrowser.browseResultsChangedHandler((nw_browse_result_t)oldResult,
|
||||
(nw_browse_result_t)newResult, true);
|
||||
}
|
||||
|
||||
[self waitForExpectationsWithTimeout:1.0 handler:nil];
|
||||
XCTAssertEqualObjects(lostServiceName, kServiceName);
|
||||
XCTAssertEqualObjects(foundServiceName, kServiceName);
|
||||
XCTAssertEqualObjects(foundTxtRecords, @{@"key" : @"value2"});
|
||||
}
|
||||
|
||||
- (void)testStartDiscoveryIgnoresLoopbackAdd API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
__block BOOL serviceFound = NO;
|
||||
NSError *error = nil;
|
||||
[framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
serviceFound = YES;
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNil(error);
|
||||
|
||||
// Simulate a loopback service being found.
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowseResult.getChangesFromResult = nw_browse_result_change_result_added;
|
||||
GNCFakeNWInterface *loopbackInterface = [[GNCFakeNWInterface alloc] init];
|
||||
loopbackInterface.type = nw_interface_type_loopback;
|
||||
fakeBrowseResult.interfaces = @[ loopbackInterface ];
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
if (fakeBrowser.browseResultsChangedHandler) {
|
||||
GNCFakeNWBrowseResult *oldFakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowser.browseResultsChangedHandler((nw_browse_result_t)oldFakeBrowseResult,
|
||||
(nw_browse_result_t)fakeBrowseResult, true);
|
||||
}
|
||||
|
||||
// Allow async blocks to run.
|
||||
XCTestExpectation *delay = [[XCTestExpectation alloc] initWithDescription:@"delay"];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
[delay fulfill];
|
||||
});
|
||||
[self waitForExpectations:@[ delay ] timeout:0.5];
|
||||
|
||||
XCTAssertFalse(serviceFound);
|
||||
}
|
||||
|
||||
- (void)testStartDiscoveryIgnoresLoopbackRemove API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
__block BOOL serviceLost = NO;
|
||||
NSError *error = nil;
|
||||
[framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
serviceLost = YES;
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
XCTAssertNil(error);
|
||||
|
||||
// Simulate a loopback service being lost.
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowseResult.getChangesFromResult = nw_browse_result_change_result_removed;
|
||||
GNCFakeNWInterface *loopbackInterface = [[GNCFakeNWInterface alloc] init];
|
||||
loopbackInterface.type = nw_interface_type_loopback;
|
||||
fakeBrowseResult.interfaces = @[ loopbackInterface ];
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
if (fakeBrowser.browseResultsChangedHandler) {
|
||||
GNCFakeNWBrowseResult *newFakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowser.browseResultsChangedHandler((nw_browse_result_t)fakeBrowseResult,
|
||||
(nw_browse_result_t)newFakeBrowseResult, true);
|
||||
}
|
||||
|
||||
// Allow async blocks to run.
|
||||
XCTestExpectation *delay = [[XCTestExpectation alloc] initWithDescription:@"delay"];
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
[delay fulfill];
|
||||
});
|
||||
[self waitForExpectations:@[ delay ] timeout:0.5];
|
||||
|
||||
XCTAssertFalse(serviceLost);
|
||||
}
|
||||
|
||||
- (void)testStopDiscoveryForServiceType API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWBrowser *fakeBrowser = [[GNCFakeNWBrowser alloc] init];
|
||||
fakeBrowser.createWithDescriptorResult = (nw_browser_t)fakeBrowser;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowserImpl alloc]).andReturn(fakeBrowser);
|
||||
|
||||
NSError *error = nil;
|
||||
BOOL result = [framework startDiscoveryForServiceType:kServiceType
|
||||
serviceFoundHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
serviceLostHandler:^(NSString *serviceName,
|
||||
NSDictionary<NSString *, NSString *> *txtRecords) {
|
||||
}
|
||||
includePeerToPeer:NO
|
||||
error:&error];
|
||||
|
||||
XCTAssertTrue(result);
|
||||
XCTAssertNil(error);
|
||||
XCTAssertTrue([framework isDiscoveringAnyService]);
|
||||
|
||||
nw_browser_t createdBrowser = fakeBrowser.createWithDescriptorResult;
|
||||
XCTAssertNotNil(createdBrowser);
|
||||
|
||||
[framework stopDiscoveryForServiceType:kServiceType];
|
||||
XCTAssertFalse([framework isDiscoveringAnyService]);
|
||||
XCTAssertTrue(fakeBrowser.cancelCalled);
|
||||
XCTAssertEqual(fakeBrowser.cancelledBrowser, createdBrowser);
|
||||
}
|
||||
|
||||
- (void)testGNCHasLoopbackInterface_WithLoopback API_AVAILABLE(ios(13.0)) {
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
GNCFakeNWInterface *wifiInterface = [[GNCFakeNWInterface alloc] init];
|
||||
wifiInterface.type = nw_interface_type_wifi;
|
||||
GNCFakeNWInterface *loopbackInterface = [[GNCFakeNWInterface alloc] init];
|
||||
loopbackInterface.type = nw_interface_type_loopback;
|
||||
fakeBrowseResult.interfaces = @[ wifiInterface, loopbackInterface ];
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
XCTAssertTrue(GNCHasLoopbackInterface((nw_browse_result_t)fakeBrowseResult));
|
||||
}
|
||||
|
||||
- (void)testGNCHasLoopbackInterface_WithoutLoopback API_AVAILABLE(ios(13.0)) {
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
GNCFakeNWInterface *wifiInterface = [[GNCFakeNWInterface alloc] init];
|
||||
wifiInterface.type = nw_interface_type_wifi;
|
||||
GNCFakeNWInterface *otherInterface = [[GNCFakeNWInterface alloc] init];
|
||||
otherInterface.type = nw_interface_type_other;
|
||||
fakeBrowseResult.interfaces = @[ wifiInterface, otherInterface ];
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
XCTAssertFalse(GNCHasLoopbackInterface((nw_browse_result_t)fakeBrowseResult));
|
||||
}
|
||||
|
||||
- (void)testGNCHasLoopbackInterface_NoInterfaces API_AVAILABLE(ios(13.0)) {
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowseResult.interfaces = @[];
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
XCTAssertFalse(GNCHasLoopbackInterface((nw_browse_result_t)fakeBrowseResult));
|
||||
}
|
||||
|
||||
- (void)testGNCTXTRecordForBrowseResult API_AVAILABLE(ios(13.0)) {
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
NSDictionary<NSString *, NSString *> *txtRecord = @{@"key1" : @"value1", @"key2" : @"value2"};
|
||||
fakeBrowseResult.txtRecord = txtRecord;
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
NSDictionary<NSString *, NSString *> *result =
|
||||
GNCTXTRecordForBrowseResult((nw_browse_result_t)fakeBrowseResult);
|
||||
XCTAssertEqualObjects(result, txtRecord);
|
||||
}
|
||||
|
||||
- (void)testGNCTXTRecordForBrowseResult_Empty API_AVAILABLE(ios(13.0)) {
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
fakeBrowseResult.txtRecord = @{};
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
NSDictionary<NSString *, NSString *> *result =
|
||||
GNCTXTRecordForBrowseResult((nw_browse_result_t)fakeBrowseResult);
|
||||
XCTAssertEqualObjects(result, @{});
|
||||
}
|
||||
|
||||
- (void)testGNCTXTRecordForBrowseResult_Nil API_AVAILABLE(ios(13.0)) {
|
||||
GNCFakeNWBrowseResult *fakeBrowseResult = [[GNCFakeNWBrowseResult alloc] init];
|
||||
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockBrowseResultImpl sharedInstance]).andReturn(fakeBrowseResult);
|
||||
|
||||
NSDictionary<NSString *, NSString *> *result =
|
||||
GNCTXTRecordForBrowseResult((nw_browse_result_t)fakeBrowseResult);
|
||||
XCTAssertEqualObjects(result, @{});
|
||||
}
|
||||
|
||||
- (void)testConnectToServiceNameSuccess API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockConnectionImpl alloc]).andReturn(fakeConnection);
|
||||
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkSocket *socket = [framework connectToServiceName:kServiceName
|
||||
serviceType:kServiceType
|
||||
error:&error];
|
||||
|
||||
XCTAssertNotNil(socket);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testConnectToServiceNameFailure API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
|
||||
fakeConnection.simulateStartFailure = YES;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockConnectionImpl alloc]).andReturn(fakeConnection);
|
||||
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkSocket *socket = [framework connectToServiceName:kServiceName
|
||||
serviceType:kServiceType
|
||||
error:&error];
|
||||
|
||||
XCTAssertNil(socket);
|
||||
XCTAssertNil(error); // As established, framework doesn't set error on non-timeout failure
|
||||
}
|
||||
|
||||
- (void)testConnectToServiceNameWithPSKSuccess API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockConnectionImpl alloc]).andReturn(fakeConnection);
|
||||
|
||||
NSData *pskIdentity = [@"testIdentity" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *pskSharedSecret = [@"testSharedSecret" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkSocket *socket = [framework connectToServiceName:kServiceName
|
||||
serviceType:kServiceType
|
||||
PSKIdentity:pskIdentity
|
||||
PSKSharedSecret:pskSharedSecret
|
||||
error:&error];
|
||||
|
||||
XCTAssertNotNil(socket);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testConnectToServiceNameWithPSKFailure API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
|
||||
fakeConnection.simulateStartFailure = YES;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockConnectionImpl alloc]).andReturn(fakeConnection);
|
||||
|
||||
NSData *pskIdentity = [@"testIdentity" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSData *pskSharedSecret = [@"testSharedSecret" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkSocket *socket = [framework connectToServiceName:kServiceName
|
||||
serviceType:kServiceType
|
||||
PSKIdentity:pskIdentity
|
||||
PSKSharedSecret:pskSharedSecret
|
||||
error:&error];
|
||||
|
||||
XCTAssertNil(socket);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testConnectToHostSuccess API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockConnectionImpl alloc]).andReturn(fakeConnection);
|
||||
|
||||
GNCIPv4Address *address = [GNCIPv4Address addressWithDottedRepresentation:kHostAddress];
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkSocket *socket = [framework connectToHost:address
|
||||
port:kPort
|
||||
includePeerToPeer:NO
|
||||
cancelSource:nil
|
||||
queue:nil
|
||||
error:&error];
|
||||
|
||||
XCTAssertNotNil(socket);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testConnectToHostFailure API_AVAILABLE(ios(13.0)) {
|
||||
GNCNWFramework *framework = [[GNCNWFramework alloc] init];
|
||||
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
|
||||
fakeConnection.simulateStartFailure = YES;
|
||||
// TODO: b/377543997 - Migrate to dependency injection and remove mocks.
|
||||
OCMStub([_mockConnectionImpl alloc]).andReturn(fakeConnection);
|
||||
|
||||
GNCIPv4Address *address = [GNCIPv4Address addressWithDottedRepresentation:kHostAddress];
|
||||
NSError *error = nil;
|
||||
GNCNWFrameworkSocket *socket = [framework connectToHost:address
|
||||
port:kPort
|
||||
includePeerToPeer:NO
|
||||
cancelSource:nil
|
||||
queue:nil
|
||||
error:&error];
|
||||
|
||||
XCTAssertNil(socket);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+13
-17
@@ -17,28 +17,24 @@
|
||||
#import <Network/Network.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWParameters.h"
|
||||
|
||||
@interface GNCNWListenerImplTest : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation GNCNWListenerImplTest {
|
||||
GNCNWListenerImpl *_listenerImpl;
|
||||
@implementation GNCNWListenerImplTest
|
||||
|
||||
- (void)testInitWithParameters {
|
||||
nw_parameters_t parameters = nw_parameters_create_secure_tcp(NW_PARAMETERS_DEFAULT_CONFIGURATION,
|
||||
NW_PARAMETERS_DEFAULT_CONFIGURATION);
|
||||
GNCNWListenerImpl *listener = [[GNCNWListenerImpl alloc] initWithParameters:parameters];
|
||||
XCTAssertNotNil(listener);
|
||||
}
|
||||
|
||||
- (void)setUp {
|
||||
[super setUp];
|
||||
if (@available(iOS 13.0, *)) {
|
||||
// Note: GNCBuildNonTLSParameters is not available in tests, so we can't fully initialize.
|
||||
// This test is limited to checking basic allocation.
|
||||
}
|
||||
- (void)testInitWithPortAndParameters {
|
||||
nw_parameters_t parameters = nw_parameters_create_secure_tcp(NW_PARAMETERS_DEFAULT_CONFIGURATION,
|
||||
NW_PARAMETERS_DEFAULT_CONFIGURATION);
|
||||
GNCNWListenerImpl *listener = [[GNCNWListenerImpl alloc] initWithPort:"1234"
|
||||
parameters:parameters];
|
||||
XCTAssertNotNil(listener);
|
||||
}
|
||||
|
||||
// No tests here. The functionality is tested through GNCNWFrameworkServerSocketTest.
|
||||
|
||||
// NOTE: Testing the interaction with the Network.framework C functions
|
||||
// is difficult without a way to mock or intercept C function calls.
|
||||
// The main test coverage for the listener logic is achieved through
|
||||
// GNCFakeNWListener in GNCNWFrameworkServerSocketTest.m.
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user