1// 2// Copyright (c) ZeroC, Inc. All rights reserved. 3// 4 5#import "ViewController.h" 6#import <Controller.h> 7#import <TestCommon.h> 8 9#include <ifaddrs.h> 10#include <arpa/inet.h> 11#include <net/if.h> 12#include <dlfcn.h> 13 14@interface MainHelper : NSThread 15{ 16 id<ViewController> _controller; 17 void* _func; 18 NSString* _exe; 19 NSArray* _args; 20 BOOL _ready; 21 BOOL _completed; 22 int _status; 23 NSMutableString* _out; 24 NSCondition* _cond; 25} 26-(void) serverReady; 27-(void) shutdown; 28-(void) print:(NSString*)str; 29-(void) completed:(int)status; 30-(void) waitReady:(int)timeout; 31-(int) waitSuccess:(int)timeout; 32-(NSString*)getOutput; 33@end 34 35@interface ProcessI : TestCommonProcess<TestCommonProcess> 36{ 37 id<ViewController> _controller; 38 MainHelper* _helper; 39} 40-(void) waitReady:(int)timeout current:(ICECurrent*)current; 41-(int) waitSuccess:(int)timeout current:(ICECurrent*)current; 42-(NSString*) terminate:(ICECurrent*)current; 43@end 44 45@interface ProcessControllerI : TestCommonProcessController<TestCommonProcessController> 46{ 47 id<ViewController> _controller; 48 NSString* _ipv4; 49 NSString* _ipv6; 50} 51-(id) init:(id<ViewController>) controller ipv4:(NSString*)ipv4 ipv6:(NSString*)ipv6; 52-(id<TestCommonProcessPrx>) start:(NSString*)testsuite exe:(NSString*)exe args:(NSArray*)args current:(ICECurrent*)current; 53-(NSString*) getHost:(NSString*)protocol ipv6:(BOOL)ipv6 current:(ICECurrent*)current; 54@end 55 56@implementation MainHelper 57-(id) init:(id<ViewController>)controller func:(void*)func exe:(NSString*)exe args:(NSArray*)args 58{ 59 self = [super init]; 60 if(self == nil) 61 { 62 return nil; 63 } 64 _controller = ICE_RETAIN(controller); 65 _func = func; 66 _exe = ICE_RETAIN(exe); 67 _args = ICE_RETAIN(args); 68 _ready = FALSE; 69 _completed = FALSE; 70 _status = 0; 71 _out = ICE_RETAIN([NSMutableString string]); 72 _cond = [NSCondition new]; 73 return self; 74} 75#if defined(__clang__) && !__has_feature(objc_arc) 76-(void) dealloc 77{ 78 [_controller release]; 79 [_exe release]; 80 [_args release]; 81 [_cond release]; 82 [_out release]; 83 [super dealloc]; 84} 85#endif 86-(void) serverReady 87{ 88 [_cond lock]; 89 @try 90 { 91 _ready = YES; 92 [_cond signal]; 93 } 94 @finally 95 { 96 [_cond unlock]; 97 } 98} 99-(void) shutdown 100{ 101 [_cond lock]; 102 @try 103 { 104 if(_completed) 105 { 106 return; 107 } 108 serverStop(); 109 } 110 @finally 111 { 112 [_cond unlock]; 113 } 114} 115-(void) print:(NSString*)msg 116{ 117 [_out appendString:msg]; 118} 119-(void) main 120{ 121 int (*mainEntryPoint)(int, char**) = (int (*)(int, char**))_func; 122 char** argv = malloc(sizeof(char*) * (_args.count + 1)); 123 int i = 0; 124 for(NSString* arg in _args) 125 { 126 argv[i++] = (char*)[arg UTF8String]; 127 } 128 argv[_args.count] = 0; 129 if([_exe isEqualToString:@"client"] || [_exe isEqualToString:@"collocated"]) 130 { 131 TestCommonSetOutput(self, @selector(print:)); 132 } 133 else 134 { 135 TestCommonTestInit(self, @selector(serverReady), @"", NO, NO); 136 } 137 @try 138 { 139 [self completed:mainEntryPoint((int)_args.count, argv)]; 140 } 141 @catch(NSException* ex) 142 { 143 [self print:[NSString stringWithFormat:@"unexpected exception while running `%s':%@\n", argv[0], ex]]; 144 [self completed:EXIT_FAILURE]; 145 } 146 if([_exe isEqualToString:@"client"] || [_exe isEqualToString:@"collocated"]) 147 { 148 TestCommonSetOutput(nil, nil); 149 } 150 free(argv); 151} 152-(void) completed:(int)status 153{ 154 [_cond lock]; 155 @try 156 { 157 _completed = YES; 158 _status = status; 159 [_cond signal]; 160 } 161 @finally 162 { 163 [_cond unlock]; 164 } 165} 166-(void) waitReady:(int)timeout 167{ 168 [_cond lock]; 169 @try 170 { 171 while(!_ready && !_completed) 172 { 173 if(![_cond waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeout]]) 174 { 175 @throw [TestCommonProcessFailedException 176 processFailedException:@"timed out waiting for the process to be ready"]; 177 } 178 } 179 if(_completed && _status == EXIT_FAILURE) 180 { 181 @throw [TestCommonProcessFailedException processFailedException:_out]; 182 } 183 } 184 @finally 185 { 186 [_cond unlock]; 187 } 188} 189-(int) waitSuccess:(int)timeout 190{ 191 [_cond lock]; 192 @try 193 { 194 while(!_completed) 195 { 196 if(timeout >= 0) 197 { 198 if(![_cond waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeout]]) 199 { 200 @throw [TestCommonProcessFailedException 201 processFailedException:@"timed out waiting for the process to succeed"]; 202 } 203 } 204 else 205 { 206 [_cond wait]; 207 } 208 } 209 } 210 @finally 211 { 212 [_cond unlock]; 213 } 214 return _status; 215} 216-(NSString*) getOutput 217{ 218 return _out; 219} 220@end 221 222@implementation ProcessI 223-(id) init:(id<ViewController>)controller helper:(MainHelper*)helper 224{ 225 self = [super init]; 226 if(self == nil) 227 { 228 return nil; 229 } 230 _controller = ICE_RETAIN(controller); 231 _helper = ICE_RETAIN(helper); 232 return self; 233} 234#if defined(__clang__) && !__has_feature(objc_arc) 235-(void) dealloc 236{ 237 [_controller release]; 238 [_helper release]; 239 [super dealloc]; 240} 241#endif 242-(void) waitReady:(int)timeout current:(ICECurrent*)current 243{ 244 [_helper waitReady:timeout]; 245} 246-(int) waitSuccess:(int)timeout current:(ICECurrent*)current 247{ 248 return [_helper waitSuccess:timeout]; 249} 250-(NSString*) terminate:(ICECurrent*)current 251{ 252 [_helper shutdown]; 253 [current.adapter remove:current.id_]; 254 [_helper waitSuccess:-1]; 255 return [_helper getOutput]; 256} 257@end 258 259@implementation ProcessControllerI 260-(id) init:(id<ViewController>)controller ipv4:(NSString*)ipv4 ipv6:(NSString*)ipv6 261{ 262 self = [super init]; 263 if(self == nil) 264 { 265 return nil; 266 } 267 _controller = ICE_RETAIN(controller); 268 _ipv4 = ICE_RETAIN(ipv4); 269 _ipv6 = ICE_RETAIN(ipv6); 270 return self; 271} 272#if defined(__clang__) && !__has_feature(objc_arc) 273-(void) dealloc 274{ 275 [_controller release]; 276 [_ipv4 release]; 277 [_ipv6 release]; 278 [super dealloc]; 279} 280#endif 281-(id<TestCommonProcessPrx>) start:(NSString*)testSuite exe:(NSString*)exe args:(NSArray*)args current:(ICECurrent*)c 282{ 283 [_controller println:[NSString stringWithFormat:@"starting %@ %@... ", testSuite, exe]]; 284 285 NSArray<NSString*>* components = [testSuite componentsSeparatedByString:@"/"]; 286 components = [components arrayByAddingObject:exe]; 287 NSMutableString* func = [NSMutableString string]; 288 [func appendString:[components objectAtIndex:1]]; 289 for(int i = 2; i < components.count; ++i) 290 { 291 NSString* comp = [components objectAtIndex:i]; 292 [func appendString:[[comp substringToIndex:1] capitalizedString]]; 293 [func appendString:[comp substringFromIndex:1]]; 294 } 295 296 void* sym = dlsym(RTLD_SELF, [func UTF8String]); 297 if(!sym) 298 { 299 @throw [TestCommonProcessFailedException processFailedException: 300 [NSString stringWithFormat:@"couldn't find %@", func]]; 301 } 302 args = [@[[NSString stringWithFormat:@"%@ %@", testSuite, exe]] arrayByAddingObjectsFromArray:args]; 303 MainHelper* helper = ICE_AUTORELEASE([[MainHelper alloc] init:_controller func:sym exe:exe args:args]); 304 305 // 306 // Use a 768KB thread stack size for the objects test. This is necessary when running the 307 // test on arm64 devices with a debug Ice libraries which require lots of stack space. 308 // 309 [helper setStackSize:768 * 1024]; 310 [helper start]; 311 id<ICEObjectPrx> prx = [c.adapter addWithUUID:ICE_AUTORELEASE([[ProcessI alloc] init:_controller helper:helper])]; 312 return [TestCommonProcessPrx uncheckedCast:prx]; 313} 314-(NSString*) getHost:(NSString*)protocol ipv6:(BOOL)ipv6 current:(ICECurrent*)c 315{ 316 return ICE_AUTORELEASE(ICE_RETAIN(ipv6 ? _ipv6 : _ipv4)); 317} 318@end 319 320@implementation ViewController 321- (void) startController 322{ 323 NSString* ipv4 = [interfacesIPv4 objectAtIndex:[interfaceIPv4 selectedRowInComponent:0]]; 324 NSString* ipv6 = [interfacesIPv6 objectAtIndex:[interfaceIPv6 selectedRowInComponent:0]]; 325 326 ICEInitializationData* initData = [ICEInitializationData initializationData]; 327 initData.properties = [ICEUtil createProperties]; 328 [initData.properties setProperty:@"Ice.ThreadPool.Server.SizeMax" value:@"10"]; 329 [initData.properties setProperty:@"Ice.Plugin.IceDiscovery" value:@"1"]; 330 [initData.properties setProperty:@"IceDiscovery.DomainId" value:@"TestController"]; 331 [initData.properties setProperty:@"ControllerAdapter.Endpoints" value:@"tcp"]; 332 //[initData.properties setProperty:@"Ice.Trace.Network", @"2"); 333 //[initData.properties setProperty:@"Ice.Trace.Protocol", @"2"); 334 [initData.properties setProperty:@"ControllerAdapter.AdapterId" value:[ICEUtil generateUUID]]; 335 336 communicator = ICE_RETAIN([ICEUtil createCommunicator:initData]); 337 338 id<ICEObjectAdapter> adapter = [communicator createObjectAdapter:@"ControllerAdapter"]; 339 ICEIdentity* ident = [ICEIdentity identity]; 340#if TARGET_IPHONE_SIMULATOR != 0 341 ident.category = @"iPhoneSimulator"; 342#else 343 ident.category = @"iPhoneOS"; 344#endif 345 ident.name = [[NSBundle mainBundle] bundleIdentifier]; 346 [adapter add:[[ProcessControllerI alloc] init:self ipv4:ipv4 ipv6:ipv6] identity:ident]; 347 [adapter activate]; 348} 349- (void) stopController 350{ 351 [communicator destroy]; 352 ICE_RELEASE(communicator); 353 communicator = nil; 354} 355- (void)viewDidLoad 356{ 357 [super viewDidLoad]; 358 ICEregisterIceDiscovery(NO); 359 360 // 361 // Search for local network interfaces 362 // 363 interfacesIPv4 = ICE_RETAIN([NSMutableArray array]); 364 [interfacesIPv4 addObject:@"127.0.0.1"]; 365 interfacesIPv6 = ICE_RETAIN([NSMutableArray array]); 366 [interfacesIPv6 addObject:@"::1"]; 367 struct ifaddrs* ifap; 368 if(getifaddrs(&ifap) == 0) 369 { 370 struct ifaddrs* curr = ifap; 371 while(curr != 0) 372 { 373 if(curr->ifa_addr && curr->ifa_flags & IFF_UP && !(curr->ifa_flags & IFF_LOOPBACK)) 374 { 375 if(curr->ifa_addr->sa_family == AF_INET) 376 { 377 char buf[INET_ADDRSTRLEN]; 378 const struct sockaddr_in *addr = (const struct sockaddr_in*)curr->ifa_addr; 379 if(inet_ntop(AF_INET, &addr->sin_addr, buf, INET_ADDRSTRLEN)) 380 { 381 [interfacesIPv4 addObject:[NSString stringWithUTF8String:buf]]; 382 } 383 } 384 else if(curr->ifa_addr->sa_family == AF_INET6) 385 { 386 char buf[INET6_ADDRSTRLEN]; 387 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)curr->ifa_addr; 388 if(inet_ntop(AF_INET6, &addr6->sin6_addr, buf, INET6_ADDRSTRLEN)) 389 { 390 [interfacesIPv6 addObject:[NSString stringWithUTF8String:buf]]; 391 } 392 } 393 } 394 curr = curr->ifa_next; 395 } 396 freeifaddrs(ifap); 397 } 398 399 // By default, use the loopback 400 [interfaceIPv4 selectRow:0 inComponent:0 animated:NO]; 401 [interfaceIPv6 selectRow:0 inComponent:0 animated:NO]; 402 [self startController]; 403} 404 405- (void) dealloc 406{ 407 [self stopController]; 408#if defined(__clang__) && !__has_feature(objc_arc) 409 [interfacesIPv4 release]; 410 [interfacesIPv6 release]; 411 [super dealloc]; 412#endif 413} 414 415-(void) write:(NSString*)msg 416{ 417 [output insertText:msg]; 418 [output layoutIfNeeded]; 419 [output scrollRangeToVisible:NSMakeRange([output.text length] - 1, 1)]; 420} 421 422#pragma mark ViewController 423 424-(void) print:(NSString*)msg 425{ 426 [self performSelectorOnMainThread:@selector(write:) withObject:msg waitUntilDone:NO]; 427} 428-(void) println:(NSString*)msg 429{ 430 [self print:[msg stringByAppendingString:@"\n"]]; 431} 432 433#pragma mark UIPickerViewDelegate 434 435- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component 436{ 437 if(pickerView == interfaceIPv4) 438 { 439 return [interfacesIPv4 objectAtIndex:row]; 440 } 441 else 442 { 443 return [interfacesIPv6 objectAtIndex:row]; 444 } 445} 446 447- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component 448{ 449 [self stopController]; 450 [self startController]; 451} 452 453#pragma mark UIPickerViewDataSource 454 455- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView 456{ 457 return 1; 458} 459 460- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component 461{ 462 if(pickerView == interfaceIPv4) 463 { 464 return interfacesIPv4.count; 465 } 466 else 467 { 468 return interfacesIPv6.count; 469 } 470} 471 472@end 473