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 +5 -5
- data/README.md +3 -3
- data/lib/trainer.rb +1 -0
- data/lib/trainer/test_parser.rb +106 -7
- data/lib/trainer/version.rb +2 -2
- data/lib/trainer/xcresult.rb +403 -0
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 370827f7984bc0877d6426ec99391c75fa14c28d028a695655511a5c3951c410
|
4
|
+
data.tar.gz: 26c93c9a62005c206a051f2608c1ef9d9e74a1779449120b7ffd70b5832167e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c2e6b31d8e060c805f75b7a19872dedd815f4d27803bbfde54903371e6a6fae2f8730355fd1ef4f224eb94128506465e3278e49a54c1860b3fd9cb302344142
|
7
|
+
data.tar.gz: 51ba7341dae7de6a368008537de7015bcf77be703c4062ac68e89b27c47c113732208878b8a9dc94af8d1dfb799730778655ca8e92375866df079e0a74a1903f
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
[](https://github.com/KrauseFx/trainer/blob/master/LICENSE)
|
5
5
|
[](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
|
data/lib/trainer.rb
CHANGED
data/lib/trainer/test_parser.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
58
|
-
|
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|
|
data/lib/trainer/version.rb
CHANGED
@@ -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.
|
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-
|
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.
|
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.
|
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
|
-
|
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: []
|