fastlane-plugin-test_center 3.6.3 → 3.7.0.parallelizing.alpha.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (26) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -0
  3. data/lib/fastlane/plugin/test_center.rb +1 -1
  4. data/lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb +1 -1
  5. data/lib/fastlane/plugin/test_center/actions/multi_scan.rb +109 -25
  6. data/lib/fastlane/plugin/test_center/actions/restart_core_simulator_service.rb +38 -0
  7. data/lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb +16 -4
  8. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager.rb +5 -0
  9. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/device_manager.rb +30 -0
  10. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/interstitial.rb +143 -0
  11. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/parallel_test_batch_worker.rb +27 -0
  12. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/report_collator.rb +115 -0
  13. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb +74 -0
  14. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb +255 -0
  15. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb +356 -0
  16. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb +49 -0
  17. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_manager.rb +317 -0
  18. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker.rb +20 -0
  19. data/lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb +129 -0
  20. data/lib/fastlane/plugin/test_center/helper/reportname_helper.rb +15 -6
  21. data/lib/fastlane/plugin/test_center/helper/test_collector.rb +49 -3
  22. data/lib/fastlane/plugin/test_center/helper/xcodebuild_string.rb +4 -0
  23. data/lib/fastlane/plugin/test_center/helper/xctestrun_info.rb +42 -0
  24. data/lib/fastlane/plugin/test_center/version.rb +1 -1
  25. metadata +38 -10
  26. data/lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb +0 -293
@@ -0,0 +1,20 @@
1
+ module TestCenter
2
+ module Helper
3
+ module MultiScanManager
4
+ class TestBatchWorker
5
+ attr_accessor :state
6
+
7
+ def initialize(options)
8
+ @options = options
9
+ @state = :ready_to_work
10
+ end
11
+
12
+ def run(run_options)
13
+ self.state = :working
14
+ RetryingScan.run(@options.merge(run_options))
15
+ self.state = :ready_to_work
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,129 @@
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_simulator_fork_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
+ @simhelper = SimulatorHelper.new(
23
+ parallelize: true,
24
+ batch_count: @options[:parallel_simulator_fork_count] || @options[:batch_count]
25
+ )
26
+ @simhelper.setup
27
+ @clones = @simhelper.clone_destination_simulators
28
+ main_pid = Process.pid
29
+ at_exit do
30
+ clean_up_cloned_simulators(@clones) if Process.pid == main_pid
31
+ end
32
+ @clones
33
+ end
34
+
35
+ def destination_from_simulators(simulators)
36
+ simulators.map do |simulator|
37
+ "platform=iOS Simulator,id=#{simulator.udid}"
38
+ end
39
+ end
40
+
41
+ def setup_parallel_workers
42
+ setup_cloned_simulators
43
+ desired_worker_count = @options[:parallel_simulator_fork_count]
44
+ @workers = []
45
+ (0...desired_worker_count).each do |worker_index|
46
+ @workers << ParallelTestBatchWorker.new(parallel_scan_options(worker_index))
47
+ end
48
+ end
49
+
50
+ def parallel_scan_options(worker_index)
51
+ options = @options.reject { |key| %i[device devices].include?(key) }
52
+ options[:destination] = destination_from_simulators(@clones[worker_index])
53
+ options[:xctestrun] = xctestrun_products_clone if @options[:xctestrun]
54
+ options[:buildlog_path] = buildlog_path_for_worker(worker_index) if @options[:buildlog_path]
55
+ options[:derived_data_path] = derived_data_path_for_worker(worker_index)
56
+ options
57
+ end
58
+
59
+ def xctestrun_products_clone
60
+ xctestrun_filename = File.basename(@options[:xctestrun])
61
+ xcproduct_dirpath = File.dirname(@options[:xctestrun])
62
+ tmp_xcproduct_dirpath = Dir.mktmpdir
63
+ FileUtils.cp_r(xcproduct_dirpath, tmp_xcproduct_dirpath)
64
+ at_exit do
65
+ FileUtils.rm_rf(tmp_xcproduct_dirpath)
66
+ end
67
+ "#{tmp_xcproduct_dirpath}/#{File.basename(xcproduct_dirpath)}/#{xctestrun_filename}"
68
+ end
69
+
70
+ def buildlog_path_for_worker(worker_index)
71
+ "#{@options[:buildlog_path]}/parallel-simulators-#{worker_index}-logs"
72
+ end
73
+
74
+ def derived_data_path_for_worker(worker_index)
75
+ Dir.mktmpdir(['derived_data_path', "-worker-#{worker_index.to_s}"])
76
+ end
77
+
78
+ def clean_up_cloned_simulators(clones)
79
+ return if clones.nil?
80
+
81
+ clones.flatten.each(&:delete)
82
+ end
83
+
84
+ def setup_serial_workers
85
+ @workers = [
86
+ TestBatchWorker.new(@options)
87
+ ]
88
+ end
89
+
90
+ def wait_for_worker
91
+ if is_serial?
92
+ return @workers[0]
93
+ else
94
+ if_no_available_workers = Proc.new do
95
+ worker = nil
96
+ loop do
97
+ freed_child_proc_pid = Process.wait
98
+ worker = @workers.find do |w|
99
+ w.pid == freed_child_proc_pid
100
+ end
101
+
102
+ break if worker
103
+ end
104
+ # worker.clean_up_or_whatever
105
+ # TODO: do not set state directly
106
+ worker.state == :ready_to_work
107
+ worker
108
+ end
109
+
110
+ first_ready_to_work_worker = @workers.find(if_no_available_workers) do |worker|
111
+ worker.state == :ready_to_work
112
+ end
113
+ end
114
+ end
115
+
116
+ def wait_for_all_workers
117
+ unless is_serial?
118
+ FastlaneCore::UI.message("TestBatchWorkerPool.wait_for_all_workers")
119
+ busy_worker_pids = @workers.each.select { |w| w.state == :working }.map(&:pid)
120
+ busy_worker_pids.each do |pid|
121
+ Process.wait(pid)
122
+ end
123
+ @workers.each { |w| w.state = :ready_to_work }
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -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,9 +17,12 @@ module TestCenter
15
17
  end
16
18
  @only_testing = options[:only_testing]
17
19
  @skip_testing = options[:skip_testing]
20
+ @batch_count = options[:batch_count]
18
21
  end
19
22
 
20
23
  def default_derived_data_path(options)
24
+ # TODO: investigate if this is needed. I believe it should already have
25
+ # been set in multi_scan
21
26
  Scan.project = FastlaneCore::Project.new(
22
27
  options.select { |k, v| %i[workspace project].include?(k) }
23
28
  )
@@ -34,7 +39,9 @@ module TestCenter
34
39
  if @only_testing
35
40
  @testables ||= only_testing_to_testables_tests.keys
36
41
  else
37
- @testables ||= Plist.parse_xml(@xctestrun_path).keys
42
+ @testables ||= Plist.parse_xml(@xctestrun_path).keys.reject do |key|
43
+ key == '__xctestrun_metadata__'
44
+ end
38
45
  end
39
46
  end
40
47
  @testables
@@ -49,13 +56,30 @@ module TestCenter
49
56
  tests
50
57
  end
51
58
 
59
+ def xctestrun_known_tests
60
+ config = FastlaneCore::Configuration.create(::Fastlane::Actions::TestsFromXctestrunAction.available_options, xctestrun: @xctestrun_path)
61
+ ::Fastlane::Actions::TestsFromXctestrunAction.run(config)
62
+ end
63
+
52
64
  def testables_tests
53
65
  unless @testables_tests
54
66
  if @only_testing
67
+ known_tests = nil
55
68
  @testables_tests = only_testing_to_testables_tests
69
+
70
+ @testables_tests.each do |testable, tests|
71
+ tests.each_with_index do |test, index|
72
+ if test.count('/') < 2
73
+ known_tests ||= xctestrun_known_tests[testable]
74
+ test_components = test.split('/')
75
+ testsuite = test_components.size == 1 ? test_components[0] : test_components[1]
76
+ @testables_tests[testable][index] = known_tests.select { |known_test| known_test.include?(testsuite) }
77
+ end
78
+ end
79
+ @testables_tests[testable].flatten!
80
+ end
56
81
  else
57
- config = FastlaneCore::Configuration.create(::Fastlane::Actions::TestsFromXctestrunAction.available_options, xctestrun: @xctestrun_path)
58
- @testables_tests = ::Fastlane::Actions::TestsFromXctestrunAction.run(config)
82
+ @testables_tests = xctestrun_known_tests
59
83
  if @skip_testing
60
84
  skipped_testable_tests = Hash.new { |h, k| h[k] = [] }
61
85
  @skip_testing.sort.each do |skipped_test_identifier|
@@ -68,8 +92,30 @@ module TestCenter
68
92
  end
69
93
  end
70
94
  end
95
+
71
96
  @testables_tests
72
97
  end
98
+
99
+ def test_batches
100
+ if @batches.nil?
101
+ @batches = []
102
+ testables.each do |testable|
103
+ testable_tests = testables_tests[testable]
104
+ next if testable_tests.empty?
105
+
106
+ if @batch_count > 1
107
+ slice_count = [(testable_tests.length / @batch_count.to_f).ceil, 1].max
108
+ testable_tests.each_slice(slice_count).to_a.each do |tests_batch|
109
+ @batches << tests_batch
110
+ end
111
+ else
112
+ @batches << testable_tests
113
+ end
114
+ end
115
+ end
116
+
117
+ @batches
118
+ end
73
119
  end
74
120
  end
75
121
  end
@@ -20,4 +20,8 @@ class String
20
20
  def shellsafe_testidentifier
21
21
  TestCenter::Helper::MUST_SHELLESCAPE_TESTIDENTIFIER ? self.shellescape : self
22
22
  end
23
+
24
+ def strip_testcase
25
+ split('/').first(2).join('/')
26
+ end
23
27
  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.6.3"
3
+ VERSION = "3.7.0.parallelizing.alpha.4"
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.6.3
4
+ version: 3.7.0.parallelizing.alpha.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: 2019-02-11 00:00:00.000000000 Z
11
+ date: 2019-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -67,7 +67,21 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: 1.1.7
69
69
  - !ruby/object:Gem::Dependency
70
- name: bundler
70
+ name: colorize
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: cocoapods
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
87
  - - ">="
@@ -81,7 +95,7 @@ dependencies:
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
97
  - !ruby/object:Gem::Dependency
84
- name: colorize
98
+ name: bundler
85
99
  requirement: !ruby/object:Gem::Requirement
86
100
  requirements:
87
101
  - - ">="
@@ -100,14 +114,14 @@ dependencies:
100
114
  requirements:
101
115
  - - ">="
102
116
  - !ruby/object:Gem::Version
103
- version: 2.56.0
117
+ version: 2.108.0
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - ">="
109
123
  - !ruby/object:Gem::Version
110
- version: 2.56.0
124
+ version: 2.108.0
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: pry
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -235,16 +249,29 @@ files:
235
249
  - lib/fastlane/plugin/test_center/actions/collate_junit_reports.rb
236
250
  - lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb
237
251
  - lib/fastlane/plugin/test_center/actions/multi_scan.rb
252
+ - lib/fastlane/plugin/test_center/actions/restart_core_simulator_service.rb
238
253
  - lib/fastlane/plugin/test_center/actions/suppress_tests.rb
239
254
  - lib/fastlane/plugin/test_center/actions/suppress_tests_from_junit.rb
240
255
  - lib/fastlane/plugin/test_center/actions/suppressed_tests.rb
241
256
  - lib/fastlane/plugin/test_center/actions/tests_from_junit.rb
242
257
  - lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb
243
- - lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb
244
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/interstitial.rb
262
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/parallel_test_batch_worker.rb
263
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/report_collator.rb
264
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan.rb
265
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb
266
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/runner.rb
267
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_helper.rb
268
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/simulator_manager.rb
269
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker.rb
270
+ - lib/fastlane/plugin/test_center/helper/multi_scan_manager/test_batch_worker_pool.rb
245
271
  - lib/fastlane/plugin/test_center/helper/reportname_helper.rb
246
272
  - lib/fastlane/plugin/test_center/helper/test_collector.rb
247
273
  - lib/fastlane/plugin/test_center/helper/xcodebuild_string.rb
274
+ - lib/fastlane/plugin/test_center/helper/xctestrun_info.rb
248
275
  - lib/fastlane/plugin/test_center/version.rb
249
276
  homepage: https://github.com/lyndsey-ferguson/fastlane-plugin-test_center
250
277
  licenses:
@@ -261,11 +288,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
261
288
  version: '0'
262
289
  required_rubygems_version: !ruby/object:Gem::Requirement
263
290
  requirements:
264
- - - ">="
291
+ - - ">"
265
292
  - !ruby/object:Gem::Version
266
- version: '0'
293
+ version: 1.3.1
267
294
  requirements: []
268
- rubygems_version: 3.0.2
295
+ rubyforge_project:
296
+ rubygems_version: 2.6.11
269
297
  signing_key:
270
298
  specification_version: 4
271
299
  summary: Makes testing your iOS app easier