From 76784726c34c3822b1651abf02cb2b7a40823b6a Mon Sep 17 00:00:00 2001 From: Paul Taykalo Date: Fri, 17 Jan 2014 22:25:47 +0200 Subject: [PATCH 1/5] Added classname attribute generation for JUnit XML reporter --- Source/Reporters/CDRJUnitXMLReporter.m | 16 +++++- Spec/Reporters/CDRJUnitXMLReporterSpec.mm | 68 ++++++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/Source/Reporters/CDRJUnitXMLReporter.m b/Source/Reporters/CDRJUnitXMLReporter.m index 26b7b77b..b08cf931 100644 --- a/Source/Reporters/CDRJUnitXMLReporter.m +++ b/Source/Reporters/CDRJUnitXMLReporter.m @@ -1,5 +1,7 @@ #import "CDRJUnitXMLReporter.h" #import "CDRExample.h" +#import "CDRSpec.h" + @implementation CDRJUnitXMLReporter @@ -46,7 +48,8 @@ - (void)runDidComplete { [xml appendString:@"\n"]; for (CDRExample *example in successExamples_) { - [xml appendFormat:@"\t\n", [self escapeString:example.fullText], example.runTime]; + NSString *className = [self classNameForExample:example]; + [xml appendFormat:@"\t\n", [self escapeString:className], [self escapeString:example.fullText], example.runTime]; } for (CDRExample *example in failureExamples_) { @@ -54,8 +57,9 @@ - (void)runDidComplete { NSArray *parts = [failureMessage componentsSeparatedByString:@"\n"]; NSString *testCaseName = [parts objectAtIndex:0]; NSString *failureDescription = [parts objectAtIndex:1]; + NSString *className = [self classNameForExample:example]; - [xml appendFormat:@"\t\n", [self escapeString:testCaseName], example.runTime]; + [xml appendFormat:@"\t\n", [self escapeString:className], [self escapeString:testCaseName], example.runTime]; [xml appendFormat:@"\t\t%@\n", [self escapeString:failureDescription]]; [xml appendString:@"\t\n"]; } @@ -66,6 +70,14 @@ - (void)runDidComplete { #pragma mark - Private +- (NSString *)classNameForExample:(CDRExample *)example { + NSString *className = @"Cedar"; + if (example.spec.fileName && [example.spec.fileName length]) { + className = example.spec.fileName; + } + return className; +} + - (NSString *)escapeString:(NSString *)unescaped { NSString *escaped = [unescaped stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; escaped = [escaped stringByReplacingOccurrencesOfString:@">" withString:@">"]; diff --git a/Spec/Reporters/CDRJUnitXMLReporterSpec.mm b/Spec/Reporters/CDRJUnitXMLReporterSpec.mm index 2411af30..2d36a02d 100644 --- a/Spec/Reporters/CDRJUnitXMLReporterSpec.mm +++ b/Spec/Reporters/CDRJUnitXMLReporterSpec.mm @@ -9,7 +9,6 @@ #import "CDRExample.h" #import "CDRJUnitXMLReporter.h" -#import "CDRSpecFailure.h" #import "GDataXMLNode.h" #import "ExampleWithPublicRunDates.h" @@ -143,7 +142,41 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; expect([[exampleXML attributeForName:@"time"] stringValue]).to_not(be_nil); expect([[[exampleXML attributeForName:@"time"] stringValue] floatValue]).to(be_close_to(5)); + }); + + it(@"should have it's classname set from spec filename", ^{ + CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; + example.spec = [[CDRSpec new] autorelease]; + example.spec.fileName = @"ExampleSpec"; + + [reporter reportOnExample:example]; + + [reporter runDidComplete]; + GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + }); + + it(@"should have it's classname escaped", ^{ + CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; + example.spec = [[CDRSpec new] autorelease]; + example.spec.fileName = @"Special ' characters \" should < be & escaped > "; + + [reporter reportOnExample:example]; + [reporter runDidComplete]; + GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + }); + + + it(@"should have it's classname to default value if spec filename is empty", ^{ + CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; + + [reporter reportOnExample:example]; + + [reporter runDidComplete]; + GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"Cedar")); }); }); @@ -212,7 +245,40 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; expect([[exampleXML attributeForName:@"time"] stringValue]).to_not(be_nil); expect([[[exampleXML attributeForName:@"time"] stringValue] floatValue]).to(be_close_to(5)); + }); + + it(@"should have it's classname set from spec filename", ^{ + CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStateFailed]; + example.spec = [[CDRSpec new] autorelease]; + example.spec.fileName = @"ExampleSpec"; + + [reporter reportOnExample:example]; + + [reporter runDidComplete]; + GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + }); + + it(@"should have it's classname escaped", ^{ + CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStateFailed]; + example.spec = [[CDRSpec new] autorelease]; + example.spec.fileName = @"Special ' characters \" should < be & escaped > "; + + [reporter reportOnExample:example]; + + [reporter runDidComplete]; + GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + }); + it(@"should have it's classname to default value if spec filename is empty", ^{ + CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStateFailed]; + + [reporter reportOnExample:example]; + + [reporter runDidComplete]; + GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"Cedar")); }); }); From 84c17b17c1488299c10b9610400fc2e0c124dbd8 Mon Sep 17 00:00:00 2001 From: Paul Taykalo Date: Fri, 17 Jan 2014 23:10:30 +0200 Subject: [PATCH 2/5] classname will be actual class name and not full file path --- Source/Reporters/CDRJUnitXMLReporter.m | 5 +++-- Spec/Reporters/CDRJUnitXMLReporterSpec.mm | 20 ++++++++------------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Source/Reporters/CDRJUnitXMLReporter.m b/Source/Reporters/CDRJUnitXMLReporter.m index b08cf931..84e28502 100644 --- a/Source/Reporters/CDRJUnitXMLReporter.m +++ b/Source/Reporters/CDRJUnitXMLReporter.m @@ -72,8 +72,9 @@ - (void)runDidComplete { - (NSString *)classNameForExample:(CDRExample *)example { NSString *className = @"Cedar"; - if (example.spec.fileName && [example.spec.fileName length]) { - className = example.spec.fileName; + NSString *fileName = [[example.spec.fileName lastPathComponent] stringByDeletingPathExtension]; + if (fileName && [fileName length]) { + className = fileName; } return className; } diff --git a/Spec/Reporters/CDRJUnitXMLReporterSpec.mm b/Spec/Reporters/CDRJUnitXMLReporterSpec.mm index 2d36a02d..bb5c60c0 100644 --- a/Spec/Reporters/CDRJUnitXMLReporterSpec.mm +++ b/Spec/Reporters/CDRJUnitXMLReporterSpec.mm @@ -147,20 +147,19 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { it(@"should have it's classname set from spec filename", ^{ CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"ExampleSpec"; + example.spec.fileName = @"/SomePath/ExampleSpec.mm"; [reporter reportOnExample:example]; [reporter runDidComplete]; GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; - expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"ExampleSpec")); }); - it(@"should have it's classname escaped", ^{ + it(@"should have it's classname set from spec filename even if it contains special characters", ^{ CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"Special ' characters \" should < be & escaped > "; - + example.spec.fileName = @"Path That \"' Contains ?*&^|+!~ Special Characters"; [reporter reportOnExample:example]; [reporter runDidComplete]; @@ -168,7 +167,6 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); }); - it(@"should have it's classname to default value if spec filename is empty", ^{ CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; @@ -220,7 +218,6 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { }); it(@"should escape the failure reason", ^{ - NSString *exampleName = @"Failing spec 1"; NSString *failureReason = @" Special ' characters \" should < be & escaped > "; NSString *fullExampleText = [NSString stringWithFormat:@"%@\n%@", exampleName, failureReason]; @@ -250,20 +247,19 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { it(@"should have it's classname set from spec filename", ^{ CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStateFailed]; example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"ExampleSpec"; + example.spec.fileName = @"/Some/Path/FailureSpec.mm"; [reporter reportOnExample:example]; [reporter runDidComplete]; GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; - expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"FailureSpec")); }); - it(@"should have it's classname escaped", ^{ + it(@"should have it's classname set from spec filename even if it contains special characters", ^{ CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStateFailed]; example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"Special ' characters \" should < be & escaped > "; - + example.spec.fileName = @"Path That \"' Contains ?*&^|+!~ Special Characters"; [reporter reportOnExample:example]; [reporter runDidComplete]; From 346cd37058550fae43a8d60a5347db653970ee2d Mon Sep 17 00:00:00 2001 From: Paul Taykalo Date: Sat, 18 Jan 2014 10:16:59 +0200 Subject: [PATCH 3/5] Reusing allowed character set in Test namer --- Source/ReporterHelpers/CDROTestNamer.m | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/Source/ReporterHelpers/CDROTestNamer.m b/Source/ReporterHelpers/CDROTestNamer.m index 62880566..2759b5a4 100644 --- a/Source/ReporterHelpers/CDROTestNamer.m +++ b/Source/ReporterHelpers/CDROTestNamer.m @@ -2,10 +2,25 @@ #import "CDRExample.h" #import "CDRExampleBase.h" +@interface CDROTestNamer () +@property (nonatomic, retain) NSMutableCharacterSet *allowedCharacterSet; +@end + + @implementation CDROTestNamer +- (id)init { + self = [super init]; + if (self) { + self.allowedCharacterSet = [[[NSCharacterSet alphanumericCharacterSet] mutableCopy] autorelease]; + [self.allowedCharacterSet addCharactersInString:@"_"]; + } + return self; +} + - (NSString *)classNameForExample:(CDRExampleBase *)example { NSString *className = NSStringFromClass([example.spec class]); + className = className ?: @"Cedar"; return [self sanitizeNameFromString:className]; } @@ -28,18 +43,19 @@ - (NSString *)sanitizeNameFromString:(NSString *)string { NSMutableString *mutableString = [string mutableCopy]; [mutableString replaceOccurrencesOfString:@" " withString:@"_" options:0 range:NSMakeRange(0, mutableString.length)]; - NSMutableCharacterSet *allowedCharacterSet = [[NSCharacterSet alphanumericCharacterSet] mutableCopy]; - [allowedCharacterSet addCharactersInString:@"_"]; - for (NSUInteger i=0; i Date: Sat, 18 Jan 2014 10:17:45 +0200 Subject: [PATCH 4/5] Added namer for resolving classNameForExample --- Source/Reporters/CDRJUnitXMLReporter.m | 20 +++++------ Spec/Reporters/CDRJUnitXMLReporterSpec.mm | 42 ++++++++--------------- 2 files changed, 23 insertions(+), 39 deletions(-) diff --git a/Source/Reporters/CDRJUnitXMLReporter.m b/Source/Reporters/CDRJUnitXMLReporter.m index 84e28502..f2663b15 100644 --- a/Source/Reporters/CDRJUnitXMLReporter.m +++ b/Source/Reporters/CDRJUnitXMLReporter.m @@ -1,14 +1,20 @@ #import "CDRJUnitXMLReporter.h" #import "CDRExample.h" #import "CDRSpec.h" +#import "CDROTestNamer.h" +@interface CDRJUnitXMLReporter () +@property (nonatomic, retain) CDROTestNamer * namer; +@end + @implementation CDRJUnitXMLReporter - (id)init { if (self = [super init]) { successExamples_ = [[NSMutableArray alloc] init]; failureExamples_ = [[NSMutableArray alloc] init]; + self.namer = [[[CDROTestNamer alloc] init] autorelease]; } return self; } @@ -16,6 +22,7 @@ - (id)init { - (void)dealloc { [successExamples_ release]; [failureExamples_ release]; + self.namer = nil; [super dealloc]; } @@ -48,7 +55,7 @@ - (void)runDidComplete { [xml appendString:@"\n"]; for (CDRExample *example in successExamples_) { - NSString *className = [self classNameForExample:example]; + NSString *className = [self.namer classNameForExample:example]; [xml appendFormat:@"\t\n", [self escapeString:className], [self escapeString:example.fullText], example.runTime]; } @@ -57,7 +64,7 @@ - (void)runDidComplete { NSArray *parts = [failureMessage componentsSeparatedByString:@"\n"]; NSString *testCaseName = [parts objectAtIndex:0]; NSString *failureDescription = [parts objectAtIndex:1]; - NSString *className = [self classNameForExample:example]; + NSString *className = [self.namer classNameForExample:example]; [xml appendFormat:@"\t\n", [self escapeString:className], [self escapeString:testCaseName], example.runTime]; [xml appendFormat:@"\t\t%@\n", [self escapeString:failureDescription]]; @@ -70,15 +77,6 @@ - (void)runDidComplete { #pragma mark - Private -- (NSString *)classNameForExample:(CDRExample *)example { - NSString *className = @"Cedar"; - NSString *fileName = [[example.spec.fileName lastPathComponent] stringByDeletingPathExtension]; - if (fileName && [fileName length]) { - className = fileName; - } - return className; -} - - (NSString *)escapeString:(NSString *)unescaped { NSString *escaped = [unescaped stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; escaped = [escaped stringByReplacingOccurrencesOfString:@">" withString:@">"]; diff --git a/Spec/Reporters/CDRJUnitXMLReporterSpec.mm b/Spec/Reporters/CDRJUnitXMLReporterSpec.mm index bb5c60c0..ddae7e9a 100644 --- a/Spec/Reporters/CDRJUnitXMLReporterSpec.mm +++ b/Spec/Reporters/CDRJUnitXMLReporterSpec.mm @@ -144,27 +144,20 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { expect([[[exampleXML attributeForName:@"time"] stringValue] floatValue]).to(be_close_to(5)); }); - it(@"should have it's classname set from spec filename", ^{ + it(@"should have it's classname", ^{ CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"/SomePath/ExampleSpec.mm"; - [reporter reportOnExample:example]; - [reporter runDidComplete]; - GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; - expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"ExampleSpec")); - }); - - it(@"should have it's classname set from spec filename even if it contains special characters", ^{ - CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStatePassed]; - example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"Path That \"' Contains ?*&^|+!~ Special Characters"; - [reporter reportOnExample:example]; + CDRExample *junitExample = [CDRExample exampleWithText:@"JUnitExample" andState:CDRExampleStatePassed]; + junitExample.spec = [[CDRJUnitXMLReporterSpec new] autorelease]; + [reporter reportOnExample:junitExample]; [reporter runDidComplete]; GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; - expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"CDRSpec")); + GDataXMLElement *junitExampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:1]; + expect([[junitExampleXML attributeForName:@"classname"] stringValue]).to(equal(@"CDRJUnitXMLReporterSpec")); }); it(@"should have it's classname to default value if spec filename is empty", ^{ @@ -244,27 +237,20 @@ + (id)exampleWithText:(NSString *)text andState:(CDRExampleState)state { expect([[[exampleXML attributeForName:@"time"] stringValue] floatValue]).to(be_close_to(5)); }); - it(@"should have it's classname set from spec filename", ^{ + it(@"should have it's classname", ^{ CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStateFailed]; example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"/Some/Path/FailureSpec.mm"; - [reporter reportOnExample:example]; - [reporter runDidComplete]; - GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; - expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"FailureSpec")); - }); - - it(@"should have it's classname set from spec filename even if it contains special characters", ^{ - CDRExample *example = [CDRExample exampleWithText:@"Spec" andState:CDRExampleStateFailed]; - example.spec = [[CDRSpec new] autorelease]; - example.spec.fileName = @"Path That \"' Contains ?*&^|+!~ Special Characters"; - [reporter reportOnExample:example]; + CDRExample *junitExample = [CDRExample exampleWithText:@"JUnitExample" andState:CDRExampleStateFailed]; + junitExample.spec = [[CDRJUnitXMLReporterSpec new] autorelease]; + [reporter reportOnExample:junitExample]; [reporter runDidComplete]; GDataXMLElement *exampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:0]; - expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(example.spec.fileName)); + expect([[exampleXML attributeForName:@"classname"] stringValue]).to(equal(@"CDRSpec")); + GDataXMLElement *junitExampleXML = [[reporter.xmlRootElement elementsForName:@"testcase"] objectAtIndex:1]; + expect([[junitExampleXML attributeForName:@"classname"] stringValue]).to(equal(@"CDRJUnitXMLReporterSpec")); }); it(@"should have it's classname to default value if spec filename is empty", ^{ From be9b966e03016627fd794afdfbf619e78c8690d1 Mon Sep 17 00:00:00 2001 From: Paul Taykalo Date: Sat, 18 Jan 2014 12:00:06 +0200 Subject: [PATCH 5/5] Returned back original object emulation with retain/release in CDRSpy. This prevent crashes when running tests on with CEDAR_SDK_RUNTIME_VERSION < 7.0 --- Source/Doubles/CDRSpy.mm | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Source/Doubles/CDRSpy.mm b/Source/Doubles/CDRSpy.mm index 96071126..16846df5 100644 --- a/Source/Doubles/CDRSpy.mm +++ b/Source/Doubles/CDRSpy.mm @@ -33,6 +33,38 @@ + (void)stopInterceptingMessagesForInstance:(id)instance { #pragma mark - Emulating the original object +- (id)retain { + __block id that = self; + [self as_spied_class:^{ + [that retain]; + }]; + return self; +} + +- (oneway void)release { + __block id that = self; + [self as_spied_class:^{ + [that release]; + }]; +} + +- (id)autorelease { + __block id that = self; + [self as_spied_class:^{ + [that autorelease]; + }]; + return self; +} + +- (NSUInteger)retainCount { + __block id that = self; + __block NSUInteger count; + [self as_spied_class:^{ + count = [that retainCount]; + }]; + return count; +} + - (NSString *)description { __block id that = self; __block NSString *description = nil;