Class: TestCenter::Helper::MultiScanManager::RetryingScanHelper

Inherits:
Object
  • Object
show all
Defined in:
lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ RetryingScanHelper

Returns a new instance of RetryingScanHelper.

Raises:

  • (ArgumentError)


8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 8

def initialize(options)
  raise ArgumentError, 'Do not use the :device or :devices option. Instead use the :destination option.' if (options.key?(:device) or options.key?(:devices))

  @options = options
  @testrun_count = 0
  @xcpretty_json_file_output = ENV['XCPRETTY_JSON_FILE_OUTPUT']
  @reportnamer = ReportNameHelper.new(
    @options[:output_types],
    @options[:output_files],
    @options[:custom_report_file_name]
  )
end

Instance Attribute Details

#testrun_countObject (readonly)

Returns the value of attribute testrun_count.



6
7
8
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 6

def testrun_count
  @testrun_count
end

Instance Method Details

#after_testrun(exception = nil) ⇒ Object

after_testrun methods



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 100

def after_testrun(exception = nil)
  move_simulator_logs_for_next_run

  @testrun_count = @testrun_count + 1
  FastlaneCore::UI.verbose("Batch ##{@options[:batch]} incrementing retry count to #{@testrun_count}")
  if exception.kind_of?(FastlaneCore::Interface::FastlaneTestFailure)
    after_testrun_message = "Scan found failing tests"
    after_testrun_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
    FastlaneCore::UI.verbose(after_testrun_message)

    handle_test_failure
  elsif exception.kind_of?(FastlaneCore::Interface::FastlaneBuildFailure)
    after_testrun_message = "Scan unable to test"
    after_testrun_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
    FastlaneCore::UI.verbose(after_testrun_message)

    handle_build_failure(exception)
  else
    after_testrun_message = "Scan passed the tests"
    after_testrun_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
    FastlaneCore::UI.verbose(after_testrun_message)

    handle_success
  end
  collate_reports
end

#before_testrunObject



21
22
23
24
25
26
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 21

def before_testrun
  delete_xcresults # has to be performed _after_ moving a *.test_result
  quit_simulator
  set_json_env
  print_starting_scan_message
end

#collate_reportsObject



133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 133

def collate_reports
  return unless @options[:collate_reports]

  report_collator_options = {
    source_reports_directory_glob: output_directory,
    output_directory: output_directory,
    reportnamer: @reportnamer,
    scheme: @options[:scheme],
    result_bundle: @options[:result_bundle]
  }
  TestCenter::Helper::MultiScanManager::ReportCollator.new(report_collator_options).collate
end

#delete_xcresultsObject



40
41
42
43
44
45
46
47
48
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 40

def delete_xcresults
  derived_data_path = File.expand_path(@options[:derived_data_path] || Scan.config[:derived_data_path])
  xcresults = Dir.glob("#{derived_data_path}/Logs/Test/*.xcresult")
  FastlaneCore::UI.verbose("Deleting xcresults:")
  xcresults.each do |xcresult|
    FastlaneCore::UI.verbose("  #{xcresult}")
  end
  FileUtils.rm_rf(xcresults)
end

#failure_details(additional_info) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 174

def failure_details(additional_info)
  return [{}, nil] if additional_info.key?(:test_operation_failure)
  
  report_filepath = File.join(output_directory, @reportnamer.junit_last_reportname)
  config = FastlaneCore::Configuration.create(
    Fastlane::Actions::TestsFromJunitAction.available_options,
    {
      junit: File.absolute_path(report_filepath)
    }
  )
  junit_results = Fastlane::Actions::TestsFromJunitAction.run(config)

  [junit_results, report_filepath]
end

#handle_build_failure(exception) ⇒ Object



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 237

def handle_build_failure(exception)  
  test_session_last_messages = last_lines_of_test_session_log
  failure = retrieve_test_operation_failure(test_session_last_messages)
  case failure
  when /Test runner exited before starting test execution/
    FastlaneCore::UI.error(failure)
  when /Lost connection to testmanagerd/
    FastlaneCore::UI.error(failure)
    FastlaneCore::UI.important("com.apple.CoreSimulator.CoreSimulatorService may have become corrupt, consider quitting it")
    if @options[:quit_core_simulator_service]
      Fastlane::Actions::RestartCoreSimulatorServiceAction.run
    end
  else
    FastlaneCore::UI.error(test_session_last_messages)
    send_callback_testrun_info(test_operation_failure: failure)
    raise exception
  end
  send_callback_testrun_info(test_operation_failure: failure)
end

#handle_successObject



127
128
129
130
131
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 127

def handle_success
  send_callback_testrun_info
  move_test_result_bundle_for_next_run
  reset_json_env
end

#handle_test_failureObject



146
147
148
149
150
151
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 146

def handle_test_failure
  send_callback_testrun_info
  move_test_result_bundle_for_next_run
  update_scan_options
  @reportnamer.increment
end

#last_lines_of_test_session_logObject



265
266
267
268
269
270
271
272
273
274
275
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 265

def last_lines_of_test_session_log
  derived_data_path = File.expand_path(@options[:derived_data_path])
  test_session_logs = Dir.glob("#{derived_data_path}/Logs/Test/*.xcresult/*_Test/Diagnostics/**/Session-*.log")
  return '' if test_session_logs.empty?
  
  test_session_logs.sort! { |logfile1, logfile2| File.mtime(logfile1) <=> File.mtime(logfile2) }
  test_session = File.open(test_session_logs.last)
  backwards_seek_offset = -1 * [1000, test_session.stat.size].min
  test_session.seek(backwards_seek_offset, IO::SEEK_END)
  test_session_last_messages = test_session.read
end

#move_simulator_logs_for_next_runObject



277
278
279
280
281
282
283
284
285
286
287
288
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 277

def move_simulator_logs_for_next_run
  return unless @options[:include_simulator_logs]

  glob_pattern = "#{output_directory}/system_logs-*.{log,logarchive}"
  logs = Dir.glob(glob_pattern)
  logs.each do |log_filepath|
    new_logname = "try-#{testrun_count}-#{File.basename(log_filepath)}"
    new_log_filepath = "#{File.dirname(log_filepath)}/#{new_logname}"
    FastlaneCore::UI.verbose("Moving simulator log '#{log_filepath}' to '#{new_log_filepath}'")
    File.rename(log_filepath, new_log_filepath)
  end
end

#move_test_result_bundle_for_next_runObject



290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 290

def move_test_result_bundle_for_next_run
  return unless @options[:result_bundle]

  glob_pattern = "#{output_directory}/*.test_result"
  preexisting_test_result_bundles = Dir.glob(glob_pattern)
  unnumbered_test_result_bundles = preexisting_test_result_bundles.reject do |test_result|
    test_result =~ /.*-\d+\.test_result/
  end
  src_test_bundle = unnumbered_test_result_bundles.first
  dst_test_bundle_parent_dir = File.dirname(src_test_bundle)
  dst_test_bundle_basename = File.basename(src_test_bundle, '.test_result')
  dst_test_bundle = "#{dst_test_bundle_parent_dir}/#{dst_test_bundle_basename}-#{@testrun_count}.test_result"
  FastlaneCore::UI.verbose("Moving test_result '#{src_test_bundle}' to '#{dst_test_bundle}'")
  File.rename(src_test_bundle, dst_test_bundle)
end

#output_directoryObject



50
51
52
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 50

def output_directory
  @options.fetch(:output_directory, 'test_results')
end


54
55
56
57
58
59
60
61
62
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 54

def print_starting_scan_message
  if @options[:only_testing]
    scan_message = "Starting scan ##{@testrun_count + 1} with #{@options.fetch(:only_testing, []).size} tests"
  else
    scan_message = "Starting scan ##{@testrun_count + 1}"
  end
  scan_message << " for batch ##{@options[:batch]}" unless @options[:batch].nil?
  FastlaneCore::UI.message("#{scan_message}.")
end

#quit_simulatorObject



28
29
30
31
32
33
34
35
36
37
38
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 28

def quit_simulator
  return unless @options[:quit_simulators]

  @options.fetch(:destination).each do |destination|
    if /id=(?<udid>\w+),?/ =~ destination
      FastlaneCore::UI.verbose("Restarting Simulator #{udid}")
      `xcrun simctl shutdown #{udid} 2>/dev/null`
      `xcrun simctl boot #{udid} 2>/dev/null`
    end
  end
end

#reset_json_envObject



75
76
77
78
79
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 75

def reset_json_env
  return unless @reportnamer.includes_json?

  ENV['XCPRETTY_JSON_FILE_OUTPUT'] = @xcpretty_json_file_output
end

#retrieve_test_operation_failure(test_session_last_messages) ⇒ Object



257
258
259
260
261
262
263
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 257

def retrieve_test_operation_failure(test_session_last_messages)
  test_operation_failure_match = /Test operation failure: (?<test_operation_failure>.*)$/ =~ test_session_last_messages
  if test_operation_failure_match.nil?
    test_operation_failure = 'Unknown test operation failure'
  end
  test_operation_failure
end

#scan_optionsObject



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 81

def scan_options
  valid_scan_keys = Fastlane::Actions::ScanAction.available_options.map(&:key)
  xcargs = @options[:xcargs]
  if xcargs&.include?('build-for-testing')
    FastlaneCore::UI.important(":xcargs, #{xcargs}, contained 'build-for-testing', removing it")
    xcargs.slice!('build-for-testing')
  end
  retrying_scan_options = @reportnamer.scan_options.merge(
    {
      output_directory: output_directory,
      xcargs: "#{xcargs} -parallel-testing-enabled NO"
    }
  )
  @options.select { |k,v| valid_scan_keys.include?(k) }
    .merge(retrying_scan_options)
end

#send_callback_testrun_info(additional_info = {}) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 153

def send_callback_testrun_info(additional_info = {})
  return unless @options[:testrun_completed_block]

  report_filepath = nil
  junit_results, report_filepath = failure_details(additional_info)

  info = {
    failed: junit_results.fetch(:failed, []),
    passing: junit_results.fetch(:passing, []),
    batch: @options[:batch] || 1,
    try_count: @testrun_count,
    report_filepath: report_filepath
  }.merge(additional_info)

  update_html_failure_details(info)
  update_json_failure_details(info)
  update_test_result_bundle_details(info)
  
  @options[:testrun_completed_block].call(info)
end

#set_json_envObject



64
65
66
67
68
69
70
71
72
73
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 64

def set_json_env
  return unless @reportnamer.includes_json?

  xcpretty_json_file_output = File.join(
    output_directory,
    @reportnamer.json_last_reportname
  )
  FastlaneCore::UI.verbose("Setting the XCPRETTY_JSON_FILE_OUTPUT to #{xcpretty_json_file_output}")
  ENV['XCPRETTY_JSON_FILE_OUTPUT'] = xcpretty_json_file_output
end

#turn_off_code_coverageObject



217
218
219
220
221
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 217

def turn_off_code_coverage
  # Turn off code coverage as code coverage reports are not merged and
  # the first, more valuable, report will be overwritten
  @options.delete(:code_coverage)
end

#update_html_failure_details(info) ⇒ Object



189
190
191
192
193
194
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 189

def update_html_failure_details(info)
  return unless @reportnamer.includes_html?

  html_report_filepath = File.join(output_directory, @reportnamer.html_last_reportname)
  info[:html_report_filepath] = html_report_filepath
end

#update_json_failure_details(info) ⇒ Object



196
197
198
199
200
201
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 196

def update_json_failure_details(info)
  return unless @reportnamer.includes_json?
  
  json_report_filepath = File.join(output_directory, @reportnamer.json_last_reportname)
  info[:json_report_filepath] = json_report_filepath
end

#update_only_testingObject



223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 223

def update_only_testing
  report_filepath = File.join(output_directory, @reportnamer.junit_last_reportname)
  config = FastlaneCore::Configuration.create(
    Fastlane::Actions::TestsFromJunitAction.available_options,
    {
      junit: File.absolute_path(report_filepath)
    }
  )
  @options[:only_testing] = Fastlane::Actions::TestsFromJunitAction.run(config)[:failed].map(&:shellsafe_testidentifier)
  if @options[:invocation_based_tests]
    @options[:only_testing] = @options[:only_testing].map(&:strip_testcase).uniq
  end
end

#update_scan_optionsObject



212
213
214
215
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 212

def update_scan_options
  update_only_testing
  turn_off_code_coverage
end

#update_test_result_bundle_details(info) ⇒ Object



203
204
205
206
207
208
209
210
# File 'lib/fastlane/plugin/test_center/helper/multi_scan_manager/retrying_scan_helper.rb', line 203

def update_test_result_bundle_details(info)
  return unless @options[:result_bundle]
  
  test_result_suffix = '.test_result'
  test_result_suffix.prepend("-#{@reportnamer.report_count}") unless @reportnamer.report_count.zero?
  test_result_bundlepath = File.join(output_directory, @options[:scheme]) + test_result_suffix
  info[:test_result_bundlepath] = test_result_bundlepath
end