summaryrefslogtreecommitdiff
path: root/objective-c/test/ios/Classes/TestViewController.m
diff options
context:
space:
mode:
Diffstat (limited to 'objective-c/test/ios/Classes/TestViewController.m')
-rw-r--r--objective-c/test/ios/Classes/TestViewController.m515
1 files changed, 515 insertions, 0 deletions
diff --git a/objective-c/test/ios/Classes/TestViewController.m b/objective-c/test/ios/Classes/TestViewController.m
new file mode 100644
index 00000000000..6b6f44a1d21
--- /dev/null
+++ b/objective-c/test/ios/Classes/TestViewController.m
@@ -0,0 +1,515 @@
+// **********************************************************************
+//
+// 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 <Test.h>
+#import <AppDelegate.h>
+
+#import <TestCommon.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;
+}
+
+#if defined(__clang__) && !__has_feature(objc_arc)
+- (void)dealloc
+{
+ [body release];
+ [super dealloc];
+}
+#endif
+
+-(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) Test* test;
+
+-(void)add:(NSString*)d;
+-(void)startTest;
+@end
+
+@interface TestRun : NSObject
+{
+ TestConfigOption option;
+ Test* test;
+ int completed;
+ int error;
+ TestViewController* viewController;
+}
+
++(id) testRun:(Test*)test;
++(id) testRunWithConfigOption:(Test*)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.
+ TestCommonInit(self, @selector(add:));
+ [super viewDidLoad];
+}
+
+-(void)viewWillAppear:(BOOL)animated
+{
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ self.test = (Test*)[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
+}
+
+#if defined(__clang__) && !__has_feature(objc_arc)
+- (void)dealloc
+{
+ [output release];
+ [activity release];
+ [nextButton release];
+
+ [currentMessage release];
+ [messages release];
+ [queue release];
+ [test release];
+
+ [super dealloc];
+}
+#endif
+
+#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];
+ }
+
+ // Clear the current message, and the the table log.
+ [currentMessage deleteCharactersInRange:NSMakeRange(0, currentMessage.length)];
+ [messages removeAllObjects];
+ [output reloadData];
+ [activity startAnimating];
+
+ if(![test isProtocolSupported:appDelegate.protocol])
+ {
+ tprintf("Test not supported with %s\n", [appDelegate.protocol UTF8String]);
+ [self testComplete:YES];
+ return;
+ }
+
+ NSMutableArray* testRuns = [NSMutableArray array];
+ [testRuns addObject:[TestRun testRun:test]];
+ if(test.runWithSlicedFormat)
+ {
+ [testRuns addObject:[TestRun testRunWithConfigOption:test option:TestConfigOptionSliced]];
+ }
+ if(test.runWith10Encoding)
+ {
+ [testRuns addObject:[TestRun testRunWithConfigOption:test option:TestConfigOptionEncoding10]];
+ }
+ testRunEnumerator = [testRuns objectEnumerator];
+#if defined(__clang__) && !__has_feature(objc_arc)
+ [testRunEnumerator retain];
+#endif
+ id testRun = [testRunEnumerator nextObject];
+ [queue addOperation:[testRun runInvocation:self]];
+}
+
+-(void)testRunComplete:(BOOL)success
+{
+ if(!success)
+ {
+ [self testComplete:NO];
+ return;
+ }
+
+ id testRun = [testRunEnumerator nextObject];
+ if(testRun == nil)
+ {
+ [self testComplete:YES];
+ }
+ else
+ {
+ [queue addOperation:[testRun runInvocation:self]];
+ }
+}
+
+-(void)testComplete:(BOOL)success
+{
+ [activity stopAnimating];
+
+ AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
+ Test* nextTest = (Test*)[appDelegate.tests objectAtIndex:(appDelegate.currentTest+1)%(appDelegate.tests.count)];
+ NSString* buttonTitle = [NSString stringWithFormat:@"Run %@", nextTest.name];
+ [nextButton setTitle:buttonTitle forState:UIControlStateNormal];
+
+ nextButton.enabled = YES;
+ [nextButton setAlpha:1.0];
+ [self.navigationItem setHidesBackButton:NO animated:YES];
+
+ self.test = nil;
+#if defined(__clang__) && !__has_feature(objc_arc)
+ [testRunEnumerator release];
+#endif
+ testRunEnumerator = nil;
+
+ // For memory leak testing comment the following line out.
+ if([appDelegate testCompleted:success])
+ {
+ self.test = (Test*)[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 = (Test*)[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)
+ {
+#if defined(__clang__) && !__has_feature(objc_arc)
+ [messages addObject:[[currentMessage copy] autorelease]];
+#else
+ [messages addObject:[currentMessage copy]];
+#endif
+ [currentMessage deleteCharactersInRange:NSMakeRange(0, currentMessage.length)];
+ [output reloadData];
+ 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)
+ {
+#if defined(__clang__) && !__has_feature(objc_arc)
+ cell = [[[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MessageCell"] autorelease];
+#else
+ cell = [[MessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MessageCell"];
+#endif
+ }
+ [cell setMessage:[messages objectAtIndex:indexPath.row]];
+ return cell;
+}
+
+-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
+{
+ return nil;
+}
+
+@end
+
+@implementation TestRun
+-(id) init:(Test*)t option:(TestConfigOption)opt
+{
+ self = [super init];
+ if(!self)
+ {
+ return nil;
+ }
+ self->test = t;
+ self->option = opt;
+ self->completed = 0;
+ self->error = 0;
+ return self;
+}
+
++(id) testRun:(Test*)test
+{
+ TestRun* run = [[TestRun alloc] init:test option:TestConfigOptionDefault];
+#if defined(__clang__) && !__has_feature(objc_arc)
+ [run autorelease];
+#endif
+ return run;
+}
++(id) testRunWithConfigOption:(Test*)test option:(TestConfigOption)option
+{
+ TestRun* run = [[TestRun alloc] init:test option:option];
+#if defined(__clang__) && !__has_feature(objc_arc)
+ [run autorelease];
+#endif
+ return run;
+}
+
+-(NSInvocationOperation*) runInvocation:(TestViewController*)ctl
+{
+ viewController = ctl;
+#if defined(__clang__) && !__has_feature(objc_arc)
+ return [[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil] autorelease];
+#else
+ return [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
+#endif
+
+}
+
+-(void) run
+{
+ // Initialize testCommon.
+ 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);
+ }
+
+ TestCommonTestInit(self,
+ @selector(serverReady),
+ appDelegate.protocol,
+ option == TestConfigOptionSliced,
+ option == TestConfigOptionEncoding10);
+
+ if(option == TestConfigOptionSliced)
+ {
+ tprintf("Running test with %s and sliced format.\n", [appDelegate.protocol UTF8String]);
+ }
+ else if(option == TestConfigOptionEncoding10)
+ {
+ tprintf("Running test with %s and 1.0 encoding.\n", [appDelegate.protocol UTF8String]);
+ }
+ else
+ {
+ tprintf("Running test with %s and default format.\n", [appDelegate.protocol UTF8String]);
+ }
+
+ if([test hasServer])
+ {
+ [self runServer];
+ }
+ else
+ {
+ [self runClient];
+ }
+}
+
+-(void)clientComplete:(NSNumber*)rc
+{
+ if([rc intValue] != 0)
+ {
+ [viewController add:[NSString stringWithFormat:@"client error: %@!\n", rc]];
+ if([test hasServer])
+ {
+ serverStop();
+ }
+ ++error;
+ }
+
+ if(![test hasServer] || ++completed == 2)
+ {
+ [viewController testRunComplete:error == 0];
+ }
+}
+
+// Run in a separate thread.
+-(void)runClient
+{
+ int rc = [test client];
+ [self performSelectorOnMainThread:@selector(clientComplete:) withObject:[NSNumber numberWithInt:rc] waitUntilDone:NO];
+}
+
+-(void)serverComplete:(NSNumber*)rc
+{
+ if([rc intValue] != 0)
+ {
+ [viewController add:[NSString stringWithFormat:@"server error: %@!\n", rc]];
+ ++error;
+ }
+
+ if(++completed == 2)
+ {
+ [viewController testRunComplete:error == 0];
+ }
+}
+
+// Run in a separate thread.
+-(void)runServer
+{
+ int rc = [test server];
+ [self performSelectorOnMainThread:@selector(serverComplete:) withObject:[NSNumber numberWithInt:rc] waitUntilDone:NO];
+}
+
+// Kick off the client.
+-(void)serverReady
+{
+ NSInvocationOperation* invocation = [[NSInvocationOperation alloc]
+ initWithTarget:self
+ selector:@selector(runClient)
+ object:nil];
+#if defined(__clang__) && !__has_feature(objc_arc)
+ [invocation autorelease];
+#endif
+ [[viewController queue] addOperation:invocation];
+}
+@end
+
+