Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDTEST-786] support logical names for test sessions #235

Merged
merged 7 commits into from
Sep 20, 2024
3 changes: 2 additions & 1 deletion lib/datadog/ci/configuration/components.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ def activate_ci!(settings)
# @type ivar @test_optimisation: Datadog::CI::TestOptimisation::Component
@test_optimisation = build_test_optimisation(settings, test_visibility_api)
@test_visibility = TestVisibility::Component.new(
test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility
test_suite_level_visibility_enabled: !settings.ci.force_test_level_visibility,
logical_test_session_name: settings.ci.test_session_name
)
end

Expand Down
5 changes: 5 additions & 0 deletions lib/datadog/ci/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ def self.add_settings!(base)
o.default false
end

option :test_session_name do |o|
o.type :string, nilable: true
o.env CI::Ext::Settings::ENV_TEST_SESSION_NAME
end

option :agentless_mode_enabled do |o|
o.type :bool
o.env CI::Ext::Settings::ENV_AGENTLESS_MODE_ENABLED
Expand Down
1 change: 1 addition & 0 deletions lib/datadog/ci/ext/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module Settings
ENV_RETRY_FAILED_TESTS_MAX_ATTEMPTS = "DD_CIVISIBILITY_FLAKY_RETRY_COUNT"
ENV_RETRY_FAILED_TESTS_TOTAL_LIMIT = "DD_CIVISIBILITY_TOTAL_FLAKY_RETRY_COUNT"
ENV_RETRY_NEW_TESTS_ENABLED = "DD_CIVISIBILITY_EARLY_FLAKE_DETECTION_ENABLED"
ENV_TEST_SESSION_NAME = "DD_TEST_SESSION_NAME"

# Source: https://docs.datadoghq.com/getting_started/site/
DD_SITE_ALLOWLIST = %w[
Expand Down
3 changes: 3 additions & 0 deletions lib/datadog/ci/ext/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ module Test
TAG_SPAN_KIND = "span.kind"
SPAN_KIND_TEST = "test"

# common tags that are serialized directly in msgpack header in metadata field
METADATA_TAG_TEST_SESSION_NAME = "test_session.name"

# tags that are common for the whole session and can be inherited from the test session
INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION].freeze

Expand Down
18 changes: 18 additions & 0 deletions lib/datadog/ci/test_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,24 @@ def name
get_tag(Ext::Test::TAG_COMMAND)
end

# Return the test session's command used to run the tests
# @return [String] the command for this test session.
def test_command
get_tag(Ext::Test::TAG_COMMAND)
end

# Return the test session's CI provider name (e.g. "travis", "circleci", etc.)
# @return [String] the provider name for this test session.
def ci_provider
get_tag(Ext::Environment::TAG_PROVIDER_NAME)
end

# Return the test session's CI job name (e.g. "build", "test", etc.)
# @return [String] the job name for this test session.
def ci_job_name
get_tag(Ext::Environment::TAG_JOB_NAME)
end

def skipping_tests?
get_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED) == "true"
end
Expand Down
17 changes: 15 additions & 2 deletions lib/datadog/ci/test_visibility/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ module CI
module TestVisibility
# Common behavior for CI tests
class Component
attr_reader :test_suite_level_visibility_enabled
attr_reader :test_suite_level_visibility_enabled, :logical_test_session_name

def initialize(
test_suite_level_visibility_enabled: false,
codeowners: Codeowners::Parser.new(Git::LocalRepository.root).parse
codeowners: Codeowners::Parser.new(Git::LocalRepository.root).parse,
logical_test_session_name: nil
)
@test_suite_level_visibility_enabled = test_suite_level_visibility_enabled
@context = Context.new
@codeowners = codeowners
@logical_test_session_name = logical_test_session_name
end

def start_test_session(service: nil, tags: {}, total_tests_count: 0)
Expand Down Expand Up @@ -152,6 +154,9 @@ def on_test_session_started(test_session)
Telemetry.test_session_started(test_session)
Telemetry.event_created(test_session)

# sets logical test session name if none provided by the user
override_logical_test_session_name!(test_session) if logical_test_session_name.nil?

# signal Remote::Component to configure the library
remote.configure(test_session)
end
Expand Down Expand Up @@ -269,6 +274,14 @@ def validate_test_suite_level_visibility_correctness(test)
end
end

def override_logical_test_session_name!(test_session)
@logical_test_session_name = test_session.test_command
ci_job_name = test_session.ci_job_name
if ci_job_name
@logical_test_session_name = "#{ci_job_name}-#{@logical_test_session_name}"
end
end

def test_optimisation
Datadog.send(:components).test_optimisation
end
Expand Down
7 changes: 7 additions & 0 deletions lib/datadog/ci/test_visibility/null_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ def set_test_finished_callback(_)
def remove_test_finished_callback
end

def test_suite_level_visibility_enabled
false
end

def logical_test_session_name
end

private

def skip_tracing(block = nil)
Expand Down
4 changes: 1 addition & 3 deletions lib/datadog/ci/test_visibility/telemetry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ def self.test_session_started(test_session)
1,
{
Ext::Telemetry::TAG_AUTO_INJECTED => "false", # ruby doesn't support auto injection yet
Ext::Telemetry::TAG_PROVIDER =>
test_session.get_tag(Ext::Environment::TAG_PROVIDER_NAME) ||
Ext::Telemetry::Provider::UNSUPPORTED
Ext::Telemetry::TAG_PROVIDER => test_session.ci_provider || Ext::Telemetry::Provider::UNSUPPORTED
}
)
end
Expand Down
24 changes: 21 additions & 3 deletions lib/datadog/ci/test_visibility/transport.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "datadog/core/environment/identity"

require_relative "serializers/factories/test_level"
require_relative "../ext/app_types"
require_relative "../ext/telemetry"
require_relative "../ext/transport"
require_relative "../transport/event_platform_transport"
Expand Down Expand Up @@ -51,7 +52,12 @@ def encode_events(traces)
end

def encode_span(trace, span)
serializer = serializers_factory.serializer(trace, span, options: {itr_correlation_id: itr&.correlation_id})
serializer = serializers_factory.serializer(
trace,
span,
options: {itr_correlation_id: test_optimisation&.correlation_id}
)

if serializer.valid?
encoded = encoder.encode(serializer)
return nil if event_too_large?(span, encoded)
Expand All @@ -75,7 +81,7 @@ def write_payload_header(packer)
packer.write(1)

packer.write("metadata")
packer.write_map_header(1)
packer.write_map_header(1 + Ext::AppTypes::CI_SPAN_TYPES.size)

packer.write("*")
metadata_fields_count = dd_env ? 4 : 3
Expand All @@ -95,12 +101,24 @@ def write_payload_header(packer)
packer.write("library_version")
packer.write(Datadog::CI::VERSION::STRING)

Ext::AppTypes::CI_SPAN_TYPES.each do |ci_span_type|
packer.write(ci_span_type)
packer.write_map_header(1)

packer.write(Ext::Test::METADATA_TAG_TEST_SESSION_NAME)
packer.write(test_visibility&.logical_test_session_name)
end

packer.write("events")
end

def itr
def test_optimisation
@test_optimisation ||= Datadog::CI.send(:test_optimisation)
end

def test_visibility
@test_visibility ||= Datadog::CI.send(:test_visibility)
end
end
end
end
Expand Down
1 change: 1 addition & 0 deletions sig/datadog/ci/ext/settings.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module Datadog
ENV_RETRY_FAILED_TESTS_MAX_ATTEMPTS: String
ENV_RETRY_FAILED_TESTS_TOTAL_LIMIT: String
ENV_RETRY_NEW_TESTS_ENABLED: String
ENV_TEST_SESSION_NAME: String

DD_SITE_ALLOWLIST: Array[String]
end
Expand Down
2 changes: 2 additions & 0 deletions sig/datadog/ci/ext/test.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ module Datadog

EARLY_FLAKE_FAULTY: "faulty"

METADATA_TAG_TEST_SESSION_NAME: "test_session.name"

module Status
PASS: "pass"

Expand Down
6 changes: 6 additions & 0 deletions sig/datadog/ci/test_session.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ module Datadog
@inheritable_tags: Hash[untyped, untyped]

def inheritable_tags: () -> Hash[untyped, untyped]

def test_command: () -> String?

def ci_provider: () -> String?

def ci_job_name: () -> String?
end
end
end
6 changes: 5 additions & 1 deletion sig/datadog/ci/test_visibility/component.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ module Datadog

attr_reader test_suite_level_visibility_enabled: bool

def initialize: (?test_suite_level_visibility_enabled: bool, ?codeowners: Datadog::CI::Codeowners::Matcher) -> void
attr_reader logical_test_session_name: String?

def initialize: (?test_suite_level_visibility_enabled: bool, ?codeowners: Datadog::CI::Codeowners::Matcher, ?logical_test_session_name: String?) -> void

def trace_test: (String span_name, String test_suite_name, ?service: String?, ?tags: Hash[untyped, untyped]) ?{ (Datadog::CI::Test span) -> untyped } -> untyped

Expand Down Expand Up @@ -77,6 +79,8 @@ module Datadog

def subscribe_to_after_stop_event: (Datadog::Tracing::SpanOperation span) -> void

def override_logical_test_session_name!: (Datadog::CI::TestSession test_session) -> void

def test_optimisation: () -> Datadog::CI::TestOptimisation::Component

def test_retries: () -> Datadog::CI::TestRetries::Component
Expand Down
5 changes: 4 additions & 1 deletion sig/datadog/ci/test_visibility/transport.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ module Datadog

@dd_env: String?
@serializers_factory: singleton(Datadog::CI::TestVisibility::Serializers::Factories::TestLevel) | singleton(Datadog::CI::TestVisibility::Serializers::Factories::TestSuiteLevel)

@test_optimisation: Datadog::CI::TestOptimisation::Component?
@test_visibility: Datadog::CI::TestVisibility::Component?

def initialize: (
api: Datadog::CI::Transport::Api::Base,
Expand All @@ -21,7 +23,8 @@ module Datadog
def send_payload: (String encoded_payload) -> Datadog::CI::Transport::Adapters::Net::Response
def encode_events: (Array[Datadog::Tracing::TraceSegment] traces) -> ::Array[String]
def encode_span: (Datadog::Tracing::TraceSegment trace, Datadog::Tracing::Span span) -> String?
def itr: () -> Datadog::CI::TestOptimisation::Component?
def test_optimisation: () -> Datadog::CI::TestOptimisation::Component?
def test_visibility: () -> Datadog::CI::TestVisibility::Component?
end
end
end
Expand Down
37 changes: 36 additions & 1 deletion spec/datadog/ci/configuration/settings_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,41 @@ def patcher
end
end

describe "#test_session_name" do
subject(:test_session_name) { settings.ci.test_session_name }

it { is_expected.to be nil }

context "when #{Datadog::CI::Ext::Settings::ENV_TEST_SESSION_NAME}" do
around do |example|
ClimateControl.modify(Datadog::CI::Ext::Settings::ENV_TEST_SESSION_NAME => test_session_name) do
example.run
end
end

context "is not defined" do
let(:test_session_name) { nil }

it { is_expected.to be nil }
end

context "is set to some value" do
let(:test_session_name) { "knapsack-pro-circle-ci-runner-1" }

it { is_expected.to eq test_session_name }
end
end
end

describe "#test_session_name=" do
it "updates the #test_session_name setting" do
expect { settings.ci.test_session_name = "rspec" }
.to change { settings.ci.test_session_name }
.from(nil)
.to("rspec")
end
end

describe "#agentless_mode_enabled" do
subject(:agentless_mode_enabled) { settings.ci.agentless_mode_enabled }

Expand Down Expand Up @@ -166,7 +201,7 @@ def patcher
end

describe "#agentless_url=" do
it "updates the #enabled setting" do
it "updates the #agentless_url setting" do
expect { settings.ci.agentless_url = "example.com" }
.to change { settings.ci.agentless_url }
.from(nil)
Expand Down
30 changes: 30 additions & 0 deletions spec/datadog/ci/test_session_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,36 @@
it { is_expected.to eq("test command") }
end

describe "#test_command" do
subject(:test_command) { ci_test_session.test_command }

before do
tracer_span.set_tag(Datadog::CI::Ext::Test::TAG_COMMAND, "test command")
end

it { is_expected.to eq("test command") }
end

describe "#ci_provider" do
subject(:ci_provider) { ci_test_session.ci_provider }

before do
tracer_span.set_tag(Datadog::CI::Ext::Environment::TAG_PROVIDER_NAME, "ci provider")
end

it { is_expected.to eq("ci provider") }
end

describe "#ci_job_name" do
subject(:ci_job_name) { ci_test_session.ci_job_name }

before do
tracer_span.set_tag(Datadog::CI::Ext::Environment::TAG_JOB_NAME, "ci job name")
end

it { is_expected.to eq("ci job name") }
end

describe "#skipping_tests?" do
subject(:skipping_tests?) { ci_test_session.skipping_tests? }

Expand Down
36 changes: 36 additions & 0 deletions spec/datadog/ci/test_visibility/component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,42 @@
end
end

context "with logical test session name set" do
let(:logical_test_session_name) { "my-logical-test-session" }

it "sets the logical test session name" do
subject

expect(test_visibility.logical_test_session_name).to eq(logical_test_session_name)
end
end

context "without logical test session name set" do
before do
allow_any_instance_of(Datadog::CI::TestSession).to receive(:ci_job_name).and_return(ci_job_name)
end

context "without CI job name available" do
let(:ci_job_name) { nil }

it "uses the test command" do
subject

expect(test_visibility.logical_test_session_name).to eq(test_command)
end
end

context "with CI job name available" do
let(:ci_job_name) { "my-ci-job" }

it "uses the CI job name and test command" do
subject

expect(test_visibility.logical_test_session_name).to eq("#{ci_job_name}-#{test_command}")
end
end
end

it_behaves_like "span with environment tags"
it_behaves_like "span with default tags"
it_behaves_like "span with runtime tags"
Expand Down
Loading
Loading