From 0c5f4edb36994f7555505d1b49c4af30b2d86e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20J=2E=20Hern=C3=A1ndez=20Blasco?= Date: Thu, 17 Dec 2015 11:56:02 +0100 Subject: [PATCH] Give support to JUnit output format for sogo-tests To integrate with CI system. sogo-tests now accepts a flag (-f) to determine the output format: * text : Current behaviour and default value * junit: XML output suitable for CI system such as Jenkins --- Tests/Unit/SOGoTest.m | 3 +- Tests/Unit/SOGoTestRunner.h | 17 +++++-- Tests/Unit/SOGoTestRunner.m | 96 ++++++++++++++++++++++++++++++++++--- Tests/Unit/sogo-tests.m | 87 ++++++++++++++++++++++++++++++++- 4 files changed, 191 insertions(+), 12 deletions(-) diff --git a/Tests/Unit/SOGoTest.m b/Tests/Unit/SOGoTest.m index 48dafb17c..c5e4dbec1 100644 --- a/Tests/Unit/SOGoTest.m +++ b/Tests/Unit/SOGoTest.m @@ -154,7 +154,8 @@ static NSString *SOGoTestAssertException = @"SOGoTestAssertException"; } NS_ENDHANDLER; - [testRunner incrementTestCounter: failureCode]; + [testRunner incrementTestCounter: failureCode + afterMethod: NSStringFromSelector (testMethod)]; } - (BOOL) run diff --git a/Tests/Unit/SOGoTestRunner.h b/Tests/Unit/SOGoTestRunner.h index 539fb5c33..66ceabdc2 100644 --- a/Tests/Unit/SOGoTestRunner.h +++ b/Tests/Unit/SOGoTestRunner.h @@ -35,20 +35,31 @@ typedef enum { SOGoTestFailureError = 2, } SOGoTestFailureCode; +typedef enum { + SOGoTestTextOutputFormat = 0, + SOGoTestJUnitOutputFormat +} SOGoTestOutputFormat; + @interface SOGoTestRunner : NSObject { - NSMutableArray *messages; + /* An array of arrays whose components are the method name and the + failure message if any */ + NSMutableArray *performedTests; int testCount; int failuresCount; int errorsCount; BOOL hasFailed; + SOGoTestOutputFormat reportFormat; } -+ (SOGoTestRunner *) testRunner; ++ (SOGoTestRunner *) testRunnerWithFormat: (SOGoTestOutputFormat) reportFormat; + +- (void) setReportFormat: (SOGoTestOutputFormat) format; - (int) run; -- (void) incrementTestCounter: (SOGoTestFailureCode) failureCode; +- (void) incrementTestCounter: (SOGoTestFailureCode) failureCode + afterMethod: (NSString *) methodName; - (void) reportException: (NSException *) exception method: (NSString *) methodName withCode: (SOGoTestFailureCode) failureCode; diff --git a/Tests/Unit/SOGoTestRunner.m b/Tests/Unit/SOGoTestRunner.m index 721d3e2d2..dfc18d3ba 100644 --- a/Tests/Unit/SOGoTestRunner.m +++ b/Tests/Unit/SOGoTestRunner.m @@ -22,6 +22,7 @@ #import #import +#import #import #import #import @@ -35,11 +36,12 @@ @implementation SOGoTestRunner -+ (SOGoTestRunner *) testRunner ++ (SOGoTestRunner *) testRunnerWithFormat: (SOGoTestOutputFormat) reportFormat { SOGoTestRunner *testRunner; testRunner = [self new]; + [testRunner setReportFormat: reportFormat]; [testRunner autorelease]; return testRunner; @@ -53,7 +55,8 @@ failuresCount = 0; errorsCount = 0; hasFailed = NO; - messages = [NSMutableArray new]; + performedTests = [NSMutableArray new]; + reportFormat = SOGoTestTextOutputFormat; } return self; @@ -61,10 +64,15 @@ - (void) dealloc { - [messages release]; + [performedTests release]; [super dealloc]; } +- (void) setReportFormat: (SOGoTestOutputFormat) format +{ + reportFormat = format; +} + - (int) run { NSEnumerator *allTestClasses; @@ -95,11 +103,16 @@ } - (void) incrementTestCounter: (SOGoTestFailureCode) failureCode + afterMethod: (NSString *) methodName { static char failureChars[] = { '.', 'F', 'E' }; testCount++; - fprintf (stderr, "%c", failureChars[failureCode]); + if (reportFormat == SOGoTestTextOutputFormat) + fprintf (stderr, "%c", failureChars[failureCode]); + if (failureCode == SOGoTestFailureSuccess) + [performedTests addObject: [NSArray arrayWithObjects: methodName, @"", nil]]; + /* else has been added by reportException method */ } - (void) reportException: (NSException *) exception @@ -134,13 +147,27 @@ errorsCount++; } [message appendString: @"\n"]; - [messages addObject: message]; + + [performedTests addObject: + [NSArray arrayWithObjects: methodName, message, [NSNumber numberWithInt: failureCode], nil]]; } -- (void) displayReport +- (void) displayTextReport { static NSString *separator = @"\n======================================================================\n"; + NSArray *performedTest; + NSMutableArray *messages; NSString *reportMessage; + NSUInteger i, max; + + messages = [NSMutableArray new]; + max = [performedTests count]; + for (i = 0; i < max; i++) + { + performedTest = [performedTests objectAtIndex: i]; + if ([[performedTest objectAtIndex: 1] length] > 0) + [messages addObject: [performedTest objectAtIndex: 1]]; + } if ([messages count]) { @@ -148,6 +175,9 @@ reportMessage = [messages componentsJoinedByString: separator]; fprintf (stderr, "%s", [reportMessage UTF8String]); } + + [messages release]; + fprintf (stderr, "\n----------------------------------------------------------------------\n" "Ran %d tests\n\n", testCount); @@ -158,4 +188,58 @@ fprintf (stderr, "OK\n"); } +- (void) displayJUnitReport +{ + /* Follow JUnit.xsd defined by Apache-Ant project */ + NSArray *performedTest; + NSMutableString *reportMessage; + NSUInteger i, max; + + /* Header */ + reportMessage = [NSMutableString stringWithFormat: @"\n" + @"\n" + @"%@\n", + @"SOGoUnitTests", testCount, errorsCount, failuresCount, + [NSDate date], + @"SOGo and SOPE Unit tests"]; + + /* Test cases */ + max = [performedTests count]; + for (i = 0; i < max; i++) + { + performedTest = [performedTests objectAtIndex: i]; + [reportMessage appendFormat: @"\n", [performedTest objectAtIndex: 0]]; + if ([[performedTest objectAtIndex: 1] length] > 0) + { + if ([performedTest count] > 2 && [[performedTest objectAtIndex: 2] intValue] == SOGoTestFailureFailure) + [reportMessage appendFormat: @"%@\n", [performedTest objectAtIndex: 1]]; + else + [reportMessage appendFormat: @"%@\n", [performedTest objectAtIndex: 1]]; + } + [reportMessage appendString: @"\n"]; + } + + /* End */ + [reportMessage appendString: @""]; + + fprintf (stdout, "%s", [reportMessage UTF8String]); +} + +- (void) displayReport +{ + switch (reportFormat) + { + case SOGoTestTextOutputFormat: + [self displayTextReport]; + break; + ;; + case SOGoTestJUnitOutputFormat: + [self displayJUnitReport]; + break; + ;; + } +} + @end diff --git a/Tests/Unit/sogo-tests.m b/Tests/Unit/sogo-tests.m index f7189d89e..d30066312 100644 --- a/Tests/Unit/sogo-tests.m +++ b/Tests/Unit/sogo-tests.m @@ -21,15 +21,91 @@ */ #import +#import #import "SOGoTestRunner.h" -int main() +static void Usage () +{ + /* Print usage and exit */ + NSLog (@"sogo-tests [-h|--help] [-f|--format=text|junit]\n" + @" -h, --help\t\t\tdisplay this help information\n" + @" -f, --format=text|junit\treport format. Default: text\n\n"); + exit(0); +} + +static SOGoTestOutputFormat ParseArguments (NSArray *args) +{ + /* Parse arguments from command line */ + BOOL help = NO; + NSString *arg, *format = nil; + NSUInteger i, max; + SOGoTestOutputFormat outFormat; + + max = [args count]; + /* Skip program name */ + i = 1; + while (!help && i < max) + { + arg = [args objectAtIndex: i]; + if ([arg isEqualToString: @"-f"] || [arg isEqualToString: @"--format"]) + { + NSArray *validFormats = [NSArray arrayWithObjects: @"text", @"junit", nil]; + i++; + if (i < max) + { + arg = [args objectAtIndex: i]; + if ([validFormats containsObject: arg]) + format = arg; + else + { + help = YES; + NSLog (@"Invalid format: '%@'. Use 'text' or 'junit'", arg); + } + } + else + { + NSLog (@"Missing format argument"); + help = YES; + } + } + else if ([arg isEqualToString: @"-h"] + || [arg isEqualToString: @"--help"]) + help = YES; + else + { + NSLog (@"Invalid command line argument: '%@'", arg); + help = YES; + } + i++; + } + + + if (help) + { + Usage (); + } + + if (format) + { + if ([format isEqualToString: @"text"]) + outFormat = SOGoTestTextOutputFormat; + else if ([format isEqualToString: @"junit"]) + outFormat = SOGoTestJUnitOutputFormat; + } + else + outFormat = SOGoTestTextOutputFormat; + + return outFormat; +} + +int main(int argc, char *argv[], char *env[]) { NSAutoreleasePool *pool; int rc; NSDictionary *defaults; NSUserDefaults *ud; + SOGoTestOutputFormat reportFormat; pool = [NSAutoreleasePool new]; @@ -42,7 +118,14 @@ int main() forName: @"sogo-tests-volatile"]; [ud addSuiteNamed: @"sogo-tests-volatile"]; - rc = [[SOGoTestRunner testRunner] run]; + /* Process arguments */ + [NSProcessInfo initializeWithArguments: argv + count: argc + environment: env]; + + reportFormat = ParseArguments ([[NSProcessInfo processInfo] arguments]); + + rc = [[SOGoTestRunner testRunnerWithFormat: reportFormat] run]; [pool release]; return rc;