Skip to content

Commit

Permalink
parse slow test retries payload from library settings
Browse files Browse the repository at this point in the history
  • Loading branch information
anmarchenko committed Aug 30, 2024
1 parent 1c91543 commit 0798794
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 1 deletion.
12 changes: 12 additions & 0 deletions lib/datadog/ci/remote/library_settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
require_relative "../transport/telemetry"
require_relative "../utils/parsing"

require_relative "slow_test_retries"

module Datadog
module CI
module Remote
Expand Down Expand Up @@ -99,6 +101,16 @@ def early_flake_detection_enabled?
)
end

def slow_test_retries
return @slow_test_retries if defined?(@slow_test_retries)

@slow_test_retries = SlowTestRetries.new(
payload
.fetch(Ext::Transport::DD_API_SETTINGS_RESPONSE_EARLY_FLAKE_DETECTION_KEY, {})
.fetch("slow_test_retries", {})
)
end

private

def default_payload
Expand Down
53 changes: 53 additions & 0 deletions lib/datadog/ci/remote/slow_test_retries.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# frozen_string_literal: true

module Datadog
module CI
module Remote
# Parses "slow_test_retries" payload for early flake detection settings
#
# Example payload:
# {
# "5s" => 10,
# "10s" => 5,
# "30s" => 3,
# "5m" => 2
# }
#
# The payload above means that for tests that run less than 5 seconds, we should retry them 10 times,
# for tests that run less than 10 seconds, we should retry them 5 times, and so on.
class SlowTestRetries
attr_reader :entries

Entry = Struct.new(:duration, :max_attempts)

DURATION_MEASURES = {
"s" => 1,
"m" => 60
}.freeze

def initialize(payload)
@entries = parse(payload)
end

def max_attempts_for_duration(duration)
@entries.each do |entry|
return entry.max_attempts if duration < entry.duration
end

1
end

private

def parse(payload)
(payload || {}).keys.filter_map do |key|
duration, measure = key.match(/(\d+)(\w+)/)&.captures
next if duration.nil? || measure.nil? || !DURATION_MEASURES.key?(measure)

Entry.new(duration.to_f * DURATION_MEASURES.fetch(measure, 1), payload[key].to_i)
end.sort_by(&:duration)
end
end
end
end
end
2 changes: 1 addition & 1 deletion lib/datadog/ci/test_retries/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def initialize(
@retry_failed_tests_enabled = retry_failed_tests_enabled
@retry_failed_tests_max_attempts = retry_failed_tests_max_attempts
@retry_failed_tests_total_limit = retry_failed_tests_total_limit
# counter that store the current number of failed tests retried
# counter that stores the current number of failed tests retried
@retry_failed_tests_count = 0

@retry_new_tests_enabled = retry_new_tests_enabled
Expand Down
3 changes: 3 additions & 0 deletions sig/datadog/ci/remote/library_settings.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module Datadog
@tests_skipping_enabled: bool
@flaky_test_retries_enabled: bool
@early_flake_detection_enabled: bool
@slow_test_retries: Datadog::CI::Remote::SlowTestRetries

def initialize: (Datadog::CI::Transport::Adapters::Net::Response? http_response) -> void

Expand All @@ -30,6 +31,8 @@ module Datadog

def early_flake_detection_enabled?: () -> bool

def slow_test_retries: () -> Datadog::CI::Remote::SlowTestRetries

private

def default_payload: () -> Hash[String, untyped]
Expand Down
27 changes: 27 additions & 0 deletions sig/datadog/ci/remote/slow_test_retries.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Datadog
module CI
module Remote
class SlowTestRetries
attr_reader entries: Enumerable[Entry]

class Entry
attr_accessor duration: Float

attr_accessor max_attempts: Integer

def initialize: (Float duration, Integer max_attempts) -> void
end

DURATION_MEASURES: Hash[String, Integer]

def initialize: (Hash[String, String | Integer] payload) -> void

def max_attempts_for_duration: (Float duration) -> Integer

private

def parse: (Hash[String, String | Integer] payload) -> Enumerable[Entry]
end
end
end
end
8 changes: 8 additions & 0 deletions spec/datadog/ci/remote/library_settings_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@
expect(response.tests_skipping_enabled?).to be false
expect(response.flaky_test_retries_enabled?).to be true
expect(response.early_flake_detection_enabled?).to be true
expect(response.slow_test_retries.entries).to eq(
[
Datadog::CI::Remote::SlowTestRetries::Entry.new(5.0, 10),
Datadog::CI::Remote::SlowTestRetries::Entry.new(10.0, 5),
Datadog::CI::Remote::SlowTestRetries::Entry.new(30.0, 3),
Datadog::CI::Remote::SlowTestRetries::Entry.new(300.0, 2)
]
)

metric = telemetry_metric(:inc, "git_requests.settings_response")
expect(metric.tags).to eq(
Expand Down
73 changes: 73 additions & 0 deletions spec/datadog/ci/remote/slow_test_retries_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

require_relative "../../../../lib/datadog/ci/remote/slow_test_retries"

RSpec.describe Datadog::CI::Remote::SlowTestRetries do
let(:payload) do
{
"5m" => 2,
"5s" => 10,
"30s" => 3,
"10s" => 5,
"ffdsfdsfds" => nil,
"10m" => nil,
"10h" => 12
}
end

subject(:slow_test_retries) { described_class.new(payload) }

describe "#initialize" do
subject { slow_test_retries.entries }

it do
is_expected.to eq([
described_class::Entry.new(5.0, 10),
described_class::Entry.new(10.0, 5),
described_class::Entry.new(30.0, 3),
described_class::Entry.new(300.0, 2),
described_class::Entry.new(600.0, 0)
])
end
end

describe "#max_attempts_for_duration" do
subject { slow_test_retries.max_attempts_for_duration(duration) }

context "when the duration is less than 5 seconds" do
let(:duration) { 4.9 }

it { is_expected.to eq(10) }
end

context "when the duration is less than 10 seconds" do
let(:duration) { 9.9 }

it { is_expected.to eq(5) }
end

context "when the duration is less than 30 seconds" do
let(:duration) { 29.9 }

it { is_expected.to eq(3) }
end

context "when the duration is less than 5 minutes" do
let(:duration) { 299.9 }

it { is_expected.to eq(2) }
end

context "when the duration is less than 10 minutes" do
let(:duration) { 599.9 }

it { is_expected.to eq(0) }
end

context "when the duration is more than 10 minutes" do
let(:duration) { 600.1 }

it { is_expected.to eq(1) }
end
end
end

0 comments on commit 0798794

Please sign in to comment.