Class: Mergify::RSpec::FlakyDetector
- Inherits:
-
Object
- Object
- Mergify::RSpec::FlakyDetector
- Defined in:
- lib/mergify/rspec/flaky_detection.rb
Overview
Manages intelligent test rerunning with budget constraints for flaky detection. rubocop:disable Metrics/ClassLength
Defined Under Namespace
Classes: TestMetrics
Instance Attribute Summary collapse
-
#budget ⇒ Object
readonly
Returns the value of attribute budget.
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
-
#tests_to_process ⇒ Object
readonly
Returns the value of attribute tests_to_process.
Instance Method Summary collapse
-
#fill_metrics_from_report(test_id, phase, duration, status) ⇒ Object
rubocop:disable Metrics/MethodLength.
-
#initialize(token:, url:, full_repository_name:, mode:) ⇒ FlakyDetector
constructor
A new instance of FlakyDetector.
- #last_rerun_for_test?(test_id) ⇒ Boolean
-
#make_report ⇒ Object
rubocop:disable Metrics/MethodLength,Metrics/AbcSize.
-
#prepare_for_session(test_ids) ⇒ Object
rubocop:disable Metrics/MethodLength,Metrics/AbcSize.
-
#rerunning_test?(test_id) ⇒ Boolean
rubocop:enable Metrics/MethodLength.
- #set_test_deadline(test_id, timeout: nil) ⇒ Object
- #test_metrics(test_id) ⇒ Object
- #test_rerun?(test_id) ⇒ Boolean
- #test_too_slow?(test_id) ⇒ Boolean
Constructor Details
#initialize(token:, url:, full_repository_name:, mode:) ⇒ FlakyDetector
Returns a new instance of FlakyDetector.
61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 61 def initialize(token:, url:, full_repository_name:, mode:) @token = token @url = url @full_repository_name = full_repository_name @mode = mode @metrics = {} @over_length_tests = Set.new @tests_to_process = [] @budget = 0.0 fetch_context validate! end |
Instance Attribute Details
#budget ⇒ Object (readonly)
Returns the value of attribute budget.
59 60 61 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 59 def budget @budget end |
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
59 60 61 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 59 def mode @mode end |
#tests_to_process ⇒ Object (readonly)
Returns the value of attribute tests_to_process.
59 60 61 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 59 def tests_to_process @tests_to_process end |
Instance Method Details
#fill_metrics_from_report(test_id, phase, duration, status) ⇒ Object
rubocop:disable Metrics/MethodLength
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 103 def fill_metrics_from_report(test_id, phase, duration, status) if status == :skipped @metrics.delete(test_id) return end return unless @tests_to_process.include?(test_id) if test_id.length > @context[:max_test_name_length] @over_length_tests.add(test_id) return end # Only initialize metrics when the first phase is "setup" return if !@metrics.key?(test_id) && phase != 'setup' @metrics[test_id] ||= TestMetrics.new @metrics[test_id].fill_from_report(phase, duration, status) end |
#last_rerun_for_test?(test_id) ⇒ Boolean
156 157 158 159 160 161 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 156 def last_rerun_for_test?(test_id) return false unless @metrics.key?(test_id) metrics = @metrics[test_id] metrics.will_exceed_deadline? || metrics.rerun_count >= @context[:max_test_execution_count] end |
#make_report ⇒ Object
rubocop:disable Metrics/MethodLength,Metrics/AbcSize
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 168 def make_report lines = [] lines << 'Mergify Flaky Detection Report' lines << " Mode : #{@mode}" lines << " Budget : #{format('%.2f', @budget)}s" lines << " Budget used : #{format('%.2f', budget_used)}s" lines << " Tests tracked: #{@metrics.size}" lines << '' @metrics.each do |test_id, m| lines << " #{test_id}" lines << " Reruns : #{m.rerun_count}" lines << " Initial dur : #{format('%.3f', m.initial_duration)}s" lines << " Total dur : #{format('%.3f', m.total_duration)}s" lines << " Timeout warn : #{m.prevented_timeout}" if m.prevented_timeout end lines << '' unless @over_length_tests.empty? @over_length_tests.each do |id| lines << " WARNING: test name too long (skipped): #{id[0, 80]}..." end lines.join("\n") end |
#prepare_for_session(test_ids) ⇒ Object
rubocop:disable Metrics/MethodLength,Metrics/AbcSize
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 76 def prepare_for_session(test_ids) existing = Set.new(@context[:existing_test_names]) unhealthy = Set.new(@context[:unhealthy_test_names]) @tests_to_process = if @mode == 'new' test_ids.reject { |id| existing.include?(id) } else test_ids.select { |id| unhealthy.include?(id) } end budget_ratio = if @mode == 'new' @context[:budget_ratio_for_new_tests] else @context[:budget_ratio_for_unhealthy_tests] end mean_duration_s = @context[:existing_tests_mean_duration_ms] / 1000.0 existing_count = @context[:existing_test_names].size min_budget_s = @context[:min_budget_duration_ms] / 1000.0 ratio_budget = budget_ratio * mean_duration_s * existing_count @budget = [ratio_budget, min_budget_s].max end |
#rerunning_test?(test_id) ⇒ Boolean
rubocop:enable Metrics/MethodLength
124 125 126 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 124 def rerunning_test?(test_id) @metrics.key?(test_id) && @metrics[test_id].rerun_count >= 1 end |
#set_test_deadline(test_id, timeout: nil) ⇒ Object
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 132 def set_test_deadline(test_id, timeout: nil) return unless @metrics.key?(test_id) remaining_tests = [remaining_tests_count, 1].max per_test_budget = remaining_budget / remaining_tests allocated = if timeout [per_test_budget, timeout * 0.9].min else per_test_budget end @metrics[test_id].deadline = Time.now.to_f + allocated end |
#test_metrics(test_id) ⇒ Object
163 164 165 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 163 def test_metrics(test_id) @metrics[test_id] end |
#test_rerun?(test_id) ⇒ Boolean
128 129 130 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 128 def test_rerun?(test_id) @metrics.key?(test_id) && @metrics[test_id].rerun_count > 1 end |
#test_too_slow?(test_id) ⇒ Boolean
148 149 150 151 152 153 154 |
# File 'lib/mergify/rspec/flaky_detection.rb', line 148 def test_too_slow?(test_id) return false unless @metrics.key?(test_id) metrics = @metrics[test_id] min_exec = @context[:min_test_execution_count] (metrics.initial_duration * min_exec) > metrics.remaining_time end |