fastlane-plugin-test_center 3.6.3.parallelizing.pre.alpha.pre.1 → 3.6.3

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.
@@ -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