xcresult 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7e803e4f4efa10ae02a23a22470d368254de016013d8f5dbccf3d5eb0653b9eb
4
+ data.tar.gz: 7bf7102868ea4e19f54851ed211cc2f13d4b7e23c91bb3d93b7947a519b38055
5
+ SHA512:
6
+ metadata.gz: f42c7fba1715507dc254005973dcdb843c7c90d4b75234db4396b7787834ce2770e3226bb5765967d3f2e6de78d58d137936bea023d254a2a849514bc9a653ef
7
+ data.tar.gz: 7b5a60d608d08d3a41799406891d0960e4e0df9da645b4bb116eed117eeaa3846427446937fbbcd6c3118276f95e1386868f5a562491b90127c534c0282d44e8
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,14 @@
1
+ Metrics/LineLength:
2
+ Enabled: false
3
+
4
+ Metrics/MethodLength:
5
+ Enabled: false
6
+
7
+ Metrics/AbcSize:
8
+ Enabled: false
9
+
10
+ Style/Documentation:
11
+ Enabled: false
12
+
13
+ Style/GuardClause:
14
+ Enabled: false
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.3.7
7
+ before_install: gem install bundler -v 1.17.3
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
@@ -0,0 +1,51 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xcresult (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ diff-lcs (1.3)
11
+ jaro_winkler (1.5.3)
12
+ parallel (1.17.0)
13
+ parser (2.6.4.1)
14
+ ast (~> 2.4.0)
15
+ rainbow (3.0.0)
16
+ rake (10.5.0)
17
+ rspec (3.8.0)
18
+ rspec-core (~> 3.8.0)
19
+ rspec-expectations (~> 3.8.0)
20
+ rspec-mocks (~> 3.8.0)
21
+ rspec-core (3.8.2)
22
+ rspec-support (~> 3.8.0)
23
+ rspec-expectations (3.8.4)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.8.0)
26
+ rspec-mocks (3.8.1)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.8.0)
29
+ rspec-support (3.8.2)
30
+ rubocop (0.73.0)
31
+ jaro_winkler (~> 1.5.1)
32
+ parallel (~> 1.10)
33
+ parser (>= 2.6)
34
+ rainbow (>= 2.2.2, < 4.0)
35
+ ruby-progressbar (~> 1.7)
36
+ unicode-display_width (>= 1.4.0, < 1.7)
37
+ ruby-progressbar (1.10.1)
38
+ unicode-display_width (1.6.0)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ bundler (~> 2.0)
45
+ rake (~> 10.0)
46
+ rspec (~> 3.0)
47
+ rubocop
48
+ xcresult!
49
+
50
+ BUNDLED WITH
51
+ 2.0.1
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Josh Holtz
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
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # Xcresult
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/xcresult`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'xcresult'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install xcresult
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/xcresult.
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'xcresult/version.rb'
4
+ require_relative 'xcresult/models.rb'
5
+ require_relative 'xcresult/parser.rb'
@@ -0,0 +1,423 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'xcresult/version'
4
+ module XCResult
5
+ # Model attributes and relationships taken from running the following command:
6
+ # xcrun xcresulttool formatDescription
7
+
8
+ class AbstractObject
9
+ attr_accessor :type
10
+ def initialize(data)
11
+ self.type = data['_type']['_name']
12
+ end
13
+
14
+ def fetch_value(data, key)
15
+ (data[key] || {})['_value']
16
+ end
17
+
18
+ def fetch_values(data, key)
19
+ return [] if data.nil?
20
+
21
+ (data[key] || {})['_values'] || []
22
+ end
23
+ end
24
+
25
+ # - ActionTestPlanRunSummaries
26
+ # * Kind: object
27
+ # * Properties:
28
+ # + summaries: [ActionTestPlanRunSummary]
29
+ class ActionTestPlanRunSummaries < AbstractObject
30
+ attr_accessor :summaries
31
+ def initialize(data)
32
+ self.summaries = fetch_values(data, 'summaries').map do |summary_data|
33
+ ActionTestPlanRunSummary.new(summary_data)
34
+ end
35
+ super
36
+ end
37
+ end
38
+
39
+ # - ActionAbstractTestSummary
40
+ # * Kind: object
41
+ # * Properties:
42
+ # + name: String?
43
+ class ActionAbstractTestSummary < AbstractObject
44
+ attr_accessor :name
45
+ def initialize(data)
46
+ self.name = fetch_value(data, 'name')
47
+ super
48
+ end
49
+ end
50
+
51
+ # - ActionTestPlanRunSummary
52
+ # * Supertype: ActionAbstractTestSummary
53
+ # * Kind: object
54
+ # * Properties:
55
+ # + testableSummaries: [ActionTestableSummary]
56
+ class ActionTestPlanRunSummary < ActionAbstractTestSummary
57
+ attr_accessor :testable_summaries
58
+ def initialize(data)
59
+ self.testable_summaries = fetch_values(data, 'testableSummaries').map do |summary_data|
60
+ ActionTestableSummary.new(summary_data)
61
+ end
62
+ super
63
+ end
64
+ end
65
+
66
+ # - ActionTestableSummary
67
+ # * Supertype: ActionAbstractTestSummary
68
+ # * Kind: object
69
+ # * Properties:
70
+ # + projectRelativePath: String?
71
+ # + targetName: String?
72
+ # + testKind: String?
73
+ # + tests: [ActionTestSummaryIdentifiableObject]
74
+ # + diagnosticsDirectoryName: String?
75
+ # + failureSummaries: [ActionTestFailureSummary]
76
+ # + testLanguage: String?
77
+ # + testRegion: String?
78
+ class ActionTestableSummary < ActionAbstractTestSummary
79
+ attr_accessor :project_relative_path
80
+ attr_accessor :target_name
81
+ attr_accessor :test_kind
82
+ attr_accessor :tests
83
+ def initialize(data)
84
+ self.project_relative_path = fetch_value(data, 'projectRelativePath')
85
+ self.target_name = fetch_value(data, 'targetName')
86
+ self.test_kind = fetch_value(data, 'testKind')
87
+ self.tests = fetch_values(data, 'tests').map do |tests_data|
88
+ ActionTestSummaryIdentifiableObject.create(tests_data, self)
89
+ end
90
+ super
91
+ end
92
+
93
+ def all_tests
94
+ tests.map(&:all_subtests).flatten
95
+ end
96
+ end
97
+
98
+ # - ActionTestSummaryIdentifiableObject
99
+ # * Supertype: ActionAbstractTestSummary
100
+ # * Kind: object
101
+ # * Properties:
102
+ # + identifier: String?
103
+ class ActionTestSummaryIdentifiableObject < ActionAbstractTestSummary
104
+ attr_accessor :identifier
105
+ attr_accessor :parent
106
+ def initialize(data, parent)
107
+ self.identifier = fetch_value(data, 'identifier')
108
+ self.parent = parent
109
+ super(data)
110
+ end
111
+
112
+ def all_subtests
113
+ raise 'Not overridden'
114
+ end
115
+
116
+ def self.create(data, parent)
117
+ type = data['_type']['_name']
118
+ if type == 'ActionTestSummaryGroup'
119
+ return ActionTestSummaryGroup.new(data, parent)
120
+ elsif type == 'ActionTestMetadata'
121
+ return ActionTestMetadata.new(data, parent)
122
+ else
123
+ raise "Unsupported type: #{type}"
124
+ end
125
+ end
126
+ end
127
+
128
+ # - ActionTestSummaryGroup
129
+ # * Supertype: ActionTestSummaryIdentifiableObject
130
+ # * Kind: object
131
+ # * Properties:
132
+ # + duration: Double
133
+ # + subtests: [ActionTestSummaryIdentifiableObject]
134
+ class ActionTestSummaryGroup < ActionTestSummaryIdentifiableObject
135
+ attr_accessor :duration
136
+ attr_accessor :subtests
137
+ def initialize(data, parent)
138
+ self.duration = fetch_value(data, 'duration').to_f
139
+ self.subtests = fetch_values(data, 'subtests').map do |subtests_data|
140
+ ActionTestSummaryIdentifiableObject.create(subtests_data, self)
141
+ end
142
+ super(data, parent)
143
+ end
144
+
145
+ def all_subtests
146
+ subtests.map(&:all_subtests).flatten
147
+ end
148
+ end
149
+
150
+ # - ActionTestMetadata
151
+ # * Supertype: ActionTestSummaryIdentifiableObject
152
+ # * Kind: object
153
+ # * Properties:
154
+ # + testStatus: String
155
+ # + duration: Double?
156
+ # + summaryRef: Reference?
157
+ # + performanceMetricsCount: Int
158
+ # + failureSummariesCount: Int
159
+ # + activitySummariesCount: Int
160
+ class ActionTestMetadata < ActionTestSummaryIdentifiableObject
161
+ attr_accessor :test_status
162
+ attr_accessor :duration
163
+ attr_accessor :performance_metrics_count
164
+ attr_accessor :failure_summaries_count
165
+ attr_accessor :activity_summaries_count
166
+ def initialize(data, parent)
167
+ self.test_status = fetch_value(data, 'testStatus')
168
+ self.duration = fetch_value(data, 'duration').to_f
169
+ self.performance_metrics_count = fetch_value(data, 'performanceMetricsCount')
170
+ self.failure_summaries_count = fetch_value(data, 'failureSummariesCount')
171
+ self.activity_summaries_count = fetch_value(data, 'activitySummariesCount')
172
+ super(data, parent)
173
+ end
174
+
175
+ def all_subtests
176
+ [self]
177
+ end
178
+
179
+ def find_failure(failures)
180
+ if test_status == 'Failure'
181
+ # Tries to match failure on test case name
182
+ # Example TestFailureIssueSummary:
183
+ # producingTarget: "TestThisDude"
184
+ # test_case_name: "TestThisDude.testFailureJosh2()" (when Swift)
185
+ # or "-[TestThisDudeTests testFailureJosh2]" (when Objective-C)
186
+ # Example ActionTestMetadata
187
+ # identifier: "TestThisDude/testFailureJosh2()" (when Swift)
188
+ # or identifier: "TestThisDude/testFailureJosh2" (when Objective-C)
189
+
190
+ found_failure = failures.find do |failure|
191
+ # Clean test_case_name to match identifier format
192
+ # Sanitize for Swift by replacing "." for "/"
193
+ # Sanitize for Objective-C by removing "-", "[", "]", and replacing " " for ?/
194
+ sanitized_test_case_name = failure.test_case_name
195
+ .tr('.', '/')
196
+ .tr('-', '')
197
+ .tr('[', '')
198
+ .tr(']', '')
199
+ .tr(' ', '/')
200
+ identifier == sanitized_test_case_name
201
+ end
202
+ found_failure
203
+ end
204
+ end
205
+ end
206
+
207
+ # - ActionsInvocationRecord
208
+ # * Kind: object
209
+ # * Properties:
210
+ # + metadataRef: Reference?
211
+ # + metrics: ResultMetrics
212
+ # + issues: ResultIssueSummaries
213
+ # + actions: [ActionRecord]
214
+ # + archive: ArchiveInfo?
215
+ class ActionsInvocationRecord < AbstractObject
216
+ attr_accessor :actions
217
+ attr_accessor :issues
218
+ def initialize(data)
219
+ self.actions = fetch_values(data, 'actions').map do |action_data|
220
+ ActionRecord.new(action_data)
221
+ end
222
+ self.issues = ResultIssueSummaries.new(data['issues'])
223
+ super
224
+ end
225
+ end
226
+
227
+ # - ActionRecord
228
+ # * Kind: object
229
+ # * Properties:
230
+ # + schemeCommandName: String
231
+ # + schemeTaskName: String
232
+ # + title: String?
233
+ # + startedTime: Date
234
+ # + endedTime: Date
235
+ # + runDestination: ActionRunDestinationRecord
236
+ # + buildResult: ActionResult
237
+ # + actionResult: ActionResult
238
+ class ActionRecord < AbstractObject
239
+ attr_accessor :scheme_command_name
240
+ attr_accessor :scheme_task_name
241
+ attr_accessor :title
242
+ attr_accessor :build_result
243
+ attr_accessor :action_result
244
+ def initialize(data)
245
+ self.scheme_command_name = fetch_value(data, 'schemeCommandName')
246
+ self.scheme_task_name = fetch_value(data, 'schemeTaskName')
247
+ self.title = fetch_value(data, 'title')
248
+ self.build_result = ActionResult.new(data['buildResult'])
249
+ self.action_result = ActionResult.new(data['actionResult'])
250
+ super
251
+ end
252
+ end
253
+
254
+ # - ActionResult
255
+ # * Kind: object
256
+ # * Properties:
257
+ # + resultName: String
258
+ # + status: String
259
+ # + metrics: ResultMetrics
260
+ # + issues: ResultIssueSummaries
261
+ # + coverage: CodeCoverageInfo
262
+ # + timelineRef: Reference?
263
+ # + logRef: Reference?
264
+ # + testsRef: Reference?
265
+ # + diagnosticsRef: Reference?
266
+ class ActionResult < AbstractObject
267
+ attr_accessor :result_name
268
+ attr_accessor :status
269
+ attr_accessor :issues
270
+ attr_accessor :coverage
271
+ attr_accessor :timeline_ref
272
+ attr_accessor :log_ref
273
+ attr_accessor :tests_ref
274
+ attr_accessor :diagnostics_ref
275
+ def initialize(data)
276
+ self.result_name = fetch_value(data, 'resultName')
277
+ self.status = fetch_value(data, 'status')
278
+ self.issues = ResultIssueSummaries.new(data['issues'])
279
+ self.coverage = CodeCoverageInfo.new(data['coverage'])
280
+
281
+ self.timeline_ref = Reference.new(data['timelineRef']) if data['timelineRef']
282
+ self.log_ref = Reference.new(data['logRef']) if data['logRef']
283
+ self.tests_ref = Reference.new(data['testsRef']) if data['testsRef']
284
+ self.diagnostics_ref = Reference.new(data['diagnosticsRef']) if data['diagnosticsRef']
285
+ super
286
+ end
287
+ end
288
+
289
+ # - CodeCoverageInfo
290
+ # * Kind: object
291
+ # * Properties:
292
+ # + hasCoverageData: Bool
293
+ # + reportRef: Reference?
294
+ # + archiveRef: Reference?
295
+ class CodeCoverageInfo < AbstractObject
296
+ attr_accessor :has_coverage_data
297
+ attr_accessor :report_ref
298
+ attr_accessor :archive_ref
299
+ def initialize(data)
300
+ self.has_coverage_data = fetch_value(data, 'hasCoverageData')
301
+ self.report_ref = Reference.new(data['reportRef']) if data['reportRef']
302
+ self.archive_ref = Reference.new(data['archiveRef']) if data['archiveRef']
303
+ end
304
+ end
305
+
306
+ # - Reference
307
+ # * Kind: object
308
+ # * Properties:
309
+ # + id: String
310
+ # + targetType: TypeDefinition?
311
+ class Reference < AbstractObject
312
+ attr_accessor :id
313
+ attr_accessor :target_type
314
+ def initialize(data)
315
+ self.id = fetch_value(data, 'id')
316
+ self.target_type = TypeDefinition.new(data['targetType']) if data['targetType']
317
+ super
318
+ end
319
+ end
320
+
321
+ # - TypeDefinition
322
+ # * Kind: object
323
+ # * Properties:
324
+ # + name: String
325
+ # + supertype: TypeDefinition?
326
+ class TypeDefinition < AbstractObject
327
+ attr_accessor :name
328
+ attr_accessor :supertype
329
+ def initialize(data)
330
+ self.name = fetch_value(data, 'name')
331
+ self.supertype = TypeDefinition.new(data['supertype']) if data['supertype']
332
+ super
333
+ end
334
+ end
335
+
336
+ # - DocumentLocation
337
+ # * Kind: object
338
+ # * Properties:
339
+ # + url: String
340
+ # + concreteTypeName: String
341
+ class DocumentLocation < AbstractObject
342
+ attr_accessor :url
343
+ attr_accessor :concrete_type_name
344
+ def initialize(data)
345
+ self.url = fetch_value(data, 'url')
346
+ self.concrete_type_name = data['concreteTypeName']['_value']
347
+ super
348
+ end
349
+ end
350
+
351
+ # - IssueSummary
352
+ # * Kind: object
353
+ # * Properties:
354
+ # + issueType: String
355
+ # + message: String
356
+ # + producingTarget: String?
357
+ # + documentLocationInCreatingWorkspace: DocumentLocation?
358
+ class IssueSummary < AbstractObject
359
+ attr_accessor :issue_type
360
+ attr_accessor :message
361
+ attr_accessor :producing_target
362
+ attr_accessor :document_location_in_creating_workspace
363
+ def initialize(data)
364
+ self.issue_type = fetch_value(data, 'issueType')
365
+ self.message = fetch_value(data, 'message')
366
+ self.producing_target = fetch_value(data, 'producingTarget')
367
+ self.document_location_in_creating_workspace = DocumentLocation.new(data['documentLocationInCreatingWorkspace']) if data['documentLocationInCreatingWorkspace']
368
+ super
369
+ end
370
+ end
371
+
372
+ # - ResultIssueSummaries
373
+ # * Kind: object
374
+ # * Properties:
375
+ # + analyzerWarningSummaries: [IssueSummary]
376
+ # + errorSummaries: [IssueSummary]
377
+ # + testFailureSummaries: [TestFailureIssueSummary]
378
+ # + warningSummaries: [IssueSummary]
379
+ class ResultIssueSummaries < AbstractObject
380
+ attr_accessor :analyzer_warning_summaries
381
+ attr_accessor :error_summaries
382
+ attr_accessor :test_failure_summaries
383
+ attr_accessor :warning_summaries
384
+ def initialize(data)
385
+ self.analyzer_warning_summaries = fetch_values(data, 'analyzerWarningSummaries').map do |summary_data|
386
+ IssueSummary.new(summary_data)
387
+ end
388
+ self.error_summaries = fetch_values(data, 'errorSummaries').map do |summary_data|
389
+ IssueSummary.new(summary_data)
390
+ end
391
+ self.test_failure_summaries = fetch_values(data, 'testFailureSummaries').map do |summary_data|
392
+ TestFailureIssueSummary.new(summary_data)
393
+ end
394
+ self.warning_summaries = fetch_values(data, 'warningSummaries').map do |summary_data|
395
+ IssueSummary.new(summary_data)
396
+ end
397
+ super
398
+ end
399
+ end
400
+
401
+ # - TestFailureIssueSummary
402
+ # * Supertype: IssueSummary
403
+ # * Kind: object
404
+ # * Properties:
405
+ # + testCaseName: String
406
+ class TestFailureIssueSummary < IssueSummary
407
+ attr_accessor :test_case_name
408
+ def initialize(data)
409
+ self.test_case_name = fetch_value(data, 'testCaseName')
410
+ super
411
+ end
412
+
413
+ def failure_message
414
+ new_message = message
415
+ if document_location_in_creating_workspace
416
+ file_path = document_location_in_creating_workspace.url.gsub('file://', '')
417
+ new_message += " (#{file_path})"
418
+ end
419
+
420
+ new_message
421
+ end
422
+ end
423
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ require 'json'
5
+
6
+ module XCResult
7
+ class Parser
8
+ attr_accessor :path, :result_bundle_json, :actions_invocation_record
9
+
10
+ def initialize(path: nil)
11
+ @path = Shellwords.escape(path)
12
+
13
+ result_bundle_json_raw = get_result_bundle_json
14
+ @result_bundle_json = JSON.parse(result_bundle_json_raw)
15
+
16
+ @actions_invocation_record = XCResult::ActionsInvocationRecord.new(@result_bundle_json)
17
+ end
18
+
19
+ def action_test_plan_summaries
20
+ return @action_test_plan_summaries if @action_test_plan_summaries
21
+
22
+ # Parses JSON into ActionsInvocationRecord to find a list of all ids for ActionTestPlanRunSummaries
23
+ test_refs = actions_invocation_record.actions.map do |action|
24
+ action.action_result.tests_ref
25
+ end.compact
26
+ ids = test_refs.map(&:id)
27
+
28
+ # Maps ids into ActionTestPlanRunSummaries by executing xcresulttool to get JSON
29
+ # containing specific information for each test summary
30
+ @action_test_plan_summaries = ids.map do |id|
31
+ raw = execute_cmd("xcrun xcresulttool get --format json --path #{path} --id #{id}")
32
+ json = JSON.parse(raw)
33
+ XCResult::ActionTestPlanRunSummaries.new(json)
34
+ end
35
+
36
+ @action_test_plan_summaries
37
+ end
38
+
39
+ def export_xccovreport(destination: nil)
40
+ destination ||= Dir.pwd
41
+
42
+ coverages = actions_invocation_record.actions.map do |action|
43
+ action.action_result.coverage.report_ref
44
+ end.compact
45
+ ids = coverages.map(&:id)
46
+
47
+ ids.each_with_index.map do |id, i|
48
+ output_path = File.join(destination, "action_#{i}.xccovreport")
49
+ cmd = "xcrun xcresulttool export --path #{path} --id '#{id}' --output-path #{output_path} --type file"
50
+ execute_cmd(cmd)
51
+
52
+ output_path
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def get_result_bundle_json(id: nil)
59
+ cmd = "xcrun xcresulttool get --format json --path #{path}"
60
+ cmd += " --id #{id}" if id
61
+ execute_cmd(cmd)
62
+ end
63
+
64
+ def execute_cmd(cmd)
65
+ output = `#{cmd}`
66
+ raise "Failed to execute - #{cmd}" unless $CHILD_STATUS.success?
67
+
68
+ output
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module XCResult
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'xcresult/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'xcresult'
7
+ spec.version = XCResult::VERSION
8
+ spec.authors = ['Josh Holtz']
9
+ spec.email = ['me@joshholtz.com']
10
+
11
+ spec.summary = 'Parses xcresult files'
12
+ spec.description = 'Parses xcresult files for viewing test summaries and code coverages'
13
+ spec.homepage = 'https://github.com/fastlane-community/xcresult'
14
+ spec.license = 'MIT'
15
+
16
+ # Specify which files should be added to the gem when it is released.
17
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
18
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
19
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
20
+ end
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_development_dependency 'bundler', '~> 2.0'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.0'
26
+ spec.add_development_dependency 'rubocop'
27
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xcresult
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Holtz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-09-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Parses xcresult files for viewing test summaries and code coverages
70
+ email:
71
+ - me@joshholtz.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".rubocop.yml"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - Gemfile.lock
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - lib/xcresult.rb
86
+ - lib/xcresult/models.rb
87
+ - lib/xcresult/parser.rb
88
+ - lib/xcresult/version.rb
89
+ - xcresult.gemspec
90
+ homepage: https://github.com/fastlane-community/xcresult
91
+ licenses:
92
+ - MIT
93
+ metadata: {}
94
+ post_install_message:
95
+ rdoc_options: []
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ requirements: []
109
+ rubygems_version: 3.0.3
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Parses xcresult files
113
+ test_files: []