summaryrefslogtreecommitdiff
path: root/cpp/test/ios/testSuiteApp/Classes/TestViewController.mm
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/test/ios/testSuiteApp/Classes/TestViewController.mm')
-rw-r--r--cpp/test/ios/testSuiteApp/Classes/TestViewController.mm523
1 files changed, 523 insertions, 0 deletions
diff --git a/cpp/test/ios/testSuiteApp/Classes/TestViewController.mm b/cpp/test/ios/testSuiteApp/Classes/TestViewController.mm
new file mode 100644
index 00000000000..0a8eac6ff8f
--- /dev/null
+++ b/cpp/test/ios/testSuiteApp/Classes/TestViewController.mm
@@ -0,0 +1,523 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice 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>
+
+NSArray* noSSL = @[ @"Ice/metrics" ];
+NSArray* noWS = @[ @"Ice/metrics" ];
+NSArray* noCpp11 = @[ @"Ice/custom" ];
+
+static NSArray* protocols = @[ @"tcp", @"ssl", @"ws", @"wss" ];
+//
+// 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) TestSuite* testSuite;
+
+-(void)add:(NSString*)d;
+-(void)startTest;
+@end
+
+@interface TestRun : NSObject
+{
+ NSString* testSuiteId;
+ TestCase* testCase;
+ NSString* protocol;
+ int completed;
+ int running;
+ int error;
+ MainHelperI* clientHelper;
+ MainHelperI* serverHelper;
+ TestViewController* viewController;
+}
+
++(id) testRun:(NSString*)testSuiteId testCase:(TestCase*)testCase protocol:(NSString*)protocol;
+
+-(NSInvocationOperation*) runInvocation:(TestViewController*)callback;
+@end
+
+@implementation TestViewController
+
+@synthesize output;
+@synthesize activity;
+@synthesize nextButton;
+@synthesize currentMessage;
+@synthesize messages;
+@synthesize queue;
+@synthesize testSuite;
+
+- (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.testSuite = (TestSuite*)[appDelegate.testSuites objectAtIndex:appDelegate.currentTestSuite];
+ [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];
+ [testSuite release];
+
+ [super dealloc];
+}
+
+#pragma mark -
+
+-(void)startTest
+{
+ self.title = testSuite.testSuiteId;
+ [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];
+#ifdef ICE_CPP11_MAPPING
+ if([testSuite isIn:noCpp11])
+ {
+ [self add:@"C++11 not supported by this test\n"];
+ [self testComplete:YES];
+ return;
+ }
+#endif
+ if(([appDelegate.protocol isEqualToString:@"ws"] || [appDelegate.protocol isEqualToString:@"wss"]) &&
+ [testSuite isIn:noWS])
+ {
+ [self add:@"WS not supported by this test\n"];
+ [self testComplete:YES];
+ return;
+ }
+ if([appDelegate.protocol isEqualToString:@"ssl"] &&
+ [testSuite isIn:noSSL])
+ {
+ [self add:@"SSL not supported by this test\n"];
+ [self testComplete:YES];
+ return;
+ }
+
+ NSMutableArray* testRuns = [NSMutableArray array];
+ for(id testCase in testSuite.testCases)
+ {
+ [testRuns addObject:[TestRun testRun:testSuite.testSuiteId testCase:testCase protocol:appDelegate.protocol]];
+ }
+ 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];
+ NSInteger next = (appDelegate.currentTestSuite + 1) % (appDelegate.testSuites.count);
+ TestSuite* nextTestSuite = (TestSuite*)[appDelegate.testSuites objectAtIndex:next];
+ NSString* buttonTitle = [NSString stringWithFormat:@"Run %@", nextTestSuite.testSuiteId];
+ [nextButton setTitle:buttonTitle forState:UIControlStateNormal];
+ self.testSuite = nil;
+ [testRunEnumator release];
+ testRunEnumator = nil;
+
+ if([appDelegate testCompleted:success])
+ {
+ NSAssert(self.testSuite == nil, @"testSuite == nil");
+ self.testSuite = (TestSuite*)[appDelegate.testSuites objectAtIndex:appDelegate.currentTestSuite];
+ [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(self.testSuite == nil, @"testSuite == nil");
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ self.testSuite = (TestSuite*)[appDelegate.testSuites objectAtIndex:appDelegate.currentTestSuite];
+ [self startTest];
+ }
+}
+-(NSOperationQueue*) queue
+{
+ return queue;
+}
+-(void)add:(NSString*)s
+{
+ [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:(NSString*)ts testCase:(TestCase*)tc protocol:(NSString*)p
+{
+ self = [super init];
+ if(!self)
+ {
+ return nil;
+ }
+ self->testSuiteId = ts;
+ self->testCase = tc;
+ self->protocol = p;
+ self->completed = 0;
+ self->error = 0;
+ self->running = 0;
+ return self;
+}
++(id) testRun:(NSString*)ts testCase:(TestCase*)tc protocol:(NSString*)p
+{
+ return [[[TestRun alloc] init:ts testCase:tc protocol:p] autorelease];
+}
+-(NSInvocationOperation*) runInvocation:(TestViewController*)ctl
+{
+ viewController = ctl;
+ return [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil] autorelease];
+}
+-(void)run
+{
+// [self add:[NSString stringWithFormat:@"[ running %@ ]\n", testCase.name]];
+ if(testCase.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(!testCase.server || completed == 2)
+ {
+ [viewController testRunComplete:error == 0];
+ }
+}
+
+// Run in a separate thread.
+-(void)runClient
+{
+ running++;
+
+ std::string name = [testCase.name UTF8String];
+ std::string prefix = std::string("test/") + [testSuiteId UTF8String];
+ replace(prefix.begin(), prefix.end(), '/', '_');
+ std::string clt = [testCase.client UTF8String];
+
+ TestConfig config;
+ config.protocol = [protocol UTF8String];
+ for(id arg in testCase.args)
+ {
+ config.args.push_back([arg UTF8String]);
+ }
+
+ clientHelper = new MainHelperI(name, prefix + "/" + clt, TestTypeClient, 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
+{
+ running++;
+
+ std::string name = [testCase.name UTF8String];
+ std::string prefix = std::string("test/") + [testSuiteId UTF8String];
+ replace(prefix.begin(), prefix.end(), '/', '_');
+ std::string srv = [testCase.server UTF8String];
+
+ TestConfig config;
+ config.protocol = [protocol UTF8String];
+ for(id arg in testCase.args)
+ {
+ config.args.push_back([arg UTF8String]);
+ }
+
+ serverHelper = new MainHelperI(name, prefix + "/" + srv, TestTypeServer, 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