fastlane-plugin-test_center 2.5.1 → 3.0.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.
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