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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '065618ff56d900cf4b45653bf6985f9f205c200820b4db80d76787bfe8eaa6e9'
4
- data.tar.gz: 7a2a565897e7a29d16b34318f595b83616c51e97afb5551af0d08ceb2a33b08c
3
+ metadata.gz: dd3718fbdf6d40f912c0bec66d449e7e920961212f5dddf72a1960dba9429d24
4
+ data.tar.gz: a261932eb1cb2eb2d24f2d934df71cb3743ef6328a25c64648052678dd95ea3b
5
5
  SHA512:
6
- metadata.gz: 4625cca2677f47cca4d501c16426aa9acc117c0adecbecc1842bb7ef116e99ab100d7827e580639ce18dc3327a4bdbb68d852762320169cffba865448f40fc21
7
- data.tar.gz: 0abfbc89d1fdc4260b199cd20a7bd8c1b05911510cf628b7339a6bd5ce2790b6c3f6b1d7195677b20e020c8810420c826181621f1dbb47a816ce0c0c8ee2c23a
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] || scan_options[:skip_build]
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, testplan].include?(k) }
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
- scheme = params[:scheme]
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 if xcscheme.test_action.nil?
16
- next if xcscheme.test_action.testables.to_a.empty?
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(container_dir, testplan.target_referenced_container.sub('container:', '')))
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
- xcworkspace.file_references.each do |file_reference|
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['ProductModuleName']
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
@@ -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
- nonuniq_testcases = testcases
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
- tc.root.parent.delete_element(failure_details)
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
- `xcrun simctl boot #{self.udid}`
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
- `xcrun simctl shutdown #{self.udid}` unless self.state == "Shutdown"
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>\w+),?/ =~ destination
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
- File.rename(log_filepath, new_log_filepath)
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
- should_run_for_invocation_tests = @options[:invocation_based_tests] && @options[:only_testing].nil?
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 + 1, test_batch),
223
+ output_directory: output_directory(batch_index, test_batch),
188
224
  try_count: @options[:try_count],
189
- batch: batch_index + 1
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
- # 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)
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
- 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
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
- found_match
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
@@ -13,7 +13,7 @@ module TestCenter
13
13
  )
14
14
  # :nocov:
15
15
  end
16
-
16
+
17
17
  def self.remove_preexisting_simulator_logs(params)
18
18
  return unless params[:include_simulator_logs]
19
19
 
@@ -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)
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TestCenter
3
- VERSION = "3.11.2"
3
+ VERSION = "3.13.0"
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.11.2
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-06-17 00:00:00.000000000 Z
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.1.8
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.1.8
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