fastlane-plugin-xcresult_to_junit 0.3.1 → 0.4.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: 5fb16088624384792afda4ba43156a6485580ab8b0cd808baf10a04c42727f4a
4
- data.tar.gz: 447f6c542dcca98230ce368e95d655e768106e7c2eeff2ed758634bed1766d24
3
+ metadata.gz: 26d80a12ddd11ffb1a610f03fbe2b553e429e50318230aab292e05a7af7c562d
4
+ data.tar.gz: ead5297fa4ef5adcf6225ba13229202143a51ae2b0194f45d1452fcd1c878cef
5
5
  SHA512:
6
- metadata.gz: a9ec29588cd4dac9e632675739885695da3b4d010a78c444212383d77dafd622e99a07dc98e954745a007c1310c6d4b0ae778761bb65867a6149cd0abd9526f0
7
- data.tar.gz: 7e181a71f6b9a3a331c8b1a969c0b26e28354f5ada6630e4a8d3aa08b8242d53c492d6b68a5573dd6261a0b2e6942a1d51cacac33b8a4149b13e23d3a8a3f656
6
+ metadata.gz: 889460cd463ecfabf20d363fcb1cd8940eb2e9390950403fcd5409d3a9e566b0dee6aa3200cba0b0869776c87063da9af3a78a4418c0a435c04a8b7fe16fda97
7
+ data.tar.gz: 0f69ba041ff93232e524f11039b8e703ec06d0d1a26dc3a92132749f81613a4f036afaa8acd74249108d53af3608e9e7eee2ca8ff7139a30969a22a1b4ab78b5
data/README.md CHANGED
@@ -31,17 +31,20 @@ The below table captures the current requirements this plugin meets. If you have
31
31
  Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
32
32
 
33
33
  Ensure you specify the following when you call scan
34
+
34
35
  ```ruby
35
36
  fail_build: false, # Allows to continue after scan
36
37
  result_bundle: true # Generate xcresult in output directory
37
38
  ```
38
39
 
39
40
  After scan you can call the plugin, which will look something like this
41
+
40
42
  ```ruby
41
43
  xcresult_to_junit(xcresult_path: './fastlane/test_output/MyScheme.test_result.xcresult', output_path: './fastlane/test_output/')
42
44
  ```
43
45
 
44
46
  We can get a list of the junit reports generated by doing something like so
47
+
45
48
  ```ruby
46
49
  junit_reports = Dir['./test_output/*.junit']
47
50
  # Now we can iterate over the reports and send them where they need to go
@@ -52,13 +55,14 @@ We can get a list of the junit reports generated by doing something like so
52
55
 
53
56
  To run both the tests, and code style validation, run
54
57
 
55
- ```
58
+ ```bash
56
59
  rake
57
60
  ```
58
61
 
59
62
  To automatically fix many of the styling issues, use
60
- ```
61
- rubocop -a
63
+
64
+ ```bash
65
+ rubocop -A
62
66
  ```
63
67
 
64
68
  ## Issues and Feedback
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'date'
2
4
  require 'fastlane/action'
3
5
  require_relative '../helper/xcresult_to_junit_helper'
@@ -6,120 +8,79 @@ module Fastlane
6
8
  module Actions
7
9
  class XcresultToJunitAction < Action
8
10
  def self.run(params)
9
- UI.message("The xcresult_to_junit plugin has started!")
10
- all_results = Helper::XcresultToJunitHelper.load_results(params[:xcresult_path])['actions']['_values']
11
- all_results.each do |test_run|
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 }
11
+ UI.message('The xcresult_to_junit plugin has started!')
12
+ result = Helper::XcresultToJunitHelper.fetch_tests(params[:xcresult_path])
41
13
 
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
- name = attachment['name']['_value']
50
- next unless name != 'kXCTAttachmentLegacyDiagnosticReportData'
51
- timestamp = DateTime.parse(attachment['timestamp']['_value']).to_time.to_i
52
- filename = attachment['filename']['_value']
53
- folder_name = "#{suite_name}.#{testcase_name}"
54
- id = attachment.dig('payloadRef', 'id', '_value')
55
- next if id.nil?
56
- Helper::XcresultToJunitHelper.fetch_screenshot(params[:xcresult_path], "#{junit_folder}/attachments/#{folder_name}", filename.to_s, id)
57
- map[folder_name]['files'].push({ 'description' => name, 'mime-type' => 'image/png', 'path' => "#{folder_name}/#{filename}", 'timestamp' => timestamp })
58
- end
59
- end
60
- end
14
+ devices = result['devices']
15
+ test_plans = result['testNodes']
16
+ map = {}
17
+ junit_folder = Helper::XcresultToJunitHelper.save_device_details_to_file(params[:output_path], devices)
18
+ test_suites = []
61
19
 
62
- if defined?(ref['performanceMetrics']['_value'])
63
- performancemetrics = ""
64
- ref['performanceMetrics']['_values'].each do |metric|
65
- metricname = metric['displayName']['_value']
66
- if defined?(metric['baselineAverage']['_value'])
67
- metricbaseline = metric['baselineAverage']['_value']
68
- else
69
- metricbaseline = 0
70
- end
71
- metricmaxdev = metric['maxPercentRelativeStandardDeviation']['_value']
72
- metricunit = metric['unitOfMeasurement']['_value']
73
- metricave = 0
74
- measurecount = 0
75
- metric['measurements']['_values'].each do |measure|
76
- metricave += measure['_value'].to_f
77
- measurecount += 1
78
- end
79
- metricave = (metricave / measurecount).round(2)
80
- if metricbaseline != 0
81
- metricresult = (((metricbaseline.to_f - metricave) / metricbaseline.to_f) * 100).round(2)
82
- else
83
- metricresult = 0
84
- end
85
- performancemetric = "\nMetric: #{metricname}\nResult: #{metricresult}%\nAverage: #{metricave}#{metricunit}\nBaseline: #{metricbaseline}#{metricunit}\nMax Deviation: #{metricmaxdev}%\n\n"
86
- performancemetrics << performancemetric
87
- end
88
- testcase[:performance] = performancemetrics
89
- end
20
+ test_plans.each do |test_plan|
21
+ test_plan['children'].each do |test_bundle|
22
+ test_bundle['children'].each do |test_suite|
23
+ suite_name = test_suite['name']
24
+ count = 0
25
+ passed = 0
26
+ failed = 0
27
+ test_cases = []
28
+ test_suite['children'].each do |test_case|
29
+ duration = 0.0
30
+ duration = test_case['duration'].sub('s', '').to_f if test_case['duration']
31
+ full_testcase_name = test_case['name'].sub('()', '')
32
+ tags = full_testcase_name.split('_')[1..]
33
+ testcase = { name: full_testcase_name, time: duration }
34
+ count += 1
35
+ if test_case['result'] == 'Passed'
36
+ passed += 1
37
+ elsif test_case['result'] == 'Failed'
38
+ failed += 1
39
+ test_case['children'].each do |failure|
40
+ testcase[:failure] = failure['name'].split(': ')[1]
41
+ testcase[:failure_location] = failure['name'].split(': ')[0]
90
42
  end
91
- if test['testStatus']['_value'] == 'Failure'
92
- failure = Helper::XcresultToJunitHelper.load_object(params[:xcresult_path], test['summaryRef']['id']['_value'])['failureSummaries']['_values'][0]
93
- filename = failure.dig('fileName', '_value')
94
- message = failure['message']['_value']
95
- if filename == '<unknown>' || filename.nil?
96
- testcase[:error] = message
97
- else
98
- testcase[:failure] = message
99
- testcase[:failure_location] = "#{filename}:#{failure['lineNumber']['_value']}"
100
- end
101
- end
102
- suite[:cases] << testcase
103
43
  end
44
+ test_cases << testcase
45
+ map["#{suite_name}.#{full_testcase_name}"] = { 'files' => [], 'tags' => tags }
104
46
  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] }
47
+ suite = { name: suite_name.to_s, count: count, failures: failed, errors: 0, cases: test_cases }
108
48
  test_suites << suite
109
49
  end
110
50
  end
111
- Helper::XcresultToJunitHelper.generate_junit(junit_folder, test_suites)
112
- Helper::XcresultToJunitHelper.save_screenshot_mapping(map, "#{junit_folder}/attachments/")
113
51
  end
114
- UI.message("The xcresult_to_junit plugin has finished!")
52
+
53
+ Helper::XcresultToJunitHelper.generate_junit(junit_folder, test_suites)
54
+ attachments_folder = "#{junit_folder}/attachments"
55
+ Helper::XcresultToJunitHelper.save_attachments(params[:xcresult_path], attachments_folder)
56
+
57
+ test_attachments = Helper::XcresultToJunitHelper.fetch_attachment_manifest(attachments_folder)
58
+
59
+ test_attachments.each do |test_attachment|
60
+ test_identifier = test_attachment['testIdentifier']
61
+ folder_name = test_identifier.sub('()', '').sub('/', '.')
62
+
63
+ test_attachment['attachments'].each do |attachment|
64
+ name = attachment['suggestedHumanReadableName']
65
+ filename = attachment['exportedFileName']
66
+ mime_type = 'image/png'
67
+ mime_type = 'text/plain' if filename.end_with?('.txt')
68
+ timestamp = attachment['timestamp']
69
+ map[folder_name]['files'].push({ 'description' => name, 'mime-type' => mime_type, 'path' => filename,
70
+ 'timestamp' => timestamp })
71
+ end
72
+ end
73
+
74
+ Helper::XcresultToJunitHelper.save_screenshot_mapping(map, attachments_folder)
75
+ UI.message('The xcresult_to_junit plugin has finished!')
115
76
  end
116
77
 
117
78
  def self.description
118
- "Produces junit xml files from Xcode 11+ xcresult files"
79
+ 'Produces junit xml files from Xcode 11+ xcresult files'
119
80
  end
120
81
 
121
82
  def self.authors
122
- ["Shane Birdsall"]
83
+ ['Shane Birdsall']
123
84
  end
124
85
 
125
86
  def self.return_value
@@ -127,26 +88,26 @@ module Fastlane
127
88
  end
128
89
 
129
90
  def self.details
130
- "By using the xcresulttool this plugin parses xcresult files and generates junit reports to be used with other tools to display iOS test results"
91
+ 'By using the xcresulttool this plugin parses xcresult files and generates junit reports to be used with other tools to display iOS test results'
131
92
  end
132
93
 
133
94
  def self.available_options
134
95
  [
135
96
  FastlaneCore::ConfigItem.new(key: :xcresult_path,
136
- env_name: "XCRESULT_TO_JUNIT_XCRESULT_PATH",
137
- description: "The path to the xcresult file",
138
- optional: false,
139
- type: String),
97
+ env_name: 'XCRESULT_TO_JUNIT_XCRESULT_PATH',
98
+ description: 'The path to the xcresult file',
99
+ optional: false,
100
+ type: String),
140
101
  FastlaneCore::ConfigItem.new(key: :output_path,
141
- env_name: "XCRESULT_TO_JUNIT_OUTPUT_PATH",
142
- description: "The path where the output will be placed",
143
- optional: false,
144
- type: String)
102
+ env_name: 'XCRESULT_TO_JUNIT_OUTPUT_PATH',
103
+ description: 'The path where the output will be placed',
104
+ optional: false,
105
+ type: String)
145
106
  ]
146
107
  end
147
108
 
148
109
  def self.is_supported?(platform)
149
- [:ios, :mac].include?(platform)
110
+ %i[ios mac].include?(platform)
150
111
  end
151
112
  end
152
113
  end
@@ -1,25 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fastlane_core/ui/ui'
2
4
  require 'json'
3
5
  require 'fileutils'
4
6
 
5
7
  module Fastlane
6
- UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
8
+ UI = FastlaneCore::UI unless Fastlane.const_defined?('UI')
7
9
 
8
10
  module Helper
9
11
  class XcresultToJunitHelper
10
- def self.load_object(xcresult_path, id)
11
- JSON.parse(FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool get --format json --path #{xcresult_path} --id #{id}"))
12
+ def self.fetch_tests(xcresult_path)
13
+ JSON.parse(FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool get test-results tests --path #{xcresult_path}"))
12
14
  end
13
15
 
14
- def self.load_results(xcresult_path)
15
- JSON.parse(FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool get --format json --path #{xcresult_path}"))
16
+ def self.save_attachments(xcresult_path, output_path)
17
+ FileUtils.mkdir(output_path) unless File.directory?(output_path)
18
+ FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool export attachments --path #{xcresult_path} --output-path #{output_path}")
16
19
  end
17
20
 
18
- def self.fetch_screenshot(xcresult_path, output_path, file_name, id)
19
- unless File.directory?(output_path)
20
- FileUtils.mkdir(output_path)
21
- end
22
- FastlaneCore::CommandExecutor.execute(command: "xcrun xcresulttool export --path #{xcresult_path} --output-path \"#{output_path}/#{file_name}\" --id #{id} --type file")
21
+ def self.fetch_attachment_manifest(attachments_folder)
22
+ JSON.parse(FastlaneCore::CommandExecutor.execute(command: "cat #{attachments_folder}/manifest.json"))
23
23
  end
24
24
 
25
25
  def self.save_screenshot_mapping(map_hash, output_path)
@@ -28,21 +28,24 @@ module Fastlane
28
28
  end
29
29
  end
30
30
 
31
- def self.save_device_details_to_file(output_path, device_destination)
32
- device_udid = device_destination['targetDeviceRecord']['identifier']['_value']
31
+ def self.save_device_details_to_file(output_path, devices)
32
+ device = devices[0]
33
33
  device_details = {
34
- 'udid' => device_udid,
35
- 'name' => device_destination['targetDeviceRecord']['modelName']['_value'],
36
- 'os' => device_destination['targetDeviceRecord']['operatingSystemVersion']['_value']
34
+ 'architecture' => device['architecture'],
35
+ 'udid' => device['deviceId'],
36
+ 'name' => device['deviceName'],
37
+ 'model' => device['modelName'],
38
+ 'os' => device['osVersion'],
39
+ 'platform' => device['platform']
37
40
  }.to_json
38
41
 
39
- junit_folder = "#{output_path}/ios-#{device_udid}.junit"
42
+ junit_folder = "#{output_path}/ios-#{device['deviceId']}.junit"
40
43
  FileUtils.rm_rf(junit_folder)
41
44
  FileUtils.mkdir_p("#{junit_folder}/attachments")
42
45
  File.open("#{junit_folder}/device.json", 'w') do |f|
43
46
  f << device_details
44
47
  end
45
- return junit_folder
48
+ junit_folder
46
49
  end
47
50
 
48
51
  def self.junit_file_start
@@ -104,9 +107,7 @@ module Fastlane
104
107
  elsif testcase[:error]
105
108
  Helper::XcresultToJunitHelper.junit_testcase_error(testcase)
106
109
  end
107
- if testcase[:performance]
108
- Helper::XcresultToJunitHelper.junit_testcase_performance(testcase)
109
- end
110
+ Helper::XcresultToJunitHelper.junit_testcase_performance(testcase) if testcase[:performance]
110
111
  Helper::XcresultToJunitHelper.junit_testcase_end
111
112
  end
112
113
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fastlane
2
4
  module XcresultToJunit
3
- VERSION = "0.3.1"
5
+ VERSION = '0.4.0'
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'fastlane/plugin/xcresult_to_junit/version'
2
4
 
3
5
  module Fastlane
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-xcresult_to_junit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.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: 2021-11-15 00:00:00.000000000 Z
11
+ date: 2024-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: pry
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: pry
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec_junit_formatter
56
+ name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -67,7 +67,7 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: rake
70
+ name: rspec_junit_formatter
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
@@ -122,20 +122,6 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
- - !ruby/object:Gem::Dependency
126
- name: fastlane
127
- requirement: !ruby/object:Gem::Requirement
128
- requirements:
129
- - - ">="
130
- - !ruby/object:Gem::Version
131
- version: 2.134.0
132
- type: :development
133
- prerelease: false
134
- version_requirements: !ruby/object:Gem::Requirement
135
- requirements:
136
- - - ">="
137
- - !ruby/object:Gem::Version
138
- version: 2.134.0
139
125
  description:
140
126
  email: shane.birdsall@fiserv.com
141
127
  executables: []
@@ -167,8 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
153
  - !ruby/object:Gem::Version
168
154
  version: '0'
169
155
  requirements: []
170
- rubyforge_project:
171
- rubygems_version: 2.7.6.2
156
+ rubygems_version: 3.4.10
172
157
  signing_key:
173
158
  specification_version: 4
174
159
  summary: Produces junit xml files from Xcode 11+ xcresult files