trainer 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0e3f70b7df496d9fabd93521f0570b31c8246c65
4
- data.tar.gz: 5b905c28029b08d09b4c76784669b422c971843c
2
+ SHA256:
3
+ metadata.gz: 370827f7984bc0877d6426ec99391c75fa14c28d028a695655511a5c3951c410
4
+ data.tar.gz: 26c93c9a62005c206a051f2608c1ef9d9e74a1779449120b7ffd70b5832167e2
5
5
  SHA512:
6
- metadata.gz: 6f99d9bc8d6c6cd851d40d576edb4bebb5d1aa8928642c78ac66981b928e6fd6e8f9d2a404109344dfa205ef03aae389ce18e46240a79e36f760a2d52e2f46ad
7
- data.tar.gz: e4b784d44beedac35cb637e08bebf9e674f8f91f19b4afaed9d2cb81b1d53c3d89ed8525d70e12365b7375d3f3fe28ba567f8a2edfad29adff88913e78d2bf27
6
+ metadata.gz: 5c2e6b31d8e060c805f75b7a19872dedd815f4d27803bbfde54903371e6a6fae2f8730355fd1ef4f224eb94128506465e3278e49a54c1860b3fd9cb302344142
7
+ data.tar.gz: 51ba7341dae7de6a368008537de7015bcf77be703c4062ac68e89b27c47c113732208878b8a9dc94af8d1dfb799730778655ca8e92375866df079e0a74a1903f
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  [![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/KrauseFx/trainer/blob/master/LICENSE)
5
5
  [![Gem](https://img.shields.io/gem/v/trainer.svg?style=flat)](http://rubygems.org/gems/trainer)
6
6
 
7
- This is an alternative approach to generate JUnit files for your CI (e.g. Jenkins) without parsing the `xcodebuild` output, but using the Xcode `plist` files instead.
7
+ This is an alternative approach to generate JUnit files for your CI (e.g. Jenkins) without parsing the `xcodebuild` output, but using the Xcode `plist` or `xcresult` files instead.
8
8
 
9
9
  Some Xcode versions has a known issue around not properly closing `stdout` ([Radar](https://openradar.appspot.com/27447948)), so you [can't use xcpretty](https://github.com/supermarin/xcpretty/issues/227).
10
10
 
@@ -81,13 +81,13 @@ cd [project]
81
81
  fastlane scan --derived_data_path "output_dir"
82
82
  ```
83
83
 
84
- #### Convert the plist files to junit
84
+ #### Convert the plist or xcresult files to junit
85
85
 
86
86
  ```
87
87
  trainer
88
88
  ```
89
89
 
90
- You can also pass a custom directory containing the plist files
90
+ You can also pass a custom directory containing the plist or xcresult files
91
91
 
92
92
  ```
93
93
  trainer --path ./something
@@ -4,6 +4,7 @@ require 'trainer/version'
4
4
  require 'trainer/options'
5
5
  require 'trainer/test_parser'
6
6
  require 'trainer/junit_generator'
7
+ require 'trainer/xcresult'
7
8
 
8
9
  module Trainer
9
10
  ROOT = File.expand_path('..', __dir__)
@@ -22,9 +22,14 @@ module Trainer
22
22
  files += Dir["#{containing_dir}/Test/*.xcresult/TestSummaries.plist"]
23
23
  files += Dir["#{containing_dir}/*.xcresult/TestSummaries.plist"]
24
24
  files += Dir[containing_dir] if containing_dir.end_with?(".plist") # if it's the exact path to a plist file
25
+ # Xcode 11
26
+ files += Dir["#{containing_dir}/**/Logs/Test/*.xcresult"]
27
+ files += Dir["#{containing_dir}/Test/*.xcresult"]
28
+ files += Dir["#{containing_dir}/*.xcresult"]
29
+ files << containing_dir if File.extname(containing_dir) == ".xcresult"
25
30
 
26
31
  if files.empty?
27
- UI.user_error!("No test result files found in directory '#{containing_dir}', make sure the file name ends with 'TestSummaries.plist'")
32
+ UI.user_error!("No test result files found in directory '#{containing_dir}', make sure the file name ends with 'TestSummaries.plist' or '.xcresult'")
28
33
  end
29
34
 
30
35
  return_hash = {}
@@ -34,7 +39,12 @@ module Trainer
34
39
  filename = File.basename(path).gsub(".plist", config[:extension])
35
40
  to_path = File.join(config[:output_directory], filename)
36
41
  else
37
- to_path = path.gsub(".plist", config[:extension])
42
+ # Remove .xcresult or .plist extension
43
+ if path.end_with?(".xcresult")
44
+ to_path = path.gsub(".xcresult", config[:extension])
45
+ else
46
+ to_path = path.gsub(".plist", config[:extension])
47
+ end
38
48
  end
39
49
 
40
50
  tp = Trainer::TestParser.new(path, config)
@@ -50,12 +60,17 @@ module Trainer
50
60
  path = File.expand_path(path)
51
61
  UI.user_error!("File not found at path '#{path}'") unless File.exist?(path)
52
62
 
53
- self.file_content = File.read(path)
54
- self.raw_json = Plist.parse_xml(self.file_content)
55
- return if self.raw_json["FormatVersion"].to_s.length.zero? # maybe that's a useless plist file
63
+ if File.directory?(path) && path.end_with?(".xcresult")
64
+ parse_xcresult(path)
65
+ else
66
+ self.file_content = File.read(path)
67
+ self.raw_json = Plist.parse_xml(self.file_content)
68
+
69
+ return if self.raw_json["FormatVersion"].to_s.length.zero? # maybe that's a useless plist file
56
70
 
57
- ensure_file_valid!
58
- parse_content(config[:xcpretty_naming])
71
+ ensure_file_valid!
72
+ parse_content(config[:xcpretty_naming])
73
+ end
59
74
  end
60
75
 
61
76
  # Returns the JUnit report as String
@@ -120,6 +135,90 @@ module Trainer
120
135
  return group, name
121
136
  end
122
137
 
138
+ def execute_cmd(cmd)
139
+ output = `#{cmd}`
140
+ raise "Failed to execute - #{cmd}" unless $?.success?
141
+ return output
142
+ end
143
+
144
+ def parse_xcresult(path)
145
+ # Executes xcresulttool to get JSON format of the result bundle object
146
+ result_bundle_object_raw = execute_cmd("xcrun xcresulttool get --format json --path #{path}")
147
+ result_bundle_object = JSON.parse(result_bundle_object_raw)
148
+
149
+ # Parses JSON into ActionsInvocationRecord to find a list of all ids for ActionTestPlanRunSummaries
150
+ actions_invocation_record = Trainer::XCResult::ActionsInvocationRecord.new(result_bundle_object)
151
+ test_refs = actions_invocation_record.actions.map do |action|
152
+ action.action_result.tests_ref
153
+ end.compact
154
+ ids = test_refs.map(&:id)
155
+
156
+ # Maps ids into ActionTestPlanRunSummaries by executing xcresulttool to get JSON
157
+ # containing specific information for each test summary,
158
+ summaries = ids.map do |id|
159
+ raw = execute_cmd("xcrun xcresulttool get --format json --path #{path} --id #{id}")
160
+ json = JSON.parse(raw)
161
+ Trainer::XCResult::ActionTestPlanRunSummaries.new(json)
162
+ end
163
+
164
+ # Converts the ActionTestPlanRunSummaries to data for junit generator
165
+ failures = actions_invocation_record.issues.test_failure_summaries || []
166
+ summaries_to_data(summaries, failures)
167
+ end
168
+
169
+ def summaries_to_data(summaries, failures)
170
+ # Gets flat list of all ActionTestableSummary
171
+ all_summaries = summaries.map(&:summaries).flatten
172
+ testable_summaries = all_summaries.map(&:testable_summaries).flatten
173
+
174
+ # Maps ActionTestableSummary to rows for junit generator
175
+ rows = testable_summaries.map do |testable_summary|
176
+ all_tests = testable_summary.all_tests.flatten
177
+
178
+ test_rows = all_tests.map do |test|
179
+ test_row = {
180
+ identifier: "#{test.parent.name}.#{test.name}",
181
+ name: test.name,
182
+ duration: test.duration,
183
+ status: test.test_status,
184
+ test_group: test.parent.name,
185
+
186
+ # These don't map to anything but keeping empty strings
187
+ guid: ""
188
+ }
189
+
190
+ # Set failure message if failure found
191
+ failure = test.find_failure(failures)
192
+ if failure
193
+ test_row[:failures] = [{
194
+ file_name: "",
195
+ line_number: 0,
196
+ message: "",
197
+ performance_failure: {},
198
+ failure_message: failure.failure_message
199
+ }]
200
+ end
201
+
202
+ test_row
203
+ end
204
+
205
+ row = {
206
+ project_path: testable_summary.project_relative_path,
207
+ target_name: testable_summary.target_name,
208
+ test_name: testable_summary.name,
209
+ duration: all_tests.map(&:duration).inject(:+),
210
+ tests: test_rows
211
+ }
212
+
213
+ row[:number_of_tests] = row[:tests].count
214
+ row[:number_of_failures] = row[:tests].find_all { |a| (a[:failures] || []).count > 0 }.count
215
+
216
+ row
217
+ end
218
+
219
+ self.data = rows
220
+ end
221
+
123
222
  # Convert the Hashes and Arrays in something more useful
124
223
  def parse_content(xcpretty_naming)
125
224
  self.data = self.raw_json["TestableSummaries"].collect do |testable_summary|
@@ -1,4 +1,4 @@
1
1
  module Trainer
2
- VERSION = "0.8.2"
3
- DESCRIPTION = "Convert xcodebuild plist files to JUnit reports"
2
+ VERSION = "0.9.0"
3
+ DESCRIPTION = "Convert xcodebuild plist and xcresult files to JUnit reports"
4
4
  end
@@ -0,0 +1,403 @@
1
+ module Trainer
2
+ module XCResult
3
+ # Model attributes and relationships taken from running the following command:
4
+ # xcrun xcresulttool formatDescription
5
+
6
+ class AbstractObject
7
+ attr_accessor :type
8
+ def initialize(data)
9
+ self.type = data["_type"]["_name"]
10
+ end
11
+
12
+ def fetch_value(data, key)
13
+ return (data[key] || {})["_value"]
14
+ end
15
+
16
+ def fetch_values(data, key)
17
+ return (data[key] || {})["_values"] || []
18
+ end
19
+ end
20
+
21
+ # - ActionTestPlanRunSummaries
22
+ # * Kind: object
23
+ # * Properties:
24
+ # + summaries: [ActionTestPlanRunSummary]
25
+ class ActionTestPlanRunSummaries < AbstractObject
26
+ attr_accessor :summaries
27
+ def initialize(data)
28
+ self.summaries = fetch_values(data, "summaries").map do |summary_data|
29
+ ActionTestPlanRunSummary.new(summary_data)
30
+ end
31
+ super
32
+ end
33
+ end
34
+
35
+ # - ActionAbstractTestSummary
36
+ # * Kind: object
37
+ # * Properties:
38
+ # + name: String?
39
+ class ActionAbstractTestSummary < AbstractObject
40
+ attr_accessor :name
41
+ def initialize(data)
42
+ self.name = fetch_value(data, "name")
43
+ super
44
+ end
45
+ end
46
+
47
+ # - ActionTestPlanRunSummary
48
+ # * Supertype: ActionAbstractTestSummary
49
+ # * Kind: object
50
+ # * Properties:
51
+ # + testableSummaries: [ActionTestableSummary]
52
+ class ActionTestPlanRunSummary < ActionAbstractTestSummary
53
+ attr_accessor :testable_summaries
54
+ def initialize(data)
55
+ self.testable_summaries = fetch_values(data, "testableSummaries").map do |summary_data|
56
+ ActionTestableSummary.new(summary_data)
57
+ end
58
+ super
59
+ end
60
+ end
61
+
62
+ # - ActionTestableSummary
63
+ # * Supertype: ActionAbstractTestSummary
64
+ # * Kind: object
65
+ # * Properties:
66
+ # + projectRelativePath: String?
67
+ # + targetName: String?
68
+ # + testKind: String?
69
+ # + tests: [ActionTestSummaryIdentifiableObject]
70
+ # + diagnosticsDirectoryName: String?
71
+ # + failureSummaries: [ActionTestFailureSummary]
72
+ # + testLanguage: String?
73
+ # + testRegion: String?
74
+ class ActionTestableSummary < ActionAbstractTestSummary
75
+ attr_accessor :project_relative_path
76
+ attr_accessor :target_name
77
+ attr_accessor :test_kind
78
+ attr_accessor :tests
79
+ def initialize(data)
80
+ self.project_relative_path = fetch_value(data, "projectRelativePath")
81
+ self.target_name = fetch_value(data, "targetName")
82
+ self.test_kind = fetch_value(data, "testKind")
83
+ self.tests = fetch_values(data, "tests").map do |tests_data|
84
+ ActionTestSummaryIdentifiableObject.create(tests_data, self)
85
+ end
86
+ super
87
+ end
88
+
89
+ def all_tests
90
+ return tests.map(&:all_subtests).flatten
91
+ end
92
+ end
93
+
94
+ # - ActionTestSummaryIdentifiableObject
95
+ # * Supertype: ActionAbstractTestSummary
96
+ # * Kind: object
97
+ # * Properties:
98
+ # + identifier: String?
99
+ class ActionTestSummaryIdentifiableObject < ActionAbstractTestSummary
100
+ attr_accessor :identifier
101
+ attr_accessor :parent
102
+ def initialize(data, parent)
103
+ self.identifier = fetch_value(data, "identifier")
104
+ self.parent = parent
105
+ super(data)
106
+ end
107
+
108
+ def all_subtests
109
+ raise "Not overridden"
110
+ end
111
+
112
+ def self.create(data, parent)
113
+ type = data["_type"]["_name"]
114
+ if type == "ActionTestSummaryGroup"
115
+ return ActionTestSummaryGroup.new(data, parent)
116
+ elsif type == "ActionTestMetadata"
117
+ return ActionTestMetadata.new(data, parent)
118
+ else
119
+ raise "Unsupported type: #{type}"
120
+ end
121
+ end
122
+ end
123
+
124
+ # - ActionTestSummaryGroup
125
+ # * Supertype: ActionTestSummaryIdentifiableObject
126
+ # * Kind: object
127
+ # * Properties:
128
+ # + duration: Double
129
+ # + subtests: [ActionTestSummaryIdentifiableObject]
130
+ class ActionTestSummaryGroup < ActionTestSummaryIdentifiableObject
131
+ attr_accessor :duration
132
+ attr_accessor :subtests
133
+ def initialize(data, parent)
134
+ self.duration = fetch_value(data, "duration").to_f
135
+ self.subtests = fetch_values(data, "subtests").map do |subtests_data|
136
+ ActionTestSummaryIdentifiableObject.create(subtests_data, self)
137
+ end
138
+ super(data, parent)
139
+ end
140
+
141
+ def all_subtests
142
+ return subtests.map(&:all_subtests).flatten
143
+ end
144
+ end
145
+
146
+ # - ActionTestMetadata
147
+ # * Supertype: ActionTestSummaryIdentifiableObject
148
+ # * Kind: object
149
+ # * Properties:
150
+ # + testStatus: String
151
+ # + duration: Double?
152
+ # + summaryRef: Reference?
153
+ # + performanceMetricsCount: Int
154
+ # + failureSummariesCount: Int
155
+ # + activitySummariesCount: Int
156
+ class ActionTestMetadata < ActionTestSummaryIdentifiableObject
157
+ attr_accessor :test_status
158
+ attr_accessor :duration
159
+ attr_accessor :performance_metrics_count
160
+ attr_accessor :failure_summaries_count
161
+ attr_accessor :activity_summaries_count
162
+ def initialize(data, parent)
163
+ self.test_status = fetch_value(data, "testStatus")
164
+ self.duration = fetch_value(data, "duration").to_f
165
+ self.performance_metrics_count = fetch_value(data, "performanceMetricsCount")
166
+ self.failure_summaries_count = fetch_value(data, "failureSummariesCount")
167
+ self.activity_summaries_count = fetch_value(data, "activitySummariesCount")
168
+ super(data, parent)
169
+ end
170
+
171
+ def all_subtests
172
+ return [self]
173
+ end
174
+
175
+ def find_failure(failures)
176
+ if self.test_status == "Failure"
177
+ # Tries to match failure on test case name
178
+ # Example TestFailureIssueSummary:
179
+ # producingTarget: "TestThisDude"
180
+ # test_case_name: "TestThisDude.testFailureJosh2()" (when Swift)
181
+ # or "-[TestThisDudeTests testFailureJosh2]" (when Objective-C)
182
+ # Example ActionTestMetadata
183
+ # identifier: "TestThisDude/testFailureJosh2()" (when Swift)
184
+ # or identifier: "TestThisDude/testFailureJosh2" (when Objective-C)
185
+
186
+ found_failure = failures.find do |failure|
187
+ # Clean test_case_name to match identifier format
188
+ # Sanitize for Swift by replacing "." for "/"
189
+ # Sanitize for Objective-C by removing "-", "[", "]", and replacing " " for ?/
190
+ sanitized_test_case_name = failure.test_case_name
191
+ .tr(".", "/")
192
+ .tr("-", "")
193
+ .tr("[", "")
194
+ .tr("]", "")
195
+ .tr(" ", "/")
196
+ self.identifier == sanitized_test_case_name
197
+ end
198
+ return found_failure
199
+ else
200
+ return nil
201
+ end
202
+ end
203
+ end
204
+
205
+ # - ActionsInvocationRecord
206
+ # * Kind: object
207
+ # * Properties:
208
+ # + metadataRef: Reference?
209
+ # + metrics: ResultMetrics
210
+ # + issues: ResultIssueSummaries
211
+ # + actions: [ActionRecord]
212
+ # + archive: ArchiveInfo?
213
+ class ActionsInvocationRecord < AbstractObject
214
+ attr_accessor :actions
215
+ attr_accessor :issues
216
+ def initialize(data)
217
+ self.actions = fetch_values(data, "actions").map do |action_data|
218
+ ActionRecord.new(action_data)
219
+ end
220
+ self.issues = ResultIssueSummaries.new(data["issues"])
221
+ super
222
+ end
223
+ end
224
+
225
+ # - ActionRecord
226
+ # * Kind: object
227
+ # * Properties:
228
+ # + schemeCommandName: String
229
+ # + schemeTaskName: String
230
+ # + title: String?
231
+ # + startedTime: Date
232
+ # + endedTime: Date
233
+ # + runDestination: ActionRunDestinationRecord
234
+ # + buildResult: ActionResult
235
+ # + actionResult: ActionResult
236
+ class ActionRecord < AbstractObject
237
+ attr_accessor :scheme_command_name
238
+ attr_accessor :scheme_task_name
239
+ attr_accessor :title
240
+ attr_accessor :build_result
241
+ attr_accessor :action_result
242
+ def initialize(data)
243
+ self.scheme_command_name = fetch_value(data, "schemeCommandName")
244
+ self.scheme_task_name = fetch_value(data, "schemeTaskName")
245
+ self.title = fetch_value(data, "title")
246
+ self.build_result = ActionResult.new(data["buildResult"])
247
+ self.action_result = ActionResult.new(data["actionResult"])
248
+ super
249
+ end
250
+ end
251
+
252
+ # - ActionResult
253
+ # * Kind: object
254
+ # * Properties:
255
+ # + resultName: String
256
+ # + status: String
257
+ # + metrics: ResultMetrics
258
+ # + issues: ResultIssueSummaries
259
+ # + coverage: CodeCoverageInfo
260
+ # + timelineRef: Reference?
261
+ # + logRef: Reference?
262
+ # + testsRef: Reference?
263
+ # + diagnosticsRef: Reference?
264
+ class ActionResult < AbstractObject
265
+ attr_accessor :result_name
266
+ attr_accessor :status
267
+ attr_accessor :issues
268
+ attr_accessor :timeline_ref
269
+ attr_accessor :log_ref
270
+ attr_accessor :tests_ref
271
+ attr_accessor :diagnostics_ref
272
+ def initialize(data)
273
+ self.result_name = fetch_value(data, "resultName")
274
+ self.status = fetch_value(data, "status")
275
+ self.issues = ResultIssueSummaries.new(data["issues"])
276
+
277
+ self.timeline_ref = Reference.new(data["timelineRef"]) if data["timelineRef"]
278
+ self.log_ref = Reference.new(data["logRef"]) if data["logRef"]
279
+ self.tests_ref = Reference.new(data["testsRef"]) if data["testsRef"]
280
+ self.diagnostics_ref = Reference.new(data["diagnosticsRef"]) if data["diagnosticsRef"]
281
+ super
282
+ end
283
+ end
284
+
285
+ # - Reference
286
+ # * Kind: object
287
+ # * Properties:
288
+ # + id: String
289
+ # + targetType: TypeDefinition?
290
+ class Reference < AbstractObject
291
+ attr_accessor :id
292
+ attr_accessor :target_type
293
+ def initialize(data)
294
+ self.id = fetch_value(data, "id")
295
+ self.target_type = TypeDefinition.new(data["targetType"]) if data["targetType"]
296
+ super
297
+ end
298
+ end
299
+
300
+ # - TypeDefinition
301
+ # * Kind: object
302
+ # * Properties:
303
+ # + name: String
304
+ # + supertype: TypeDefinition?
305
+ class TypeDefinition < AbstractObject
306
+ attr_accessor :name
307
+ attr_accessor :supertype
308
+ def initialize(data)
309
+ self.name = fetch_value(data, "name")
310
+ self.supertype = TypeDefinition.new(data["supertype"]) if data["supertype"]
311
+ super
312
+ end
313
+ end
314
+
315
+ # - DocumentLocation
316
+ # * Kind: object
317
+ # * Properties:
318
+ # + url: String
319
+ # + concreteTypeName: String
320
+ class DocumentLocation < AbstractObject
321
+ attr_accessor :url
322
+ attr_accessor :concrete_type_name
323
+ def initialize(data)
324
+ self.url = fetch_value(data, "url")
325
+ self.concrete_type_name = data["concreteTypeName"]["_value"]
326
+ super
327
+ end
328
+ end
329
+
330
+ # - IssueSummary
331
+ # * Kind: object
332
+ # * Properties:
333
+ # + issueType: String
334
+ # + message: String
335
+ # + producingTarget: String?
336
+ # + documentLocationInCreatingWorkspace: DocumentLocation?
337
+ class IssueSummary < AbstractObject
338
+ attr_accessor :issue_type
339
+ attr_accessor :message
340
+ attr_accessor :producing_target
341
+ attr_accessor :document_location_in_creating_workspace
342
+ def initialize(data)
343
+ self.issue_type = fetch_value(data, "issueType")
344
+ self.message = fetch_value(data, "message")
345
+ self.producing_target = fetch_value(data, "producingTarget")
346
+ self.document_location_in_creating_workspace = DocumentLocation.new(data["documentLocationInCreatingWorkspace"]) if data["documentLocationInCreatingWorkspace"]
347
+ super
348
+ end
349
+ end
350
+
351
+ # - ResultIssueSummaries
352
+ # * Kind: object
353
+ # * Properties:
354
+ # + analyzerWarningSummaries: [IssueSummary]
355
+ # + errorSummaries: [IssueSummary]
356
+ # + testFailureSummaries: [TestFailureIssueSummary]
357
+ # + warningSummaries: [IssueSummary]
358
+ class ResultIssueSummaries < AbstractObject
359
+ attr_accessor :analyzer_warning_summaries
360
+ attr_accessor :error_summaries
361
+ attr_accessor :test_failure_summaries
362
+ attr_accessor :warning_summaries
363
+ def initialize(data)
364
+ self.analyzer_warning_summaries = fetch_values(data, "analyzerWarningSummaries").map do |summary_data|
365
+ IssueSummary.new(summary_data)
366
+ end
367
+ self.error_summaries = fetch_values(data, "errorSummaries").map do |summary_data|
368
+ IssueSummary.new(summary_data)
369
+ end
370
+ self.test_failure_summaries = fetch_values(data, "testFailureSummaries").map do |summary_data|
371
+ TestFailureIssueSummary.new(summary_data)
372
+ end
373
+ self.warning_summaries = fetch_values(data, "warningSummaries").map do |summary_data|
374
+ IssueSummary.new(summary_data)
375
+ end
376
+ super
377
+ end
378
+ end
379
+
380
+ # - TestFailureIssueSummary
381
+ # * Supertype: IssueSummary
382
+ # * Kind: object
383
+ # * Properties:
384
+ # + testCaseName: String
385
+ class TestFailureIssueSummary < IssueSummary
386
+ attr_accessor :test_case_name
387
+ def initialize(data)
388
+ self.test_case_name = fetch_value(data, "testCaseName")
389
+ super
390
+ end
391
+
392
+ def failure_message
393
+ new_message = self.message
394
+ if self.document_location_in_creating_workspace
395
+ file_path = self.document_location_in_creating_workspace.url.gsub("file://", "")
396
+ new_message += " (#{file_path})"
397
+ end
398
+
399
+ return new_message
400
+ end
401
+ end
402
+ end
403
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trainer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felix Krause
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-30 00:00:00.000000000 Z
11
+ date: 2019-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: plist
@@ -106,15 +106,15 @@ dependencies:
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: 0.38.0
109
+ version: 0.49.0
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: 0.38.0
117
- description: Convert xcodebuild plist files to JUnit reports
116
+ version: 0.49.0
117
+ description: Convert xcodebuild plist and xcresult files to JUnit reports
118
118
  email:
119
119
  - fastlane@krausefx.com
120
120
  executables:
@@ -132,6 +132,7 @@ files:
132
132
  - lib/trainer/options.rb
133
133
  - lib/trainer/test_parser.rb
134
134
  - lib/trainer/version.rb
135
+ - lib/trainer/xcresult.rb
135
136
  homepage: https://fastlane.tools
136
137
  licenses:
137
138
  - MIT
@@ -151,9 +152,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
151
152
  - !ruby/object:Gem::Version
152
153
  version: '0'
153
154
  requirements: []
154
- rubyforge_project:
155
- rubygems_version: 2.5.2.3
155
+ rubygems_version: 3.0.3
156
156
  signing_key:
157
157
  specification_version: 4
158
- summary: Convert xcodebuild plist files to JUnit reports
158
+ summary: Convert xcodebuild plist and xcresult files to JUnit reports
159
159
  test_files: []