fastlane-plugin-test_center 3.7.0 → 3.8.0.parallelizing.beta.1

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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +71 -6
  3. data/lib/fastlane/plugin/test_center/actions/collate_html_reports.rb +2 -0
  4. data/lib/fastlane/plugin/test_center/actions/collate_json_reports.rb +2 -0
  5. data/lib/fastlane/plugin/test_center/actions/collate_junit_reports.rb +2 -1
  6. data/lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb +2 -0
  7. data/lib/fastlane/plugin/test_center/actions/multi_scan.rb +127 -32
  8. data/lib/fastlane/plugin/test_center/actions/quit_core_simulator_service.rb +40 -0
  9. data/lib/fastlane/plugin/test_center/actions/suppress_tests.rb +2 -0
  10. data/lib/fastlane/plugin/test_center/actions/suppress_tests_from_junit.rb +3 -1
  11. data/lib/fastlane/plugin/test_center/actions/suppressed_tests.rb +2 -0
  12. data/lib/fastlane/plugin/test_center/actions/tests_from_junit.rb +2 -0
  13. data/lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb +2 -0
  14. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb +30 -0
  15. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/parallel_test_batch_worker.rb +73 -0
  16. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/report_collator.rb +129 -0
  17. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb +77 -0
  18. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb +272 -0
  19. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +109 -0
  20. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb +49 -0
  21. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker.rb +24 -0
  22. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb +133 -0
  23. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb +3 -0
  24. data/lib/fastlane/plugin/test_center/helper/reportname_helper.rb +15 -6
  25. data/lib/fastlane/plugin/test_center/helper/test_collector.rb +55 -14
  26. data/lib/fastlane/plugin/test_center/helper/xctestrun_info.rb +42 -0
  27. data/lib/fastlane/plugin/test_center/version.rb +1 -1
  28. data/lib/fastlane/plugin/test_center.rb +1 -1
  29. metadata +22 -11
  30. data/lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb +0 -315
@@ -0,0 +1,133 @@
1
+ module TestCenter
2
+ module Helper
3
+ module MultiScanManager
4
+ class TestBatchWorkerPool
5
+ def initialize(options)
6
+ @options = options
7
+ end
8
+
9
+ def is_serial?
10
+ @options.fetch(:parallel_testrun_count, 1) == 1
11
+ end
12
+
13
+ def setup_workers
14
+ if is_serial?
15
+ setup_serial_workers
16
+ else
17
+ setup_parallel_workers
18
+ end
19
+ end
20
+
21
+ def setup_cloned_simulators
22
+ return [] unless @options[:platform] == :ios
23
+
24
+ @simhelper = SimulatorHelper.new(
25
+ parallel_testrun_count: @options[:parallel_testrun_count]
26
+ )
27
+ @simhelper.setup
28
+ @clones = @simhelper.clone_destination_simulators
29
+ main_pid = Process.pid
30
+ at_exit do
31
+ clean_up_cloned_simulators(@clones) if Process.pid == main_pid
32
+ end
33
+ @clones
34
+ end
35
+
36
+ def destination_for_worker(worker_index)
37
+ return @options[:destination] unless @options[:platform] == :ios
38
+
39
+ @clones[worker_index].map do |simulator|
40
+ "platform=iOS Simulator,id=#{simulator.udid}"
41
+ end
42
+ end
43
+
44
+ def setup_parallel_workers
45
+ setup_cloned_simulators
46
+ desired_worker_count = @options[:parallel_testrun_count]
47
+ @workers = []
48
+ (0...desired_worker_count).each do |worker_index|
49
+ @workers << ParallelTestBatchWorker.new(parallel_scan_options(worker_index))
50
+ end
51
+ end
52
+
53
+ def parallel_scan_options(worker_index)
54
+ options = @options.reject { |key| %i[device devices].include?(key) }
55
+ options[:destination] = destination_for_worker(worker_index)
56
+ options[:xctestrun] = xctestrun_products_clone if @options[:xctestrun]
57
+ options[:buildlog_path] = buildlog_path_for_worker(worker_index) if @options[:buildlog_path]
58
+ options[:derived_data_path] = derived_data_path_for_worker(worker_index)
59
+ options[:batch_index] = worker_index
60
+ options[:test_batch_results] = @options[:test_batch_results]
61
+ options
62
+ end
63
+
64
+ def xctestrun_products_clone
65
+ xctestrun_filename = File.basename(@options[:xctestrun])
66
+ xcproduct_dirpath = File.dirname(@options[:xctestrun])
67
+ tmp_xcproduct_dirpath = Dir.mktmpdir
68
+ FileUtils.cp_r(xcproduct_dirpath, tmp_xcproduct_dirpath)
69
+ at_exit do
70
+ FileUtils.rm_rf(tmp_xcproduct_dirpath)
71
+ end
72
+ "#{tmp_xcproduct_dirpath}/#{File.basename(xcproduct_dirpath)}/#{xctestrun_filename}"
73
+ end
74
+
75
+ def buildlog_path_for_worker(worker_index)
76
+ "#{@options[:buildlog_path]}/parallel-simulators-#{worker_index}-logs"
77
+ end
78
+
79
+ def derived_data_path_for_worker(worker_index)
80
+ Dir.mktmpdir(['derived_data_path', "-worker-#{worker_index.to_s}"])
81
+ end
82
+
83
+ def clean_up_cloned_simulators(clones)
84
+ return if clones.nil?
85
+
86
+ clones.flatten.each(&:delete)
87
+ end
88
+
89
+ def setup_serial_workers
90
+ serial_scan_options = @options.reject { |key| %i[device devices].include?(key) }
91
+ serial_scan_options[:destination] ||= Scan&.config&.fetch(:destination)
92
+ @workers = [
93
+ TestBatchWorker.new(serial_scan_options)
94
+ ]
95
+ end
96
+
97
+ def wait_for_worker
98
+ if is_serial?
99
+ return @workers[0]
100
+ else
101
+ if_no_available_workers = Proc.new do
102
+ worker = nil
103
+ loop do
104
+ freed_child_proc_pid = Process.wait
105
+ worker = @workers.find do |w|
106
+ w.pid == freed_child_proc_pid
107
+ end
108
+
109
+ break if worker
110
+ end
111
+ worker.process_results
112
+ worker
113
+ end
114
+
115
+ first_ready_to_work_worker = @workers.find(if_no_available_workers) do |worker|
116
+ worker.state == :ready_to_work
117
+ end
118
+ end
119
+ end
120
+
121
+ def wait_for_all_workers
122
+ unless is_serial?
123
+ busy_workers = @workers.each.select { |w| w.state == :working }
124
+ busy_workers.map(&:pid).each do |pid|
125
+ Process.wait(pid)
126
+ end
127
+ busy_workers.each { |w| w.process_results }
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'multi_scan_manager/report_collator'
2
+ require_relative 'multi_scan_manager/runner'
3
+ require_relative 'xctestrun_info'
@@ -64,9 +64,12 @@ module TestCenter
64
64
  numbered_filename(@output_files.to_s.split(',')[junit_index])
65
65
  end
66
66
 
67
- def junit_reportname
67
+ def junit_reportname(suffix = '')
68
68
  junit_index = @output_types.split(',').find_index('junit')
69
- @output_files.to_s.split(',')[junit_index]
69
+ report_name = @output_files.to_s.split(',')[junit_index]
70
+ return report_name if suffix.empty?
71
+
72
+ "#{File.basename(report_name, '.*')}-#{suffix}#{junit_filextension}"
70
73
  end
71
74
 
72
75
  def junit_filextension
@@ -90,9 +93,12 @@ module TestCenter
90
93
  numbered_filename(@output_files.to_s.split(',')[html_index])
91
94
  end
92
95
 
93
- def html_reportname
96
+ def html_reportname(suffix = '')
94
97
  html_index = @output_types.split(',').find_index('html')
95
- @output_files.to_s.split(',')[html_index]
98
+ report_name = @output_files.to_s.split(',')[html_index]
99
+ return report_name if suffix.empty?
100
+
101
+ "#{File.basename(report_name, '.*')}-#{suffix}#{html_filextension}"
96
102
  end
97
103
 
98
104
  def html_filextension
@@ -116,9 +122,12 @@ module TestCenter
116
122
  numbered_filename(@output_files.to_s.split(',')[json_index])
117
123
  end
118
124
 
119
- def json_reportname
125
+ def json_reportname(suffix = '')
120
126
  json_index = @output_types.split(',').find_index('json')
121
- @output_files.to_s.split(',')[json_index]
127
+ report_name = @output_files.to_s.split(',')[json_index]
128
+ return report_name if suffix.empty?
129
+
130
+ "#{File.basename(report_name, '.*')}-#{suffix}#{json_filextension}"
122
131
  end
123
132
 
124
133
  def json_filextension
@@ -5,6 +5,8 @@ module TestCenter
5
5
  require 'plist'
6
6
 
7
7
  class TestCollector
8
+ attr_reader :xctestrun_path
9
+
8
10
  def initialize(options)
9
11
  unless options[:xctestrun] || options[:derived_data_path]
10
12
  options[:derived_data_path] = default_derived_data_path(options)
@@ -15,13 +17,13 @@ module TestCenter
15
17
  end
16
18
  @only_testing = options[:only_testing]
17
19
  @skip_testing = options[:skip_testing]
18
- @invocation_based_tests = options[:invocation_based_tests]
20
+ @batch_count = options[:batch_count]
21
+ if @batch_count == 1 && options[:parallel_testrun_count] > 1
22
+ @batch_count = options[:parallel_testrun_count]
23
+ end
19
24
  end
20
25
 
21
26
  def default_derived_data_path(options)
22
- Scan.project = FastlaneCore::Project.new(
23
- options.select { |k, v| %i[workspace project].include?(k) }
24
- )
25
27
  project_derived_data_path = Scan.project.build_settings(key: "BUILT_PRODUCTS_DIR")
26
28
  File.expand_path("../../..", project_derived_data_path)
27
29
  end
@@ -35,8 +37,8 @@ module TestCenter
35
37
  if @only_testing
36
38
  @testables ||= only_testing_to_testables_tests.keys
37
39
  else
38
- @testables ||= Plist.parse_xml(@xctestrun_path).keys.reject do |retrievedTestable|
39
- Fastlane::Actions::TestsFromXctestrunAction.ignoredTestables.include?(retrievedTestable)
40
+ @testables ||= Plist.parse_xml(@xctestrun_path).keys.reject do |key|
41
+ key == '__xctestrun_metadata__'
40
42
  end
41
43
  end
42
44
  end
@@ -52,19 +54,36 @@ module TestCenter
52
54
  tests
53
55
  end
54
56
 
57
+ def xctestrun_known_tests
58
+ config = FastlaneCore::Configuration.create(
59
+ ::Fastlane::Actions::TestsFromXctestrunAction.available_options,
60
+ {
61
+ xctestrun: @xctestrun_path,
62
+ invocation_based_tests: @invocation_based_tests
63
+ }
64
+ )
65
+ ::Fastlane::Actions::TestsFromXctestrunAction.run(config)
66
+ end
67
+
55
68
  def testables_tests
56
69
  unless @testables_tests
57
70
  if @only_testing
71
+ known_tests = nil
58
72
  @testables_tests = only_testing_to_testables_tests
73
+
74
+ @testables_tests.each do |testable, tests|
75
+ tests.each_with_index do |test, index|
76
+ if test.count('/') < 2
77
+ known_tests ||= xctestrun_known_tests[testable]
78
+ test_components = test.split('/')
79
+ testsuite = test_components.size == 1 ? test_components[0] : test_components[1]
80
+ @testables_tests[testable][index] = known_tests.select { |known_test| known_test.include?(testsuite) }
81
+ end
82
+ end
83
+ @testables_tests[testable].flatten!
84
+ end
59
85
  else
60
- config = FastlaneCore::Configuration.create(
61
- ::Fastlane::Actions::TestsFromXctestrunAction.available_options,
62
- {
63
- xctestrun: @xctestrun_path,
64
- invocation_based_tests: @invocation_based_tests
65
- }
66
- )
67
- @testables_tests = ::Fastlane::Actions::TestsFromXctestrunAction.run(config)
86
+ @testables_tests = xctestrun_known_tests
68
87
  if @skip_testing
69
88
  skipped_testable_tests = Hash.new { |h, k| h[k] = [] }
70
89
  @skip_testing.sort.each do |skipped_test_identifier|
@@ -77,8 +96,30 @@ module TestCenter
77
96
  end
78
97
  end
79
98
  end
99
+
80
100
  @testables_tests
81
101
  end
102
+
103
+ def test_batches
104
+ if @batches.nil?
105
+ @batches = []
106
+ testables.each do |testable|
107
+ testable_tests = testables_tests[testable]
108
+ next if testable_tests.empty?
109
+
110
+ if @batch_count > 1
111
+ slice_count = [(testable_tests.length / @batch_count.to_f).ceil, 1].max
112
+ testable_tests.each_slice(slice_count).to_a.each do |tests_batch|
113
+ @batches << tests_batch
114
+ end
115
+ else
116
+ @batches << testable_tests
117
+ end
118
+ end
119
+ end
120
+
121
+ @batches
122
+ end
82
123
  end
83
124
  end
84
125
  end
@@ -0,0 +1,42 @@
1
+ module TestCenter
2
+ module Helper
3
+ require 'plist'
4
+
5
+ class XCTestrunInfo
6
+ def initialize(xctestrun_filepath)
7
+ raise Errno::ENOENT, xctestrun_filepath unless File.exist?(xctestrun_filepath)
8
+
9
+ @xctestrun = Plist.parse_xml(xctestrun_filepath)
10
+ @xctestrun_rootpath = File.dirname(xctestrun_filepath)
11
+ end
12
+
13
+ def app_path_for_testable(testable)
14
+ @xctestrun[testable].fetch('UITargetAppPath') do |_|
15
+ @xctestrun[testable]['TestHostPath']
16
+ end.sub('__TESTROOT__', @xctestrun_rootpath)
17
+ end
18
+
19
+ def app_plist_for_testable(testable)
20
+ binary_plistfile = File.join(app_path_for_testable(testable), 'Info.plist')
21
+
22
+ Plist.parse_binary_xml(binary_plistfile)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ require 'plist'
29
+
30
+ class Hash
31
+ def save_binary_plist(filename, options = {})
32
+ Plist::Emit.save_plist(self, filename)
33
+ `plutil -convert binary1 \"#{filename}\"`
34
+ end
35
+ end
36
+
37
+ module Plist
38
+ def self.parse_binary_xml(filename)
39
+ `plutil -convert xml1 \"#{filename}\"`
40
+ Plist.parse_xml(filename)
41
+ end
42
+ end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TestCenter
3
- VERSION = "3.7.0"
3
+ VERSION = "3.8.0.parallelizing.beta.1"
4
4
  end
5
5
  end
@@ -4,7 +4,7 @@ module Fastlane
4
4
  module TestCenter
5
5
  # Return all .rb files inside the "actions" and "helper" directory
6
6
  def self.all_classes
7
- Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
7
+ Dir[File.expand_path('**/{actions,helper}/**/*.rb', File.dirname(__FILE__))]
8
8
  end
9
9
  end
10
10
  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.7.0
4
+ version: 3.8.0.parallelizing.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lyndsey Ferguson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-30 00:00:00.000000000 Z
11
+ date: 2019-06-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -67,13 +67,13 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.1.7
69
69
  - !ruby/object:Gem::Dependency
70
- name: cocoapods
70
+ name: colorize
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
- type: :development
76
+ type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: bundler
84
+ name: cocoapods
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -95,7 +95,7 @@ dependencies:
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
97
  - !ruby/object:Gem::Dependency
98
- name: colorize
98
+ name: bundler
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ">="
@@ -114,14 +114,14 @@ dependencies:
114
114
  requirements:
115
115
  - - ">="
116
116
  - !ruby/object:Gem::Version
117
- version: 2.56.0
117
+ version: 2.108.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
- version: 2.56.0
124
+ version: 2.108.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: pry
127
127
  requirement: !ruby/object:Gem::Requirement
@@ -249,16 +249,27 @@ files:
249
249
  - lib/fastlane/plugin/test_center/actions/collate_junit_reports.rb
250
250
  - lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb
251
251
  - lib/fastlane/plugin/test_center/actions/multi_scan.rb
252
+ - lib/fastlane/plugin/test_center/actions/quit_core_simulator_service.rb
252
253
  - lib/fastlane/plugin/test_center/actions/suppress_tests.rb
253
254
  - lib/fastlane/plugin/test_center/actions/suppress_tests_from_junit.rb
254
255
  - lib/fastlane/plugin/test_center/actions/suppressed_tests.rb
255
256
  - lib/fastlane/plugin/test_center/actions/tests_from_junit.rb
256
257
  - lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb
257
- - lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb
258
258
  - lib/fastlane/plugin/test_center/helper/junit_helper.rb
259
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb
260
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb
261
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/parallel_test_batch_worker.rb
262
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/report_collator.rb
263
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb
264
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb
265
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb
266
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb
267
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker.rb
268
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb
259
269
  - lib/fastlane/plugin/test_center/helper/reportname_helper.rb
260
270
  - lib/fastlane/plugin/test_center/helper/test_collector.rb
261
271
  - lib/fastlane/plugin/test_center/helper/xcodebuild_string.rb
272
+ - lib/fastlane/plugin/test_center/helper/xctestrun_info.rb
262
273
  - lib/fastlane/plugin/test_center/version.rb
263
274
  homepage: https://github.com/lyndsey-ferguson/fastlane-plugin-test_center
264
275
  licenses:
@@ -275,9 +286,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
275
286
  version: '0'
276
287
  required_rubygems_version: !ruby/object:Gem::Requirement
277
288
  requirements:
278
- - - ">="
289
+ - - ">"
279
290
  - !ruby/object:Gem::Version
280
- version: '0'
291
+ version: 1.3.1
281
292
  requirements: []
282
293
  rubyforge_project:
283
294
  rubygems_version: 2.6.11