Skip to content

Commit

Permalink
dedup skippable percentage claasses with inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
anmarchenko committed Oct 14, 2024
1 parent 233fd12 commit 80f26cb
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 152 deletions.
13 changes: 7 additions & 6 deletions lib/datadog/ci/cli/cli.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "datadog"
require "optparse"

module Datadog
Expand All @@ -17,7 +18,7 @@ def self.exec(action)
end

def self.exec_skippable_tests_percentage
require "datadog/ci/test_optimisation/skippable_percentage"
require "datadog/ci/test_optimisation/skippable_percentage/calculator"

ddcirb_options = {}
OptionParser.new do |opts|
Expand All @@ -31,11 +32,11 @@ def self.exec_skippable_tests_percentage

additional_rspec_opts = (ddcirb_options[:"rspec-opts"] || "").split

percentage_skipped = ::Datadog::CI::TestOptimisation::SkippablePercentage.new(
percentage_skipped = ::Datadog::CI::TestOptimisation::SkippablePercentage::Calculator.new(
rspec_cli_options: additional_rspec_opts,
verbose: !ddcirb_options[:verbose].nil?,
spec_path: ddcirb_options[:"spec-path"] || "spec"
).calculate
).call

if ddcirb_options[:file]
File.write(ddcirb_options[:file], percentage_skipped)
Expand All @@ -45,7 +46,7 @@ def self.exec_skippable_tests_percentage
end

def self.exec_skippable_tests_percentage_estimate
require "datadog/ci/test_optimisation/estimate_skippable_percentage"
require "datadog/ci/test_optimisation/skippable_percentage/estimator"

ddcirb_options = {}
OptionParser.new do |opts|
Expand All @@ -56,10 +57,10 @@ def self.exec_skippable_tests_percentage_estimate
opts.on("--verbose", "verbose output to stdout")
end.parse!(into: ddcirb_options)

percentage_skipped = ::Datadog::CI::TestOptimisation::EstimateSkippablePercentage.new(
percentage_skipped = ::Datadog::CI::TestOptimisation::SkippablePercentage::Estimator.new(
verbose: !ddcirb_options[:verbose].nil?,
spec_path: ddcirb_options[:"spec-path"] || "spec"
).calculate
).call

if ddcirb_options[:file]
File.write(ddcirb_options[:file], percentage_skipped)
Expand Down
57 changes: 0 additions & 57 deletions lib/datadog/ci/test_optimisation/estimate_skippable_percentage.rb

This file was deleted.

60 changes: 0 additions & 60 deletions lib/datadog/ci/test_optimisation/skippable_percentage.rb

This file was deleted.

28 changes: 28 additions & 0 deletions lib/datadog/ci/test_optimisation/skippable_percentage/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

module Datadog
module CI
module TestOptimisation
module SkippablePercentage
class Base
def initialize(verbose: false, spec_path: "spec")
@verbose = verbose
@spec_path = spec_path

log("Spec path: #{@spec_path}")
end

def call
0.0
end

private

def log(message)
Datadog.logger.info(message) if @verbose
end
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

require_relative "base"

module Datadog
module CI
module TestOptimisation
module SkippablePercentage
# This class calculates the percentage of tests that are going to be skipped in the next run
# without actually running the tests.
#
# It is useful to determine the number of parallel jobs that are required for the CI pipeline.
#
# NOTE: Only RSpec is supported at the moment.
class Calculator < Base
def initialize(rspec_cli_options: [], verbose: false, spec_path: "spec")
super(verbose: verbose, spec_path: spec_path)

@rspec_cli_options = rspec_cli_options || []
end

def call
require_rspec!
configure_datadog

exit_code = dry_run

if exit_code != 0
Datadog.logger.error("RSpec dry-run failed with exit code #{exit_code}")
end

test_optimisation = Datadog.send(:components).test_optimisation

log("Total tests count: #{test_optimisation.total_tests_count}")
log("Skipped tests count: #{test_optimisation.skipped_tests_count}")

(test_optimisation.skipped_tests_count.to_f / test_optimisation.total_tests_count).floor(2)
end

private

def require_rspec!
require "rspec/core"
rescue LoadError
Datadog.logger.error("RSpec is not installed, currently this functionality is only supported for RSpec.")
Kernel.exit(1)
end

def configure_datadog
require "datadog/ci"

Datadog.configure do |c|
c.ci.enabled = true
c.ci.itr_enabled = true
c.ci.retry_failed_tests_enabled = false
c.ci.retry_new_tests_enabled = false
c.ci.discard_traces = true
c.ci.instrument :rspec, dry_run_enabled: true
c.tracing.enabled = true
end
end

def dry_run
cli_options_array = @rspec_cli_options + ["--dry-run", @spec_path]

rspec_config_options = ::RSpec::Core::ConfigurationOptions.new(cli_options_array)
devnull = File.new("/dev/null", "w")
out = @verbose ? $stdout : devnull
err = @verbose ? $stderr : devnull

::RSpec::Core::Runner.new(rspec_config_options).run(out, err)
end
end
end
end
end
end
58 changes: 58 additions & 0 deletions lib/datadog/ci/test_optimisation/skippable_percentage/estimator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# frozen_string_literal: true

require_relative "base"

module Datadog
module CI
module TestOptimisation
module SkippablePercentage
# This class estimates the percentage of tests that are going to be skipped in the next run
# without actually running the tests. This estimate is very rough:
#
# - it counts the number of lines that start with "it" or "scenario" in the spec files, which could be inaccurate
# if you use shared examples
# - it only counts the number of tests that could be skipped, this does not mean that they will be actually skipped:
# if in this commit you replaced all the tests in your test suite with new ones, all the tests would be run (but
# this is highly unlikely)
#
# It is useful to determine the number of parallel jobs that are required for the CI pipeline.
#
# NOTE: Only RSpec is supported at the moment.
class Estimator < Base
def initialize(verbose: false, spec_path: "spec")
super
end

def call
require "datadog/ci"

Datadog.configure do |c|
c.ci.enabled = true
c.ci.itr_enabled = true
c.ci.retry_failed_tests_enabled = false
c.ci.retry_new_tests_enabled = false
c.ci.discard_traces = true
c.tracing.enabled = true
end

spec_files = Dir["#{@spec_path}/**/*_spec.rb"]
estimated_tests_count = spec_files.sum do |file|
content = File.read(file)
content.scan(/(^\s*it\s+)|(^\s*scenario\s+)/).size
end

# starting and finishing a test session is required to get the skippable tests response
Datadog::CI.start_test_session(total_tests_count: estimated_tests_count)&.finish
# TODO: law of demeter violation
skippable_tests_count = Datadog.send(:components).test_optimisation.skippable_tests.count

log("Estimated tests count: #{estimated_tests_count}")
log("Skippable tests count: #{skippable_tests_count}")

[(skippable_tests_count.to_f / estimated_tests_count).floor(2), 0.99].min
end
end
end
end
end
end
14 changes: 0 additions & 14 deletions sig/datadog/ci/test_optimisation/estimate_skippable_percentage.rbs

This file was deleted.

15 changes: 0 additions & 15 deletions sig/datadog/ci/test_optimisation/skippable_percentage.rbs

This file was deleted.

21 changes: 21 additions & 0 deletions sig/datadog/ci/test_optimisation/skippable_percentage/base.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Datadog
module CI
module TestOptimisation
module SkippablePercentage
class Base
@verbose: bool

@spec_path: String

def initialize: (?verbose: bool, ?spec_path: ::String) -> void

def call: () -> Float

private

def log: (String message) -> void
end
end
end
end
end
Loading

0 comments on commit 80f26cb

Please sign in to comment.