fastlane-plugin-test_center 3.10.3 → 3.11.4
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 +7 -4
- data/lib/fastlane/plugin/test_center/actions/multi_scan.rb +6 -2
- data/lib/fastlane/plugin/test_center/actions/test_options_from_testplan.rb +91 -0
- data/lib/fastlane/plugin/test_center/actions/testplans_from_scheme.rb +140 -0
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb +2 -0
- 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 +15 -7
- data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +9 -4
- 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/test_collector.rb +31 -1
- data/lib/fastlane/plugin/test_center/version.rb +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cad9b8dc9f0a32d294ac1d2a854ed5245992ef5b42ab354387c8b3dba6c5571a
|
|
4
|
+
data.tar.gz: 6112ba8a64cf378c96c5997be419f13b57a75c156f5781b9f1d755a09d8559c1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d158818861f01c4e1d59f2625dbc40cb73df7327566f5ad933eab199530217e5bd2f97a7684e21e9fd29e0df18ad9f8c6ac0648b5d8619d81c913d2eb8427dde
|
|
7
|
+
data.tar.gz: 8b70dfc5f9e97937177edccbc708c3ac13a1b74be9b5035dab31fbd76cb195ad86c8ddc62a1b3efcacb052dc0d4a7fe010514d4ed48c5e8b1217189ce5f1ba21
|
data/README.md
CHANGED
|
@@ -21,7 +21,7 @@ Have you ever spent too much time trying to fix fragile tests only to give up wi
|
|
|
21
21
|
|
|
22
22
|
## Features
|
|
23
23
|
|
|
24
|
-
This plugin makes testing your iOS app easier by providing you actions that give you greater control over everything related to testing your app.
|
|
24
|
+
This plugin makes testing your iOS app easier by providing you actions that give you greater control over everything related to testing your app.
|
|
25
25
|
|
|
26
26
|
`multi_scan` began when I created an action to only re-run the failed tests in order to determine if they were truly failing, or if they were failing randomly due to a fragile infrastructure. This action morphed into an entire plugin with many actions in the testing category.
|
|
27
27
|
|
|
@@ -35,6 +35,8 @@ _read the documentation on each action by clicking on the action name_
|
|
|
35
35
|
| [`suppress_tests_from_junit`](docs/feature_details/suppress_tests_from_junit.md) | suppress tests in an Xcode Scheme using those in a Junit test report | ios, mac |
|
|
36
36
|
| [`suppress_tests`](docs/feature_details/suppress_tests.md) | suppress tests in an Xcode Scheme | ios, mac |
|
|
37
37
|
| [`suppressed_tests`](docs/feature_details/suppressed_tests.md) | returns a list of the suppressed tests in your Xcode Project or Scheme | ios, mac |
|
|
38
|
+
| [`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
|
+
| [`testplans_from_scheme`](docs/feature_details/testplans_from_scheme.md) | returns the testplans that an Xcode Scheme references | ios, mac |
|
|
38
40
|
| [`tests_from_junit`](docs/feature_details/tests_from_junit.md) | returns the passing and failing tests in a Junit test report | ios, mac |
|
|
39
41
|
| [`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 |
|
|
40
42
|
| [`collate_junit_reports`](docs/feature_details/collate_junit_reports.md) | combines multiple Junit test reports into one report | ios, mac |
|
|
@@ -62,7 +64,7 @@ The most popular action in the `test_center` plugin is `multi_scan`, and if you
|
|
|
62
64
|
multi_scan(
|
|
63
65
|
project: File.absolute_path('../AtomicBoy/AtomicBoy.xcodeproj'),
|
|
64
66
|
scheme: 'AtomicBoy',
|
|
65
|
-
try_count: 3, # retry _failing_ tests up to three times^1.
|
|
67
|
+
try_count: 3, # retry _failing_ tests up to three times^1.
|
|
66
68
|
fail_build: false,
|
|
67
69
|
parallel_testrun_count: 4 # run subsets of your tests on parallel simulators^2
|
|
68
70
|
)
|
|
@@ -91,9 +93,10 @@ For more information about how the `fastlane` plugin system works, check out the
|
|
|
91
93
|
|
|
92
94
|
_fastlane_ is the easiest way to automate beta deployments and releases for your iOS and Android apps. To learn more, check out [fastlane.tools](https://fastlane.tools).
|
|
93
95
|
|
|
94
|
-
##
|
|
96
|
+
## Supporters
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+

|
|
99
|
+
[vdavydovHH](https://github.com/vdavydovHH)
|
|
97
100
|
|
|
98
101
|
## License
|
|
99
102
|
|
|
@@ -232,7 +232,7 @@ module Fastlane
|
|
|
232
232
|
end
|
|
233
233
|
|
|
234
234
|
def self.prepare_scan_options_for_build_for_testing(scan_options)
|
|
235
|
-
build_options = scan_options.merge(build_for_testing: true).reject { |k|
|
|
235
|
+
build_options = scan_options.merge(build_for_testing: true).reject { |k| %i[test_without_building, testplan, include_simulator_logs].include?(k) }
|
|
236
236
|
Scan.config = FastlaneCore::Configuration.create(
|
|
237
237
|
Fastlane::Actions::ScanAction.available_options,
|
|
238
238
|
ScanHelper.scan_options_from_multi_scan_options(build_options)
|
|
@@ -244,7 +244,11 @@ module Fastlane
|
|
|
244
244
|
end
|
|
245
245
|
|
|
246
246
|
def self.update_xctestrun_after_build(scan_options)
|
|
247
|
-
|
|
247
|
+
glob_pattern = "#{Scan.config[:derived_data_path]}/Build/Products/*.xctestrun"
|
|
248
|
+
if scan_options[:testplan]
|
|
249
|
+
glob_pattern = "#{Scan.config[:derived_data_path]}/Build/Products/*_#{scan_options[:testplan]}_*.xctestrun"
|
|
250
|
+
end
|
|
251
|
+
xctestrun_files = Dir.glob(glob_pattern)
|
|
248
252
|
UI.verbose("After building, found xctestrun files #{xctestrun_files} (choosing 1st)")
|
|
249
253
|
scan_options[:xctestrun] = xctestrun_files.first
|
|
250
254
|
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module Fastlane
|
|
4
|
+
module Actions
|
|
5
|
+
class TestOptionsFromTestplanAction < Action
|
|
6
|
+
def self.run(params)
|
|
7
|
+
testplan_path = params[:testplan]
|
|
8
|
+
|
|
9
|
+
testplan = JSON.load(File.open(testplan_path))
|
|
10
|
+
only_testing = []
|
|
11
|
+
UI.verbose("Examining testplan JSON: #{testplan}")
|
|
12
|
+
testplan['testTargets'].each do |test_target|
|
|
13
|
+
testable = test_target.dig('target', 'name')
|
|
14
|
+
if test_target.key?('selectedTests')
|
|
15
|
+
UI.verbose(" Found selectedTests")
|
|
16
|
+
test_identifiers = test_target['selectedTests'].each do |selected_test|
|
|
17
|
+
selected_test.delete!('()')
|
|
18
|
+
UI.verbose(" Found test: '#{selected_test}'")
|
|
19
|
+
only_testing << "#{testable}/#{selected_test.sub('\/', '/')}"
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
UI.verbose(" No selected tests, using testable '#{testable}'")
|
|
23
|
+
only_testing << testable
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
{
|
|
27
|
+
code_coverage: testplan.dig('defaultOptions', 'codeCoverage'),
|
|
28
|
+
only_testing: only_testing
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#####################################################
|
|
33
|
+
# @!group Documentation
|
|
34
|
+
#####################################################
|
|
35
|
+
|
|
36
|
+
def self.description
|
|
37
|
+
"☑️ Gets test info from a given test plan"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.details
|
|
41
|
+
"Gets tests info consisting of tests to run and whether or not code coverage is enabled"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def self.available_options
|
|
45
|
+
[
|
|
46
|
+
FastlaneCore::ConfigItem.new(
|
|
47
|
+
key: :testplan,
|
|
48
|
+
optional: true,
|
|
49
|
+
env_name: "FL_TEST_OPTIONS_FROM_TESTPLAN_TESTPLAN",
|
|
50
|
+
description: "The Xcode testplan to read the test info from",
|
|
51
|
+
verify_block: proc do |test_plan|
|
|
52
|
+
UI.user_error!("Error: Xcode Test Plan '#{test_plan}' is not valid!") if test_plan and test_plan.empty?
|
|
53
|
+
UI.user_error!("Error: Test Plan does not exist at path '#{test_plan}'") unless File.exist?(test_plan)
|
|
54
|
+
end
|
|
55
|
+
)
|
|
56
|
+
]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.return_value
|
|
60
|
+
"Returns a Hash with keys :code_coverage and :only_testing for the given testplan"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.example_code
|
|
64
|
+
[
|
|
65
|
+
"
|
|
66
|
+
UI.important(
|
|
67
|
+
'example: ' \\
|
|
68
|
+
'get the tests and the test coverage configuration from a given testplan'
|
|
69
|
+
)
|
|
70
|
+
test_options = test_options_from_testplan(
|
|
71
|
+
testplan: 'AtomicBoy/AtomicBoy_2.xctestplan'
|
|
72
|
+
)
|
|
73
|
+
UI.message(\"The AtomicBoy_2 testplan has the following tests: \#{test_options[:only_testing]}\")
|
|
74
|
+
"
|
|
75
|
+
]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def self.authors
|
|
79
|
+
["lyndsey-ferguson/lyndseydf"]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.category
|
|
83
|
+
:testing
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.is_supported?(platform)
|
|
87
|
+
%i[ios mac].include?(platform)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module Fastlane
|
|
2
|
+
module Actions
|
|
3
|
+
class TestplansFromSchemeAction < Action
|
|
4
|
+
def self.run(params)
|
|
5
|
+
scheme_filepaths = schemes(params)
|
|
6
|
+
testplan_paths = []
|
|
7
|
+
scheme_filepaths.each do |scheme_filepath|
|
|
8
|
+
UI.verbose("Looking in Scheme '#{scheme_filepath}' for any testplans")
|
|
9
|
+
xcscheme = Xcodeproj::XCScheme.new(scheme_filepath)
|
|
10
|
+
next unless scheme_has_testplans?(xcscheme)
|
|
11
|
+
scheme_container_dir = File.absolute_path(scheme_filepath).sub(%r{/[^/]*\.(xcworkspace|xcodeproj)/.*}, '')
|
|
12
|
+
xcscheme.test_action.test_plans.each do |testplan|
|
|
13
|
+
testplan_path = File.absolute_path(File.join(scheme_container_dir, testplan.target_referenced_container.sub('container:', '')))
|
|
14
|
+
UI.verbose(" found testplan '#{testplan_path}'")
|
|
15
|
+
testplan_paths << testplan_path
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
testplan_paths
|
|
19
|
+
end
|
|
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
|
+
|
|
44
|
+
def self.schemes_from_project(project_path, scheme)
|
|
45
|
+
return nil unless project_path
|
|
46
|
+
|
|
47
|
+
Dir.glob("#{project_path}/{xcshareddata,xcuserdata}/**/xcschemes/#{scheme || '*'}.xcscheme")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.schemes_from_workspace(workspace_path, scheme)
|
|
51
|
+
return nil unless workspace_path
|
|
52
|
+
|
|
53
|
+
scheme_filepaths = []
|
|
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|
|
|
63
|
+
next if file_reference.path.include?('Pods/Pods.xcodeproj')
|
|
64
|
+
|
|
65
|
+
project_path = file_reference.absolute_path(File.dirname(workspace_path))
|
|
66
|
+
scheme_filepaths.concat(schemes_from_project(project_path, scheme))
|
|
67
|
+
end
|
|
68
|
+
scheme_filepaths
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def self.description
|
|
72
|
+
"☑️Gets all the testplans that a Scheme references"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.authors
|
|
76
|
+
["lyndsey-ferguson/lyndseydf"]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.available_options
|
|
80
|
+
[
|
|
81
|
+
FastlaneCore::ConfigItem.new(
|
|
82
|
+
key: :xcodeproj,
|
|
83
|
+
env_name: "FL_TESTPLANS_FROM_SCHEME_XCODE_PROJECT",
|
|
84
|
+
optional: true,
|
|
85
|
+
description: "The file path to the Xcode project file that references the Scheme",
|
|
86
|
+
verify_block: proc do |path|
|
|
87
|
+
path = File.expand_path(path.to_s)
|
|
88
|
+
UI.user_error!("Error: Xcode project file path not given!") unless path and !path.empty?
|
|
89
|
+
UI.user_error!("Error: Xcode project '#{path}' not found!") unless Dir.exist?(path)
|
|
90
|
+
end
|
|
91
|
+
),
|
|
92
|
+
FastlaneCore::ConfigItem.new(
|
|
93
|
+
key: :workspace,
|
|
94
|
+
env_name: "FL_TESTPLANS_FROM_SCHEME_XCODE_WORKSPACE",
|
|
95
|
+
optional: true,
|
|
96
|
+
description: "The file path to the Xcode workspace file that references the Scheme",
|
|
97
|
+
verify_block: proc do |value|
|
|
98
|
+
v = File.expand_path(value.to_s)
|
|
99
|
+
UI.user_error!("Error: Workspace file not found at path '#{v}'") unless Dir.exist?(v)
|
|
100
|
+
UI.user_error!("Error: Workspace file is not a workspace, must end with .xcworkspace") unless v.include?(".xcworkspace")
|
|
101
|
+
end
|
|
102
|
+
),
|
|
103
|
+
FastlaneCore::ConfigItem.new(
|
|
104
|
+
key: :scheme,
|
|
105
|
+
optional: true,
|
|
106
|
+
env_name: "FL_TESTPLANS_FROM_SCHEME_XCODE_SCHEME",
|
|
107
|
+
description: "The Xcode scheme referencing the testplan",
|
|
108
|
+
verify_block: proc do |scheme_name|
|
|
109
|
+
UI.user_error!("Error: Xcode Scheme '#{scheme_name}' is not valid!") if scheme_name and scheme_name.empty?
|
|
110
|
+
end
|
|
111
|
+
)
|
|
112
|
+
]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def self.example_code
|
|
116
|
+
[
|
|
117
|
+
"
|
|
118
|
+
UI.important(
|
|
119
|
+
'example: ' \\
|
|
120
|
+
'get all the testplans that an Xcode Scheme references'
|
|
121
|
+
)
|
|
122
|
+
testplans = testplans_from_scheme(
|
|
123
|
+
xcodeproj: 'AtomicBoy/AtomicBoy.xcodeproj',
|
|
124
|
+
scheme: 'AtomicBoy'
|
|
125
|
+
)
|
|
126
|
+
UI.message(\"The AtomicBoy uses the following testplans: \#{testplans}\")
|
|
127
|
+
"
|
|
128
|
+
]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.category
|
|
132
|
+
:testing
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def self.is_supported?(platform)
|
|
136
|
+
%i[ios mac].include?(platform)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
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:
|
|
@@ -45,7 +45,7 @@ module TestCenter
|
|
|
45
45
|
|
|
46
46
|
derived_data_path = File.expand_path(@options[:derived_data_path] || Scan.config[:derived_data_path])
|
|
47
47
|
xcresults = Dir.glob("#{derived_data_path}/Logs/Test/*.xcresult")
|
|
48
|
-
if FastlaneCore::Helper.xcode_at_least?('11
|
|
48
|
+
if FastlaneCore::Helper.xcode_at_least?('11')
|
|
49
49
|
xcresults += Dir.glob("#{output_directory}/*.xcresult")
|
|
50
50
|
end
|
|
51
51
|
FastlaneCore::UI.verbose("Deleting xcresults:")
|
|
@@ -292,7 +292,7 @@ module TestCenter
|
|
|
292
292
|
end
|
|
293
293
|
|
|
294
294
|
def retrieve_test_operation_failure(test_session_last_messages)
|
|
295
|
-
if FastlaneCore::Helper.xcode_at_least?(11)
|
|
295
|
+
if FastlaneCore::Helper.xcode_at_least?('11')
|
|
296
296
|
retrieve_test_operation_failure_post_xcode11(test_session_last_messages)
|
|
297
297
|
else
|
|
298
298
|
retrieve_test_operation_failure_pre_xcode11(test_session_last_messages)
|
|
@@ -306,6 +306,8 @@ module TestCenter
|
|
|
306
306
|
test_operation_failure = 'Test device locked'
|
|
307
307
|
elsif /Test runner exited before starting test execution/ =~ test_session_last_messages
|
|
308
308
|
test_operation_failure = 'Test runner exited before starting test execution'
|
|
309
|
+
else
|
|
310
|
+
test_operation_failure = 'Unknown test operation failure'
|
|
309
311
|
end
|
|
310
312
|
test_operation_failure
|
|
311
313
|
end
|
|
@@ -335,8 +337,12 @@ module TestCenter
|
|
|
335
337
|
|
|
336
338
|
glob_pattern = "#{output_directory}/system_logs-*.{log,logarchive}"
|
|
337
339
|
logs = Dir.glob(glob_pattern)
|
|
340
|
+
batch_prefix = ''
|
|
341
|
+
if @options[:batch]
|
|
342
|
+
batch_prefix = "batch-#{@options[:batch]}-"
|
|
343
|
+
end
|
|
338
344
|
logs.each do |log_filepath|
|
|
339
|
-
new_logname = "try-#{testrun_count}-#{File.basename(log_filepath)}"
|
|
345
|
+
new_logname = "#{batch_prefix}try-#{testrun_count}-#{File.basename(log_filepath)}"
|
|
340
346
|
new_log_filepath = "#{File.dirname(log_filepath)}/#{new_logname}"
|
|
341
347
|
FastlaneCore::UI.verbose("Moving simulator log '#{log_filepath}' to '#{new_log_filepath}'")
|
|
342
348
|
File.rename(log_filepath, new_log_filepath)
|
|
@@ -346,15 +352,17 @@ module TestCenter
|
|
|
346
352
|
def move_test_result_bundle_for_next_run
|
|
347
353
|
return unless @options[:result_bundle]
|
|
348
354
|
|
|
349
|
-
|
|
355
|
+
result_extension = FastlaneCore::Helper.xcode_at_least?('11') ? '.xcresult' : '.test_result'
|
|
356
|
+
|
|
357
|
+
glob_pattern = "#{output_directory}/*#{result_extension}"
|
|
350
358
|
preexisting_test_result_bundles = Dir.glob(glob_pattern)
|
|
351
359
|
unnumbered_test_result_bundles = preexisting_test_result_bundles.reject do |test_result|
|
|
352
|
-
test_result =~ /.*-\d
|
|
360
|
+
test_result =~ /.*-\d+\#{result_extension}/
|
|
353
361
|
end
|
|
354
362
|
src_test_bundle = unnumbered_test_result_bundles.first
|
|
355
363
|
dst_test_bundle_parent_dir = File.dirname(src_test_bundle)
|
|
356
|
-
dst_test_bundle_basename = File.basename(src_test_bundle,
|
|
357
|
-
dst_test_bundle = "#{dst_test_bundle_parent_dir}/#{dst_test_bundle_basename}-#{@testrun_count}
|
|
364
|
+
dst_test_bundle_basename = File.basename(src_test_bundle, result_extension)
|
|
365
|
+
dst_test_bundle = "#{dst_test_bundle_parent_dir}/#{dst_test_bundle_basename}-#{@testrun_count}#{result_extension}"
|
|
358
366
|
FastlaneCore::UI.verbose("Moving test_result '#{src_test_bundle}' to '#{dst_test_bundle}'")
|
|
359
367
|
File.rename(src_test_bundle, dst_test_bundle)
|
|
360
368
|
end
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
module TestCenter
|
|
3
2
|
module Helper
|
|
4
3
|
module MultiScanManager
|
|
@@ -40,7 +39,8 @@ module TestCenter
|
|
|
40
39
|
return if @options[:invocation_based_tests] && @options[:only_testing].nil?
|
|
41
40
|
return if @test_collector
|
|
42
41
|
|
|
43
|
-
@test_collector =
|
|
42
|
+
@test_collector = TestCollector.new(@options)
|
|
43
|
+
@options.reject! { |key| %i[testplan].include?(key) }
|
|
44
44
|
@batch_count = @test_collector.test_batches.size
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -224,8 +224,10 @@ module TestCenter
|
|
|
224
224
|
src_xcresult_bundlepath = File.join(testable_output_dir, xcresult_bundlename)
|
|
225
225
|
dst_xcresult_bundlepath = File.join(final_output_dir, xcresult_bundlename)
|
|
226
226
|
|
|
227
|
-
#
|
|
228
|
-
return
|
|
227
|
+
# if there is no destination bundle to merge to, skip it as any source bundle will be copied when complete.
|
|
228
|
+
return if !File.exist?(dst_xcresult_bundlepath)
|
|
229
|
+
# if there is no source bundle to merge, skip it as there is nothing to merge.
|
|
230
|
+
return if !File.exist?(src_xcresult_bundlepath)
|
|
229
231
|
|
|
230
232
|
config = FastlaneCore::Configuration.create(
|
|
231
233
|
Fastlane::Actions::CollateXcresultsAction.available_options,
|
|
@@ -282,6 +284,9 @@ module TestCenter
|
|
|
282
284
|
scheme: @options[:scheme],
|
|
283
285
|
result_bundle: @options[:result_bundle]
|
|
284
286
|
).collate
|
|
287
|
+
logs_glog_pattern = "#{source_reports_directory_glob}/*system_logs-*.{log,logarchive}"
|
|
288
|
+
logs = Dir.glob(logs_glog_pattern)
|
|
289
|
+
FileUtils.mv(logs, absolute_output_directory)
|
|
285
290
|
FileUtils.rm_rf(Dir.glob(source_reports_directory_glob))
|
|
286
291
|
symlink_result_bundle_to_xcresult(absolute_output_directory, report_name_helper)
|
|
287
292
|
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
|
|
@@ -15,7 +15,7 @@ module TestCenter
|
|
|
15
15
|
unless @xctestrun_path && File.exist?(@xctestrun_path)
|
|
16
16
|
FastlaneCore::UI.user_error!("Error: cannot find xctestrun file '#{@xctestrun_path}'")
|
|
17
17
|
end
|
|
18
|
-
@only_testing = options[:only_testing]
|
|
18
|
+
@only_testing = options[:only_testing] || only_testing_from_testplan(options)
|
|
19
19
|
if @only_testing.kind_of?(String)
|
|
20
20
|
@only_testing = @only_testing.split(',')
|
|
21
21
|
end
|
|
@@ -27,6 +27,36 @@ module TestCenter
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
def only_testing_from_testplan(options)
|
|
31
|
+
return unless options[:testplan] && options[:scheme]
|
|
32
|
+
|
|
33
|
+
config = FastlaneCore::Configuration.create(
|
|
34
|
+
Fastlane::Actions::TestplansFromSchemeAction.available_options,
|
|
35
|
+
{
|
|
36
|
+
workspace: options[:workspace],
|
|
37
|
+
xcodeproj: options[:project],
|
|
38
|
+
scheme: options[:scheme]
|
|
39
|
+
}
|
|
40
|
+
)
|
|
41
|
+
testplans = Fastlane::Actions::TestplansFromSchemeAction.run(config)
|
|
42
|
+
FastlaneCore::UI.verbose("TestCollector found testplans: #{testplans}")
|
|
43
|
+
testplan = testplans.find do |testplan_path|
|
|
44
|
+
%r{(.*/?#{ options[:testplan] })\.xctestplan}.match?(testplan_path)
|
|
45
|
+
end
|
|
46
|
+
FastlaneCore::UI.verbose(" using :testplan option, #{options[:testplan]}, using found one: #{testplan}")
|
|
47
|
+
|
|
48
|
+
return if testplan.nil?
|
|
49
|
+
|
|
50
|
+
config = FastlaneCore::Configuration.create(
|
|
51
|
+
Fastlane::Actions::TestOptionsFromTestplanAction.available_options,
|
|
52
|
+
{
|
|
53
|
+
testplan: testplan
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
test_options = Fastlane::Actions::TestOptionsFromTestplanAction.run(config)
|
|
57
|
+
return test_options[:only_testing]
|
|
58
|
+
end
|
|
59
|
+
|
|
30
60
|
def default_derived_data_path(options)
|
|
31
61
|
project_derived_data_path = Scan.project.build_settings(key: "BUILT_PRODUCTS_DIR")
|
|
32
62
|
File.expand_path("../../..", project_derived_data_path)
|
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.11.4
|
|
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-06-
|
|
11
|
+
date: 2020-06-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -267,6 +267,8 @@ files:
|
|
|
267
267
|
- lib/fastlane/plugin/test_center/actions/suppress_tests.rb
|
|
268
268
|
- lib/fastlane/plugin/test_center/actions/suppress_tests_from_junit.rb
|
|
269
269
|
- lib/fastlane/plugin/test_center/actions/suppressed_tests.rb
|
|
270
|
+
- lib/fastlane/plugin/test_center/actions/test_options_from_testplan.rb
|
|
271
|
+
- lib/fastlane/plugin/test_center/actions/testplans_from_scheme.rb
|
|
270
272
|
- lib/fastlane/plugin/test_center/actions/tests_from_junit.rb
|
|
271
273
|
- lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb
|
|
272
274
|
- lib/fastlane/plugin/test_center/helper/html_test_report.rb
|
|
@@ -307,7 +309,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
307
309
|
- !ruby/object:Gem::Version
|
|
308
310
|
version: '0'
|
|
309
311
|
requirements: []
|
|
310
|
-
rubygems_version: 3.0.
|
|
312
|
+
rubygems_version: 3.0.3
|
|
311
313
|
signing_key:
|
|
312
314
|
specification_version: 4
|
|
313
315
|
summary: Makes testing your iOS app easier
|