fastlane-plugin-test_center 3.6.3 → 3.7.0.parallelizing.alpha.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +4 -0
- 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 +109 -25
- data/lib/fastlane/plugin/test_center/actions/restart_core_simulator_service.rb +38 -0
- data/lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb +16 -4
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb +5 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb +30 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/interstitial.rb +143 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/parallel_test_batch_worker.rb +27 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/report_collator.rb +115 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb +74 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb +255 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +356 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb +49 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_manager.rb +317 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker.rb +20 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb +129 -0
- data/lib/fastlane/plugin/test_center/helper/reportname_helper.rb +15 -6
- data/lib/fastlane/plugin/test_center/helper/test_collector.rb +49 -3
- data/lib/fastlane/plugin/test_center/helper/xcodebuild_string.rb +4 -0
- data/lib/fastlane/plugin/test_center/helper/xctestrun_info.rb +42 -0
- data/lib/fastlane/plugin/test_center/version.rb +1 -1
- metadata +38 -10
- data/lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb +0 -293
@@ -0,0 +1,27 @@
|
|
1
|
+
module TestCenter
|
2
|
+
module Helper
|
3
|
+
module MultiScanManager
|
4
|
+
class ParallelTestBatchWorker < TestBatchWorker
|
5
|
+
attr_reader :pid
|
6
|
+
|
7
|
+
def state=(new_state)
|
8
|
+
super(new_state)
|
9
|
+
@pid = nil unless new_state == :working
|
10
|
+
end
|
11
|
+
|
12
|
+
def run(run_options)
|
13
|
+
self.state = :working
|
14
|
+
@pid = Process.fork do
|
15
|
+
begin
|
16
|
+
super(run_options)
|
17
|
+
ensure
|
18
|
+
exit!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module TestCenter
|
2
|
+
module Helper
|
3
|
+
module MultiScanManager
|
4
|
+
class ReportCollator
|
5
|
+
CollateJunitReportsAction = Fastlane::Actions::CollateJunitReportsAction
|
6
|
+
CollateHtmlReportsAction = Fastlane::Actions::CollateHtmlReportsAction
|
7
|
+
CollateJsonReportsAction = Fastlane::Actions::CollateJsonReportsAction
|
8
|
+
CollateTestResultBundlesAction = Fastlane::Actions::CollateTestResultBundlesAction
|
9
|
+
|
10
|
+
def initialize(params)
|
11
|
+
@source_reports_directory_glob = params[:source_reports_directory_glob]
|
12
|
+
@output_directory = params[:output_directory]
|
13
|
+
@reportnamer = params[:reportnamer]
|
14
|
+
@scheme = params[:scheme]
|
15
|
+
@result_bundle = params[:result_bundle]
|
16
|
+
@suffix = params[:suffix] || ''
|
17
|
+
end
|
18
|
+
|
19
|
+
def collate
|
20
|
+
collate_junit_reports
|
21
|
+
collate_html_reports
|
22
|
+
collate_json_reports
|
23
|
+
collate_test_result_bundles
|
24
|
+
end
|
25
|
+
|
26
|
+
def sort_globbed_files(glob)
|
27
|
+
files = Dir.glob(glob).map do |relative_filepath|
|
28
|
+
File.absolute_path(relative_filepath)
|
29
|
+
end
|
30
|
+
files.sort! { |f1, f2| File.mtime(f1) <=> File.mtime(f2) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_globbed_intermediatefiles(glob)
|
34
|
+
retried_reportfiles = Dir.glob(glob)
|
35
|
+
FileUtils.rm_f(retried_reportfiles)
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_config(klass, options)
|
39
|
+
FastlaneCore::Configuration.create(klass.available_options, options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def collate_junit_reports
|
43
|
+
glob = "#{@source_reports_directory_glob}/#{@reportnamer.junit_fileglob}"
|
44
|
+
report_files = sort_globbed_files(glob)
|
45
|
+
if report_files.size > 1
|
46
|
+
collated_file = File.absolute_path(File.join(@output_directory, @reportnamer.junit_reportname(@suffix)))
|
47
|
+
config = create_config(
|
48
|
+
CollateJunitReportsAction,
|
49
|
+
{
|
50
|
+
reports: report_files,
|
51
|
+
collated_report: collated_file
|
52
|
+
}
|
53
|
+
)
|
54
|
+
CollateJunitReportsAction.run(config)
|
55
|
+
FileUtils.rm_rf(report_files - [collated_file])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def collate_html_reports
|
60
|
+
return unless @reportnamer.includes_html?
|
61
|
+
|
62
|
+
report_files = sort_globbed_files("#{@source_reports_directory_glob}/#{@reportnamer.html_fileglob}")
|
63
|
+
if report_files.size > 1
|
64
|
+
collated_file = File.absolute_path(File.join(@output_directory, @reportnamer.html_reportname(@suffix)))
|
65
|
+
config = create_config(
|
66
|
+
CollateJunitReportsAction,
|
67
|
+
{
|
68
|
+
reports: report_files,
|
69
|
+
collated_report: collated_file
|
70
|
+
}
|
71
|
+
)
|
72
|
+
CollateHtmlReportsAction.run(config)
|
73
|
+
FileUtils.rm_rf(report_files - [collated_file])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def collate_json_reports
|
78
|
+
return unless @reportnamer.includes_json?
|
79
|
+
|
80
|
+
report_files = sort_globbed_files("#{@source_reports_directory_glob}/#{@reportnamer.json_fileglob}")
|
81
|
+
if report_files.size > 1
|
82
|
+
collated_file = File.absolute_path(File.join(@output_directory, @reportnamer.json_reportname(@suffix)))
|
83
|
+
config = create_config(
|
84
|
+
CollateJsonReportsAction,
|
85
|
+
{
|
86
|
+
reports: report_files,
|
87
|
+
collated_report: collated_file
|
88
|
+
}
|
89
|
+
)
|
90
|
+
CollateJsonReportsAction.run(config)
|
91
|
+
FileUtils.rm_rf(report_files - [collated_file])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def collate_test_result_bundles
|
96
|
+
return unless @result_bundle
|
97
|
+
|
98
|
+
test_result_bundlepaths = sort_globbed_files("#{@source_reports_directory_glob}/#{@scheme}*.test_result")
|
99
|
+
if test_result_bundlepaths.size > 1
|
100
|
+
collated_test_result_bundlepath = File.absolute_path("#{File.join(@output_directory, @scheme)}.test_result")
|
101
|
+
config = create_config(
|
102
|
+
CollateTestResultBundlesAction,
|
103
|
+
{
|
104
|
+
bundles: test_result_bundlepaths,
|
105
|
+
collated_bundle: collated_test_result_bundlepath
|
106
|
+
}
|
107
|
+
)
|
108
|
+
CollateTestResultBundlesAction.run(config)
|
109
|
+
FileUtils.rm_rf(test_result_bundlepaths - [collated_test_result_bundlepath])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module TestCenter
|
2
|
+
module Helper
|
3
|
+
module MultiScanManager
|
4
|
+
|
5
|
+
class RetryingScan
|
6
|
+
def initialize(options = {})
|
7
|
+
@options = options
|
8
|
+
@retrying_scan_helper = RetryingScanHelper.new(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def scan_config
|
12
|
+
Scan.config
|
13
|
+
end
|
14
|
+
|
15
|
+
def scan_cache
|
16
|
+
Scan.cache
|
17
|
+
end
|
18
|
+
|
19
|
+
def prepare_scan_config_for_destination
|
20
|
+
# this allows multi_scan's `destination` option to be picked up by `scan`
|
21
|
+
scan_config._values.delete(:device)
|
22
|
+
scan_config._values.delete(:devices)
|
23
|
+
scan_cache.clear
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_scan_options
|
27
|
+
valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
|
28
|
+
scan_options = @options.select { |k,v| valid_scan_keys.include?(k) }
|
29
|
+
.merge(@retrying_scan_helper.scan_options)
|
30
|
+
|
31
|
+
prepare_scan_config_for_destination
|
32
|
+
scan_options.each do |k,v|
|
33
|
+
scan_config.set(k,v) unless v.nil?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.run(options)
|
38
|
+
RetryingScan.new(options).run
|
39
|
+
end
|
40
|
+
|
41
|
+
def run
|
42
|
+
try_count = @options[:try_count] || 1
|
43
|
+
begin
|
44
|
+
# TODO move delete_xcresults to `before_testrun`
|
45
|
+
@retrying_scan_helper.before_testrun
|
46
|
+
update_scan_options
|
47
|
+
|
48
|
+
values = scan_config.values(ask: false)
|
49
|
+
values[:xcode_path] = File.expand_path("../..", FastlaneCore::Helper.xcode_path)
|
50
|
+
FastlaneCore::PrintTable.print_values(
|
51
|
+
config: values,
|
52
|
+
hide_keys: [:destination, :slack_url],
|
53
|
+
title: "Summary for scan #{Fastlane::VERSION}"
|
54
|
+
) unless FastlaneCore::Helper.test?
|
55
|
+
|
56
|
+
Scan::Runner.new.run
|
57
|
+
@retrying_scan_helper.after_testrun
|
58
|
+
true
|
59
|
+
rescue FastlaneCore::Interface::FastlaneTestFailure => e
|
60
|
+
FastlaneCore::UI.message("retrying_scan after test failure")
|
61
|
+
@retrying_scan_helper.after_testrun(e)
|
62
|
+
retry if @retrying_scan_helper.testrun_count < try_count
|
63
|
+
false
|
64
|
+
rescue FastlaneCore::Interface::FastlaneBuildFailure => e
|
65
|
+
FastlaneCore::UI.message("retrying_scan after build failure")
|
66
|
+
@retrying_scan_helper.after_testrun(e)
|
67
|
+
retry if @retrying_scan_helper.testrun_count < try_count
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,255 @@
|
|
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
|
+
@reportnamer = ReportNameHelper.new(
|
17
|
+
@options[:output_types],
|
18
|
+
@options[:output_files],
|
19
|
+
@options[:custom_report_file_name]
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def before_testrun
|
24
|
+
remove_preexisting_test_result_bundles
|
25
|
+
delete_xcresults # has to be performed _after_ moving a *.test_result
|
26
|
+
|
27
|
+
set_json_env
|
28
|
+
print_starting_scan_message
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete_xcresults
|
32
|
+
derived_data_path = File.expand_path(@options[:derived_data_path] || Scan.config[:derived_data_path])
|
33
|
+
xcresults = Dir.glob("#{derived_data_path}/Logs/Test/*.xcresult")
|
34
|
+
FastlaneCore::UI.message("Deleting xcresults: #{xcresults}")
|
35
|
+
FileUtils.rm_rf(xcresults)
|
36
|
+
end
|
37
|
+
|
38
|
+
def output_directory
|
39
|
+
absolute_output_directory = File.absolute_path(@options[:output_directory])
|
40
|
+
if @options[:batch]
|
41
|
+
testable = @options.fetch(:only_testing, ['']).first.split('/').first || ''
|
42
|
+
absolute_output_directory = File.join(absolute_output_directory, "#{testable}-batch-#{@options[:batch]}")
|
43
|
+
end
|
44
|
+
absolute_output_directory
|
45
|
+
end
|
46
|
+
|
47
|
+
def print_starting_scan_message
|
48
|
+
scan_message = "Starting scan ##{@testrun_count + 1} with #{@options.fetch(:only_testing, []).size} tests"
|
49
|
+
scan_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
|
50
|
+
FastlaneCore::UI.message("#{scan_message}.")
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_json_env
|
54
|
+
return unless @reportnamer.includes_json?
|
55
|
+
|
56
|
+
ENV['XCPRETTY_JSON_FILE_OUTPUT'] = File.join(
|
57
|
+
output_directory,
|
58
|
+
@reportnamer.json_last_reportname
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def reset_json_env
|
63
|
+
return unless @reportnamer.includes_json?
|
64
|
+
|
65
|
+
ENV['XCPRETTY_JSON_FILE_OUTPUT'] = @xcpretty_json_file_output
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove_preexisting_test_result_bundles
|
69
|
+
return unless @options[:result_bundle]
|
70
|
+
|
71
|
+
glob_pattern = "#{output_directory}/*.test_result"
|
72
|
+
preexisting_test_result_bundles = Dir.glob(glob_pattern)
|
73
|
+
FileUtils.rm_rf(preexisting_test_result_bundles)
|
74
|
+
end
|
75
|
+
|
76
|
+
def scan_options
|
77
|
+
valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
|
78
|
+
@options.select { |k,v| valid_scan_keys.include?(k) }
|
79
|
+
.merge(@reportnamer.scan_options)
|
80
|
+
.merge(output_directory: output_directory)
|
81
|
+
end
|
82
|
+
|
83
|
+
# after_testrun methods
|
84
|
+
|
85
|
+
def after_testrun(exception = nil)
|
86
|
+
@testrun_count = @testrun_count + 1
|
87
|
+
if exception.kind_of?(FastlaneCore::Interface::FastlaneTestFailure)
|
88
|
+
handle_test_failure
|
89
|
+
elsif exception.kind_of?(FastlaneCore::Interface::FastlaneBuildFailure)
|
90
|
+
handle_build_failure(exception)
|
91
|
+
else
|
92
|
+
handle_success
|
93
|
+
end
|
94
|
+
collate_reports
|
95
|
+
end
|
96
|
+
|
97
|
+
def handle_success
|
98
|
+
send_callback_testrun_info
|
99
|
+
move_test_result_bundle_for_next_run
|
100
|
+
reset_json_env
|
101
|
+
end
|
102
|
+
|
103
|
+
def collate_reports
|
104
|
+
|
105
|
+
report_collator_options = {
|
106
|
+
source_reports_directory_glob: output_directory,
|
107
|
+
output_directory: output_directory,
|
108
|
+
reportnamer: @reportnamer,
|
109
|
+
scheme: @options[:scheme],
|
110
|
+
result_bundle: @options[:result_bundle]
|
111
|
+
}
|
112
|
+
TestCenter::Helper::MultiScanManager::ReportCollator.new(report_collator_options).collate
|
113
|
+
end
|
114
|
+
|
115
|
+
def handle_test_failure
|
116
|
+
send_callback_testrun_info
|
117
|
+
reset_simulators
|
118
|
+
move_test_result_bundle_for_next_run
|
119
|
+
update_scan_options
|
120
|
+
@reportnamer.increment
|
121
|
+
end
|
122
|
+
|
123
|
+
def send_callback_testrun_info(additional_info = {})
|
124
|
+
return unless @options[:testrun_completed_block]
|
125
|
+
|
126
|
+
report_filepath = nil
|
127
|
+
junit_results = {}
|
128
|
+
unless additional_info.key?(:test_operation_failure)
|
129
|
+
report_filepath = File.join(output_directory, @reportnamer.junit_last_reportname)
|
130
|
+
|
131
|
+
config = FastlaneCore::Configuration.create(
|
132
|
+
Fastlane::Actions::TestsFromJunitAction.available_options,
|
133
|
+
{
|
134
|
+
junit: File.absolute_path(report_filepath)
|
135
|
+
}
|
136
|
+
)
|
137
|
+
junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)
|
138
|
+
end
|
139
|
+
|
140
|
+
info = {
|
141
|
+
failed: junit_results[:failed],
|
142
|
+
passing: junit_results[:passing],
|
143
|
+
batch: @options[:batch] || 1,
|
144
|
+
try_count: @testrun_count,
|
145
|
+
report_filepath: report_filepath
|
146
|
+
}.merge(additional_info)
|
147
|
+
|
148
|
+
if @reportnamer.includes_html?
|
149
|
+
html_report_filepath = File.join(output_directory, @reportnamer.html_last_reportname)
|
150
|
+
info[:html_report_filepath] = html_report_filepath
|
151
|
+
end
|
152
|
+
if @reportnamer.includes_json?
|
153
|
+
json_report_filepath = File.join(output_directory, @reportnamer.json_last_reportname)
|
154
|
+
info[:json_report_filepath] = json_report_filepath
|
155
|
+
end
|
156
|
+
if @options[:result_bundle]
|
157
|
+
test_result_suffix = '.test_result'
|
158
|
+
test_result_suffix.prepend("-#{@reportnamer.report_count}") unless @reportnamer.report_count.zero?
|
159
|
+
test_result_bundlepath = File.join(output_directory, @options[:scheme]) + test_result_suffix
|
160
|
+
info[:test_result_bundlepath] = test_result_bundlepath
|
161
|
+
end
|
162
|
+
@options[:testrun_completed_block].call(info)
|
163
|
+
end
|
164
|
+
|
165
|
+
def update_scan_options
|
166
|
+
update_only_testing
|
167
|
+
turn_off_code_coverage
|
168
|
+
end
|
169
|
+
|
170
|
+
def turn_off_code_coverage
|
171
|
+
# Turn off code coverage as code coverage reports are not merged and
|
172
|
+
# the first, more valuable, report will be overwritten
|
173
|
+
@options.delete(:code_coverage)
|
174
|
+
end
|
175
|
+
|
176
|
+
def update_only_testing
|
177
|
+
report_filepath = File.join(output_directory, @reportnamer.junit_last_reportname)
|
178
|
+
config = FastlaneCore::Configuration.create(
|
179
|
+
Fastlane::Actions::TestsFromJunitAction.available_options,
|
180
|
+
{
|
181
|
+
junit: File.absolute_path(report_filepath)
|
182
|
+
}
|
183
|
+
)
|
184
|
+
@options[:only_testing] = Fastlane::Actions::TestsFromJunitAction.run(config)[:failed]
|
185
|
+
if @options[:invocation_based_tests]
|
186
|
+
@options[:only_testing] = @options[:only_testing].map(&:strip_testcase).uniq
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def reset_simulators
|
191
|
+
return unless @options[:reset_simulators]
|
192
|
+
|
193
|
+
@options[:simulators].each(&:reset)
|
194
|
+
end
|
195
|
+
|
196
|
+
def handle_build_failure(exception)
|
197
|
+
test_operation_failure = ''
|
198
|
+
|
199
|
+
test_session_last_messages = last_lines_of_test_session_log
|
200
|
+
test_operation_failure_match = /Test operation failure: (?<test_operation_failure>.*)$/ =~ test_session_last_messages
|
201
|
+
if test_operation_failure_match.nil?
|
202
|
+
test_operation_failure = 'Unknown test operation failure'
|
203
|
+
end
|
204
|
+
|
205
|
+
case test_operation_failure
|
206
|
+
when /Test runner exited before starting test execution/
|
207
|
+
FastlaneCore::UI.error(test_operation_failure)
|
208
|
+
when /Lost connection to testmanagerd/
|
209
|
+
FastlaneCore::UI.error(test_operation_failure)
|
210
|
+
FastlaneCore::UI.important("com.apple.CoreSimulator.CoreSimulatorService may have become corrupt, consider quitting it")
|
211
|
+
if @options[:quit_core_simulator_service]
|
212
|
+
Fastlane::Actions::RestartCoreSimulatorServiceAction.run
|
213
|
+
end
|
214
|
+
else
|
215
|
+
FastlaneCore::UI.error(test_operation_failure)
|
216
|
+
send_callback_testrun_info(test_operation_failure: test_operation_failure)
|
217
|
+
raise exception
|
218
|
+
end
|
219
|
+
if @options[:reset_simulators]
|
220
|
+
@options[:simulators].each do |simulator|
|
221
|
+
simulator.reset
|
222
|
+
end
|
223
|
+
end
|
224
|
+
send_callback_testrun_info(test_operation_failure: test_operation_failure)
|
225
|
+
end
|
226
|
+
|
227
|
+
def last_lines_of_test_session_log
|
228
|
+
derived_data_path = File.expand_path(@options[:derived_data_path])
|
229
|
+
test_session_logs = Dir.glob("#{derived_data_path}/Logs/Test/*.xcresult/*_Test/Diagnostics/**/Session-*.log")
|
230
|
+
test_session_logs.sort! { |logfile1, logfile2| File.mtime(logfile1) <=> File.mtime(logfile2) }
|
231
|
+
test_session = File.open(test_session_logs.last)
|
232
|
+
backwards_seek_offset = -1 * [1000, test_session.stat.size].min
|
233
|
+
test_session.seek(backwards_seek_offset, IO::SEEK_END)
|
234
|
+
test_session_last_messages = test_session.read
|
235
|
+
end
|
236
|
+
|
237
|
+
def move_test_result_bundle_for_next_run
|
238
|
+
return unless @options[:result_bundle]
|
239
|
+
|
240
|
+
glob_pattern = "#{output_directory}/*.test_result"
|
241
|
+
preexisting_test_result_bundles = Dir.glob(glob_pattern)
|
242
|
+
unnumbered_test_result_bundles = preexisting_test_result_bundles.reject do |test_result|
|
243
|
+
test_result =~ /.*-\d+\.test_result/
|
244
|
+
end
|
245
|
+
src_test_bundle = unnumbered_test_result_bundles.first
|
246
|
+
dst_test_bundle_parent_dir = File.dirname(src_test_bundle)
|
247
|
+
dst_test_bundle_basename = File.basename(src_test_bundle, '.test_result')
|
248
|
+
dst_test_bundle = "#{dst_test_bundle_parent_dir}/#{dst_test_bundle_basename}-#{@testrun_count}.test_result"
|
249
|
+
FileUtils.mkdir_p(dst_test_bundle)
|
250
|
+
FileUtils.mv(src_test_bundle, dst_test_bundle)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|