fastlane-plugin-test_center 2.5.1 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4a225615997474cf16464c3fd57d09d783e18cef
4
- data.tar.gz: 4204b66e3a8c23b16e823b03c38b84b72c559293
3
+ metadata.gz: e52d3f37f8075a9050200d5d5cf19b8fa0b02fb8
4
+ data.tar.gz: b2c7b4dd8c192c105dbb71a8aa390425c856aa24
5
5
  SHA512:
6
- metadata.gz: a146b8bb62a9fd8a0a28ed0c5edd5e61a820004ca96fbdc8afc494bc6c2906dd865644265f9170e034c5b4606abf0adcf737d3a8d1f175ed6d2db80860b1b758
7
- data.tar.gz: 50ffa07ac1b8ca36e3c8362ee399ab75d5722763169b91f65d9612c1c903b3445b7d9286bf54bfe66fadbd7df209f50c828ffb29e199a34af82d7217055d3138
6
+ metadata.gz: 4dc6d5e02ea3c35c4504a493b4c0e3197576acaf203b5f92fffb3487a28f5e3309943df72b852df10d012374066f63949a570b319b768046eec2c67113cc342c
7
+ data.tar.gz: 9b6c0b59e057fe140964369185e2c9373939441f031d5ce19570d521fd961b64ef4d8539f30ccab2a8772fcc7084c13e1436cd2bfeceec739d23f9ecd2660565
@@ -1,5 +1,6 @@
1
1
  module Fastlane
2
2
  module Actions
3
+ require 'fastlane_core/ui/errors/fastlane_common_error'
3
4
  require 'fastlane/actions/scan'
4
5
  require 'shellwords'
5
6
  require 'xctest_list'
@@ -7,218 +8,70 @@ module Fastlane
7
8
 
8
9
  class MultiScanAction < Action
9
10
  def self.run(params)
10
- scan_options = params.values.reject { |k| %i[try_count batch_count].include?(k) }
11
-
12
11
  unless Helper.test?
13
12
  FastlaneCore::PrintTable.print_values(
14
- config: params._values.reject { |k, v| scan_options.key?(k) },
13
+ config: params._values.select { |k, _| %i[try_count batch_count fail_build].include?(k) },
15
14
  title: "Summary for multi_scan (test_center v#{Fastlane::TestCenter::VERSION})"
16
15
  )
17
16
  end
18
-
19
- scan_options = config_with_junit_report(scan_options)
20
-
21
- unless scan_options[:test_without_building]
22
- build_for_testing(scan_options)
23
- scan_options.delete(:build_for_testing)
24
- scan_options[:test_without_building] = true
25
- end
26
-
27
- batch_count = params[:batch_count]
28
- if batch_count == 1
29
- try_scan_with_retry(scan_options, params[:try_count])
30
- else
31
- tests = tests_to_batch(scan_options)
32
- tests.each_slice((tests.length / batch_count.to_f).round).to_a.each do |tests_batch|
33
- scan_options.reject! { |key| key == :skip_testing }
34
- scan_options[:only_testing] = tests_batch
35
- try_scan_with_retry(scan_options, params[:try_count])
36
- end
37
- end
38
- collate_reports(scan_options)
39
- end
40
-
41
- def self.try_scan_with_retry(scan_options, maximum_try_count)
42
- try_count = 0
43
- begin
44
- try_count += 1
45
- config = FastlaneCore::Configuration.create(Fastlane::Actions::ScanAction.available_options, scan_options)
46
- Fastlane::Actions::ScanAction.run(config)
47
- rescue FastlaneCore::Interface::FastlaneTestFailure => e
48
- UI.verbose("Scan failed with #{e}")
49
- if try_count < maximum_try_count
50
- report_filepath = junit_report_filepath(scan_options)
51
- failed_tests = other_action.tests_from_junit(junit: report_filepath)[:failed]
52
- scan_options[:only_testing] = failed_tests.map(&:shellescape)
53
- increment_report_filenames(scan_options)
54
- retry
55
- end
56
- else
57
- increment_report_filenames(scan_options)
58
- end
59
- end
60
-
61
- def self.collate_reports(scan_options)
62
- extension = junit_report_fileextension(scan_options)
63
- report_files = Dir.glob("#{scan_options[:output_directory]}/*#{extension}").map do |relative_filepath|
64
- File.absolute_path(relative_filepath)
65
- end
66
- if report_files.size > 1
67
- r = Regexp.new("^(?<filename>.*)-(\\d+)\\.(junit|xml|html)")
68
- final_report_name = junit_report_filename(scan_options)
69
- match = r.match(final_report_name)
70
- if match
71
- final_report_name = "#{match[:filename]}#{extension}"
72
- end
73
- other_action.collate_junit_reports(
74
- reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
75
- collated_report: File.absolute_path(File.join(scan_options[:output_directory], final_report_name))
17
+ unless params[:test_without_building]
18
+ build_for_testing(
19
+ params._values
76
20
  )
77
21
  end
78
- FileUtils.rm_f(Dir.glob("#{scan_options[:output_directory]}/*-[1-9]*#{extension}"))
79
- end
80
-
81
- def self.kill(program)
82
- Actions.sh("killall -9 '#{program}' &> /dev/null || true", log: false)
83
- end
84
-
85
- def self.quit_simulators
86
- kill('iPhone Simulator')
87
- kill('Simulator')
88
- kill('SimulatorBridge')
89
-
90
- launchctl_list_count = 0
91
- while Actions.sh('launchctl list | grep com.apple.CoreSimulator.CoreSimulatorService || true', log: false) != ''
92
- break if (launchctl_list_count += 1) > 10
93
- Actions.sh('launchctl remove com.apple.CoreSimulator.CoreSimulatorService &> /dev/null || true', log: false)
94
- sleep(1)
95
- end
96
- end
97
-
98
- def self.tests_to_batch(scan_options)
99
- unless scan_options[:only_testing].nil?
100
- return scan_options[:only_testing]
22
+ smart_scanner = ::TestCenter::Helper::CorrectingScanHelper.new(params.values)
23
+ tests_passed = smart_scanner.scan
24
+ if params[:fail_build] && !tests_passed
25
+ raise UI.test_failure!('Tests have failed')
101
26
  end
102
-
103
- xctestrun_path = scan_options[:xctestrun] || testrun_path(scan_options)
104
- if xctestrun_path.nil? || !File.exist?(xctestrun_path)
105
- UI.user_error!("Error: cannot find expected xctestrun file '#{xctestrun_path}'")
27
+ summary = run_summary(params, tests_passed, smart_scanner.retry_total_count)
28
+ unless Helper.test?
29
+ FastlaneCore::PrintTable.print_values(
30
+ config: summary,
31
+ title: "multi_scan results"
32
+ )
106
33
  end
107
- tests = xctestrun_tests(xctestrun_path)
108
- remove_skipped_tests(tests, scan_options[:skip_testing])
109
- end
110
-
111
- def self.remove_skipped_tests(tests_to_batch, tests_to_skip)
112
- if tests_to_skip.nil?
113
- return tests_to_batch
34
+ summary
35
+ end
36
+
37
+ def self.run_summary(scan_options, tests_passed, retry_total_count)
38
+ reportnamer = ::TestCenter::Helper::ReportNameHelper.new(
39
+ scan_options[:output_types],
40
+ scan_options[:output_files],
41
+ scan_options[:custom_report_file_name]
42
+ )
43
+ passing_testcount = 0
44
+ failed_tests = []
45
+ report_files = Dir.glob("#{scan_options[:output_directory]}/**/*#{reportnamer.junit_filextension}").map do |relative_filepath|
46
+ File.absolute_path(relative_filepath)
114
47
  end
115
-
116
- if tests_to_skip
117
- tests_to_skip = tests_to_skip
118
- testsuites_to_skip = tests_to_skip.select do |testsuite|
119
- testsuite.count('/') == 1 # just the testable/testsuite
120
- end
121
- tests_to_skip -= testsuites_to_skip
122
- tests_to_batch.reject! do |test_identifier|
123
- test_suite = test_identifier.split('/').first(2).join('/')
124
- testsuites_to_skip.include?(test_suite)
125
- end
126
- tests_to_batch -= tests_to_skip
48
+ report_files.each do |report_file|
49
+ junit_results = other_action.tests_from_junit(junit: report_file)
50
+ failed_tests.concat(junit_results[:failed])
51
+ passing_testcount += junit_results[:passing].size
127
52
  end
128
- tests_to_batch
129
- end
130
-
131
- def self.xctestrun_tests(xctestrun_path)
132
- tests_across_testables = other_action.tests_from_xctestrun(xctestrun: xctestrun_path)
133
- tests_across_testables.values.flatten
134
- end
135
-
136
- def self.xctest_bundle_path(xctestrun_rootpath, xctestrun_config)
137
- xctest_host_path = xctestrun_config['TestHostPath'].sub('__TESTROOT__', xctestrun_rootpath)
138
- xctestrun_config['TestBundlePath'].sub!('__TESTHOST__', xctest_host_path)
139
- xctestrun_config['TestBundlePath'].sub('__TESTROOT__', xctestrun_rootpath)
140
- end
141
-
142
- def self.testrun_path(scan_options)
143
- Dir.glob("#{scan_options[:derived_data_path]}/Build/Products/*.xctestrun").first
144
- end
145
-
146
- def self.test_products_path(derived_data_path)
147
- File.join(derived_data_path, "Build", "Products")
53
+ {
54
+ result: tests_passed,
55
+ total_tests: passing_testcount + failed_tests.size,
56
+ passing_testcount: passing_testcount,
57
+ failed_testcount: failed_tests.size,
58
+ failed_tests: failed_tests,
59
+ total_retry_count: retry_total_count,
60
+ report_files: report_files
61
+ }
148
62
  end
149
63
 
150
64
  def self.build_for_testing(scan_options)
151
- scan_options.delete(:test_without_building)
152
- scan_options[:build_for_testing] = true
153
- config = FastlaneCore::Configuration.create(Fastlane::Actions::ScanAction.available_options, scan_options)
65
+ config = FastlaneCore::Configuration.create(
66
+ Fastlane::Actions::ScanAction.available_options,
67
+ scan_options.merge(build_for_testing: true).reject { |k, _| %i[try_count batch_count test_without_building].include?(k) }
68
+ )
154
69
  Fastlane::Actions::ScanAction.run(config)
155
- scan_options[:derived_data_path] = Scan.config[:derived_data_path]
156
- end
157
-
158
- def self.config_has_junit_report(config)
159
- output_types = config.fetch(:output_types, '').to_s.split(',')
160
- output_filenames = config.fetch(:output_files, '').to_s.split(',')
161
-
162
- output_type_file_count_match = output_types.size == output_filenames.size
163
- output_types.include?('junit') && (output_type_file_count_match || config[:custom_report_file_name].to_s.strip.length > 0)
164
- end
165
-
166
- def self.config_with_junit_report(config)
167
- return config if config_has_junit_report(config)
168
-
169
- if config[:output_types].to_s.strip.empty? || config[:custom_report_file_name]
170
- config[:custom_report_file_name] ||= 'report.xml'
171
- config[:output_types] = 'junit'
172
- elsif config[:output_types].strip == 'junit' && config[:output_files].to_s.strip.empty?
173
- config[:custom_report_file_name] ||= 'report.xml'
174
- elsif !config[:output_types].split(',').include?('junit')
175
- config[:output_types] << ',junit'
176
- config[:output_files] << ',report.xml'
177
- elsif config[:output_files].nil?
178
- config[:output_files] = config[:output_types].split(',').map { |type| "report.#{type}" }.join(',')
179
- end
180
- config
181
- end
182
-
183
- def self.junit_report_filename(config)
184
- report_filename = config[:custom_report_file_name]
185
- if report_filename.nil?
186
- junit_index = config[:output_types].split(',').find_index('junit')
187
- report_filename = config[:output_files].to_s.split(',')[junit_index]
188
- end
189
- report_filename
190
- end
191
70
 
192
- def self.junit_report_fileextension(config)
193
- File.extname(junit_report_filename(config))
194
- end
195
-
196
- def self.junit_report_filepath(config)
197
- File.absolute_path(File.join(config[:output_directory], junit_report_filename(config)))
198
- end
199
-
200
- def self.increment_report_filename(filename)
201
- new_report_number = 2
202
- basename = File.basename(filename, '.*')
203
- r = Regexp.new("^(?<filename>.*)-(?<report_number>\\d+)\\.(junit|xml|html)")
204
- match = r.match(filename)
205
- if match
206
- new_report_number = match[:report_number].to_i + 1
207
- basename = match[:filename]
208
- end
209
- "#{basename}-#{new_report_number}#{File.extname(filename)}"
210
- end
211
-
212
- def self.increment_report_filenames(config)
213
- if config[:output_files]
214
- output_files = config[:output_files].to_s.split(',')
215
- output_files = output_files.map do |output_file|
216
- increment_report_filename(output_file)
217
- end
218
- config[:output_files] = output_files.join(',')
219
- elsif config[:custom_report_file_name]
220
- config[:custom_report_file_name] = increment_report_filename(config[:custom_report_file_name])
221
- end
71
+ scan_options.merge!(
72
+ test_without_building: true,
73
+ derived_data_path: Scan.config[:derived_data_path]
74
+ ).delete(:build_for_testing)
222
75
  end
223
76
 
224
77
  #####################################################
@@ -226,11 +79,14 @@ module Fastlane
226
79
  #####################################################
227
80
 
228
81
  def self.description
229
- "Uses scan to run Xcode tests a given number of times: only re-testing failing tests."
82
+ "Uses scan to run Xcode tests a given number of times, with the option " \
83
+ "of batching them, only re-testing failing tests."
230
84
  end
231
85
 
232
86
  def self.details
233
- "Use this action to run your tests if you have fragile tests that fail sporadically."
87
+ "Use this action to run your tests if you have fragile tests that fail" \
88
+ "sporadically, if you have a huge number of tests that should be " \
89
+ "batched, or have multiple test targets and need meaningful junit reports."
234
90
  end
235
91
 
236
92
  def self.scan_options
@@ -258,10 +114,54 @@ module Fastlane
258
114
  verify_block: proc do |count|
259
115
  UI.user_error!("Error: Batch counts must be greater than zero") unless count > 0
260
116
  end
117
+ ),
118
+ FastlaneCore::ConfigItem.new(
119
+ key: :testrun_completed_block,
120
+ description: 'A block invoked each time a test run completes',
121
+ optional: true,
122
+ is_string: false,
123
+ default_value: nil
261
124
  )
262
125
  ]
263
126
  end
264
127
 
128
+ def self.example_code
129
+ [
130
+ 'multi_scan(
131
+ project: File.absolute_path("../AtomicBoy/AtomicBoy.xcodeproj"),
132
+ scheme: "Professor",
133
+ try_count: 3,
134
+ custom_report_file_name: "atomic_report.xml",
135
+ output_types: "junit"
136
+ )',
137
+ 'multi_scan(
138
+ project: File.absolute_path("../GalaxyFamily/GalaxyFamily.xcodeproj"),
139
+ scheme: "Everything",
140
+ try_count: 3,
141
+ batch_count: 2, # splits the tests into two chunks to not overload the iOS Simulator
142
+ custom_report_file_name: "atomic_report.xml",
143
+ output_types: "junit"
144
+ )',
145
+ 'multi_scan(
146
+ project: File.absolute_path("../GalaxyFamily/GalaxyFamily.xcodeproj"),
147
+ scheme: "Everything",
148
+ try_count: 3,
149
+ batch_count: 8, # splits the tests into 8 chunks to not overload the iOS Simulator
150
+ testrun_completed_block: lambda { |testrun_info|
151
+ passed_test_count = testrun_info[:failed].size
152
+ failed_test_count = testrun_info[:passing].size
153
+ try_attempt = testrun_info[:try_count]
154
+ batch = testrun_info[:batch]
155
+
156
+ if failed_test_count > passed_test_count / 2
157
+ UI.abort_with_message!("Too many tests are failing")
158
+ end
159
+ UI.message("😊 everything is fine, let\'s continue try #{try_attempt} for batch #{batch}")
160
+ }
161
+ )'
162
+ ]
163
+ end
164
+
265
165
  def self.authors
266
166
  ["lyndsey-ferguson/@lyndseydf"]
267
167
  end
@@ -1,3 +1,5 @@
1
+ require 'plist'
2
+
1
3
  module Fastlane
2
4
  module Actions
3
5
  class TestsFromXctestrunAction < Action
@@ -23,8 +25,7 @@ module Fastlane
23
25
 
24
26
  def self.xctest_bundle_path(xctestrun_rootpath, xctestrun_config)
25
27
  xctest_host_path = xctestrun_config['TestHostPath'].sub('__TESTROOT__', xctestrun_rootpath)
26
- xctestrun_config['TestBundlePath'].sub!('__TESTHOST__', xctest_host_path)
27
- xctestrun_config['TestBundlePath'].sub('__TESTROOT__', xctestrun_rootpath)
28
+ xctestrun_config['TestBundlePath'].sub('__TESTHOST__', xctest_host_path).sub('__TESTROOT__', xctestrun_rootpath)
28
29
  end
29
30
 
30
31
  #####################################################
@@ -0,0 +1,163 @@
1
+ module TestCenter
2
+ module Helper
3
+ require 'fastlane_core/ui/ui.rb'
4
+ require 'plist'
5
+
6
+ class CorrectingScanHelper
7
+ attr_reader :retry_total_count
8
+
9
+ def initialize(multi_scan_options)
10
+ @batch_count = multi_scan_options[:batch_count] || 1
11
+ @output_directory = multi_scan_options[:output_directory] || 'test_results'
12
+ @try_count = multi_scan_options[:try_count]
13
+ @retry_total_count = 0
14
+ @testrun_completed_block = multi_scan_options[:testrun_completed_block]
15
+ @given_custom_report_file_name = multi_scan_options[:custom_report_file_name]
16
+ @given_output_types = multi_scan_options[:output_types]
17
+ @given_output_files = multi_scan_options[:output_files]
18
+ @scan_options = multi_scan_options.reject do |option, _|
19
+ %i[
20
+ output_directory
21
+ only_testing
22
+ skip_testing
23
+ try_count
24
+ batch_count
25
+ custom_report_file_name
26
+ fail_build
27
+ testrun_completed_block
28
+ ].include?(option)
29
+ end
30
+ @test_collector = TestCollector.new(multi_scan_options)
31
+ end
32
+
33
+ def scan
34
+ tests_passed = true
35
+ @testables_count = @test_collector.testables.size
36
+ @test_collector.testables.each do |testable|
37
+ tests_passed = scan_testable(testable) && tests_passed
38
+ end
39
+ tests_passed
40
+ end
41
+
42
+ def scan_testable(testable)
43
+ tests_passed = true
44
+ reportnamer = ReportNameHelper.new(
45
+ @given_output_types,
46
+ @given_output_files,
47
+ @given_custom_report_file_name
48
+ )
49
+ output_directory = @output_directory
50
+ if @batch_count > 1 || @testables_count > 1
51
+ testable_tests = @test_collector.testables_tests[testable]
52
+ current_batch = 1
53
+ testable_tests.each_slice((testable_tests.length / @batch_count.to_f).round).to_a.each do |tests_batch|
54
+ if @testables_count > 1
55
+ output_directory = File.join(@output_directory, "results-#{testable}")
56
+ end
57
+ FastlaneCore::UI.header("Starting test run on testable '#{testable}'")
58
+ tests_passed = correcting_scan(
59
+ {
60
+ only_testing: tests_batch,
61
+ output_directory: output_directory
62
+ },
63
+ current_batch,
64
+ reportnamer
65
+ ) && tests_passed
66
+ current_batch += 1
67
+ reportnamer.increment
68
+ end
69
+ else
70
+ options = {
71
+ output_directory: output_directory
72
+ }
73
+ options[:skip_testing] = @skip_testing if @skip_testing
74
+ tests_passed = correcting_scan(options, 1, reportnamer) && tests_passed
75
+ end
76
+ collate_reports(output_directory, reportnamer)
77
+ tests_passed
78
+ end
79
+
80
+ def collate_reports(output_directory, reportnamer)
81
+ report_files = Dir.glob("#{output_directory}/*#{reportnamer.junit_filextension}").map do |relative_filepath|
82
+ File.absolute_path(relative_filepath)
83
+ end
84
+ if report_files.size > 1
85
+ config = FastlaneCore::Configuration.create(
86
+ Fastlane::Actions::CollateJunitReportsAction.available_options,
87
+ {
88
+ reports: report_files.sort { |f1, f2| File.mtime(f1) <=> File.mtime(f2) },
89
+ collated_report: File.absolute_path(File.join(output_directory, reportnamer.junit_reportname))
90
+ }
91
+ )
92
+ Fastlane::Actions::CollateJunitReportsAction.run(config)
93
+ end
94
+ retried_junit_reportfiles = Dir.glob("#{output_directory}/**/*-[1-9]*#{reportnamer.junit_filextension}")
95
+ FileUtils.rm_f(retried_junit_reportfiles)
96
+ end
97
+
98
+ def correcting_scan(scan_run_options, batch, reportnamer)
99
+ scan_options = @scan_options.merge(scan_run_options)
100
+ try_count = 0
101
+ tests_passed = true
102
+ begin
103
+ try_count += 1
104
+ config = FastlaneCore::Configuration.create(
105
+ Fastlane::Actions::ScanAction.available_options,
106
+ scan_options.merge(reportnamer.scan_options)
107
+ )
108
+ quit_simulators
109
+ Fastlane::Actions::ScanAction.run(config)
110
+ @testrun_completed_block && @testrun_completed_block.call(
111
+ testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
112
+ )
113
+ tests_passed = true
114
+ rescue FastlaneCore::Interface::FastlaneTestFailure => e
115
+ FastlaneCore::UI.verbose("Scan failed with #{e}")
116
+ if try_count < @try_count
117
+ @retry_total_count += 1
118
+
119
+ info = testrun_info(batch, try_count, reportnamer, scan_options[:output_directory])
120
+ @testrun_completed_block && @testrun_completed_block.call(
121
+ info
122
+ )
123
+ scan_options[:only_testing] = info[:failed].map(&:shellescape)
124
+ FastlaneCore::UI.message('Re-running scan on only failed tests')
125
+ reportnamer.increment
126
+ retry
127
+ end
128
+ tests_passed = false
129
+ end
130
+ tests_passed
131
+ end
132
+
133
+ def testrun_info(batch, try_count, reportnamer, output_directory)
134
+ report_filepath = File.join(output_directory, reportnamer.junit_last_reportname)
135
+ config = FastlaneCore::Configuration.create(
136
+ Fastlane::Actions::TestsFromJunitAction.available_options,
137
+ {
138
+ junit: File.absolute_path(report_filepath)
139
+ }
140
+ )
141
+ junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)
142
+
143
+ {
144
+ failed: junit_results[:failed],
145
+ passing: junit_results[:passing],
146
+ batch: batch,
147
+ try_count: try_count,
148
+ report_filepath: report_filepath
149
+ }
150
+ end
151
+
152
+ def quit_simulators
153
+ Fastlane::Actions.sh("killall -9 'iPhone Simulator' 'Simulator' 'SimulatorBridge' &> /dev/null || true", log: false)
154
+ launchctl_list_count = 0
155
+ while Fastlane::Actions.sh('launchctl list | grep com.apple.CoreSimulator.CoreSimulatorService || true', log: false) != ''
156
+ break if (launchctl_list_count += 1) > 10
157
+ Fastlane::Actions.sh('launchctl remove com.apple.CoreSimulator.CoreSimulatorService &> /dev/null || true', log: false)
158
+ sleep(1)
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,70 @@
1
+ module TestCenter
2
+ module Helper
3
+ require 'fastlane_core/ui/ui.rb'
4
+
5
+ class ReportNameHelper
6
+ def initialize(output_types = nil, output_files = nil, custom_report_file_name = nil)
7
+ @output_types = output_types || 'junit'
8
+ @output_files = output_files || custom_report_file_name
9
+ @report_count = 0
10
+
11
+ if @output_types && @output_files.nil?
12
+ @output_files = @output_types.split(',').map { |type| "report.#{type}" }.join(',')
13
+ end
14
+ unless @output_types.include?('junit')
15
+ FastlaneCore::UI.important('Scan output types missing \'junit\', adding it')
16
+ @output_types = @output_types.split(',').push('junit').join(',')
17
+ if @output_types.split(',').size == @output_files.split(',').size + 1
18
+ @output_files = @output_files.split(',').push('report.xml').join(',')
19
+ FastlaneCore::UI.message('As output files has one less than the new number of output types, assumming the filename for the junit was missing and added it')
20
+ end
21
+ end
22
+
23
+ types = @output_types.split(',').each(&:chomp)
24
+ files = @output_files.split(',').each(&:chomp)
25
+ unless files.size == types.size
26
+ raise ArgumentError, "Error: count of :output_types, #{types}, does not match the output filename(s) #{files}"
27
+ end
28
+ end
29
+
30
+ def numbered_filename(filename)
31
+ if @report_count > 0
32
+ basename = File.basename(filename, '.*')
33
+ extension = File.extname(filename)
34
+ filename = "#{basename}-#{@report_count + 1}#{extension}"
35
+ end
36
+ filename
37
+ end
38
+
39
+ def scan_options
40
+ files = @output_files.split(',').each(&:chomp)
41
+ files.map! do |filename|
42
+ filename.chomp
43
+ numbered_filename(filename)
44
+ end
45
+ {
46
+ output_types: @output_types,
47
+ output_files: files.join(',')
48
+ }
49
+ end
50
+
51
+ def junit_last_reportname
52
+ junit_index = @output_types.split(',').find_index('junit')
53
+ numbered_filename(@output_files.to_s.split(',')[junit_index])
54
+ end
55
+
56
+ def junit_reportname
57
+ junit_index = @output_types.split(',').find_index('junit')
58
+ @output_files.to_s.split(',')[junit_index]
59
+ end
60
+
61
+ def junit_filextension
62
+ File.extname(junit_reportname)
63
+ end
64
+
65
+ def increment
66
+ @report_count += 1
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,60 @@
1
+ module TestCenter
2
+ module Helper
3
+ require 'fastlane_core/ui/ui.rb'
4
+ require 'plist'
5
+
6
+ class TestCollector
7
+ def initialize(options)
8
+ @xctestrun_path = options[:xctestrun] || derived_testrun_path(options[:derived_data_path], options[:scheme])
9
+ @only_testing = options[:only_testing]
10
+ @skip_testing = options[:skip_testing]
11
+ end
12
+
13
+ def derived_testrun_path(derived_data_path, scheme)
14
+ Dir.glob("#{derived_data_path}/Build/Products/#{scheme}*.xctestrun").first
15
+ end
16
+
17
+ def testables
18
+ unless @testables
19
+ if @only_testing
20
+ @testables ||= only_testing_to_testables_tests.keys
21
+ else
22
+ @testables ||= Plist.parse_xml(@xctestrun_path).keys
23
+ end
24
+ end
25
+ @testables
26
+ end
27
+
28
+ def only_testing_to_testables_tests
29
+ tests = Hash.new { |h, k| h[k] = [] }
30
+ @only_testing.sort.each do |test_identifier|
31
+ testable = test_identifier.split('/', 2)[0]
32
+ tests[testable] << test_identifier
33
+ end
34
+ tests
35
+ end
36
+
37
+ def testables_tests
38
+ unless @testables_tests
39
+ if @only_testing
40
+ @testables_tests = only_testing_to_testables_tests
41
+ else
42
+ config = FastlaneCore::Configuration.create(::Fastlane::Actions::TestsFromXctestrunAction.available_options, xctestrun: @xctestrun_path)
43
+ @testables_tests = ::Fastlane::Actions::TestsFromXctestrunAction.run(config)
44
+ if @skip_testing
45
+ skipped_testable_tests = Hash.new { |h, k| h[k] = [] }
46
+ @skip_testing.sort.each do |skipped_test_identifier|
47
+ testable = skipped_test_identifier.split('/', 2)[0]
48
+ skipped_testable_tests[testable] << skipped_test_identifier
49
+ end
50
+ @testables_tests.each_key do |testable|
51
+ @testables_tests[testable] -= skipped_testable_tests[testable]
52
+ end
53
+ end
54
+ end
55
+ end
56
+ @testables_tests
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module TestCenter
3
- VERSION = "2.5.1"
3
+ VERSION = "3.0.1"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-test_center
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.1
4
+ version: 3.0.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: 2018-02-04 00:00:00.000000000 Z
11
+ date: 2018-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: plist
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: xcodeproj
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -39,13 +53,13 @@ dependencies:
39
53
  - !ruby/object:Gem::Version
40
54
  version: 1.1.2
41
55
  - !ruby/object:Gem::Dependency
42
- name: plist
56
+ name: bundler
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
46
60
  - !ruby/object:Gem::Version
47
61
  version: '0'
48
- type: :runtime
62
+ type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
@@ -53,7 +67,7 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: bundler
70
+ name: colorize
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -188,8 +202,10 @@ files:
188
202
  - lib/fastlane/plugin/test_center/actions/suppressed_tests.rb
189
203
  - lib/fastlane/plugin/test_center/actions/tests_from_junit.rb
190
204
  - lib/fastlane/plugin/test_center/actions/tests_from_xctestrun.rb
205
+ - lib/fastlane/plugin/test_center/helper/correcting_scan_helper.rb
191
206
  - lib/fastlane/plugin/test_center/helper/junit_helper.rb
192
- - lib/fastlane/plugin/test_center/helper/test_center_helper.rb
207
+ - lib/fastlane/plugin/test_center/helper/reportname_helper.rb
208
+ - lib/fastlane/plugin/test_center/helper/test_collector.rb
193
209
  - lib/fastlane/plugin/test_center/helper/xcodebuild_string.rb
194
210
  - lib/fastlane/plugin/test_center/version.rb
195
211
  homepage: https://github.com/lyndsey-ferguson/fastlane-plugin-test_center
@@ -217,3 +233,4 @@ signing_key:
217
233
  specification_version: 4
218
234
  summary: Makes testing your iOS app easier
219
235
  test_files: []
236
+ has_rdoc:
@@ -1,12 +0,0 @@
1
- module Fastlane
2
- module Helper
3
- class TestCenterHelper
4
- # class methods that you define here become available in your action
5
- # as `Helper::TestCenterHelper.your_method`
6
- #
7
- def self.show_message
8
- UI.message("Hello from the test_center plugin helper!")
9
- end
10
- end
11
- end
12
- end