Skip to content

Commit

Permalink
serializer for test modules
Browse files Browse the repository at this point in the history
  • Loading branch information
anmarchenko committed Nov 30, 2023
1 parent b48270f commit 3f10e27
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 13 deletions.
3 changes: 2 additions & 1 deletion lib/datadog/ci/recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ def start_test_module(test_module_name, service_name: nil, tags: {})
test_module
end

# Creates a new span for a CI test
def trace_test(test_name, service_name: nil, operation_name: "test", tags: {}, &block)
return skip_tracing(block) unless enabled

Expand All @@ -78,6 +77,8 @@ def trace_test(test_name, service_name: nil, operation_name: "test", tags: {}, &
span_options = build_span_options(
service_name,
Ext::AppTypes::TYPE_TEST,
# :resource is needed for the agent APM protocol to work correctly (for older agent versions)
# :continue_from is required to start a new trace for each test
{resource: test_name, continue_from: Datadog::Tracing::TraceDigest.new}
)

Expand Down
4 changes: 4 additions & 0 deletions lib/datadog/ci/test_visibility/serializers/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ def test_session_id
@span.get_tag(Ext::Test::TAG_TEST_SESSION_ID)
end

def test_module_id
@span.get_tag(Ext::Test::TAG_TEST_MODULE_ID)
end

def type
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require_relative "../test_v2"
require_relative "../test_session"
require_relative "../test_module"
require_relative "../span"

module Datadog
Expand All @@ -19,6 +20,8 @@ def serializer(trace, span)
Serializers::TestV2.new(trace, span)
when Datadog::CI::Ext::AppTypes::TYPE_TEST_SESSION
Serializers::TestSession.new(trace, span)
when Datadog::CI::Ext::AppTypes::TYPE_TEST_MODULE
Serializers::TestModule.new(trace, span)
else
Serializers::Span.new(trace, span)
end
Expand Down
61 changes: 61 additions & 0 deletions lib/datadog/ci/test_visibility/serializers/test_module.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# frozen_string_literal: true

require_relative "base"
require_relative "../../ext/test"

module Datadog
module CI
module TestVisibility
module Serializers
class TestModule < Base
CONTENT_FIELDS = [
"test_session_id",
"test_module_id",
"name", "resource", "service",
"error", "start", "duration",
"meta", "metrics",
"type" => "span_type"
].freeze

CONTENT_MAP_SIZE = calculate_content_map_size(CONTENT_FIELDS)

REQUIRED_FIELDS = [
"test_session_id",
"test_module_id",
"error",
"name",
"resource",
"start",
"duration"
].freeze

def content_fields
CONTENT_FIELDS
end

def content_map_size
CONTENT_MAP_SIZE
end

def type
Ext::AppTypes::TYPE_TEST_MODULE
end

def name
"#{@span.get_tag(Ext::Test::TAG_FRAMEWORK)}.test_module"
end

def resource
"#{@span.get_tag(Ext::Test::TAG_FRAMEWORK)}.test_module.#{@span.get_tag(Ext::Test::TAG_MODULE)}"
end

private

def required_fields
REQUIRED_FIELDS
end
end
end
end
end
end
4 changes: 2 additions & 2 deletions lib/datadog/ci/test_visibility/serializers/test_v2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ module CI
module TestVisibility
module Serializers
class TestV2 < TestV1
CONTENT_FIELDS = (["test_session_id"] + TestV1::CONTENT_FIELDS).freeze
CONTENT_FIELDS = (["test_session_id", "test_module_id"] + TestV1::CONTENT_FIELDS).freeze

CONTENT_MAP_SIZE = calculate_content_map_size(CONTENT_FIELDS)

REQUIRED_FIELDS = (["test_session_id"] + TestV1::REQUIRED_FIELDS).freeze
REQUIRED_FIELDS = (["test_session_id", "test_module_id"] + TestV1::REQUIRED_FIELDS).freeze

def content_fields
CONTENT_FIELDS
Expand Down
26 changes: 26 additions & 0 deletions sig/datadog/ci/test_visibility/serializers/test_module.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Datadog
module CI
module TestVisibility
module Serializers
class TestModule < Base
CONTENT_FIELDS: Array[String | Hash[String, String]]
CONTENT_MAP_SIZE: Integer
REQUIRED_FIELDS: Array[String]

def content_fields: () -> Array[String | Hash[String, String]]
def content_map_size: () -> Integer

def type: () -> ::String

def name: () -> ::String

def resource: () -> ::String

private

def required_fields: () -> Array[String]
end
end
end
end
end
106 changes: 106 additions & 0 deletions spec/datadog/ci/test_visibility/serializers/test_module_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
RSpec.describe Datadog::CI::TestVisibility::Serializers::TestModule do
include_context "CI mode activated" do
let(:integration_name) { :rspec }
end

include_context "Test visibility event serialized" do
subject { described_class.new(trace_for_span(test_module_span), test_module_span) }
end

describe "#to_msgpack" do
context "traced a single test execution with Recorder" do
before do
produce_test_session_trace
end

it "serializes test event to messagepack" do
expect_event_header(type: Datadog::CI::Ext::AppTypes::TYPE_TEST_MODULE)

expect(content).to include(
{
"test_session_id" => test_session_span.id.to_s,
"test_module_id" => test_module_span.id.to_s,
"name" => "rspec.test_module",
"service" => "rspec-test-suite",
"type" => Datadog::CI::Ext::AppTypes::TYPE_TEST_MODULE,
"resource" => "rspec.test_module.arithmetic"
}
)

expect(meta).to include(
{
"test.command" => test_command,
"test.module" => "arithmetic",
"test.framework" => "rspec",
"test.framework_version" => "1.0.0",
"test.type" => "test",
"test.status" => "pass",
"_dd.origin" => "ciapp-test"
}
)

expect(meta["_test.session_id"]).to be_nil
expect(meta["_test.module_id"]).to be_nil
end
end

context "trace a failed test" do
before do
produce_test_session_trace(result: "FAILED", exception: StandardError.new("1 + 2 are not equal to 5"))
end

it "has error" do
expect_event_header(type: Datadog::CI::Ext::AppTypes::TYPE_TEST_MODULE)

expect(content).to include({"error" => 1})
expect(meta).to include({"test.status" => "fail"})
end
end
end

describe "#valid?" do
context "test_session_id" do
before do
produce_test_session_trace
end

context "when test_session_id is not nil" do
it "returns true" do
expect(subject.valid?).to eq(true)
end
end

context "when test_session_id is nil" do
before do
test_module_span.clear_tag("_test.session_id")
end

it "returns false" do
expect(subject.valid?).to eq(false)
end
end
end

context "test_module_id" do
before do
produce_test_session_trace
end

context "when test_module_id is not nil" do
it "returns true" do
expect(subject.valid?).to eq(true)
end
end

context "when test_module_id is nil" do
before do
test_module_span.clear_tag("_test.module_id")
end

it "returns false" do
expect(subject.valid?).to eq(false)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
require_relative "../../../../../lib/datadog/ci/test_visibility/serializers/test_session"
require_relative "../../../../../lib/datadog/ci/recorder"
require_relative "../../../../../lib/datadog/ci/ext/app_types"

RSpec.describe Datadog::CI::TestVisibility::Serializers::TestSession do
include_context "CI mode activated" do
let(:integration_name) { :rspec }
Expand Down Expand Up @@ -34,7 +30,9 @@
{
"test.command" => test_command,
"test.framework" => "rspec",
"test.framework_version" => "1.0.0",
"test.status" => "pass",
"test.type" => "test",
"_dd.origin" => "ciapp-test"
}
)
Expand Down
29 changes: 28 additions & 1 deletion spec/datadog/ci/test_visibility/serializers/test_v2_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,25 @@
"service" => "rspec-test-suite",
"type" => "test",
"resource" => "calculator_tests.test_add.run.0",
"test_session_id" => test_session_span.id.to_s
"test_session_id" => test_session_span.id.to_s,
"test_module_id" => test_module_span.id.to_s
}
)

expect(meta).to include(
{
"test.name" => "test_add.run.0",
"test.framework" => "rspec",
"test.framework_version" => "1.0.0",
"test.suite" => "calculator_tests",
"test.module" => "arithmetic",
"test.status" => "pass",
"_dd.origin" => "ciapp-test",
"test_owner" => "my_team"
}
)
expect(meta["_test.session_id"]).to be_nil
expect(meta["_test.module_id"]).to be_nil

expect(metrics).to eq({"_dd.top_level" => 1, "memory_allocations" => 16})
end
Expand Down Expand Up @@ -128,5 +133,27 @@
end
end
end

context "test_module_id" do
before do
produce_test_session_trace
end

context "when test_module_id is not nil" do
it "returns true" do
expect(subject.valid?).to eq(true)
end
end

context "when test_module_id is nil" do
before do
first_test_span.clear_tag("_test.module_id")
end

it "returns false" do
expect(subject.valid?).to eq(false)
end
end
end
end
end
19 changes: 14 additions & 5 deletions spec/support/tracer_helpers.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require "datadog/tracing"
require "datadog/ci"
require "datadog/ci/recorder"

# For contrib, we only allow one tracer to be active:
# the global tracer in +Datadog::Tracing+.
Expand Down Expand Up @@ -52,15 +53,14 @@ def produce_test_trace(
end

def produce_test_session_trace(
tests_count: 1,
framework: "rspec", operation: "rspec.example",
test_name: "test_add", test_suite: "calculator_tests",
tests_count: 1, framework: "rspec", operation: "rspec.example",
test_name: "test_add", test_suite: "calculator_tests", test_module_name: "arithmetic",
service: "rspec-test-suite", result: "PASSED", exception: nil,
skip_reason: nil, start_time: Time.now, duration_seconds: 2,
with_http_span: false
)
allow(Process).to receive(:clock_gettime).and_return(
0, duration_seconds, 2 * duration_seconds, 3 * duration_seconds
0, duration_seconds, 2 * duration_seconds, 3 * duration_seconds, 4 * duration_seconds, 5 * duration_seconds
)

test_session = Datadog::CI.start_test_session(
Expand All @@ -72,6 +72,8 @@ def produce_test_session_trace(
}
)

test_module = Datadog::CI.start_test_module(test_module_name)

tests_count.times do |num|
produce_test_trace(
framework: framework, operation: operation,
Expand All @@ -84,14 +86,21 @@ def produce_test_session_trace(
)
end

set_result(test_module, result: result, exception: exception, skip_reason: skip_reason)
set_result(test_session, result: result, exception: exception, skip_reason: skip_reason)

test_module.finish
test_session.finish
end

def test_session_span
spans.find { |span| span.type == "test_session_end" }
end

def test_module_span
spans.find { |span| span.type == "test_module_end" }
end

def first_test_span
spans.find { |span| span.type == "test" }
end
Expand Down

0 comments on commit 3f10e27

Please sign in to comment.