fastlane-plugin-xcresult_to_junit 0.2.5 → 0.3.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
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c185b9548c916b38b2ed3a08299517bc82a3694f53389bc5cd71ade036935755
|
4
|
+
data.tar.gz: 9503d30e4b60b7ae44920cc2d088086cfe605b56e70e80bc3b6c4687a282b152
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f14cbe747d41187ba986958d91118e98fe6f6fd458eb5d9d30e6d518e964b4f88cbe4a8e5a6eca6dfe3e68fb4a8917b3aae531eea9b86b0c7010a1b8a004f4bb
|
7
|
+
data.tar.gz: 36c44c53cc8bece4598c77033ed0251e275cd0b407cba993161606e48397c656eafe1ab5ab4aeed1601534878924ce0f9c59805a1c5117012f3296f2543a47b6
|
@@ -9,108 +9,105 @@ module Fastlane
|
|
9
9
|
UI.message("The xcresult_to_junit plugin has started!")
|
10
10
|
all_results = Helper::XcresultToJunitHelper.load_results(params[:xcresult_path])['actions']['_values']
|
11
11
|
all_results.each do |test_run|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
12
|
+
next unless test_run['actionResult']['testsRef'] # Skip if section has no testRef data as this means its not a test run
|
13
|
+
map = {}
|
14
|
+
junit_folder = Helper::XcresultToJunitHelper.save_device_details_to_file(params[:output_path], test_run['runDestination'])
|
15
|
+
test_run_id = test_run['actionResult']['testsRef']['id']['_value']
|
16
|
+
all_tests = Helper::XcresultToJunitHelper.load_object(params[:xcresult_path], test_run_id)['summaries']['_values'][0]['testableSummaries']['_values']
|
17
|
+
test_suites = []
|
18
|
+
all_tests.each do |target|
|
19
|
+
target_name = target['targetName']['_value']
|
20
|
+
unless target['tests']
|
21
|
+
failure_summary = target['failureSummaries']['_values'][0]
|
22
|
+
test_suites << { name: target_name, error: failure_summary['message']['_value'] }
|
23
|
+
next
|
24
|
+
end
|
25
|
+
test_classes = []
|
26
|
+
if defined?(target['tests']['_values'][0]['subtests']['_values'][0]['subtests']['_values'])
|
27
|
+
test_classes = target['tests']['_values'][0]['subtests']['_values'][0]['subtests']['_values']
|
28
|
+
end
|
29
|
+
test_classes.each do |test_class|
|
30
|
+
suite_name = "#{target_name}.#{test_class['name']['_value']}"
|
31
|
+
suite = { name: suite_name, cases: [] }
|
32
|
+
if test_class['subtests']
|
33
|
+
test_class['subtests']['_values'].each do |test|
|
34
|
+
duration = 0
|
35
|
+
duration = test['duration']['_value'] if test['duration']
|
36
|
+
testcase_name = test['name']['_value'].tr('()', '')
|
37
|
+
tags = testcase_name.split('_')[1..-1]
|
38
|
+
testcase_name = testcase_name.split('_').first
|
39
|
+
testcase = { name: testcase_name, time: duration }
|
40
|
+
map["#{suite_name}.#{testcase_name}"] = { 'files' => [], 'tags' => tags }
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
42
|
+
if defined?(test['summaryRef']['id']['_value'])
|
43
|
+
summary_ref = test['summaryRef']['id']['_value']
|
44
|
+
ref = Helper::XcresultToJunitHelper.load_object(params[:xcresult_path], summary_ref)
|
45
|
+
if defined?(ref['activitySummaries']['_values'])
|
46
|
+
ref['activitySummaries']['_values'].each do |summary|
|
47
|
+
next unless summary['attachments']
|
48
|
+
summary['attachments']['_values'].each do |attachment|
|
49
|
+
timestamp = DateTime.parse(attachment['timestamp']['_value']).to_time.to_i
|
50
|
+
filename = attachment['filename']['_value']
|
51
|
+
name = attachment['name']['_value']
|
52
|
+
folder_name = "#{suite_name}.#{testcase_name}"
|
53
|
+
id = attachment['payloadRef']['id']['_value']
|
54
|
+
Helper::XcresultToJunitHelper.fetch_screenshot(params[:xcresult_path], "#{junit_folder}/attachments/#{folder_name}", filename.to_s, id)
|
55
|
+
map[folder_name]['files'].push({ 'description' => name, 'mime-type' => 'image/png', 'path' => "#{folder_name}/#{filename}", 'timestamp' => timestamp })
|
58
56
|
end
|
59
57
|
end
|
58
|
+
end
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
78
|
-
metricave = (metricave/measurecount).round(2)
|
79
|
-
if metricbaseline != 0
|
80
|
-
metricresult = (((metricbaseline.to_f-metricave)/metricbaseline.to_f)*100).round(2)
|
81
|
-
else
|
82
|
-
metricresult = 0
|
83
|
-
end
|
84
|
-
performancemetric = "\nMetric: #{metricname}\nResult: #{metricresult}%\nAverage: #{metricave}#{metricunit}\nBaseline: #{metricbaseline}#{metricunit}\nMax Deviation: #{metricmaxdev}%\n\n"
|
85
|
-
performancemetrics << performancemetric
|
60
|
+
if defined?(ref['performanceMetrics']['_value'])
|
61
|
+
performancemetrics = ""
|
62
|
+
ref['performanceMetrics']['_values'].each do |metric|
|
63
|
+
metricname = metric['displayName']['_value']
|
64
|
+
if defined?(metric['baselineAverage']['_value'])
|
65
|
+
metricbaseline = metric['baselineAverage']['_value']
|
66
|
+
else
|
67
|
+
metricbaseline = 0
|
68
|
+
end
|
69
|
+
metricmaxdev = metric['maxPercentRelativeStandardDeviation']['_value']
|
70
|
+
metricunit = metric['unitOfMeasurement']['_value']
|
71
|
+
metricave = 0
|
72
|
+
measurecount = 0
|
73
|
+
metric['measurements']['_values'].each do |measure|
|
74
|
+
metricave += measure['_value'].to_f
|
75
|
+
measurecount += 1
|
86
76
|
end
|
87
|
-
|
77
|
+
metricave = (metricave / measurecount).round(2)
|
78
|
+
if metricbaseline != 0
|
79
|
+
metricresult = (((metricbaseline.to_f - metricave) / metricbaseline.to_f) * 100).round(2)
|
80
|
+
else
|
81
|
+
metricresult = 0
|
82
|
+
end
|
83
|
+
performancemetric = "\nMetric: #{metricname}\nResult: #{metricresult}%\nAverage: #{metricave}#{metricunit}\nBaseline: #{metricbaseline}#{metricunit}\nMax Deviation: #{metricmaxdev}%\n\n"
|
84
|
+
performancemetrics << performancemetric
|
88
85
|
end
|
86
|
+
testcase[:performance] = performancemetrics
|
89
87
|
end
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
88
|
+
end
|
89
|
+
if test['testStatus']['_value'] == 'Failure'
|
90
|
+
failure = Helper::XcresultToJunitHelper.load_object(params[:xcresult_path], test['summaryRef']['id']['_value'])['failureSummaries']['_values'][0]
|
91
|
+
filename = failure.dig('fileName', '_value')
|
92
|
+
message = failure['message']['_value']
|
93
|
+
if filename == '<unknown>' || filename.nil?
|
94
|
+
testcase[:error] = message
|
95
|
+
else
|
96
|
+
testcase[:failure] = message
|
97
|
+
testcase[:failure_location] = "#{filename}:#{failure['lineNumber']['_value']}"
|
101
98
|
end
|
102
|
-
suite[:cases] << testcase
|
103
99
|
end
|
100
|
+
suite[:cases] << testcase
|
104
101
|
end
|
105
|
-
suite[:count] = suite[:cases].size
|
106
|
-
suite[:failures] = suite[:cases].count { |testcase| testcase[:failure] }
|
107
|
-
suite[:errors] = suite[:cases].count { |testcase| testcase[:error] }
|
108
|
-
test_suites << suite
|
109
102
|
end
|
103
|
+
suite[:count] = suite[:cases].size
|
104
|
+
suite[:failures] = suite[:cases].count { |testcase| testcase[:failure] }
|
105
|
+
suite[:errors] = suite[:cases].count { |testcase| testcase[:error] }
|
106
|
+
test_suites << suite
|
110
107
|
end
|
111
|
-
Helper::XcresultToJunitHelper.generate_junit(junit_folder, test_suites)
|
112
|
-
Helper::XcresultToJunitHelper.save_screenshot_mapping(map, "#{junit_folder}/attachments/")
|
113
108
|
end
|
109
|
+
Helper::XcresultToJunitHelper.generate_junit(junit_folder, test_suites)
|
110
|
+
Helper::XcresultToJunitHelper.save_screenshot_mapping(map, "#{junit_folder}/attachments/")
|
114
111
|
end
|
115
112
|
UI.message("The xcresult_to_junit plugin has finished!")
|
116
113
|
end
|
@@ -138,17 +135,17 @@ module Fastlane
|
|
138
135
|
description: "The path to the xcresult file",
|
139
136
|
optional: false,
|
140
137
|
type: String),
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
end
|
152
|
-
end
|
138
|
+
FastlaneCore::ConfigItem.new(key: :output_path,
|
139
|
+
env_name: "XCRESULT_TO_JUNIT_OUTPUT_PATH",
|
140
|
+
description: "The path where the output will be placed",
|
141
|
+
optional: false,
|
142
|
+
type: String)
|
143
|
+
]
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.is_supported?(platform)
|
147
|
+
[:ios, :mac].include?(platform)
|
153
148
|
end
|
154
149
|
end
|
150
|
+
end
|
151
|
+
end
|
@@ -8,18 +8,18 @@ module Fastlane
|
|
8
8
|
module Helper
|
9
9
|
class XcresultToJunitHelper
|
10
10
|
def self.load_object(xcresult_path, id)
|
11
|
-
JSON.
|
11
|
+
JSON.parse(FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool get --format json --path #{xcresult_path} --id #{id}"))
|
12
12
|
end
|
13
13
|
|
14
14
|
def self.load_results(xcresult_path)
|
15
|
-
JSON.
|
15
|
+
JSON.parse(FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool get --format json --path #{xcresult_path}"))
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.fetch_screenshot(xcresult_path, output_path, file_name, id)
|
19
|
-
|
20
|
-
FileUtils.mkdir
|
19
|
+
unless File.directory?(output_path)
|
20
|
+
FileUtils.mkdir(output_path)
|
21
21
|
end
|
22
|
-
|
22
|
+
FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool export --path #{xcresult_path} --output-path \"#{output_path}/#{file_name}\" --id #{id} --type file")
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.save_screenshot_mapping(map_hash, output_path)
|
@@ -37,9 +37,8 @@ module Fastlane
|
|
37
37
|
}.to_json
|
38
38
|
|
39
39
|
junit_folder = "#{output_path}/ios-#{device_udid}.junit"
|
40
|
-
FileUtils.rm_rf
|
41
|
-
FileUtils.
|
42
|
-
FileUtils.mkdir "#{junit_folder}/attachments"
|
40
|
+
FileUtils.rm_rf(junit_folder)
|
41
|
+
FileUtils.mkdir_p("#{junit_folder}/attachments")
|
43
42
|
File.open("#{junit_folder}/device.json", 'w') do |f|
|
44
43
|
f << device_details
|
45
44
|
end
|
@@ -47,52 +46,52 @@ module Fastlane
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def self.junit_file_start
|
50
|
-
puts
|
51
|
-
puts
|
49
|
+
puts('<?xml version="1.0" encoding="UTF-8"?>')
|
50
|
+
puts('<testsuites>')
|
52
51
|
end
|
53
52
|
|
54
53
|
def self.junit_file_end
|
55
|
-
puts
|
54
|
+
puts('</testsuites>')
|
56
55
|
end
|
57
56
|
|
58
57
|
def self.junit_suite_error(suite)
|
59
|
-
puts
|
60
|
-
puts
|
58
|
+
puts("<testsuite name=#{suite[:name].encode(xml: :attr)} errors='1'>")
|
59
|
+
puts("<error>#{suite[:error].encode(xml: :text)}</error>")
|
61
60
|
end
|
62
61
|
|
63
62
|
def self.junit_suite_start(suite)
|
64
|
-
puts
|
63
|
+
puts("<testsuite name=#{suite[:name].encode(xml: :attr)} tests='#{suite[:count]}' failures='#{suite[:failures]}' errors='#{suite[:errors]}'>")
|
65
64
|
end
|
66
65
|
|
67
66
|
def self.junit_suite_end
|
68
|
-
puts
|
67
|
+
puts('</testsuite>')
|
69
68
|
end
|
70
69
|
|
71
70
|
def self.junit_testcase_start(suite, testcase)
|
72
|
-
print
|
71
|
+
print("<testcase name=#{testcase[:name].encode(xml: :attr)} classname=#{suite[:name].encode(xml: :attr)} time='#{testcase[:time]}'>")
|
73
72
|
end
|
74
73
|
|
75
74
|
def self.junit_testcase_end
|
76
|
-
puts
|
75
|
+
puts('</testcase>')
|
77
76
|
end
|
78
77
|
|
79
78
|
def self.junit_testcase_failure(testcase)
|
80
|
-
puts
|
79
|
+
puts("<failure message=#{testcase[:failure].encode(xml: :attr)}>#{testcase[:failure_location].encode(xml: :text)}</failure>")
|
81
80
|
end
|
82
81
|
|
83
82
|
def self.junit_testcase_error(testcase)
|
84
|
-
puts
|
83
|
+
puts("<error>#{testcase[:error].encode(xml: :text)}</error>")
|
85
84
|
end
|
86
85
|
|
87
86
|
def self.junit_testcase_performance(testcase)
|
88
|
-
puts
|
87
|
+
puts("<system-out>#{testcase[:performance]}</system-out>")
|
89
88
|
end
|
90
89
|
|
91
90
|
def self.generate_junit(junit_folder, test_suites)
|
92
91
|
File.open("#{junit_folder}/results.xml", 'w') do |fo|
|
93
92
|
old_stdout = $stdout
|
94
93
|
$stdout = fo
|
95
|
-
Helper::XcresultToJunitHelper.junit_file_start
|
94
|
+
Helper::XcresultToJunitHelper.junit_file_start
|
96
95
|
test_suites.each do |suite|
|
97
96
|
if suite[:error]
|
98
97
|
Helper::XcresultToJunitHelper.junit_suite_error(suite)
|
@@ -111,9 +110,9 @@ module Fastlane
|
|
111
110
|
Helper::XcresultToJunitHelper.junit_testcase_end
|
112
111
|
end
|
113
112
|
end
|
114
|
-
Helper::XcresultToJunitHelper.junit_suite_end
|
113
|
+
Helper::XcresultToJunitHelper.junit_suite_end
|
115
114
|
end
|
116
|
-
Helper::XcresultToJunitHelper.junit_file_end
|
115
|
+
Helper::XcresultToJunitHelper.junit_file_end
|
117
116
|
$stdout = old_stdout
|
118
117
|
end
|
119
118
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-xcresult_to_junit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Birdsall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|