fastlane-plugin-test_center 3.6.3.parallelizing.pre.alpha.pre.1 → 3.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/fastlane/plugin/test_center.rb +1 -1
- data/lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb +1 -1
- data/lib/fastlane/plugin/test_center/actions/multi_scan.rb +4 -48
- data/lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb +293 -0
- data/lib/fastlane/plugin/test_center/helper/reportname_helper.rb +6 -15
- data/lib/fastlane/plugin/test_center/helper/test_collector.rb +3 -47
- data/lib/fastlane/plugin/test_center/version.rb +1 -1
- metadata +10 -20
- data/lib/fastlane/plugin/test_center/actions/restart_core_simulator_service.rb +0 -37
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb +0 -5
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb +0 -26
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/interstitial.rb +0 -143
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/report_collator.rb +0 -113
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb +0 -93
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb +0 -236
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +0 -313
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb +0 -59
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_manager.rb +0 -317
- data/lib/fastlane/plugin/test_center/helper/xctestrun_info.rb +0 -42
@@ -1,93 +0,0 @@
|
|
1
|
-
module TestCenter
|
2
|
-
module Helper
|
3
|
-
module MultiScanManager
|
4
|
-
class RetryingScan
|
5
|
-
def initialize(options = {})
|
6
|
-
@options = options
|
7
|
-
@retrying_scan_helper = RetryingScanHelper.new(options)
|
8
|
-
end
|
9
|
-
|
10
|
-
def delete_xcresults
|
11
|
-
derived_data_path = File.expand_path(scan_config[:derived_data_path])
|
12
|
-
xcresults = Dir.glob("#{derived_data_path}/Logs/Test/*.xcresult")
|
13
|
-
FastlaneCore::UI.message("Deleting xcresults: #{xcresults}")
|
14
|
-
FileUtils.rm_rf(xcresults)
|
15
|
-
end
|
16
|
-
|
17
|
-
def scan_config
|
18
|
-
if Scan.config.nil?
|
19
|
-
Scan.config = FastlaneCore::Configuration.create(
|
20
|
-
Fastlane::Actions::ScanAction.available_options,
|
21
|
-
@options.select { |k,v| %i[project workspace scheme].include?(k) }
|
22
|
-
)
|
23
|
-
end
|
24
|
-
Scan.config
|
25
|
-
end
|
26
|
-
|
27
|
-
def update_scan_options
|
28
|
-
valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
|
29
|
-
scan_options = @options.select { |k,v| valid_scan_keys.include?(k) }
|
30
|
-
.merge(@retrying_scan_helper.scan_options)
|
31
|
-
|
32
|
-
sc = scan_config
|
33
|
-
scan_options.each do |k,v|
|
34
|
-
sc.set(k,v) unless v.nil?
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def run
|
39
|
-
update_scan_options
|
40
|
-
delete_xcresults
|
41
|
-
|
42
|
-
try_count = @options[:try_count] || 1
|
43
|
-
begin
|
44
|
-
@retrying_scan_helper.before_testrun
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
# TODO: Investigate the following error:
|
49
|
-
"""
|
50
|
-
2019-05-09 13:32:40.707 xcodebuild[78535:1944070] [MT] DVTAssertions: Warning in /Library/Caches/com.apple.xbs/Sources/IDEFrameworks_Fall2018/IDEFrameworks-14460.46/IDEFoundation/ProjectModel/ActionRecords/IDESchemeActionTestAttachment.m:186
|
51
|
-
Details: Error writing attachment data to file /Users/lyndsey.ferguson/Library/Developer/Xcode/DerivedData/AtomicBoy-flqqvvvzbouqymbyffgdbtjoiufr/Logs/Test/Test-Transient Testing-2019.05.09_13-32-08--0400.xcresult/1_Test/Attachments/Screenshot_E0AE7940-E7F4-4CA8-BB2B-8822D730D10F.jpg: Error Domain=NSCocoaErrorDomain Code=4 \"The folder “Screenshot_E0AE7940-E7F4-4CA8-BB2B-8822D730D10F.jpg” doesn’t exist.\" UserInfo={NSFilePath=/Users/lyndsey.ferguson/Library/Developer/Xcode/DerivedData/AtomicBoy-flqqvvvzbouqymbyffgdbtjoiufr/Logs/Test/Test-Transient Testing-2019.05.09_13-32-08--0400.xcresult/1_Test/Attachments/Screenshot_E0AE7940-E7F4-4CA8-BB2B-8822D730D10F.jpg, NSUserStringVariant=Folder, NSUnderlyingError=0x7fa6ef34ef90 {Error Domain=NSPOSIXErrorDomain Code=2 \"No such file or directory\"}}
|
52
|
-
Object: <IDESchemeActionTestAttachment: 0x7fa6ef22d270>
|
53
|
-
Method: -_savePayload:
|
54
|
-
Thread: <NSThread: 0x7fa6ea516110>{number = 1, name = main}
|
55
|
-
Please file a bug at https://bugreport.apple.com with this warning message and any useful information you can provide.
|
56
|
-
|
57
|
-
It may be due to:
|
58
|
-
2019-05-09 14:17:30.933 xcodebuild[86893:2045058] IDETestOperationsObserverDebug: Failed to move logarchive from /Users/lyndsey.ferguson/Library/Developer/CoreSimulator/Devices/0D312041-2D60-4221-94CC-3B0040154D74/data/tmp/test-session-systemlogs-2019.05.09_14-17-04--0400.logarchive to diagnostics location /Users/lyndsey.ferguson/Library/Developer/Xcode/DerivedData/AtomicBoy-flqqvvvzbouqymbyffgdbtjoiufr/Logs/Test/Test-Transient Testing-2019.05.09_14-17-04--0400.xcresult/1_Test/Diagnostics/iPhone 5s_0D312041-2D60-4221-94CC-3B0040154D74/test-session-systemlogs-2019.05.09_14-17-04--0400.logarchive: Error Domain=NSCocoaErrorDomain Code=4 \"“test-session-systemlogs-2019.05.09_14-17-04--0400.logarchive” couldn’t be moved to “iPhone 5s_0D312041-2D60-4221-94CC-3B0040154D74” because either the former doesn’t exist, or the folder containing the latter doesn’t exist.\" UserInfo={NSSourceFilePathErrorKey=/Users/lyndsey.ferguson/Library/Developer/CoreSimulator/Devices/0D312041-2D60-4221-94CC-3B0040154D74/data/tmp/test-session-systemlogs-2019.05.09_14-17-04--0400.logarchive, NSUserStringVariant=(
|
59
|
-
Move
|
60
|
-
), NSDestinationFilePath=/Users/lyndsey.ferguson/Library/Developer/Xcode/DerivedData/AtomicBoy-flqqvvvzbouqymbyffgdbtjoiufr/Logs/Test/Test-Transient Testing-2019.05.09_14-17-04--0400.xcresult/1_Test/Diagnostics/iPhone 5s_0D312041-2D60-4221-94CC-3B0040154D74/test-session-systemlogs-2019.05.09_14-17-04--0400.logarchive, NSFilePath=/Users/lyndsey.ferguson/Library/Developer/CoreSimulator/Devices/0D312041-2D60-4221-94CC-3B0040154D74/data/tmp/test-session-systemlogs-2019.05.09_14-17-04--0400.logarchive, NSUnderlyingError=0x7fdf96c6de90 {Error Domain=NSPOSIXErrorDomain Code=2 \"No such file or directory\"}}
|
61
|
-
|
62
|
-
"""
|
63
|
-
update_scan_options
|
64
|
-
Scan::Runner.new.run
|
65
|
-
@retrying_scan_helper.after_testrun
|
66
|
-
true
|
67
|
-
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
68
|
-
@retrying_scan_helper.after_testrun(e)
|
69
|
-
retry if @retrying_scan_helper.testrun_count < try_count
|
70
|
-
false
|
71
|
-
rescue FastlaneCore::Interface::FastlaneBuildFailure => e
|
72
|
-
@retrying_scan_helper.after_testrun(e)
|
73
|
-
retry if @retrying_scan_helper.testrun_count < try_count
|
74
|
-
false
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
# Ug. How do I name this class?
|
82
|
-
# I want a class that retries a scan
|
83
|
-
# I want a class that controls or manages the class that retries the scan and the set up for that
|
84
|
-
# etc.
|
85
|
-
# So, the class or the realm could be:
|
86
|
-
# MultiScanManager
|
87
|
-
# MultiScanController
|
88
|
-
# ScanManager
|
89
|
-
# ScanMaster
|
90
|
-
# MultiScanMaster
|
91
|
-
# MasterScanManser
|
92
|
-
# MasterMultiScan
|
93
|
-
# I like MultiScanManager
|
@@ -1,236 +0,0 @@
|
|
1
|
-
module TestCenter
|
2
|
-
module Helper
|
3
|
-
module MultiScanManager
|
4
|
-
require_relative 'device_manager'
|
5
|
-
|
6
|
-
class RetryingScanHelper
|
7
|
-
|
8
|
-
attr_reader :testrun_count
|
9
|
-
|
10
|
-
def initialize(options)
|
11
|
-
raise ArgumentError, 'Do not use the :device or :devices option. Instead use the :destination option.' if (options.key?(:device) or options.key?(:devices))
|
12
|
-
|
13
|
-
@options = options
|
14
|
-
@testrun_count = 0
|
15
|
-
@xcpretty_json_file_output = ENV['XCPRETTY_JSON_FILE_OUTPUT']
|
16
|
-
|
17
|
-
@reportnamer = ReportNameHelper.new(
|
18
|
-
options[:output_types],
|
19
|
-
options[:output_files],
|
20
|
-
options[:custom_report_file_name]
|
21
|
-
)
|
22
|
-
end
|
23
|
-
|
24
|
-
def before_testrun
|
25
|
-
remove_preexisting_test_result_bundles
|
26
|
-
set_json_env
|
27
|
-
print_starting_scan_message
|
28
|
-
end
|
29
|
-
|
30
|
-
def print_starting_scan_message
|
31
|
-
scan_message = "Starting scan ##{@testrun_count + 1} with #{@options.fetch(:only_testing, []).size} tests"
|
32
|
-
scan_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
|
33
|
-
FastlaneCore::UI.message("#{scan_message}.")
|
34
|
-
end
|
35
|
-
|
36
|
-
def set_json_env
|
37
|
-
return unless @reportnamer.includes_json?
|
38
|
-
|
39
|
-
ENV['XCPRETTY_JSON_FILE_OUTPUT'] = File.join(
|
40
|
-
@options[:output_directory],
|
41
|
-
@reportnamer.json_last_reportname
|
42
|
-
)
|
43
|
-
end
|
44
|
-
|
45
|
-
def reset_json_env
|
46
|
-
return unless @reportnamer.includes_json?
|
47
|
-
|
48
|
-
ENV['XCPRETTY_JSON_FILE_OUTPUT'] = @xcpretty_json_file_output
|
49
|
-
end
|
50
|
-
|
51
|
-
def remove_preexisting_test_result_bundles
|
52
|
-
return unless @options[:result_bundle]
|
53
|
-
|
54
|
-
absolute_output_directory = File.absolute_path(@options[:output_directory])
|
55
|
-
glob_pattern = "#{absolute_output_directory}/*.test_result"
|
56
|
-
preexisting_test_result_bundles = Dir.glob(glob_pattern)
|
57
|
-
FileUtils.rm_rf(preexisting_test_result_bundles)
|
58
|
-
end
|
59
|
-
|
60
|
-
def scan_options
|
61
|
-
valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
|
62
|
-
@options.select { |k,v| valid_scan_keys.include?(k) }
|
63
|
-
.merge(@reportnamer.scan_options)
|
64
|
-
end
|
65
|
-
|
66
|
-
# after_testrun methods
|
67
|
-
|
68
|
-
def after_testrun(exception = nil)
|
69
|
-
@testrun_count = @testrun_count + 1
|
70
|
-
if exception.kind_of?(FastlaneCore::Interface::FastlaneTestFailure)
|
71
|
-
handle_test_failure
|
72
|
-
elsif exception.kind_of?(FastlaneCore::Interface::FastlaneBuildFailure)
|
73
|
-
handle_build_failure(exception)
|
74
|
-
else
|
75
|
-
handle_success
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def handle_success
|
80
|
-
send_callback_testrun_info
|
81
|
-
move_test_result_bundle_for_next_run
|
82
|
-
reset_json_env
|
83
|
-
collate_reports
|
84
|
-
end
|
85
|
-
|
86
|
-
def collate_reports
|
87
|
-
absolute_output_directory = File.absolute_path(@options[:output_directory])
|
88
|
-
|
89
|
-
TestCenter::Helper::MultiScanManager::ReportCollator.new(
|
90
|
-
source_reports_directory_glob: absolute_output_directory,
|
91
|
-
output_directory: absolute_output_directory,
|
92
|
-
reportnamer: @reportnamer,
|
93
|
-
scheme: @options[:scheme],
|
94
|
-
result_bundle: @options[:result_bundle]
|
95
|
-
).collate
|
96
|
-
end
|
97
|
-
|
98
|
-
def handle_test_failure
|
99
|
-
send_callback_testrun_info
|
100
|
-
reset_simulators
|
101
|
-
move_test_result_bundle_for_next_run
|
102
|
-
update_scan_options
|
103
|
-
@reportnamer.increment
|
104
|
-
end
|
105
|
-
|
106
|
-
def send_callback_testrun_info(additional_info = {})
|
107
|
-
return unless @options[:testrun_completed_block]
|
108
|
-
|
109
|
-
report_filepath = nil
|
110
|
-
junit_results = {}
|
111
|
-
unless additional_info.key?(:test_operation_failure)
|
112
|
-
report_filepath = File.absolute_path(File.join(@options[:output_directory], @reportnamer.junit_last_reportname))
|
113
|
-
|
114
|
-
config = FastlaneCore::Configuration.create(
|
115
|
-
Fastlane::Actions::TestsFromJunitAction.available_options,
|
116
|
-
{
|
117
|
-
junit: File.absolute_path(report_filepath)
|
118
|
-
}
|
119
|
-
)
|
120
|
-
junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)
|
121
|
-
end
|
122
|
-
|
123
|
-
info = {
|
124
|
-
failed: junit_results[:failed],
|
125
|
-
passing: junit_results[:passing],
|
126
|
-
batch: 1,
|
127
|
-
try_count: @testrun_count,
|
128
|
-
report_filepath: report_filepath
|
129
|
-
}.merge(additional_info)
|
130
|
-
|
131
|
-
if @reportnamer.includes_html?
|
132
|
-
html_report_filepath = File.join(@options[:output_directory], @reportnamer.html_last_reportname)
|
133
|
-
info[:html_report_filepath] = html_report_filepath
|
134
|
-
end
|
135
|
-
if @reportnamer.includes_json?
|
136
|
-
json_report_filepath = File.join(@options[:output_directory], @reportnamer.json_last_reportname)
|
137
|
-
info[:json_report_filepath] = json_report_filepath
|
138
|
-
end
|
139
|
-
if @options[:result_bundle]
|
140
|
-
test_result_suffix = '.test_result'
|
141
|
-
test_result_suffix.prepend("-#{@reportnamer.report_count}") unless @reportnamer.report_count.zero?
|
142
|
-
test_result_bundlepath = File.join(@options[:output_directory], @options[:scheme]) + test_result_suffix
|
143
|
-
info[:test_result_bundlepath] = test_result_bundlepath
|
144
|
-
end
|
145
|
-
@options[:testrun_completed_block].call(info)
|
146
|
-
end
|
147
|
-
|
148
|
-
def update_scan_options
|
149
|
-
update_only_testing
|
150
|
-
turn_off_code_coverage
|
151
|
-
end
|
152
|
-
|
153
|
-
def turn_off_code_coverage
|
154
|
-
# Turn off code coverage as code coverage reports are not merged and
|
155
|
-
# the first, more valuable, report will be overwritten
|
156
|
-
@options.delete(:code_coverage)
|
157
|
-
end
|
158
|
-
|
159
|
-
def update_only_testing
|
160
|
-
report_filepath = File.join(@options[:output_directory], @reportnamer.junit_last_reportname)
|
161
|
-
config = FastlaneCore::Configuration.create(
|
162
|
-
Fastlane::Actions::TestsFromJunitAction.available_options,
|
163
|
-
{
|
164
|
-
junit: File.absolute_path(report_filepath)
|
165
|
-
}
|
166
|
-
)
|
167
|
-
@options[:only_testing] = Fastlane::Actions::TestsFromJunitAction.run(config)[:failed]
|
168
|
-
end
|
169
|
-
|
170
|
-
def reset_simulators
|
171
|
-
return unless @options[:reset_simulators]
|
172
|
-
|
173
|
-
@options[:simulators].each(&:reset)
|
174
|
-
end
|
175
|
-
|
176
|
-
def handle_build_failure(exception)
|
177
|
-
test_operation_failure = ''
|
178
|
-
|
179
|
-
test_session_last_messages = last_lines_of_test_session_log
|
180
|
-
test_operation_failure_match = /Test operation failure: (?<test_operation_failure>.*)$/ =~ test_session_last_messages
|
181
|
-
if test_operation_failure_match.nil?
|
182
|
-
test_operation_failure = 'Unknown test operation failure'
|
183
|
-
end
|
184
|
-
|
185
|
-
case test_operation_failure
|
186
|
-
when /Test runner exited before starting test execution/
|
187
|
-
FastlaneCore::UI.error(test_operation_failure)
|
188
|
-
when /Lost connection to testmanagerd/
|
189
|
-
FastlaneCore::UI.error(test_operation_failure)
|
190
|
-
FastlaneCore::UI.important("com.apple.CoreSimulator.CoreSimulatorService may have become corrupt, consider quitting it")
|
191
|
-
if @options[:quit_core_simulator_service]
|
192
|
-
Fastlane::Actions::RestartCoreSimulatorServiceAction.run
|
193
|
-
end
|
194
|
-
else
|
195
|
-
FastlaneCore::UI.error(test_operation_failure)
|
196
|
-
send_callback_testrun_info(test_operation_failure: test_operation_failure)
|
197
|
-
raise exception
|
198
|
-
end
|
199
|
-
if @options[:reset_simulators]
|
200
|
-
@options[:simulators].each do |simulator|
|
201
|
-
simulator.reset
|
202
|
-
end
|
203
|
-
end
|
204
|
-
send_callback_testrun_info(test_operation_failure: test_operation_failure)
|
205
|
-
end
|
206
|
-
|
207
|
-
def last_lines_of_test_session_log
|
208
|
-
derived_data_path = File.expand_path(@options[:derived_data_path])
|
209
|
-
test_session_logs = Dir.glob("#{derived_data_path}/Logs/Test/*.xcresult/*_Test/Diagnostics/**/Session-*.log")
|
210
|
-
test_session_logs.sort! { |logfile1, logfile2| File.mtime(logfile1) <=> File.mtime(logfile2) }
|
211
|
-
test_session = File.open(test_session_logs.last)
|
212
|
-
backwards_seek_offset = -1 * [1000, test_session.stat.size].min
|
213
|
-
test_session.seek(backwards_seek_offset, IO::SEEK_END)
|
214
|
-
test_session_last_messages = test_session.read
|
215
|
-
end
|
216
|
-
|
217
|
-
def move_test_result_bundle_for_next_run
|
218
|
-
return unless @options[:result_bundle]
|
219
|
-
|
220
|
-
absolute_output_directory = File.absolute_path(@options[:output_directory])
|
221
|
-
glob_pattern = "#{absolute_output_directory}/*.test_result"
|
222
|
-
preexisting_test_result_bundles = Dir.glob(glob_pattern)
|
223
|
-
unnumbered_test_result_bundles = preexisting_test_result_bundles.reject do |test_result|
|
224
|
-
test_result =~ /.*-\d+\.test_result/
|
225
|
-
end
|
226
|
-
src_test_bundle = unnumbered_test_result_bundles.first
|
227
|
-
dst_test_bundle_parent_dir = File.dirname(src_test_bundle)
|
228
|
-
dst_test_bundle_basename = File.basename(src_test_bundle, '.test_result')
|
229
|
-
dst_test_bundle = "#{dst_test_bundle_parent_dir}/#{dst_test_bundle_basename}-#{@testrun_count}.test_result"
|
230
|
-
FileUtils.mkdir_p(dst_test_bundle)
|
231
|
-
FileUtils.mv(src_test_bundle, dst_test_bundle)
|
232
|
-
end
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
end
|
@@ -1,313 +0,0 @@
|
|
1
|
-
|
2
|
-
module TestCenter
|
3
|
-
module Helper
|
4
|
-
module MultiScanManager
|
5
|
-
require 'fastlane_core/ui/ui.rb'
|
6
|
-
require 'plist'
|
7
|
-
require 'json'
|
8
|
-
require 'shellwords'
|
9
|
-
require 'snapshot/reset_simulators'
|
10
|
-
require_relative './simulator_manager'
|
11
|
-
|
12
|
-
class Runner
|
13
|
-
Parallelization = TestCenter::Helper::MultiScanManager::Parallelization
|
14
|
-
|
15
|
-
attr_reader :retry_total_count
|
16
|
-
|
17
|
-
def initialize(multi_scan_options)
|
18
|
-
@output_directory = multi_scan_options[:output_directory] || 'test_results'
|
19
|
-
@try_count = multi_scan_options[:try_count]
|
20
|
-
@retry_total_count = 0
|
21
|
-
@testrun_completed_block = multi_scan_options[:testrun_completed_block]
|
22
|
-
@given_custom_report_file_name = multi_scan_options[:custom_report_file_name]
|
23
|
-
@given_output_types = multi_scan_options[:output_types]
|
24
|
-
@given_output_files = multi_scan_options[:output_files]
|
25
|
-
@parallelize = multi_scan_options[:parallelize]
|
26
|
-
@test_collector = TestCenter::Helper::TestCollector.new(multi_scan_options)
|
27
|
-
@scan_options = multi_scan_options.reject do |option, _|
|
28
|
-
%i[
|
29
|
-
output_directory
|
30
|
-
only_testing
|
31
|
-
skip_testing
|
32
|
-
clean
|
33
|
-
try_count
|
34
|
-
batch_count
|
35
|
-
custom_report_file_name
|
36
|
-
fail_build
|
37
|
-
testrun_completed_block
|
38
|
-
output_types
|
39
|
-
output_files
|
40
|
-
parallelize
|
41
|
-
quit_simulators
|
42
|
-
].include?(option)
|
43
|
-
end
|
44
|
-
@scan_options[:clean] = false
|
45
|
-
@scan_options[:disable_concurrent_testing] = true
|
46
|
-
@scan_options[:xctestrun] = @test_collector.xctestrun_path
|
47
|
-
@batch_count = @test_collector.test_batches.size
|
48
|
-
if @parallelize
|
49
|
-
@scan_options.delete(:derived_data_path)
|
50
|
-
@parallelizer = Parallelization.new(@batch_count, @output_directory, @testrun_completed_block)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def scan
|
55
|
-
all_tests_passed = true
|
56
|
-
@testables_count = @test_collector.testables.size
|
57
|
-
all_tests_passed = each_batch do |test_batch, current_batch_index|
|
58
|
-
if ENV['USE_REFACTORED_PARALLELIZED_MULTI_SCAN']
|
59
|
-
# destination = @parallelizer&.destination_for_batch(current_batch_index) || Scan&.config&.fetch(:destination)
|
60
|
-
# if destination.nil?
|
61
|
-
# app_infoplist ||= XCTestrunInfo.new(@scan_options[:xctestrun])
|
62
|
-
|
63
|
-
# batch_deploymentversions = @test_collector.test_batches.map do |test_batch|
|
64
|
-
# testable = test_batch.first.split('/').first.gsub('\\', '')
|
65
|
-
# # TODO: investigate the reason for this call that doesn't seem to do
|
66
|
-
# # anything other than query for and then discard MinimumOSVersion
|
67
|
-
# app_infoplist.app_plist_for_testable(testable)['MinimumOSVersion']
|
68
|
-
# end
|
69
|
-
|
70
|
-
# device_description = @scan_options[:device]
|
71
|
-
# device_name = 'iPhone 5s'
|
72
|
-
# device_version = batch_deploymentversions[current_batch_index]
|
73
|
-
# unless device_description.nil?
|
74
|
-
# /(?<device_name>.*)\s+\((?<device_version>.*)\)/ =~ device_description
|
75
|
-
# end
|
76
|
-
# # TODO: check for an existing simulator that can serve
|
77
|
-
# all_runtimes = ::Snapshot::ResetSimulators.runtimes
|
78
|
-
|
79
|
-
# all_ios_runtimes = all_runtimes.select { |v, id| v[/^iOS/] }
|
80
|
-
# ios_runtimes_greater_than_equal = all_ios_runtimes.select do |versionstr, _|
|
81
|
-
# _, version = versionstr.split(' ')
|
82
|
-
# Gem::Version.new(device_version) <= Gem::Version.new(version)
|
83
|
-
# end
|
84
|
-
# ios_runtimes_greater_than_equal.sort! do |a, b|
|
85
|
-
# version_a = a.first.split(' ').last
|
86
|
-
# version_b = b.first.split(' ').last
|
87
|
-
# Gem::Version.new(version_a) <=> Gem::Version.new(version_b)
|
88
|
-
# end
|
89
|
-
|
90
|
-
# runtime = ios_runtimes_greater_than_equal.first.last
|
91
|
-
# device_type = `xcrun simctl list devicetypes`.scan(/(.*)\s\((.*)\)/)
|
92
|
-
# .find { |name, device_id| name == device_name }
|
93
|
-
|
94
|
-
# command = "xcrun simctl create '#{device_name}' #{device_type.last} #{runtime}"
|
95
|
-
# UI.command(command) if ::FastlaneCore::Globals.verbose?
|
96
|
-
# udid = `#{command}`.chomp
|
97
|
-
# destination = "platform=iOS Simulator,id=#{udid}"
|
98
|
-
# end
|
99
|
-
retrying_scan = TestCenter::Helper::MultiScanManager::RetryingScan.new(
|
100
|
-
@scan_options.merge(
|
101
|
-
# destination: destination,
|
102
|
-
only_testing: test_batch.map(&:shellsafe_testidentifier),
|
103
|
-
output_directory: @output_directory
|
104
|
-
).reject { |key| %i[device devices].include?(key) }
|
105
|
-
)
|
106
|
-
retrying_scan.run
|
107
|
-
else
|
108
|
-
output_directory = testrun_output_directory(@output_directory, test_batch, current_batch_index)
|
109
|
-
reset_for_new_testable(output_directory)
|
110
|
-
FastlaneCore::UI.header("Starting test run on batch '#{current_batch_index}'")
|
111
|
-
@interstitial.batch = current_batch_index
|
112
|
-
@interstitial.output_directory = output_directory
|
113
|
-
@interstitial.before_all
|
114
|
-
testrun_passed = correcting_scan(
|
115
|
-
{
|
116
|
-
only_testing: test_batch.map(&:shellsafe_testidentifier),
|
117
|
-
output_directory: output_directory
|
118
|
-
},
|
119
|
-
current_batch_index,
|
120
|
-
@reportnamer
|
121
|
-
)
|
122
|
-
all_tests_passed = testrun_passed && all_tests_passed
|
123
|
-
TestCenter::Helper::MultiScanManager::ReportCollator.new(
|
124
|
-
source_reports_directory_glob: output_directory,
|
125
|
-
output_directory: output_directory,
|
126
|
-
reportnamer: @reportnamer,
|
127
|
-
scheme: @scan_options[:scheme],
|
128
|
-
result_bundle: @scan_options[:result_bundle]
|
129
|
-
).collate
|
130
|
-
end
|
131
|
-
testrun_passed && all_tests_passed
|
132
|
-
end
|
133
|
-
all_tests_passed
|
134
|
-
end
|
135
|
-
|
136
|
-
def each_batch
|
137
|
-
tests_passed = true
|
138
|
-
if @parallelize
|
139
|
-
xctestrun_filename = File.basename(@test_collector.xctestrun_path)
|
140
|
-
xcproduct_dirpath = File.dirname(@test_collector.xctestrun_path)
|
141
|
-
tmp_xcproduct_dirpath = Dir.mktmpdir
|
142
|
-
|
143
|
-
FileUtils.copy_entry(xcproduct_dirpath, tmp_xcproduct_dirpath)
|
144
|
-
|
145
|
-
tmp_xctestrun_path = File.join(tmp_xcproduct_dirpath, xctestrun_filename)
|
146
|
-
app_infoplist = XCTestrunInfo.new(tmp_xctestrun_path)
|
147
|
-
@scan_options[:xctestrun] = tmp_xctestrun_path
|
148
|
-
batch_deploymentversions = @test_collector.test_batches.map do |test_batch|
|
149
|
-
testable = test_batch.first.split('/').first.gsub('\\', '')
|
150
|
-
# TODO: investigate the reason for this call that doesn't seem to do
|
151
|
-
# anything other than query for and then discard MinimumOSVersion
|
152
|
-
app_infoplist.app_plist_for_testable(testable)['MinimumOSVersion']
|
153
|
-
end
|
154
|
-
@parallelizer.setup_simulators(@scan_options[:devices] || Array(@scan_options[:device]), batch_deploymentversions)
|
155
|
-
@parallelizer.setup_pipes_for_fork
|
156
|
-
@test_collector.test_batches.each_with_index do |test_batch, current_batch_index|
|
157
|
-
fork do
|
158
|
-
@parallelizer.connect_subprocess_endpoint(current_batch_index)
|
159
|
-
begin
|
160
|
-
@parallelizer.setup_scan_options_for_testrun(@scan_options, current_batch_index)
|
161
|
-
# add output_directory to map of test-target: [ output_directories ]
|
162
|
-
tests_passed = yield(test_batch, current_batch_index)
|
163
|
-
ensure
|
164
|
-
@parallelizer.send_subprocess_result(current_batch_index, tests_passed)
|
165
|
-
end
|
166
|
-
# processes to disconnect from the Simulator subsystems
|
167
|
-
FastlaneCore::UI.message("batched scan #{current_batch_index} finishing")
|
168
|
-
end
|
169
|
-
end
|
170
|
-
# @parallelizer.wait_for_subprocesses
|
171
|
-
# tests_passed = @parallelizer.handle_subprocesses_results && tests_passed
|
172
|
-
@parallelizer.handle_subprocesses
|
173
|
-
@parallelizer.cleanup_simulators
|
174
|
-
@test_collector.testables.each do |testable|
|
175
|
-
# ReportCollator with a testable-batch glob pattern
|
176
|
-
source_reports_directory_glob = batched_testable_output_directory(@output_directory, '*', testable)
|
177
|
-
@reportnamer = ReportNameHelper.new(
|
178
|
-
@given_output_types,
|
179
|
-
@given_output_files,
|
180
|
-
@given_custom_report_file_name
|
181
|
-
)
|
182
|
-
TestCenter::Helper::MultiScanManager::ReportCollator.new(
|
183
|
-
source_reports_directory_glob: source_reports_directory_glob,
|
184
|
-
output_directory: @output_directory,
|
185
|
-
reportnamer: @reportnamer,
|
186
|
-
scheme: @scan_options[:scheme],
|
187
|
-
result_bundle: @scan_options[:result_bundle],
|
188
|
-
suffix: testable
|
189
|
-
).collate
|
190
|
-
FileUtils.rm_rf(Dir.glob(source_reports_directory_glob))
|
191
|
-
end
|
192
|
-
# for each key in test-target : [ output_directories ], call
|
193
|
-
# collate_junit_reports for each key, the [ output_directories ] being
|
194
|
-
# used to find the report files.
|
195
|
-
# the resultant report file is to be placed in the originally requested
|
196
|
-
# output_directory, with the name changed to include a suffix matching
|
197
|
-
# the test target's name
|
198
|
-
else
|
199
|
-
@test_collector.test_batches.each_with_index do |test_batch, current_batch_index|
|
200
|
-
tests_passed = yield(test_batch, current_batch_index)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
tests_passed
|
204
|
-
end
|
205
|
-
|
206
|
-
def batched_testable_output_directory(output_directory, batch_index, testable_name)
|
207
|
-
File.join(output_directory, "results-#{testable_name}-batch-#{batch_index}")
|
208
|
-
end
|
209
|
-
|
210
|
-
def testrun_output_directory(base_output_directory, test_batch, batch_index)
|
211
|
-
return base_output_directory if @batch_count == 1
|
212
|
-
|
213
|
-
testable_name = test_batch.first.split('/').first.gsub(/\\/, '')
|
214
|
-
batched_testable_output_directory(base_output_directory, batch_index, testable_name)
|
215
|
-
end
|
216
|
-
|
217
|
-
def reset_reportnamer
|
218
|
-
@reportnamer = ReportNameHelper.new(
|
219
|
-
@given_output_types,
|
220
|
-
@given_output_files,
|
221
|
-
@given_custom_report_file_name
|
222
|
-
)
|
223
|
-
end
|
224
|
-
|
225
|
-
def test_run_completed_callback
|
226
|
-
if @parallelize && @testrun_completed_block
|
227
|
-
Proc.new do |info|
|
228
|
-
puts "about to call @parallelizer.send_subprocess_tryinfo(#{info})"
|
229
|
-
@parallelizer.send_subprocess_tryinfo(info)
|
230
|
-
end
|
231
|
-
else
|
232
|
-
@testrun_completed_block
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def reset_interstitial(output_directory)
|
237
|
-
@interstitial = TestCenter::Helper::MultiScanManager::Interstitial.new(
|
238
|
-
@scan_options.merge(
|
239
|
-
{
|
240
|
-
output_directory: output_directory,
|
241
|
-
reportnamer: @reportnamer,
|
242
|
-
testrun_completed_block: @testrun_completed_block,
|
243
|
-
parallelize: @parallelize
|
244
|
-
}
|
245
|
-
)
|
246
|
-
)
|
247
|
-
end
|
248
|
-
|
249
|
-
def reset_for_new_testable(output_directory)
|
250
|
-
reset_reportnamer
|
251
|
-
reset_interstitial(output_directory)
|
252
|
-
end
|
253
|
-
|
254
|
-
def correcting_scan(scan_run_options, batch, reportnamer)
|
255
|
-
scan_options = @scan_options.merge(scan_run_options)
|
256
|
-
try_count = 0
|
257
|
-
tests_passed = true
|
258
|
-
begin
|
259
|
-
try_count += 1
|
260
|
-
config = FastlaneCore::Configuration.create(
|
261
|
-
Fastlane::Actions::ScanAction.available_options,
|
262
|
-
scan_options.merge(reportnamer.scan_options)
|
263
|
-
)
|
264
|
-
Fastlane::Actions::ScanAction.run(config)
|
265
|
-
@interstitial.finish_try(try_count)
|
266
|
-
tests_passed = true
|
267
|
-
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
268
|
-
FastlaneCore::UI.verbose("Scan failed with #{e}")
|
269
|
-
if try_count < @try_count
|
270
|
-
@retry_total_count += 1
|
271
|
-
scan_options.delete(:code_coverage)
|
272
|
-
tests_to_retry = failed_tests(reportnamer, scan_options[:output_directory])
|
273
|
-
|
274
|
-
scan_options[:only_testing] = tests_to_retry.map(&:shellsafe_testidentifier)
|
275
|
-
FastlaneCore::UI.message('Re-running scan on only failed tests')
|
276
|
-
@interstitial.finish_try(try_count)
|
277
|
-
retry
|
278
|
-
end
|
279
|
-
tests_passed = false
|
280
|
-
end
|
281
|
-
tests_passed
|
282
|
-
end
|
283
|
-
|
284
|
-
def failed_tests(reportnamer, output_directory)
|
285
|
-
report_filepath = File.join(output_directory, reportnamer.junit_last_reportname)
|
286
|
-
config = FastlaneCore::Configuration.create(
|
287
|
-
Fastlane::Actions::TestsFromJunitAction.available_options,
|
288
|
-
{
|
289
|
-
junit: File.absolute_path(report_filepath)
|
290
|
-
}
|
291
|
-
)
|
292
|
-
Fastlane::Actions::TestsFromJunitAction.run(config)[:failed]
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
|
300
|
-
module FastlaneCore
|
301
|
-
class Shell < Interface
|
302
|
-
def format_string(datetime = Time.now, severity = "")
|
303
|
-
prefix = $batch_index.nil? ? '' : "#{$batch_index}: "
|
304
|
-
if FastlaneCore::Globals.verbose?
|
305
|
-
return "#{prefix}#{severity} [#{datetime.strftime('%Y-%m-%d %H:%M:%S.%2N')}]: "
|
306
|
-
elsif FastlaneCore::Env.truthy?("FASTLANE_HIDE_TIMESTAMP")
|
307
|
-
return prefix
|
308
|
-
else
|
309
|
-
return "#{prefix}[#{datetime.strftime('%H:%M:%S')}]: "
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|