fastlane-plugin-test_center 3.11.2 → 3.13.0
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/README.md +3 -0
- data/lib/fastlane/plugin/test_center/actions/multi_scan.rb +34 -3
- data/lib/fastlane/plugin/test_center/actions/test_options_from_testplan.rb +1 -0
- data/lib/fastlane/plugin/test_center/actions/testplans_from_scheme.rb +36 -16
- data/lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb +16 -4
- data/lib/fastlane/plugin/test_center/helper/fastlane_core/device_manager/simulator_extensions.rb +16 -0
- data/lib/fastlane/plugin/test_center/helper/html_test_report.rb +17 -2
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb +15 -2
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb +5 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb +7 -3
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +48 -7
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb +24 -15
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb +6 -0
- data/lib/fastlane/plugin/test_center/helper/scan_helper.rb +1 -1
- data/lib/fastlane/plugin/test_center/helper/test_collector.rb +5 -1
- data/lib/fastlane/plugin/test_center/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd3718fbdf6d40f912c0bec66d449e7e920961212f5dddf72a1960dba9429d24
|
4
|
+
data.tar.gz: a261932eb1cb2eb2d24f2d934df71cb3743ef6328a25c64648052678dd95ea3b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f1324b7256876f8cd0f7d8a05c24ee4e72954443b652ed869a445c9edf3bb29c2b28b34b88dd7d79879cc26ddd2fd0366437781c8f75f757c54e86a5103ed7f
|
7
|
+
data.tar.gz: 9932b4bb2db2134e913ed25b79797612bd1796c9dc74938ac9fda788cb5c58b07cadfd94688552045898d2443de34767726c26f8623413483b22b280486d1e27
|
data/README.md
CHANGED
@@ -7,6 +7,8 @@
|
|
7
7
|
|
8
8
|
Have you ever spent too much time trying to fix fragile tests only to give up with nothing real to show? Use the `fastlane` actions from `test_center` to remove the pain around your tests, so that you can focus on what makes 💰: features that customers love 😍.
|
9
9
|
|
10
|
+
> For those of you new to fastlane, I recommend that you read my article [Rescue Your Mobile Builds from Madness Using Fastlane](https://medium.com/appian-engineering/rescue-your-mobile-builds-from-madness-using-fastlane-cf123622f2d3).
|
11
|
+
|
10
12
|
<p align="center">
|
11
13
|
<a href="#features">Features</a> |
|
12
14
|
<a href="#installation">Installation</a> |
|
@@ -38,6 +40,7 @@ _read the documentation on each action by clicking on the action name_
|
|
38
40
|
| [`test_options_from_testplan`](docs/feature_details/test_options_from_testplan.md) | returns the tests and test code coverage configuration for a given testplan | ios, mac |
|
39
41
|
| [`testplans_from_scheme`](docs/feature_details/testplans_from_scheme.md) | returns the testplans that an Xcode Scheme references | ios, mac |
|
40
42
|
| [`tests_from_junit`](docs/feature_details/tests_from_junit.md) | returns the passing and failing tests in a Junit test report | ios, mac |
|
43
|
+
| [`tests_from_xcresult`](docs/feature_details/tests_from_xcresult.md) | returns the passing and failing tests in a xcresult bundle | ios, mac |
|
41
44
|
| [`tests_from_xctestrun`](docs/feature_details/tests_from_xctestrun.md) | returns a list of tests for each test target in a `xctestrun` file | ios, mac |
|
42
45
|
| [`collate_junit_reports`](docs/feature_details/collate_junit_reports.md) | combines multiple Junit test reports into one report | ios, mac |
|
43
46
|
| [`collate_html_reports`](docs/feature_details/collate_html_reports.md) | combines multiple HTML test reports into one report | ios, mac |
|
@@ -170,8 +170,10 @@ module Fastlane
|
|
170
170
|
reset_scan_config_to_defaults
|
171
171
|
use_scanfile_to_override_settings(scan_options)
|
172
172
|
turn_off_concurrent_workers(scan_options)
|
173
|
+
UI.important("Turning off :skip_build as it doesn't do anything with multi_scan") if scan_options[:skip_build]
|
174
|
+
scan_options.reject! { |k,v| k == :skip_build }
|
173
175
|
ScanHelper.remove_preexisting_simulator_logs(scan_options)
|
174
|
-
if scan_options[:test_without_building]
|
176
|
+
if scan_options[:test_without_building]
|
175
177
|
UI.verbose("Preparing Scan config options for multi_scan testing")
|
176
178
|
prepare_scan_config(scan_options)
|
177
179
|
else
|
@@ -231,11 +233,31 @@ module Fastlane
|
|
231
233
|
scan_options[:derived_data_path] = Scan.config[:derived_data_path]
|
232
234
|
end
|
233
235
|
|
236
|
+
def self.remove_xcresult_from_build_options(build_options)
|
237
|
+
# convert the :output_types comma separated string of types into an array with no whitespace
|
238
|
+
output_types = build_options[:output_types].to_s.split(',').map(&:strip)
|
239
|
+
xcresult_index = output_types.index('xcresult')
|
240
|
+
|
241
|
+
unless xcresult_index.nil?
|
242
|
+
output_types.delete_at(xcresult_index)
|
243
|
+
# set :output_types value to comma separated string of remaining output types
|
244
|
+
build_options[:output_types] = output_types.join(',')
|
245
|
+
|
246
|
+
if build_options[:output_files] # not always set
|
247
|
+
output_files = build_options[:output_files].split(',').map(&:strip)
|
248
|
+
output_files.delete_at(xcresult_index)
|
249
|
+
|
250
|
+
build_options[:output_files] = output_files.join(',')
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
234
255
|
def self.prepare_scan_options_for_build_for_testing(scan_options)
|
235
|
-
build_options = scan_options.merge(build_for_testing: true).reject { |k| %i[test_without_building
|
256
|
+
build_options = scan_options.merge(build_for_testing: true).reject { |k| %i[test_without_building testplan include_simulator_logs].include?(k) }
|
257
|
+
remove_xcresult_from_build_options(build_options)
|
236
258
|
Scan.config = FastlaneCore::Configuration.create(
|
237
259
|
Fastlane::Actions::ScanAction.available_options,
|
238
|
-
ScanHelper.scan_options_from_multi_scan_options(build_options)
|
260
|
+
ScanHelper.scan_options_from_multi_scan_options(build_options).merge(include_simulator_logs: false)
|
239
261
|
)
|
240
262
|
values = Scan.config.values(ask: false)
|
241
263
|
values[:xcode_path] = File.expand_path("../..", FastlaneCore::Helper.xcode_path)
|
@@ -357,6 +379,15 @@ module Fastlane
|
|
357
379
|
"because the number of tests is unknown")
|
358
380
|
end
|
359
381
|
),
|
382
|
+
FastlaneCore::ConfigItem.new(
|
383
|
+
key: :swift_test_prefix,
|
384
|
+
description: "The prefix used to find test methods. In standard XCTests, this is `test`. If you are using Quick with Swift, set this to `spec`",
|
385
|
+
default_value: "test",
|
386
|
+
optional: true,
|
387
|
+
verify_block: proc do |swift_test_prefix|
|
388
|
+
UI.user_error!("Error: swift_test_prefix must be non-nil and non-empty") if swift_test_prefix.nil? || swift_test_prefix.empty?
|
389
|
+
end
|
390
|
+
),
|
360
391
|
FastlaneCore::ConfigItem.new(
|
361
392
|
key: :quit_simulators,
|
362
393
|
env_name: "FL_MULTI_SCAN_QUIT_SIMULATORS",
|
@@ -14,6 +14,7 @@ module Fastlane
|
|
14
14
|
if test_target.key?('selectedTests')
|
15
15
|
UI.verbose(" Found selectedTests")
|
16
16
|
test_identifiers = test_target['selectedTests'].each do |selected_test|
|
17
|
+
selected_test.delete!('()')
|
17
18
|
UI.verbose(" Found test: '#{selected_test}'")
|
18
19
|
only_testing << "#{testable}/#{selected_test.sub('\/', '/')}"
|
19
20
|
end
|
@@ -2,25 +2,15 @@ module Fastlane
|
|
2
2
|
module Actions
|
3
3
|
class TestplansFromSchemeAction < Action
|
4
4
|
def self.run(params)
|
5
|
-
|
6
|
-
scheme_filepaths = schemes_from_project(params[:xcodeproj], scheme) || schemes_from_workspace(params[:workspace], scheme)
|
7
|
-
if scheme_filepaths.length.zero?
|
8
|
-
UI.user_error!("Error: cannot find any schemes in the Xcode project") if params[:xcodeproj]
|
9
|
-
UI.user_error!("Error: cannot find any schemes in the Xcode workspace") if params[:workspace]
|
10
|
-
end
|
5
|
+
scheme_filepaths = schemes(params)
|
11
6
|
testplan_paths = []
|
12
7
|
scheme_filepaths.each do |scheme_filepath|
|
13
8
|
UI.verbose("Looking in Scheme '#{scheme_filepath}' for any testplans")
|
14
9
|
xcscheme = Xcodeproj::XCScheme.new(scheme_filepath)
|
15
|
-
next
|
16
|
-
|
17
|
-
next if xcscheme.test_action.testables[0].buildable_references.to_a.empty?
|
18
|
-
next if xcscheme.test_action.test_plans.to_a.empty?
|
19
|
-
|
20
|
-
xcodeproj = xcscheme.test_action.testables[0].buildable_references[0].target_referenced_container.sub('container:', '')
|
21
|
-
container_dir = scheme_filepath.sub(/#{xcodeproj}.*/, '')
|
10
|
+
next unless scheme_has_testplans?(xcscheme)
|
11
|
+
scheme_container_dir = File.absolute_path(scheme_filepath).sub(%r{/[^/]*\.(xcworkspace|xcodeproj)/.*}, '')
|
22
12
|
xcscheme.test_action.test_plans.each do |testplan|
|
23
|
-
testplan_path = File.absolute_path(File.join(
|
13
|
+
testplan_path = File.absolute_path(File.join(scheme_container_dir, testplan.target_referenced_container.sub('container:', '')))
|
24
14
|
UI.verbose(" found testplan '#{testplan_path}'")
|
25
15
|
testplan_paths << testplan_path
|
26
16
|
end
|
@@ -28,6 +18,29 @@ module Fastlane
|
|
28
18
|
testplan_paths
|
29
19
|
end
|
30
20
|
|
21
|
+
def self.scheme_has_testplans?(xcscheme)
|
22
|
+
return !(
|
23
|
+
xcscheme.test_action.nil? ||
|
24
|
+
xcscheme.test_action.testables.to_a.empty? ||
|
25
|
+
xcscheme.test_action.testables[0].buildable_references.to_a.empty? ||
|
26
|
+
xcscheme.test_action.test_plans.to_a.empty?
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.schemes(params)
|
31
|
+
scheme = params[:scheme]
|
32
|
+
scheme_filepaths = schemes_from_project(params[:xcodeproj], scheme) || schemes_from_workspace(params[:workspace], scheme)
|
33
|
+
if scheme_filepaths.length.zero?
|
34
|
+
scheme_detail_message = ''
|
35
|
+
if scheme
|
36
|
+
scheme_detail_message = "named '#{scheme}' "
|
37
|
+
end
|
38
|
+
UI.user_error!("Error: cannot find any schemes #{scheme_detail_message}in the Xcode project") if params[:xcodeproj]
|
39
|
+
UI.user_error!("Error: cannot find any schemes #{scheme_detail_message}in the Xcode workspace") if params[:workspace]
|
40
|
+
end
|
41
|
+
scheme_filepaths
|
42
|
+
end
|
43
|
+
|
31
44
|
def self.schemes_from_project(project_path, scheme)
|
32
45
|
return nil unless project_path
|
33
46
|
|
@@ -37,9 +50,16 @@ module Fastlane
|
|
37
50
|
def self.schemes_from_workspace(workspace_path, scheme)
|
38
51
|
return nil unless workspace_path
|
39
52
|
|
40
|
-
xcworkspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
|
41
53
|
scheme_filepaths = []
|
42
|
-
|
54
|
+
scheme_filepaths.concat(schemes_from_project(workspace_path, scheme))
|
55
|
+
return scheme_filepaths unless scheme_filepaths.empty?
|
56
|
+
|
57
|
+
xcworkspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
|
58
|
+
xcodeprojects = xcworkspace.file_references.select do |file_reference|
|
59
|
+
file_reference.path.end_with?('xcodeproj')
|
60
|
+
end
|
61
|
+
|
62
|
+
xcodeprojects.each do |file_reference|
|
43
63
|
next if file_reference.path.include?('Pods/Pods.xcodeproj')
|
44
64
|
|
45
65
|
project_path = file_reference.absolute_path(File.dirname(workspace_path))
|
@@ -5,10 +5,10 @@ module Fastlane
|
|
5
5
|
class TestsFromXctestrunAction < Action
|
6
6
|
def self.run(params)
|
7
7
|
UI.verbose("Getting tests from xctestrun file at '#{params[:xctestrun]}'")
|
8
|
-
return xctestrun_tests(params[:xctestrun], params[:invocation_based_tests])
|
8
|
+
return xctestrun_tests(params[:xctestrun], params[:invocation_based_tests], swift_test_prefix: params[:swift_test_prefix])
|
9
9
|
end
|
10
10
|
|
11
|
-
def self.xctestrun_tests(xctestrun_path, invocation_based_tests)
|
11
|
+
def self.xctestrun_tests(xctestrun_path, invocation_based_tests, swift_test_prefix: "test")
|
12
12
|
xctestrun = Plist.parse_xml(xctestrun_path)
|
13
13
|
xctestrun_rootpath = File.dirname(xctestrun_path)
|
14
14
|
xctestrun_version = xctestrun.fetch('__xctestrun_metadata__', Hash.new).fetch('FormatVersion', 1)
|
@@ -17,12 +17,14 @@ module Fastlane
|
|
17
17
|
if xctestrun_version == 1
|
18
18
|
xctestrun.each do |testable_name, test_target_config|
|
19
19
|
next if ignoredTestables.include? testable_name
|
20
|
+
test_target_config['TestableName'] = testable_name
|
20
21
|
test_targets << test_target_config
|
21
22
|
end
|
22
23
|
else
|
23
24
|
test_configurations = xctestrun['TestConfigurations']
|
24
25
|
test_configurations.each do |configuration|
|
25
26
|
configuration['TestTargets'].each do |test_target|
|
27
|
+
test_target['TestableName'] = test_target['BlueprintName']
|
26
28
|
test_targets << test_target
|
27
29
|
end
|
28
30
|
end
|
@@ -30,14 +32,14 @@ module Fastlane
|
|
30
32
|
|
31
33
|
tests = Hash.new([])
|
32
34
|
test_targets.each do |xctestrun_config|
|
33
|
-
testable_name = xctestrun_config['
|
35
|
+
testable_name = xctestrun_config['TestableName']
|
34
36
|
xctest_path = xctest_bundle_path(xctestrun_rootpath, xctestrun_config)
|
35
37
|
test_identifiers = []
|
36
38
|
if xctestrun_config.key?('OnlyTestIdentifiers')
|
37
39
|
test_identifiers = xctestrun_config['OnlyTestIdentifiers']
|
38
40
|
UI.verbose("Identifiers after adding onlytest tests: #{test_identifiers.join("\n\t")}")
|
39
41
|
else
|
40
|
-
test_identifiers = XCTestList.tests(xctest_path)
|
42
|
+
test_identifiers = XCTestList.tests(xctest_path, swift_test_prefix: swift_test_prefix)
|
41
43
|
UI.verbose("Found the following tests: #{test_identifiers.join("\n\t")}")
|
42
44
|
end
|
43
45
|
if xctestrun_config.key?('SkipTestIdentifiers')
|
@@ -50,6 +52,7 @@ module Fastlane
|
|
50
52
|
if test_identifiers.empty? && !invocation_based_tests
|
51
53
|
UI.error("No tests found in '#{xctest_path}'!")
|
52
54
|
UI.important("Is the Build Setting, `ENABLE_TESTABILITY` enabled for the test target #{testable_name}?")
|
55
|
+
UI.message("If your Swift test method names use a prefix other than `test`, consider setting `:swift_test_prefix`.")
|
53
56
|
end
|
54
57
|
tests[testable_name] = test_identifiers.map do |test_identifier|
|
55
58
|
"#{testable_name}/#{test_identifier}"
|
@@ -114,6 +117,15 @@ module Fastlane
|
|
114
117
|
is_string: false,
|
115
118
|
default_value: false,
|
116
119
|
optional: true
|
120
|
+
),
|
121
|
+
FastlaneCore::ConfigItem.new(
|
122
|
+
key: :swift_test_prefix,
|
123
|
+
description: "The prefix used to find test methods. In standard XCTests, this is `test`. If you are using Quick with Swift, set this to `spec`",
|
124
|
+
default_value: "test",
|
125
|
+
optional: true,
|
126
|
+
verify_block: proc do |swift_test_prefix|
|
127
|
+
UI.user_error!("Error: swift_test_prefix must be non-nil and non-empty") if swift_test_prefix.nil? || swift_test_prefix.empty?
|
128
|
+
end
|
117
129
|
)
|
118
130
|
]
|
119
131
|
end
|
data/lib/fastlane/plugin/test_center/helper/fastlane_core/device_manager/simulator_extensions.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
module FixedCopyLogarchiveFastlaneSimulator
|
3
|
+
def self.included(base)
|
4
|
+
base.instance_eval do
|
5
|
+
def copy_logarchive(device, log_identity, logs_destination_dir)
|
6
|
+
require 'shellwords'
|
7
|
+
FastlaneCore::UI.verbose("> FixedCopyLogarchiveFastlaneSimulator.copy_logarchive")
|
8
|
+
logarchive_dst = File.join(logs_destination_dir, "system_logs-#{log_identity}.logarchive")
|
9
|
+
FileUtils.rm_rf(logarchive_dst)
|
10
|
+
FileUtils.mkdir_p(File.expand_path("..", logarchive_dst))
|
11
|
+
command = "xcrun simctl spawn #{device.udid} log collect --output #{logarchive_dst.shellescape} 2>/dev/null"
|
12
|
+
FastlaneCore::CommandExecutor.execute(command: command, print_all: false, print_command: true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -181,11 +181,26 @@ module TestCenter
|
|
181
181
|
end
|
182
182
|
|
183
183
|
def remove_duplicate_testcases
|
184
|
-
|
184
|
+
# Get a list of all the testcases in the report's testsuite
|
185
|
+
# and reverse the order so that we'll get the tests that
|
186
|
+
# passed _after_ they failed first. That way, when
|
187
|
+
# uniq is called, it will grab the first non-repeated test
|
188
|
+
# it finds; for duplicated tests (tests that were re-run), it will
|
189
|
+
# actually grab the last test that was run of that set.
|
190
|
+
#
|
191
|
+
# For example, if `testcases` is
|
192
|
+
# `['a(passing)', 'b(passing)', 'c(passing)', 'dup1(failing)', 'dup2(failing)', 'dup1(passing)', 'dup2(passing)' ]`
|
193
|
+
# then, testcases.reverse will be
|
194
|
+
# `['dup2(passing)', 'dup1(passing)', 'dup2(failing)', 'dup1(failing)', 'c(passing)', 'b(passing)', 'a(passing)']`
|
195
|
+
# then `uniq_testcases` will be
|
196
|
+
# `['dup2(passing)', 'dup1(passing)', 'c(passing)', 'b(passing)', 'a(passing)']`
|
197
|
+
nonuniq_testcases = testcases.reverse
|
185
198
|
uniq_testcases = nonuniq_testcases.uniq { |tc| tc.title }
|
186
199
|
(nonuniq_testcases - uniq_testcases).each do |tc|
|
200
|
+
# here, we would be deleting ['dup2(failing)', 'dup1(failing)']
|
187
201
|
failure_details = tc.failure_details
|
188
|
-
|
202
|
+
# failure_details can be nil if this is a passing testcase
|
203
|
+
tc.root.parent.delete_element(failure_details) unless failure_details.nil?
|
189
204
|
tc.root.parent.delete_element(tc.root)
|
190
205
|
end
|
191
206
|
end
|
@@ -19,11 +19,24 @@ module FastlaneCore
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def boot
|
22
|
-
|
22
|
+
return unless is_simulator
|
23
|
+
return unless os_type == "iOS"
|
24
|
+
return if self.state == 'Booted'
|
25
|
+
|
26
|
+
UI.message("Booting #{self}")
|
27
|
+
|
28
|
+
`xcrun simctl boot #{self.udid} 2>/dev/null`
|
29
|
+
self.state = 'Booted'
|
23
30
|
end
|
24
31
|
|
25
32
|
def shutdown
|
26
|
-
|
33
|
+
return unless is_simulator
|
34
|
+
return unless os_type == "iOS"
|
35
|
+
return if self.state == 'Shutdown'
|
36
|
+
|
37
|
+
UI.message("Shutting down #{self.udid}")
|
38
|
+
`xcrun simctl shutdown #{self.udid} 2>/dev/null`
|
39
|
+
self.state = 'Shutdown'
|
27
40
|
end
|
28
41
|
end
|
29
42
|
end
|
@@ -47,6 +47,11 @@ module TestCenter
|
|
47
47
|
scan_config.set(k,v) unless v.nil?
|
48
48
|
FastlaneCore::UI.verbose("\tSetting #{k.to_s} to #{v}")
|
49
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
|
50
55
|
end
|
51
56
|
|
52
57
|
# :nocov:
|
@@ -29,7 +29,7 @@ module TestCenter
|
|
29
29
|
return unless @options[:quit_simulators]
|
30
30
|
|
31
31
|
@options.fetch(:destination).each do |destination|
|
32
|
-
if /id=(?<udid
|
32
|
+
if /id=(?<udid>[^,$]+)/ =~ destination
|
33
33
|
FastlaneCore::UI.verbose("Restarting Simulator #{udid}")
|
34
34
|
`xcrun simctl shutdown #{udid} 2>/dev/null`
|
35
35
|
`xcrun simctl boot #{udid} 2>/dev/null`
|
@@ -337,11 +337,15 @@ module TestCenter
|
|
337
337
|
|
338
338
|
glob_pattern = "#{output_directory}/system_logs-*.{log,logarchive}"
|
339
339
|
logs = Dir.glob(glob_pattern)
|
340
|
+
batch_prefix = ''
|
341
|
+
if @options[:batch]
|
342
|
+
batch_prefix = "batch-#{@options[:batch]}-"
|
343
|
+
end
|
340
344
|
logs.each do |log_filepath|
|
341
|
-
new_logname = "try-#{testrun_count}-#{File.basename(log_filepath)}"
|
345
|
+
new_logname = "#{batch_prefix}try-#{testrun_count}-#{File.basename(log_filepath)}"
|
342
346
|
new_log_filepath = "#{File.dirname(log_filepath)}/#{new_logname}"
|
343
347
|
FastlaneCore::UI.verbose("Moving simulator log '#{log_filepath}' to '#{new_log_filepath}'")
|
344
|
-
|
348
|
+
FileUtils.mv(log_filepath, new_log_filepath, force: true)
|
345
349
|
end
|
346
350
|
end
|
347
351
|
|
@@ -6,6 +6,7 @@ module TestCenter
|
|
6
6
|
require 'json'
|
7
7
|
require 'shellwords'
|
8
8
|
require 'snapshot/reset_simulators'
|
9
|
+
require_relative '../fastlane_core/device_manager/simulator_extensions'
|
9
10
|
|
10
11
|
class Runner
|
11
12
|
attr_reader :retry_total_count
|
@@ -21,6 +22,8 @@ module TestCenter
|
|
21
22
|
end
|
22
23
|
@batch_count = 1 # default count. Will be updated by setup_testcollector
|
23
24
|
setup_testcollector
|
25
|
+
setup_logcollection
|
26
|
+
FastlaneCore::UI.verbose("< done in TestCenter::Helper::MultiScanManager.initialize")
|
24
27
|
end
|
25
28
|
|
26
29
|
def update_options_to_use_xcresult_output
|
@@ -35,6 +38,27 @@ module TestCenter
|
|
35
38
|
@options.reject! { |k,_| k == :result_bundle }
|
36
39
|
end
|
37
40
|
|
41
|
+
def setup_logcollection
|
42
|
+
FastlaneCore::UI.verbose("> setup_logcollection")
|
43
|
+
return unless @options[:include_simulator_logs]
|
44
|
+
return if Scan::Runner.method_defined?(:prelaunch_simulators)
|
45
|
+
|
46
|
+
# We need to prelaunch the simulators so xcodebuild
|
47
|
+
# doesn't shut it down before we have a chance to get
|
48
|
+
# the logs.
|
49
|
+
FastlaneCore::UI.verbose("\t collecting devices to boot for log collection")
|
50
|
+
devices_to_shutdown = []
|
51
|
+
Scan.devices.each do |device|
|
52
|
+
devices_to_shutdown << device if device.state == "Shutdown"
|
53
|
+
device.boot
|
54
|
+
end
|
55
|
+
at_exit do
|
56
|
+
devices_to_shutdown.each(&:shutdown)
|
57
|
+
end
|
58
|
+
FastlaneCore::UI.verbose("\t fixing FastlaneCore::Simulator.copy_logarchive")
|
59
|
+
FastlaneCore::Simulator.send(:include, FixedCopyLogarchiveFastlaneSimulator)
|
60
|
+
end
|
61
|
+
|
38
62
|
def setup_testcollector
|
39
63
|
return if @options[:invocation_based_tests] && @options[:only_testing].nil?
|
40
64
|
return if @test_collector
|
@@ -42,6 +66,10 @@ module TestCenter
|
|
42
66
|
@test_collector = TestCollector.new(@options)
|
43
67
|
@options.reject! { |key| %i[testplan].include?(key) }
|
44
68
|
@batch_count = @test_collector.test_batches.size
|
69
|
+
if @test_collector.test_batches.flatten.size < @options[:parallel_testrun_count].to_i
|
70
|
+
FastlaneCore::UI.important(":parallel_testrun_count greater than the number of tests (#{@test_collector.only_testing.size}). Reducing to that number.")
|
71
|
+
@options[:parallel_testrun_count] = @test_collector.only_testing.size
|
72
|
+
end
|
45
73
|
end
|
46
74
|
|
47
75
|
def output_directory(batch_index = 0, test_batch = [])
|
@@ -73,9 +101,7 @@ module TestCenter
|
|
73
101
|
end
|
74
102
|
|
75
103
|
def should_run_tests_through_single_try?
|
76
|
-
|
77
|
-
should_run_for_skip_build = @options[:skip_build]
|
78
|
-
(should_run_for_invocation_tests || should_run_for_skip_build)
|
104
|
+
@options[:invocation_based_tests] && @options[:only_testing].nil?
|
79
105
|
end
|
80
106
|
|
81
107
|
|
@@ -182,11 +208,21 @@ module TestCenter
|
|
182
208
|
end
|
183
209
|
|
184
210
|
def scan_options_for_worker(test_batch, batch_index)
|
211
|
+
if @test_collector.test_batches.size > 1
|
212
|
+
# If there are more than 1 batch, then we want each batch result
|
213
|
+
# sent to a "batch index" output folder to be collated later
|
214
|
+
# into the requested output_folder.
|
215
|
+
# Otherwise, send the results from the one and only one batch
|
216
|
+
# to the requested output_folder
|
217
|
+
batch_index += 1
|
218
|
+
batch = batch_index
|
219
|
+
end
|
220
|
+
|
185
221
|
{
|
186
222
|
only_testing: test_batch.map(&:shellsafe_testidentifier),
|
187
|
-
output_directory: output_directory(batch_index
|
223
|
+
output_directory: output_directory(batch_index, test_batch),
|
188
224
|
try_count: @options[:try_count],
|
189
|
-
batch:
|
225
|
+
batch: batch
|
190
226
|
}
|
191
227
|
end
|
192
228
|
|
@@ -224,8 +260,10 @@ module TestCenter
|
|
224
260
|
src_xcresult_bundlepath = File.join(testable_output_dir, xcresult_bundlename)
|
225
261
|
dst_xcresult_bundlepath = File.join(final_output_dir, xcresult_bundlename)
|
226
262
|
|
227
|
-
#
|
228
|
-
return
|
263
|
+
# if there is no destination bundle to merge to, skip it as any source bundle will be copied when complete.
|
264
|
+
return if !File.exist?(dst_xcresult_bundlepath)
|
265
|
+
# if there is no source bundle to merge, skip it as there is nothing to merge.
|
266
|
+
return if !File.exist?(src_xcresult_bundlepath)
|
229
267
|
|
230
268
|
config = FastlaneCore::Configuration.create(
|
231
269
|
Fastlane::Actions::CollateXcresultsAction.available_options,
|
@@ -282,6 +320,9 @@ module TestCenter
|
|
282
320
|
scheme: @options[:scheme],
|
283
321
|
result_bundle: @options[:result_bundle]
|
284
322
|
).collate
|
323
|
+
logs_glog_pattern = "#{source_reports_directory_glob}/*system_logs-*.{log,logarchive}"
|
324
|
+
logs = Dir.glob(logs_glog_pattern)
|
325
|
+
FileUtils.mv(logs, absolute_output_directory, force: true)
|
285
326
|
FileUtils.rm_rf(Dir.glob(source_reports_directory_glob))
|
286
327
|
symlink_result_bundle_to_xcresult(absolute_output_directory, report_name_helper)
|
287
328
|
true
|
@@ -12,34 +12,43 @@ module TestCenter
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def simulator_matches_destination(simulator, destination)
|
16
|
+
match = destination.match(/id=(?<udid>[^,]+)/)
|
17
|
+
if match
|
18
|
+
found_match = (match[:udid] == simulator.udid)
|
19
|
+
else
|
20
|
+
match = destination.match(/name=(?<name>[^,]+)/)
|
21
|
+
name = match[:name] || ''
|
22
|
+
match = destination.match(/OS=(?<os_version>[^,]+)/)
|
23
|
+
os_version = match[:os_version] || ''
|
24
|
+
|
25
|
+
found_match = (name == simulator.name && os_version == simulator.os_version)
|
26
|
+
end
|
27
|
+
found_match
|
28
|
+
end
|
29
|
+
|
15
30
|
def clone_destination_simulators
|
16
31
|
cloned_simulators = []
|
17
32
|
|
18
33
|
run_count = @options[:parallel_testrun_count] || 0
|
34
|
+
destinations = Scan.config[:destination].clone
|
19
35
|
original_simulators = FastlaneCore::DeviceManager.simulators('iOS').find_all do |simulator|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
match = destination.match(/name=(?<name>[^,]+)/)
|
27
|
-
name = match[:name] || ''
|
28
|
-
match = destination.match(/OS=(?<os_version>[^,]+)/)
|
29
|
-
os_version = match[:os_version] || ''
|
30
|
-
|
31
|
-
found_match = (name == simulator.name && os_version == simulator.os_version)
|
32
|
-
end
|
36
|
+
found_simulator = destinations.find do |destination|
|
37
|
+
simulator_matches_destination(simulator, destination)
|
38
|
+
end
|
39
|
+
if found_simulator
|
40
|
+
destinations.delete(found_simulator)
|
33
41
|
end
|
34
|
-
|
42
|
+
|
43
|
+
!found_simulator.nil?
|
35
44
|
end
|
36
45
|
original_simulators.each(&:shutdown)
|
37
46
|
(0...run_count).each do |batch_index|
|
38
47
|
cloned_simulators << []
|
39
48
|
original_simulators.each do |simulator|
|
40
|
-
FastlaneCore::UI.verbose("Cloning simulator")
|
41
49
|
cloned_simulator = simulator.clone
|
42
50
|
new_first_name = simulator.name.sub(/( ?\(.*| ?$)/, " Clone #{batch_index + 1}")
|
51
|
+
FastlaneCore::UI.verbose("Cloned simulator #{simulator.name} to (name=#{new_first_name}, udid=#{cloned_simulator.udid}, OS=#{cloned_simulator.ios_version})")
|
43
52
|
new_last_name = "#{self.class.name}<#{self.object_id}>"
|
44
53
|
cloned_simulator.rename("#{new_first_name} #{new_last_name}")
|
45
54
|
|
@@ -46,6 +46,11 @@ module TestCenter
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
def simulator_devices_for_worker(worker_index)
|
50
|
+
return nil unless @options[:platform] == :ios_simulator
|
51
|
+
@clones[worker_index]
|
52
|
+
end
|
53
|
+
|
49
54
|
def setup_parallel_workers
|
50
55
|
setup_cloned_simulators
|
51
56
|
desired_worker_count = @options[:parallel_testrun_count]
|
@@ -58,6 +63,7 @@ module TestCenter
|
|
58
63
|
def parallel_scan_options(worker_index)
|
59
64
|
options = @options.reject { |key| %i[device devices].include?(key) }
|
60
65
|
options[:destination] = destination_for_worker(worker_index)
|
66
|
+
options[:scan_devices_override] = simulator_devices_for_worker(worker_index)
|
61
67
|
options[:buildlog_path] = buildlog_path_for_worker(worker_index) if @options[:buildlog_path]
|
62
68
|
options[:derived_data_path] = derived_data_path_for_worker(worker_index)
|
63
69
|
options[:batch_index] = worker_index
|
@@ -6,6 +6,7 @@ module TestCenter
|
|
6
6
|
|
7
7
|
class TestCollector
|
8
8
|
attr_reader :xctestrun_path
|
9
|
+
attr_reader :only_testing
|
9
10
|
|
10
11
|
def initialize(options)
|
11
12
|
unless options[:xctestrun] || options[:derived_data_path]
|
@@ -25,6 +26,8 @@ module TestCenter
|
|
25
26
|
if @batch_count == 1 && options[:parallel_testrun_count] > 1
|
26
27
|
@batch_count = options[:parallel_testrun_count]
|
27
28
|
end
|
29
|
+
|
30
|
+
@swift_test_prefix = options[:swift_test_prefix]
|
28
31
|
end
|
29
32
|
|
30
33
|
def only_testing_from_testplan(options)
|
@@ -92,7 +95,8 @@ module TestCenter
|
|
92
95
|
::Fastlane::Actions::TestsFromXctestrunAction.available_options,
|
93
96
|
{
|
94
97
|
xctestrun: @xctestrun_path,
|
95
|
-
invocation_based_tests: @invocation_based_tests
|
98
|
+
invocation_based_tests: @invocation_based_tests,
|
99
|
+
swift_test_prefix: @swift_test_prefix
|
96
100
|
}
|
97
101
|
)
|
98
102
|
::Fastlane::Actions::TestsFromXctestrunAction.run(config)
|
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.13.0
|
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-
|
11
|
+
date: 2020-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
61
|
+
version: 1.2.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.
|
68
|
+
version: 1.2.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: colorize
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -271,6 +271,7 @@ files:
|
|
271
271
|
- lib/fastlane/plugin/test_center/actions/testplans_from_scheme.rb
|
272
272
|
- lib/fastlane/plugin/test_center/actions/tests_from_junit.rb
|
273
273
|
- lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb
|
274
|
+
- lib/fastlane/plugin/test_center/helper/fastlane_core/device_manager/simulator_extensions.rb
|
274
275
|
- lib/fastlane/plugin/test_center/helper/html_test_report.rb
|
275
276
|
- lib/fastlane/plugin/test_center/helper/junit_helper.rb
|
276
277
|
- lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb
|