[NC Apple coverage] Refactor GNCNWFrameworkServerSocket and add more tests.

PiperOrigin-RevId: 831142947
This commit is contained in:
Edwin Wu
2025-11-11 18:08:21 -08:00
committed by Copybara-Service
parent 50de2233f5
commit a96818b409
3 changed files with 164 additions and 5 deletions
@@ -96,6 +96,21 @@ NS_ASSUME_NONNULL_BEGIN
state:(nw_connection_state_t)state
error:(nullable nw_error_t)error;
/**
* Handles a new incoming connection from the listener.
*
* @param connection The new connection object.
*/
- (void)handleNewConnection:(nw_connection_t)connection;
/**
* Configures a connection's queue and state handler, then starts it.
* NOTE: This method should not be called directly except for testing purposes.
*
* @param connection The connection to configure and start.
*/
- (void)configureAndStartConnection:(nw_connection_t)connection;
@end
NS_ASSUME_NONNULL_END
@@ -66,6 +66,7 @@ NS_ASSUME_NONNULL_BEGIN
@implementation GNCNWFrameworkServerSocket {
NSInteger _port;
// TODO: b/434870979 - Use a dispatch_semaphore instead of a condition to simplify the logic.
NSCondition *_condition;
dispatch_queue_t _dispatchQueue;
id<GNCNWListener> _listener;
@@ -288,17 +289,18 @@ NS_ASSUME_NONNULL_BEGIN
// This doesn't start flaking until 0.0005 seconds, so 0.5 should be plenty of time.
BOOL didSignal = [_condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]];
// We timed out waiting for the listener to transition into a state.
if (!didSignal) {
_listenerError = [NSError errorWithDomain:GNCNWFrameworkErrorDomain
code:GNCNWFrameworkErrorTimedOut
userInfo:nil];
}
if (error != nil) {
*error = [_listenerError copy];
}
[_condition unlock];
// We timed out waiting for the listener to transition into a state.
if (!didSignal) {
[self close];
_listenerError = [NSError errorWithDomain:GNCNWFrameworkErrorDomain
code:GNCNWFrameworkErrorTimedOut
userInfo:nil];
return NO;
}
@@ -322,7 +324,10 @@ NS_ASSUME_NONNULL_BEGIN
// Keep track of any incoming connections. We must keep a reference otherwise the connection will
// be dropped.
[self.pendingConnections addObject:connection];
[self configureAndStartConnection:connection];
}
- (void)configureAndStartConnection:(nw_connection_t)connection {
// Register for state changes so we can clean up any failed/canceled connections and inform Nearby
// of any ready connections when asked.
nw_connection_set_queue(connection, _dispatchQueue);
@@ -25,6 +25,7 @@
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/GNCNWFrameworkSocket.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWConnection.h"
#import "internal/platform/implementation/apple/Mediums/WiFiCommon/Tests/GNCFakeNWListener.h"
#import "third_party/objective_c/ocmock/v3/Source/OCMock/OCMock.h"
@interface GNCNWFrameworkServerSocketTest : XCTestCase
@end
@@ -90,6 +91,59 @@
XCTAssertNil(error, @"error should be nil when accept is successful");
}
- (void)testAcceptWithError_FailureWhenListenerFails API_AVAILABLE(ios(13.0)) {
// Simulate listener entering a failed state.
_serverSocket.listenerState = nw_listener_state_failed;
_serverSocket.listenerError = [NSError errorWithDomain:@"Test" code:123 userInfo:nil];
NSError *error = nil;
GNCNWFrameworkSocket *socket = [_serverSocket acceptWithError:&error];
XCTAssertNil(socket, @"acceptWithError should return nil when listener has failed");
XCTAssertNotNil(error, @"error should be non-nil when listener has failed");
XCTAssertEqualObjects(error.domain, @"Test");
XCTAssertEqual(error.code, 123);
}
- (void)testStartListening_Timeout API_AVAILABLE(ios(13.0)) {
_fakeListener.simulateTimeout = YES;
NSError *error = nil;
XCTAssertFalse([_serverSocket startListeningWithError:&error includePeerToPeer:NO]);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, GNCNWFrameworkErrorDomain);
XCTAssertEqual(error.code, GNCNWFrameworkErrorTimedOut);
}
- (void)testStartListening_StateInvalid API_AVAILABLE(ios(13.0)) {
_fakeListener.stateForStart = nw_listener_state_invalid;
_serverSocket.listenerError = [NSError errorWithDomain:@"Test" code:123 userInfo:nil];
NSError *error = nil;
XCTAssertFalse([_serverSocket startListeningWithError:&error includePeerToPeer:NO]);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, @"Test");
XCTAssertEqual(error.code, 123);
}
- (void)testStartListening_StateWaiting API_AVAILABLE(ios(13.0)) {
_fakeListener.stateForStart = nw_listener_state_waiting;
_serverSocket.listenerError = [NSError errorWithDomain:@"Test" code:123 userInfo:nil];
NSError *error = nil;
XCTAssertFalse([_serverSocket startListeningWithError:&error includePeerToPeer:NO]);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, @"Test");
XCTAssertEqual(error.code, 123);
}
- (void)testStartListening_StateCancelled API_AVAILABLE(ios(13.0)) {
_fakeListener.stateForStart = nw_listener_state_cancelled;
_serverSocket.listenerError = [NSError errorWithDomain:@"Test" code:123 userInfo:nil];
NSError *error = nil;
XCTAssertFalse([_serverSocket startListeningWithError:&error includePeerToPeer:NO]);
XCTAssertNotNil(error);
XCTAssertEqualObjects(error.domain, @"Test");
XCTAssertEqual(error.code, 123);
}
- (void)testStartListeningWithError_Success API_AVAILABLE(ios(13.0)) {
// Configure the fake listener to simulate a successful start
_fakeListener.port = 12345;
@@ -220,4 +274,89 @@
@"Connection should not be added to ready connections on failure.");
}
- (void)testHandleNewConnectionAddsConnectionToPendingList API_AVAILABLE(ios(13.0)) {
id partialServerSocket = OCMPartialMock(_serverSocket);
OCMStub([partialServerSocket configureAndStartConnection:[OCMArg any]]);
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
nw_connection_t connection = (nw_connection_t)fakeConnection;
[partialServerSocket handleNewConnection:connection];
XCTAssertTrue([_serverSocket.pendingConnections containsObject:connection],
@"handleNewConnection should add the connection to pendingConnections.");
}
- (void)testHandleConnectionStateChange_Cancelled API_AVAILABLE(ios(13.0)) {
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
nw_connection_t connection = (nw_connection_t)fakeConnection;
// Manually add to pending connections.
[_serverSocket.pendingConnections addObject:connection];
// Simulate the connection failing.
[_serverSocket handleConnectionStateChange:connection
state:nw_connection_state_cancelled
error:nil];
XCTAssertFalse([_serverSocket.pendingConnections containsObject:connection],
@"Connection should be removed from pending connections on failure.");
XCTAssertFalse([_serverSocket.readyConnections containsObject:connection],
@"Connection should not be added to ready connections on failure.");
}
- (void)testHandleConnectionStateChange_Invalid API_AVAILABLE(ios(13.0)) {
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
nw_connection_t connection = (nw_connection_t)fakeConnection;
// Manually add to pending connections.
[_serverSocket.pendingConnections addObject:connection];
// Simulate the connection failing.
[_serverSocket handleConnectionStateChange:connection
state:nw_connection_state_invalid
error:nil];
XCTAssertFalse([_serverSocket.pendingConnections containsObject:connection],
@"Connection should be removed from pending connections on failure.");
XCTAssertFalse([_serverSocket.readyConnections containsObject:connection],
@"Connection should not be added to ready connections on failure.");
}
- (void)testHandleConnectionStateChange_Waiting API_AVAILABLE(ios(13.0)) {
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
nw_connection_t connection = (nw_connection_t)fakeConnection;
// Manually add to pending connections.
[_serverSocket.pendingConnections addObject:connection];
// Simulate the connection failing.
[_serverSocket handleConnectionStateChange:connection
state:nw_connection_state_waiting
error:nil];
XCTAssertFalse([_serverSocket.pendingConnections containsObject:connection],
@"Connection should be removed from pending connections on failure.");
XCTAssertFalse([_serverSocket.readyConnections containsObject:connection],
@"Connection should not be added to ready connections on failure.");
}
- (void)testHandleConnectionStateChange_Preparing API_AVAILABLE(ios(13.0)) {
GNCFakeNWConnection *fakeConnection = [[GNCFakeNWConnection alloc] init];
nw_connection_t connection = (nw_connection_t)fakeConnection;
// Manually add to pending connections.
[_serverSocket.pendingConnections addObject:connection];
// Simulate the connection failing.
[_serverSocket handleConnectionStateChange:connection
state:nw_connection_state_preparing
error:nil];
XCTAssertTrue([_serverSocket.pendingConnections containsObject:connection],
@"Connection should remain in pending connections when preparing.");
XCTAssertFalse([_serverSocket.readyConnections containsObject:connection],
@"Connection should not be added to ready connections when preparing.");
}
@end