fastlane-plugin-retry_tests 1.2.5 → 1.2.6
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 +4 -4
- metadata +1 -15
- data/lib/fastlane/plugin/retry_tests.rb +0 -16
- data/lib/fastlane/plugin/retry_tests/actions/collate_junit_reports.rb +0 -178
- data/lib/fastlane/plugin/retry_tests/actions/multi_scan.rb +0 -143
- data/lib/fastlane/plugin/retry_tests/actions/suppress_tests.rb +0 -93
- data/lib/fastlane/plugin/retry_tests/actions/suppress_tests_from_junit.rb +0 -102
- data/lib/fastlane/plugin/retry_tests/actions/suppressed_tests.rb +0 -72
- data/lib/fastlane/plugin/retry_tests/actions/tests_from_junit.rb +0 -59
- data/lib/fastlane/plugin/retry_tests/actions/tests_from_xctestrun.rb +0 -65
- data/lib/fastlane/plugin/retry_tests/helper/correcting_scan_helper.rb +0 -164
- data/lib/fastlane/plugin/retry_tests/helper/junit_helper.rb +0 -94
- data/lib/fastlane/plugin/retry_tests/helper/reportname_helper.rb +0 -70
- data/lib/fastlane/plugin/retry_tests/helper/test_collector.rb +0 -60
- data/lib/fastlane/plugin/retry_tests/helper/xcodebuild_string.rb +0 -14
- data/lib/fastlane/plugin/retry_tests/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 365832dd6472ba1a347a30744ed61d39c9df4290
|
4
|
+
data.tar.gz: 6e2efd77e21cec3df9f7cd06e9a3c93754c86680
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f13887c8852db136a3267e4c6723226337e0cabc617fc51b5ed2d568542fef8aa812cba646dcace661d2162bc649c8e16cbc33e752ce7b006248faaa2264d972
|
7
|
+
data.tar.gz: 1cdd26937449d5a2af8f001866af143876e35a83db33550aa3e4e4bfa3769885f2c259798ccfb741d47bddc35b17c8e5ba6cad076eeef89796f3c5dce6b32035
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-retry_tests
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gloria Chow
|
@@ -180,20 +180,6 @@ extra_rdoc_files: []
|
|
180
180
|
files:
|
181
181
|
- LICENSE
|
182
182
|
- README.md
|
183
|
-
- lib/fastlane/plugin/retry_tests.rb
|
184
|
-
- lib/fastlane/plugin/retry_tests/actions/collate_junit_reports.rb
|
185
|
-
- lib/fastlane/plugin/retry_tests/actions/multi_scan.rb
|
186
|
-
- lib/fastlane/plugin/retry_tests/actions/suppress_tests.rb
|
187
|
-
- lib/fastlane/plugin/retry_tests/actions/suppress_tests_from_junit.rb
|
188
|
-
- lib/fastlane/plugin/retry_tests/actions/suppressed_tests.rb
|
189
|
-
- lib/fastlane/plugin/retry_tests/actions/tests_from_junit.rb
|
190
|
-
- lib/fastlane/plugin/retry_tests/actions/tests_from_xctestrun.rb
|
191
|
-
- lib/fastlane/plugin/retry_tests/helper/correcting_scan_helper.rb
|
192
|
-
- lib/fastlane/plugin/retry_tests/helper/junit_helper.rb
|
193
|
-
- lib/fastlane/plugin/retry_tests/helper/reportname_helper.rb
|
194
|
-
- lib/fastlane/plugin/retry_tests/helper/test_collector.rb
|
195
|
-
- lib/fastlane/plugin/retry_tests/helper/xcodebuild_string.rb
|
196
|
-
- lib/fastlane/plugin/retry_tests/version.rb
|
197
183
|
homepage: https://github.com/kouzoh/fastlane-plugin-retry_tests
|
198
184
|
licenses:
|
199
185
|
- MIT
|
@@ -1,16 +0,0 @@
|
|
1
|
-
#require 'fastlane/plugin/retry_tests/version'
|
2
|
-
|
3
|
-
module Fastlane
|
4
|
-
module TestCenter
|
5
|
-
# Return all .rb files inside the "actions" and "helper" directory
|
6
|
-
def self.all_classes
|
7
|
-
Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# By default we want to import all available actions and helpers
|
13
|
-
# A plugin can contain any number of actions and plugins
|
14
|
-
Fastlane::TestCenter.all_classes.each do |current|
|
15
|
-
require current
|
16
|
-
end
|
@@ -1,178 +0,0 @@
|
|
1
|
-
|
2
|
-
require 'nokogiri'
|
3
|
-
require 'nokogiri-plist'
|
4
|
-
require 'FileUtils'
|
5
|
-
module Fastlane
|
6
|
-
module Actions
|
7
|
-
class CollateJunitReportsAction < Action
|
8
|
-
|
9
|
-
def self.run(params)
|
10
|
-
report_filepaths = params[:reports].reverse
|
11
|
-
if report_filepaths.size == 1
|
12
|
-
FileUtils.cp(report_filepaths[0], params[:collated_report])
|
13
|
-
else
|
14
|
-
target_report = File.open(report_filepaths.shift) {|f| Nokogiri::XML(f)}
|
15
|
-
file_name = params[:collated_report] + "/#{params[:scheme]}.test_result/1_Test/action_TestSummaries.plist"
|
16
|
-
reports = report_filepaths.map { |report_filepath| Nokogiri::XML(Nokogiri::PList(open(report_filepath)).to_plist_xml) }
|
17
|
-
reports.each do |retry_report|
|
18
|
-
mergeLists(target_report, retry_report, params)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
merge_assets(params[:assets], params[:collated_report] + "/Attachments")
|
22
|
-
copy_log(params[:logs], params[:collated_report] + "/")
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.clean_report(report)
|
26
|
-
report = report.gsub(/<(\w+): 0x(\w+)>/, '(\1: 0x\2)')
|
27
|
-
report = report.gsub(/<XCAccessibilityElement:\/>0x(\w+)/, '(XCAccessibilityElement): 0x\1')
|
28
|
-
report
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.copy_log(log_files, logs_folder)
|
32
|
-
target_log = log_files.shift
|
33
|
-
FileUtils.cp_r(target_log, logs_folder)
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.mergeLists(target_report, retry_report, params)
|
37
|
-
retry_report = clean_report(retry_report)
|
38
|
-
UI.verbose("Merging retried results...")
|
39
|
-
Dir.mkdir(params[:collated_report]) unless File.exists?(params[:collated_report])
|
40
|
-
file_name = params[:collated_report] + "/action_TestSummaries.plist"
|
41
|
-
retried_tests = retry_report.xpath("//key[contains(.,'TestSummaryGUID')]/..")
|
42
|
-
current_node = retried_tests.shift
|
43
|
-
while (current_node != nil)
|
44
|
-
testName = get_test_name(current_node)
|
45
|
-
matching_node = target_report.at_xpath("//string[contains(.,'#{testName}')]/..")
|
46
|
-
|
47
|
-
if (!matching_node.nil?)
|
48
|
-
matching_node.previous.next.replace(current_node)
|
49
|
-
write_report_to_file(target_report, file_name)
|
50
|
-
end
|
51
|
-
current_node = retried_tests.shift
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.merge_assets(asset_files, assets_folder)
|
56
|
-
UI.verbose ("Merging screenshot folders...")
|
57
|
-
Dir.mkdir(assets_folder) unless File.exists?(assets_folder)
|
58
|
-
asset_files.each do |folder|
|
59
|
-
FileUtils.cp_r(Dir[folder + '/*'], assets_folder)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.merge_logs(log_files, logs_folder)
|
64
|
-
UI.verbose("Merging console logs...")
|
65
|
-
target_log = log_files.shift
|
66
|
-
log_files.each do |log|
|
67
|
-
to_append = File.read(log)
|
68
|
-
File.open(target_log, "a") do |handle|
|
69
|
-
handle.puts to_append
|
70
|
-
end
|
71
|
-
FileUtils.cp_r(target_log, logs_folder)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.write_report_to_file(report, file_name)
|
76
|
-
UI.verbose("Writing merged results to file...")
|
77
|
-
File.new(file_name, 'w')
|
78
|
-
File.open(file_name, 'w') do |f|
|
79
|
-
f.write(report.to_xml)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def self.get_test_name(test_data)
|
84
|
-
test_name = test_data.xpath("(//key[contains(.,'TestSummaryGUID')])/../key[contains(.,'TestName')]/following-sibling::string").to_a[0].to_s
|
85
|
-
test_name = test_name[8..-10]
|
86
|
-
test_name
|
87
|
-
end
|
88
|
-
|
89
|
-
#####################################################
|
90
|
-
# @!group Documentation
|
91
|
-
#####################################################
|
92
|
-
|
93
|
-
def self.description
|
94
|
-
"Combines and combines tests from multiple junit report files"
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.details
|
98
|
-
"The first junit report is used as the base report. Testcases " \
|
99
|
-
"from other reports are added if they do not already exist, or " \
|
100
|
-
"if the testcases already exist, they are replaced." \
|
101
|
-
"" \
|
102
|
-
"This is done because it is assumed that fragile tests, when " \
|
103
|
-
"re-run will often succeed due to less interference from other " \
|
104
|
-
"tests and the subsequent junit reports will have more passed tests." \
|
105
|
-
"" \
|
106
|
-
"Inspired by Derek Yang's fastlane-plugin-merge_junit_report"
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.available_options
|
110
|
-
[
|
111
|
-
FastlaneCore::ConfigItem.new(
|
112
|
-
key: :scheme,
|
113
|
-
env_name: 'SCHEME',
|
114
|
-
description: 'The test scheme being run',
|
115
|
-
optional: false,
|
116
|
-
default_value: '',
|
117
|
-
type: String
|
118
|
-
),
|
119
|
-
FastlaneCore::ConfigItem.new(
|
120
|
-
key: :reports,
|
121
|
-
env_name: 'COLLATE_JUNIT_REPORTS_REPORTS',
|
122
|
-
description: 'An array of junit reports to collate. The first report is used as the base into which other reports are merged in',
|
123
|
-
optional: false,
|
124
|
-
type: Array,
|
125
|
-
verify_block: proc do |reports|
|
126
|
-
UI.user_error!('No junit report files found') if reports.empty?
|
127
|
-
reports.each do |report|
|
128
|
-
UI.user_error!("Error: junit report not found: '#{report}'") unless File.exist?(report)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
),
|
132
|
-
FastlaneCore::ConfigItem.new(
|
133
|
-
key: :collated_report,
|
134
|
-
env_name: 'COLLATE_JUNIT_REPORTS_COLLATED_REPORT',
|
135
|
-
description: 'The final junit report file where all testcases will be merged into',
|
136
|
-
optional: true,
|
137
|
-
default_value: 'result.xml',
|
138
|
-
type: String
|
139
|
-
),
|
140
|
-
FastlaneCore::ConfigItem.new(
|
141
|
-
key: :assets,
|
142
|
-
env_name: 'COLLATE_JUNIT_REPORTS_ASSETS',
|
143
|
-
description: 'An array of junit reports to collate. The first report is used as the base into which other reports are merged in',
|
144
|
-
optional: false,
|
145
|
-
type: Array,
|
146
|
-
verify_block: proc do |assets|
|
147
|
-
UI.user_error!('No junit report files found') if assets.empty?
|
148
|
-
assets.each do |asset|
|
149
|
-
UI.user_error!("Error: junit report not found: '#{asset}'") unless File.exist?(asset)
|
150
|
-
end
|
151
|
-
end
|
152
|
-
),
|
153
|
-
FastlaneCore::ConfigItem.new(
|
154
|
-
key: :logs,
|
155
|
-
env_name: 'COLLATE_JUNIT_REPORTS_LOGS',
|
156
|
-
description: 'An array of junit reports to collate. The first report is used as the base into which other reports are merged in',
|
157
|
-
optional: false,
|
158
|
-
type: Array,
|
159
|
-
verify_block: proc do |logs|
|
160
|
-
UI.user_error!('No junit report files found') if logs.empty?
|
161
|
-
logs.each do |log|
|
162
|
-
UI.user_error!("Error: junit report not found: '#{log}'") unless File.exist?(log)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
)
|
166
|
-
]
|
167
|
-
end
|
168
|
-
|
169
|
-
def self.authors
|
170
|
-
["lyndsey-ferguson/@lyndseydf"]
|
171
|
-
end
|
172
|
-
|
173
|
-
def self.is_supported?(platform)
|
174
|
-
platform == :ios
|
175
|
-
end
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
@@ -1,143 +0,0 @@
|
|
1
|
-
module Fastlane
|
2
|
-
module Actions
|
3
|
-
require 'fastlane/actions/scan'
|
4
|
-
require 'shellwords'
|
5
|
-
|
6
|
-
class MultiScanAction < Action
|
7
|
-
def self.run(params)
|
8
|
-
try_count = 0
|
9
|
-
scan_options = params.values.reject { |k| k == :try_count }
|
10
|
-
final_report_path = scan_options[:result_bundle]
|
11
|
-
unless Helper.test?
|
12
|
-
FastlaneCore::PrintTable.print_values(
|
13
|
-
config: params._values.reject { |k, v| scan_options.key?(k) },
|
14
|
-
title: "Summary for multi_scan (test_center v#{Fastlane::TestCenter::VERSION})"
|
15
|
-
)
|
16
|
-
end
|
17
|
-
|
18
|
-
scan_options = config_with_junit_report(scan_options)
|
19
|
-
|
20
|
-
begin
|
21
|
-
try_count += 1
|
22
|
-
scan_options = config_with_retry(scan_options, try_count)
|
23
|
-
config = FastlaneCore::Configuration.create(Fastlane::Actions::ScanAction.available_options, scan_options)
|
24
|
-
Fastlane::Actions::ScanAction.run(config)
|
25
|
-
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
26
|
-
UI.verbose("Scan failed with #{e}")
|
27
|
-
if try_count < params[:try_count]
|
28
|
-
report_filepath = junit_report_filepath(scan_options)
|
29
|
-
failed_tests = other_action.tests_from_junit(junit: report_filepath)[:failed]
|
30
|
-
scan_options[:only_testing] = failed_tests.map(&:shellescape)
|
31
|
-
retry
|
32
|
-
end
|
33
|
-
end
|
34
|
-
merge_reports(scan_options, final_report_path)
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.merge_reports(scan_options, final_report_path)
|
38
|
-
folder = get_folder_root(scan_options[:output_directory])
|
39
|
-
report_files = Dir.glob("#{folder}*/#{scan_options[:scheme]}.test_result/1_Test/action_TestSummaries.plist")
|
40
|
-
asset_files = Dir.glob("#{folder}*/#{scan_options[:scheme]}.test_result/1_Test/Attachments")
|
41
|
-
log_files = Dir.glob("#{folder}*/#{scan_options[:scheme]}.test_result/1_Test/action.xcactivitylog")
|
42
|
-
#Merge all reports, screenshots, and logs if there were retried tests
|
43
|
-
|
44
|
-
if report_files.size > 1
|
45
|
-
other_action.collate_junit_reports(
|
46
|
-
scheme: scan_options[:scheme],
|
47
|
-
reports: report_files,
|
48
|
-
collated_report: final_report_path,
|
49
|
-
assets: asset_files,
|
50
|
-
logs: log_files,
|
51
|
-
)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def self.config_has_junit_report(config)
|
56
|
-
output_types = config.fetch(:output_types, '').to_s.split(',')
|
57
|
-
output_filenames = config.fetch(:output_files, '').to_s.split(',')
|
58
|
-
|
59
|
-
output_type_file_count_match = output_types.size == output_filenames.size
|
60
|
-
output_types.include?('junit') && (output_type_file_count_match || config[:custom_report_file_name].to_s.strip.length > 0)
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.config_with_retry(config, count)
|
64
|
-
folder = get_folder_root(config[:result_bundle])
|
65
|
-
config[:result_bundle] = (folder + count.to_s)
|
66
|
-
config[:output_directory] = (folder + count.to_s)
|
67
|
-
config
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.get_folder_root(folder)
|
71
|
-
folder = folder.gsub(/ *\d+$/, '')
|
72
|
-
folder
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.config_with_junit_report(config)
|
76
|
-
return config if config_has_junit_report(config)
|
77
|
-
|
78
|
-
if config[:output_types].to_s.strip.empty? || config[:custom_report_file_name]
|
79
|
-
config[:custom_report_file_name] ||= 'report.xml'
|
80
|
-
config[:output_types] = 'junit'
|
81
|
-
elsif config[:output_types].strip == 'junit' && config[:output_files].to_s.strip.empty?
|
82
|
-
config[:custom_report_file_name] ||= 'report.xml'
|
83
|
-
elsif !config[:output_types].split(',').include?('junit')
|
84
|
-
config[:output_types] << ',junit'
|
85
|
-
config[:output_files] << ',report.xml'
|
86
|
-
elsif config[:output_files].nil?
|
87
|
-
config[:output_files] = config[:output_types].split(',').map { |type| "report.#{type}" }.join(',')
|
88
|
-
end
|
89
|
-
config
|
90
|
-
end
|
91
|
-
|
92
|
-
def self.junit_report_filename(config)
|
93
|
-
report_filename = config[:custom_report_file_name]
|
94
|
-
if report_filename.nil?
|
95
|
-
junit_index = config[:output_types].split(',').find_index('junit')
|
96
|
-
report_filename = config[:output_files].to_s.split(',')[junit_index]
|
97
|
-
end
|
98
|
-
report_filename
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.junit_report_filepath(config)
|
102
|
-
File.absolute_path(File.join(config[:output_directory], junit_report_filename(config)))
|
103
|
-
end
|
104
|
-
|
105
|
-
#####################################################
|
106
|
-
# @!group Documentation
|
107
|
-
#####################################################
|
108
|
-
|
109
|
-
def self.description
|
110
|
-
"Uses scan to run Xcode tests a given number of times: only re-testing failing tests."
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.details
|
114
|
-
"Use this action to run your tests if you have fragile tests that fail sporadically."
|
115
|
-
end
|
116
|
-
|
117
|
-
def self.scan_options
|
118
|
-
ScanAction.available_options
|
119
|
-
end
|
120
|
-
|
121
|
-
def self.available_options
|
122
|
-
scan_options + [
|
123
|
-
FastlaneCore::ConfigItem.new(
|
124
|
-
key: :try_count,
|
125
|
-
env_name: "FL_MULTI_SCAN_TRY_COUNT",
|
126
|
-
description: "The number of times to retry running tests via scan",
|
127
|
-
type: Integer,
|
128
|
-
is_string: false,
|
129
|
-
default_value: 1
|
130
|
-
)
|
131
|
-
]
|
132
|
-
end
|
133
|
-
|
134
|
-
def self.authors
|
135
|
-
["lyndsey-ferguson/@lyndseydf"]
|
136
|
-
end
|
137
|
-
|
138
|
-
def self.is_supported?(platform)
|
139
|
-
platform == :ios
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
module Fastlane
|
2
|
-
module Actions
|
3
|
-
class SuppressTestsAction < Action
|
4
|
-
require 'xcodeproj'
|
5
|
-
|
6
|
-
def self.run(params)
|
7
|
-
project_path = params[:xcodeproj]
|
8
|
-
all_tests_to_skip = params[:tests]
|
9
|
-
scheme = params[:scheme]
|
10
|
-
|
11
|
-
scheme_filepaths = Dir.glob("#{project_path}/{xcshareddata,xcuserdata}/**/xcschemes/#{scheme || '*'}.xcscheme")
|
12
|
-
if scheme_filepaths.length.zero?
|
13
|
-
UI.user_error!("Error: cannot find any scheme named #{scheme}") unless scheme.nil?
|
14
|
-
UI.user_error!("Error: cannot find any schemes in the Xcode project")
|
15
|
-
end
|
16
|
-
|
17
|
-
scheme_filepaths.each do |scheme_filepath|
|
18
|
-
is_dirty = false
|
19
|
-
xcscheme = Xcodeproj::XCScheme.new(scheme_filepath)
|
20
|
-
xcscheme.test_action.testables.each do |testable|
|
21
|
-
buildable_name = File.basename(testable.buildable_references[0].buildable_name, '.xctest')
|
22
|
-
|
23
|
-
tests_to_skip = all_tests_to_skip.select { |test| test.start_with?(buildable_name) }
|
24
|
-
.map { |test| test.sub("#{buildable_name}/", '') }
|
25
|
-
|
26
|
-
tests_to_skip.each do |test_to_skip|
|
27
|
-
skipped_test = Xcodeproj::XCScheme::TestAction::TestableReference::SkippedTest.new
|
28
|
-
skipped_test.identifier = test_to_skip
|
29
|
-
testable.add_skipped_test(skipped_test)
|
30
|
-
is_dirty = true
|
31
|
-
end
|
32
|
-
end
|
33
|
-
xcscheme.save! if is_dirty
|
34
|
-
end
|
35
|
-
nil
|
36
|
-
end
|
37
|
-
|
38
|
-
#####################################################
|
39
|
-
# @!group Documentation
|
40
|
-
#####################################################
|
41
|
-
|
42
|
-
def self.description
|
43
|
-
"Suppresses specific tests in a specific or all Xcode Schemes in a given project"
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.available_options
|
47
|
-
[
|
48
|
-
FastlaneCore::ConfigItem.new(
|
49
|
-
key: :xcodeproj,
|
50
|
-
env_name: "FL_SUPPRESS_TESTS_XCODE_PROJECT",
|
51
|
-
description: "The file path to the Xcode project file to modify",
|
52
|
-
verify_block: proc do |path|
|
53
|
-
UI.user_error!("Error: Xcode project file path not given!") unless path and !path.empty?
|
54
|
-
UI.user_error!("Error: Xcode project '#{path}' not found!") unless Dir.exist?(path)
|
55
|
-
end
|
56
|
-
),
|
57
|
-
FastlaneCore::ConfigItem.new(
|
58
|
-
key: :tests,
|
59
|
-
env_name: "FL_SUPPRESS_TESTS_TESTS_TO_SUPPRESS",
|
60
|
-
description: "A list of tests to suppress",
|
61
|
-
verify_block: proc do |tests|
|
62
|
-
UI.user_error!("Error: no tests were given to suppress!") unless tests and !tests.empty?
|
63
|
-
tests.each do |test_identifier|
|
64
|
-
is_valid_test_identifier = %r{^[a-zA-Z][a-zA-Z0-9]+\/[a-zA-Z][a-zA-Z0-9]+(\/test[a-zA-Z0-9]+)?$} =~ test_identifier
|
65
|
-
unless is_valid_test_identifier
|
66
|
-
UI.user_error!("Error: invalid test identifier '#{test_identifier}'. It must be in the format of 'Testable/TestSuiteToSuppress' or 'Testable/TestSuiteToSuppress/testToSuppress'")
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end,
|
70
|
-
type: Array
|
71
|
-
),
|
72
|
-
FastlaneCore::ConfigItem.new(
|
73
|
-
key: :scheme,
|
74
|
-
optional: true,
|
75
|
-
env_name: "FL_SUPPRESS_TESTS_SCHEME_TO_UPDATE",
|
76
|
-
description: "The Xcode scheme where the tests should be suppressed",
|
77
|
-
verify_block: proc do |scheme_name|
|
78
|
-
UI.user_error!("Error: Xcode Scheme '#{scheme_name}' is not valid!") if scheme_name and scheme_name.empty?
|
79
|
-
end
|
80
|
-
)
|
81
|
-
]
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.authors
|
85
|
-
["lyndsey-ferguson/@lyndseydf"]
|
86
|
-
end
|
87
|
-
|
88
|
-
def self.is_supported?(platform)
|
89
|
-
platform == :ios
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
@@ -1,102 +0,0 @@
|
|
1
|
-
module Fastlane
|
2
|
-
module Actions
|
3
|
-
class SuppressTestsFromJunitAction < Action
|
4
|
-
def self.run(params)
|
5
|
-
project_path = params[:xcodeproj]
|
6
|
-
scheme = params[:scheme]
|
7
|
-
|
8
|
-
scheme_filepaths = Dir.glob("#{project_path}/{xcshareddata,xcuserdata}/**/xcschemes/#{scheme || '*'}.xcscheme")
|
9
|
-
if scheme_filepaths.length.zero?
|
10
|
-
UI.user_error!("Error: cannot find any scheme named #{scheme}") unless scheme.nil?
|
11
|
-
UI.user_error!("Error: cannot find any schemes in the Xcode project")
|
12
|
-
end
|
13
|
-
|
14
|
-
testables = Hash.new([])
|
15
|
-
desired_passed_status = (params[:suppress_type] == :passing)
|
16
|
-
|
17
|
-
report = ::TestCenter::Helper::XcodeJunit::Report.new(params[:junit])
|
18
|
-
|
19
|
-
report.testables.each do |testable|
|
20
|
-
testables[testable.name] = []
|
21
|
-
testable.testsuites.each do |testsuite|
|
22
|
-
testsuite.testcases.each do |testcase|
|
23
|
-
if testcase.passed? == desired_passed_status
|
24
|
-
testables[testable.name] << testcase.skipped_test
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
scheme_filepaths.each do |scheme_filepath|
|
31
|
-
is_dirty = false
|
32
|
-
xcscheme = Xcodeproj::XCScheme.new(scheme_filepath)
|
33
|
-
|
34
|
-
xcscheme.test_action.testables.each do |testable|
|
35
|
-
buildable_name = testable.buildable_references[0].buildable_name
|
36
|
-
testables[buildable_name].each do |skipped_test|
|
37
|
-
testable.add_skipped_test(skipped_test)
|
38
|
-
is_dirty = true
|
39
|
-
end
|
40
|
-
end
|
41
|
-
xcscheme.save! if is_dirty
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
#####################################################
|
46
|
-
# @!group Documentation
|
47
|
-
#####################################################
|
48
|
-
|
49
|
-
def self.description
|
50
|
-
"Uses a junit xml report file to suppress either passing or failing tests in an Xcode Scheme"
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.available_options
|
54
|
-
[
|
55
|
-
FastlaneCore::ConfigItem.new(
|
56
|
-
key: :xcodeproj,
|
57
|
-
env_name: "FL_SUPPRESS_TESTS_FROM_JUNIT_XCODE_PROJECT",
|
58
|
-
description: "The file path to the Xcode project file to modify",
|
59
|
-
verify_block: proc do |path|
|
60
|
-
UI.user_error!("Error: Xcode project file path not given!") unless path and !path.empty?
|
61
|
-
UI.user_error!("Error: Xcode project '#{path}' not found!") unless Dir.exist?(path)
|
62
|
-
end
|
63
|
-
),
|
64
|
-
FastlaneCore::ConfigItem.new(
|
65
|
-
key: :junit,
|
66
|
-
env_name: "FL_SUPPRESS_TESTS_FROM_JUNIT_JUNIT_REPORT",
|
67
|
-
description: "The junit xml report file from which to collect the tests to suppress",
|
68
|
-
verify_block: proc do |path|
|
69
|
-
UI.user_error!("Error: cannot find the junit xml report file '#{path}'") unless File.exist?(path)
|
70
|
-
end
|
71
|
-
),
|
72
|
-
FastlaneCore::ConfigItem.new(
|
73
|
-
key: :scheme,
|
74
|
-
optional: true,
|
75
|
-
env_name: "FL_SUPPRESS_TESTS_FROM_JUNIT_SCHEME_TO_UPDATE",
|
76
|
-
description: "The Xcode scheme where the tests should be suppressed",
|
77
|
-
verify_block: proc do |scheme_name|
|
78
|
-
UI.user_error!("Error: Xcode Scheme '#{scheme_name}' is not valid!") if scheme_name and scheme_name.empty?
|
79
|
-
end
|
80
|
-
),
|
81
|
-
FastlaneCore::ConfigItem.new(
|
82
|
-
key: :suppress_type,
|
83
|
-
type: Symbol,
|
84
|
-
env_name: "FL_SUPPRESS_TESTS_FROM_JUNIT_SUPPRESS_TYPE",
|
85
|
-
description: "Tests to suppress are either :failed or :passing",
|
86
|
-
verify_block: proc do |type|
|
87
|
-
UI.user_error!("Error: suppress type ':#{type}' is invalid! Only :failed or :passing are valid types") unless %i[failed passing].include?(type)
|
88
|
-
end
|
89
|
-
)
|
90
|
-
]
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.authors
|
94
|
-
["lyndsey-ferguson/@lyndseydf"]
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.is_supported?(platform)
|
98
|
-
platform == :ios
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
module Fastlane
|
2
|
-
module Actions
|
3
|
-
class SuppressedTestsAction < Action
|
4
|
-
require 'set'
|
5
|
-
|
6
|
-
def self.run(params)
|
7
|
-
project_path = params[:xcodeproj]
|
8
|
-
scheme = params[:scheme]
|
9
|
-
|
10
|
-
scheme_filepaths = Dir.glob("#{project_path}/{xcshareddata,xcuserdata}/**/xcschemes/#{scheme || '*'}.xcscheme")
|
11
|
-
if scheme_filepaths.length.zero?
|
12
|
-
UI.user_error!("Error: cannot find any scheme named #{scheme}") unless scheme.nil?
|
13
|
-
UI.user_error!("Error: cannot find any schemes in the Xcode project")
|
14
|
-
end
|
15
|
-
|
16
|
-
skipped_tests = Set.new
|
17
|
-
scheme_filepaths.each do |scheme_filepath|
|
18
|
-
xcscheme = Xcodeproj::XCScheme.new(scheme_filepath)
|
19
|
-
xcscheme.test_action.testables.each do |testable|
|
20
|
-
buildable_name = testable.buildable_references[0]
|
21
|
-
.buildable_name
|
22
|
-
|
23
|
-
buildable_name = File.basename(buildable_name, '.xctest')
|
24
|
-
testable.skipped_tests.map do |skipped_test|
|
25
|
-
skipped_tests.add("#{buildable_name}/#{skipped_test.identifier}")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
skipped_tests.to_a
|
30
|
-
end
|
31
|
-
|
32
|
-
#####################################################
|
33
|
-
# @!group Documentation
|
34
|
-
#####################################################
|
35
|
-
|
36
|
-
def self.description
|
37
|
-
"Retrieves a list of tests that are suppressed in a specific or all Xcode Schemes in a project"
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.available_options
|
41
|
-
[
|
42
|
-
FastlaneCore::ConfigItem.new(
|
43
|
-
key: :xcodeproj,
|
44
|
-
env_name: "FL_SUPPRESSED_TESTS_XCODE_PROJECT",
|
45
|
-
description: "The file path to the Xcode project file to read the skipped tests from",
|
46
|
-
verify_block: proc do |path|
|
47
|
-
UI.user_error!("Error: Xcode project file path not given!") unless path and !path.empty?
|
48
|
-
UI.user_error!("Error: Xcode project '#{path}' not found!") unless Dir.exist?(path)
|
49
|
-
end
|
50
|
-
),
|
51
|
-
FastlaneCore::ConfigItem.new(
|
52
|
-
key: :scheme,
|
53
|
-
optional: true,
|
54
|
-
env_name: "FL_SUPPRESSED_TESTS_SCHEME_TO_UPDATE",
|
55
|
-
description: "The Xcode scheme where the suppressed tests may be",
|
56
|
-
verify_block: proc do |scheme_name|
|
57
|
-
UI.user_error!("Error: Xcode Scheme '#{scheme_name}' is not valid!") if scheme_name and scheme_name.empty?
|
58
|
-
end
|
59
|
-
)
|
60
|
-
]
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.authors
|
64
|
-
["lyndsey-ferguson/@lyndseydf"]
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.is_supported?(platform)
|
68
|
-
platform == :ios
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Fastlane
|
2
|
-
module Actions
|
3
|
-
class TestsFromJunitAction < Action
|
4
|
-
def self.run(params)
|
5
|
-
report = ::TestCenter::Helper::XcodeJunit::Report.new(params[:junit])
|
6
|
-
passing = []
|
7
|
-
failed = []
|
8
|
-
report.testables.each do |testable|
|
9
|
-
testable.testsuites.each do |testsuite|
|
10
|
-
testsuite.testcases.each do |testcase|
|
11
|
-
if testcase.passed?
|
12
|
-
passing << testcase.identifier
|
13
|
-
else
|
14
|
-
failed << testcase.identifier
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
{
|
20
|
-
failed: failed,
|
21
|
-
passing: passing
|
22
|
-
}
|
23
|
-
end
|
24
|
-
|
25
|
-
#####################################################
|
26
|
-
# @!group Documentation
|
27
|
-
#####################################################
|
28
|
-
|
29
|
-
def self.description
|
30
|
-
"Retrieves the failing and passing tests as reported in a junit xml file"
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.available_options
|
34
|
-
[
|
35
|
-
FastlaneCore::ConfigItem.new(
|
36
|
-
key: :junit,
|
37
|
-
env_name: "FL_SUPPRESS_TESTS_FROM_JUNIT_JUNIT_REPORT", # The name of the environment variable
|
38
|
-
description: "The junit xml report file from which to collect the tests to suppress",
|
39
|
-
verify_block: proc do |path|
|
40
|
-
UI.user_error!("Error: cannot find the junit xml report file '#{path}'") unless File.exist?(path)
|
41
|
-
end
|
42
|
-
)
|
43
|
-
]
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.return_value
|
47
|
-
"A Hash with an Array of :passing and :failed tests"
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.authors
|
51
|
-
["lyndsey-ferguson/lyndseydf"]
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.is_supported?(platform)
|
55
|
-
platform == :ios
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'plist'
|
2
|
-
|
3
|
-
module Fastlane
|
4
|
-
module Actions
|
5
|
-
class TestsFromXctestrunAction < Action
|
6
|
-
def self.run(params)
|
7
|
-
return xctestrun_tests(params[:xctestrun])
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.xctestrun_tests(xctestrun_path)
|
11
|
-
xctestrun = Plist.parse_xml(xctestrun_path)
|
12
|
-
xctestrun_rootpath = File.dirname(xctestrun_path)
|
13
|
-
tests = Hash.new([])
|
14
|
-
xctestrun.each do |testable_name, xctestrun_config|
|
15
|
-
test_identifiers = XCTestList.tests(xctest_bundle_path(xctestrun_rootpath, xctestrun_config))
|
16
|
-
if xctestrun_config.key?('SkipTestIdentifiers')
|
17
|
-
test_identifiers.reject! { |test_identifier| xctestrun_config['SkipTestIdentifiers'].include?(test_identifier) }
|
18
|
-
end
|
19
|
-
tests[testable_name] = test_identifiers.map do |test_identifier|
|
20
|
-
"#{testable_name.shellescape}/#{test_identifier}"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
tests
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.xctest_bundle_path(xctestrun_rootpath, xctestrun_config)
|
27
|
-
xctest_host_path = xctestrun_config['TestHostPath'].sub('__TESTROOT__', xctestrun_rootpath)
|
28
|
-
xctestrun_config['TestBundlePath'].sub('__TESTHOST__', xctest_host_path).sub('__TESTROOT__', xctestrun_rootpath)
|
29
|
-
end
|
30
|
-
|
31
|
-
#####################################################
|
32
|
-
# @!group Documentation
|
33
|
-
#####################################################
|
34
|
-
|
35
|
-
def self.description
|
36
|
-
"Retrieves all of the tests from xctest bundles referenced by the xctestrun file"
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.available_options
|
40
|
-
[
|
41
|
-
FastlaneCore::ConfigItem.new(
|
42
|
-
key: :xctestrun,
|
43
|
-
env_name: "FL_SUPPRESS_TESTS_FROM_XCTESTRUN_FILE",
|
44
|
-
description: "The xctestrun file to use to find where the xctest bundle file is for test retrieval",
|
45
|
-
verify_block: proc do |path|
|
46
|
-
UI.user_error!("Error: cannot find the xctestrun file '#{path}'") unless File.exist?(path)
|
47
|
-
end
|
48
|
-
)
|
49
|
-
]
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.return_value
|
53
|
-
"A Hash of testable => tests, where testable is the name of the test target and tests is an array of test identifiers"
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.authors
|
57
|
-
["lyndsey-ferguson/lyndseydf"]
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.is_supported?(platform)
|
61
|
-
platform == :ios
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,164 +0,0 @@
|
|
1
|
-
module TestCenter
|
2
|
-
module Helper
|
3
|
-
require 'fastlane_core/ui/ui.rb'
|
4
|
-
require 'plist'
|
5
|
-
|
6
|
-
class CorrectingScanHelper
|
7
|
-
attr_reader :retry_total_count
|
8
|
-
|
9
|
-
def initialize(multi_scan_options)
|
10
|
-
@batch_count = multi_scan_options[:batch_count] || 1
|
11
|
-
@output_directory = multi_scan_options[:output_directory] || 'test_results'
|
12
|
-
@try_count = multi_scan_options[:try_count]
|
13
|
-
@retry_total_count = 0
|
14
|
-
@testrun_completed_block = multi_scan_options[:testrun_completed_block]
|
15
|
-
@given_custom_report_file_name = multi_scan_options[:custom_report_file_name]
|
16
|
-
@given_output_types = multi_scan_options[:output_types]
|
17
|
-
@given_output_files = multi_scan_options[:output_files]
|
18
|
-
@scan_options = multi_scan_options.reject do |option, _|
|
19
|
-
%i[
|
20
|
-
output_directory
|
21
|
-
only_testing
|
22
|
-
skip_testing
|
23
|
-
clean
|
24
|
-
try_count
|
25
|
-
batch_count
|
26
|
-
custom_report_file_name
|
27
|
-
fail_build
|
28
|
-
testrun_completed_block
|
29
|
-
].include?(option)
|
30
|
-
end
|
31
|
-
@test_collector = TestCollector.new(multi_scan_options)
|
32
|
-
end
|
33
|
-
|
34
|
-
def scan
|
35
|
-
tests_passed = true
|
36
|
-
@testables_count = @test_collector.testables.size
|
37
|
-
@test_collector.testables.each do |testable|
|
38
|
-
tests_passed = scan_testable(testable) && tests_passed
|
39
|
-
end
|
40
|
-
tests_passed
|
41
|
-
end
|
42
|
-
|
43
|
-
def scan_testable(testable)
|
44
|
-
tests_passed = true
|
45
|
-
reportnamer = ReportNameHelper.new(
|
46
|
-
@given_output_types,
|
47
|
-
@given_output_files,
|
48
|
-
@given_custom_report_file_name
|
49
|
-
)
|
50
|
-
output_directory = @output_directory
|
51
|
-
testable_tests = @test_collector.testables_tests[testable]
|
52
|
-
if @batch_count > 1 || @testables_count > 1
|
53
|
-
current_batch = 1
|
54
|
-
testable_tests.each_slice((testable_tests.length / @batch_count.to_f).round).to_a.each do |tests_batch|
|
55
|
-
if @testables_count > 1
|
56
|
-
output_directory = File.join(@output_directory, "results-#{testable}")
|
57
|
-
end
|
58
|
-
FastlaneCore::UI.header("Starting test run on testable '#{testable}'")
|
59
|
-
tests_passed = correcting_scan(
|
60
|
-
{
|
61
|
-
only_testing: tests_batch,
|
62
|
-
output_directory: output_directory
|
63
|
-
},
|
64
|
-
current_batch,
|
65
|
-
reportnamer
|
66
|
-
) && tests_passed
|
67
|
-
current_batch += 1
|
68
|
-
reportnamer.increment
|
69
|
-
end
|
70
|
-
else
|
71
|
-
options = {
|
72
|
-
output_directory: output_directory,
|
73
|
-
only_testing: testable_tests
|
74
|
-
}
|
75
|
-
tests_passed = correcting_scan(options, 1, reportnamer) && tests_passed
|
76
|
-
end
|
77
|
-
collate_reports(output_directory, reportnamer)
|
78
|
-
tests_passed
|
79
|
-
end
|
80
|
-
|
81
|
-
def collate_reports(output_directory, reportnamer)
|
82
|
-
report_files = Dir.glob("#{output_directory}/*#{reportnamer.junit_filextension}").map do |relative_filepath|
|
83
|
-
File.absolute_path(relative_filepath)
|
84
|
-
end
|
85
|
-
if report_files.size > 1
|
86
|
-
config = FastlaneCore::Configuration.create(
|
87
|
-
Fastlane::Actions::CollateJunitReportsAction.available_options,
|
88
|
-
{
|
89
|
-
reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
|
90
|
-
collated_report: File.absolute_path(File.join(output_directory, reportnamer.junit_reportname))
|
91
|
-
}
|
92
|
-
)
|
93
|
-
Fastlane::Actions::CollateJunitReportsAction.run(config)
|
94
|
-
end
|
95
|
-
retried_junit_reportfiles = Dir.glob("#{output_directory}/**/*-[1-9]*#{reportnamer.junit_filextension}")
|
96
|
-
FileUtils.rm_f(retried_junit_reportfiles)
|
97
|
-
end
|
98
|
-
|
99
|
-
def correcting_scan(scan_run_options, batch, reportnamer)
|
100
|
-
scan_options = @scan_options.merge(scan_run_options)
|
101
|
-
try_count = 0
|
102
|
-
tests_passed = true
|
103
|
-
begin
|
104
|
-
try_count += 1
|
105
|
-
config = FastlaneCore::Configuration.create(
|
106
|
-
Fastlane::Actions::ScanAction.available_options,
|
107
|
-
scan_options.merge(reportnamer.scan_options)
|
108
|
-
)
|
109
|
-
quit_simulators
|
110
|
-
Fastlane::Actions::ScanAction.run(config)
|
111
|
-
@testrun_completed_block && @testrun_completed_block.call(
|
112
|
-
testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
|
113
|
-
)
|
114
|
-
tests_passed = true
|
115
|
-
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
116
|
-
FastlaneCore::UI.verbose("Scan failed with #{e}")
|
117
|
-
if try_count < @try_count
|
118
|
-
@retry_total_count += 1
|
119
|
-
|
120
|
-
info = testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
|
121
|
-
@testrun_completed_block && @testrun_completed_block.call(
|
122
|
-
info
|
123
|
-
)
|
124
|
-
scan_options[:only_testing] = info[:failed].map(&:shellescape)
|
125
|
-
FastlaneCore::UI.message('Re-running scan on only failed tests')
|
126
|
-
reportnamer.increment
|
127
|
-
retry
|
128
|
-
end
|
129
|
-
tests_passed = false
|
130
|
-
end
|
131
|
-
tests_passed
|
132
|
-
end
|
133
|
-
|
134
|
-
def testrun_info(batch, try_count, reportnamer, output_directory)
|
135
|
-
report_filepath = File.join(output_directory, reportnamer.junit_last_reportname)
|
136
|
-
config = FastlaneCore::Configuration.create(
|
137
|
-
Fastlane::Actions::TestsFromJunitAction.available_options,
|
138
|
-
{
|
139
|
-
junit: File.absolute_path(report_filepath)
|
140
|
-
}
|
141
|
-
)
|
142
|
-
junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)
|
143
|
-
|
144
|
-
{
|
145
|
-
failed: junit_results[:failed],
|
146
|
-
passing: junit_results[:passing],
|
147
|
-
batch: batch,
|
148
|
-
try_count: try_count,
|
149
|
-
report_filepath: report_filepath
|
150
|
-
}
|
151
|
-
end
|
152
|
-
|
153
|
-
def quit_simulators
|
154
|
-
Fastlane::Actions.sh("killall -9 'iPhone Simulator' 'Simulator' 'SimulatorBridge' &> /dev/null || true", log: false)
|
155
|
-
launchctl_list_count = 0
|
156
|
-
while Fastlane::Actions.sh('launchctl list | grep com.apple.CoreSimulator.CoreSimulatorService || true', log: false) != ''
|
157
|
-
break if (launchctl_list_count += 1) > 10
|
158
|
-
Fastlane::Actions.sh('launchctl remove com.apple.CoreSimulator.CoreSimulatorService &> /dev/null || true', log: false)
|
159
|
-
sleep(1)
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require_relative 'xcodebuild_string'
|
2
|
-
|
3
|
-
module TestCenter
|
4
|
-
module Helper
|
5
|
-
module XcodeJunit
|
6
|
-
require 'xcodeproj'
|
7
|
-
|
8
|
-
class Report
|
9
|
-
def initialize(junit_report_filepath)
|
10
|
-
report_file = File.open(junit_report_filepath) { |f| REXML::Document.new(f) }
|
11
|
-
UI.user_error!("Malformed XML test report file given") if report_file.root.nil?
|
12
|
-
UI.user_error!("Valid XML file is not an Xcode test report") if report_file.get_elements('testsuites').empty?
|
13
|
-
|
14
|
-
@testables = []
|
15
|
-
report_file.elements.each('testsuites') do |testsuites_element|
|
16
|
-
@testables << Testable.new(testsuites_element)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def testables
|
21
|
-
return @testables
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class Testable
|
26
|
-
def initialize(xml_element)
|
27
|
-
@root = xml_element
|
28
|
-
@testsuites = []
|
29
|
-
@root.elements.each('testsuite') do |testsuite_element|
|
30
|
-
@testsuites << TestSuite.new(testsuite_element)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def name
|
35
|
-
return @root.attributes['name']
|
36
|
-
end
|
37
|
-
|
38
|
-
def testsuites
|
39
|
-
return @testsuites
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class TestSuite
|
44
|
-
def initialize(xml_element)
|
45
|
-
@root = xml_element
|
46
|
-
@testcases = []
|
47
|
-
@root.elements.each('testcase') do |testcase_element|
|
48
|
-
@testcases << TestCase.new(testcase_element)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def name
|
53
|
-
return @root.attributes['name']
|
54
|
-
end
|
55
|
-
|
56
|
-
def identifier
|
57
|
-
name.testsuite
|
58
|
-
end
|
59
|
-
|
60
|
-
def is_swift?
|
61
|
-
return name.include?('.')
|
62
|
-
end
|
63
|
-
|
64
|
-
def testcases
|
65
|
-
return @testcases
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class TestCase
|
70
|
-
attr_reader :identifier
|
71
|
-
attr_reader :skipped_test
|
72
|
-
|
73
|
-
def initialize(xml_element)
|
74
|
-
@root = xml_element
|
75
|
-
name = xml_element.attributes['name']
|
76
|
-
full_testsuite = xml_element.parent.attributes['name']
|
77
|
-
testsuite = full_testsuite.testsuite
|
78
|
-
is_swift = full_testsuite.testsuite_swift?
|
79
|
-
|
80
|
-
testable_filename = xml_element.parent.parent.attributes['name']
|
81
|
-
testable = File.basename(testable_filename, '.xctest')
|
82
|
-
@identifier = "#{testable}/#{testsuite}/#{name}"
|
83
|
-
@skipped_test = Xcodeproj::XCScheme::TestAction::TestableReference::SkippedTest.new
|
84
|
-
@skipped_test.identifier = "#{testsuite}/#{name}#{'()' if is_swift}"
|
85
|
-
@passed = xml_element.get_elements('failure').size.zero?
|
86
|
-
end
|
87
|
-
|
88
|
-
def passed?
|
89
|
-
@passed
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
module TestCenter
|
2
|
-
module Helper
|
3
|
-
require 'fastlane_core/ui/ui.rb'
|
4
|
-
|
5
|
-
class ReportNameHelper
|
6
|
-
def initialize(output_types = nil, output_files = nil, custom_report_file_name = nil)
|
7
|
-
@output_types = output_types || 'junit'
|
8
|
-
@output_files = output_files || custom_report_file_name
|
9
|
-
@report_count = 0
|
10
|
-
|
11
|
-
if @output_types && @output_files.nil?
|
12
|
-
@output_files = @output_types.split(',').map { |type| "report.#{type}" }.join(',')
|
13
|
-
end
|
14
|
-
unless @output_types.include?('junit')
|
15
|
-
FastlaneCore::UI.important('Scan output types missing \'junit\', adding it')
|
16
|
-
@output_types = @output_types.split(',').push('junit').join(',')
|
17
|
-
if @output_types.split(',').size == @output_files.split(',').size + 1
|
18
|
-
@output_files = @output_files.split(',').push('report.xml').join(',')
|
19
|
-
FastlaneCore::UI.message('As output files has one less than the new number of output types, assumming the filename for the junit was missing and added it')
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
types = @output_types.split(',').each(&:chomp)
|
24
|
-
files = @output_files.split(',').each(&:chomp)
|
25
|
-
unless files.size == types.size
|
26
|
-
raise ArgumentError, "Error: count of :output_types, #{types}, does not match the output filename(s) #{files}"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def numbered_filename(filename)
|
31
|
-
if @report_count > 0
|
32
|
-
basename = File.basename(filename, '.*')
|
33
|
-
extension = File.extname(filename)
|
34
|
-
filename = "#{basename}-#{@report_count + 1}#{extension}"
|
35
|
-
end
|
36
|
-
filename
|
37
|
-
end
|
38
|
-
|
39
|
-
def scan_options
|
40
|
-
files = @output_files.split(',').each(&:chomp)
|
41
|
-
files.map! do |filename|
|
42
|
-
filename.chomp
|
43
|
-
numbered_filename(filename)
|
44
|
-
end
|
45
|
-
{
|
46
|
-
output_types: @output_types,
|
47
|
-
output_files: files.join(',')
|
48
|
-
}
|
49
|
-
end
|
50
|
-
|
51
|
-
def junit_last_reportname
|
52
|
-
junit_index = @output_types.split(',').find_index('junit')
|
53
|
-
numbered_filename(@output_files.to_s.split(',')[junit_index])
|
54
|
-
end
|
55
|
-
|
56
|
-
def junit_reportname
|
57
|
-
junit_index = @output_types.split(',').find_index('junit')
|
58
|
-
@output_files.to_s.split(',')[junit_index]
|
59
|
-
end
|
60
|
-
|
61
|
-
def junit_filextension
|
62
|
-
File.extname(junit_reportname)
|
63
|
-
end
|
64
|
-
|
65
|
-
def increment
|
66
|
-
@report_count += 1
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
module TestCenter
|
2
|
-
module Helper
|
3
|
-
require 'fastlane_core/ui/ui.rb'
|
4
|
-
require 'plist'
|
5
|
-
|
6
|
-
class TestCollector
|
7
|
-
def initialize(options)
|
8
|
-
@xctestrun_path = options[:xctestrun] || derived_testrun_path(options[:derived_data_path], options[:scheme])
|
9
|
-
@only_testing = options[:only_testing]
|
10
|
-
@skip_testing = options[:skip_testing]
|
11
|
-
end
|
12
|
-
|
13
|
-
def derived_testrun_path(derived_data_path, scheme)
|
14
|
-
Dir.glob("#{derived_data_path}/Build/Products/#{scheme}*.xctestrun").first
|
15
|
-
end
|
16
|
-
|
17
|
-
def testables
|
18
|
-
unless @testables
|
19
|
-
if @only_testing
|
20
|
-
@testables ||= only_testing_to_testables_tests.keys
|
21
|
-
else
|
22
|
-
@testables ||= Plist.parse_xml(@xctestrun_path).keys
|
23
|
-
end
|
24
|
-
end
|
25
|
-
@testables
|
26
|
-
end
|
27
|
-
|
28
|
-
def only_testing_to_testables_tests
|
29
|
-
tests = Hash.new { |h, k| h[k] = [] }
|
30
|
-
@only_testing.sort.each do |test_identifier|
|
31
|
-
testable = test_identifier.split('/', 2)[0]
|
32
|
-
tests[testable] << test_identifier
|
33
|
-
end
|
34
|
-
tests
|
35
|
-
end
|
36
|
-
|
37
|
-
def testables_tests
|
38
|
-
unless @testables_tests
|
39
|
-
if @only_testing
|
40
|
-
@testables_tests = only_testing_to_testables_tests
|
41
|
-
else
|
42
|
-
config = FastlaneCore::Configuration.create(::Fastlane::Actions::TestsFromXctestrunAction.available_options, xctestrun: @xctestrun_path)
|
43
|
-
@testables_tests = ::Fastlane::Actions::TestsFromXctestrunAction.run(config)
|
44
|
-
if @skip_testing
|
45
|
-
skipped_testable_tests = Hash.new { |h, k| h[k] = [] }
|
46
|
-
@skip_testing.sort.each do |skipped_test_identifier|
|
47
|
-
testable = skipped_test_identifier.split('/', 2)[0]
|
48
|
-
skipped_testable_tests[testable] << skipped_test_identifier
|
49
|
-
end
|
50
|
-
@testables_tests.each_key do |testable|
|
51
|
-
@testables_tests[testable] -= skipped_testable_tests[testable]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
@testables_tests
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|