diff --git a/lib/datadog/ci/contrib/cucumber/configuration/settings.rb b/lib/datadog/ci/contrib/cucumber/configuration/settings.rb index e99a4948..5a84ad29 100644 --- a/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +++ b/lib/datadog/ci/contrib/cucumber/configuration/settings.rb @@ -4,6 +4,7 @@ require_relative "../ext" require_relative "../../settings" +require_relative "../../../utils/configuration" module Datadog module CI @@ -21,7 +22,9 @@ class Settings < Datadog::CI::Contrib::Settings option :service_name do |o| o.type :string - o.default { Datadog.configuration.service_without_fallback || Ext::DEFAULT_SERVICE_NAME } + o.default do + Utils::Configuration.fetch_service_name(Ext::DEFAULT_SERVICE_NAME) + end end # @deprecated Will be removed in 1.0 diff --git a/lib/datadog/ci/contrib/minitest/configuration/settings.rb b/lib/datadog/ci/contrib/minitest/configuration/settings.rb index b3caa11c..d6bd0a61 100644 --- a/lib/datadog/ci/contrib/minitest/configuration/settings.rb +++ b/lib/datadog/ci/contrib/minitest/configuration/settings.rb @@ -2,6 +2,7 @@ require_relative "../ext" require_relative "../../settings" +require_relative "../../../utils/configuration" module Datadog module CI @@ -19,7 +20,9 @@ class Settings < Datadog::CI::Contrib::Settings option :service_name do |o| o.type :string - o.default { Datadog.configuration.service_without_fallback || Ext::DEFAULT_SERVICE_NAME } + o.default do + Utils::Configuration.fetch_service_name(Ext::DEFAULT_SERVICE_NAME) + end end # @deprecated Will be removed in 1.0 diff --git a/lib/datadog/ci/contrib/rspec/configuration/settings.rb b/lib/datadog/ci/contrib/rspec/configuration/settings.rb index a4231343..6195c2be 100644 --- a/lib/datadog/ci/contrib/rspec/configuration/settings.rb +++ b/lib/datadog/ci/contrib/rspec/configuration/settings.rb @@ -2,6 +2,7 @@ require_relative "../ext" require_relative "../../settings" +require_relative "../../../utils/configuration" module Datadog module CI @@ -19,7 +20,9 @@ class Settings < Datadog::CI::Contrib::Settings option :service_name do |o| o.type :string - o.default { Datadog.configuration.service_without_fallback || Ext::DEFAULT_SERVICE_NAME } + o.default do + Utils::Configuration.fetch_service_name(Ext::DEFAULT_SERVICE_NAME) + end end # @deprecated Will be removed in 1.0 diff --git a/lib/datadog/ci/utils/configuration.rb b/lib/datadog/ci/utils/configuration.rb new file mode 100644 index 00000000..36450320 --- /dev/null +++ b/lib/datadog/ci/utils/configuration.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require_relative "git" + +module Datadog + module CI + module Utils + module Configuration + def self.fetch_service_name(default) + Datadog.configuration.service_without_fallback || Git.repository_name || default + end + end + end + end +end diff --git a/lib/datadog/ci/utils/git.rb b/lib/datadog/ci/utils/git.rb index 88906728..4d23e1d3 100644 --- a/lib/datadog/ci/utils/git.rb +++ b/lib/datadog/ci/utils/git.rb @@ -43,6 +43,31 @@ def self.relative_to_root(path) path.relative_path_from(git_root).to_s end + def self.repository_name + return @@repository_name if defined?(@@repository_name) + + git_remote_url = exec_git_command("git ls-remote --get-url origin") + + # return git repository name from remote url without .git extension + last_path_segment = git_remote_url.split("/").last if git_remote_url + @@repository_name = last_path_segment.gsub(".git", "") if last_path_segment + @@repository_name ||= current_folder_name + rescue => e + Datadog.logger.debug( + "Unable to get git remote: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}" + ) + @@repository_name = current_folder_name + end + + def self.current_folder_name + root_folder = root + if root_folder.nil? + File.basename(Dir.pwd) + else + File.basename(root_folder) + end + end + def self.exec_git_command(cmd) out, status = Open3.capture2e(cmd) diff --git a/sig/datadog/ci/utils/configuration.rbs b/sig/datadog/ci/utils/configuration.rbs new file mode 100644 index 00000000..16977dac --- /dev/null +++ b/sig/datadog/ci/utils/configuration.rbs @@ -0,0 +1,9 @@ +module Datadog + module CI + module Utils + module Configuration + def self.fetch_service_name: (String default) -> String + end + end + end +end diff --git a/sig/datadog/ci/utils/git.rbs b/sig/datadog/ci/utils/git.rbs index b413f3bc..04e96f78 100644 --- a/sig/datadog/ci/utils/git.rbs +++ b/sig/datadog/ci/utils/git.rbs @@ -12,6 +12,10 @@ module Datadog def self.root: -> String? + def self.repository_name: -> String + + def self.current_folder_name: -> String + def self.relative_to_root: (String? path) -> String? end end diff --git a/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb b/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb index 9d866533..623d0edf 100644 --- a/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb +++ b/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb @@ -10,660 +10,679 @@ module Kernel end RSpec.describe "Minitest instrumentation" do - include_context "CI mode activated" do - let(:integration_name) { :minitest } - let(:integration_options) { {service_name: "ltest"} } - end - - before do - # required to call .runnable_methods - Minitest.seed = 1 - end - - it "creates span for test" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" - end - - def test_foo - end - end - - klass.new(:test_foo).run - - expect(span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST) - expect(span.name).to eq("SomeTest#test_foo") - expect(span.resource).to eq("SomeTest#test_foo") - expect(span.service).to eq("ltest") - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_NAME)).to eq("SomeTest#test_foo") - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE)).to eq( - "SomeTest at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" - ) - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST) - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq(Datadog::CI::Ext::Test::TEST_TYPE) - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq(Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK) - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( - Datadog::CI::Contrib::Minitest::Integration.version.to_s - ) - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::PASS) - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SOURCE_FILE)).to eq( - "spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" - ) - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SOURCE_START)).to eq("29") - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_CODEOWNERS)).to eq( - "[\"@DataDog/ruby-guild\", \"@DataDog/ci-app-libraries\"]" - ) - end - - it "creates spans for several tests" do - expect(Datadog::CI::Ext::Environment).to receive(:tags).never - - num_tests = 20 - - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" - end - - num_tests.times do |i| - define_method(:"test_#{i}") {} - end - end - - num_tests.times do |i| - klass.new("test_#{i}").run + context "without service name configured" do + include_context "CI mode activated" do + let(:integration_name) { :minitest } end - expect(spans).to have(num_tests).items - end - - it "creates span for spec" do - klass = Class.new(Minitest::Spec) do - def self.name - "SomeSpec" - end - - it "does not fail" do - end - end - - method_name = klass.runnable_methods.first - klass.new(method_name).run - - expect(span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST) - expect(span.resource).to eq("SomeSpec##{method_name}") - expect(span.service).to eq("ltest") - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_NAME)).to eq("SomeSpec##{method_name}") - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE)).to eq( - "SomeSpec at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" - ) - end - - it "creates spans for several specs" do - num_specs = 20 - - klass = Class.new(Minitest::Spec) do - def self.name - "SomeSpec" - end - - num_specs.times do |i| - it "does not fail #{i}" do + it "uses repo name as default service name" do + klass = Class.new(Minitest::Test) do + def test_foo end end - end - - klass.runnable_methods.each do |method_name| - klass.new(method_name).run - end - expect(spans).to have(num_specs).items - end - - it "creates spans for example with instrumentation" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" - end + klass.new(:test_foo).run - def test_foo - Datadog::Tracing.trace("get_time") do - Time.now - end - end + expect(span.service).to eq("datadog-ci-rb") end + end - klass.new(:test_foo).run - - expect(spans).to have(2).items - - spans.each do |span| - expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Distributed::TAG_ORIGIN)) - .to eq(Datadog::CI::Ext::Test::CONTEXT_ORIGIN) + context "with service name configured" do + include_context "CI mode activated" do + let(:integration_name) { :minitest } + let(:integration_options) { {service_name: "ltest"} } end - end - context "catches failures" do - def expect_failure - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::FAIL) - expect(span).to have_error - expect(span).to have_error_type - expect(span).to have_error_message - expect(span).to have_error_stack + before do + # required to call .runnable_methods + Minitest.seed = 1 end - it "within test" do + it "creates span for test" do klass = Class.new(Minitest::Test) do def self.name "SomeTest" end def test_foo - assert false end end klass.new(:test_foo).run - expect_failure + expect(span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST) + expect(span.name).to eq("SomeTest#test_foo") + expect(span.resource).to eq("SomeTest#test_foo") + expect(span.service).to eq("ltest") + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_NAME)).to eq("SomeTest#test_foo") + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE)).to eq( + "SomeTest at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" + ) + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST) + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq(Datadog::CI::Ext::Test::TEST_TYPE) + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq(Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK) + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( + Datadog::CI::Contrib::Minitest::Integration.version.to_s + ) + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::PASS) + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SOURCE_FILE)).to eq( + "spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" + ) + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SOURCE_START)).to eq("47") + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_CODEOWNERS)).to eq( + "[\"@DataDog/ruby-guild\", \"@DataDog/ci-app-libraries\"]" + ) end - it "within setup" do + it "creates spans for several tests" do + expect(Datadog::CI::Ext::Environment).to receive(:tags).never + + num_tests = 20 + klass = Class.new(Minitest::Test) do def self.name "SomeTest" end - def setup - assert false - end - - def test_foo + num_tests.times do |i| + define_method(:"test_#{i}") {} end end - klass.new(:test_foo).run + num_tests.times do |i| + klass.new("test_#{i}").run + end - expect_failure + expect(spans).to have(num_tests).items end - it "within teardown" do - klass = Class.new(Minitest::Test) do + it "creates span for spec" do + klass = Class.new(Minitest::Spec) do def self.name - "SomeTest" + "SomeSpec" end - def teardown - assert false - end - - def test_foo + it "does not fail" do end end - klass.new(:test_foo).run + method_name = klass.runnable_methods.first + klass.new(method_name).run - expect_failure + expect(span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST) + expect(span.resource).to eq("SomeSpec##{method_name}") + expect(span.service).to eq("ltest") + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_NAME)).to eq("SomeSpec##{method_name}") + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE)).to eq( + "SomeSpec at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" + ) end - end - context "catches errors" do - def expect_failure - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::FAIL) - expect(span).to have_error - expect(span).to have_error_type - expect(span).to have_error_message - expect(span).to have_error_stack - end + it "creates spans for several specs" do + num_specs = 20 - it "within test" do - klass = Class.new(Minitest::Test) do + klass = Class.new(Minitest::Spec) do def self.name - "SomeTest" + "SomeSpec" end - def test_foo - raise "Error!" + num_specs.times do |i| + it "does not fail #{i}" do + end end end - klass.new(:test_foo).run + klass.runnable_methods.each do |method_name| + klass.new(method_name).run + end - expect_failure + expect(spans).to have(num_specs).items end - it "within setup" do + it "creates spans for example with instrumentation" do klass = Class.new(Minitest::Test) do def self.name "SomeTest" end - def setup - raise "Error!" - end - def test_foo + Datadog::Tracing.trace("get_time") do + Time.now + end end end klass.new(:test_foo).run - expect_failure + expect(spans).to have(2).items + + spans.each do |span| + expect(span.get_tag(Datadog::Tracing::Metadata::Ext::Distributed::TAG_ORIGIN)) + .to eq(Datadog::CI::Ext::Test::CONTEXT_ORIGIN) + end end - it "within teardown" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" - end + context "catches failures" do + def expect_failure + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::FAIL) + expect(span).to have_error + expect(span).to have_error_type + expect(span).to have_error_message + expect(span).to have_error_stack + end - def teardown - raise "Error!" - end + it "within test" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - def test_foo + def test_foo + assert false + end end - end - klass.new(:test_foo).run + klass.new(:test_foo).run - expect_failure - end - end + expect_failure + end - context "catches skips" do - def expect_skip - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::SKIP) - expect(span).to_not have_error - end + it "within setup" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - it "with reason" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" - end + def setup + assert false + end - def test_foo - skip "Skip!" + def test_foo + end end + + klass.new(:test_foo).run + + expect_failure end - klass.new(:test_foo).run + it "within teardown" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - expect_skip - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SKIP_REASON)).to eq("Skip!") - end + def teardown + assert false + end - it "without reason" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" + def test_foo + end end - def test_foo - skip - end + klass.new(:test_foo).run + + expect_failure end + end - klass.new(:test_foo).run + context "catches errors" do + def expect_failure + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::FAIL) + expect(span).to have_error + expect(span).to have_error_type + expect(span).to have_error_message + expect(span).to have_error_stack + end - expect_skip - expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SKIP_REASON)).to eq("Skipped, no message given") - end + it "within test" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - it "within test" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" + def test_foo + raise "Error!" + end end - def test_foo - skip "Skip!" - end + klass.new(:test_foo).run + + expect_failure end - klass.new(:test_foo).run + it "within setup" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - expect_skip - end + def setup + raise "Error!" + end - it "within setup" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" + def test_foo + end end - def setup - skip "Skip!" - end + klass.new(:test_foo).run - def test_foo - end + expect_failure end - klass.new(:test_foo).run + it "within teardown" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - expect_skip - end + def teardown + raise "Error!" + end - it "within teardown" do - klass = Class.new(Minitest::Test) do - def self.name - "SomeTest" + def test_foo + end end - def teardown - skip "Skip!" - end + klass.new(:test_foo).run - def test_foo - end + expect_failure end + end - klass.new(:test_foo).run + context "catches skips" do + def expect_skip + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq(Datadog::CI::Ext::Test::Status::SKIP) + expect(span).to_not have_error + end - expect_skip - end - end + it "with reason" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - context "run minitest suite" do - before do - Minitest.run([]) - end + def test_foo + skip "Skip!" + end + end + + klass.new(:test_foo).run - context "single test passed" do - before(:context) do - Minitest::Runnable.reset + expect_skip + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SKIP_REASON)).to eq("Skip!") + end - class SomeTest < Minitest::Test - def test_pass - assert true + it "without reason" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" end - def test_pass_other - assert true + def test_foo + skip end end - end - it "creates a test session span" do - expect(test_session_span).not_to be_nil - expect(test_session_span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST_SESSION) - expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq( - Datadog::CI::Ext::AppTypes::TYPE_TEST - ) - expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq( - Datadog::CI::Ext::Test::TEST_TYPE - ) - expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( - Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK - ) - expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( - Datadog::CI::Contrib::Minitest::Integration.version.to_s - ) - expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::PASS - ) - end + klass.new(:test_foo).run - it "creates a test module span" do - expect(test_module_span).not_to be_nil - - expect(test_module_span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST_MODULE) - expect(test_module_span.name).to eq(test_command) - - expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq( - Datadog::CI::Ext::AppTypes::TYPE_TEST - ) - expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq( - Datadog::CI::Ext::Test::TEST_TYPE - ) - expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( - Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK - ) - expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( - Datadog::CI::Contrib::Minitest::Integration.version.to_s - ) - expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::PASS - ) + expect_skip + expect(span.get_tag(Datadog::CI::Ext::Test::TAG_SKIP_REASON)).to eq("Skipped, no message given") end - it "creates a test suite span" do - expect(test_suite_span).not_to be_nil - - expect(test_suite_span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST_SUITE) - expect(test_suite_span.name).to eq("SomeTest at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb") - - expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq( - Datadog::CI::Ext::AppTypes::TYPE_TEST - ) - expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq( - Datadog::CI::Ext::Test::TEST_TYPE - ) - expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( - Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK - ) - expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( - Datadog::CI::Contrib::Minitest::Integration.version.to_s - ) - expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::PASS - ) - end + it "within test" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end + + def test_foo + skip "Skip!" + end + end - it "creates test spans and connects them to the session, module, and suite" do - expect(test_spans.count).to eq(2) - - expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE)).to eq( - "SomeTest at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" - ) - expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( - Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK - ) - expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( - Datadog::CI::Contrib::Minitest::Integration.version.to_s - ) - expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::PASS - ) - - test_session_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SESSION_ID) }.uniq - test_module_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_MODULE_ID) }.uniq - test_suite_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SUITE_ID) }.uniq - - expect(test_session_ids.count).to eq(1) - expect(test_session_ids.first).to eq(test_session_span.id.to_s) - - expect(test_module_ids.count).to eq(1) - expect(test_module_ids.first).to eq(test_module_span.id.to_s) - - expect(test_suite_ids.count).to eq(1) - expect(test_suite_ids.first).to eq(test_suite_span.id.to_s) + klass.new(:test_foo).run + + expect_skip end - end - context "single test failed" do - before(:context) do - Minitest::Runnable.reset + it "within setup" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end - class SomeFailedTest < Minitest::Test - def test_fail - assert false + def setup + skip "Skip!" + end + + def test_foo end end + + klass.new(:test_foo).run + + expect_skip end - it "traces test, test session, test module with failed status" do - expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_NAME)).to eq("SomeFailedTest#test_fail") - expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::FAIL - ) - - expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::FAIL - ) - expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::FAIL - ) - expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( - Datadog::CI::Ext::Test::Status::FAIL - ) + it "within teardown" do + klass = Class.new(Minitest::Test) do + def self.name + "SomeTest" + end + + def teardown + skip "Skip!" + end + + def test_foo + end + end + + klass.new(:test_foo).run + + expect_skip end end - context "using Minitest::Spec" do - before(:context) do - Minitest::Runnable.reset + context "run minitest suite" do + before do + Minitest.run([]) + end + + context "single test passed" do + before(:context) do + Minitest::Runnable.reset - class SomeSpec < Minitest::Spec - it "does not fail" do + class SomeTest < Minitest::Test + def test_pass + assert true + end + + def test_pass_other + assert true + end end + end - minitest_describe "in context" do + it "creates a test session span" do + expect(test_session_span).not_to be_nil + expect(test_session_span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST_SESSION) + expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq( + Datadog::CI::Ext::AppTypes::TYPE_TEST + ) + expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq( + Datadog::CI::Ext::Test::TEST_TYPE + ) + expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( + Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK + ) + expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( + Datadog::CI::Contrib::Minitest::Integration.version.to_s + ) + expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::PASS + ) + end + + it "creates a test module span" do + expect(test_module_span).not_to be_nil + + expect(test_module_span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST_MODULE) + expect(test_module_span.name).to eq(test_command) + + expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq( + Datadog::CI::Ext::AppTypes::TYPE_TEST + ) + expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq( + Datadog::CI::Ext::Test::TEST_TYPE + ) + expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( + Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK + ) + expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( + Datadog::CI::Contrib::Minitest::Integration.version.to_s + ) + expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::PASS + ) + end + + it "creates a test suite span" do + expect(test_suite_span).not_to be_nil + + expect(test_suite_span.type).to eq(Datadog::CI::Ext::AppTypes::TYPE_TEST_SUITE) + expect(test_suite_span.name).to eq("SomeTest at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb") + + expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_SPAN_KIND)).to eq( + Datadog::CI::Ext::AppTypes::TYPE_TEST + ) + expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_TYPE)).to eq( + Datadog::CI::Ext::Test::TEST_TYPE + ) + expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( + Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK + ) + expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( + Datadog::CI::Contrib::Minitest::Integration.version.to_s + ) + expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::PASS + ) + end + + it "creates test spans and connects them to the session, module, and suite" do + expect(test_spans.count).to eq(2) + + expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE)).to eq( + "SomeTest at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" + ) + expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK)).to eq( + Datadog::CI::Contrib::Minitest::Ext::FRAMEWORK + ) + expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_FRAMEWORK_VERSION)).to eq( + Datadog::CI::Contrib::Minitest::Integration.version.to_s + ) + expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::PASS + ) + + test_session_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SESSION_ID) }.uniq + test_module_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_MODULE_ID) }.uniq + test_suite_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SUITE_ID) }.uniq + + expect(test_session_ids.count).to eq(1) + expect(test_session_ids.first).to eq(test_session_span.id.to_s) + + expect(test_module_ids.count).to eq(1) + expect(test_module_ids.first).to eq(test_module_span.id.to_s) + + expect(test_suite_ids.count).to eq(1) + expect(test_suite_ids.first).to eq(test_suite_span.id.to_s) + end + end + + context "single test failed" do + before(:context) do + Minitest::Runnable.reset + + class SomeFailedTest < Minitest::Test + def test_fail + assert false + end + end + end + + it "traces test, test session, test module with failed status" do + expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_NAME)).to eq("SomeFailedTest#test_fail") + expect(first_test_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::FAIL + ) + + expect(test_session_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::FAIL + ) + expect(test_module_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::FAIL + ) + expect(test_suite_span.get_tag(Datadog::CI::Ext::Test::TAG_STATUS)).to eq( + Datadog::CI::Ext::Test::Status::FAIL + ) + end + end + + context "using Minitest::Spec" do + before(:context) do + Minitest::Runnable.reset + + class SomeSpec < Minitest::Spec it "does not fail" do end - minitest_describe "deeper context" do + minitest_describe "in context" do it "does not fail" do end + + minitest_describe "deeper context" do + it "does not fail" do + end + end end - end - minitest_describe "in other context" do - it "does not fail" do + minitest_describe "in other context" do + it "does not fail" do + end end end end - end - it "traces tests with unique names" do - test_names = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_NAME) }.sort - - expect(test_names).to eq( - [ - "SomeSpec#test_0001_does not fail", - "in context#test_0001_does not fail", - "in context::deeper context#test_0001_does not fail", - "in other context#test_0001_does not fail" - ] - ) - end + it "traces tests with unique names" do + test_names = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_NAME) }.sort - it "connects tests to different test suites (one per spec context)" do - test_suite_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SUITE_ID) }.uniq - test_suite_names = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE) }.sort - - expect(test_suite_ids).to have(4).items - expect(test_suite_names).to eq( - [ - "SomeSpec at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb", - "in context at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb", - "in context::deeper context at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb", - "in other context at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" - ] - ) - end + expect(test_names).to eq( + [ + "SomeSpec#test_0001_does not fail", + "in context#test_0001_does not fail", + "in context::deeper context#test_0001_does not fail", + "in other context#test_0001_does not fail" + ] + ) + end - it "connects tests to a single test session" do - test_session_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SESSION_ID) }.uniq + it "connects tests to different test suites (one per spec context)" do + test_suite_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SUITE_ID) }.uniq + test_suite_names = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_SUITE) }.sort - expect(test_session_ids.count).to eq(1) - expect(test_session_ids.first).to eq(test_session_span.id.to_s) - end - end + expect(test_suite_ids).to have(4).items + expect(test_suite_names).to eq( + [ + "SomeSpec at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb", + "in context at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb", + "in context::deeper context at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb", + "in other context at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb" + ] + ) + end - context "using parallel executor" do - before(:context) do - Minitest::Runnable.reset + it "connects tests to a single test session" do + test_session_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SESSION_ID) }.uniq - class ParallelTest < Minitest::Test - parallelize_me! + expect(test_session_ids.count).to eq(1) + expect(test_session_ids.first).to eq(test_session_span.id.to_s) end + end - class TestA < ParallelTest - def test_a_1 - Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) - sleep 0.1 - end + context "using parallel executor" do + before(:context) do + Minitest::Runnable.reset - def test_a_2 - Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) - sleep 0.1 + class ParallelTest < Minitest::Test + parallelize_me! end - end - class TestB < ParallelTest - def test_b_1 - Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) - sleep 0.1 + class TestA < ParallelTest + def test_a_1 + Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) + sleep 0.1 + end + + def test_a_2 + Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) + sleep 0.1 + end end - def test_b_2 - Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) - sleep 0.1 + class TestB < ParallelTest + def test_b_1 + Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) + sleep 0.1 + end + + def test_b_2 + Datadog::CI.active_test.set_tag("minitest_thread", Thread.current.object_id) + sleep 0.1 + end end end - end - it "traces all tests correctly, assigning a separate test suite to each of them" do - test_threads = test_spans.map { |span| span.get_tag("minitest_thread") }.uniq - - # make sure that tests were executed concurrently - # note that this test could be flaky - expect(test_threads.count).to be > 1 - - test_names = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_NAME) }.sort - expect(test_names).to eq( - [ - "TestA#test_a_1", - "TestA#test_a_2", - "TestB#test_b_1", - "TestB#test_b_2" - ] - ) - - test_suite_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SUITE_ID) }.uniq - expect(test_suite_ids).to have(4).items - end + it "traces all tests correctly, assigning a separate test suite to each of them" do + test_threads = test_spans.map { |span| span.get_tag("minitest_thread") }.uniq - it "connects tests to a single test session and a single test module" do - test_session_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SESSION_ID) }.uniq - test_module_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_MODULE_ID) }.uniq + # make sure that tests were executed concurrently + # note that this test could be flaky + expect(test_threads.count).to be > 1 - expect(test_session_ids.count).to eq(1) - expect(test_session_ids.first).to eq(test_session_span.id.to_s) + test_names = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_NAME) }.sort + expect(test_names).to eq( + [ + "TestA#test_a_1", + "TestA#test_a_2", + "TestB#test_b_1", + "TestB#test_b_2" + ] + ) - expect(test_module_ids.count).to eq(1) - expect(test_module_ids.first).to eq(test_module_span.id.to_s) - end + test_suite_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SUITE_ID) }.uniq + expect(test_suite_ids).to have(4).items + end - it "correctly tracks test and session durations" do - test_session_duration = test_session_span.duration + it "connects tests to a single test session and a single test module" do + test_session_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_SESSION_ID) }.uniq + test_module_ids = test_spans.map { |span| span.get_tag(Datadog::CI::Ext::Test::TAG_TEST_MODULE_ID) }.uniq - test_durations_sum = test_spans.map { |span| span.duration }.sum - # with parallel execution test durations sum should be greater than test session duration - expect(test_durations_sum).to be > test_session_duration + expect(test_session_ids.count).to eq(1) + expect(test_session_ids.first).to eq(test_session_span.id.to_s) - # each individual test duration should be less than test session duration - test_spans.each do |span| - expect(span.duration).to be < test_session_duration + expect(test_module_ids.count).to eq(1) + expect(test_module_ids.first).to eq(test_module_span.id.to_s) end - end - it "creates test suite spans" do - expect(test_suite_spans).to have(4).items - - test_suite_names = test_suite_spans.map { |span| span.name }.sort - expect(test_suite_names).to eq( - [ - "TestA at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_a_1 concurrently)", - "TestA at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_a_2 concurrently)", - "TestB at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_b_1 concurrently)", - "TestB at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_b_2 concurrently)" - ] - ) + it "correctly tracks test and session durations" do + test_session_duration = test_session_span.duration + + test_durations_sum = test_spans.map { |span| span.duration }.sum + # with parallel execution test durations sum should be greater than test session duration + expect(test_durations_sum).to be > test_session_duration + + # each individual test duration should be less than test session duration + test_spans.each do |span| + expect(span.duration).to be < test_session_duration + end + end + + it "creates test suite spans" do + expect(test_suite_spans).to have(4).items + + test_suite_names = test_suite_spans.map { |span| span.name }.sort + expect(test_suite_names).to eq( + [ + "TestA at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_a_1 concurrently)", + "TestA at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_a_2 concurrently)", + "TestB at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_b_1 concurrently)", + "TestB at spec/datadog/ci/contrib/minitest/instrumentation_spec.rb (test_b_2 concurrently)" + ] + ) + end end end end diff --git a/spec/datadog/ci/utils/configuration_spec.rb b/spec/datadog/ci/utils/configuration_spec.rb new file mode 100644 index 00000000..3a3dd358 --- /dev/null +++ b/spec/datadog/ci/utils/configuration_spec.rb @@ -0,0 +1,37 @@ +RSpec.describe ::Datadog::CI::Utils::Configuration do + describe ".fetch_service_name" do + subject { described_class.fetch_service_name(default) } + + let(:default) { "default" } + + before do + allow(::Datadog.configuration).to receive(:service_without_fallback).and_return(service) + end + + context "when service is set in Datadog config" do + let(:service) { "service_without_fallback" } + + it { is_expected.to eq(service) } + end + + context "when service is not set" do + let(:service) { nil } + + before do + expect(::Datadog::CI::Utils::Git).to receive(:repository_name).and_return(repository_name) + end + + context "when repository_name can be fetched" do + let(:repository_name) { "repository_name" } + + it { is_expected.to eq(repository_name) } + end + + context "when repository_name can not be fetched" do + let(:repository_name) { nil } + + it { is_expected.to eq(default) } + end + end + end +end diff --git a/spec/datadog/ci/utils/git_spec.rb b/spec/datadog/ci/utils/git_spec.rb index 770f69d0..dfa185d7 100644 --- a/spec/datadog/ci/utils/git_spec.rb +++ b/spec/datadog/ci/utils/git_spec.rb @@ -105,4 +105,44 @@ end end end + + describe ".current_folder_name" do + subject { described_class.current_folder_name } + let(:path) { "/foo/bar" } + + context "when git root is nil" do + before do + allow(described_class).to receive(:root).and_return(nil) + allow(Dir).to receive(:pwd).and_return(path) + end + + it { is_expected.to eq("bar") } + end + + context "when git root is not nil" do + before do + allow(described_class).to receive(:root).and_return(path) + end + + it { is_expected.to eq("bar") } + end + end + + describe ".repository_name" do + subject { described_class.repository_name } + + it { is_expected.to eq("datadog-ci-rb") } + + context "caches the result" do + before do + expect(Open3).to receive(:capture2e).never + end + + it "returns the same result" do + 2.times do + expect(described_class.root).to eq(Dir.pwd) + end + end + end + end end