├── resources
├── scr1.png
├── scr2.png
├── scr3.png
├── header.png
├── example_cropped.gif
└── example
│ ├── resources
│ ├── Screenshot_47A75F84-354F-404E-B959-AD58198B938F.png
│ ├── Screenshot_A343C7F1-A5DA-49EE-B3FB-3F2A61188752.png
│ └── Screenshot_C55265E9-4AD5-4E6C-B740-AB146893BD23.png
│ └── result.html
├── .github
└── CODEOWNERS
├── XCSummary
├── Templates
│ ├── Template.html
│ ├── TestCaseTemplate.html
│ ├── TemplateHeader.h
│ ├── TestCaseTemplateFailed.html
│ ├── ActivityTemplateWithoutImage.html
│ ├── ActivityTemplateWithImage.html
│ ├── SummaryTemplate.html
│ ├── CMHTMLReportBuilder.h
│ ├── TemplateGeneratedHeader.h
│ └── CMHTMLReportBuilder.m
├── Models
│ ├── CMAttachment.h
│ ├── CMEntity.h
│ ├── CMEntity.m
│ ├── CMAttachment.m
│ ├── CMActivitySummary.h
│ ├── CMTestableSummary.h
│ ├── CMTest.h
│ ├── CMActivitySummary.m
│ ├── CMTestableSummary.m
│ └── CMTest.m
├── Parser
│ ├── CMTestSummaryParser.h
│ └── CMTestSummaryParser.m
├── Additions
│ ├── NSArrayAdditions.h
│ └── NSArrayAdditions.m
└── main.m
├── XCSummary.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── .infrastructure
└── azp
│ └── security-pipeline.yml
├── .gitignore
├── LICENSE
└── README.md
/resources/scr1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/scr1.png
--------------------------------------------------------------------------------
/resources/scr2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/scr2.png
--------------------------------------------------------------------------------
/resources/scr3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/scr3.png
--------------------------------------------------------------------------------
/resources/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/header.png
--------------------------------------------------------------------------------
/resources/example_cropped.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/example_cropped.gif
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Security checks owner is Security team
2 | .infrastructure/azp/security-pipeline.yml @MacPaw/security
--------------------------------------------------------------------------------
/resources/example/resources/Screenshot_47A75F84-354F-404E-B959-AD58198B938F.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/example/resources/Screenshot_47A75F84-354F-404E-B959-AD58198B938F.png
--------------------------------------------------------------------------------
/resources/example/resources/Screenshot_A343C7F1-A5DA-49EE-B3FB-3F2A61188752.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/example/resources/Screenshot_A343C7F1-A5DA-49EE-B3FB-3F2A61188752.png
--------------------------------------------------------------------------------
/resources/example/resources/Screenshot_C55265E9-4AD5-4E6C-B740-AB146893BD23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MacPaw/xcsummary/HEAD/resources/example/resources/Screenshot_C55265E9-4AD5-4E6C-B740-AB146893BD23.png
--------------------------------------------------------------------------------
/XCSummary/Templates/Template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | %@
9 |
10 |
11 |
--------------------------------------------------------------------------------
/XCSummary.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMAttachment.h:
--------------------------------------------------------------------------------
1 | //
2 | // CMAttachment.h
3 | // XCSummary
4 | //
5 | // Created by Volodymyr Borodavchuk on 10/3/18.
6 | // Copyright © 2018 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "CMEntity.h"
11 |
12 | @interface CMAttachment : CMEntity
13 |
14 | @property (nonatomic, copy, readonly) NSString *filename;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMEntity.h:
--------------------------------------------------------------------------------
1 | //
2 | // CMEntity.h
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface CMEntity : NSObject
14 |
15 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary;
16 |
17 | @end
18 |
19 | NS_ASSUME_NONNULL_END
20 |
--------------------------------------------------------------------------------
/XCSummary/Templates/TestCaseTemplate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | | %@ (%2.2f sec) |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/XCSummary/Templates/TemplateHeader.h:
--------------------------------------------------------------------------------
1 | #define TestCaseTemplate @"XCTestCaseTemplateXC"
2 |
3 |
4 | #define TestCaseTemplateFailed @"XCTestCaseTemplateFailedXC"
5 |
6 |
7 | #define ActivityTemplateWithoutImage @"XCActivityTemplateWithoutImageXC"
8 |
9 |
10 | #define ActivityTemplateWithImage @"XCActivityTemplateWithImageXC"
11 |
12 |
13 | #define Template @"XCTemplateXC"
14 |
15 |
16 | #define SummaryTemplate @"XCSummaryTemplateXC"
17 |
--------------------------------------------------------------------------------
/XCSummary/Templates/TestCaseTemplateFailed.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | | %@ (%2.2f sec) |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMEntity.m:
--------------------------------------------------------------------------------
1 | //
2 | // CMEntity.m
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMEntity.h"
10 |
11 | @implementation CMEntity
12 |
13 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary
14 | {
15 | self = [super init];
16 | if (self)
17 | {
18 | //No-op
19 | }
20 | return self;
21 | }
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/XCSummary/Templates/ActivityTemplateWithoutImage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | | %@ (%2.2f sec) |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/XCSummary/Templates/ActivityTemplateWithImage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | | %@ (%2.2f sec) |
6 |  |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.infrastructure/azp/security-pipeline.yml:
--------------------------------------------------------------------------------
1 | name: security-scan
2 | trigger: none
3 | pr:
4 | - master
5 |
6 | variables:
7 | - group: vault
8 | - group: github
9 |
10 | resources:
11 | repositories:
12 | - repository: sec-azp-pipelines
13 | type: github
14 | name: MacPaw/sec-azp-pipelines
15 | ref: refs/heads/main
16 | endpoint: MacPaw
17 |
18 | stages:
19 | - stage: main
20 | pool:
21 | name: default-sre
22 | displayName: scan
23 | jobs:
24 | - template: azp/templates/base-security-checks.yaml@sec-azp-pipelines
25 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMAttachment.m:
--------------------------------------------------------------------------------
1 | //
2 | // CMAttachment.m
3 | // xcsummary
4 | //
5 | // Created by Volodymyr Borodavchuk on 10/3/18.
6 | // Copyright © 2018 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "CMAttachment.h"
11 | #import "NSArrayAdditions.h"
12 |
13 | @implementation CMAttachment
14 |
15 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary
16 | {
17 | self = [super init];
18 | if (self)
19 | {
20 | _filename = dictionary[@"Filename"];
21 | }
22 | return self;
23 | }
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | DerivedData/
7 |
8 | ## Various settings
9 | *.pbxuser
10 | !default.pbxuser
11 | *.mode1v3
12 | !default.mode1v3
13 | *.mode2v3
14 | !default.mode2v3
15 | *.perspectivev3
16 | !default.perspectivev3
17 | xcuserdata/
18 |
19 | ## Other
20 | *.moved-aside
21 | *.xcuserstate
22 |
23 | ## Obj-C/Swift specific
24 | *.hmap
25 | *.ipa
26 | *.dSYM.zip
27 | *.dSYM
28 |
29 | /XCSummary/Templates/TemplateGeneratedHeader.h
30 | /build/XCSummary.build
31 |
--------------------------------------------------------------------------------
/XCSummary/Parser/CMTestSummaryParser.h:
--------------------------------------------------------------------------------
1 | //
2 | // CMTestSummaryParser.h
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @class CMTestableSummary;
14 | @interface CMTestSummaryParser : NSObject
15 |
16 | /**
17 | @param path NSString * The path of the 'summary'.plist. Commonly located at DerivedData/Project/Logs/Test
18 | @return CMTestSummaryParser
19 | */
20 | - (instancetype)initWithPath:(NSString *)path;
21 |
22 | @property (nonatomic, copy, readonly) NSString *path;
23 |
24 | /**
25 | @return NSArray * an array of the test summaries.
26 | */
27 | - (NSArray *)testSummaries;
28 |
29 | @end
30 |
31 | NS_ASSUME_NONNULL_END
32 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMActivitySummary.h:
--------------------------------------------------------------------------------
1 | //
2 | // CMActivitySummary.h
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMEntity.h"
10 | #import "CMAttachment.h"
11 |
12 | @interface CMActivitySummary : CMEntity
13 |
14 | @property (nonatomic, copy, readonly) NSString *title;
15 | @property (nonatomic, strong, readonly) NSUUID *uuid;
16 |
17 | @property (nonatomic, readonly) NSTimeInterval startTimeInterval;
18 | @property (nonatomic, readonly) NSTimeInterval finishTimeInterval;
19 |
20 | @property (nonatomic, readonly) BOOL hasElementsOfInterest;
21 | @property (nonatomic, readonly) BOOL hasScreenshotData;
22 |
23 | @property (nonatomic, strong, readonly) NSArray *subActivities;
24 | @property (nonatomic, strong, readonly) NSArray *attachments;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMTestableSummary.h:
--------------------------------------------------------------------------------
1 | //
2 | // CMTestableSummary.h
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "CMEntity.h"
11 |
12 | @class CMTest;
13 | @interface CMTestableSummary : CMEntity
14 |
15 | @property (nonatomic, copy, readonly) NSString *projectPath;
16 | @property (nonatomic, copy, readonly) NSString *targetName;
17 | @property (nonatomic, copy, readonly) NSString *testName;
18 | @property (nonatomic, copy, readonly) NSString *testObjectClass;
19 |
20 | @property (nonatomic, readonly) NSUInteger numberOfSuccessfulTests;
21 | @property (nonatomic, readonly) NSUInteger numberOfFailedTests;
22 | @property (nonatomic, readonly) NSTimeInterval totalDuration;
23 |
24 | @property (nonatomic, strong, readonly) NSArray *tests;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/XCSummary/Templates/SummaryTemplate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | | Test Results: |
6 |
7 |
8 | %d Tests
9 |
10 | (%@)
11 |
12 |
13 | %d successful
14 | %d failed
15 | |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/XCSummary/Parser/CMTestSummaryParser.m:
--------------------------------------------------------------------------------
1 | //
2 | // CMTestSummaryParser.m
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMTestSummaryParser.h"
10 | #import "NSArrayAdditions.h"
11 | #import "CMTestableSummary.h"
12 |
13 | @implementation CMTestSummaryParser
14 |
15 | - (instancetype)initWithPath:(NSString *)path
16 | {
17 | self = [super init];
18 | if (self)
19 | {
20 | _path = path;
21 | }
22 | return self;
23 | }
24 |
25 | - (NSArray *)testSummaries
26 | {
27 | NSDictionary *summaryInfo = [NSDictionary dictionaryWithContentsOfFile:self.path];
28 | NSArray *testSummaries = summaryInfo[@"TestableSummaries"];
29 | return [testSummaries map:^id(NSDictionary *testSummary, NSUInteger index, BOOL *stop) {
30 | return [[CMTestableSummary alloc] initWithDictionary:testSummary];
31 | }];
32 | }
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMTest.h:
--------------------------------------------------------------------------------
1 | //
2 | // CMTest.h
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMEntity.h"
10 |
11 | typedef NS_ENUM(NSInteger, CMTestStatus)
12 | {
13 | CMTestStatusUnknown = 0,
14 | CMTestStatusSuccess = 1,
15 | CMTestStatusFailure = 2
16 | };
17 |
18 | @class CMActivitySummary;
19 | @interface CMTest : CMEntity
20 |
21 | @property (nonatomic, strong, readonly) NSUUID *testSummaryGUID;
22 | @property (nonatomic, readonly) NSTimeInterval duration;
23 | @property (nonatomic, readonly) CMTestStatus status;
24 | @property (nonatomic, copy, readonly) NSString *testIdentifier;
25 | @property (nonatomic, copy, readonly) NSString *testName;
26 | @property (nonatomic, copy, readonly) NSString *testObjectClass;
27 |
28 | @property (nonatomic, strong, readonly) NSArray *subTests;
29 | @property (nonatomic, strong, readonly) NSArray *activities;
30 |
31 | @end
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 MacPaw Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/XCSummary/Templates/CMHTMLReportBuilder.h:
--------------------------------------------------------------------------------
1 | //
2 | // CMHTMLReportBuilder.h
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/13/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class CMTestableSummary, CMTest;
12 | @interface CMHTMLReportBuilder : NSObject
13 |
14 | /**
15 | Designated initializer
16 |
17 | @param path NSString attachments path
18 | @param resultsPath NSString
19 | @param showSuccessTests BOOL
20 | @return CMHTMLReportBuilder
21 | */
22 | - (instancetype)initWithAttachmentsPath:(NSString *)path
23 | resultsPath:(NSString *)resultsPath
24 | showSuccessTests:(BOOL)showSuccessTests;
25 |
26 | /**
27 | Appends summaries info as a header
28 |
29 | @param summaries NSArray *
30 | */
31 | - (void)appendSummaries:(NSArray *)summaries;
32 |
33 | /**
34 | Appends tests to the html template
35 |
36 | @param tests NSArray
37 | @param indentation CGGloat
38 | */
39 | - (void)appendTests:(NSArray *)tests indentation:(CGFloat)indentation;
40 |
41 | /**
42 | Builds a compilted html page
43 |
44 | @return NSString
45 | */
46 | - (NSString *)build;
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/XCSummary/Additions/NSArrayAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+Additions.h
3 | // xcsummary
4 | //
5 | // Created by Anton Mironov on 1/13/15.
6 | // Copyright (c) 2015 MacPaw. All rights reserved.
7 | //
8 |
9 | @import Foundation;
10 |
11 | @interface NSArray<__covariant ObjectType> (Extensions)
12 |
13 | - (NSMutableArray *)map:(id(^)(ObjectType object, NSUInteger index, BOOL *stop))mappingBlock;
14 | - (NSMutableArray *)flatMap:(NSArray *(^)(id object, NSUInteger index, BOOL *stop))mappingBlock;
15 | - (NSMutableArray *)filter:(BOOL(^)(ObjectType object, NSUInteger index, BOOL *stop))filteringBlock;
16 |
17 | @property (readonly) ObjectType singleObject; // non-observable
18 | @property (readonly) ObjectType singleObjectOrNil; // non-observable
19 |
20 | - (ObjectType)safeObjectAtIndex:(NSInteger)index;
21 |
22 | @end
23 |
24 |
25 | @interface NSMutableArray (Extensions)
26 |
27 | - (BOOL)safeAddObject:(ObjectType)object;
28 | - (void)filterIn:(BOOL(^)(ObjectType object, NSUInteger index, BOOL *stop))filteringBlock;
29 |
30 | - (NSArray *)takeObjectsAtIndexes:(NSIndexSet *)indexes;
31 | - (ObjectType)takeObjectAtIndex:(NSUInteger)index;
32 | - (ObjectType)takeLastObject;
33 | - (ObjectType)takeFirstObject;
34 |
35 | @end
36 |
37 | @interface NSArray (CAAdditions)
38 |
39 | - (instancetype)normalizedKeyTimes;
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMActivitySummary.m:
--------------------------------------------------------------------------------
1 | //
2 | // CMActivitySummary.m
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMActivitySummary.h"
10 | #import "NSArrayAdditions.h"
11 |
12 | @implementation CMActivitySummary
13 |
14 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary
15 | {
16 | self = [super init];
17 | if (self)
18 | {
19 | NSString *uuidString = dictionary[@"UUID"];
20 | NSArray *subActivitesInfo = dictionary[@"SubActivities"];
21 | NSArray *attachmetnsInfo = dictionary[@"Attachments"];
22 | _title = dictionary[@"Title"];
23 | _uuid = [[NSUUID alloc] initWithUUIDString:uuidString];
24 | _startTimeInterval = [dictionary[@"StartTimeInterval"] doubleValue];
25 | _finishTimeInterval = [dictionary[@"FinishTimeInterval"] doubleValue];
26 | _hasScreenshotData = [dictionary[@"HasScreenshotData"] boolValue];
27 | _hasElementsOfInterest = [dictionary[@"HasElementsOfInterest"] boolValue];
28 | _subActivities = [subActivitesInfo map:^id(NSDictionary *activityInfo, NSUInteger index, BOOL *stop) {
29 | return [[CMActivitySummary alloc] initWithDictionary:activityInfo];
30 | }];
31 | _attachments = [attachmetnsInfo map:^id(NSDictionary *attachmetnsInfo, NSUInteger index, BOOL *stop) {
32 | return [[CMAttachment alloc] initWithDictionary:attachmetnsInfo];
33 | }];
34 | }
35 | return self;
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMTestableSummary.m:
--------------------------------------------------------------------------------
1 | //
2 | // CMTestableSummary.m
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMTestableSummary.h"
10 | #import "NSArrayAdditions.h"
11 | #import "CMTest.h"
12 |
13 | @implementation CMTestableSummary
14 |
15 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary
16 | {
17 | self = [super initWithDictionary:dictionary];
18 | if (self)
19 | {
20 | NSArray *rawTests = dictionary[@"Tests"];
21 | _projectPath = dictionary[@"ProjectPath"];
22 | _targetName = dictionary[@"TargetName"];
23 | _testName = dictionary[@"TestName"];
24 | _testObjectClass = dictionary[@"TestObjectClass"];
25 | _tests = [rawTests map:^id(NSDictionary *rawTest, NSUInteger index, BOOL *stop) {
26 | return [[CMTest alloc] initWithDictionary:rawTest];
27 | }];
28 | _numberOfSuccessfulTests = [self _countOfTests:self.tests withStatus:CMTestStatusSuccess];
29 | _numberOfFailedTests = [self _countOfTests:self.tests withStatus:CMTestStatusFailure];
30 | _totalDuration = [[self.tests valueForKeyPath:@"@sum.duration"] doubleValue];
31 | }
32 | return self;
33 | }
34 |
35 | - (NSInteger)_countOfTests:(NSArray *)tests withStatus:(CMTestStatus)status
36 | {
37 | __block NSInteger result = 0;
38 | [tests enumerateObjectsUsingBlock:^(CMTest * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
39 | if (obj.status == status) {
40 | result++;
41 | }
42 | result += [self _countOfTests:obj.subTests withStatus:status];
43 | }];
44 | return result;
45 | }
46 |
47 | - (NSString *)description
48 | {
49 | return [NSString stringWithFormat:@"\n%@ %@ %@\n", [super description], self.testName, self.tests];
50 | }
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/XCSummary/Models/CMTest.m:
--------------------------------------------------------------------------------
1 | //
2 | // CMTest.m
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/12/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMTest.h"
10 | #import "NSArrayAdditions.h"
11 | #import "CMActivitySummary.h"
12 |
13 | CMTestStatus CMTestStatusFromString(NSString * string);
14 |
15 | @implementation CMTest
16 |
17 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary
18 | {
19 | self = [super initWithDictionary:dictionary];
20 | if (self)
21 | {
22 | NSArray *rawTests = dictionary[@"Subtests"];
23 | NSString *status = dictionary[@"TestStatus"];
24 | NSArray *activities = dictionary[@"ActivitySummaries"];
25 | NSString *summaryGUIDString = dictionary[@"TestSummaryGUID"];
26 | _testName = dictionary[@"TestName"];
27 | _testIdentifier = dictionary[@"TestIdentifier"];
28 | _testObjectClass = dictionary[@"TestObjectClass"];
29 | _duration = [dictionary[@"Duration"] doubleValue];
30 | _subTests = [rawTests map:^id(NSDictionary *rawTest, NSUInteger index, BOOL *stop) {
31 | return [[CMTest alloc] initWithDictionary:rawTest];
32 | }];
33 | _activities = [activities map:^id(NSDictionary *rawActivity, NSUInteger index, BOOL *stop) {
34 | return [[CMActivitySummary alloc] initWithDictionary:rawActivity];
35 | }];
36 | _status = CMTestStatusFromString(status);
37 | _testSummaryGUID = [[NSUUID alloc] initWithUUIDString:summaryGUIDString];
38 | }
39 | return self;
40 | }
41 |
42 | - (NSString *)description
43 | {
44 | return [NSString stringWithFormat:@"\n%@ %@ %@\n", [super description], self.testName, self.subTests];
45 | }
46 |
47 | @end
48 |
49 | CMTestStatus CMTestStatusFromString(NSString * string)
50 | {
51 | CMTestStatus status = CMTestStatusUnknown;
52 | if ([string isEqualToString:@"Failure"])
53 | {
54 | status = CMTestStatusFailure;
55 | }
56 | else if ([string isEqualToString:@"Success"])
57 | {
58 | status = CMTestStatusSuccess;
59 | }
60 | return status;
61 | }
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | `xcsummary` is a macOS command line tool that parses `xcbodebuild` test results and generates html output with activity screenshots. We use it as a part of our CI Server and find it very useful and helpful. We'd like to share it.
4 |
5 | * [Installation](#installation)
6 | * [Usage](#usage)
7 | * [Example](#example)
8 | * [Output](#output)
9 | * [Inspiration](#inspiration)
10 | * [Links](#links)
11 | * [Contribution](#contribution)
12 |
13 | ## Installation
14 |
15 | Open terminal and do the following:
16 |
17 | * Clone the project.
18 | ```shell
19 | git clone https://github.com/MacPaw/xcsummary.git
20 | ```
21 |
22 | * Navigate to source folder
23 | ```shell
24 | cd xcsummary
25 | ```
26 |
27 | * Copy binary to the folder you prefer. For example /usr/local/bin/
28 | ```shell
29 | cp build/xcsummary /usr/local/bin/xcsummary
30 | ```
31 | ## Usage
32 |
33 | As easy as pie:
34 | ```shell
35 | xcsummary -in -out
36 | ```
37 |
38 | Optional -show_success argument can be used to expand green test cases as well.
39 |
40 | ## Example
41 |
42 | Tip: we use `-resultBundlePath` parameter in xcodebuild to know the exact location for every single build.
43 | ```shell
44 | #Run your test as usual
45 | xcodebuild test -workspace YourProject.xcworkspace -scheme YourProjectScheme -resultBundlePath ''
46 | #Here we suggest to browse folder to check the folders structure.
47 | #... and generate the report
48 | xcsummary -in /1_Test/action_TestSummaries.plist -out ~/Desktop/result.html
49 | ```
50 |
51 | 
52 |
53 | ## Screenshots
54 |
55 | 
56 | 
57 | 
58 |
59 | ## Output
60 |
61 | [Live HTML Example](http://htmlpreview.github.io/?https://github.com/MacPaw/xcsummary/blob/master/resources/example/result.html)
62 |
63 | ## Inspiration
64 | xcsummary is completely inspired by [xcpretty](https://github.com/supermarin/xcpretty), we really love it and use it. But since xcpretty has some [issues](https://github.com/supermarin/xcpretty/issues/251) with screenshots collection, we decided to create our own one :)
65 |
66 | ## Links
67 | Great article to understand [Test Logs in Xcode](http://michele.io/test-logs-in-xcode)
68 |
69 | ## Contribution
70 | Feel free to open issues and pull requests.
71 |
--------------------------------------------------------------------------------
/XCSummary/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/13/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "CMTestSummaryParser.h"
11 | #import "CMHTMLReportBuilder.h"
12 | #import "CMTestableSummary.h"
13 | #import "CMTest.h"
14 |
15 | NSString *CMSummaryGetValue(NSArray *arguments, NSString *argument);
16 | BOOL CMSummaryValueExists(NSArray *arguments, NSString *argument);
17 |
18 | int main(int argc, const char * argv[]) {
19 | @autoreleasepool {
20 |
21 | NSArray *arguments = [[NSProcessInfo processInfo] arguments];
22 | if (arguments.count < 3) {
23 | NSLog(@"Not enough arguments %@", arguments);
24 | return EXIT_FAILURE;
25 | }
26 |
27 | NSString *input = CMSummaryGetValue(arguments, @"-in");
28 | NSString *output = CMSummaryGetValue(arguments, @"-out");
29 | if (!input || !output) {
30 | NSLog(@"-in or -out was not provided %@", arguments);
31 | return EXIT_FAILURE;
32 | }
33 |
34 | NSString *summaryPath = [input stringByExpandingTildeInPath];
35 | NSString *attachmentsPath = [[summaryPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"Attachments"];
36 |
37 | CMTestSummaryParser *parser = [[CMTestSummaryParser alloc] initWithPath:summaryPath];
38 | NSArray *summaries = [parser testSummaries];
39 |
40 | BOOL showSuccess = CMSummaryValueExists(arguments, @"-show_success");
41 | CMHTMLReportBuilder *builder = [[CMHTMLReportBuilder alloc] initWithAttachmentsPath:attachmentsPath
42 | resultsPath:output.stringByExpandingTildeInPath
43 | showSuccessTests:showSuccess];
44 | [builder appendSummaries:summaries];
45 | [summaries enumerateObjectsUsingBlock:^(CMTestableSummary *summary, NSUInteger idx, BOOL * _Nonnull stop) {
46 | [builder appendTests:summary.tests indentation:10.0f];
47 | }];
48 |
49 | NSString *htmlResult = [builder build];
50 | return [[htmlResult dataUsingEncoding:NSUTF8StringEncoding] writeToFile:output.stringByExpandingTildeInPath
51 | atomically:YES] == YES ? EXIT_SUCCESS : EXIT_FAILURE;
52 | }
53 | return 0;
54 | }
55 |
56 | NSString *CMSummaryGetValue(NSArray *arguments, NSString *argument) {
57 | NSInteger index = [arguments indexOfObject:argument];
58 | if (index != NSNotFound) {
59 | return arguments[index+1];
60 | }
61 | return nil;
62 | }
63 |
64 | BOOL CMSummaryValueExists(NSArray *arguments, NSString *argument) {
65 | NSInteger index = [arguments indexOfObject:argument];
66 | return index != NSNotFound;
67 | }
68 |
--------------------------------------------------------------------------------
/XCSummary/Templates/TemplateGeneratedHeader.h:
--------------------------------------------------------------------------------
1 | #define TestCaseTemplate @"PCEtLSBDb21tb24gVGVzdGNhc2UgLS0+CjxkaXYgc3R5bGU9Im1hcmdpbi1sZWZ0OiAlMi4yZiVAOyBiYWNrZ3JvdW5kLWNvbG9yOiAjQ0JGNEEzOyBwYWRkaW5nOjEwcHg7IHRleHQtYWxpZ246IHJpZ2h0OyI+CiAgICA8dGFibGUgc3R5bGU9IndpZHRoOiAxMDAlIj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0ZCBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsiPiVAIDxkaXYgc3R5bGU9ImRpc3BsYXk6aW5saW5lOyBjb2xvcjogZ3JleSI+KCUyLjJmIHNlYyk8L2Rpdj4gPC90ZD4KICAgICAgICA8L3RyPgogICAgPC90YWJsZT4KPC9kaXY+CjwhLS0gU2VwYXJhdG9yIC0tPgo8ZGl2IHN0eWxlPSJtYXJnaW46MTBweCI+PC9kaXY+Cg=="
2 |
3 |
4 | #define TestCaseTemplateFailed @"PCEtLSBUZXN0Y2FzZSBGYWlsZWQgLS0+CjxkaXYgc3R5bGU9Im1hcmdpbi1sZWZ0OiAlMi4yZiVAOyBiYWNrZ3JvdW5kLWNvbG9yOiAjRkY2MjdDOyBwYWRkaW5nOjEwcHg7IHRleHQtYWxpZ246IHJpZ2h0OyI+CiAgICA8dGFibGUgc3R5bGU9IndpZHRoOiAxMDAlIj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0ZCBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsiPiVAIDxkaXYgc3R5bGU9ImRpc3BsYXk6aW5saW5lOyBjb2xvcjogI2QzZDNkMyI+KCUyLjJmIHNlYyk8L2Rpdj48L3RkPgogICAgICAgIDwvdHI+CiAgICA8L3RhYmxlPgo8L2Rpdj4KPCEtLSBTZXBhcmF0b3IgLS0+CjxkaXYgc3R5bGU9Im1hcmdpbjoxMHB4Ij48L2Rpdj4K"
5 |
6 |
7 | #define ActivityTemplateWithoutImage @"PCEtLSBDb21tb24gQWN0aXZpdHkgV2l0aG91dCBJbWFnZSAtLT4KPGRpdiBzdHlsZT0ibWFyZ2luLWxlZnQ6ICUyLjJmJUA7IGJhY2tncm91bmQtY29sb3I6ICNGM0YzRjM7IHBhZGRpbmc6MnB4OyB0ZXh0LWFsaWduOiByaWdodDsgZm9udC1zaXplOjkwJSUiPgogICAgPHRhYmxlIHN0eWxlPSJ3aWR0aDogMTAwJSI+CiAgICAgICAgPHRyPgogICAgICAgICAgICA8dGQgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7Ij4lQCA8ZGl2IHN0eWxlPSJkaXNwbGF5OmlubGluZTsgY29sb3I6IGdyZXkiPiglMi4yZiBzZWMpPC9kaXY+PC90ZD4KICAgICAgICA8L3RyPgogICAgPC90YWJsZT4KPC9kaXY+CjwhLS0gU2VwYXJhdG9yIC0tPgo8ZGl2IHN0eWxlPSJtYXJnaW46NHB4Ij48L2Rpdj4K"
8 |
9 |
10 | #define ActivityTemplateWithImage @"PCEtLSBDb21tb24gQWN0aXZpdHkgV2l0aCBJbWFnZSAtLT4KPGRpdiBzdHlsZT0ibWFyZ2luLWxlZnQ6ICUyLjJmJUA7IGJhY2tncm91bmQtY29sb3I6ICNGM0YzRjM7IHBhZGRpbmc6MnB4OyB0ZXh0LWFsaWduOiByaWdodDsgZm9udC1zaXplOjkwJSUiPgogICAgPHRhYmxlIHN0eWxlPSJ3aWR0aDogMTAwJSUiPgogICAgICAgIDx0cj4KICAgICAgICAgICAgPHRkIHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyI+JUAgPGRpdiBzdHlsZT0iZGlzcGxheTppbmxpbmU7IGNvbG9yOiBncmV5Ij4oJTIuMmYgc2VjKTwvZGl2PiA8L3RkPgogICAgICAgICAgICA8dGQ+PGltZyBzdHlsZT0idGV4dC1hbGlnbjogcmlnaHQ7IHdpZHRoOiA2MDBweDsgaGVpZ2h0OiBhdXRvOyIgc3JjPSIlQCI+PC90ZD4KICAgICAgICA8L3RyPgogICAgPC90YWJsZT4KPC9kaXY+CjwhLS0gU2VwYXJhdG9yIC0tPgo8ZGl2IHN0eWxlPSJtYXJnaW46NHB4Ij48L2Rpdj4K"
11 |
12 |
13 | #define Template @"PCFET0NUWVBFIGh0bWw+CjxodG1sPgogICAgPGhlYWQ+CiAgICAgICAgPG1ldGEgY2hhcnNldD0iVVRGLTgiPgogICAgICAgIDx0aXRsZT48L3RpdGxlPgogICAgPC9oZWFkPgogICAgPGJvZHk+CiAgICAgICAgJUAKICAgIDwvYm9keT4KPC9odG1sPgo="
14 |
15 |
16 | #define SummaryTemplate @"PCEtLSBUZXN0IFN1bW1hcnkgLS0+CjxkaXYgc3R5bGU9Im1hcmdpbi1sZWZ0OiAxMHB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjZWVlZWVlOyBwYWRkaW5nOjRweDsgdGV4dC1hbGlnbjogcmlnaHQ7IGZvbnQtc2l6ZToyMHB4Ij4KICAgIDx0YWJsZSBzdHlsZT0id2lkdGg6IDEwMCUiPgogICAgICAgIDx0cj4KICAgICAgICAgICAgPHRkIHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0O3BhZGRpbmc6MTBweDsiPlRlc3QgUmVzdWx0czo8L3RkPgogICAgICAgICAgICA8dGQgc3R5bGU9InRleHQtYWxpZ246IHJpZ2h0O3BhZGRpbmc6MTBweDsiPgogICAgICAgICAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTppbmxpbmU7bWFyZ2luOjEwcHgiPgogICAgICAgICAgICAgICAgICAgICVkIFRlc3RzCiAgICAgICAgICAgICAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTppbmxpbmU7Y29sb3I6Z3JheTtmb250LXNpemU6MTRweCI+CiAgICAgICAgICAgICAgICAgICAgICAgICglQCkKICAgICAgICAgICAgICAgICAgICA8L2Rpdj4KICAgICAgICAgICAgICAgIDwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTppbmxpbmU7bWFyZ2luOjEwcHg7Y29sb3I6IzAwQUU0RCI+JWQgc3VjY2Vzc2Z1bDwvZGl2PgogICAgICAgICAgICAgICAgPGRpdiBzdHlsZT0iZGlzcGxheTolQDttYXJnaW46MTBweDtjb2xvcjojRkY2MjdDIj4lZCBmYWlsZWQ8L2Rpdj4KICAgICAgICAgICAgPC90ZD4KICAgICAgICA8L3RyPgogICAgPC90YWJsZT4KPC9kaXY+Cgo8IS0tIFNlcGFyYXRvciAtLT4KPGRpdiBzdHlsZT0ibWFyZ2luOjEwcHgiPjwvZGl2Pgo="
17 |
--------------------------------------------------------------------------------
/XCSummary/Additions/NSArrayAdditions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSArray+Additions.m
3 | // xcsummary
4 | //
5 | // Created by Anton Mironov on 1/13/15.
6 | // Copyright (c) 2015 MacPaw. All rights reserved.
7 | //
8 |
9 | #import "NSArrayAdditions.h"
10 |
11 | @implementation NSArray (Extensions)
12 |
13 | - (NSMutableArray *)map:(id(^)(id object, NSUInteger index, BOOL *stop))mappingBlock
14 | {
15 | NSMutableArray *result = [NSMutableArray arrayWithCapacity:self.count];
16 |
17 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
18 | {
19 | id mappedObject = mappingBlock(obj, idx, stop);
20 | if (mappedObject) {
21 | [result addObject:mappedObject];
22 | }
23 | }];
24 |
25 | return result;
26 | }
27 |
28 | - (NSMutableArray *)flatMap:(NSArray *(^)(id object, NSUInteger index, BOOL *stop))mappingBlock
29 | {
30 | NSMutableArray *result = [NSMutableArray array];
31 |
32 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
33 | {
34 | NSArray *mappedArray = mappingBlock(obj, idx, stop);
35 | if (mappedArray.count > 0) {
36 | [result addObjectsFromArray:mappedArray];
37 | }
38 | }];
39 |
40 | return result;
41 | }
42 |
43 | - (NSMutableArray *)filter:(BOOL(^)(id object, NSUInteger index, BOOL *stop))filteringBlock
44 | {
45 | return [self map:^id(id object, NSUInteger index, BOOL *stop)
46 | {
47 | return filteringBlock(object, index, stop) ? object : nil;
48 | }];
49 | }
50 |
51 | - (id)singleObject
52 | {
53 | NSAssert(self.count <= 1, @"This method may not be applied for containers with multiple items");
54 | return self.lastObject;
55 | }
56 |
57 | - (id)singleObjectOrNil
58 | {
59 | return (1 == self.count) ? self.lastObject : nil;
60 | }
61 |
62 | - (id)safeObjectAtIndex:(NSInteger)index
63 | {
64 | id result = nil;
65 | if (0 <= index && index < self.count)
66 | {
67 | result = [self objectAtIndex:index];
68 | }
69 | return result;
70 | }
71 |
72 | @end
73 |
74 |
75 | @implementation NSMutableArray (Extensions)
76 |
77 | - (BOOL)safeAddObject:(id)object
78 | {
79 | BOOL didAdd;
80 |
81 | if (object)
82 | {
83 | [self addObject:object];
84 | didAdd = YES;
85 | }
86 | else
87 | {
88 | NSAssert(NO, @"Attempt to add an empty object");
89 | didAdd = NO;
90 | }
91 |
92 | return didAdd;
93 | }
94 |
95 | - (void)filterIn:(BOOL(^)(id object, NSUInteger index, BOOL *stop))filteringBlock
96 | {
97 | NSMutableIndexSet *indexesOfObjectsToRemove = [NSMutableIndexSet new];
98 | [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop)
99 | {
100 | if (!filteringBlock(obj, idx, stop))
101 | {
102 | [indexesOfObjectsToRemove addIndex:idx];
103 | }
104 | }];
105 | [self removeObjectsAtIndexes:indexesOfObjectsToRemove];
106 | }
107 |
108 | - (NSArray *)takeObjectsAtIndexes:(NSIndexSet *)indexes
109 | {
110 | NSArray *result = [self objectsAtIndexes:indexes];
111 | [self removeObjectsAtIndexes:indexes];
112 | return result;
113 | }
114 |
115 | - (id)takeObjectAtIndex:(NSUInteger)index
116 | {
117 | id result = [self objectAtIndex:index];
118 | [self removeObjectAtIndex:index];
119 | return result;
120 | }
121 |
122 | - (id)takeLastObject
123 | {
124 | NSUInteger count = self.count;
125 | return (count > 0) ? [self takeObjectAtIndex:count - 1] : nil;
126 | }
127 |
128 | - (id)takeFirstObject
129 | {
130 | NSUInteger count = self.count;
131 | return (count > 0) ? [self takeObjectAtIndex:0] : nil;
132 | }
133 |
134 | @end
135 |
136 | @implementation NSArray (CAAdditions)
137 |
138 | - (instancetype)normalizedKeyTimes
139 | {
140 | CGFloat maxValue = [self.firstObject doubleValue];
141 | for (NSNumber *keyTime in self)
142 | {
143 | maxValue = MAX(maxValue, keyTime.doubleValue);
144 | }
145 |
146 | return [self map:^id(NSNumber *keyTime, NSUInteger index, BOOL *stop)
147 | {
148 | return @(keyTime.doubleValue / maxValue);
149 | }];
150 | }
151 |
152 | @end
153 |
--------------------------------------------------------------------------------
/XCSummary/Templates/CMHTMLReportBuilder.m:
--------------------------------------------------------------------------------
1 | //
2 | // CMHTMLReportBuilder.m
3 | // xcsummary
4 | //
5 | // Created by Kryvoblotskyi Sergii on 12/13/16.
6 | // Copyright © 2016 MacPaw inc. All rights reserved.
7 | //
8 |
9 | #import "CMHTMLReportBuilder.h"
10 | #import "CMTest.h"
11 | #import "CMTestableSummary.h"
12 | #import "CMActivitySummary.h"
13 | #import "TemplateGeneratedHeader.h"
14 |
15 | @interface CMHTMLReportBuilder ()
16 |
17 | @property (nonatomic, copy) NSString *path;
18 | @property (nonatomic, copy) NSString *resultsPath;
19 | @property (nonatomic, copy) NSString *htmlResourcePath;
20 |
21 | @property (nonatomic, strong) NSMutableString *resultString;
22 | @property (nonatomic, strong) NSFileManager *fileManager;
23 | @property (nonatomic) BOOL showSuccessTests;
24 |
25 | @property (nonatomic, strong) NSDateComponentsFormatter *timeFormatter;
26 |
27 | @end
28 |
29 | @implementation CMHTMLReportBuilder
30 |
31 | - (instancetype)initWithAttachmentsPath:(NSString *)path
32 | resultsPath:(NSString *)resultsPath
33 | showSuccessTests:(BOOL)showSuccessTests
34 | {
35 | self = [super init];
36 | if (self)
37 | {
38 | _fileManager = [NSFileManager defaultManager];
39 | _path = path;
40 | _resultsPath = resultsPath;
41 | _htmlResourcePath = [[resultsPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"resources"];
42 | _resultString = [NSMutableString new];
43 | _showSuccessTests = showSuccessTests;
44 | [self _prepareResourceFolder];
45 | }
46 | return self;
47 | }
48 |
49 | - (NSDateComponentsFormatter *)timeFormatter
50 | {
51 | if (!_timeFormatter)
52 | {
53 | NSDateComponentsFormatter *formatter = [NSDateComponentsFormatter new];
54 | formatter.unitsStyle = NSDateComponentsFormatterUnitsStyleAbbreviated;
55 | formatter.allowedUnits = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
56 | _timeFormatter = formatter;
57 | }
58 | return _timeFormatter;
59 | }
60 |
61 | #pragma mark - Public
62 |
63 | - (void)appendSummaries:(NSArray *)summaries
64 | {
65 | NSUInteger successfullTests = [[summaries valueForKeyPath:@"@sum.numberOfSuccessfulTests"] integerValue];
66 | NSUInteger failedTests = [[summaries valueForKeyPath:@"@sum.numberOfFailedTests"] integerValue];
67 |
68 | BOOL failuresPresent = failedTests > 0;
69 | NSString *templateFormat = [self _decodeTemplateWithName:SummaryTemplate];
70 | NSTimeInterval totalTime = [[summaries valueForKeyPath:@"@sum.totalDuration"] doubleValue];
71 | NSString *timeString = [self.timeFormatter stringFromTimeInterval:totalTime];
72 | NSString *header = [NSString stringWithFormat:templateFormat, successfullTests + failedTests, timeString, successfullTests, failuresPresent ? @"inline": @"none", failedTests];
73 | [self.resultString appendString:header];
74 | }
75 |
76 | - (void)appendTests:(NSArray *)tests indentation:(CGFloat)indentation
77 | {
78 | [tests enumerateObjectsUsingBlock:^(CMTest * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
79 |
80 | [self _appendTestCase:obj indentation:indentation];
81 | if (obj.subTests.count > 0)
82 | {
83 | [self appendTests:obj.subTests indentation:indentation + 50];
84 | }
85 | else
86 | {
87 | if (self.showSuccessTests == NO)
88 | {
89 | if (obj.status == CMTestStatusFailure)
90 | {
91 | [self _appendActivities:obj.activities indentation:indentation + 50];
92 | }
93 | }
94 | else
95 | {
96 | [self _appendActivities:obj.activities indentation:indentation + 50];
97 | }
98 | }
99 | }];
100 | }
101 | - (NSString *)build
102 | {
103 | NSString *templateFormat = [self _decodeTemplateWithName:Template];
104 | return [NSString stringWithFormat:templateFormat, self.resultString.copy];
105 | }
106 |
107 | #pragma mark - Private
108 |
109 | - (NSString *)_decodeTemplateWithName:(NSString *)fileName
110 | {
111 | NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:fileName options:0];
112 | NSString *format = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
113 | return format;
114 | }
115 |
116 | - (void)_appendTestCase:(CMTest *)testCase indentation:(CGFloat)indentation
117 | {
118 | NSString *templateFormat = testCase.status == CMTestStatusFailure ?
119 | [self _decodeTemplateWithName:TestCaseTemplateFailed] :
120 | [self _decodeTemplateWithName:TestCaseTemplate];
121 | NSString *composedString = [NSString stringWithFormat:templateFormat, indentation, @"px", testCase.testName, testCase.duration];
122 | [self.resultString appendString:composedString];
123 | }
124 |
125 | - (void)_appendActivities:(NSArray *)activities indentation:(CGFloat)indentation
126 | {
127 | [activities enumerateObjectsUsingBlock:^(CMActivitySummary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
128 | [self _appendActivity:obj indentation:indentation];
129 | [self _appendActivities:obj.subActivities indentation:indentation + 50];
130 | }];
131 | }
132 |
133 | - (void)_appendActivity:(CMActivitySummary *)activity indentation:(CGFloat)indentation
134 | {
135 | NSString *templateFormat = nil;
136 | NSString *composedString = nil;
137 | CMAttachment *attachment = nil;
138 |
139 | attachment = [activity.attachments firstObject];
140 |
141 | if (activity.hasScreenshotData && attachment)
142 | {
143 | templateFormat = [self _decodeTemplateWithName:ActivityTemplateWithImage];
144 | NSString *imageName = [NSString stringWithFormat:@"%@", attachment.filename];
145 | NSString *fullPath = [self.path stringByAppendingPathComponent:imageName];
146 |
147 | [self.fileManager copyItemAtPath:fullPath toPath:[self.htmlResourcePath stringByAppendingPathComponent:imageName] error:nil];
148 |
149 | NSString *localImageName = [NSString stringWithFormat:@"resources/%@", imageName];
150 | composedString = [NSString stringWithFormat:templateFormat, indentation, @"px", activity.title, activity.finishTimeInterval - activity.startTimeInterval, localImageName];
151 | }
152 | else
153 | {
154 | templateFormat = [self _decodeTemplateWithName:ActivityTemplateWithoutImage];
155 | composedString = [NSString stringWithFormat:templateFormat, indentation, @"px", activity.title, activity.finishTimeInterval - activity.startTimeInterval];
156 | }
157 |
158 | [self.resultString appendString:composedString];
159 | }
160 |
161 | #pragma mark - File Operations
162 |
163 | - (void)_prepareResourceFolder
164 | {
165 | if ([self.fileManager fileExistsAtPath:self.htmlResourcePath] == NO) {
166 | [self.fileManager createDirectoryAtPath:self.htmlResourcePath withIntermediateDirectories:NO attributes:nil error:nil];
167 | }
168 | }
169 |
170 | @end
171 |
--------------------------------------------------------------------------------
/resources/example/result.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | | Selected tests (81.60 sec) |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | | UITests.xctest (81.60 sec) |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | | ActionvationRegularActionsUITests (22.24 sec) |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | | testLicenseActivation() (15.16 sec) |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | | testLicenseDeactivation() (7.08 sec) |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | | ActivationUITests (59.35 sec) |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | | testActivateButtonDisabled() (4.01 sec) |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | | testActivateButtonEnabled() (4.79 sec) |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | | testActivationDialogBuyButton() (2.64 sec) |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | | Start Test at 2016-12-13 14:42:14.718 (0.00 sec) |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | | Set Up (1.86 sec) |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | | Launch com.macpaw.CleanMyMac3 (1.85 sec) |
123 |  |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | | Terminate (0.20 sec) |
134 |  |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | | Wait for app to idle (0.22 sec) |
145 |  |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 | | Check predicate `exists == 1` against object `"License Activation" Window` (0.33 sec) |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 | | Snapshot accessibility hierarchy for com.macpaw.CleanMyMac3 (0.30 sec) |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | | Find: Descendants matching type Window (0.00 sec) |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | | Find: Elements matching predicate '"License Activation" IN identifiers' (0.00 sec) |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | | Snapshot accessibility hierarchy for com.macpaw.CleanMyMac3 (0.21 sec) |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 | | Find: Descendants matching type Window (0.00 sec) |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 | | Find: Elements matching predicate '"License Activation" IN identifiers' (0.00 sec) |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 | | Find: Descendants matching type StaticText (0.00 sec) |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 | | Find: Elements matching predicate '"Please enter your Activation Number:" IN identifiers' (0.00 sec) |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 | | Assertion Failure: ActivationUITests.swift:77: XCTAssertTrue failed - (0.00 sec) |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 | | Tear Down (0.00 sec) |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 | | testActivationDialogEnterCodeElements() (4.39 sec) |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 | | testActivationDialogLaterButton() (3.91 sec) |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 | | testActivationDialogUIElements() (2.89 sec) |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 | | testActivationErrorCancel() (8.51 sec) |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 | | testActivationErrorTryAgain() (8.44 sec) |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 | | testCancelInEnterCodeDialog() (4.97 sec) |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 | | testEnterInvalidCode() (7.35 sec) |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 | | testLostCodeButton() (5.08 sec) |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 | | testUnlockFullVersionButtonExists() (2.37 sec) |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
--------------------------------------------------------------------------------
/XCSummary.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | A7BC74361E0052640072AD47 /* NSArrayAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC741F1E0052640072AD47 /* NSArrayAdditions.m */; };
11 | A7BC74371E0052640072AD47 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC74201E0052640072AD47 /* main.m */; };
12 | A7BC74381E0052640072AD47 /* CMActivitySummary.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC74231E0052640072AD47 /* CMActivitySummary.m */; };
13 | A7BC74391E0052640072AD47 /* CMEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC74251E0052640072AD47 /* CMEntity.m */; };
14 | A7BC743A1E0052640072AD47 /* CMTest.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC74271E0052640072AD47 /* CMTest.m */; };
15 | A7BC743B1E0052640072AD47 /* CMTestableSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC74291E0052640072AD47 /* CMTestableSummary.m */; };
16 | A7BC743C1E0052640072AD47 /* CMTestSummaryParser.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC742C1E0052640072AD47 /* CMTestSummaryParser.m */; };
17 | A7BC743D1E0052640072AD47 /* CMHTMLReportBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = A7BC74321E0052640072AD47 /* CMHTMLReportBuilder.m */; };
18 | CB3DCA592165270000DA9D8C /* CMAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = CB3DCA582165270000DA9D8C /* CMAttachment.m */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXCopyFilesBuildPhase section */
22 | A7B461D11E00171C002B13B0 /* CopyFiles */ = {
23 | isa = PBXCopyFilesBuildPhase;
24 | buildActionMask = 2147483647;
25 | dstPath = /usr/share/man/man1/;
26 | dstSubfolderSpec = 0;
27 | files = (
28 | );
29 | runOnlyForDeploymentPostprocessing = 1;
30 | };
31 | /* End PBXCopyFilesBuildPhase section */
32 |
33 | /* Begin PBXFileReference section */
34 | A723D1D61E128315000F0F65 /* SummaryTemplate.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = SummaryTemplate.html; sourceTree = ""; };
35 | A7B461D31E00171C002B13B0 /* xcsummary */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xcsummary; sourceTree = BUILT_PRODUCTS_DIR; };
36 | A7BC741E1E0052640072AD47 /* NSArrayAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSArrayAdditions.h; sourceTree = ""; };
37 | A7BC741F1E0052640072AD47 /* NSArrayAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSArrayAdditions.m; sourceTree = ""; };
38 | A7BC74201E0052640072AD47 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
39 | A7BC74221E0052640072AD47 /* CMActivitySummary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMActivitySummary.h; sourceTree = ""; };
40 | A7BC74231E0052640072AD47 /* CMActivitySummary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMActivitySummary.m; sourceTree = ""; };
41 | A7BC74241E0052640072AD47 /* CMEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMEntity.h; sourceTree = ""; };
42 | A7BC74251E0052640072AD47 /* CMEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMEntity.m; sourceTree = ""; };
43 | A7BC74261E0052640072AD47 /* CMTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMTest.h; sourceTree = ""; };
44 | A7BC74271E0052640072AD47 /* CMTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMTest.m; sourceTree = ""; };
45 | A7BC74281E0052640072AD47 /* CMTestableSummary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMTestableSummary.h; sourceTree = ""; };
46 | A7BC74291E0052640072AD47 /* CMTestableSummary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMTestableSummary.m; sourceTree = ""; };
47 | A7BC742B1E0052640072AD47 /* CMTestSummaryParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMTestSummaryParser.h; sourceTree = ""; };
48 | A7BC742C1E0052640072AD47 /* CMTestSummaryParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMTestSummaryParser.m; sourceTree = ""; };
49 | A7BC742F1E0052640072AD47 /* ActivityTemplateWithImage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = ActivityTemplateWithImage.html; sourceTree = ""; };
50 | A7BC74301E0052640072AD47 /* ActivityTemplateWithoutImage.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = ActivityTemplateWithoutImage.html; sourceTree = ""; };
51 | A7BC74311E0052640072AD47 /* CMHTMLReportBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMHTMLReportBuilder.h; sourceTree = ""; };
52 | A7BC74321E0052640072AD47 /* CMHTMLReportBuilder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CMHTMLReportBuilder.m; sourceTree = ""; };
53 | A7BC74331E0052640072AD47 /* Template.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = Template.html; sourceTree = ""; };
54 | A7BC74341E0052640072AD47 /* TestCaseTemplate.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = TestCaseTemplate.html; sourceTree = ""; };
55 | A7BC74351E0052640072AD47 /* TestCaseTemplateFailed.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = TestCaseTemplateFailed.html; sourceTree = ""; };
56 | CB3DCA57216525E300DA9D8C /* CMAttachment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CMAttachment.h; sourceTree = ""; };
57 | CB3DCA582165270000DA9D8C /* CMAttachment.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CMAttachment.m; sourceTree = ""; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | A7B461D01E00171C002B13B0 /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | /* End PBXFrameworksBuildPhase section */
69 |
70 | /* Begin PBXGroup section */
71 | A7B461851DFEE9F6002B13B0 = {
72 | isa = PBXGroup;
73 | children = (
74 | A7BC741C1E0052640072AD47 /* XCSummary */,
75 | A7B4618F1DFEE9F6002B13B0 /* Products */,
76 | );
77 | sourceTree = "";
78 | };
79 | A7B4618F1DFEE9F6002B13B0 /* Products */ = {
80 | isa = PBXGroup;
81 | children = (
82 | A7B461D31E00171C002B13B0 /* xcsummary */,
83 | );
84 | name = Products;
85 | sourceTree = "";
86 | };
87 | A7BC741C1E0052640072AD47 /* XCSummary */ = {
88 | isa = PBXGroup;
89 | children = (
90 | A7BC74201E0052640072AD47 /* main.m */,
91 | A7BC741D1E0052640072AD47 /* Additions */,
92 | A7BC74211E0052640072AD47 /* Models */,
93 | A7BC742A1E0052640072AD47 /* Parser */,
94 | A7BC742E1E0052640072AD47 /* Templates */,
95 | );
96 | path = XCSummary;
97 | sourceTree = "";
98 | };
99 | A7BC741D1E0052640072AD47 /* Additions */ = {
100 | isa = PBXGroup;
101 | children = (
102 | A7BC741E1E0052640072AD47 /* NSArrayAdditions.h */,
103 | A7BC741F1E0052640072AD47 /* NSArrayAdditions.m */,
104 | );
105 | path = Additions;
106 | sourceTree = "";
107 | };
108 | A7BC74211E0052640072AD47 /* Models */ = {
109 | isa = PBXGroup;
110 | children = (
111 | A7BC74221E0052640072AD47 /* CMActivitySummary.h */,
112 | A7BC74231E0052640072AD47 /* CMActivitySummary.m */,
113 | A7BC74281E0052640072AD47 /* CMTestableSummary.h */,
114 | A7BC74291E0052640072AD47 /* CMTestableSummary.m */,
115 | A7BC74261E0052640072AD47 /* CMTest.h */,
116 | A7BC74271E0052640072AD47 /* CMTest.m */,
117 | A7BC74241E0052640072AD47 /* CMEntity.h */,
118 | A7BC74251E0052640072AD47 /* CMEntity.m */,
119 | CB3DCA57216525E300DA9D8C /* CMAttachment.h */,
120 | CB3DCA582165270000DA9D8C /* CMAttachment.m */,
121 | );
122 | path = Models;
123 | sourceTree = "";
124 | };
125 | A7BC742A1E0052640072AD47 /* Parser */ = {
126 | isa = PBXGroup;
127 | children = (
128 | A7BC742B1E0052640072AD47 /* CMTestSummaryParser.h */,
129 | A7BC742C1E0052640072AD47 /* CMTestSummaryParser.m */,
130 | );
131 | path = Parser;
132 | sourceTree = "";
133 | };
134 | A7BC742E1E0052640072AD47 /* Templates */ = {
135 | isa = PBXGroup;
136 | children = (
137 | A7BC74311E0052640072AD47 /* CMHTMLReportBuilder.h */,
138 | A7BC74321E0052640072AD47 /* CMHTMLReportBuilder.m */,
139 | A7BC742F1E0052640072AD47 /* ActivityTemplateWithImage.html */,
140 | A7BC74301E0052640072AD47 /* ActivityTemplateWithoutImage.html */,
141 | A7BC74341E0052640072AD47 /* TestCaseTemplate.html */,
142 | A7BC74351E0052640072AD47 /* TestCaseTemplateFailed.html */,
143 | A7BC74331E0052640072AD47 /* Template.html */,
144 | A723D1D61E128315000F0F65 /* SummaryTemplate.html */,
145 | );
146 | path = Templates;
147 | sourceTree = "";
148 | };
149 | /* End PBXGroup section */
150 |
151 | /* Begin PBXNativeTarget section */
152 | A7B461D21E00171C002B13B0 /* xcsummary */ = {
153 | isa = PBXNativeTarget;
154 | buildConfigurationList = A7B461D71E00171C002B13B0 /* Build configuration list for PBXNativeTarget "xcsummary" */;
155 | buildPhases = (
156 | A7BC743F1E005B6F0072AD47 /* Encode Templates */,
157 | A7B461CF1E00171C002B13B0 /* Sources */,
158 | A7B461D01E00171C002B13B0 /* Frameworks */,
159 | A7B461D11E00171C002B13B0 /* CopyFiles */,
160 | A701334C1E01AFBC00854391 /* chmod */,
161 | );
162 | buildRules = (
163 | );
164 | dependencies = (
165 | );
166 | name = xcsummary;
167 | productName = summary;
168 | productReference = A7B461D31E00171C002B13B0 /* xcsummary */;
169 | productType = "com.apple.product-type.tool";
170 | };
171 | /* End PBXNativeTarget section */
172 |
173 | /* Begin PBXProject section */
174 | A7B461861DFEE9F6002B13B0 /* Project object */ = {
175 | isa = PBXProject;
176 | attributes = {
177 | LastUpgradeCheck = 0810;
178 | ORGANIZATIONNAME = "MacPaw inc";
179 | TargetAttributes = {
180 | A7B461D21E00171C002B13B0 = {
181 | CreatedOnToolsVersion = 8.1;
182 | DevelopmentTeam = S8EX82NJP6;
183 | ProvisioningStyle = Automatic;
184 | };
185 | };
186 | };
187 | buildConfigurationList = A7B461891DFEE9F6002B13B0 /* Build configuration list for PBXProject "XCSummary" */;
188 | compatibilityVersion = "Xcode 3.2";
189 | developmentRegion = English;
190 | hasScannedForEncodings = 0;
191 | knownRegions = (
192 | en,
193 | Base,
194 | );
195 | mainGroup = A7B461851DFEE9F6002B13B0;
196 | productRefGroup = A7B4618F1DFEE9F6002B13B0 /* Products */;
197 | projectDirPath = "";
198 | projectRoot = "";
199 | targets = (
200 | A7B461D21E00171C002B13B0 /* xcsummary */,
201 | );
202 | };
203 | /* End PBXProject section */
204 |
205 | /* Begin PBXShellScriptBuildPhase section */
206 | A701334C1E01AFBC00854391 /* chmod */ = {
207 | isa = PBXShellScriptBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | );
211 | inputPaths = (
212 | );
213 | name = chmod;
214 | outputPaths = (
215 | );
216 | runOnlyForDeploymentPostprocessing = 0;
217 | shellPath = /bin/sh;
218 | shellScript = "chmod 777 build/xcsummary";
219 | };
220 | A7BC743F1E005B6F0072AD47 /* Encode Templates */ = {
221 | isa = PBXShellScriptBuildPhase;
222 | buildActionMask = 2147483647;
223 | files = (
224 | );
225 | inputPaths = (
226 | );
227 | name = "Encode Templates";
228 | outputPaths = (
229 | );
230 | runOnlyForDeploymentPostprocessing = 0;
231 | shellPath = /bin/sh;
232 | shellScript = "#!/bin/bash\n\ncd XCSummary/Templates\n\n#!/bin/bash\n\nTemplate=$(openssl base64 -in Template.html | tr -d '\\n')\nActivityTemplateWithoutImage=$(openssl base64 -in ActivityTemplateWithoutImage.html | tr -d '\\n')\nActivityTemplateWithImage=$(openssl base64 -in ActivityTemplateWithImage.html | tr -d '\\n')\nTestCaseTemplate=$(openssl base64 -in TestCaseTemplate.html | tr -d '\\n')\nTestCaseTemplateFailed=$(openssl base64 -in TestCaseTemplateFailed.html | tr -d '\\n')\nSummaryTemplate=$(openssl base64 -in SummaryTemplate.html | tr -d '\\n')\n\nsed \"s/XCTemplateXC/$Template/g\" TemplateHeader.h > temp.h\nsed \"s/XCActivityTemplateWithoutImageXC/$ActivityTemplateWithoutImage/g\" temp.h > temp1.h\nsed \"s/XCActivityTemplateWithImageXC/$ActivityTemplateWithImage/g\" temp1.h > temp2.h\nsed \"s/XCTestCaseTemplateXC/$TestCaseTemplate/g\" temp2.h > temp3.h\nsed \"s/XCTestCaseTemplateFailedXC/$TestCaseTemplateFailed/g\" temp3.h > temp4.h\nsed \"s/XCSummaryTemplateXC/$SummaryTemplate/g\" temp4.h > TemplateGeneratedHeader.h\n\nrm -rf temp.h\nrm -rf temp1.h\nrm -rf temp2.h\nrm -rf temp3.h\nrm -rf temp4.h";
233 | };
234 | /* End PBXShellScriptBuildPhase section */
235 |
236 | /* Begin PBXSourcesBuildPhase section */
237 | A7B461CF1E00171C002B13B0 /* Sources */ = {
238 | isa = PBXSourcesBuildPhase;
239 | buildActionMask = 2147483647;
240 | files = (
241 | A7BC743D1E0052640072AD47 /* CMHTMLReportBuilder.m in Sources */,
242 | A7BC74371E0052640072AD47 /* main.m in Sources */,
243 | A7BC74381E0052640072AD47 /* CMActivitySummary.m in Sources */,
244 | CB3DCA592165270000DA9D8C /* CMAttachment.m in Sources */,
245 | A7BC743C1E0052640072AD47 /* CMTestSummaryParser.m in Sources */,
246 | A7BC743B1E0052640072AD47 /* CMTestableSummary.m in Sources */,
247 | A7BC74391E0052640072AD47 /* CMEntity.m in Sources */,
248 | A7BC74361E0052640072AD47 /* NSArrayAdditions.m in Sources */,
249 | A7BC743A1E0052640072AD47 /* CMTest.m in Sources */,
250 | );
251 | runOnlyForDeploymentPostprocessing = 0;
252 | };
253 | /* End PBXSourcesBuildPhase section */
254 |
255 | /* Begin XCBuildConfiguration section */
256 | A7B461A01DFEE9F6002B13B0 /* Debug */ = {
257 | isa = XCBuildConfiguration;
258 | buildSettings = {
259 | ALWAYS_SEARCH_USER_PATHS = NO;
260 | CLANG_ANALYZER_NONNULL = YES;
261 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
262 | CLANG_CXX_LIBRARY = "libc++";
263 | CLANG_ENABLE_MODULES = YES;
264 | CLANG_ENABLE_OBJC_ARC = YES;
265 | CLANG_WARN_BOOL_CONVERSION = YES;
266 | CLANG_WARN_CONSTANT_CONVERSION = YES;
267 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
268 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
269 | CLANG_WARN_EMPTY_BODY = YES;
270 | CLANG_WARN_ENUM_CONVERSION = YES;
271 | CLANG_WARN_INFINITE_RECURSION = YES;
272 | CLANG_WARN_INT_CONVERSION = YES;
273 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
274 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
275 | CLANG_WARN_UNREACHABLE_CODE = YES;
276 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
277 | CODE_SIGN_IDENTITY = "-";
278 | COPY_PHASE_STRIP = NO;
279 | DEBUG_INFORMATION_FORMAT = dwarf;
280 | ENABLE_STRICT_OBJC_MSGSEND = YES;
281 | ENABLE_TESTABILITY = YES;
282 | GCC_C_LANGUAGE_STANDARD = gnu99;
283 | GCC_DYNAMIC_NO_PIC = NO;
284 | GCC_NO_COMMON_BLOCKS = YES;
285 | GCC_OPTIMIZATION_LEVEL = 0;
286 | GCC_PREPROCESSOR_DEFINITIONS = (
287 | "DEBUG=1",
288 | "$(inherited)",
289 | );
290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
292 | GCC_WARN_UNDECLARED_SELECTOR = YES;
293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
294 | GCC_WARN_UNUSED_FUNCTION = YES;
295 | GCC_WARN_UNUSED_VARIABLE = YES;
296 | MACOSX_DEPLOYMENT_TARGET = 10.12;
297 | MTL_ENABLE_DEBUG_INFO = YES;
298 | ONLY_ACTIVE_ARCH = YES;
299 | SDKROOT = macosx;
300 | };
301 | name = Debug;
302 | };
303 | A7B461A11DFEE9F6002B13B0 /* Release */ = {
304 | isa = XCBuildConfiguration;
305 | buildSettings = {
306 | ALWAYS_SEARCH_USER_PATHS = NO;
307 | CLANG_ANALYZER_NONNULL = YES;
308 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
309 | CLANG_CXX_LIBRARY = "libc++";
310 | CLANG_ENABLE_MODULES = YES;
311 | CLANG_ENABLE_OBJC_ARC = YES;
312 | CLANG_WARN_BOOL_CONVERSION = YES;
313 | CLANG_WARN_CONSTANT_CONVERSION = YES;
314 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
315 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
316 | CLANG_WARN_EMPTY_BODY = YES;
317 | CLANG_WARN_ENUM_CONVERSION = YES;
318 | CLANG_WARN_INFINITE_RECURSION = YES;
319 | CLANG_WARN_INT_CONVERSION = YES;
320 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
321 | CLANG_WARN_SUSPICIOUS_MOVES = YES;
322 | CLANG_WARN_UNREACHABLE_CODE = YES;
323 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
324 | CODE_SIGN_IDENTITY = "-";
325 | COPY_PHASE_STRIP = NO;
326 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
327 | ENABLE_NS_ASSERTIONS = NO;
328 | ENABLE_STRICT_OBJC_MSGSEND = YES;
329 | GCC_C_LANGUAGE_STANDARD = gnu99;
330 | GCC_NO_COMMON_BLOCKS = YES;
331 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
332 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
333 | GCC_WARN_UNDECLARED_SELECTOR = YES;
334 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
335 | GCC_WARN_UNUSED_FUNCTION = YES;
336 | GCC_WARN_UNUSED_VARIABLE = YES;
337 | MACOSX_DEPLOYMENT_TARGET = 10.12;
338 | MTL_ENABLE_DEBUG_INFO = NO;
339 | SDKROOT = macosx;
340 | };
341 | name = Release;
342 | };
343 | A7B461D81E00171C002B13B0 /* Debug */ = {
344 | isa = XCBuildConfiguration;
345 | buildSettings = {
346 | CONFIGURATION_BUILD_DIR = "$(SRCROOT)/build";
347 | DEVELOPMENT_TEAM = S8EX82NJP6;
348 | HEADER_SEARCH_PATHS = "$(SRCROOT)/XCSummary/Templates";
349 | OTHER_LDFLAGS = "";
350 | PRODUCT_NAME = xcsummary;
351 | SYMROOT = "$(SRCROOT)/build";
352 | };
353 | name = Debug;
354 | };
355 | A7B461D91E00171C002B13B0 /* Release */ = {
356 | isa = XCBuildConfiguration;
357 | buildSettings = {
358 | CONFIGURATION_BUILD_DIR = "$(SRCROOT)/build";
359 | DEVELOPMENT_TEAM = S8EX82NJP6;
360 | HEADER_SEARCH_PATHS = "$(SRCROOT)/XCSummary/Templates";
361 | OTHER_LDFLAGS = "";
362 | PRODUCT_NAME = xcsummary;
363 | SYMROOT = "$(SRCROOT)/build";
364 | };
365 | name = Release;
366 | };
367 | /* End XCBuildConfiguration section */
368 |
369 | /* Begin XCConfigurationList section */
370 | A7B461891DFEE9F6002B13B0 /* Build configuration list for PBXProject "XCSummary" */ = {
371 | isa = XCConfigurationList;
372 | buildConfigurations = (
373 | A7B461A01DFEE9F6002B13B0 /* Debug */,
374 | A7B461A11DFEE9F6002B13B0 /* Release */,
375 | );
376 | defaultConfigurationIsVisible = 0;
377 | defaultConfigurationName = Release;
378 | };
379 | A7B461D71E00171C002B13B0 /* Build configuration list for PBXNativeTarget "xcsummary" */ = {
380 | isa = XCConfigurationList;
381 | buildConfigurations = (
382 | A7B461D81E00171C002B13B0 /* Debug */,
383 | A7B461D91E00171C002B13B0 /* Release */,
384 | );
385 | defaultConfigurationIsVisible = 0;
386 | defaultConfigurationName = Release;
387 | };
388 | /* End XCConfigurationList section */
389 | };
390 | rootObject = A7B461861DFEE9F6002B13B0 /* Project object */;
391 | }
392 |
--------------------------------------------------------------------------------