diff --git a/bluepill/src/BPReportCollector.h b/bluepill/src/BPReportCollector.h index b28970af..428f4525 100644 --- a/bluepill/src/BPReportCollector.h +++ b/bluepill/src/BPReportCollector.h @@ -15,9 +15,11 @@ * @discussion collect xml reports from the reportsPath(recursive) and output a finalized report at finalReportPath * @param reportsPath parent path to the reports * @param finalReportsDir the directory where to save the final reports + * @param latestResultOnly If true, keeps only the latest result in the final report. */ + (void)collectReportsFromPath:(NSString *)reportsPath deleteCollected:(BOOL)deleteCollected - withOutputAtDir:(NSString *)finalReportsDir; + withOutputAtDir:(NSString *)finalReportsDir + keepingOnlyLatestResult:(BOOL)latestResultOnly; @end diff --git a/bluepill/src/BPReportCollector.m b/bluepill/src/BPReportCollector.m index 211706d8..c3f41022 100644 --- a/bluepill/src/BPReportCollector.m +++ b/bluepill/src/BPReportCollector.m @@ -33,7 +33,8 @@ @implementation BPReportCollector + (void)collectReportsFromPath:(NSString *)reportsPath deleteCollected:(BOOL)deleteCollected - withOutputAtDir:(NSString *)finalReportsDir { + withOutputAtDir:(NSString *)finalReportsDir + keepingOnlyLatestResult:(BOOL)latestResultOnly { NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *finalReportPath = [finalReportsDir stringByAppendingPathComponent:@"TEST-FinalReport.xml"]; @@ -97,7 +98,8 @@ + (void)collectReportsFromPath:(NSString *)reportsPath } NSXMLDocument *jUnitReport = [self collateReports:reports andDeleteCollated:deleteCollected - withOutputAt:finalReportPath]; + withOutputAt:finalReportPath + keepingOnlyLatestResult:latestResultOnly]; // write a html report [[BPHTMLReportWriter new] writeHTMLReportWithJUnitReport:jUnitReport @@ -106,7 +108,10 @@ + (void)collectReportsFromPath:(NSString *)reportsPath + (NSXMLDocument *)collateReports:(NSMutableArray *)reports andDeleteCollated:(BOOL)deleteCollated - withOutputAt:(NSString *)finalReportPath { + withOutputAt:(NSString *)finalReportPath + keepingOnlyLatestResult: (BOOL)latestResultOnly { + NSError *err; + // sort them by modification date, newer reports trump old reports NSMutableArray *sortedReports; sortedReports = [NSMutableArray arrayWithArray:[reports sortedArrayUsingComparator:^NSComparisonResult(id a, id b) { @@ -133,7 +138,7 @@ + (NSXMLDocument *)collateReports:(NSMutableArray *)reports [BPUtils printInfo:DEBUGINFO withString:@"TestSuite: %@", testSuiteName]; NSXMLElement *targetTestSuite = [[targetReport nodesForXPath:[NSString stringWithFormat:@"//testsuite[@name='%@']", testSuiteName] error:nil] firstObject]; if (targetTestSuite) { - [self collateTestSuite:testSuite into:targetTestSuite]; + [self collateTestSuite:testSuite into:targetTestSuite keepingOnlyLatestResult:latestResultOnly]; } else { NSXMLElement *testSuites = [[targetReport nodesForXPath:@"/testsuites" error:nil] firstObject]; [testSuites addChild:[testSuite copy]]; @@ -174,28 +179,28 @@ + (NSXMLDocument *)newEmptyXMLDocumentWithName:(NSString *)name { return doc; } -+ (void) collateTestSuite:(NSXMLElement *)testSuite into:(NSXMLElement *)targetTestSuite { ++ (void) collateTestSuite:(NSXMLElement *)testSuite into:(NSXMLElement *)targetTestSuite keepingOnlyLatestResult:(BOOL)latestResultOnly { [BPUtils printInfo:DEBUGINFO withString:@"Collating '%@' into '%@'", [[testSuite attributeForName:@"name"] stringValue], [[targetTestSuite attributeForName:@"name"] stringValue]]; // testsuite elements must have either all testsuite children or all testcase children NSString *firstChild = [[[testSuite children] firstObject] name]; if ([firstChild isEqualToString:@"testsuite"]) { - [self collateTestSuiteTestSuites:testSuite into:targetTestSuite]; + [self collateTestSuiteTestSuites:testSuite into:targetTestSuite keepingOnlyLatestResult:latestResultOnly]; } else if ([firstChild isEqualToString:@"testcase"]) { - [self collateTestSuiteTestCases:testSuite into:targetTestSuite]; + [self collateTestSuiteTestCases:testSuite into:targetTestSuite keepingOnlyLatestResult:latestResultOnly]; } else if (firstChild) { // empty [BPUtils printInfo:ERROR withString:@"Unknown child node in '%@': %@", [[testSuite attributeForName:@"name"] stringValue], firstChild]; assert(false); } } -+ (void)collateTestSuiteTestSuites:(NSXMLElement *)testSuite into:(NSXMLElement *)targetTestSuite { ++ (void)collateTestSuiteTestSuites:(NSXMLElement *)testSuite into:(NSXMLElement *)targetTestSuite keepingOnlyLatestResult:(BOOL)latestResultOnly { [BPUtils printInfo:DEBUGINFO withString:@"Collating TestSuites under: %@", [[testSuite attributeForName:@"name"]stringValue]]; for (NSXMLElement *ts in [testSuite nodesForXPath:@"testsuite" error:nil]) { NSXMLElement *tts = [[targetTestSuite nodesForXPath:[NSString stringWithFormat:@"testsuite[@name='%@']", [[ts attributeForName:@"name"] stringValue]] error:nil] firstObject]; if (tts) { [BPUtils printInfo:DEBUGINFO withString:@"match: %@", [[tts attributeForName:@"name"] stringValue]]; - [self collateTestSuiteTestCases:ts into:tts]; + [self collateTestSuiteTestCases:ts into:tts keepingOnlyLatestResult:latestResultOnly]; } else { [BPUtils printInfo:DEBUGINFO withString:@"inserting: %@", [[ts attributeForName:@"name"] stringValue]]; [targetTestSuite addChild:[ts copy]]; @@ -203,7 +208,7 @@ + (void)collateTestSuiteTestSuites:(NSXMLElement *)testSuite into:(NSXMLElement } } -+ (void)collateTestSuiteTestCases:(NSXMLElement *)testSuite into:(NSXMLElement *)targetTestSuite { ++ (void)collateTestSuiteTestCases:(NSXMLElement *)testSuite into:(NSXMLElement *)targetTestSuite keepingOnlyLatestResult:(BOOL)latestResultOnly { [BPUtils printInfo:DEBUGINFO withString:@"Collating TestCases under: %@", [[testSuite attributeForName:@"name"] stringValue]]; int testCaseCount = 0; for (NSXMLElement *testCase in [testSuite nodesForXPath:@"testcase" error:nil]) { @@ -211,10 +216,15 @@ + (void)collateTestSuiteTestCases:(NSXMLElement *)testSuite into:(NSXMLElement * NSString *name = [[testCase attributeForName:@"name"] stringValue]; NSXMLElement *targetTestCase = [[targetTestSuite nodesForXPath:[NSString stringWithFormat:@"testcase[@name='%@' and @classname='%@']", name, className] error:nil] firstObject]; - NSXMLElement *parent; + NSXMLElement *parent = (NSXMLElement *)[targetTestCase parent]; + + if (latestResultOnly) { + [parent removeChildAtIndex:[targetTestCase index]]; + testCaseCount--; + } + if (targetTestCase) { [BPUtils printInfo:DEBUGINFO withString:@"testcase match: %@", [[targetTestCase attributeForName:@"name"] stringValue]]; - parent = (NSXMLElement *)[targetTestCase parent]; // append the latest result at the end NSUInteger insertIndex; for (insertIndex = [targetTestCase index]; insertIndex < [[parent children] count]; insertIndex++) { diff --git a/bluepill/src/main.m b/bluepill/src/main.m index 0bd4884e..3e92884e 100644 --- a/bluepill/src/main.m +++ b/bluepill/src/main.m @@ -125,7 +125,8 @@ int main(int argc, char * argv[]) { // collect all the reports [BPReportCollector collectReportsFromPath:config.outputDirectory deleteCollected:(!config.keepIndividualTestReports) - withOutputAtDir:config.outputDirectory]; + withOutputAtDir:config.outputDirectory + keepingOnlyLatestResult:config.keepOnlyLatestTestResults]; } exit(rc); } diff --git a/bp/src/BPConfiguration.h b/bp/src/BPConfiguration.h index 3e845ab9..40d6ac15 100644 --- a/bp/src/BPConfiguration.h +++ b/bp/src/BPConfiguration.h @@ -107,6 +107,7 @@ typedef NS_ENUM(NSInteger, BPProgram) { @property (nonatomic, strong) NSNumber *launchTimeout; @property (nonatomic, strong) NSNumber *deleteTimeout; @property (nonatomic) BOOL keepIndividualTestReports; +@property (nonatomic) BOOL keepOnlyLatestTestResults; @property (nonatomic, strong) NSArray *commandLineArguments; // command line arguments for the app @property (nonatomic, strong) NSDictionary *environmentVariables; diff --git a/bp/src/BPConfiguration.m b/bp/src/BPConfiguration.m index 92c02ae8..b5d256de 100644 --- a/bp/src/BPConfiguration.m +++ b/bp/src/BPConfiguration.m @@ -110,6 +110,8 @@ typedef NS_OPTIONS(NSUInteger, BPOptionType) { "Enable verbose logging"}, {'k', "keep-individual-test-reports", BLUEPILL_BINARY | BP_BINARY, NO, NO, no_argument, "Off", BP_VALUE | BP_BOOL, "keepIndividualTestReports", "Keep individual test reports, in addition to the aggregated final report"}, + {'K', "keep-only-latest-test-results", BLUEPILL_BINARY | BP_BINARY, NO, NO, no_argument, "Off", BP_VALUE | BP_BOOL, "keepOnlyLatestTestResults", + "Keeps only the latest test result for each test case in the final report. Hence, the passed tests with retry attempts won't show their failed attempts."}, // options without short-options {349, "additional-unit-xctests", BLUEPILL_BINARY | BP_BINARY, NO, NO, required_argument, NULL, BP_LIST | BP_PATH, "additionalUnitTestBundles",