summaryrefslogtreecommitdiff
path: root/cpp/test/ios/Classes/TestViewController.mm
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/test/ios/Classes/TestViewController.mm')
-rw-r--r--cpp/test/ios/Classes/TestViewController.mm580
1 files changed, 580 insertions, 0 deletions
diff --git a/cpp/test/ios/Classes/TestViewController.mm b/cpp/test/ios/Classes/TestViewController.mm
new file mode 100644
index 00000000000..d6e68e06ef1
--- /dev/null
+++ b/cpp/test/ios/Classes/TestViewController.mm
@@ -0,0 +1,580 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice Touch is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#import <TestViewController.h>
+#import <TestUtil.h>
+#import <AppDelegate.h>
+//
+// Avoid warning for undocumented method.
+//
+@interface UIApplication(UndocumentedAPI)
+-(void)launchApplicationWithIdentifier:(NSString*)id suspended:(BOOL)flag;
+@end
+
+// TODO: Would be nice to have a red font for fatal, and error messages.
+@interface MessageCell : UITableViewCell
+{
+@private
+ UILabel* body;
+}
+
+@property (nonatomic, retain) UILabel* body;
+
++(CGFloat)heightForMessage:(NSString*)messsage;
+
+@end
+
+@implementation MessageCell
+@synthesize body;
+
+- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
+{
+ if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
+ {
+ body = [[UILabel alloc] initWithFrame:CGRectZero];
+ body.textColor = [UIColor blackColor];
+ body.font = [UIFont boldSystemFontOfSize:14];
+ body.numberOfLines = 0;
+
+ [self.contentView addSubview:self.body];
+ }
+
+ return self;
+}
+
++(CGFloat)heightForMessage:(NSString*)text
+{
+ // The header is always one line, the body is multiple lines.
+ // The width of the table is 320 - 20px of left & right padding. We don't want to let the body
+ // text go past 200px.
+ CGRect body = [text boundingRectWithSize:CGSizeMake(300.f, 200.0f) options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName:[UIFont boldSystemFontOfSize:14] } context:nil];
+ return body.size.height + 20.f;
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+
+ CGRect contentRect = self.contentView.bounds;
+
+ CGRect bodyFrame = CGRectMake(10.f, 0.f, CGRectGetWidth(contentRect)-20.f, CGRectGetHeight(contentRect));
+
+ self.body.frame = bodyFrame;
+}
+
+- (void)dealloc
+{
+ [body release];
+ [super dealloc];
+}
+
+-(void)setMessage:(NSString*)m
+{
+ self.body.text = m;
+}
+
+@end
+
+@interface TestViewController()
+
+@property (nonatomic, retain) UITableView* output;
+@property (nonatomic, retain) UIActivityIndicatorView* activity;
+@property (nonatomic, retain) UIButton* nextButton;
+
+@property (nonatomic, retain) NSMutableString* currentMessage;
+@property (nonatomic, retain) NSMutableArray* messages;
+@property (nonatomic, retain) NSOperationQueue* queue;
+@property (retain) TestCase* test;
+
+-(void)add:(NSString*)d;
+-(void)startTest;
+@end
+
+@interface TestRun : NSObject
+{
+ TestConfigOption option;
+ TestCase* test;
+ NSString* server;
+ NSString* client;
+ BOOL collocated;
+
+ int completed;
+ int running;
+ int error;
+ MainHelperI* clientHelper;
+ MainHelperI* serverHelper;
+ TestViewController* viewController;
+}
+
++(id) testRunClient:(TestCase*)test;
++(id) testRunCollocated:(TestCase*)test;
++(id) testRunClientAMDServer:(TestCase*)test;
++(id) testRunClientServer:(TestCase*)test;
++(id) testRunClientServerWithConfigOption:(TestCase*)test option:(TestConfigOption)option;
++(id) testRunClientAMDServerWithConfigOption:(TestCase*)test option:(TestConfigOption)option;
+
+-(NSInvocationOperation*) runInvocation:(TestViewController*)callback;
+@end
+
+@implementation TestViewController
+
+@synthesize output;
+@synthesize activity;
+@synthesize nextButton;
+@synthesize currentMessage;
+@synthesize messages;
+@synthesize queue;
+@synthesize test;
+
+- (void)viewDidLoad
+{
+ self.currentMessage = [NSMutableString string];
+ self.messages = [NSMutableArray array];
+ queue = [[NSOperationQueue alloc] init];
+ self.queue.maxConcurrentOperationCount = 2; // We need at least 2 concurrent operations.
+
+ [super viewDidLoad];
+}
+
+-(void)viewWillAppear:(BOOL)animated
+{
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ self.test = (TestCase*)[appDelegate.tests objectAtIndex:appDelegate.currentTest];
+ [self startTest];
+}
+
+-(void)viewWillDisappear:(BOOL)animated
+{
+ // TODO: Waiting isn't possible until the tests periodically find out whether
+ // they should terminate.
+ // Wait until the tests are complete.
+ //[queue waitUntilAllOperationsAreFinished];
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+ // Return YES for supported orientations
+ return (interfaceOrientation == UIInterfaceOrientationPortrait);
+}
+
+- (void)didReceiveMemoryWarning
+{
+ [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
+ // Release anything that's not essential, such as cached data
+}
+
+- (void)dealloc
+{
+ [output release];
+ [activity release];
+ [nextButton release];
+
+ [currentMessage release];
+ [messages release];
+ [queue release];
+ [test release];
+
+ [super dealloc];
+}
+
+#pragma mark -
+
+-(void)startTest
+{
+ self.title = test.name;
+ [self.navigationItem setHidesBackButton:YES animated:YES];
+
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ if(appDelegate.loop)
+ {
+ [nextButton setTitle:@"Stop running" forState:UIControlStateNormal];
+ }
+ else
+ {
+ nextButton.enabled = NO;
+ [nextButton setAlpha:0.5];
+ [nextButton setTitle:@"Test is running" forState:UIControlStateDisabled];
+ }
+
+ [currentMessage deleteCharactersInRange:NSMakeRange(0, currentMessage.length)];
+ [messages removeAllObjects];
+ [output reloadData];
+
+ [activity startAnimating];
+ if(![test isProtocolSupported:appDelegate.protocol])
+ {
+ [self testComplete:YES];
+ return;
+ }
+#ifdef ICE_CPP11_MAPPING
+ if(!test.cpp11Support)
+ {
+ [self add:@"C++11 not supported by this test\n"];
+ [self testComplete:YES];
+ return;
+ }
+#endif
+
+ NSMutableArray* testRuns = [NSMutableArray array];
+ if([test hasServer])
+ {
+ [testRuns addObject:[TestRun testRunClientServer:test]];
+ if([test hasAMDServer])
+ {
+ [testRuns addObject:[TestRun testRunClientAMDServer:test]];
+ }
+ if(test.runWithSlicedFormat)
+ {
+ [testRuns addObject:[TestRun testRunClientServerWithConfigOption:test option:TestConfigOptionSliced]];
+ if([test hasAMDServer])
+ {
+ [testRuns addObject:[TestRun testRunClientAMDServerWithConfigOption:test option:TestConfigOptionSliced]];
+ }
+ }
+ if(test.runWith10Encoding)
+ {
+ [testRuns addObject:[TestRun testRunClientServerWithConfigOption:test option:TestConfigOptionEncoding10]];
+ if([test hasAMDServer])
+ {
+ [testRuns addObject:[TestRun testRunClientAMDServerWithConfigOption:test option:TestConfigOptionEncoding10]];
+ }
+ }
+ }
+ else
+ {
+ [testRuns addObject:[TestRun testRunClient:test]];
+ }
+ if([test hasCollocated])
+ {
+ [testRuns addObject:[TestRun testRunCollocated:test]];
+ }
+
+ testRunEnumator = [[testRuns objectEnumerator] retain];
+ id testRun = [testRunEnumator nextObject];
+ [queue addOperation:[testRun runInvocation:self]];
+}
+-(void)testRunComplete:(BOOL)success
+{
+ if(!success)
+ {
+ [self testComplete:NO];
+ return;
+ }
+
+ id testRun = [testRunEnumator nextObject];
+ if(testRun == nil)
+ {
+ [self testComplete:YES];
+ }
+ else
+ {
+ [queue addOperation:[testRun runInvocation:self]];
+ }
+}
+
+-(void)testComplete:(BOOL)success
+{
+ [activity stopAnimating];
+
+ nextButton.enabled = YES;
+ [nextButton setAlpha:1.0];
+ [self.navigationItem setHidesBackButton:NO animated:YES];
+
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ TestCase* nextTest = (TestCase*)[appDelegate.tests objectAtIndex:(appDelegate.currentTest+1)%(appDelegate.tests.count)];
+ NSString* buttonTitle = [NSString stringWithFormat:@"Run %@", nextTest.name];
+ [nextButton setTitle:buttonTitle forState:UIControlStateNormal];
+ self.test = nil;
+ [testRunEnumator release];
+ testRunEnumator = nil;
+
+ if([appDelegate testCompleted:success])
+ {
+ NSAssert(test == nil, @"test == nil");
+ self.test = (TestCase*)[appDelegate.tests objectAtIndex:appDelegate.currentTest];
+ [self startTest];
+ }
+}
+
+-(IBAction)next:(id)sender
+{
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ if(appDelegate.loop)
+ {
+ appDelegate.loop = NO;
+ nextButton.enabled = NO;
+ [nextButton setAlpha:0.5];
+ [nextButton setTitle:@"Waiting..." forState:UIControlStateDisabled];
+ }
+ else
+ {
+ NSAssert(test == nil, @"test == nil");
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ self.test = (TestCase*)[appDelegate.tests objectAtIndex:appDelegate.currentTest];
+ [self startTest];
+ }
+}
+-(NSOperationQueue*) queue
+{
+ return queue;
+}
+-(void)add:(NSString*)s
+{
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ if(appDelegate.runAll)
+ {
+ printf("%s", [s UTF8String]);
+ }
+
+ [currentMessage appendString:s];
+ NSRange range = [currentMessage rangeOfString:@"\n" options:NSBackwardsSearch];
+ if(range.location != NSNotFound)
+ {
+ [messages addObject:[[currentMessage copy] autorelease]];
+ [currentMessage deleteCharactersInRange:NSMakeRange(0, currentMessage.length)];
+
+ // reloadData hangs if called too rapidly... we call at most every 100ms.
+ if(!reloadScheduled) {
+ reloadScheduled = TRUE;
+ [self performSelector:@selector(reloadOutput) withObject:nil afterDelay:0.1];
+ }
+ }
+}
+-(void) reloadOutput
+{
+ reloadScheduled = FALSE;
+ [output reloadData];
+
+ if(messages.count == 0)
+ {
+ return;
+ }
+
+ NSUInteger path[] = {0, messages.count -1};
+ [output scrollToRowAtIndexPath:[NSIndexPath indexPathWithIndexes:path length:2]
+ atScrollPosition:UITableViewScrollPositionBottom
+ animated:NO];
+
+}
+
+#pragma mark <UITableViewDelegate, UITableViewDataSource> Methods
+
+-(NSInteger)numberOfSectionsInTableView:(UITableView *)tv
+{
+ return 1;
+}
+
+-(NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section
+{
+ return messages.count;
+}
+
+-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ if([messages count] <= indexPath.row)
+ {
+ return [MessageCell heightForMessage:@""];
+ }
+ return [MessageCell heightForMessage:[messages objectAtIndex:indexPath.row]];
+}
+
+-(UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ MessageCell *cell = (MessageCell*)[output dequeueReusableCellWithIdentifier:@"MessageCell"];
+ if(cell == nil)
+ {
+ cell = [[[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MessageCell"] autorelease];
+ }
+ [cell setMessage:[messages objectAtIndex:indexPath.row]];
+ return cell;
+}
+
+-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ return nil;
+}
+
+@end
+
+@implementation TestRun
+-(id) init:(TestCase*)t client:(NSString*)cl server:(NSString*)srv option:(TestConfigOption)opt
+{
+ self = [super init];
+ if(!self)
+ {
+ return nil;
+ }
+ self->test = t;
+ self->client = cl;
+ self->server = srv;
+ self->option = opt;
+ self->clientHelper = 0;
+ self->serverHelper = 0;
+ self->completed = 0;
+ self->error = 0;
+ self->running = 0;
+ self->collocated = NO;
+ return self;
+}
+
++(id) testRunCollocated:(TestCase*)test
+{
+ TestRun* r;
+ r = [[[TestRun alloc] init:test client:test.collocated server:nil option:TestConfigOptionDefault] autorelease];
+ r->collocated = YES;
+ return r;
+}
++(id) testRunClient:(TestCase*)test
+{
+ return [[[TestRun alloc] init:test client:test.client server:nil option:TestConfigOptionDefault] autorelease];
+}
++(id) testRunClientServer:(TestCase*)test
+{
+ return [[[TestRun alloc] init:test client:test.client server:test.server option:TestConfigOptionDefault] autorelease];
+}
++(id) testRunClientAMDServer:(TestCase*)test
+{
+ return [[[TestRun alloc] init:test client:test.client server:test.serveramd option:TestConfigOptionDefault] autorelease];
+}
++(id) testRunClientServerWithConfigOption:(TestCase*)test option:(TestConfigOption)option
+{
+ return [[[TestRun alloc] init:test client:test.client server:test.server option:option] autorelease];
+}
++(id) testRunClientAMDServerWithConfigOption:(TestCase*)test option:(TestConfigOption)option
+{
+ return [[[TestRun alloc] init:test client:test.client server:test.serveramd option:option] autorelease];
+}
+
+-(NSInvocationOperation*) runInvocation:(TestViewController*)ctl
+{
+ viewController = ctl;
+ return [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil] autorelease];
+}
+-(void)run
+{
+ AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
+ if(appDelegate.runAll)
+ {
+ printf("\n*** running %s test %ld/%lu ...\n", [test.name UTF8String], (long)appDelegate.currentTest + 1,
+ (unsigned long)[appDelegate.tests count]);
+ printf("*** protocol: %s\n", [appDelegate.protocol UTF8String]);
+ fflush(stdout);
+ }
+ if(server)
+ {
+ [self runServer];
+ }
+ else
+ {
+ [self runClient];
+ }
+}
+-(void)add:(NSString*)s
+{
+ [viewController add:s];
+}
+
+-(void)clientComplete:(NSNumber*)rc
+{
+ if(clientHelper)
+ {
+ delete clientHelper;
+ clientHelper = 0;
+ }
+
+ if([rc intValue] != 0)
+ {
+ [viewController add:[NSString stringWithFormat:@"client error: %@!\n", rc]];
+ if(serverHelper)
+ {
+ serverHelper->shutdown();
+ }
+ ++error;
+ }
+
+ completed++;
+ if(!server || completed == 2)
+ {
+ [viewController testRunComplete:error == 0];
+ }
+}
+
+// Run in a separate thread.
+-(void)runClient
+{
+ AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
+ running++;
+
+ std::string name = [[test name] UTF8String];
+ std::string prefix = std::string("test_") + [[test prefix] UTF8String];
+ std::string clt = [client UTF8String];
+
+ TestConfig config;
+ config.protocol = [[appDelegate protocol] UTF8String];
+ config.type = collocated ? TestConfigTypeColloc : TestConfigTypeClient;
+ config.option = option;
+ config.hasServer = [test hasServer];
+
+ clientHelper = new MainHelperI(name, prefix + "/" + clt, config, self, @selector(add:), @selector(serverReady));
+ clientHelper->run();
+ int rc = clientHelper->status();
+ [self performSelectorOnMainThread:@selector(clientComplete:) withObject:[NSNumber numberWithInt:rc] waitUntilDone:NO];
+}
+
+-(void)serverComplete:(NSNumber*)rc
+{
+ if(serverHelper)
+ {
+ delete serverHelper;
+ serverHelper = 0;
+ }
+
+ if([rc intValue] != 0)
+ {
+ [viewController add:[NSString stringWithFormat:@"server error: %@!\n", rc]];
+ ++error;
+ }
+ completed++;
+ if(completed == 2)
+ {
+ [viewController testRunComplete:error == 0];
+ }
+}
+
+// Run in a separate thread.
+-(void)runServer
+{
+ AppDelegate* appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
+ running++;
+ std::string name = [[test name] UTF8String];
+ std::string prefix = std::string("test_") + [[test prefix] UTF8String];
+ std::string srv = [server UTF8String];
+
+ TestConfig config;
+ config.protocol = [[appDelegate protocol] UTF8String];
+ config.type = TestConfigTypeServer;
+ config.option = option;
+ config.hasServer = true;
+
+ serverHelper = new MainHelperI(name, prefix + "/" + srv, config, self, @selector(add:), @selector(serverReady));
+ serverHelper->run();
+ int rc = serverHelper->status();
+ [self performSelectorOnMainThread:@selector(serverComplete:) withObject:[NSNumber numberWithInt:rc] waitUntilDone:NO];
+}
+
+// Kick off the client.
+-(void)serverReady
+{
+ [[viewController queue] addOperation:[[[NSInvocationOperation alloc]
+ initWithTarget:self
+ selector:@selector(runClient)
+ object:nil] autorelease]];
+}
+
+@end