Class: Fastlane::Actions::CollateTestResultBundlesAction

Inherits:
Action
  • Object
show all
Defined in:
lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb

Documentation collapse

Class Method Summary collapse

Class Method Details

.authorsObject



234
235
236
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 234

def self.authors
  ["lyndsey-ferguson/@lyndseydf"]
end

.available_optionsObject



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 190

def self.available_options
  [
    FastlaneCore::ConfigItem.new(
      key: :bundles,
      env_name: 'COLLATE_TEST_RESULT_BUNDLES_BUNDLES',
      description: 'An array of test_result bundles to collate. The first bundle is used as the base into which other bundles are merged in',
      optional: false,
      type: Array,
      verify_block: proc do |bundles|
        UI.user_error!('No test_result bundles found') if bundles.empty?
        bundles.each do |bundle|
          UI.user_error!("Error: test_result bundle not found: '#{bundle}'") unless Dir.exist?(bundle)
        end
      end
    ),
    FastlaneCore::ConfigItem.new(
      key: :collated_bundle,
      env_name: 'COLLATE_TEST_RESULT_BUNDLES_COLLATED_BUNDLE',
      description: 'The final test_result bundle where all testcases will be merged into',
      optional: true,
      default_value: 'result.test_result',
      type: String
    )
  ]
end

.categoryObject



238
239
240
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 238

def self.category
  :testing
end

.collate_bundle(base_bundle_path, other_bundle_path, child_item_name) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 102

def self.collate_bundle(base_bundle_path, other_bundle_path, child_item_name)
  if %w{Attachments Diagnostics}.include?(child_item_name)
    FileUtils.cp_r(
      File.join(other_bundle_path, child_item_name, '.'),
      File.join(base_bundle_path, child_item_name)
    )
  elsif child_item_name.end_with?('.xcactivitylog')
    concatenate_zipped_activitylogs(
      File.join(base_bundle_path, child_item_name),
      File.join(other_bundle_path, child_item_name)
    )
  elsif child_item_name.end_with?('TestSummaries.plist')
    collate_testsummaries_plist(
      File.join(base_bundle_path, child_item_name),
      File.join(other_bundle_path, child_item_name)
    )
  end
end

.collate_bundles(base_bundle_path, other_bundle_path) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 83

def self.collate_bundles(base_bundle_path, other_bundle_path)
  Dir.foreach(other_bundle_path) do |child_item|
    if child_item == 'Info.plist'
      collate_infoplist(
        File.join(base_bundle_path, child_item),
        File.join(other_bundle_path, child_item)
      )
    elsif /\d_Test/ =~ child_item
      test_target_path = File.join(base_bundle_path, child_item)
      other_target_path = File.join(other_bundle_path, child_item)
      Dir.foreach(other_target_path) do |grandchild_item|
        collate_bundle(test_target_path, other_target_path, grandchild_item)
      end
    else
      collate_bundle(base_bundle_path, other_bundle_path, child_item)
    end
  end
end

.collate_infoplist(base_infoplist_filepath, other_infoplist_filepath) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 154

def self.collate_infoplist(base_infoplist_filepath, other_infoplist_filepath)
  if File.exist?(base_infoplist_filepath)
    base_infoplist = Plist.parse_xml(base_infoplist_filepath)
    other_infoplist = Plist.parse_xml(other_infoplist_filepath)
    other_infoplist['Actions'].zip(base_infoplist['Actions']).each do |other_action, base_action|
      if other_action['Title'] != base_action['Title']
        raise 'Info.plist Actions do not align: cannot collate'
      end
      base_action['EndedTime'] = other_action['EndedTime']
      base_action['ActionResult']['TestsFailedCount'] = other_action['ActionResult']['TestsFailedCount']
    end
    Plist::Emit.save_plist(base_infoplist, base_infoplist_filepath)
  else
    FileUtils.cp(other_infoplist_filepath, base_infoplist_filepath)
  end
end

.collate_testsummaries_plist(base_testsummaries_plist_filepath, other_testsummaries_plist_filepath) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 137

def self.collate_testsummaries_plist(base_testsummaries_plist_filepath, other_testsummaries_plist_filepath)
  if File.exist?(base_testsummaries_plist_filepath)
    base_testsummaries_plist = Plist.parse_xml(base_testsummaries_plist_filepath)
    other_testsummaries_plist = Plist.parse_xml(other_testsummaries_plist_filepath)
    base_testsummaries_plist['TestableSummaries'].zip(other_testsummaries_plist['TestableSummaries']).each do |base_testable_summary, other_testable_summary|
      unless base_testable_summary.key?('PreviousTests')
        base_testable_summary['PreviousTests'] = []
      end
      base_testable_summary['PreviousTests'].concat(base_testable_summary['Tests'] || [])
      base_testable_summary['Tests'] = other_testable_summary['Tests']
    end
    Plist::Emit.save_plist(base_testsummaries_plist, base_testsummaries_plist_filepath)
  else
    FileUtils.cp(other_testsummaries_plist, base_testsummaries_plist)
  end
end

.collate_version3_formatted_bundles(params) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 25

def self.collate_version3_formatted_bundles(params)
  test_result_bundlepaths = params[:bundles]
  found_version3_format_bundle = test_result_bundlepaths.any? do |test_result_bundlepath|
    is_bundle_format_3?(test_result_bundlepath)
  end
  
  if found_version3_format_bundle
    FastlaneCore::UI.verbose("result bundles are of format version 3")
    `xcrun xcresulttool version 2> /dev/null`
    unless $?.exitstatus.zero?
      UI.user_error!("""
        Unable to collate version 3 format test_result bundle without the xcrun xcresulttool.
        Please install and select Xcode 11, and then run the command again.""")
    end
    xcresulttool_merge(params)
    return true
  end
  FastlaneCore::UI.verbose("result bundles are NOT of format version 3")
  return false
end

.concatenate_zipped_activitylogs(base_activity_log_path, other_activity_log_path) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 121

def self.concatenate_zipped_activitylogs(base_activity_log_path, other_activity_log_path)
  gunzipped_base_filepath = File.join(
    File.dirname(base_activity_log_path),
    File.basename(base_activity_log_path, '.*')
  )
  gunzipped_other_filepath = File.join(
    File.dirname(other_activity_log_path),
    File.basename(other_activity_log_path, '.*')
  )
  sh("gunzip -k -S .xcactivitylog '#{other_activity_log_path}'", print_command: false, print_command_output: false)
  sh("gunzip -S .xcactivitylog '#{base_activity_log_path}'", print_command: false, print_command_output: false)
  sh("cat '#{gunzipped_other_filepath}' > '#{gunzipped_base_filepath}'", print_command: false, print_command_output: false)
  FileUtils.rm(gunzipped_other_filepath)
  sh("gzip -S .xcactivitylog '#{gunzipped_base_filepath}'", print_command: false, print_command_output: false)
end

.descriptionObject

:nocov:



176
177
178
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 176

def self.description
  "🔸 Combines multiple test_result bundles into one test_result bundle"
end

.detailsObject



180
181
182
183
184
185
186
187
188
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 180

def self.details
  "The first test_result bundle is used as the base bundle. " \
  "Testcases that failed in previous bundles that no longer appear in " \
  "later bundles are assumed to have passed in a re-run, thus not appearing " \
  "in the collated test_result bundle. " \
  "This is done because it is assumed that fragile tests, when " \
  "re-run will often succeed due to less interference from other " \
  "tests and the subsequent test_result bundles will have fewer failing tests."
end

.example_codeObject



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 216

def self.example_code
  [
    "
    UI.important(
      'example: ' \\
      'collate the test_result bundles to a temporary bundle \"result.test_result\"'
    )
    bundles = Dir['../spec/fixtures/*.test_result'].map { |relpath| File.absolute_path(relpath) }
    Dir.mktmpdir('test_output') do |dir|
      collate_test_result_bundles(
        bundles: bundles,
        collated_bundle: File.join(dir, 'result.test_result')
      )
    end
    "
  ]
end

.is_bundle_format_3?(bundle_path) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
75
76
77
78
79
80
81
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 72

def self.is_bundle_format_3?(bundle_path)
  infoplist_filepath = File.join(bundle_path, 'Info.plist')
  if File.exist?(infoplist_filepath)
    base_infoplist = Plist.parse_xml(infoplist_filepath)
    if base_infoplist.key?('version')
      return true if base_infoplist.dig('version', 'major') > 2
    end
  end
  false
end

.is_supported?(platform) ⇒ Boolean

Returns:

  • (Boolean)


242
243
244
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 242

def self.is_supported?(platform)
  %i[ios mac].include?(platform)
end

.run(params) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 6

def self.run(params)
  test_result_bundlepaths = params[:bundles]
  base_bundle_path = test_result_bundlepaths[0]

  return if collate_version3_formatted_bundles(params)

  if test_result_bundlepaths.size > 1
    base_bundle_path = Dir.mktmpdir(['base', '.test_result'])
    FileUtils.cp_r(File.join(test_result_bundlepaths[0], '.'), base_bundle_path)
    test_result_bundlepaths.shift
    test_result_bundlepaths.each do |other_bundlepath|
      collate_bundles(base_bundle_path, other_bundlepath)
    end
  end
  FileUtils.rm_rf(params[:collated_bundle])
  FileUtils.cp_r(base_bundle_path, params[:collated_bundle])
  UI.message("Finished collating test_result bundle to '#{params[:collated_bundle]}'")
end

.xcresulttool_merge(params) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/fastlane/plugin/test_center/actions/collate_test_result_bundles.rb', line 46

def self.xcresulttool_merge(params)
  test_result_bundlepaths = params[:bundles]
  collated_bundlepath = File.expand_path(params[:collated_bundle])
  Dir.mktmpdir do |dir|
    tmp_xcresult_bundlepaths = []
    test_result_bundlepaths.each do |test_result_bundlepath|
      bundlename = File.basename(test_result_bundlepath)
      # Note: the `xcresulttool` requires that the bundle names end in `.xcresult`.
      tmp_xcresult_bundlepath = Dir.mktmpdir([bundlename, '.xcresult'])
      FileUtils.rmdir([tmp_xcresult_bundlepath])
      FileUtils.symlink(test_result_bundlepath, "#{tmp_xcresult_bundlepath}", force: true)
      tmp_xcresult_bundlepaths << tmp_xcresult_bundlepath
    end
    tmp_collated_bundlepath = File.join(dir, File.basename(collated_bundlepath))
    xcresulttool_cmd = 'xcrun xcresulttool merge '
    xcresulttool_cmd += tmp_xcresult_bundlepaths.map(&:shellescape).join(' ')
    xcresulttool_cmd += " --output-path #{tmp_collated_bundlepath.shellescape}"
    UI.message(xcresulttool_cmd)
    sh(xcresulttool_cmd)
    FileUtils.safe_unlink(tmp_xcresult_bundlepaths)
    FileUtils.rm_rf(collated_bundlepath)
    FileUtils.cp_r(tmp_collated_bundlepath, collated_bundlepath)
    UI.message("Finished collating test_result bundle to '#{collated_bundlepath}'")
  end
end