fastlane-plugin-test_center 2.5.1 → 3.0.1
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/test_center/actions/multi_scan.rb +98 -198
- data/lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb +3 -2
- data/lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb +163 -0
- data/lib/fastlane/plugin/test_center/helper/reportname_helper.rb +70 -0
- data/lib/fastlane/plugin/test_center/helper/test_collector.rb +60 -0
- data/lib/fastlane/plugin/test_center/version.rb +1 -1
- metadata +23 -6
- data/lib/fastlane/plugin/test_center/helper/test_center_helper.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e52d3f37f8075a9050200d5d5cf19b8fa0b02fb8
|
4
|
+
data.tar.gz: b2c7b4dd8c192c105dbb71a8aa390425c856aa24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4dc6d5e02ea3c35c4504a493b4c0e3197576acaf203b5f92fffb3487a28f5e3309943df72b852df10d012374066f63949a570b319b768046eec2c67113cc342c
|
7
|
+
data.tar.gz: 9b6c0b59e057fe140964369185e2c9373939441f031d5ce19570d521fd961b64ef4d8539f30ccab2a8772fcc7084c13e1436cd2bfeceec739d23f9ecd2660565
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module Fastlane
|
2
2
|
module Actions
|
3
|
+
require 'fastlane_core/ui/errors/fastlane_common_error'
|
3
4
|
require 'fastlane/actions/scan'
|
4
5
|
require 'shellwords'
|
5
6
|
require 'xctest_list'
|
@@ -7,218 +8,70 @@ module Fastlane
|
|
7
8
|
|
8
9
|
class MultiScanAction < Action
|
9
10
|
def self.run(params)
|
10
|
-
scan_options = params.values.reject { |k| %i[try_count batch_count].include?(k) }
|
11
|
-
|
12
11
|
unless Helper.test?
|
13
12
|
FastlaneCore::PrintTable.print_values(
|
14
|
-
config: params._values.
|
13
|
+
config: params._values.select { |k, _| %i[try_count batch_count fail_build].include?(k) },
|
15
14
|
title: "Summary for multi_scan (test_center v#{Fastlane::TestCenter::VERSION})"
|
16
15
|
)
|
17
16
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
unless scan_options[:test_without_building]
|
22
|
-
build_for_testing(scan_options)
|
23
|
-
scan_options.delete(:build_for_testing)
|
24
|
-
scan_options[:test_without_building] = true
|
25
|
-
end
|
26
|
-
|
27
|
-
batch_count = params[:batch_count]
|
28
|
-
if batch_count == 1
|
29
|
-
try_scan_with_retry(scan_options, params[:try_count])
|
30
|
-
else
|
31
|
-
tests = tests_to_batch(scan_options)
|
32
|
-
tests.each_slice((tests.length / batch_count.to_f).round).to_a.each do |tests_batch|
|
33
|
-
scan_options.reject! { |key| key == :skip_testing }
|
34
|
-
scan_options[:only_testing] = tests_batch
|
35
|
-
try_scan_with_retry(scan_options, params[:try_count])
|
36
|
-
end
|
37
|
-
end
|
38
|
-
collate_reports(scan_options)
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.try_scan_with_retry(scan_options, maximum_try_count)
|
42
|
-
try_count = 0
|
43
|
-
begin
|
44
|
-
try_count += 1
|
45
|
-
config = FastlaneCore::Configuration.create(Fastlane::Actions::ScanAction.available_options, scan_options)
|
46
|
-
Fastlane::Actions::ScanAction.run(config)
|
47
|
-
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
48
|
-
UI.verbose("Scan failed with #{e}")
|
49
|
-
if try_count < maximum_try_count
|
50
|
-
report_filepath = junit_report_filepath(scan_options)
|
51
|
-
failed_tests = other_action.tests_from_junit(junit: report_filepath)[:failed]
|
52
|
-
scan_options[:only_testing] = failed_tests.map(&:shellescape)
|
53
|
-
increment_report_filenames(scan_options)
|
54
|
-
retry
|
55
|
-
end
|
56
|
-
else
|
57
|
-
increment_report_filenames(scan_options)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.collate_reports(scan_options)
|
62
|
-
extension = junit_report_fileextension(scan_options)
|
63
|
-
report_files = Dir.glob("#{scan_options[:output_directory]}/*#{extension}").map do |relative_filepath|
|
64
|
-
File.absolute_path(relative_filepath)
|
65
|
-
end
|
66
|
-
if report_files.size > 1
|
67
|
-
r = Regexp.new("^(?<filename>.*)-(\\d+)\\.(junit|xml|html)")
|
68
|
-
final_report_name = junit_report_filename(scan_options)
|
69
|
-
match = r.match(final_report_name)
|
70
|
-
if match
|
71
|
-
final_report_name = "#{match[:filename]}#{extension}"
|
72
|
-
end
|
73
|
-
other_action.collate_junit_reports(
|
74
|
-
reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
|
75
|
-
collated_report: File.absolute_path(File.join(scan_options[:output_directory], final_report_name))
|
17
|
+
unless params[:test_without_building]
|
18
|
+
build_for_testing(
|
19
|
+
params._values
|
76
20
|
)
|
77
21
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
Actions.sh("killall -9 '#{program}' &> /dev/null || true", log: false)
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.quit_simulators
|
86
|
-
kill('iPhone Simulator')
|
87
|
-
kill('Simulator')
|
88
|
-
kill('SimulatorBridge')
|
89
|
-
|
90
|
-
launchctl_list_count = 0
|
91
|
-
while Actions.sh('launchctl list | grep com.apple.CoreSimulator.CoreSimulatorService || true', log: false) != ''
|
92
|
-
break if (launchctl_list_count += 1) > 10
|
93
|
-
Actions.sh('launchctl remove com.apple.CoreSimulator.CoreSimulatorService &> /dev/null || true', log: false)
|
94
|
-
sleep(1)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.tests_to_batch(scan_options)
|
99
|
-
unless scan_options[:only_testing].nil?
|
100
|
-
return scan_options[:only_testing]
|
22
|
+
smart_scanner = ::TestCenter::Helper::CorrectingScanHelper.new(params.values)
|
23
|
+
tests_passed = smart_scanner.scan
|
24
|
+
if params[:fail_build] && !tests_passed
|
25
|
+
raise UI.test_failure!('Tests have failed')
|
101
26
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
27
|
+
summary = run_summary(params, tests_passed, smart_scanner.retry_total_count)
|
28
|
+
unless Helper.test?
|
29
|
+
FastlaneCore::PrintTable.print_values(
|
30
|
+
config: summary,
|
31
|
+
title: "multi_scan results"
|
32
|
+
)
|
106
33
|
end
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
34
|
+
summary
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.run_summary(scan_options, tests_passed, retry_total_count)
|
38
|
+
reportnamer = ::TestCenter::Helper::ReportNameHelper.new(
|
39
|
+
scan_options[:output_types],
|
40
|
+
scan_options[:output_files],
|
41
|
+
scan_options[:custom_report_file_name]
|
42
|
+
)
|
43
|
+
passing_testcount = 0
|
44
|
+
failed_tests = []
|
45
|
+
report_files = Dir.glob("#{scan_options[:output_directory]}/**/*#{reportnamer.junit_filextension}").map do |relative_filepath|
|
46
|
+
File.absolute_path(relative_filepath)
|
114
47
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
testsuite.count('/') == 1 # just the testable/testsuite
|
120
|
-
end
|
121
|
-
tests_to_skip -= testsuites_to_skip
|
122
|
-
tests_to_batch.reject! do |test_identifier|
|
123
|
-
test_suite = test_identifier.split('/').first(2).join('/')
|
124
|
-
testsuites_to_skip.include?(test_suite)
|
125
|
-
end
|
126
|
-
tests_to_batch -= tests_to_skip
|
48
|
+
report_files.each do |report_file|
|
49
|
+
junit_results = other_action.tests_from_junit(junit: report_file)
|
50
|
+
failed_tests.concat(junit_results[:failed])
|
51
|
+
passing_testcount += junit_results[:passing].size
|
127
52
|
end
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
xctest_host_path = xctestrun_config['TestHostPath'].sub('__TESTROOT__', xctestrun_rootpath)
|
138
|
-
xctestrun_config['TestBundlePath'].sub!('__TESTHOST__', xctest_host_path)
|
139
|
-
xctestrun_config['TestBundlePath'].sub('__TESTROOT__', xctestrun_rootpath)
|
140
|
-
end
|
141
|
-
|
142
|
-
def self.testrun_path(scan_options)
|
143
|
-
Dir.glob("#{scan_options[:derived_data_path]}/Build/Products/*.xctestrun").first
|
144
|
-
end
|
145
|
-
|
146
|
-
def self.test_products_path(derived_data_path)
|
147
|
-
File.join(derived_data_path, "Build", "Products")
|
53
|
+
{
|
54
|
+
result: tests_passed,
|
55
|
+
total_tests: passing_testcount + failed_tests.size,
|
56
|
+
passing_testcount: passing_testcount,
|
57
|
+
failed_testcount: failed_tests.size,
|
58
|
+
failed_tests: failed_tests,
|
59
|
+
total_retry_count: retry_total_count,
|
60
|
+
report_files: report_files
|
61
|
+
}
|
148
62
|
end
|
149
63
|
|
150
64
|
def self.build_for_testing(scan_options)
|
151
|
-
|
152
|
-
|
153
|
-
|
65
|
+
config = FastlaneCore::Configuration.create(
|
66
|
+
Fastlane::Actions::ScanAction.available_options,
|
67
|
+
scan_options.merge(build_for_testing: true).reject { |k, _| %i[try_count batch_count test_without_building].include?(k) }
|
68
|
+
)
|
154
69
|
Fastlane::Actions::ScanAction.run(config)
|
155
|
-
scan_options[:derived_data_path] = Scan.config[:derived_data_path]
|
156
|
-
end
|
157
|
-
|
158
|
-
def self.config_has_junit_report(config)
|
159
|
-
output_types = config.fetch(:output_types, '').to_s.split(',')
|
160
|
-
output_filenames = config.fetch(:output_files, '').to_s.split(',')
|
161
|
-
|
162
|
-
output_type_file_count_match = output_types.size == output_filenames.size
|
163
|
-
output_types.include?('junit') && (output_type_file_count_match || config[:custom_report_file_name].to_s.strip.length > 0)
|
164
|
-
end
|
165
|
-
|
166
|
-
def self.config_with_junit_report(config)
|
167
|
-
return config if config_has_junit_report(config)
|
168
|
-
|
169
|
-
if config[:output_types].to_s.strip.empty? || config[:custom_report_file_name]
|
170
|
-
config[:custom_report_file_name] ||= 'report.xml'
|
171
|
-
config[:output_types] = 'junit'
|
172
|
-
elsif config[:output_types].strip == 'junit' && config[:output_files].to_s.strip.empty?
|
173
|
-
config[:custom_report_file_name] ||= 'report.xml'
|
174
|
-
elsif !config[:output_types].split(',').include?('junit')
|
175
|
-
config[:output_types] << ',junit'
|
176
|
-
config[:output_files] << ',report.xml'
|
177
|
-
elsif config[:output_files].nil?
|
178
|
-
config[:output_files] = config[:output_types].split(',').map { |type| "report.#{type}" }.join(',')
|
179
|
-
end
|
180
|
-
config
|
181
|
-
end
|
182
|
-
|
183
|
-
def self.junit_report_filename(config)
|
184
|
-
report_filename = config[:custom_report_file_name]
|
185
|
-
if report_filename.nil?
|
186
|
-
junit_index = config[:output_types].split(',').find_index('junit')
|
187
|
-
report_filename = config[:output_files].to_s.split(',')[junit_index]
|
188
|
-
end
|
189
|
-
report_filename
|
190
|
-
end
|
191
70
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
def self.junit_report_filepath(config)
|
197
|
-
File.absolute_path(File.join(config[:output_directory], junit_report_filename(config)))
|
198
|
-
end
|
199
|
-
|
200
|
-
def self.increment_report_filename(filename)
|
201
|
-
new_report_number = 2
|
202
|
-
basename = File.basename(filename, '.*')
|
203
|
-
r = Regexp.new("^(?<filename>.*)-(?<report_number>\\d+)\\.(junit|xml|html)")
|
204
|
-
match = r.match(filename)
|
205
|
-
if match
|
206
|
-
new_report_number = match[:report_number].to_i + 1
|
207
|
-
basename = match[:filename]
|
208
|
-
end
|
209
|
-
"#{basename}-#{new_report_number}#{File.extname(filename)}"
|
210
|
-
end
|
211
|
-
|
212
|
-
def self.increment_report_filenames(config)
|
213
|
-
if config[:output_files]
|
214
|
-
output_files = config[:output_files].to_s.split(',')
|
215
|
-
output_files = output_files.map do |output_file|
|
216
|
-
increment_report_filename(output_file)
|
217
|
-
end
|
218
|
-
config[:output_files] = output_files.join(',')
|
219
|
-
elsif config[:custom_report_file_name]
|
220
|
-
config[:custom_report_file_name] = increment_report_filename(config[:custom_report_file_name])
|
221
|
-
end
|
71
|
+
scan_options.merge!(
|
72
|
+
test_without_building: true,
|
73
|
+
derived_data_path: Scan.config[:derived_data_path]
|
74
|
+
).delete(:build_for_testing)
|
222
75
|
end
|
223
76
|
|
224
77
|
#####################################################
|
@@ -226,11 +79,14 @@ module Fastlane
|
|
226
79
|
#####################################################
|
227
80
|
|
228
81
|
def self.description
|
229
|
-
"Uses scan to run Xcode tests a given number of times
|
82
|
+
"Uses scan to run Xcode tests a given number of times, with the option " \
|
83
|
+
"of batching them, only re-testing failing tests."
|
230
84
|
end
|
231
85
|
|
232
86
|
def self.details
|
233
|
-
"Use this action to run your tests if you have fragile tests that fail
|
87
|
+
"Use this action to run your tests if you have fragile tests that fail" \
|
88
|
+
"sporadically, if you have a huge number of tests that should be " \
|
89
|
+
"batched, or have multiple test targets and need meaningful junit reports."
|
234
90
|
end
|
235
91
|
|
236
92
|
def self.scan_options
|
@@ -258,10 +114,54 @@ module Fastlane
|
|
258
114
|
verify_block: proc do |count|
|
259
115
|
UI.user_error!("Error: Batch counts must be greater than zero") unless count > 0
|
260
116
|
end
|
117
|
+
),
|
118
|
+
FastlaneCore::ConfigItem.new(
|
119
|
+
key: :testrun_completed_block,
|
120
|
+
description: 'A block invoked each time a test run completes',
|
121
|
+
optional: true,
|
122
|
+
is_string: false,
|
123
|
+
default_value: nil
|
261
124
|
)
|
262
125
|
]
|
263
126
|
end
|
264
127
|
|
128
|
+
def self.example_code
|
129
|
+
[
|
130
|
+
'multi_scan(
|
131
|
+
project: File.absolute_path("../AtomicBoy/AtomicBoy.xcodeproj"),
|
132
|
+
scheme: "Professor",
|
133
|
+
try_count: 3,
|
134
|
+
custom_report_file_name: "atomic_report.xml",
|
135
|
+
output_types: "junit"
|
136
|
+
)',
|
137
|
+
'multi_scan(
|
138
|
+
project: File.absolute_path("../GalaxyFamily/GalaxyFamily.xcodeproj"),
|
139
|
+
scheme: "Everything",
|
140
|
+
try_count: 3,
|
141
|
+
batch_count: 2, # splits the tests into two chunks to not overload the iOS Simulator
|
142
|
+
custom_report_file_name: "atomic_report.xml",
|
143
|
+
output_types: "junit"
|
144
|
+
)',
|
145
|
+
'multi_scan(
|
146
|
+
project: File.absolute_path("../GalaxyFamily/GalaxyFamily.xcodeproj"),
|
147
|
+
scheme: "Everything",
|
148
|
+
try_count: 3,
|
149
|
+
batch_count: 8, # splits the tests into 8 chunks to not overload the iOS Simulator
|
150
|
+
testrun_completed_block: lambda { |testrun_info|
|
151
|
+
passed_test_count = testrun_info[:failed].size
|
152
|
+
failed_test_count = testrun_info[:passing].size
|
153
|
+
try_attempt = testrun_info[:try_count]
|
154
|
+
batch = testrun_info[:batch]
|
155
|
+
|
156
|
+
if failed_test_count > passed_test_count / 2
|
157
|
+
UI.abort_with_message!("Too many tests are failing")
|
158
|
+
end
|
159
|
+
UI.message("😊 everything is fine, let\'s continue try #{try_attempt} for batch #{batch}")
|
160
|
+
}
|
161
|
+
)'
|
162
|
+
]
|
163
|
+
end
|
164
|
+
|
265
165
|
def self.authors
|
266
166
|
["lyndsey-ferguson/@lyndseydf"]
|
267
167
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'plist'
|
2
|
+
|
1
3
|
module Fastlane
|
2
4
|
module Actions
|
3
5
|
class TestsFromXctestrunAction < Action
|
@@ -23,8 +25,7 @@ module Fastlane
|
|
23
25
|
|
24
26
|
def self.xctest_bundle_path(xctestrun_rootpath, xctestrun_config)
|
25
27
|
xctest_host_path = xctestrun_config['TestHostPath'].sub('__TESTROOT__', xctestrun_rootpath)
|
26
|
-
xctestrun_config['TestBundlePath'].sub
|
27
|
-
xctestrun_config['TestBundlePath'].sub('__TESTROOT__', xctestrun_rootpath)
|
28
|
+
xctestrun_config['TestBundlePath'].sub('__TESTHOST__', xctest_host_path).sub('__TESTROOT__', xctestrun_rootpath)
|
28
29
|
end
|
29
30
|
|
30
31
|
#####################################################
|
@@ -0,0 +1,163 @@
|
|
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
|
+
try_count
|
24
|
+
batch_count
|
25
|
+
custom_report_file_name
|
26
|
+
fail_build
|
27
|
+
testrun_completed_block
|
28
|
+
].include?(option)
|
29
|
+
end
|
30
|
+
@test_collector = TestCollector.new(multi_scan_options)
|
31
|
+
end
|
32
|
+
|
33
|
+
def scan
|
34
|
+
tests_passed = true
|
35
|
+
@testables_count = @test_collector.testables.size
|
36
|
+
@test_collector.testables.each do |testable|
|
37
|
+
tests_passed = scan_testable(testable) && tests_passed
|
38
|
+
end
|
39
|
+
tests_passed
|
40
|
+
end
|
41
|
+
|
42
|
+
def scan_testable(testable)
|
43
|
+
tests_passed = true
|
44
|
+
reportnamer = ReportNameHelper.new(
|
45
|
+
@given_output_types,
|
46
|
+
@given_output_files,
|
47
|
+
@given_custom_report_file_name
|
48
|
+
)
|
49
|
+
output_directory = @output_directory
|
50
|
+
if @batch_count > 1 || @testables_count > 1
|
51
|
+
testable_tests = @test_collector.testables_tests[testable]
|
52
|
+
current_batch = 1
|
53
|
+
testable_tests.each_slice((testable_tests.length / @batch_count.to_f).round).to_a.each do |tests_batch|
|
54
|
+
if @testables_count > 1
|
55
|
+
output_directory = File.join(@output_directory, "results-#{testable}")
|
56
|
+
end
|
57
|
+
FastlaneCore::UI.header("Starting test run on testable '#{testable}'")
|
58
|
+
tests_passed = correcting_scan(
|
59
|
+
{
|
60
|
+
only_testing: tests_batch,
|
61
|
+
output_directory: output_directory
|
62
|
+
},
|
63
|
+
current_batch,
|
64
|
+
reportnamer
|
65
|
+
) && tests_passed
|
66
|
+
current_batch += 1
|
67
|
+
reportnamer.increment
|
68
|
+
end
|
69
|
+
else
|
70
|
+
options = {
|
71
|
+
output_directory: output_directory
|
72
|
+
}
|
73
|
+
options[:skip_testing] = @skip_testing if @skip_testing
|
74
|
+
tests_passed = correcting_scan(options, 1, reportnamer) && tests_passed
|
75
|
+
end
|
76
|
+
collate_reports(output_directory, reportnamer)
|
77
|
+
tests_passed
|
78
|
+
end
|
79
|
+
|
80
|
+
def collate_reports(output_directory, reportnamer)
|
81
|
+
report_files = Dir.glob("#{output_directory}/*#{reportnamer.junit_filextension}").map do |relative_filepath|
|
82
|
+
File.absolute_path(relative_filepath)
|
83
|
+
end
|
84
|
+
if report_files.size > 1
|
85
|
+
config = FastlaneCore::Configuration.create(
|
86
|
+
Fastlane::Actions::CollateJunitReportsAction.available_options,
|
87
|
+
{
|
88
|
+
reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
|
89
|
+
collated_report: File.absolute_path(File.join(output_directory, reportnamer.junit_reportname))
|
90
|
+
}
|
91
|
+
)
|
92
|
+
Fastlane::Actions::CollateJunitReportsAction.run(config)
|
93
|
+
end
|
94
|
+
retried_junit_reportfiles = Dir.glob("#{output_directory}/**/*-[1-9]*#{reportnamer.junit_filextension}")
|
95
|
+
FileUtils.rm_f(retried_junit_reportfiles)
|
96
|
+
end
|
97
|
+
|
98
|
+
def correcting_scan(scan_run_options, batch, reportnamer)
|
99
|
+
scan_options = @scan_options.merge(scan_run_options)
|
100
|
+
try_count = 0
|
101
|
+
tests_passed = true
|
102
|
+
begin
|
103
|
+
try_count += 1
|
104
|
+
config = FastlaneCore::Configuration.create(
|
105
|
+
Fastlane::Actions::ScanAction.available_options,
|
106
|
+
scan_options.merge(reportnamer.scan_options)
|
107
|
+
)
|
108
|
+
quit_simulators
|
109
|
+
Fastlane::Actions::ScanAction.run(config)
|
110
|
+
@testrun_completed_block && @testrun_completed_block.call(
|
111
|
+
testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
|
112
|
+
)
|
113
|
+
tests_passed = true
|
114
|
+
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
115
|
+
FastlaneCore::UI.verbose("Scan failed with #{e}")
|
116
|
+
if try_count < @try_count
|
117
|
+
@retry_total_count += 1
|
118
|
+
|
119
|
+
info = testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
|
120
|
+
@testrun_completed_block && @testrun_completed_block.call(
|
121
|
+
info
|
122
|
+
)
|
123
|
+
scan_options[:only_testing] = info[:failed].map(&:shellescape)
|
124
|
+
FastlaneCore::UI.message('Re-running scan on only failed tests')
|
125
|
+
reportnamer.increment
|
126
|
+
retry
|
127
|
+
end
|
128
|
+
tests_passed = false
|
129
|
+
end
|
130
|
+
tests_passed
|
131
|
+
end
|
132
|
+
|
133
|
+
def testrun_info(batch, try_count, reportnamer, output_directory)
|
134
|
+
report_filepath = File.join(output_directory, reportnamer.junit_last_reportname)
|
135
|
+
config = FastlaneCore::Configuration.create(
|
136
|
+
Fastlane::Actions::TestsFromJunitAction.available_options,
|
137
|
+
{
|
138
|
+
junit: File.absolute_path(report_filepath)
|
139
|
+
}
|
140
|
+
)
|
141
|
+
junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)
|
142
|
+
|
143
|
+
{
|
144
|
+
failed: junit_results[:failed],
|
145
|
+
passing: junit_results[:passing],
|
146
|
+
batch: batch,
|
147
|
+
try_count: try_count,
|
148
|
+
report_filepath: report_filepath
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def quit_simulators
|
153
|
+
Fastlane::Actions.sh("killall -9 'iPhone Simulator' 'Simulator' 'SimulatorBridge' &> /dev/null || true", log: false)
|
154
|
+
launchctl_list_count = 0
|
155
|
+
while Fastlane::Actions.sh('launchctl list | grep com.apple.CoreSimulator.CoreSimulatorService || true', log: false) != ''
|
156
|
+
break if (launchctl_list_count += 1) > 10
|
157
|
+
Fastlane::Actions.sh('launchctl remove com.apple.CoreSimulator.CoreSimulatorService &> /dev/null || true', log: false)
|
158
|
+
sleep(1)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
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,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-test_center
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lyndsey Ferguson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-02-
|
11
|
+
date: 2018-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: plist
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: xcodeproj
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -39,13 +53,13 @@ dependencies:
|
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: 1.1.2
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: bundler
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
46
60
|
- !ruby/object:Gem::Version
|
47
61
|
version: '0'
|
48
|
-
type: :
|
62
|
+
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
@@ -53,7 +67,7 @@ dependencies:
|
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: colorize
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
@@ -188,8 +202,10 @@ files:
|
|
188
202
|
- lib/fastlane/plugin/test_center/actions/suppressed_tests.rb
|
189
203
|
- lib/fastlane/plugin/test_center/actions/tests_from_junit.rb
|
190
204
|
- lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb
|
205
|
+
- lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb
|
191
206
|
- lib/fastlane/plugin/test_center/helper/junit_helper.rb
|
192
|
-
- lib/fastlane/plugin/test_center/helper/
|
207
|
+
- lib/fastlane/plugin/test_center/helper/reportname_helper.rb
|
208
|
+
- lib/fastlane/plugin/test_center/helper/test_collector.rb
|
193
209
|
- lib/fastlane/plugin/test_center/helper/xcodebuild_string.rb
|
194
210
|
- lib/fastlane/plugin/test_center/version.rb
|
195
211
|
homepage: https://github.com/lyndsey-ferguson/fastlane-plugin-test_center
|
@@ -217,3 +233,4 @@ signing_key:
|
|
217
233
|
specification_version: 4
|
218
234
|
summary: Makes testing your iOS app easier
|
219
235
|
test_files: []
|
236
|
+
has_rdoc:
|
@@ -1,12 +0,0 @@
|
|
1
|
-
module Fastlane
|
2
|
-
module Helper
|
3
|
-
class TestCenterHelper
|
4
|
-
# class methods that you define here become available in your action
|
5
|
-
# as `Helper::TestCenterHelper.your_method`
|
6
|
-
#
|
7
|
-
def self.show_message
|
8
|
-
UI.message("Hello from the test_center plugin helper!")
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|