fastlane-plugin-retry_failed_tests 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 20fd4cac4f47a56fd5f417bbad92baab1eb987b3
4
+ data.tar.gz: f26377521254943b98724be7fb045d94e65b0047
5
+ SHA512:
6
+ metadata.gz: 986d917d1a0991f2e4fb5819153dc28c3a10989dcdc13587711fa9c8bdcd1111db22d94171783716e34b307ef8943fdb277b2bc413a71970227b91b8d02a64c5
7
+ data.tar.gz: 16b51dad340b3517bba29d10134272da4dd29fad6b814be3a05820bf4c0636825cb0195847652ad7d2e4364c1b3b8c376107b64118b19d3ff9e7782f020696df
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Lyndsey Ferguson <lyndsey.ferguson@appian.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # fastlane-plugin-retry
2
+ A fastlane plugin for retrying iOS automation tests. Based on the "fastlane-plugin-test_center" plugin repository by Github user lyndsey-ferguson.
@@ -0,0 +1,163 @@
1
+ require 'pry-byebug'
2
+ require 'nokogiri'
3
+ require 'nokogiri-plist'
4
+ require 'FileUtils'
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class CollateJunitReportsAction < Action
9
+
10
+ def self.run(params)
11
+ report_filepaths = params[:reports].reverse
12
+ if report_filepaths.size == 1
13
+ FileUtils.cp(report_filepaths[0], params[:collated_report])
14
+ else
15
+ target_report = File.open(report_filepaths.shift) {|f| Nokogiri::XML(f)}
16
+ reports = report_filepaths.map { |report_filepath| Nokogiri::XML(Nokogiri::PList(open(report_filepath)).to_plist) }
17
+ reports.each do |retry_report|
18
+ retry_report = clean_report(retry_report.to_s)
19
+ mergeLists(target_report, retry_report, params)
20
+ end
21
+ end
22
+ merge_assets(params[:assets], params[:collated_report] + "/Attachments")
23
+ merge_logs(params[:logs], params[:collated_report] + "/")
24
+ end
25
+
26
+ # Merges .plist reports
27
+ def self.mergeLists(target_report, retry_report, params)
28
+ UI.verbose("Merging retried results...")
29
+ Dir.mkdir(params[:collated_report]) unless File.exists?(params[:collated_report])
30
+ file_name = params[:collated_report] + "/action_TestSummaries.plist"
31
+ retried_tests = retry_report.xpath("//key[contains(.,'TestSummaryGUID')]/..")
32
+ current_node = retried_tests.shift
33
+ while (current_node != nil)
34
+ testName = get_test_name(current_node)
35
+ matching_node = target_report.at_xpath("//string[contains(.,'#{testName}')]/..")
36
+ if (!matching_node.nil?)
37
+ matching_node.previous.next.replace(current_node)
38
+ write_report_to_file(target_report, file_name)
39
+ end
40
+ current_node = retried_tests.shift
41
+ end
42
+ end
43
+
44
+ # Merges screenshots from all retries
45
+ def self.merge_assets(asset_files, assets_folder)
46
+ UI.verbose ("Merging screenshot folders...")
47
+ Dir.mkdir(assets_folder) unless File.exists?(assets_folder)
48
+ asset_files.each do |folder|
49
+ FileUtils.cp_r(Dir[folder + '/*'], assets_folder)
50
+ end
51
+ end
52
+
53
+ # Cleans formatting of report
54
+ def self.clean_report(report)
55
+ report = report.gsub("<XCAccessibilityElement:/>0x", " XCAccessibilityElement ")
56
+ report = report.gsub("<XCAccessibilityElement:></XCAccessibilityElement:>", " XCAccessibilityElement ")
57
+ report = Nokogiri::XML(report)
58
+ report
59
+ end
60
+
61
+ # Merges console log of reports
62
+ def self.merge_logs(log_files, logs_folder)
63
+ UI.verbose("Merging console logs...")
64
+ target_log = log_files.shift
65
+ log_files.each do |log|
66
+ to_append = File.read(log)
67
+ File.open(target_log, "a") do |handle|
68
+ handle.puts to_append
69
+ end
70
+ FileUtils.cp_r(target_log, logs_folder)
71
+ end
72
+ end
73
+
74
+ # Outputs report to a new file
75
+ def self.write_report_to_file(report, file_name)
76
+ UI.verbose("Writing merged results to file...")
77
+ File.new(file_name, 'w')
78
+ File.open(file_name, 'w') do |f|
79
+ f.write(report.to_xml)
80
+ end
81
+ end
82
+
83
+ # Returns the test name of the retried test
84
+ def self.get_test_name(test_data)
85
+ test_name = test_data.xpath("(//key[contains(.,'TestSummaryGUID')])/../key[contains(.,'TestName')]/following-sibling::string").to_a[0].to_s
86
+ test_name = test_name[8..-10]
87
+ test_name
88
+ end
89
+
90
+ #####################################################
91
+ # @!group Documentation
92
+ #####################################################
93
+
94
+ def self.description
95
+ "Combines test results from multiple plist files."
96
+ end
97
+
98
+ def self.details
99
+ "Based on the fastlane-plugins-test_center plugin by lyndsey-ferguson/@lyndseydf"
100
+ end
101
+
102
+ def self.available_options
103
+ [
104
+ FastlaneCore::ConfigItem.new(
105
+ key: :reports,
106
+ env_name: 'COLLATE_PLIST_REPORTS_REPORTS',
107
+ description: 'An array of plist reports to collate. The first report is used as the base into which other reports are merged in',
108
+ optional: false,
109
+ type: Array,
110
+ verify_block: proc do |reports|
111
+ UI.user_error!('No plist report files found') if reports.empty?
112
+ reports.each do |report|
113
+ UI.user_error!("Error: plist report not found: '#{report}'") unless File.exist?(report)
114
+ end
115
+ end
116
+ ),
117
+ FastlaneCore::ConfigItem.new(
118
+ key: :collated_report,
119
+ env_name: 'COLLATE_PLIST_REPORTS_COLLATED_REPORT',
120
+ description: 'The final plist report file where all testcases will be merged into',
121
+ optional: true,
122
+ default_value: 'result.xml',
123
+ type: String
124
+ ),
125
+ FastlaneCore::ConfigItem.new(
126
+ key: :assets,
127
+ env_name: 'COLLATE_PLIST_REPORTS_ASSETS',
128
+ description: 'An array of plist reports to collate. The first report is used as the base into which other reports are merged in',
129
+ optional: false,
130
+ type: Array,
131
+ verify_block: proc do |assets|
132
+ UI.user_error!('No plist report files found') if assets.empty?
133
+ assets.each do |asset|
134
+ UI.user_error!("Error: plist report not found: '#{asset}'") unless File.exist?(asset)
135
+ end
136
+ end
137
+ ),
138
+ FastlaneCore::ConfigItem.new(
139
+ key: :logs,
140
+ env_name: 'COLLATE_PLIST_REPORTS_LOGS',
141
+ description: 'An array of plist reports to collate. The first report is used as the base into which other reports are merged in',
142
+ optional: false,
143
+ type: Array,
144
+ verify_block: proc do |logs|
145
+ UI.user_error!('No plist report files found') if logs.empty?
146
+ logs.each do |log|
147
+ UI.user_error!("Error: plist report not found: '#{log}'") unless File.exist?(log)
148
+ end
149
+ end
150
+ )
151
+ ]
152
+ end
153
+
154
+ def self.authors
155
+ ["Gloria Chow/@gmgchow"]
156
+ end
157
+
158
+ def self.is_supported?(platform)
159
+ platform == :ios
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,118 @@
1
+ module Fastlane
2
+ module Actions
3
+ require 'fastlane/actions/scan'
4
+ require 'shellwords'
5
+ require 'nokogiri'
6
+ require 'nokogiri-plist'
7
+ require 'pry'
8
+
9
+ class MultiScanAction < Action
10
+ def self.run(params)
11
+ try_count = 0
12
+ scan_options = params.values.reject { |k| k == :try_count }
13
+ final_report_path = scan_options[:result_bundle]
14
+ unless Helper.test?
15
+ FastlaneCore::PrintTable.print_values(
16
+ config: params._values.reject { |k, v| scan_options.key?(k) },
17
+ title: "Summary for multi_scan"
18
+ )
19
+ end
20
+
21
+ scan_options = config_with_junit_report(scan_options)
22
+ begin
23
+ try_count += 1
24
+ scan_options = config_with_retry(scan_options, try_count)
25
+ config = FastlaneCore::Configuration.create(Fastlane::Actions::ScanAction.available_options, scan_options)
26
+ Fastlane::Actions::ScanAction.run(config)
27
+ rescue FastlaneCore::Interface::FastlaneTestFailure => e
28
+ UI.verbose("Scan failed with #{e}")
29
+ if try_count < params[:try_count]
30
+ report_filepath = junit_report_filepath(scan_options)
31
+ failed_tests = parse_failures(report_filepath)
32
+ scan_options[:only_testing] = failed_tests
33
+ retry
34
+ end
35
+ end
36
+ merge_reports(scan_options, final_report_path)
37
+ end
38
+
39
+ # Parse the names of the failed test cases
40
+ def self.parse_failures(plist)
41
+ failures = Array.new
42
+ target_report = File.open(plist) {|f| Nokogiri::XML(f)}
43
+ failed = target_report.xpath("//key[contains(.,'Failure')]/../key[contains(.,'TestIdentifier')]/following-sibling::string[contains(.,'()') and contains (., '/')]")
44
+ failed.each do |test_name|
45
+ failures << ("MercariUITests/" + test_name.to_s.split('(')[0].split('>')[1])
46
+ end
47
+ failures
48
+ end
49
+
50
+ # Merge results from all retries
51
+ def self.merge_reports(scan_options, final_report_path)
52
+ folder = get_folder_root(scan_options[:output_directory])
53
+ report_files = Dir.glob("#{folder}*/MercariUITests.test_result/1_Test/action_TestSummaries.plist")
54
+ asset_files = Dir.glob("#{folder}*/MercariUITests.test_result/1_Test/Attachments")
55
+ log_files = Dir.glob("#{folder}*/MercariUITests.test_result/1_Test/action.xcactivitylog")
56
+ if report_files.size > 1
57
+ other_action.collate_junit_reports(
58
+ reports: report_files,
59
+ collated_report: final_report_path,
60
+ assets: asset_files,
61
+ logs: log_files,
62
+ )
63
+ end
64
+ end
65
+
66
+ # Create scan config
67
+ def self.config_with_retry(config, count)
68
+ folder = get_folder_root(config[:result_bundle])
69
+ config[:result_bundle] = (folder + count.to_s)
70
+ config[:output_directory] = (folder + count.to_s)
71
+ config
72
+ end
73
+
74
+ # Get folder location
75
+ def self.get_folder_root(folder)
76
+ folder = folder.gsub(/ *\d+$/, '')
77
+ folder
78
+ end
79
+
80
+ #####################################################
81
+ # @!group Documentation
82
+ #####################################################
83
+
84
+ def self.description
85
+ "Uses scan to run Xcode tests a given number of times: only re-testing failing tests."
86
+ end
87
+
88
+ def self.details
89
+ "Use this action to run your tests if you have fragile tests that fail sporadically."
90
+ end
91
+
92
+ def self.scan_options
93
+ ScanAction.available_options
94
+ end
95
+
96
+ def self.available_options
97
+ scan_options + [
98
+ FastlaneCore::ConfigItem.new(
99
+ key: :try_count,
100
+ env_name: "FL_MULTI_SCAN_TRY_COUNT",
101
+ description: "The number of times to retry running tests via scan",
102
+ type: Integer,
103
+ is_string: false,
104
+ default_value: 1
105
+ )
106
+ ]
107
+ end
108
+
109
+ def self.authors
110
+ ["Gloria Chow/@gmgchow"]
111
+ end
112
+
113
+ def self.is_supported?(platform)
114
+ platform == :ios
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,5 @@
1
+ module Fastlane
2
+ module TestCenter
3
+ VERSION = "3.0.6"
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require 'fastlane/plugin/retry_tests/version'
2
+
3
+ module Fastlane
4
+ module RetryTests
5
+ # Return all .rb files inside the "actions" and "helper" directory
6
+ def self.all_classes
7
+ Dir[File.expand_path('**/{actions,helper}/*.rb', File.dirname(__FILE__))]
8
+ end
9
+ end
10
+ end
11
+
12
+ # By default we want to import all available actions and helpers
13
+ # A plugin can contain any number of actions and plugins
14
+ Fastlane::RetryTests.all_classes.each do |current|
15
+ require current
16
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fastlane-plugin-retry_failed_tests
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Gloria Chow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: xcodeproj
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'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fastlane
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 2.56.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 2.56.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
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: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: simplecov
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: " Retries failed iOS tests\n"
140
+ email: gchow@mercari.com
141
+ executables: []
142
+ extensions: []
143
+ extra_rdoc_files: []
144
+ files:
145
+ - LICENSE
146
+ - README.md
147
+ - lib/fastlane/plugin/retry_tests.rb
148
+ - lib/fastlane/plugin/retry_tests/actions/collate_junit_reports.rb
149
+ - lib/fastlane/plugin/retry_tests/actions/multi_scan.rb
150
+ - lib/fastlane/plugin/retry_tests/version.rb
151
+ homepage: ''
152
+ licenses:
153
+ - ''
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.6.14
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: Retries failed iOS tests
175
+ test_files: []