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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b03566f2644f92ffde801c8d83a6bd009e59fd947437cf9b6ea26b737ae0996
4
- data.tar.gz: 80d1d785126c154dfda7a2aee7ded8654bd58776746144550e7d37c863e126c4
3
+ metadata.gz: d4b6e643411a6c7dc79cc1ffdf6f2e92d6eca01a2a31c14c8f2904290f0792a3
4
+ data.tar.gz: d57683f16900bd9460adfe2c613dff614dc11af4a3cccbb315391a773f7ffd12
5
5
  SHA512:
6
- metadata.gz: 481326edd2420e6cc1f5347c434b0089b32dc526e0e09a1279fd8131855771711fb8676a3251e5d3003bb7a35dcd6ba2cb1b505d62b482395fe06ec3df4e5f3b
7
- data.tar.gz: f4360d6b14dda570832bba2b9c86e127beea79f83a23fd3e4c0730e017b2f51e6216c5b0fa4ccd688fc0acd1d01d5d0376718cae53f13f402477f7aa8054ed8e
6
+ metadata.gz: 2662f68881f0cfd6e75721f7fd13e9ee229981d1ddaf6b01ba3c1effd1510ba3e0df7542cf04b89ac7f36d9a83c94f11f201bed01246f376a30ad561c806d610
7
+ data.tar.gz: 5855c0fd93652ac1a034a8a268268c9587ffe5de8420b65d4c1fa4fe614198d4ab9190f63589abaacd961e20517b879b8508453db719c1aff78dd57337b21495
@@ -4,7 +4,7 @@ module Fastlane
4
4
  module TestCenter
5
5
  # Return all .rb files inside the "actions" and "helper" directory
6
6
  def self.all_classes
7
- Dir[File.expand_path('**/{actions,helper}/**/*.rb', File.dirname(__FILE__))]
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
8
  end
9
9
  end
10
10
  end
@@ -14,7 +14,7 @@ module Fastlane
14
14
  collate_bundles(base_bundle_path, other_bundlepath)
15
15
  end
16
16
  end
17
- # FileUtils.rm_rf(params[:collated_bundle])
17
+ FileUtils.rm_rf(params[:collated_bundle])
18
18
  FileUtils.cp_r(base_bundle_path, params[:collated_bundle])
19
19
  UI.message("Finished collating test_result bundle to '#{params[:collated_bundle]}'")
20
20
  end
@@ -5,7 +5,6 @@ module Fastlane
5
5
  require 'shellwords'
6
6
  require 'xctest_list'
7
7
  require 'plist'
8
- require_relative '../helper/multi_scan_manager/runner'
9
8
 
10
9
  class MultiScanAction < Action
11
10
  def self.run(params)
@@ -20,13 +19,9 @@ module Fastlane
20
19
  params._values
21
20
  )
22
21
  end
23
- runner = ::TestCenter::Helper::MultiScanManager::Runner.new(params.values)
24
- tests_passed = runner.scan
25
- if params[:fail_build] && !tests_passed
26
- raise UI.test_failure!('Tests have failed')
27
- end
28
-
29
- summary = run_summary(params, tests_passed, runner.retry_total_count)
22
+ smart_scanner = ::TestCenter::Helper::CorrectingScanHelper.new(params.values)
23
+ tests_passed = smart_scanner.scan
24
+ summary = run_summary(params, tests_passed, smart_scanner.retry_total_count)
30
25
  unless Helper.test?
31
26
  FastlaneCore::PrintTable.print_values(
32
27
  config: summary,
@@ -89,19 +84,17 @@ module Fastlane
89
84
  options_to_remove = %i[
90
85
  try_count
91
86
  batch_count
92
- output_files
93
- parallelize
94
87
  quit_simulators
95
88
  testrun_completed_block
96
89
  test_without_building
97
90
  output_types
91
+ output_files
98
92
  ]
99
93
  config = FastlaneCore::Configuration.create(
100
94
  Fastlane::Actions::ScanAction.available_options,
101
95
  scan_options.merge(build_for_testing: true).reject { |k, _| options_to_remove.include?(k) }
102
96
  )
103
97
  Fastlane::Actions::ScanAction.run(config)
104
- remove_build_report_files
105
98
 
106
99
  scan_options.merge!(
107
100
  test_without_building: true,
@@ -109,16 +102,6 @@ module Fastlane
109
102
  ).delete(:build_for_testing)
110
103
  end
111
104
 
112
- def self.remove_build_report_files
113
- report_options = Scan::XCPrettyReporterOptionsGenerator.generate_from_scan_config
114
- output_files = report_options.instance_variable_get(:@output_files)
115
- output_directory = report_options.instance_variable_get(:@output_directory)
116
-
117
- output_files.each do |output_file|
118
- FileUtils.rm_f(File.join(output_directory, output_file))
119
- end
120
- end
121
-
122
105
  #####################################################
123
106
  # @!group Documentation
124
107
  #####################################################
@@ -189,13 +172,6 @@ module Fastlane
189
172
  description: "Comma separated list of the output types (e.g. html, junit, json, json-compilation-database)",
190
173
  default_value: "html,junit"
191
174
  ),
192
- FastlaneCore::ConfigItem.new(
193
- key: :parallelize,
194
- description: 'Run each batch of tests and/or each test target in parallel on its own Simulator',
195
- optional: true,
196
- is_string: false,
197
- default_value: false
198
- ),
199
175
  FastlaneCore::ConfigItem.new(
200
176
  key: :testrun_completed_block,
201
177
  description: 'A block invoked each time a test run completes',
@@ -341,26 +317,6 @@ module Fastlane
341
317
  output_files: 'report.json',
342
318
  fail_build: false
343
319
  )
344
- ",
345
- "
346
- UI.important(
347
- 'example: ' \\
348
- 'use the :xctestrun parameter instead of the :project parameter to find, ' \\
349
- 'build, and test the iOS app.'
350
- )
351
- Dir.mktmpdir do |derived_data_path|
352
- project_path = '/Users/lyndsey.ferguson/repo/fastlane-plugin-test_center/AtomicBoy/AtomicBoy.xcodeproj'
353
- command = \"bundle exec fastlane scan --build_for_testing true --project '#{project_path}' --derived_data_path #{derived_data_path} --scheme AtomicBoy\"
354
- `#{command}`
355
- xctestrun_file = Dir.glob(\"#{derived_data_path}/Build/Products/AtomicBoy*.xctestrun\").first
356
- multi_scan(
357
- scheme: 'AtomicBoy',
358
- try_count: 3,
359
- fail_build: false,
360
- xctestrun: xctestrun_file,
361
- test_without_building: true
362
- )
363
- end
364
320
  "
365
321
  ]
366
322
  end
@@ -0,0 +1,293 @@
1
+ module TestCenter
2
+ module Helper
3
+ require 'fastlane_core/ui/ui.rb'
4
+ require 'plist'
5
+ require 'json'
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
+ @quit_simulators = multi_scan_options[:quit_simulators]
14
+ @retry_total_count = 0
15
+ @testrun_completed_block = multi_scan_options[:testrun_completed_block]
16
+ @given_custom_report_file_name = multi_scan_options[:custom_report_file_name]
17
+ @given_output_types = multi_scan_options[:output_types]
18
+ @given_output_files = multi_scan_options[:output_files]
19
+ @scan_options = multi_scan_options.reject do |option, _|
20
+ %i[
21
+ output_directory
22
+ only_testing
23
+ skip_testing
24
+ clean
25
+ try_count
26
+ batch_count
27
+ quit_simulators
28
+ custom_report_file_name
29
+ fail_build
30
+ testrun_completed_block
31
+ output_types
32
+ output_files
33
+ ].include?(option)
34
+ end
35
+ @scan_options[:clean] = false
36
+ @test_collector = TestCollector.new(multi_scan_options)
37
+ end
38
+
39
+ def scan
40
+ tests_passed = true
41
+ @testables_count = @test_collector.testables.size
42
+ @test_collector.testables.each do |testable|
43
+ tests_passed = scan_testable(testable) && tests_passed
44
+ end
45
+ tests_passed
46
+ end
47
+
48
+ def scan_testable(testable)
49
+ tests_passed = true
50
+ reportnamer = ReportNameHelper.new(
51
+ @given_output_types,
52
+ @given_output_files,
53
+ @given_custom_report_file_name
54
+ )
55
+ output_directory = @output_directory
56
+ testable_tests = @test_collector.testables_tests[testable]
57
+ if testable_tests.empty?
58
+ FastlaneCore::UI.important("There are no tests to run in testable '#{testable}'. Skipping")
59
+ return true
60
+ end
61
+
62
+ if @batch_count > 1 || @testables_count > 1
63
+ current_batch = 1
64
+ testable_tests.each_slice((testable_tests.length / @batch_count.to_f).round).to_a.each do |tests_batch|
65
+ if @testables_count > 1
66
+ output_directory = File.join(@output_directory, "results-#{testable}")
67
+ end
68
+ FastlaneCore::UI.header("Starting test run on testable '#{testable}'")
69
+ if @scan_options[:result_bundle]
70
+ FastlaneCore::UI.message("Clearing out previous test_result bundles in #{output_directory}")
71
+ FileUtils.rm_rf(Dir.glob("#{output_directory}/*.test_result"))
72
+ end
73
+
74
+ tests_passed = correcting_scan(
75
+ {
76
+ only_testing: tests_batch,
77
+ output_directory: output_directory
78
+ },
79
+ current_batch,
80
+ reportnamer
81
+ ) && tests_passed
82
+ current_batch += 1
83
+ reportnamer.increment
84
+ end
85
+ else
86
+ options = {
87
+ output_directory: output_directory,
88
+ only_testing: testable_tests
89
+ }
90
+ tests_passed = correcting_scan(options, 1, reportnamer) && tests_passed
91
+ end
92
+ collate_reports(output_directory, reportnamer)
93
+ tests_passed
94
+ end
95
+
96
+ def test_result_bundlepaths(output_directory, reportnamer)
97
+ [
98
+ File.join(output_directory, @scan_options[:scheme]) + '.test_result',
99
+ File.join(output_directory, @scan_options[:scheme]) + "_#{reportnamer.report_count}.test_result"
100
+ ]
101
+ end
102
+
103
+ def collate_reports(output_directory, reportnamer)
104
+ collate_junit_reports(output_directory, reportnamer)
105
+
106
+ if reportnamer.includes_html?
107
+ collate_html_reports(output_directory, reportnamer)
108
+ end
109
+
110
+ if reportnamer.includes_json?
111
+ collate_json_reports(output_directory, reportnamer)
112
+ end
113
+
114
+ if @scan_options[:result_bundle]
115
+ collate_test_result_bundles(output_directory, reportnamer)
116
+ end
117
+ end
118
+
119
+ def passed_test_count_from_summary(summary)
120
+ /.*Executed (?<test_count>\d+) test, with (?<test_failures>\d+) failure/ =~ summary
121
+ test_count.to_i - test_failures.to_i
122
+ end
123
+
124
+ def collate_test_result_bundles(output_directory, reportnamer)
125
+ test_result_bundlepaths = Dir.glob("#{output_directory}/#{@scan_options[:scheme]}*.test_result").map do |relative_test_result_bundle_filepath|
126
+ File.absolute_path(relative_test_result_bundle_filepath)
127
+ end
128
+ if test_result_bundlepaths.size > 1
129
+ config = FastlaneCore::Configuration.create(
130
+ Fastlane::Actions::CollateTestResultBundlesAction.available_options,
131
+ {
132
+ bundles: test_result_bundlepaths.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
133
+ collated_bundle: File.join(output_directory, @scan_options[:scheme]) + '.test_result'
134
+ }
135
+ )
136
+ Fastlane::Actions::CollateTestResultBundlesAction.run(config)
137
+ end
138
+ retried_test_result_bundlepaths = Dir.glob("#{output_directory}/#{@scan_options[:scheme]}_*.test_result")
139
+ FileUtils.rm_rf(retried_test_result_bundlepaths)
140
+ end
141
+
142
+ def collate_json_reports(output_directory, reportnamer)
143
+ report_filepaths = Dir.glob("#{output_directory}/#{reportnamer.json_fileglob}").map do |relative_filepath|
144
+ File.absolute_path(relative_filepath)
145
+ end
146
+ if report_filepaths.size > 1
147
+ config = FastlaneCore::Configuration.create(
148
+ Fastlane::Actions::CollateJsonReportsAction.available_options,
149
+ {
150
+ reports: report_filepaths.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
151
+ collated_report: File.absolute_path(File.join(output_directory, reportnamer.json_reportname))
152
+ }
153
+ )
154
+ Fastlane::Actions::CollateJsonReportsAction.run(config)
155
+ end
156
+ retried_json_reportfiles = Dir.glob("#{output_directory}/#{reportnamer.json_numbered_fileglob}")
157
+ FileUtils.rm_f(retried_json_reportfiles)
158
+ end
159
+
160
+ def collate_html_reports(output_directory, reportnamer)
161
+ report_files = Dir.glob("#{output_directory}/#{reportnamer.html_fileglob}").map do |relative_filepath|
162
+ File.absolute_path(relative_filepath)
163
+ end
164
+ if report_files.size > 1
165
+ config = FastlaneCore::Configuration.create(
166
+ Fastlane::Actions::CollateHtmlReportsAction.available_options,
167
+ {
168
+ reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
169
+ collated_report: File.absolute_path(File.join(output_directory, reportnamer.html_reportname))
170
+ }
171
+ )
172
+ Fastlane::Actions::CollateHtmlReportsAction.run(config)
173
+ end
174
+ retried_html_reportfiles = Dir.glob("#{output_directory}/#{reportnamer.html_numbered_fileglob}")
175
+ FileUtils.rm_f(retried_html_reportfiles)
176
+ end
177
+
178
+ def collate_junit_reports(output_directory, reportnamer)
179
+ report_files = Dir.glob("#{output_directory}/#{reportnamer.junit_fileglob}").map do |relative_filepath|
180
+ File.absolute_path(relative_filepath)
181
+ end
182
+ if report_files.size > 1
183
+ config = FastlaneCore::Configuration.create(
184
+ Fastlane::Actions::CollateJunitReportsAction.available_options,
185
+ {
186
+ reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
187
+ collated_report: File.absolute_path(File.join(output_directory, reportnamer.junit_reportname))
188
+ }
189
+ )
190
+ Fastlane::Actions::CollateJunitReportsAction.run(config)
191
+ end
192
+ retried_junit_reportfiles = Dir.glob("#{output_directory}/#{reportnamer.junit_numbered_fileglob}")
193
+ FileUtils.rm_f(retried_junit_reportfiles)
194
+ end
195
+
196
+ def correcting_scan(scan_run_options, batch, reportnamer)
197
+ scan_options = @scan_options.merge(scan_run_options)
198
+ try_count = 0
199
+ tests_passed = true
200
+ xcpretty_json_file_output = ENV['XCPRETTY_JSON_FILE_OUTPUT']
201
+ begin
202
+ try_count += 1
203
+ config = FastlaneCore::Configuration.create(
204
+ Fastlane::Actions::ScanAction.available_options,
205
+ scan_options.merge(reportnamer.scan_options)
206
+ )
207
+ quit_simulators
208
+ if reportnamer.includes_json?
209
+ ENV['XCPRETTY_JSON_FILE_OUTPUT'] = File.join(
210
+ scan_options[:output_directory],
211
+ reportnamer.json_last_reportname
212
+ )
213
+ end
214
+ Fastlane::Actions::ScanAction.run(config)
215
+ @testrun_completed_block && @testrun_completed_block.call(
216
+ testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
217
+ )
218
+ tests_passed = true
219
+ rescue FastlaneCore::Interface::FastlaneTestFailure => e
220
+ FastlaneCore::UI.verbose("Scan failed with #{e}")
221
+ info = testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
222
+ @testrun_completed_block && @testrun_completed_block.call(
223
+ info
224
+ )
225
+ if try_count < @try_count
226
+ @retry_total_count += 1
227
+ scan_options.delete(:code_coverage)
228
+ scan_options[:only_testing] = info[:failed].map(&:shellsafe_testidentifier)
229
+ FastlaneCore::UI.message('Re-running scan on only failed tests')
230
+ reportnamer.increment
231
+ if @scan_options[:result_bundle]
232
+ built_test_result, moved_test_result = test_result_bundlepaths(
233
+ scan_options[:output_directory], reportnamer
234
+ )
235
+ FileUtils.mv(built_test_result, moved_test_result)
236
+ end
237
+ retry
238
+ end
239
+ tests_passed = false
240
+ ensure
241
+ ENV['XCPRETTY_JSON_FILE_OUTPUT'] = xcpretty_json_file_output
242
+ end
243
+ tests_passed
244
+ end
245
+
246
+ def testrun_info(batch, try_count, reportnamer, output_directory)
247
+ report_filepath = File.join(output_directory, reportnamer.junit_last_reportname)
248
+ config = FastlaneCore::Configuration.create(
249
+ Fastlane::Actions::TestsFromJunitAction.available_options,
250
+ {
251
+ junit: File.absolute_path(report_filepath)
252
+ }
253
+ )
254
+ junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)
255
+
256
+ info = {
257
+ failed: junit_results[:failed],
258
+ passing: junit_results[:passing],
259
+ batch: batch,
260
+ try_count: try_count,
261
+ report_filepath: report_filepath
262
+ }
263
+ if reportnamer.includes_html?
264
+ html_report_filepath = File.join(output_directory, reportnamer.html_last_reportname)
265
+ info[:html_report_filepath] = html_report_filepath
266
+ end
267
+ if reportnamer.includes_json?
268
+ json_report_filepath = File.join(output_directory, reportnamer.json_last_reportname)
269
+ info[:json_report_filepath] = json_report_filepath
270
+ end
271
+ if @scan_options[:result_bundle]
272
+ test_result_suffix = '.test_result'
273
+ test_result_suffix.prepend("_#{reportnamer.report_count}") unless reportnamer.report_count.zero?
274
+ test_result_bundlepath = File.join(output_directory, @scan_options[:scheme]) + test_result_suffix
275
+ info[:test_result_bundlepath] = test_result_bundlepath
276
+ end
277
+ info
278
+ end
279
+
280
+ def quit_simulators
281
+ return unless @quit_simulators
282
+
283
+ Fastlane::Actions.sh("killall -9 'iPhone Simulator' 'Simulator' 'SimulatorBridge' &> /dev/null || true", log: false)
284
+ launchctl_list_count = 0
285
+ while Fastlane::Actions.sh('launchctl list | grep com.apple.CoreSimulator.CoreSimulatorService || true', log: false) != ''
286
+ break if (launchctl_list_count += 1) > 10
287
+ Fastlane::Actions.sh('launchctl remove com.apple.CoreSimulator.CoreSimulatorService &> /dev/null || true', log: false)
288
+ sleep(1)
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end
@@ -64,12 +64,9 @@ module TestCenter
64
64
  numbered_filename(@output_files.to_s.split(',')[junit_index])
65
65
  end
66
66
 
67
- def junit_reportname(suffix = '')
67
+ def junit_reportname
68
68
  junit_index = @output_types.split(',').find_index('junit')
69
- report_name = @output_files.to_s.split(',')[junit_index]
70
- return report_name if suffix.empty?
71
-
72
- "#{File.basename(report_name, '.*')}-#{suffix}#{junit_filextension}"
69
+ @output_files.to_s.split(',')[junit_index]
73
70
  end
74
71
 
75
72
  def junit_filextension
@@ -93,12 +90,9 @@ module TestCenter
93
90
  numbered_filename(@output_files.to_s.split(',')[html_index])
94
91
  end
95
92
 
96
- def html_reportname(suffix = '')
93
+ def html_reportname
97
94
  html_index = @output_types.split(',').find_index('html')
98
- report_name = @output_files.to_s.split(',')[html_index]
99
- return report_name if suffix.empty?
100
-
101
- "#{File.basename(report_name, '.*')}-#{suffix}#{html_filextension}"
95
+ @output_files.to_s.split(',')[html_index]
102
96
  end
103
97
 
104
98
  def html_filextension
@@ -122,12 +116,9 @@ module TestCenter
122
116
  numbered_filename(@output_files.to_s.split(',')[json_index])
123
117
  end
124
118
 
125
- def json_reportname(suffix = '')
119
+ def json_reportname
126
120
  json_index = @output_types.split(',').find_index('json')
127
- report_name = @output_files.to_s.split(',')[json_index]
128
- return report_name if suffix.empty?
129
-
130
- "#{File.basename(report_name, '.*')}-#{suffix}#{json_filextension}"
121
+ @output_files.to_s.split(',')[json_index]
131
122
  end
132
123
 
133
124
  def json_filextension