fastlane-plugin-test_center 3.9.0 → 3.10.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7dd99b394133508622e5daab51025772002b0b0369e6b7cfd04055dbb51cc0aa
4
- data.tar.gz: 30abd18975a3259a86ded79a1e466b386f86ef5f56cab4fefa04397bc80fa25a
3
+ metadata.gz: '098e845a533aae1482f49c2265234f95e6b6415a6f389fca3956851f009841da'
4
+ data.tar.gz: 612fa05cd1d3c550ed344ecc34f9f7a54a4d50a701711e33c7154f496d05104e
5
5
  SHA512:
6
- metadata.gz: 225ffa3158f7bf9b8bea8c6d47c897d4926e003a90f4eae9cd4f6cb1074e5de3943bcaf4111d2d819b611260503f9c2fb537cb334fd8d0ee07c0f501dc75b136
7
- data.tar.gz: 449cb428920924775c5aa28c236d367c34453e0ef68d534981c03f8292eb0e8a582043c4992b28e3dff4d21dee6fd96957d2280d39322f34c19a26845aa0e3df
6
+ metadata.gz: cd691d0fd30fc8a3a06d082f9a751a7101c1de6843819136d1fb18b66051435412bf8fe41606012ce65552096337a844ea1536896394b9ce6d679b3e80f00014
7
+ data.tar.gz: 25354716d6138969a45954a0f77429be98ea188e1a8fb9c4bda0b92300ea1d85df128526b65c4ef9e76575561f1aeddcb9c7fd9a9dd1133a6dbed3031af9dd21
data/README.md CHANGED
@@ -91,6 +91,10 @@ For more information about how the `fastlane` plugin system works, check out the
91
91
 
92
92
  _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
93
 
94
+ ## Supporter
95
+
96
+ I'm grateful to all of the people who contributed back to `test_center`, all the people who helped test for new features or bug fixes, but most importantly I am grateful to my wife who supports my crazy passion for making software.
97
+
94
98
  ## License
95
99
 
96
100
  MIT
@@ -14,25 +14,21 @@ module Fastlane
14
14
  class MultiScanAction < Action
15
15
  def self.run(params)
16
16
  params[:quit_simulators] = params._values[:force_quit_simulator] if params._values[:force_quit_simulator]
17
- if params[:output_types]
18
- params[:output_types] = params[:output_types].split(',').map(&:strip).join(',')
19
- end
20
- if params[:output_files]
21
- params[:output_files] = params[:output_files].split(',').map(&:strip).join(',')
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
22
20
  end
23
21
 
24
- if FastlaneCore::Helper.xcode_at_least?('11.0.0')
25
- if params[:result_bundle]
26
- UI.important('As of Xcode 11, test_result bundles created in the output directory are actually symbolic links to an xcresult bundle')
27
- end
28
- elsif params[:output_types]&.include?('xcresult')
29
- UI.important("The 'xcresult' :output_type is only supported for Xcode 11 and greater. You are using #{FastlaneCore::Helper.xcode_version}.")
30
- end
22
+ strip_leading_and_trailing_whitespace_from_output_types(params)
23
+
24
+ warn_of_xcode11_result_bundle_incompatability(params)
25
+ warn_of_parallelism_with_circle_ci(params)
26
+
31
27
  print_multi_scan_parameters(params)
32
28
  force_quit_simulator_processes if params[:quit_simulators]
33
29
 
34
30
  prepare_for_testing(params.values)
35
-
31
+
36
32
  coerce_destination_to_array(params)
37
33
  platform = :mac
38
34
  platform = :ios_simulator if Scan.config[:destination].any? { |d| d.include?('platform=iOS Simulator') }
@@ -43,17 +39,43 @@ module Fastlane
43
39
 
44
40
  summary = run_summary(params, tests_passed)
45
41
  print_run_summary(summary)
46
-
42
+
47
43
  if params[:fail_build] && !tests_passed
48
44
  raise UI.test_failure!('Tests have failed')
49
45
  end
50
46
  summary
51
47
  end
52
48
 
49
+ def self.warn_of_parallelism_with_circle_ci(params)
50
+ if params[:parallel_testrun_count] > 1 && Helper.is_circle_ci?
51
+ UI.important("Warning: problems have occurreed when running parallel simulators on Circle CI.")
52
+ UI.message(" See https://github.com/lyndsey-ferguson/fastlane-plugin-test_center/issues/179")
53
+ end
54
+ end
55
+
56
+ def self.strip_leading_and_trailing_whitespace_from_output_types(params)
57
+ if params[:output_types]
58
+ params[:output_types] = params[:output_types].split(',').map(&:strip).join(',')
59
+ end
60
+ if params[:output_files]
61
+ params[:output_files] = params[:output_files].split(',').map(&:strip).join(',')
62
+ end
63
+ end
64
+
65
+ def self.warn_of_xcode11_result_bundle_incompatability(params)
66
+ if FastlaneCore::Helper.xcode_at_least?('11.0.0')
67
+ if params[:result_bundle]
68
+ UI.important('As of Xcode 11, test_result bundles created in the output directory are actually symbolic links to an xcresult bundle')
69
+ end
70
+ elsif params[:output_types]&.include?('xcresult')
71
+ UI.important("The 'xcresult' :output_type is only supported for Xcode 11 and greater. You are using #{FastlaneCore::Helper.xcode_version}.")
72
+ end
73
+ end
74
+
53
75
  def self.coerce_destination_to_array(params)
54
76
  destination = params[:destination] || Scan.config[:destination] || []
55
77
  unless destination.kind_of?(Array)
56
- params[:destination] = Scan.config[:destination] = [destination]
78
+ params[:destination] = Scan.config[:destination] = [destination]
57
79
  end
58
80
  end
59
81
 
@@ -68,7 +90,7 @@ module Fastlane
68
90
  # :nocov:
69
91
  end
70
92
 
71
- def self.print_run_summary(summary)
93
+ def self.print_run_summary(summary)
72
94
  return if Helper.test?
73
95
 
74
96
  # :nocov:
@@ -147,6 +169,7 @@ module Fastlane
147
169
  def self.prepare_for_testing(scan_options)
148
170
  reset_scan_config_to_defaults
149
171
  use_scanfile_to_override_settings(scan_options)
172
+ turn_off_concurrent_workers(scan_options)
150
173
  ScanHelper.remove_preexisting_simulator_logs(scan_options)
151
174
  if scan_options[:test_without_building] || scan_options[:skip_build]
152
175
  UI.verbose("Preparing Scan config options for multi_scan testing")
@@ -157,6 +180,12 @@ module Fastlane
157
180
  end
158
181
  end
159
182
 
183
+ def self.turn_off_concurrent_workers(scan_options)
184
+ if Gem::Version.new(Fastlane::VERSION) >= Gem::Version.new('2.142.0')
185
+ scan_options.delete(:concurrent_workers) if scan_options[:concurrent_workers].to_i > 0
186
+ end
187
+ end
188
+
160
189
  def self.reset_scan_config_to_defaults
161
190
  return unless Scan.config
162
191
 
@@ -173,7 +202,7 @@ module Fastlane
173
202
  overridden_options = ScanHelper.options_from_configuration_file(
174
203
  ScanHelper.scan_options_from_multi_scan_options(scan_options)
175
204
  )
176
-
205
+
177
206
  unless overridden_options.empty?
178
207
  FastlaneCore::UI.important("Scanfile found: overriding multi_scan options with it's values.")
179
208
  overridden_options.each do |k,v|
@@ -303,6 +332,13 @@ module Fastlane
303
332
  UI.user_error!("Error: Batch counts must be greater than zero") unless count > 0
304
333
  end
305
334
  ),
335
+ FastlaneCore::ConfigItem.new(
336
+ key: :retry_test_runner_failures,
337
+ description: "Set to true If you want to treat build failures during testing, like 'Test runner exited before starting test execution', as 'all tests failed'",
338
+ type: Boolean,
339
+ default_value: false,
340
+ optional: true
341
+ ),
306
342
  FastlaneCore::ConfigItem.new(
307
343
  key: :invocation_based_tests,
308
344
  description: "Set to true If your test suit have invocation based tests like Kiwi",
@@ -374,7 +410,7 @@ module Fastlane
374
410
  UI.important(
375
411
  'example: ' \\
376
412
  'split the tests into 4 batches and run each batch of tests in ' \\
377
- 'parallel up to 3 times if tests fail. Abort the testing early ' \\
413
+ 'parallel up to 3 times if tests fail. Abort the testing early ' \\
378
414
  'if there are too many failing tests by passing in a ' \\
379
415
  ':testrun_completed_block that is called by :multi_scan ' \\
380
416
  'after each run of tests.'
@@ -17,17 +17,23 @@ module Fastlane
17
17
  scheme_filepaths.each do |scheme_filepath|
18
18
  is_dirty = false
19
19
  xcscheme = Xcodeproj::XCScheme.new(scheme_filepath)
20
- xcscheme.test_action.testables.each do |testable|
21
- buildable_name = File.basename(testable.buildable_references[0].buildable_name, '.xctest')
22
-
23
- tests_to_skip = all_tests_to_skip.select { |test| test.start_with?(buildable_name) }
24
- .map { |test| test.sub("#{buildable_name}/", '') }
25
-
26
- tests_to_skip.each do |test_to_skip|
27
- skipped_test = Xcodeproj::XCScheme::TestAction::TestableReference::SkippedTest.new
28
- skipped_test.identifier = test_to_skip
29
- testable.add_skipped_test(skipped_test)
30
- is_dirty = true
20
+ testplans = xcscheme.test_action.test_plans
21
+ unless testplans.nil?
22
+ container_directory = File.absolute_path(File.dirname(params[:xcodeproj] || params[:workspace]))
23
+ update_testplans(container_directory, testplans, all_tests_to_skip)
24
+ else
25
+ xcscheme.test_action.testables.each do |testable|
26
+ buildable_name = File.basename(testable.buildable_references[0].buildable_name, '.xctest')
27
+
28
+ tests_to_skip = all_tests_to_skip.select { |test| test.start_with?(buildable_name) }
29
+ .map { |test| test.sub("#{buildable_name}/", '') }
30
+
31
+ tests_to_skip.each do |test_to_skip|
32
+ skipped_test = Xcodeproj::XCScheme::TestAction::TestableReference::SkippedTest.new
33
+ skipped_test.identifier = test_to_skip
34
+ testable.add_skipped_test(skipped_test)
35
+ is_dirty = true
36
+ end
31
37
  end
32
38
  end
33
39
  xcscheme.save! if is_dirty
@@ -35,6 +41,24 @@ module Fastlane
35
41
  nil
36
42
  end
37
43
 
44
+ def self.update_testplans(container_directory, testplans, all_tests_to_skip)
45
+ testplans.each do |testplan_reference|
46
+ testplan_filename = testplan_reference.target_referenced_container.sub('container:', '')
47
+ testplan_filepath = File.join(container_directory, testplan_filename)
48
+ file = File.read(testplan_filepath)
49
+ testplan = JSON.parse(file)
50
+ testplan['testTargets'].each do |test_target|
51
+ buildable_name = test_target.dig('target', 'name')
52
+ tests_to_skip = all_tests_to_skip.select { |test| test.start_with?(buildable_name) }
53
+ .map { |test| test.sub("#{buildable_name}/", '') }
54
+ test_target['selectedTests'].reject! { |t| tests_to_skip.include?(t) }
55
+ end
56
+ File.open(testplan_filepath, 'w') do |f|
57
+ f.write(JSON.pretty_generate(testplan).gsub('/', '\/'))
58
+ end
59
+ end
60
+ end
61
+
38
62
  def self.schemes_from_project(project_path, scheme)
39
63
  return nil unless project_path
40
64
 
@@ -2,6 +2,7 @@ module Fastlane
2
2
  module Actions
3
3
  class SuppressedTestsAction < Action
4
4
  require 'set'
5
+ require 'json'
5
6
 
6
7
  def self.run(params)
7
8
  scheme = params[:scheme]
@@ -15,13 +16,19 @@ module Fastlane
15
16
  skipped_tests = Set.new
16
17
  scheme_filepaths.each do |scheme_filepath|
17
18
  xcscheme = Xcodeproj::XCScheme.new(scheme_filepath)
18
- xcscheme.test_action.testables.each do |testable|
19
- buildable_name = testable.buildable_references[0]
20
- .buildable_name
19
+ testplans = xcscheme.test_action.test_plans
20
+ unless testplans.nil?
21
+ UI.important("Error: unable to read suppressed tests from Xcode Scheme #{File.basename(scheme_filepath)}.")
22
+ UI.message("The scheme is using a testplan which does not list skipped tests.")
23
+ else
24
+ xcscheme.test_action.testables.each do |testable|
25
+ buildable_name = testable.buildable_references[0]
26
+ .buildable_name
21
27
 
22
- buildable_name = File.basename(buildable_name, '.xctest')
23
- testable.skipped_tests.map do |skipped_test|
24
- skipped_tests.add("#{buildable_name}/#{skipped_test.identifier}")
28
+ buildable_name = File.basename(buildable_name, '.xctest')
29
+ testable.skipped_tests.map do |skipped_test|
30
+ skipped_tests.add("#{buildable_name}/#{skipped_test.identifier}")
31
+ end
25
32
  end
26
33
  end
27
34
  end
@@ -11,10 +11,26 @@ module Fastlane
11
11
  def self.xctestrun_tests(xctestrun_path, invocation_based_tests)
12
12
  xctestrun = Plist.parse_xml(xctestrun_path)
13
13
  xctestrun_rootpath = File.dirname(xctestrun_path)
14
- tests = Hash.new([])
15
- xctestrun.each do |testable_name, xctestrun_config|
16
- next if ignoredTestables.include? testable_name
14
+ xctestrun_version = xctestrun.fetch('__xctestrun_metadata__', Hash.new).fetch('FormatVersion', 1)
17
15
 
16
+ test_targets = []
17
+ if xctestrun_version == 1
18
+ xctestrun.each do |testable_name, test_target_config|
19
+ next if ignoredTestables.include? testable_name
20
+ test_targets << test_target_config
21
+ end
22
+ else
23
+ test_configurations = xctestrun['TestConfigurations']
24
+ test_configurations.each do |configuration|
25
+ configuration['TestTargets'].each do |test_target|
26
+ test_targets << test_target
27
+ end
28
+ end
29
+ end
30
+
31
+ tests = Hash.new([])
32
+ test_targets.each do |xctestrun_config|
33
+ testable_name = xctestrun_config['ProductModuleName']
18
34
  xctest_path = xctest_bundle_path(xctestrun_rootpath, xctestrun_config)
19
35
  test_identifiers = []
20
36
  if xctestrun_config.key?('OnlyTestIdentifiers')
@@ -17,11 +17,18 @@ module TestCenter
17
17
  end
18
18
  # :nocov:
19
19
 
20
- def prepare_scan_config_for_destination
20
+ def prepare_scan_config
21
21
  # this allows multi_scan's `destination` option to be picked up by `scan`
22
22
  scan_config._values.delete(:device)
23
+ ENV.delete('SCAN_DEVICE')
23
24
  scan_config._values.delete(:devices)
24
- scan_config._values.delete(:result_bundle) if ReportNameHelper.includes_xcresult?(@options[:output_types])
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)
25
32
  scan_cache.clear
26
33
  end
27
34
 
@@ -30,12 +37,13 @@ module TestCenter
30
37
  scan_options = @options.select { |k,v| valid_scan_keys.include?(k) }
31
38
  .merge(@retrying_scan_helper.scan_options)
32
39
 
33
- prepare_scan_config_for_destination
40
+ prepare_scan_config
34
41
  scan_options[:build_for_testing] = false
42
+ scan_options.delete(:skip_testing)
35
43
  FastlaneCore::UI.verbose("retrying_scan #update_scan_options")
36
44
  scan_options.each do |k,v|
37
45
  next if v.nil?
38
-
46
+
39
47
  scan_config.set(k,v) unless v.nil?
40
48
  FastlaneCore::UI.verbose("\tSetting #{k.to_s} to #{v}")
41
49
  end
@@ -52,7 +60,7 @@ module TestCenter
52
60
  begin
53
61
  @retrying_scan_helper.before_testrun
54
62
  update_scan_options
55
-
63
+
56
64
  values = scan_config.values(ask: false)
57
65
  values[:xcode_path] = File.expand_path("../..", FastlaneCore::Helper.xcode_path)
58
66
  ScanHelper.print_scan_parameters(values)
@@ -88,11 +88,16 @@ module TestCenter
88
88
 
89
89
  def scan_options
90
90
  valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
91
- xcargs = @options[:xcargs]
91
+ xcargs = @options[:xcargs] || ''
92
92
  if xcargs&.include?('build-for-testing')
93
93
  FastlaneCore::UI.important(":xcargs, #{xcargs}, contained 'build-for-testing', removing it")
94
94
  xcargs.slice!('build-for-testing')
95
95
  end
96
+ if xcargs.include?('-quiet')
97
+ FastlaneCore::UI.important('Disabling -quiet as failing tests cannot be found with it enabled.')
98
+ xcargs.gsub!('-quiet', '')
99
+ end
100
+ xcargs.gsub!(/-parallel-testing-enabled(=|\s+)(YES|NO)/, '')
96
101
  retrying_scan_options = @reportnamer.scan_options.merge(
97
102
  {
98
103
  output_directory: output_directory,
@@ -125,7 +130,11 @@ module TestCenter
125
130
  after_testrun_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
126
131
  FastlaneCore::UI.verbose(after_testrun_message)
127
132
 
128
- handle_build_failure(exception)
133
+ if @options[:retry_test_runner_failures]
134
+ continue_with_build_failure(exception)
135
+ else
136
+ handle_build_failure(exception)
137
+ end
129
138
  else
130
139
  after_testrun_message = "Scan passed the tests"
131
140
  after_testrun_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
@@ -240,12 +249,27 @@ module TestCenter
240
249
  junit: File.absolute_path(report_filepath)
241
250
  }
242
251
  )
243
- @options[:only_testing] = Fastlane::Actions::TestsFromJunitAction.run(config)[:failed].map(&:shellsafe_testidentifier)
252
+ @options[:only_testing] = (@options[:only_testing] || []) - Fastlane::Actions::TestsFromJunitAction.run(config).fetch(:passing, Hash.new).map(&:shellsafe_testidentifier)
244
253
  if @options[:invocation_based_tests]
245
254
  @options[:only_testing] = @options[:only_testing].map(&:strip_testcase).uniq
246
255
  end
247
256
  end
248
257
 
258
+ def continue_with_build_failure(exception)
259
+ test_session_last_messages = last_lines_of_test_session_log
260
+ failure = retrieve_test_operation_failure(test_session_last_messages)
261
+ case failure
262
+ when /Lost connection to testmanagerd/
263
+ FastlaneCore::UI.important("com.apple.CoreSimulator.CoreSimulatorService may have become corrupt, consider quitting it")
264
+ if @options[:quit_core_simulator_service]
265
+ Fastlane::Actions::RestartCoreSimulatorServiceAction.run
266
+ end
267
+ else
268
+ FastlaneCore::UI.important(test_session_last_messages)
269
+ end
270
+ send_callback_testrun_info(test_operation_failure: failure)
271
+ end
272
+
249
273
  def handle_build_failure(exception)
250
274
  test_session_last_messages = last_lines_of_test_session_log
251
275
  failure = retrieve_test_operation_failure(test_session_last_messages)
@@ -262,11 +286,31 @@ module TestCenter
262
286
  FastlaneCore::UI.error(test_session_last_messages)
263
287
  send_callback_testrun_info(test_operation_failure: failure)
264
288
  raise exception
289
+ FastlaneCore::UI.important(test_session_last_messages)
265
290
  end
266
291
  send_callback_testrun_info(test_operation_failure: failure)
267
292
  end
268
293
 
269
294
  def retrieve_test_operation_failure(test_session_last_messages)
295
+ if FastlaneCore::Helper.xcode_at_least?(11)
296
+ retrieve_test_operation_failure_post_xcode11(test_session_last_messages)
297
+ else
298
+ retrieve_test_operation_failure_pre_xcode11(test_session_last_messages)
299
+ end
300
+ end
301
+
302
+ def retrieve_test_operation_failure_post_xcode11(test_session_last_messages)
303
+ if /Connection peer refused channel request/ =~ test_session_last_messages
304
+ test_operation_failure = 'Lost connection to testmanagerd'
305
+ elsif /Please unlock your device and reattach/ =~ test_session_last_messages
306
+ test_operation_failure = 'Test device locked'
307
+ elsif /Test runner exited before starting test execution/ =~ test_session_last_messages
308
+ test_operation_failure = 'Test runner exited before starting test execution'
309
+ end
310
+ test_operation_failure
311
+ end
312
+
313
+ def retrieve_test_operation_failure_pre_xcode11(test_session_last_messages)
270
314
  test_operation_failure_match = /Test operation failure: (?<test_operation_failure>.*)$/ =~ test_session_last_messages
271
315
  if test_operation_failure_match.nil?
272
316
  test_operation_failure = 'Unknown test operation failure'
@@ -7,7 +7,7 @@ module TestCenter
7
7
  require 'json'
8
8
  require 'shellwords'
9
9
  require 'snapshot/reset_simulators'
10
-
10
+
11
11
  class Runner
12
12
  attr_reader :retry_total_count
13
13
 
@@ -66,12 +66,12 @@ module TestCenter
66
66
  end
67
67
 
68
68
  unless tests_passed || @options[:try_count] < 1
69
- setup_testcollector
69
+ setup_testcollector
70
70
  tests_passed = run_test_batches
71
71
  end
72
72
  tests_passed
73
73
  end
74
-
74
+
75
75
  def should_run_tests_through_single_try?
76
76
  should_run_for_invocation_tests = @options[:invocation_based_tests] && @options[:only_testing].nil?
77
77
  should_run_for_skip_build = @options[:skip_build]
@@ -114,16 +114,16 @@ module TestCenter
114
114
  end
115
115
  @options[:output_directory] = output_directory
116
116
  @options[:destination] = Scan.config[:destination]
117
-
117
+
118
118
  # We do not want Scan.config to _not_ have :device :devices, we want to
119
119
  # use :destination. We remove :force_quit_simulator as we do not want
120
120
  # Scan to handle it as multi_scan takes care of it in its own way
121
121
  options = @options.reject { |key| %i[device devices force_quit_simulator].include?(key) }
122
122
  options[:try_count] = 1
123
-
123
+
124
124
  tests_passed = RetryingScan.run(options)
125
125
  @options[:try_count] -= 1
126
-
126
+
127
127
  reportnamer = ReportNameHelper.new(
128
128
  @options[:output_types],
129
129
  @options[:output_files],
@@ -138,12 +138,12 @@ module TestCenter
138
138
  )
139
139
  @options[:only_testing] = retrieve_failed_single_try_tests
140
140
  @options[:only_testing] = @options[:only_testing].map(&:strip_testcase).uniq
141
-
141
+
142
142
  symlink_result_bundle_to_xcresult(output_directory, reportnamer)
143
143
 
144
144
  tests_passed
145
145
  end
146
-
146
+
147
147
  def retrieve_failed_single_try_tests
148
148
  reportnamer = ReportNameHelper.new(
149
149
  @options[:output_types],
@@ -168,10 +168,10 @@ module TestCenter
168
168
 
169
169
  pool = TestBatchWorkerPool.new(pool_options)
170
170
  pool.setup_workers
171
-
171
+
172
172
  remaining_test_batches = @test_collector.test_batches.clone
173
173
  remaining_test_batches.each_with_index do |test_batch, current_batch_index|
174
- worker = pool.wait_for_worker
174
+ worker = pool.wait_for_worker
175
175
  FastlaneCore::UI.message("Starting test run #{current_batch_index + 1}")
176
176
  worker.run(scan_options_for_worker(test_batch, current_batch_index))
177
177
  end
@@ -189,7 +189,7 @@ module TestCenter
189
189
  batch: batch_index + 1
190
190
  }
191
191
  end
192
-
192
+
193
193
  def collate_batched_reports
194
194
  return unless @batch_count > 1
195
195
  return unless @options[:collate_reports]
@@ -207,10 +207,44 @@ module TestCenter
207
207
  File.absolute_path(output_directory),
208
208
  @test_collector.testables.first
209
209
  )
210
+ merge_single_testable_xcresult_with_final_xcresult(report_files_dir, File.absolute_path(output_directory))
210
211
  FileUtils.cp_r("#{report_files_dir}/.", File.absolute_path(output_directory))
211
212
  FileUtils.rm_rf(report_files_dir)
212
213
  end
213
214
 
215
+ def merge_single_testable_xcresult_with_final_xcresult(testable_output_dir, final_output_dir)
216
+ reportnamer = ReportNameHelper.new(
217
+ @options[:output_types],
218
+ @options[:output_files],
219
+ @options[:custom_report_file_name]
220
+ )
221
+ return unless reportnamer.includes_xcresult?
222
+
223
+ xcresult_bundlename = reportnamer.xcresult_bundlename
224
+ src_xcresult_bundlepath = File.join(testable_output_dir, xcresult_bundlename)
225
+ dst_xcresult_bundlepath = File.join(final_output_dir, xcresult_bundlename)
226
+
227
+ # We do not need to merge if one of these do not exist
228
+ return unless File.exist?(src_xcresult_bundlepath) || File.exist?(dst_xcresult_bundlepath)
229
+
230
+ config = FastlaneCore::Configuration.create(
231
+ Fastlane::Actions::CollateXcresultsAction.available_options,
232
+ {
233
+ xcresults: [src_xcresult_bundlepath, dst_xcresult_bundlepath],
234
+ collated_xcresult: dst_xcresult_bundlepath
235
+ }
236
+ )
237
+ FastlaneCore::UI.verbose("Merging xcresult '#{src_xcresult_bundlepath}' to '#{dst_xcresult_bundlepath}'")
238
+ Fastlane::Actions::CollateXcresultsAction.run(config)
239
+ FileUtils.rm_rf(src_xcresult_bundlepath)
240
+ if @result_bundle_desired
241
+ xcresult_bundlename = reportnamer.xcresult_bundlename
242
+ test_result_bundlename = File.basename(xcresult_bundlename, '.*') + '.test_result'
243
+ test_result_bundlename_path = File.join(testable_output_dir, test_result_bundlename)
244
+ FileUtils.rm_rf(test_result_bundlename_path)
245
+ end
246
+ end
247
+
214
248
  def symlink_result_bundle_to_xcresult(output_dir, reportname_helper)
215
249
  return unless @result_bundle_desired && reportname_helper.includes_xcresult?
216
250
 
@@ -16,11 +16,22 @@ module TestCenter
16
16
  cloned_simulators = []
17
17
 
18
18
  run_count = @options[:parallel_testrun_count] || 0
19
- destination_simulator_ids = Scan.config[:destination].map do |destination|
20
- destination.split(',id=').last
21
- end
22
19
  original_simulators = FastlaneCore::DeviceManager.simulators('iOS').find_all do |simulator|
23
- destination_simulator_ids.include?(simulator.udid)
20
+ found_match = false
21
+ Scan.config[:destination].each do |destination|
22
+ match = destination.match(/id=(?<udid>[^,]+)/)
23
+ if match
24
+ found_match = (match[:udid] == simulator.udid)
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
33
+ end
34
+ found_match
24
35
  end
25
36
  original_simulators.each(&:shutdown)
26
37
  (0...run_count).each do |batch_index|
@@ -42,9 +42,7 @@ module TestCenter
42
42
  if @only_testing
43
43
  @testables ||= only_testing_to_testables_tests.keys
44
44
  else
45
- @testables ||= Plist.parse_xml(@xctestrun_path).keys.reject do |key|
46
- key == '__xctestrun_metadata__'
47
- end
45
+ @testables = xctestrun_known_tests.keys
48
46
  end
49
47
  end
50
48
  @testables
@@ -80,7 +78,7 @@ module TestCenter
80
78
  known_tests += xctestrun_known_tests[testable]
81
79
  test_components = test.split('/')
82
80
  testsuite = test_components.size == 1 ? test_components[0] : test_components[1]
83
- @testables_tests[testable][index] = known_tests.select { |known_test| known_test.include?(testsuite) }
81
+ @testables_tests[testable][index] = known_tests.select { |known_test| known_test.include?(testsuite) }
84
82
  end
85
83
  end
86
84
  @testables_tests[testable].flatten!
@@ -106,7 +104,7 @@ module TestCenter
106
104
  end
107
105
  end
108
106
  end
109
-
107
+
110
108
  @testables_tests
111
109
  end
112
110
 
@@ -0,0 +1,25 @@
1
+ module Xcodeproj
2
+ class XCScheme
3
+ class TestAction < AbstractSchemeAction
4
+ def test_plans
5
+ return [] unless @xml_element.elements['TestPlans']
6
+
7
+ @xml_element.elements['TestPlans'].get_elements('TestPlanReference').map do |node|
8
+ TestPlanReference.new(node)
9
+ end
10
+ end
11
+ end
12
+
13
+ class TestPlanReference < XMLElementWrapper
14
+ def initialize(node)
15
+ create_xml_element_with_fallback(node, 'TestPlanReference') do
16
+ end
17
+ end
18
+
19
+ def target_referenced_container
20
+ @xml_element.attributes['reference']
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TestCenter
3
- VERSION = "3.9.0"
3
+ VERSION = "3.10.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-test_center
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.0
4
+ version: 3.10.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lyndsey Ferguson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-20 00:00:00.000000000 Z
11
+ date: 2020-06-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -285,6 +285,7 @@ files:
285
285
  - lib/fastlane/plugin/test_center/helper/scan_helper.rb
286
286
  - lib/fastlane/plugin/test_center/helper/test_collector.rb
287
287
  - lib/fastlane/plugin/test_center/helper/xcodebuild_string.rb
288
+ - lib/fastlane/plugin/test_center/helper/xcodeproj/scheme/test_action.rb
288
289
  - lib/fastlane/plugin/test_center/helper/xctestrun_info.rb
289
290
  - lib/fastlane/plugin/test_center/version.rb
290
291
  homepage: https://github.com/lyndsey-ferguson/fastlane-plugin-test_center