Skip to content

Commit addad4a

Browse files
author
Seweryn Plazuk
committed
Improve reliability of communication between test and the tunneled app
1 parent b6eb65e commit addad4a

4 files changed

Lines changed: 49 additions & 18 deletions

File tree

Sources/SBTUITestTunnelClient/SBTUITestTunnelClient.m

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ - (void)resetInternalState
7979
self.connected = NO;
8080
self.connectionPort = 0;
8181
self.connectionTimeout = SBTUITunneledApplicationDefaultTimeout;
82+
83+
[self.ipcConnection invalidate];
84+
self.ipcConnection = nil;
85+
self.ipcProxy = nil;
8286
}
8387

8488
- (void)shutDownWithError:(NSError *)error
@@ -159,20 +163,24 @@ - (void)launchTunnelWithStartupBlock:(void (^)(void))startupBlock
159163
self.application.launchEnvironment = launchEnvironment;
160164

161165
__weak typeof(self)weakSelf = self;
162-
// Start polling the server with the choosen port
163166
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
164167
[weakSelf waitForConnection];
165-
NSLog(@"[SBTUITestTunnel] HTTP tunnel did connect after, %fs", CFAbsoluteTimeGetCurrent() - self.launchStart);
166-
168+
169+
if (!weakSelf || weakSelf.connectionPort == 0) {
170+
return;
171+
}
172+
173+
NSLog(@"[SBTUITestTunnel] HTTP tunnel did connect after, %fs", CFAbsoluteTimeGetCurrent() - weakSelf.launchStart);
174+
167175
dispatch_async(dispatch_get_main_queue(), ^{
168176
weakSelf.connected = YES;
169177
if (weakSelf.startupBlock) {
170178
weakSelf.startupBlock();
171179
NSLog(@"[SBTUITestTunnel] Did perform startupBlock");
172180
}
173-
181+
174182
NSAssert([NSThread isMainThread], @"We synch on main thread");
175-
weakSelf.startupCompleted = [[self sendSynchronousRequestWithPath:SBTUITunneledApplicationCommandStartupCommandsCompleted params:@{}] isEqualToString:@"YES"];
183+
weakSelf.startupCompleted = [[weakSelf sendSynchronousRequestWithPath:SBTUITunneledApplicationCommandStartupCommandsCompleted params:@{}] isEqualToString:@"YES"];
176184
});
177185
});
178186
}
@@ -1131,8 +1139,10 @@ - (NSString *)sendSynchronousRequestWithPath:(NSString *)path params:(NSDictiona
11311139
dispatch_semaphore_signal(synchRequestSemaphore);
11321140
}] resume];
11331141

1134-
if (dispatch_semaphore_wait(synchRequestSemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SBTUITunneledApplicationDefaultTimeout * NSEC_PER_SEC))) != 0) {}
1135-
1142+
if (dispatch_semaphore_wait(synchRequestSemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SBTUITunneledApplicationDefaultTimeout * NSEC_PER_SEC))) != 0) {
1143+
NSLog(@"[SBTUITestTunnel] HTTP request timeout after %ds for %@", (int)SBTUITunneledApplicationDefaultTimeout, request.URL);
1144+
}
1145+
11361146
return responseId;
11371147
}
11381148

Sources/SBTUITestTunnelCommon/DetoxIPC/DTXIPCConnection-Private.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
@protocol _DTXIPCImpl <NSObject>
1212

13-
- (oneway void)_slaveDidConnectWithName:(NSString*)slaveServiceName;
13+
// Intentionally synchronous (not oneway): caller must block until master sets _otherConnection.
14+
- (void)_slaveDidConnectWithName:(NSString*)slaveServiceName;
1415
- (oneway void)_invokeFromRemote:(NSDictionary*)serializedInvocation;
1516
- (oneway void)_invokeRemoteBlock:(NSDictionary*)serializedBlock;
1617
- (oneway void)_cleanupRemoteBlock:(NSString*)identifier;

Sources/SBTUITestTunnelCommon/DetoxIPC/DTXIPCConnection.m

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,22 @@ @implementation DTXIPCConnection
192192
BOOL _resumed;
193193
}
194194

195-
- (void)_runQueue
195+
- (void)_startConnectionSignalingReady:(dispatch_semaphore_t)readySemaphore
196196
{
197197
_runLoop = NSRunLoop.currentRunLoop;
198-
199-
[_connection run];
200-
201198
_resumed = YES;
199+
200+
[_connection addRunLoop:_runLoop];
201+
202+
if (readySemaphore) {
203+
CFRunLoopRef rl = CFRunLoopGetCurrent();
204+
CFRunLoopPerformBlock(rl, kCFRunLoopDefaultMode, ^{
205+
dispatch_semaphore_signal(readySemaphore);
206+
});
207+
CFRunLoopWakeUp(rl);
208+
}
209+
210+
CFRunLoopRun();
202211
}
203212

204213
- (BOOL)_commonInit
@@ -282,12 +291,20 @@ - (void)resume
282291
{
283292
return;
284293
}
285-
294+
286295
NSAssert(_exportedObject != nil || _remoteObjectInterface != nil, @"An exported object or a remote object interface must be set before resuming the connection.");
287-
296+
297+
dispatch_semaphore_t readySemaphore = dispatch_semaphore_create(0);
288298
dispatch_async(_dispatchQueue, ^{
289-
[self _runQueue];
299+
[self _startConnectionSignalingReady:readySemaphore];
290300
});
301+
// Wait for the connection's run loop to be actively processing messages.
302+
// This prevents a race where the remote endpoint connects before
303+
// the run loop is ready to handle incoming Mach port messages.
304+
long waitResult = dispatch_semaphore_wait(readySemaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)));
305+
if (waitResult != 0) {
306+
NSLog(@"[DTXIPCConnection] WARNING: Timed out waiting for connection run loop to start for service '%@'", _serviceName);
307+
}
291308
}
292309

293310
- (void)invalidate
@@ -330,7 +347,7 @@ - (void)setExportedObject:(id)exportedObject
330347

331348
#pragma mark _DTXIPCImpl
332349

333-
- (oneway void)_slaveDidConnectWithName:(NSString*)slaveServiceName
350+
- (void)_slaveDidConnectWithName:(NSString*)slaveServiceName
334351
{
335352
dispatch_sync(_otherConnectionQueue, ^{
336353
_otherConnection = [NSConnection connectionWithRegisteredName:slaveServiceName host:nil];

Sources/SBTUITestTunnelServer/SBTUITestTunnelServer.m

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,11 @@ - (BOOL)takeOffOnceUsingHTTPPort:(NSString *)tunnelPort
263263

264264
dispatch_semaphore_signal(sem);
265265
});
266-
267-
if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SBTUITunneledServerDefaultTimeout * NSEC_PER_SEC))) != 0) {}
266+
267+
if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(SBTUITunneledServerDefaultTimeout * NSEC_PER_SEC))) != 0) {
268+
NSString *command = [request.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
269+
NSLog(@"[SBTUITestTunnel] Server command queue timeout after %ds for command '%@'", (int)SBTUITunneledServerDefaultTimeout, command);
270+
}
268271
return ret;
269272
}];
270273

0 commit comments

Comments
 (0)