fastlane-plugin-test_center 3.14.6 → 3.15.2
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 +4 -4
- data/lib/fastlane/plugin/test_center/actions/collate_junit_reports.rb +17 -11
- data/lib/fastlane/plugin/test_center/actions/multi_scan.rb +39 -7
- data/lib/fastlane/plugin/test_center/actions/tests_from_xcresult.rb +5 -4
- data/lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb +25 -18
- data/lib/fastlane/plugin/test_center/helper/html_test_report.rb +23 -10
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb +11 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb +0 -53
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb +74 -2
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +10 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb +46 -3
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb +10 -4
- data/lib/fastlane/plugin/test_center/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7803bf468642bcd0cb4f690b348d9849e604a63670efc0e4f270fec079579013
|
|
4
|
+
data.tar.gz: 1176ee96e31ace7572b053b2a955447aaf0ae94930167b178e2e83c03ef39ed5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b19221fc40f6ed5582d828424f24ee715cc9208cd3a592bee8def42ad56068c6c66d2fe7689efc525608aeb17d07d04403bb74029fb17d94763815cc03a5f5b3
|
|
7
|
+
data.tar.gz: dab38ff12ecc8d5f71a0ea4c617ecb796ff34a51c62740c7d982e00cac7ad2cb008c7e810ca039a49e574d728f0199fcdaa7e52e7d2c7080e0ea9239e5899df5
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
+
verbose(" > collating testsuite #{testsuite_name}")
|
|
36
36
|
collate_testsuite(target_testsuite, testsuite)
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
118
|
-
|
|
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
|
|
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
|
|
@@ -34,13 +34,14 @@ module Fastlane
|
|
|
34
34
|
failed = []
|
|
35
35
|
passing = []
|
|
36
36
|
failure_details = {}
|
|
37
|
-
|
|
37
|
+
testable_summaries.map do |testable_summary|
|
|
38
|
+
target_name = testable_summary.target_name
|
|
38
39
|
all_tests = testable_summary.all_tests.flatten
|
|
39
40
|
all_tests.each do |t|
|
|
40
41
|
if t.test_status == 'Success'
|
|
41
|
-
passing << "#{
|
|
42
|
+
passing << "#{target_name}/#{t.identifier.sub('()', '')}"
|
|
42
43
|
else
|
|
43
|
-
test_identifier = "#{
|
|
44
|
+
test_identifier = "#{target_name}/#{t.identifier.sub('()', '')}"
|
|
44
45
|
failed << test_identifier
|
|
45
46
|
failure = t.find_failure(failures)
|
|
46
47
|
if failure
|
|
@@ -139,25 +139,32 @@ module Fastlane
|
|
|
139
139
|
"
|
|
140
140
|
require 'fastlane/actions/scan'
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
+
HtmlTestReport.verbose("\tadding testsuite\n\t\t#{given_testsuite}")
|
|
32
45
|
add_testsuite(given_testsuite)
|
|
33
46
|
else
|
|
34
|
-
|
|
47
|
+
HtmlTestReport.verbose("\tcollating testsuite\n\t\t#{given_testsuite.root}")
|
|
35
48
|
existing_testsuite.collate_testsuite(given_testsuite)
|
|
36
|
-
|
|
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
|
-
|
|
226
|
+
HtmlTestReport.verbose("\t\tadding testcase\n\t\t\t#{given_testcase.root}")
|
|
214
227
|
unless given_testcase.passing?
|
|
215
|
-
|
|
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
|
-
|
|
232
|
+
HtmlTestReport.verbose("\t\tupdating testcase\n\t\t\t#{existing_testcase.root}")
|
|
220
233
|
unless given_testcase.passing?
|
|
221
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,53 +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
|
-
Scan.devices.replace(@options[:scan_devices_override])
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
10
|
# :nocov:
|
|
58
11
|
def self.run(options)
|
|
59
12
|
RetryingScan.new(options).run
|
|
@@ -64,12 +17,6 @@ module TestCenter
|
|
|
64
17
|
try_count = @options[:try_count] || 1
|
|
65
18
|
begin
|
|
66
19
|
@retrying_scan_helper.before_testrun
|
|
67
|
-
update_scan_options
|
|
68
|
-
|
|
69
|
-
values = scan_config.values(ask: false)
|
|
70
|
-
values[:xcode_path] = File.expand_path("../..", FastlaneCore::Helper.xcode_path)
|
|
71
|
-
ScanHelper.print_scan_parameters(values)
|
|
72
|
-
|
|
73
20
|
Scan::Runner.new.run
|
|
74
21
|
@retrying_scan_helper.after_testrun
|
|
75
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
|
-
|
|
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:
|
|
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 =
|
|
67
|
+
run_count = remaining_desired_simulators
|
|
34
68
|
destinations = Scan.config[:destination].clone
|
|
35
|
-
original_simulators =
|
|
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.
|
|
30
|
+
@clones = @simhelper.parallel_destination_simulators
|
|
30
31
|
main_pid = Process.pid
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
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.
|
|
4
|
+
version: 3.15.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lyndsey Ferguson
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2021-01-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -310,7 +310,7 @@ homepage: https://github.com/lyndsey-ferguson/fastlane-plugin-test_center
|
|
|
310
310
|
licenses:
|
|
311
311
|
- MIT
|
|
312
312
|
metadata: {}
|
|
313
|
-
post_install_message:
|
|
313
|
+
post_install_message:
|
|
314
314
|
rdoc_options: []
|
|
315
315
|
require_paths:
|
|
316
316
|
- lib
|
|
@@ -326,7 +326,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
326
326
|
version: '0'
|
|
327
327
|
requirements: []
|
|
328
328
|
rubygems_version: 3.1.2
|
|
329
|
-
signing_key:
|
|
329
|
+
signing_key:
|
|
330
330
|
specification_version: 4
|
|
331
331
|
summary: Makes testing your iOS app easier
|
|
332
332
|
test_files: []
|