Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 9397c67

Browse files
Merge pull request #320 from lyndsey-ferguson/ga-3.15.0
Ga 3.15.0
2 parents 0646151 + bad3a3f commit 9397c67

File tree

16 files changed

+723
-217
lines changed

16 files changed

+723
-217
lines changed

docs/feature_details/multi_scan.md

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,6 @@ If you have a large number of tests, and you want to inspect the overall status
3333

3434
<center><img src="./images/retrying_tests.png" alt="retrying_failed_tests" /></center>
3535

36-
> **Note**: I'm making the control of subsequent runs of `multi_scan` via the `:testrun_complete_block` available to Supporters first to show them appreciation. I'll open it up to everyone else on October 5th, 2020.
37-
>
38-
> Interested in joining? Click [here ♥️](https://github.com/sponsors/lyndsey-ferguson) and select a tier that gives you early access to new features.
39-
>
40-
> **Bonus**: if your organization (👨‍👩‍👧‍) becomes a Sponsor, every member of that org gets that same early access!
41-
42-
4336
## Better Results Reporting
4437

4538
Do you have multiple test targets and the normal operation of `:scan` is providing you a junit report that implies that all the tests ran in just one test target? Don't worry, `:multi_scan` has fixed that in v3.14.0 by adding a `package` attribute to each testsuite to let you which test target the test was run in. `:multi_scan` can handle JUnit, HTML, JSON, and Apple's `xcresult` bundles.
@@ -53,20 +46,6 @@ Do you have multiple test targets and the normal operation of `:scan` is providi
5346
>
5447
> 3. The `result_bundle` option used to provide `.result_bundle` files. `scan` recently changed that file extension to `.xcresult` to match what Apple is naming the file, and this broke builds expecting the `.result_bundle` extension. As a fix, `multi_scan` creates a `.result_bundle` symbolic link to the `.xcresult` bundle.
5548
56-
****
57-
58-
> **Note**: I'm making the following features available to the plugin's Supporters first. I'll open it up to everyone else on December 18th, 2020:
59-
> - A new option to pass in a callback when a simulator is started before iOS tests, `:simulator_started_callback`, that will be sent the device's udid.
60-
> - A new option to allow users to "reuse" a cloned simulator in parallel test runs via the `:reuse_simulators_for_parallel_testruns` option. This improves the speed of startup because apps do not have to be re-installed.
61-
> - Hardware keyboards are disabled for cloned iOS Simulators.
62-
> - A new option, `:override_scan_options_block`, to pass in a callback to override the options that will be passed into scan just before it is called for those special cases.
63-
>
64-
> Interested in joining? Click [here ♥️](https://github.com/sponsors/lyndsey-ferguson) and select a tier that gives you early access to new features.
65-
>
66-
> **Bonus**: if your organization (👨<U+200D>👩<U+200D>👧<U+200D>) becomes a Sponsor, every member of that org gets that same early access!
67-
68-
69-
7049

7150
## Examples
7251

@@ -111,7 +90,9 @@ multi_scan(
11190
batch_count: 4,
11291
fail_build: false,
11392
parallel_testrun_count: 4,
114-
testrun_completed_block: test_run_block
93+
testrun_completed_block: test_run_block,
94+
simulator_started_callback: sim_callback,
95+
override_scan_options_block: override_scan_options_callback
11596
)
11697

11798
```
@@ -174,8 +155,10 @@ In addition to the parameters supported by [`scan`](https://docs.fastlane.tools/
174155
|collate_reports|Whether or not to collate the reports generated by multiple retries, batches, and parallel test runs|true|
175156
|parallel_testrun_count|Run simulators each batch of tests and/or each test target in parallel on its own Simulator|1|
176157
|pre_delete_cloned_simulators|Delete left over cloned simulators before running a parallel testrun|true|
177-
|_reuse_simulators_for_parallel_testruns_<br/>♥️ Coming Dec 18th, 2020|Find simulators (or clone new ones) that match the requested device for the parallel test runs|false|
178-
|_override_scan_options_block_<br/>♥️ Coming Dec 18th, 2020|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||
158+
|reuse_simulators_for_parallel_testruns|Find simulators (or clone new ones) that match the requested device for the parallel test runs|false|
159+
|override_scan_options_block|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||
160+
|reuse_simulators_for_parallel_testruns|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|false|
161+
|override_scan_options_block|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||
179162
|testrun_completed_block|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||
180-
|_simulator_started_callback_<br/>♥️ Coming Dec 18th, 2020|A block invoked after the iOS simulators have started||
163+
|simulator_started_callback|A block invoked after the iOS simulators have started||
181164
<!-- multi_scan parameters: end -->

lib/fastlane/plugin/test_center/actions/multi_scan.rb

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,7 @@ module Actions
1313

1414
class MultiScanAction < Action
1515
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)
2217
strip_leading_and_trailing_whitespace_from_output_types(params)
2318

2419
warn_of_xcode11_result_bundle_incompatability(params)
@@ -53,6 +48,14 @@ def self.run(params)
5348
summary
5449
end
5550

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+
5659
def self.warn_of_parallelism_with_circle_ci(params)
5760
if params[:parallel_testrun_count] > 1 && Helper.is_circle_ci?
5861
UI.important("Warning: problems have occurreed when running parallel simulators on Circle CI.")
@@ -456,13 +459,37 @@ def self.available_options
456459
is_string: false,
457460
default_value: true
458461
),
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+
),
459478
FastlaneCore::ConfigItem.new(
460479
key: :testrun_completed_block,
461480
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',
462481
optional: true,
463482
is_string: false,
464483
default_value: nil,
465484
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
466493
)
467494
]
468495
end
@@ -492,14 +519,19 @@ def self.example_code
492519
}
493520
end
494521
522+
sim_callback = lambda do |simulator_device_udid|
523+
puts \"Start streaming system log for device \#{simulator_device_udid}\"
524+
end
525+
495526
multi_scan(
496527
project: File.absolute_path('../AtomicBoy/AtomicBoy.xcodeproj'),
497528
scheme: 'AtomicBoy',
498529
try_count: 3,
499530
batch_count: 4,
500531
fail_build: false,
501532
parallel_testrun_count: 4,
502-
testrun_completed_block: test_run_block
533+
testrun_completed_block: test_run_block,
534+
simulator_started_callback: sim_callback
503535
)
504536
",
505537
"

lib/fastlane/plugin/test_center/helper/html_test_report.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ def self.verbose(message)
88
FastlaneCore::UI.verbose(message)
99
end
1010

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+
1117
class Report
1218
require 'rexml/formatters/transitive'
1319

lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ def rename(newname)
1818
self.name = newname
1919
end
2020

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+
2132
def boot
2233
return unless is_simulator
2334
return unless os_type == "iOS"

lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -7,57 +7,6 @@ def initialize(options = {})
77
@retrying_scan_helper = RetryingScanHelper.new(@options)
88
end
99

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-
6110
# :nocov:
6211
def self.run(options)
6312
RetryingScan.new(options).run
@@ -68,12 +17,6 @@ def run
6817
try_count = @options[:try_count] || 1
6918
begin
7019
@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-
7720
Scan::Runner.new.run
7821
@retrying_scan_helper.after_testrun
7922
true

lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,78 @@ def before_testrun
2323
delete_xcresults # has to be performed _after_ moving a *.test_result
2424
quit_simulator
2525
set_json_env
26+
set_scan_config
2627
print_starting_scan_message
2728
end
2829

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+
2998
def quit_simulator
3099
return unless @options[:quit_simulators]
31100

lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ def initialize(multi_scan_options)
2121
update_options_to_use_xcresult_output
2222
end
2323
@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]
2426
setup_testcollector
2527
setup_logcollection
2628
FastlaneCore::UI.verbose("< done in TestCenter::Helper::MultiScanManager.initialize")
@@ -67,6 +69,7 @@ def setup_testcollector
6769
@test_collector = TestCollector.new(@options)
6870
@options.reject! { |key| %i[testplan].include?(key) }
6971
@batch_count = @test_collector.batches.size
72+
@options[:parallel_testrun_count] = @initial_parallel_testrun_count
7073
tests = @test_collector.batches.flatten
7174
if tests.size < @options[:parallel_testrun_count].to_i
7275
FastlaneCore::UI.important(":parallel_testrun_count greater than the number of tests (#{tests.size}). Reducing to that number.")
@@ -184,6 +187,8 @@ def run_tests_through_single_try
184187
options = @options.reject { |key| %i[device devices force_quit_simulator].include?(key) }
185188
options[:try_count] = 1
186189

190+
SimulatorHelper.call_simulator_started_callback(@options, Scan.devices)
191+
187192
tests_passed = RetryingScan.run(options)
188193
@options[:try_count] -= 1
189194

@@ -229,6 +234,11 @@ def run_test_batches
229234
pool_options[:test_batch_results] = test_batch_results
230235
pool_options[:xctestrun] = @test_collector.xctestrun_path
231236

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+
232242
pool = TestBatchWorkerPool.new(pool_options)
233243
pool.setup_workers
234244

0 commit comments

Comments
 (0)