fastlane-plugin-retry_tests 1.2.9 → 1.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 +4 -4
- data/lib/fastlane/plugin/retry_tests.rb +3 -3
- data/lib/fastlane/plugin/retry_tests/actions/collate_junit_reports.rb +24 -19
- data/lib/fastlane/plugin/retry_tests/actions/multi_scan.rb +5 -2
- data/lib/fastlane/plugin/retry_tests/actions/suppress_tests.rb +0 -0
- data/lib/fastlane/plugin/retry_tests/actions/suppress_tests_from_junit.rb +0 -0
- data/lib/fastlane/plugin/retry_tests/actions/suppressed_tests.rb +0 -0
- data/lib/fastlane/plugin/retry_tests/actions/tests_from_junit.rb +0 -0
- data/lib/fastlane/plugin/retry_tests/actions/tests_from_xctestrun.rb +65 -0
- data/lib/fastlane/plugin/retry_tests/helper/correcting_scan_helper.rb +164 -0
- data/lib/fastlane/plugin/retry_tests/helper/junit_helper.rb +94 -0
- data/lib/fastlane/plugin/retry_tests/helper/reportname_helper.rb +70 -0
- data/lib/fastlane/plugin/retry_tests/helper/test_collector.rb +60 -0
- data/lib/fastlane/plugin/retry_tests/helper/xcodebuild_string.rb +14 -0
- data/lib/fastlane/plugin/retry_tests/version.rb +5 -0
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ad6db1868ae50026fe22c693857fb1a1a78f61fb
|
|
4
|
+
data.tar.gz: 103f996af1ed6c2ee7bb026ef53731233648f1e1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6f5106da53a539dcd50e7e3136e8a4bad77226e3d7d7cb5e3884e8206cb9bbef2f2072df341383feb574cb1a447aa5722b1ca7de40de3ebc6fac462cc9415eb1
|
|
7
|
+
data.tar.gz: 7e1f5374483038c240082c79dab5ad1597001ceb3d0d51c674b285005a2b770933ced8463780b02c1a329952f6587561207d8cf2d057146a6a3569d333b9bb2b
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
require 'fastlane/plugin/retry_tests/version'
|
|
2
2
|
|
|
3
3
|
module Fastlane
|
|
4
|
-
module
|
|
4
|
+
module RetryTests
|
|
5
5
|
# Return all .rb files inside the "actions" and "helper" directory
|
|
6
6
|
def self.all_classes
|
|
7
7
|
Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
|
|
@@ -11,6 +11,6 @@ end
|
|
|
11
11
|
|
|
12
12
|
# By default we want to import all available actions and helpers
|
|
13
13
|
# A plugin can contain any number of actions and plugins
|
|
14
|
-
Fastlane::
|
|
14
|
+
Fastlane::RetryTests.all_classes.each do |current|
|
|
15
15
|
require current
|
|
16
16
|
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#Adapted from the "fastlane-plugin-test_center" plugin
|
|
1
2
|
|
|
2
3
|
require 'nokogiri'
|
|
3
4
|
require 'nokogiri-plist'
|
|
@@ -11,9 +12,12 @@ module Fastlane
|
|
|
11
12
|
if report_filepaths.size == 1
|
|
12
13
|
FileUtils.cp(report_filepaths[0], params[:collated_report])
|
|
13
14
|
else
|
|
15
|
+
#Convert .plist reports to Nokogiri XML objects
|
|
14
16
|
target_report = File.open(report_filepaths.shift) {|f| Nokogiri::XML(f)}
|
|
15
17
|
file_name = params[:collated_report] + "/#{params[:scheme]}.test_result/1_Test/action_TestSummaries.plist"
|
|
16
18
|
reports = report_filepaths.map { |report_filepath| Nokogiri::XML(Nokogiri::PList(open(report_filepath)).to_plist_xml) }
|
|
19
|
+
|
|
20
|
+
#Merge .plist reports
|
|
17
21
|
reports.each do |retry_report|
|
|
18
22
|
mergeLists(target_report, retry_report, params)
|
|
19
23
|
end
|
|
@@ -22,24 +26,15 @@ module Fastlane
|
|
|
22
26
|
copy_log(params[:logs], params[:collated_report] + "/")
|
|
23
27
|
end
|
|
24
28
|
|
|
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
29
|
def self.mergeLists(target_report, retry_report, params)
|
|
37
30
|
retry_report = clean_report(retry_report)
|
|
38
|
-
UI.verbose("Merging retried results...")
|
|
39
31
|
Dir.mkdir(params[:collated_report]) unless File.exists?(params[:collated_report])
|
|
40
32
|
file_name = params[:collated_report] + "/action_TestSummaries.plist"
|
|
41
33
|
retried_tests = retry_report.xpath("//key[contains(.,'TestSummaryGUID')]/..")
|
|
34
|
+
|
|
42
35
|
current_node = retried_tests.shift
|
|
36
|
+
|
|
37
|
+
#Copy retried test results into the base report
|
|
43
38
|
while (current_node != nil)
|
|
44
39
|
testName = get_test_name(current_node)
|
|
45
40
|
matching_node = target_report.at_xpath("//string[contains(.,'#{testName}')]/..")
|
|
@@ -52,28 +47,37 @@ module Fastlane
|
|
|
52
47
|
end
|
|
53
48
|
end
|
|
54
49
|
|
|
50
|
+
def self.clean_report(report)
|
|
51
|
+
report = report.gsub(/<(\w+): 0x(\w+)>/, '(\1: 0x\2)')
|
|
52
|
+
report = report.gsub(/<XCAccessibilityElement:\/>0x(\w+)/, '(XCAccessibilityElement): 0x\1')
|
|
53
|
+
report
|
|
54
|
+
end
|
|
55
|
+
|
|
55
56
|
def self.merge_assets(asset_files, assets_folder)
|
|
56
|
-
|
|
57
|
+
#copy screenshots from all retries into the results folder
|
|
57
58
|
Dir.mkdir(assets_folder) unless File.exists?(assets_folder)
|
|
58
59
|
asset_files.each do |folder|
|
|
59
60
|
FileUtils.cp_r(Dir[folder + '/*'], assets_folder)
|
|
60
61
|
end
|
|
61
62
|
end
|
|
62
63
|
|
|
64
|
+
def self.copy_log(log_files, logs_folder)
|
|
65
|
+
target_log = log_files.shift
|
|
66
|
+
FileUtils.cp_r(target_log, logs_folder)
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
def self.merge_logs(log_files, logs_folder)
|
|
64
70
|
UI.verbose("Merging console logs...")
|
|
71
|
+
all = ""
|
|
65
72
|
target_log = log_files.shift
|
|
66
73
|
log_files.each do |log|
|
|
67
|
-
|
|
68
|
-
File.open(target_log, "a") do |handle|
|
|
69
|
-
handle.puts to_append
|
|
70
|
-
end
|
|
71
|
-
FileUtils.cp_r(target_log, logs_folder)
|
|
74
|
+
all = all + log + " "
|
|
72
75
|
end
|
|
76
|
+
sh("cat #{all}> #{target_log}")
|
|
77
|
+
FileUtils.cp_r(target_log, logs_folder)
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
def self.write_report_to_file(report, file_name)
|
|
76
|
-
UI.verbose("Writing merged results to file...")
|
|
77
81
|
File.new(file_name, 'w')
|
|
78
82
|
File.open(file_name, 'w') do |f|
|
|
79
83
|
f.write(report.to_xml)
|
|
@@ -167,6 +171,7 @@ module Fastlane
|
|
|
167
171
|
end
|
|
168
172
|
|
|
169
173
|
def self.authors
|
|
174
|
+
#Adapted from the "fastlane-plugin-test_center" plugin by:
|
|
170
175
|
["lyndsey-ferguson/@lyndseydf"]
|
|
171
176
|
end
|
|
172
177
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
#Adapted from the "fastlane-plugin-test_center" plugin
|
|
2
|
+
|
|
1
3
|
module Fastlane
|
|
2
4
|
module Actions
|
|
3
5
|
require 'fastlane/actions/scan'
|
|
@@ -23,7 +25,8 @@ module Fastlane
|
|
|
23
25
|
config = FastlaneCore::Configuration.create(Fastlane::Actions::ScanAction.available_options, scan_options)
|
|
24
26
|
Fastlane::Actions::ScanAction.run(config)
|
|
25
27
|
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
|
26
|
-
UI.verbose("Scan failed with #{e}")
|
|
28
|
+
FastlaneCore::UI.verbose("Scan failed with error #{e}")
|
|
29
|
+
#Retry for the specified number of times if there were failed tests
|
|
27
30
|
if try_count < params[:try_count]
|
|
28
31
|
report_filepath = junit_report_filepath(scan_options)
|
|
29
32
|
failed_tests = other_action.tests_from_junit(junit: report_filepath)[:failed]
|
|
@@ -40,7 +43,6 @@ module Fastlane
|
|
|
40
43
|
asset_files = Dir.glob("#{folder}*/#{scan_options[:scheme]}.test_result/1_Test/Attachments")
|
|
41
44
|
log_files = Dir.glob("#{folder}*/#{scan_options[:scheme]}.test_result/1_Test/action.xcactivitylog")
|
|
42
45
|
#Merge all reports, screenshots, and logs if there were retried tests
|
|
43
|
-
|
|
44
46
|
if report_files.size > 1
|
|
45
47
|
other_action.collate_junit_reports(
|
|
46
48
|
scheme: scan_options[:scheme],
|
|
@@ -132,6 +134,7 @@ module Fastlane
|
|
|
132
134
|
end
|
|
133
135
|
|
|
134
136
|
def self.authors
|
|
137
|
+
#Adapted from the "fastlane-plugin-test_center" plugin by:
|
|
135
138
|
["lyndsey-ferguson/@lyndseydf"]
|
|
136
139
|
end
|
|
137
140
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
|
@@ -0,0 +1,164 @@
|
|
|
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
|
|
@@ -0,0 +1,94 @@
|
|
|
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
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
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.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gloria Chow
|
|
@@ -187,6 +187,13 @@ files:
|
|
|
187
187
|
- lib/fastlane/plugin/retry_tests/actions/suppress_tests_from_junit.rb
|
|
188
188
|
- lib/fastlane/plugin/retry_tests/actions/suppressed_tests.rb
|
|
189
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
|
|
190
197
|
homepage: https://github.com/kouzoh/fastlane-plugin-retry_tests
|
|
191
198
|
licenses:
|
|
192
199
|
- MIT
|
|
@@ -210,5 +217,5 @@ rubyforge_project:
|
|
|
210
217
|
rubygems_version: 2.6.14
|
|
211
218
|
signing_key:
|
|
212
219
|
specification_version: 4
|
|
213
|
-
summary: Makes testing your iOS app easier
|
|
220
|
+
summary: Makes testing your iOS app easier
|
|
214
221
|
test_files: []
|