fastlane-plugin-test_center 3.7.0.parallelizing.alpha.4 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (23) hide show
  1. checksums.yaml +4 -4
  2. data/lib/fastlane/plugin/test_center.rb +1 -1
  3. data/lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb +1 -1
  4. data/lib/fastlane/plugin/test_center/actions/multi_scan.rb +34 -89
  5. data/lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb +315 -0
  6. data/lib/fastlane/plugin/test_center/helper/reportname_helper.rb +6 -15
  7. data/lib/fastlane/plugin/test_center/helper/test_collector.rb +11 -48
  8. data/lib/fastlane/plugin/test_center/version.rb +1 -1
  9. metadata +11 -24
  10. data/lib/fastlane/plugin/test_center/actions/restart_core_simulator_service.rb +0 -38
  11. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb +0 -5
  12. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb +0 -30
  13. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/interstitial.rb +0 -143
  14. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/parallel_test_batch_worker.rb +0 -27
  15. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/report_collator.rb +0 -115
  16. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb +0 -74
  17. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb +0 -255
  18. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +0 -356
  19. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb +0 -49
  20. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_manager.rb +0 -317
  21. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker.rb +0 -20
  22. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb +0 -129
  23. data/lib/fastlane/plugin/test_center/helper/xctestrun_info.rb +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac1d3616de1a2ad7f95123d43b899227325b485c
4
- data.tar.gz: 6cdd4e31e41ef0c998b52ad8666c8eb08c0c5e8d
3
+ metadata.gz: dd7fbaf52ee79115ad9ea8f260bdd35e90868c14
4
+ data.tar.gz: 03ab87b0d261fde47d37edfa7d997e33a6f3faa7
5
5
  SHA512:
6
- metadata.gz: '09d7060687e873c399e8d77833db3ba1c7a804e94b9de5ec7b4cd32fda0a5b9813a3b02942d3f9dbe2c26d7df9cab3f369842dc246ac9dd6efac8f94b339133a'
7
- data.tar.gz: 8b027123c0ff018e9227326652a6a298e9ed2a90b8b795d1a6c6841a35188f5cf1efb148f156c4c7e706dc72c3ab4d8a693e4833487301763ba90b0a45bb0618
6
+ metadata.gz: 9cd9189973cddeab19e82f2bee16577eca64b0eea355d0229cf17fa555b6231f536d558ac44a086ddbfb22ae6877cf857f45db20a9fedbe80a392cc0594db7b6
7
+ data.tar.gz: 3d3747b5497960d65c97b44b20291eea6133c68752449f29d442b6f2e144b4dfc853a7b44eb09523f383749b11d143abbdab028ff73ee314e2bef1e056725ef7
@@ -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)
@@ -15,16 +14,14 @@ module Fastlane
15
14
  title: "Summary for multi_scan (test_center v#{Fastlane::TestCenter::VERSION})"
16
15
  )
17
16
  end
18
-
19
- prepare_for_testing(params._values)
20
-
21
- runner = ::TestCenter::Helper::MultiScanManager::Runner.new(params.values)
22
- tests_passed = runner.run
23
- if params[:fail_build] && !tests_passed
24
- raise UI.test_failure!('Tests have failed')
17
+ unless params[:test_without_building] || params[:skip_build]
18
+ build_for_testing(
19
+ params._values
20
+ )
25
21
  end
26
-
27
- 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)
28
25
  unless Helper.test?
29
26
  FastlaneCore::PrintTable.print_values(
30
27
  config: summary,
@@ -83,51 +80,27 @@ module Fastlane
83
80
  }
84
81
  end
85
82
 
86
- def self.prepare_for_testing(scan_options)
87
- if scan_options[:test_without_building] || scan_options[:skip_build]
88
- prepare_scan_config(scan_options)
89
- else
90
- build_for_testing(scan_options)
91
- end
92
- end
93
-
94
- def self.prepare_scan_config(scan_options)
95
- Scan.config ||= FastlaneCore::Configuration.create(
83
+ def self.build_for_testing(scan_options)
84
+ options_to_remove = %i[
85
+ try_count
86
+ batch_count
87
+ quit_simulators
88
+ testrun_completed_block
89
+ test_without_building
90
+ invocation_based_tests
91
+ output_types
92
+ output_files
93
+ ]
94
+ config = FastlaneCore::Configuration.create(
96
95
  Fastlane::Actions::ScanAction.available_options,
97
- scan_options.select { |k,v| %i[project workspace scheme].include?(k) }
96
+ scan_options.merge(build_for_testing: true).reject { |k, _| options_to_remove.include?(k) }
98
97
  )
99
- end
100
-
101
- def self.build_for_testing(scan_options)
102
- valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
103
- scan_options = scan_options.select { |k,v| valid_scan_keys.include?(k) }
104
-
105
- Scan.config = FastlaneCore::Configuration.create(
106
- Fastlane::Actions::ScanAction.available_options,
107
- scan_options.merge(build_for_testing: true)
108
- )
109
- values = Scan.config.values(ask: false)
110
- values[:xcode_path] = File.expand_path("../..", FastlaneCore::Helper.xcode_path)
111
- FastlaneCore::PrintTable.print_values(
112
- config: values,
113
- hide_keys: [:destination, :slack_url],
114
- title: "Summary for scan #{Fastlane::VERSION}"
115
- )
116
- Scan::Runner.new.run
117
- remove_build_report_files
98
+ Fastlane::Actions::ScanAction.run(config)
118
99
 
119
- Scan.config._values.delete(:build_for_testing)
120
- scan_options[:derived_data_path] = Scan.config[:derived_data_path]
121
- end
122
-
123
- def self.remove_build_report_files
124
- report_options = Scan::XCPrettyReporterOptionsGenerator.generate_from_scan_config
125
- output_files = report_options.instance_variable_get(:@output_files)
126
- output_directory = report_options.instance_variable_get(:@output_directory)
127
-
128
- output_files.each do |output_file|
129
- FileUtils.rm_f(File.join(output_directory, output_file))
130
- end
100
+ scan_options.merge!(
101
+ test_without_building: true,
102
+ derived_data_path: Scan.config[:derived_data_path]
103
+ ).delete(:build_for_testing)
131
104
  end
132
105
 
133
106
  #####################################################
@@ -214,28 +187,6 @@ module Fastlane
214
187
  description: "Comma separated list of the output types (e.g. html, junit, json, json-compilation-database)",
215
188
  default_value: "html,junit"
216
189
  ),
217
- FastlaneCore::ConfigItem.new(
218
- key: :parallelize,
219
- description: 'Run each batch of tests and/or each test target in parallel on its own Simulator',
220
- optional: true,
221
- is_string: false,
222
- default_value: false,
223
- conflicting_options: [:parallel_simulator_count],
224
- conflict_block: proc do |value|
225
- UI.user_error!("You can't use 'parallelize' and 'parallel_simulator_count' options in one run")
226
- end
227
- ),
228
- FastlaneCore::ConfigItem.new(
229
- key: :parallel_simulator_fork_count,
230
- description: 'Run simulators each batch of tests and/or each test target in parallel on its own Simulator',
231
- optional: true,
232
- is_string: false,
233
- default_value: false,
234
- conflicting_options: [:parallelize],
235
- conflict_block: proc do |value|
236
- UI.user_error!("You can't use 'parallelize' and 'parallel_simulator_count' options in one run")
237
- end
238
- ),
239
190
  FastlaneCore::ConfigItem.new(
240
191
  key: :testrun_completed_block,
241
192
  description: 'A block invoked each time a test run completes',
@@ -385,22 +336,16 @@ module Fastlane
385
336
  "
386
337
  UI.important(
387
338
  'example: ' \\
388
- 'use the :xctestrun parameter instead of the :project parameter to find, ' \\
389
- 'build, and test the iOS app.'
339
+ 'multi_scan also works with invocation based tests.'
340
+ )
341
+ cocoapods
342
+ multi_scan(
343
+ workspace: File.absolute_path('../KiwiDemo/KiwiDemo.xcworkspace'),
344
+ scheme: 'KiwiDemoTests',
345
+ try_count: 3,
346
+ invocation_based_tests: true,
347
+ fail_build: false
390
348
  )
391
- Dir.mktmpdir do |derived_data_path|
392
- project_path = '/Users/lyndsey.ferguson/repo/fastlane-plugin-test_center/AtomicBoy/AtomicBoy.xcodeproj'
393
- command = \"bundle exec fastlane scan --build_for_testing true --project '#{project_path}' --derived_data_path #{derived_data_path} --scheme AtomicBoy\"
394
- `#{command}`
395
- xctestrun_file = Dir.glob(\"#{derived_data_path}/Build/Products/AtomicBoy*.xctestrun\").first
396
- multi_scan(
397
- scheme: 'AtomicBoy',
398
- try_count: 3,
399
- fail_build: false,
400
- xctestrun: xctestrun_file,
401
- test_without_building: true
402
- )
403
- end
404
349
  "
405
350
  ]
406
351
  end
@@ -0,0 +1,315 @@
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
+ @invocation_based_tests = multi_scan_options[:invocation_based_tests]
20
+ @scan_options = multi_scan_options.reject do |option, _|
21
+ %i[
22
+ output_directory
23
+ only_testing
24
+ skip_testing
25
+ clean
26
+ try_count
27
+ batch_count
28
+ invocation_based_tests
29
+ quit_simulators
30
+ custom_report_file_name
31
+ fail_build
32
+ testrun_completed_block
33
+ output_types
34
+ output_files
35
+ ].include?(option)
36
+ end
37
+ @scan_options[:clean] = false
38
+ @test_collector = TestCollector.new(multi_scan_options)
39
+ end
40
+
41
+ def scan
42
+ tests_passed = true
43
+ @testables_count = @test_collector.testables.size
44
+ @test_collector.testables.each do |testable|
45
+ tests_passed = scan_testable(testable) && tests_passed
46
+ end
47
+ tests_passed
48
+ end
49
+
50
+ def scan_testable(testable)
51
+ tests_passed = true
52
+ reportnamer = ReportNameHelper.new(
53
+ @given_output_types,
54
+ @given_output_files,
55
+ @given_custom_report_file_name
56
+ )
57
+ output_directory = @output_directory
58
+
59
+ if @invocation_based_tests
60
+ FastlaneCore::UI.header("Starting test run on testable '#{testable}'")
61
+ if @testables_count > 1
62
+ output_directory = File.join(@output_directory, "results-#{testable}")
63
+ end
64
+ tests_passed = correcting_scan(
65
+ {
66
+ output_directory: output_directory,
67
+ only_testing: [testable]
68
+ },
69
+ 1,
70
+ reportnamer
71
+ ) && tests_passed
72
+ else
73
+ testable_tests = @test_collector.testables_tests[testable]
74
+ if testable_tests.empty?
75
+ FastlaneCore::UI.important("There are no tests to run in testable '#{testable}'. Skipping")
76
+ return true
77
+ end
78
+
79
+ if @batch_count > 1 || @testables_count > 1
80
+ current_batch = 1
81
+
82
+ testable_tests.each_slice((testable_tests.length / @batch_count.to_f).round).to_a.each do |tests_batch|
83
+ if @testables_count > 1
84
+ output_directory = File.join(@output_directory, "results-#{testable}")
85
+ end
86
+ FastlaneCore::UI.header("Starting test run on testable '#{testable}'")
87
+ if @scan_options[:result_bundle]
88
+ FastlaneCore::UI.message("Clearing out previous test_result bundles in #{output_directory}")
89
+ FileUtils.rm_rf(Dir.glob("#{output_directory}/*.test_result"))
90
+ end
91
+
92
+ options = {
93
+ output_directory: output_directory,
94
+ only_testing: tests_batch
95
+ }
96
+ tests_passed = correcting_scan(
97
+ options,
98
+ current_batch,
99
+ reportnamer
100
+ ) && tests_passed
101
+ current_batch += 1
102
+ reportnamer.increment
103
+ end
104
+ else
105
+ options = {
106
+ output_directory: output_directory,
107
+ only_testing: testable_tests
108
+ }
109
+ tests_passed = correcting_scan(options, 1, reportnamer) && tests_passed
110
+ end
111
+ end
112
+ collate_reports(output_directory, reportnamer)
113
+ tests_passed
114
+ end
115
+
116
+ def test_result_bundlepaths(output_directory, reportnamer)
117
+ [
118
+ File.join(output_directory, @scan_options[:scheme]) + '.test_result',
119
+ File.join(output_directory, @scan_options[:scheme]) + "_#{reportnamer.report_count}.test_result"
120
+ ]
121
+ end
122
+
123
+ def collate_reports(output_directory, reportnamer)
124
+ collate_junit_reports(output_directory, reportnamer)
125
+
126
+ if reportnamer.includes_html?
127
+ collate_html_reports(output_directory, reportnamer)
128
+ end
129
+
130
+ if reportnamer.includes_json?
131
+ collate_json_reports(output_directory, reportnamer)
132
+ end
133
+
134
+ if @scan_options[:result_bundle]
135
+ collate_test_result_bundles(output_directory, reportnamer)
136
+ end
137
+ end
138
+
139
+ def passed_test_count_from_summary(summary)
140
+ /.*Executed (?<test_count>\d+) test, with (?<test_failures>\d+) failure/ =~ summary
141
+ test_count.to_i - test_failures.to_i
142
+ end
143
+
144
+ def collate_test_result_bundles(output_directory, reportnamer)
145
+ test_result_bundlepaths = Dir.glob("#{output_directory}/#{@scan_options[:scheme]}*.test_result").map do |relative_test_result_bundle_filepath|
146
+ File.absolute_path(relative_test_result_bundle_filepath)
147
+ end
148
+ if test_result_bundlepaths.size > 1
149
+ config = FastlaneCore::Configuration.create(
150
+ Fastlane::Actions::CollateTestResultBundlesAction.available_options,
151
+ {
152
+ bundles: test_result_bundlepaths.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
153
+ collated_bundle: File.join(output_directory, @scan_options[:scheme]) + '.test_result'
154
+ }
155
+ )
156
+ Fastlane::Actions::CollateTestResultBundlesAction.run(config)
157
+ end
158
+ retried_test_result_bundlepaths = Dir.glob("#{output_directory}/#{@scan_options[:scheme]}_*.test_result")
159
+ FileUtils.rm_rf(retried_test_result_bundlepaths)
160
+ end
161
+
162
+ def collate_json_reports(output_directory, reportnamer)
163
+ report_filepaths = Dir.glob("#{output_directory}/#{reportnamer.json_fileglob}").map do |relative_filepath|
164
+ File.absolute_path(relative_filepath)
165
+ end
166
+ if report_filepaths.size > 1
167
+ config = FastlaneCore::Configuration.create(
168
+ Fastlane::Actions::CollateJsonReportsAction.available_options,
169
+ {
170
+ reports: report_filepaths.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
171
+ collated_report: File.absolute_path(File.join(output_directory, reportnamer.json_reportname))
172
+ }
173
+ )
174
+ Fastlane::Actions::CollateJsonReportsAction.run(config)
175
+ end
176
+ retried_json_reportfiles = Dir.glob("#{output_directory}/#{reportnamer.json_numbered_fileglob}")
177
+ FileUtils.rm_f(retried_json_reportfiles)
178
+ end
179
+
180
+ def collate_html_reports(output_directory, reportnamer)
181
+ report_files = Dir.glob("#{output_directory}/#{reportnamer.html_fileglob}").map do |relative_filepath|
182
+ File.absolute_path(relative_filepath)
183
+ end
184
+ if report_files.size > 1
185
+ config = FastlaneCore::Configuration.create(
186
+ Fastlane::Actions::CollateHtmlReportsAction.available_options,
187
+ {
188
+ reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
189
+ collated_report: File.absolute_path(File.join(output_directory, reportnamer.html_reportname))
190
+ }
191
+ )
192
+ Fastlane::Actions::CollateHtmlReportsAction.run(config)
193
+ end
194
+ retried_html_reportfiles = Dir.glob("#{output_directory}/#{reportnamer.html_numbered_fileglob}")
195
+ FileUtils.rm_f(retried_html_reportfiles)
196
+ end
197
+
198
+ def collate_junit_reports(output_directory, reportnamer)
199
+ report_files = Dir.glob("#{output_directory}/#{reportnamer.junit_fileglob}").map do |relative_filepath|
200
+ File.absolute_path(relative_filepath)
201
+ end
202
+ if report_files.size > 1
203
+ config = FastlaneCore::Configuration.create(
204
+ Fastlane::Actions::CollateJunitReportsAction.available_options,
205
+ {
206
+ reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
207
+ collated_report: File.absolute_path(File.join(output_directory, reportnamer.junit_reportname))
208
+ }
209
+ )
210
+ Fastlane::Actions::CollateJunitReportsAction.run(config)
211
+ end
212
+ retried_junit_reportfiles = Dir.glob("#{output_directory}/#{reportnamer.junit_numbered_fileglob}")
213
+ FileUtils.rm_f(retried_junit_reportfiles)
214
+ end
215
+
216
+ def correcting_scan(scan_run_options, batch, reportnamer)
217
+ scan_options = @scan_options.merge(scan_run_options)
218
+ try_count = 0
219
+ tests_passed = true
220
+ xcpretty_json_file_output = ENV['XCPRETTY_JSON_FILE_OUTPUT']
221
+ begin
222
+ try_count += 1
223
+ config = FastlaneCore::Configuration.create(
224
+ Fastlane::Actions::ScanAction.available_options,
225
+ scan_options.merge(reportnamer.scan_options)
226
+ )
227
+ quit_simulators
228
+ if reportnamer.includes_json?
229
+ ENV['XCPRETTY_JSON_FILE_OUTPUT'] = File.join(
230
+ scan_options[:output_directory],
231
+ reportnamer.json_last_reportname
232
+ )
233
+ end
234
+ Fastlane::Actions::ScanAction.run(config)
235
+ @testrun_completed_block && @testrun_completed_block.call(
236
+ testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
237
+ )
238
+ tests_passed = true
239
+ rescue FastlaneCore::Interface::FastlaneTestFailure => e
240
+ FastlaneCore::UI.verbose("Scan failed with #{e}")
241
+ info = testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
242
+ @testrun_completed_block && @testrun_completed_block.call(
243
+ info
244
+ )
245
+ if try_count < @try_count
246
+ @retry_total_count += 1
247
+ scan_options.delete(:code_coverage)
248
+ last_failed_tests = info[:failed].map(&:shellsafe_testidentifier)
249
+ last_failed_tests = last_failed_tests.map(&:strip_testcase) if @invocation_based_tests
250
+ scan_options[:only_testing] = last_failed_tests.uniq
251
+ FastlaneCore::UI.message('Re-running scan on only failed tests')
252
+ reportnamer.increment
253
+ if @scan_options[:result_bundle]
254
+ built_test_result, moved_test_result = test_result_bundlepaths(
255
+ scan_options[:output_directory], reportnamer
256
+ )
257
+ FileUtils.mv(built_test_result, moved_test_result)
258
+ end
259
+ retry
260
+ end
261
+ tests_passed = false
262
+ ensure
263
+ ENV['XCPRETTY_JSON_FILE_OUTPUT'] = xcpretty_json_file_output
264
+ end
265
+ tests_passed
266
+ end
267
+
268
+ def testrun_info(batch, try_count, reportnamer, output_directory)
269
+ report_filepath = File.join(output_directory, reportnamer.junit_last_reportname)
270
+ config = FastlaneCore::Configuration.create(
271
+ Fastlane::Actions::TestsFromJunitAction.available_options,
272
+ {
273
+ junit: File.absolute_path(report_filepath)
274
+ }
275
+ )
276
+ junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)
277
+
278
+ info = {
279
+ failed: junit_results[:failed],
280
+ passing: junit_results[:passing],
281
+ batch: batch,
282
+ try_count: try_count,
283
+ report_filepath: report_filepath
284
+ }
285
+ if reportnamer.includes_html?
286
+ html_report_filepath = File.join(output_directory, reportnamer.html_last_reportname)
287
+ info[:html_report_filepath] = html_report_filepath
288
+ end
289
+ if reportnamer.includes_json?
290
+ json_report_filepath = File.join(output_directory, reportnamer.json_last_reportname)
291
+ info[:json_report_filepath] = json_report_filepath
292
+ end
293
+ if @scan_options[:result_bundle]
294
+ test_result_suffix = '.test_result'
295
+ test_result_suffix.prepend("_#{reportnamer.report_count}") unless reportnamer.report_count.zero?
296
+ test_result_bundlepath = File.join(output_directory, @scan_options[:scheme]) + test_result_suffix
297
+ info[:test_result_bundlepath] = test_result_bundlepath
298
+ end
299
+ info
300
+ end
301
+
302
+ def quit_simulators
303
+ return unless @quit_simulators
304
+
305
+ Fastlane::Actions.sh("killall -9 'iPhone Simulator' 'Simulator' 'SimulatorBridge' &> /dev/null || true", log: false)
306
+ launchctl_list_count = 0
307
+ while Fastlane::Actions.sh('launchctl list | grep com.apple.CoreSimulator.CoreSimulatorService || true', log: false) != ''
308
+ break if (launchctl_list_count += 1) > 10
309
+ Fastlane::Actions.sh('launchctl remove com.apple.CoreSimulator.CoreSimulatorService &> /dev/null || true', log: false)
310
+ sleep(1)
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end