fastlane-plugin-test_center 3.14.7 → 3.15.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83032c7ae1a7fd821330d9729adcc3a6adc82a3767f7e1aa04327227bf1dfcb0
4
- data.tar.gz: 44fe3edc2e95fdcf129b6031e4e01e586c7acecba69b99e47a9d4034fff57878
3
+ metadata.gz: b7ed186518bf972a65bb1664e5843cfb5da505a93c216af4402ae2fecb6ed52f
4
+ data.tar.gz: 557cb341dbda37fb2eae18ac8f494ddff889039a95e0337a2f47122cd436c83c
5
5
  SHA512:
6
- metadata.gz: 2a50e19f593fb782828cd14ca45468f5841b1b20781701c0982c644a4cb88d9261f83ccf75eb900c562c4611baca19691e7f5d9094bb909fba11512eb0e813b1
7
- data.tar.gz: 56bb12fd6b0ed79041d98d97977b5946fd83d6a05b7f623dbb1ac6145ebf4f8da852f2f69a0766aeb11b617ad7b01c62f404d63b378e7656f5589db8d59813c0
6
+ metadata.gz: b11864d9cc3a2863c0418b70da52e879fe8c281a85eba39c6c625fc33d96b9e437d95526e5d7b93bfbaa16a86904d9577ebb5c6de2ed7139b39fcb74d3989eac
7
+ data.tar.gz: 9eb0523c3ce1114b5e39fb1dcac9169fbcf6c5b1e519b8a573b18389277102ac1368b66adb09b874ffc0cf51f29357a79808d4fcd1b890d9809da71f09ed43c9
@@ -8,7 +8,7 @@ module Fastlane
8
8
  if report_filepaths.size == 1
9
9
  FileUtils.cp(report_filepaths[0], params[:collated_report])
10
10
  else
11
- UI.verbose("collate_junit_reports with #{report_filepaths}")
11
+ verbose("collate_junit_reports with #{report_filepaths}")
12
12
  reports = report_filepaths.map { |report_filepath| REXML::Document.new(File.new(report_filepath)) }
13
13
  packages = reports.map { |r| r.root.attribute('name').value }.uniq
14
14
  combine_multiple_targets = packages.size > 1
@@ -23,7 +23,7 @@ module Fastlane
23
23
  increment_testable_tries(target_report.root, report.root)
24
24
  package = report.root.attribute('name').value
25
25
  preprocess_testsuites(report, package, combine_multiple_targets)
26
- UI.verbose("> collating last report file #{report_filepaths.last}")
26
+ verbose("> collating last report file #{report_filepaths.last}")
27
27
  report.elements.each('//testsuite') do |testsuite|
28
28
  testsuite_name = testsuite.attribute('name').value
29
29
  package_attribute = ''
@@ -32,15 +32,15 @@ module Fastlane
32
32
  end
33
33
  target_testsuite = REXML::XPath.first(target_report, "//testsuite[@name='#{testsuite_name}' #{package_attribute}]")
34
34
  if target_testsuite
35
- UI.verbose(" > collating testsuite #{testsuite_name}")
35
+ verbose(" > collating testsuite #{testsuite_name}")
36
36
  collate_testsuite(target_testsuite, testsuite)
37
- UI.verbose(" < collating testsuite #{testsuite_name}")
37
+ verbose(" < collating testsuite #{testsuite_name}")
38
38
  else
39
39
  testable = REXML::XPath.first(target_report, "//testsuites")
40
40
  testable << testsuite
41
41
  end
42
42
  end
43
- UI.verbose("< collating last report file #{report_filepaths.last}")
43
+ verbose("< collating last report file #{report_filepaths.last}")
44
44
  end
45
45
  target_report.elements.each('//testsuite') do |testsuite|
46
46
  update_testsuite_counts(testsuite)
@@ -83,12 +83,12 @@ module Fastlane
83
83
  testsuite_name = testsuite.attribute('name').value
84
84
  duplicate_testsuites = REXML::XPath.match(report, "//testsuite[@name='#{testsuite_name}']")
85
85
  if duplicate_testsuites.size > 1
86
- UI.verbose(" > flattening_duplicate_testsuites")
86
+ verbose(" > flattening_duplicate_testsuites")
87
87
  duplicate_testsuites.drop(1).each do |duplicate_testsuite|
88
88
  collate_testsuite(testsuite, duplicate_testsuite)
89
89
  duplicate_testsuite.parent.delete_element(duplicate_testsuite)
90
90
  end
91
- UI.verbose(" < flattening_duplicate_testsuites")
91
+ verbose(" < flattening_duplicate_testsuites")
92
92
  end
93
93
  update_testsuite_counts(testsuite)
94
94
  end
@@ -108,14 +108,14 @@ module Fastlane
108
108
  target_testcase = REXML::XPath.first(target_testsuite, "testcase[@name='#{name}' and @classname='#{classname}']")
109
109
  # Replace target_testcase with testcase
110
110
  if target_testcase
111
- UI.verbose(" collate_testsuite with testcase #{name}")
112
- UI.verbose(" replacing \"#{target_testcase}\" with \"#{testcase}\"")
111
+ verbose(" collate_testsuite with testcase #{name}")
112
+ verbose(" replacing \"#{target_testcase}\" with \"#{testcase}\"")
113
113
  parent = target_testcase.parent
114
114
  increment_testcase_tries(target_testcase, testcase) unless testcase.root == target_testcase.root
115
115
  parent.insert_after(target_testcase, testcase)
116
116
  parent.delete_element(target_testcase)
117
- UI.verbose("")
118
- UI.verbose(" target_testcase after replacement \"#{parent}\"")
117
+ verbose("")
118
+ verbose(" target_testcase after replacement \"#{parent}\"")
119
119
  else
120
120
  target_testsuite << testcase
121
121
  end
@@ -165,6 +165,12 @@ module Fastlane
165
165
  (value1 + value2).to_s
166
166
  end
167
167
 
168
+ def self.verbose(message)
169
+ return if ENV.fetch('COLLATE_JUNIT_REPORTS_VERBOSITY', 1).to_i.zero?
170
+
171
+ UI.verbose(message)
172
+ end
173
+
168
174
  #####################################################
169
175
  # @!group Documentation
170
176
  #####################################################
@@ -13,12 +13,7 @@ module Fastlane
13
13
 
14
14
  class MultiScanAction < Action
15
15
  def self.run(params)
16
- params[:quit_simulators] = params._values[:force_quit_simulator] if params._values[:force_quit_simulator]
17
- if params[:try_count] < 1
18
- UI.important('multi_scan will not test any if :try_count < 0, setting to 1')
19
- params[:try_count] = 1
20
- end
21
-
16
+ update_interdependent_params(params)
22
17
  strip_leading_and_trailing_whitespace_from_output_types(params)
23
18
 
24
19
  warn_of_xcode11_result_bundle_incompatability(params)
@@ -53,6 +48,14 @@ module Fastlane
53
48
  summary
54
49
  end
55
50
 
51
+ def self.update_interdependent_params(params)
52
+ params[:quit_simulators] = params._values[:force_quit_simulator] if params._values[:force_quit_simulator]
53
+ if params[:try_count] < 1
54
+ UI.important('multi_scan will not test any if :try_count < 0, setting to 1')
55
+ params[:try_count] = 1
56
+ end
57
+ end
58
+
56
59
  def self.warn_of_parallelism_with_circle_ci(params)
57
60
  if params[:parallel_testrun_count] > 1 && Helper.is_circle_ci?
58
61
  UI.important("Warning: problems have occurreed when running parallel simulators on Circle CI.")
@@ -456,6 +459,22 @@ module Fastlane
456
459
  is_string: false,
457
460
  default_value: true
458
461
  ),
462
+ FastlaneCore::ConfigItem.new(
463
+ key: :override_scan_options_block,
464
+ description: 'A block invoked with a Hash of the scan options that will be used when test run is about to start. This allows your code to modify the arguments that will be sent to scan',
465
+ optional: true,
466
+ is_string: false,
467
+ default_value: nil,
468
+ type: Proc
469
+ ),
470
+ FastlaneCore::ConfigItem.new(
471
+ key: :reuse_simulators_for_parallel_testruns,
472
+ description: 'Find simulators (or clone new ones) that match the requested device for the parallel test runs. This option sets :pre_delete_cloned_simulators to false',
473
+ optional: true,
474
+ is_string: false,
475
+ type: Boolean,
476
+ default_value: false
477
+ ),
459
478
  FastlaneCore::ConfigItem.new(
460
479
  key: :testrun_completed_block,
461
480
  description: 'A block invoked each time a test run completes. When combined with :parallel_testrun_count, will be called separately in each child process. Return a Hash with :continue set to false to stop retrying tests, or :only_testing to change which tests will be run in the next try',
@@ -463,6 +482,14 @@ module Fastlane
463
482
  is_string: false,
464
483
  default_value: nil,
465
484
  type: Proc
485
+ ),
486
+ FastlaneCore::ConfigItem.new(
487
+ key: :simulator_started_callback,
488
+ description: 'A block invoked after the iOS simulators have started',
489
+ optional: true,
490
+ is_string: false,
491
+ default_value: nil,
492
+ type: Proc
466
493
  )
467
494
  ]
468
495
  end
@@ -492,6 +519,10 @@ module Fastlane
492
519
  }
493
520
  end
494
521
 
522
+ sim_callback = lambda do |simulator_device_udid|
523
+ puts \"Start streaming system log for device \#{simulator_device_udid}\"
524
+ end
525
+
495
526
  multi_scan(
496
527
  project: File.absolute_path('../AtomicBoy/AtomicBoy.xcodeproj'),
497
528
  scheme: 'AtomicBoy',
@@ -499,7 +530,8 @@ module Fastlane
499
530
  batch_count: 4,
500
531
  fail_build: false,
501
532
  parallel_testrun_count: 4,
502
- testrun_completed_block: test_run_block
533
+ testrun_completed_block: test_run_block,
534
+ simulator_started_callback: sim_callback
503
535
  )
504
536
  ",
505
537
  "
@@ -16,7 +16,7 @@ module Fastlane
16
16
  result_bundle_object_raw = sh("xcrun xcresulttool get --path #{xcresult_path.shellescape} --format json", print_command: false, print_command_output: false)
17
17
  result_bundle_object = JSON.parse(result_bundle_object_raw)
18
18
 
19
- # Parses JSON into ActionsInvocationRecord to find a list of all ids for ActionTestPlanRunSummaries
19
+ # Parses JSON into ActionsInvocationRecord to find a list of all ids for ActionTestPlanRunSummaries.
20
20
  actions_invocation_record = Trainer::XCResult::ActionsInvocationRecord.new(result_bundle_object)
21
21
  test_refs = actions_invocation_record.actions.map do |action|
22
22
  action.action_result.tests_ref
@@ -33,14 +33,18 @@ module Fastlane
33
33
  testable_summaries = all_summaries.map(&:testable_summaries).flatten
34
34
  failed = []
35
35
  passing = []
36
+ skipped = []
36
37
  failure_details = {}
37
- rows = testable_summaries.map do |testable_summary|
38
+ testable_summaries.map do |testable_summary|
39
+ target_name = testable_summary.target_name
38
40
  all_tests = testable_summary.all_tests.flatten
39
41
  all_tests.each do |t|
40
42
  if t.test_status == 'Success'
41
- passing << "#{t.parent.name}/#{t.identifier}"
43
+ passing << "#{target_name}/#{t.identifier.sub('()', '')}"
44
+ elsif t.test_status == 'Skipped'
45
+ skipped << "#{target_name}/#{t.identifier.sub('()', '')}"
42
46
  else
43
- test_identifier = "#{t.parent.name}/#{t.identifier}"
47
+ test_identifier = "#{target_name}/#{t.identifier.sub('()', '')}"
44
48
  failed << test_identifier
45
49
  failure = t.find_failure(failures)
46
50
  if failure
@@ -54,6 +58,7 @@ module Fastlane
54
58
  {
55
59
  failed: failed.uniq,
56
60
  passing: passing.uniq,
61
+ skipped: skipped.uniq,
57
62
  failure_details: failure_details
58
63
  }
59
64
  end
@@ -63,7 +68,7 @@ module Fastlane
63
68
  #####################################################
64
69
 
65
70
  def self.description
66
- "☑️ Retrieves the failing and passing tests as reportedn an xcresult bundle"
71
+ "☑️ Retrieves the failing, passing, and skipped tests as reported in a xcresult bundle"
67
72
  end
68
73
 
69
74
 
@@ -84,7 +89,8 @@ module Fastlane
84
89
  def self.return_value
85
90
  "A Hash with information about the test results:\r\n" \
86
91
  "failed: an Array of the failed test identifiers\r\n" \
87
- "passing: an Array of the passing test identifiers\r\n"
92
+ "passing: an Array of the passing test identifiers\r\n" \
93
+ "skipped: an Array of the skipped test identifiers\r\n"
88
94
  end
89
95
 
90
96
  def self.authors
@@ -139,25 +139,32 @@ module Fastlane
139
139
  "
140
140
  require 'fastlane/actions/scan'
141
141
 
142
- UI.important(
143
- 'example: ' \\
144
- 'get list of tests that are referenced from an xctestrun file'
145
- )
146
- # build the tests so that we have a xctestrun file to parse
147
- scan(
148
- build_for_testing: true,
149
- workspace: File.absolute_path('../AtomicBoy/AtomicBoy.xcworkspace'),
150
- scheme: 'AtomicBoy'
151
- )
152
-
153
- # find the xctestrun file
154
- derived_data_path = Scan.config[:derived_data_path]
155
- xctestrun_file = Dir.glob(\"\#{derived_data_path}/Build/Products/*.xctestrun\").first
142
+ lane :split_tests do
143
+ scan(
144
+ build_for_testing: true,
145
+ workspace: File.absolute_path('../AtomicBoy/AtomicBoy.xcworkspace'),
146
+ scheme: 'AtomicBoy'
147
+ )
148
+ derived_data_path = Scan.config[:derived_data_path]
149
+ xctestrun_file = Dir.glob(\"\#{derived_data_path}/Build/Products/*.xctestrun\").first
150
+ tests = tests_from_xctestrun(xctestrun: xctestrun_file).values.flatten.shuffle
151
+ slice_size = (tests.size/4.0).ceil
152
+ tests.each_slice(slice_size).each_with_index do |inner_array, index|
153
+ File.write(\"test_output/batch\#{index}.txt\", inner_array.join(','))
154
+ end
155
+ end
156
156
 
157
- # get the tests from the xctestrun file
158
- tests = tests_from_xctestrun(xctestrun: xctestrun_file)
159
- UI.header('xctestrun file contains the following tests')
160
- tests.values.flatten.each { |test_identifier| puts test_identifier }
157
+ lane :run_split_tests do |options|
158
+ batch_file = File.join('test_output', \"batch\#{options[:batch_index]}.txt\")
159
+ only_testing = File.read(batch_file).split(',')
160
+ multi_scan(
161
+ workspace: File.absolute_path('../AtomicBoy/AtomicBoy.xcworkspace'),
162
+ scheme: 'AtomicBoy',
163
+ try_count: 3,
164
+ fail_build: false,
165
+ only_testing: only_testing
166
+ )
167
+ end
161
168
  "
162
169
  ]
163
170
  end
@@ -1,6 +1,19 @@
1
1
  module TestCenter
2
2
  module Helper
3
3
  module HtmlTestReport
4
+
5
+ def self.verbose(message)
6
+ return if ENV.fetch('COLLATE_HTML_REPORTS_VERBOSITY', 1).to_i.zero?
7
+
8
+ FastlaneCore::UI.verbose(message)
9
+ end
10
+
11
+ def self.error(message)
12
+ return if ENV.fetch('COLLATE_HTML_REPORTS_VERBOSITY', 1).to_i.zero?
13
+
14
+ FastlaneCore::UI.error(message)
15
+ end
16
+
4
17
  class Report
5
18
  require 'rexml/formatters/transitive'
6
19
 
@@ -24,16 +37,16 @@ module TestCenter
24
37
  def collate_report(report)
25
38
  testsuites.each(&:remove_duplicate_testcases)
26
39
  report.testsuites.each(&:remove_duplicate_testcases)
27
- FastlaneCore::UI.verbose("TestCenter::Helper::HtmlTestReport::Report.collate_report to report:\n\t#{@root}")
40
+ HtmlTestReport.verbose("TestCenter::Helper::HtmlTestReport::Report.collate_report to report:\n\t#{@root}")
28
41
  report.testsuites.each do |given_testsuite|
29
42
  existing_testsuite = testsuite_with_title(given_testsuite.title)
30
43
  if existing_testsuite.nil?
31
- FastlaneCore::UI.verbose("\tadding testsuite\n\t\t#{given_testsuite}")
44
+ HtmlTestReport.verbose("\tadding testsuite\n\t\t#{given_testsuite}")
32
45
  add_testsuite(given_testsuite)
33
46
  else
34
- FastlaneCore::UI.verbose("\tcollating testsuite\n\t\t#{given_testsuite.root}")
47
+ HtmlTestReport.verbose("\tcollating testsuite\n\t\t#{given_testsuite.root}")
35
48
  existing_testsuite.collate_testsuite(given_testsuite)
36
- FastlaneCore::UI.verbose("\tafter collation exiting testsuite\n\t\t#{existing_testsuite.root}")
49
+ HtmlTestReport.verbose("\tafter collation exiting testsuite\n\t\t#{existing_testsuite.root}")
37
50
  end
38
51
  end
39
52
  update_test_count
@@ -210,15 +223,15 @@ module TestCenter
210
223
  given_testcases.each do |given_testcase|
211
224
  existing_testcase = testcase_with_title(given_testcase.title)
212
225
  if existing_testcase.nil?
213
- FastlaneCore::UI.verbose("\t\tadding testcase\n\t\t\t#{given_testcase.root}")
226
+ HtmlTestReport.verbose("\t\tadding testcase\n\t\t\t#{given_testcase.root}")
214
227
  unless given_testcase.passing?
215
- FastlaneCore::UI.verbose("\t\t\twith failure:\n\t\t\t\t#{given_testcase.failure_details}")
228
+ HtmlTestReport.verbose("\t\t\twith failure:\n\t\t\t\t#{given_testcase.failure_details}")
216
229
  end
217
230
  add_testcase(given_testcase)
218
231
  else
219
- FastlaneCore::UI.verbose("\t\tupdating testcase\n\t\t\t#{existing_testcase.root}")
232
+ HtmlTestReport.verbose("\t\tupdating testcase\n\t\t\t#{existing_testcase.root}")
220
233
  unless given_testcase.passing?
221
- FastlaneCore::UI.verbose("\t\t\twith failure:\n\t\t\t\t#{given_testcase.failure_details}")
234
+ HtmlTestReport.verbose("\t\t\twith failure:\n\t\t\t\t#{given_testcase.failure_details}")
222
235
  end
223
236
  existing_testcase.update_testcase(given_testcase)
224
237
  end
@@ -275,7 +288,7 @@ module TestCenter
275
288
  color = row_color
276
289
  failure = failure_details
277
290
  if failure.nil? && !passing?
278
- FastlaneCore::UI.error("\t\t\t\tupdating failing test case that does not have failure_details")
291
+ HtmlTestReport.error("\t\t\t\tupdating failing test case that does not have failure_details")
279
292
  end
280
293
  parent = @root.parent
281
294
 
@@ -283,7 +296,7 @@ module TestCenter
283
296
 
284
297
  new_failure = testcase.failure_details
285
298
  if new_failure && testcase.passing?
286
- FastlaneCore::UI.error("\t\t\t\tswapping passing failing test case that _does_have_ failure_details")
299
+ HtmlTestReport.error("\t\t\t\tswapping passing failing test case that _does_have_ failure_details")
287
300
  end
288
301
 
289
302
  parent.replace_child(@root, testcase.root)
@@ -18,6 +18,17 @@ module FastlaneCore
18
18
  self.name = newname
19
19
  end
20
20
 
21
+ def disable_hardware_keyboard
22
+ UI.verbose("Disabling hardware keyboard for #{self.udid}")
23
+ plist_filepath = File.expand_path("~/Library/Preferences/com.apple.iphonesimulator.plist")
24
+ keyboard_pref_key = ":DevicePreferences:#{self.udid}:ConnectHardwareKeyboard"
25
+
26
+ command = "/usr/libexec/PlistBuddy -c \"Set #{keyboard_pref_key} false\" #{plist_filepath} 2>/dev/null || "
27
+ command << "/usr/libexec/PlistBuddy -c \"Add #{keyboard_pref_key} bool false\" #{plist_filepath}"
28
+
29
+ `#{command}`
30
+ end
31
+
21
32
  def boot
22
33
  return unless is_simulator
23
34
  return unless os_type == "iOS"
@@ -7,57 +7,6 @@ module TestCenter
7
7
  @retrying_scan_helper = RetryingScanHelper.new(@options)
8
8
  end
9
9
 
10
- # :nocov:
11
- def scan_config
12
- Scan.config
13
- end
14
-
15
- def scan_cache
16
- Scan.cache
17
- end
18
- # :nocov:
19
-
20
- def prepare_scan_config
21
- # this allows multi_scan's `destination` option to be picked up by `scan`
22
- scan_config._values.delete(:device)
23
- ENV.delete('SCAN_DEVICE')
24
- scan_config._values.delete(:devices)
25
- ENV.delete('SCAN_DEVICES')
26
- # this prevents double -resultBundlePath args to xcodebuild
27
- if ReportNameHelper.includes_xcresult?(@options[:output_types])
28
- scan_config._values.delete(:result_bundle)
29
- ENV.delete('SCAN_RESULT_BUNDLE')
30
- end
31
- scan_config._values.delete(:skip_testing)
32
- scan_cache.clear
33
- end
34
-
35
- def update_scan_options
36
- valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
37
- scan_options = @options.select { |k,v| valid_scan_keys.include?(k) }
38
- .merge(@retrying_scan_helper.scan_options)
39
-
40
- prepare_scan_config
41
- scan_options[:build_for_testing] = false
42
- scan_options.delete(:skip_testing)
43
- FastlaneCore::UI.verbose("retrying_scan #update_scan_options")
44
- scan_options.each do |k,v|
45
- next if v.nil?
46
-
47
- scan_config.set(k,v) unless v.nil?
48
- FastlaneCore::UI.verbose("\tSetting #{k.to_s} to #{v}")
49
- end
50
- if @options[:scan_devices_override]
51
- scan_device_names = @options[:scan_devices_override].map { |device| device.name }
52
- FastlaneCore::UI.verbose("\tSetting Scan.devices to #{scan_device_names}")
53
- if Scan.devices
54
- Scan.devices.replace(@options[:scan_devices_override])
55
- else
56
- Scan.devices = @options[:scan_devices_override]
57
- end
58
- end
59
- end
60
-
61
10
  # :nocov:
62
11
  def self.run(options)
63
12
  RetryingScan.new(options).run
@@ -68,12 +17,6 @@ module TestCenter
68
17
  try_count = @options[:try_count] || 1
69
18
  begin
70
19
  @retrying_scan_helper.before_testrun
71
- update_scan_options
72
-
73
- values = scan_config.values(ask: false)
74
- values[:xcode_path] = File.expand_path("../..", FastlaneCore::Helper.xcode_path)
75
- ScanHelper.print_scan_parameters(values)
76
-
77
20
  Scan::Runner.new.run
78
21
  @retrying_scan_helper.after_testrun
79
22
  true
@@ -23,9 +23,78 @@ module TestCenter
23
23
  delete_xcresults # has to be performed _after_ moving a *.test_result
24
24
  quit_simulator
25
25
  set_json_env
26
+ set_scan_config
26
27
  print_starting_scan_message
27
28
  end
28
29
 
30
+ def set_scan_config
31
+ valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
32
+ new_scan_options = @options.select { |k,v| valid_scan_keys.include?(k) }
33
+ .merge(scan_options)
34
+
35
+ prepare_scan_config
36
+ new_scan_options[:build_for_testing] = false
37
+ new_scan_options.delete(:skip_testing)
38
+
39
+ new_scan_options = send_callback_override_scan_options_block(new_scan_options)
40
+
41
+ FastlaneCore::UI.verbose("retrying_scan #update_scan_options")
42
+ new_scan_options.each do |k,v|
43
+ next if v.nil?
44
+
45
+ scan_config.set(k,v) unless v.nil?
46
+ FastlaneCore::UI.verbose("\tSetting #{k.to_s} to #{v}")
47
+ end
48
+ if @options[:scan_devices_override]
49
+ scan_device_names = @options[:scan_devices_override].map { |device| device.name }
50
+ FastlaneCore::UI.verbose("\tSetting Scan.devices to #{scan_device_names}")
51
+ if Scan.devices
52
+ Scan.devices.replace(@options[:scan_devices_override])
53
+ else
54
+ Scan.devices = @options[:scan_devices_override]
55
+ end
56
+ end
57
+
58
+ values = scan_config.values(ask: false)
59
+ values[:xcode_path] = File.expand_path("../..", FastlaneCore::Helper.xcode_path)
60
+ ScanHelper.print_scan_parameters(values)
61
+ end
62
+
63
+ # :nocov:
64
+ def scan_config
65
+ Scan.config
66
+ end
67
+
68
+ def scan_cache
69
+ Scan.cache
70
+ end
71
+ # :nocov:
72
+
73
+ def prepare_scan_config
74
+ # this allows multi_scan's `destination` option to be picked up by `scan`
75
+ scan_config._values.delete(:device)
76
+ ENV.delete('SCAN_DEVICE')
77
+ scan_config._values.delete(:devices)
78
+ ENV.delete('SCAN_DEVICES')
79
+ # this prevents double -resultBundlePath args to xcodebuild
80
+ if ReportNameHelper.includes_xcresult?(@options[:output_types])
81
+ scan_config._values.delete(:result_bundle)
82
+ ENV.delete('SCAN_RESULT_BUNDLE')
83
+ end
84
+ scan_config._values.delete(:skip_testing)
85
+ scan_cache.clear
86
+ end
87
+
88
+ def send_callback_override_scan_options_block(new_scan_options)
89
+ return new_scan_options unless @options[:override_scan_options_block]
90
+
91
+ callback_result = @options[:override_scan_options_block].call(new_scan_options)
92
+ if callback_result.kind_of?(Hash)
93
+ return callback_result
94
+ end
95
+ new_scan_options
96
+ end
97
+
29
98
  def quit_simulator
30
99
  return unless @options[:quit_simulators]
31
100
 
@@ -98,11 +167,14 @@ module TestCenter
98
167
  FastlaneCore::UI.important('Disabling -quiet as failing tests cannot be found with it enabled.')
99
168
  xcargs.gsub!('-quiet', '')
100
169
  end
101
- xcargs.gsub!(/-parallel-testing-enabled(=|\s+)(YES|NO)/, '')
170
+ if FastlaneCore::Helper.xcode_at_least?(10)
171
+ xcargs.gsub!(/-parallel-testing-enabled(=|\s+)(YES|NO)/, '')
172
+ xcargs << " -parallel-testing-enabled NO "
173
+ end
102
174
  retrying_scan_options = @reportnamer.scan_options.merge(
103
175
  {
104
176
  output_directory: output_directory,
105
- xcargs: "#{xcargs} -parallel-testing-enabled NO "
177
+ xcargs: xcargs
106
178
  }
107
179
  )
108
180
  if @reportnamer.includes_xcresult?
@@ -21,6 +21,8 @@ module TestCenter
21
21
  update_options_to_use_xcresult_output
22
22
  end
23
23
  @batch_count = 1 # default count. Will be updated by setup_testcollector
24
+ @options[:parallel_testrun_count] ||= 1
25
+ @initial_parallel_testrun_count = @options[:parallel_testrun_count]
24
26
  setup_testcollector
25
27
  setup_logcollection
26
28
  FastlaneCore::UI.verbose("< done in TestCenter::Helper::MultiScanManager.initialize")
@@ -67,6 +69,7 @@ module TestCenter
67
69
  @test_collector = TestCollector.new(@options)
68
70
  @options.reject! { |key| %i[testplan].include?(key) }
69
71
  @batch_count = @test_collector.batches.size
72
+ @options[:parallel_testrun_count] = @initial_parallel_testrun_count
70
73
  tests = @test_collector.batches.flatten
71
74
  if tests.size < @options[:parallel_testrun_count].to_i
72
75
  FastlaneCore::UI.important(":parallel_testrun_count greater than the number of tests (#{tests.size}). Reducing to that number.")
@@ -184,6 +187,8 @@ module TestCenter
184
187
  options = @options.reject { |key| %i[device devices force_quit_simulator].include?(key) }
185
188
  options[:try_count] = 1
186
189
 
190
+ SimulatorHelper.call_simulator_started_callback(@options, Scan.devices)
191
+
187
192
  tests_passed = RetryingScan.run(options)
188
193
  @options[:try_count] -= 1
189
194
 
@@ -229,6 +234,11 @@ module TestCenter
229
234
  pool_options[:test_batch_results] = test_batch_results
230
235
  pool_options[:xctestrun] = @test_collector.xctestrun_path
231
236
 
237
+ serial_test_batches = (@options.fetch(:parallel_testrun_count, 1) == 1)
238
+ if serial_test_batches && !@options[:invocation_based_tests]
239
+ SimulatorHelper.call_simulator_started_callback(@options, Scan.devices)
240
+ end
241
+
232
242
  pool = TestBatchWorkerPool.new(pool_options)
233
243
  pool.setup_workers
234
244
 
@@ -4,6 +4,8 @@ module TestCenter
4
4
  class SimulatorHelper
5
5
  def initialize(options)
6
6
  @options = options
7
+ # TODO: add byebug to make sure we're mocking this
8
+ @all_simulators = FastlaneCore::DeviceManager.simulators('iOS')
7
9
  end
8
10
 
9
11
  def setup
@@ -12,6 +14,38 @@ module TestCenter
12
14
  end
13
15
  end
14
16
 
17
+ def parallel_destination_simulators
18
+ remaining_desired_simulators = @options[:parallel_testrun_count] || 0
19
+
20
+ simulators = []
21
+ if @options[:reuse_simulators_for_parallel_testruns]
22
+ matching_simulators = find_matching_destination_simulators(remaining_desired_simulators)
23
+ remaining_desired_simulators -= matching_simulators.size
24
+ (0...matching_simulators.size).each do |s|
25
+ simulators << [matching_simulators[s]]
26
+ end
27
+ end
28
+
29
+ if remaining_desired_simulators > 0
30
+ simulators.concat(clone_destination_simulators(remaining_desired_simulators))
31
+ end
32
+ simulators
33
+ end
34
+
35
+ def find_matching_destination_simulators(remaining_desired_simulators)
36
+ destination = Scan.config[:destination].clone.first
37
+
38
+ desired_device = @all_simulators.find do |simulator|
39
+ match = destination.match(/id=(?<udid>[^,]+)/)
40
+ match && match[:udid] == simulator.udid
41
+ end
42
+
43
+ matching_simulators = @all_simulators.find_all do |simulator|
44
+ desired_device.os_version == simulator.os_version && simulator.name =~ /#{Regexp.escape(desired_device.name)} Clone \d #{self.class.name}<[^>]+>/
45
+ end
46
+ matching_simulators.first(remaining_desired_simulators)
47
+ end
48
+
15
49
  def simulator_matches_destination(simulator, destination)
16
50
  match = destination.match(/id=(?<udid>[^,]+)/)
17
51
  if match
@@ -27,12 +61,12 @@ module TestCenter
27
61
  found_match
28
62
  end
29
63
 
30
- def clone_destination_simulators
64
+ def clone_destination_simulators(remaining_desired_simulators)
31
65
  cloned_simulators = []
32
66
 
33
- run_count = @options[:parallel_testrun_count] || 0
67
+ run_count = remaining_desired_simulators
34
68
  destinations = Scan.config[:destination].clone
35
- original_simulators = FastlaneCore::DeviceManager.simulators('iOS').find_all do |simulator|
69
+ original_simulators = @all_simulators.find_all do |simulator|
36
70
  found_simulator = destinations.find do |destination|
37
71
  simulator_matches_destination(simulator, destination)
38
72
  end
@@ -63,6 +97,15 @@ module TestCenter
63
97
  simulator.delete if /#{self.class.name}<\d+>/ =~ simulator.name
64
98
  end
65
99
  end
100
+
101
+ def self.call_simulator_started_callback(options, devices)
102
+ return unless options[:simulator_started_callback]
103
+ return unless options[:platform] == :ios_simulator
104
+
105
+ devices.each do |device|
106
+ options[:simulator_started_callback].call(device.udid)
107
+ end
108
+ end
66
109
  end
67
110
  end
68
111
  end
@@ -23,17 +23,23 @@ module TestCenter
23
23
 
24
24
  @simhelper = SimulatorHelper.new(
25
25
  parallel_testrun_count: @options[:parallel_testrun_count],
26
- pre_delete_cloned_simulators: @options.fetch(:pre_delete_cloned_simulators, true)
26
+ pre_delete_cloned_simulators: @options.fetch(:pre_delete_cloned_simulators, true),
27
+ reuse_simulators_for_parallel_testruns: @options[:reuse_simulators_for_parallel_testruns] || false
27
28
  )
28
29
  @simhelper.setup
29
- @clones = @simhelper.clone_destination_simulators
30
+ @clones = @simhelper.parallel_destination_simulators
30
31
  main_pid = Process.pid
31
- at_exit do
32
- clean_up_cloned_simulators(@clones) if Process.pid == main_pid
32
+ unless @options[:reuse_simulators_for_parallel_testruns]
33
+ at_exit do
34
+ clean_up_cloned_simulators(@clones) if Process.pid == main_pid
35
+ end
33
36
  end
34
37
  # boot all the simulators _before_ calling `xcodebuilt test` to avoid
35
38
  # testmanagerd connection failures.
39
+ @clones.flatten.each(&:shutdown)
40
+ @clones.flatten.each(&:disable_hardware_keyboard)
36
41
  @clones.flatten.each(&:boot)
42
+ SimulatorHelper.call_simulator_started_callback(@options, @clones.flatten)
37
43
  @clones
38
44
  end
39
45
 
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TestCenter
3
- VERSION = "3.14.7"
3
+ VERSION = "3.15.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-test_center
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.14.7
4
+ version: 3.15.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lyndsey Ferguson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-23 00:00:00.000000000 Z
11
+ date: 2021-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json