Skip to content

Commit c898a26

Browse files
committed
refactor Datadog::CI::Contrib::Integration module and integrations registry
1 parent 4905314 commit c898a26

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+313
-357
lines changed

lib/datadog/ci.rb

+8-1
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,16 @@ def test_optimisation
406406
end
407407

408408
# Integrations
409+
410+
# Test frameworks (manual instrumentation)
409411
require_relative "ci/contrib/cucumber/integration"
410-
require_relative "ci/contrib/rspec/integration"
411412
require_relative "ci/contrib/minitest/integration"
413+
require_relative "ci/contrib/rspec/integration"
414+
415+
# Test runners (instrumented automatically when corresponding frameworks are instrumented)
416+
require_relative "ci/contrib/knapsack/integration"
417+
418+
# Additional test libraries (auto instrumented later on test session start)
412419
require_relative "ci/contrib/selenium/integration"
413420
require_relative "ci/contrib/simplecov/integration"
414421

lib/datadog/ci/contrib/cucumber/formatter.rb

+10-5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require_relative "../../ext/test"
44
require_relative "../../git/local_repository"
55
require_relative "../../utils/test_run"
6+
require_relative "../instrumentation"
67
require_relative "ext"
78

89
module Datadog
@@ -38,9 +39,9 @@ def on_test_run_started(event)
3839
test_visibility_component.start_test_session(
3940
tags: {
4041
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
41-
CI::Ext::Test::TAG_FRAMEWORK_VERSION => CI::Contrib::Cucumber::Integration.version.to_s
42+
CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s
4243
},
43-
service: configuration[:service_name]
44+
service: datadog_configuration[:service_name]
4445
)
4546
test_visibility_component.start_test_module(Ext::FRAMEWORK)
4647
end
@@ -61,7 +62,7 @@ def on_test_case_started(event)
6162
# @type var tags: Hash[String, String]
6263
tags = {
6364
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
64-
CI::Ext::Test::TAG_FRAMEWORK_VERSION => CI::Contrib::Cucumber::Integration.version.to_s,
65+
CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s,
6566
CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(event.test_case.location.file),
6667
CI::Ext::Test::TAG_SOURCE_START => event.test_case.location.line.to_s
6768
}
@@ -81,7 +82,7 @@ def on_test_case_started(event)
8182
event.test_case.name,
8283
test_suite_name,
8384
tags: tags,
84-
service: configuration[:service_name]
85+
service: datadog_configuration[:service_name]
8586
)
8687
if event.test_case.match_tags?("@#{CI::Ext::Test::ITR_UNSKIPPABLE_OPTION}")
8788
test_span&.itr_unskippable!
@@ -199,7 +200,11 @@ def ok?(result, strict)
199200
end
200201
end
201202

202-
def configuration
203+
def datadog_integration
204+
CI::Contrib::Instrumentation.fetch_integration(:cucumber)
205+
end
206+
207+
def datadog_configuration
203208
Datadog.configuration.ci[:cucumber]
204209
end
205210

lib/datadog/ci/contrib/cucumber/integration.rb

+4-8
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,18 @@ module CI
99
module Contrib
1010
module Cucumber
1111
# Description of Cucumber integration
12-
class Integration
13-
include Datadog::CI::Contrib::Integration
14-
12+
class Integration < Contrib::Integration
1513
MINIMUM_VERSION = Gem::Version.new("3.0.0")
1614

17-
register_as :cucumber
18-
19-
def self.version
15+
def version
2016
Gem.loaded_specs["cucumber"]&.version
2117
end
2218

23-
def self.loaded?
19+
def loaded?
2420
!defined?(::Cucumber).nil? && !defined?(::Cucumber::Runtime).nil?
2521
end
2622

27-
def self.compatible?
23+
def compatible?
2824
super && version >= MINIMUM_VERSION
2925
end
3026

lib/datadog/ci/contrib/cucumber/patcher.rb

-4
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ module Patcher
1414

1515
module_function
1616

17-
def target_version
18-
Integration.version
19-
end
20-
2117
def patch
2218
::Cucumber::Runtime.include(Instrumentation)
2319
end

lib/datadog/ci/contrib/integration.rb

+100-113
Original file line numberDiff line numberDiff line change
@@ -5,143 +5,130 @@
55
module Datadog
66
module CI
77
module Contrib
8-
module Integration
8+
class Integration
99
@registry = {}
1010

11-
def self.included(base)
12-
base.extend(ClassMethods)
13-
base.include(InstanceMethods)
11+
def self.inherited(subclass)
12+
@registry[integration_name(subclass)] = subclass.new
1413
end
1514

16-
def self.register(klass, name)
17-
registry[name] = klass.new
15+
# take the parent module name and downcase it
16+
# for example for Datadog::CI::Contrib::RSpec::Integration it will be :rspec
17+
def self.integration_name(subclass)
18+
result = subclass.name&.split("::")&.[](-2)&.downcase&.to_sym
19+
raise "Integration name could not be derived for #{subclass}" if result.nil?
20+
result
1821
end
1922

2023
def self.registry
2124
@registry
2225
end
2326

24-
# Class-level methods for Integration
25-
module ClassMethods
26-
def register_as(name)
27-
Integration.register(self, name)
28-
end
29-
30-
# Version of the integration target code in the environment.
31-
#
32-
# This is the gem version, when the instrumentation target is a Ruby gem.
33-
#
34-
# If the target for instrumentation has concept of versioning, override {.version},
35-
# otherwise override {.available?} and implement a custom target presence check.
36-
# @return [Object] the target version
37-
def version
38-
nil
39-
end
27+
# Version of the integration target code in the environment.
28+
#
29+
# This is the gem version, when the instrumentation target is a Ruby gem.
30+
#
31+
# If the target for instrumentation has concept of versioning, override {.version},
32+
# otherwise override {.available?} and implement a custom target presence check.
33+
# @return [Object] the target version
34+
def version
35+
nil
36+
end
4037

41-
# Is the target available to be instrumented? (e.g. gem installed?)
42-
#
43-
# The target doesn't have to be loaded (e.g. `require`) yet, but needs to be able
44-
# to be loaded before instrumentation can commence.
45-
#
46-
# By default, {.available?} checks if {.version} returned a non-nil object.
47-
#
48-
# If the target for instrumentation has concept of versioning, override {.version},
49-
# otherwise override {.available?} and implement a custom target presence check.
50-
# @return [Boolean] is the target available for instrumentation in this Ruby environment?
51-
def available?
52-
!version.nil?
53-
end
38+
# Is the target available to be instrumented? (e.g. gem installed?)
39+
#
40+
# The target doesn't have to be loaded (e.g. `require`) yet, but needs to be able
41+
# to be loaded before instrumentation can commence.
42+
#
43+
# By default, {.available?} checks if {.version} returned a non-nil object.
44+
#
45+
# If the target for instrumentation has concept of versioning, override {.version},
46+
# otherwise override {.available?} and implement a custom target presence check.
47+
# @return [Boolean] is the target available for instrumentation in this Ruby environment?
48+
def available?
49+
!version.nil?
50+
end
5451

55-
# Is the target loaded into the application? (e.g. gem required? Constant defined?)
56-
#
57-
# The target's objects should be ready to be referenced by the instrumented when {.loaded}
58-
# returns `true`.
59-
#
60-
# @return [Boolean] is the target ready to be referenced during instrumentation?
61-
def loaded?
62-
true
63-
end
52+
# Is the target loaded into the application? (e.g. gem required? Constant defined?)
53+
#
54+
# The target's objects should be ready to be referenced by the instrumented when {.loaded}
55+
# returns `true`.
56+
#
57+
# @return [Boolean] is the target ready to be referenced during instrumentation?
58+
def loaded?
59+
true
60+
end
6461

65-
# Is this instrumentation compatible with the available target? (e.g. minimum version met?)
66-
# @return [Boolean] is the available target compatible with this instrumentation?
67-
def compatible?
68-
available?
69-
end
62+
# Is this instrumentation compatible with the available target? (e.g. minimum version met?)
63+
# @return [Boolean] is the available target compatible with this instrumentation?
64+
def compatible?
65+
available?
66+
end
7067

71-
# Can the patch for this integration be applied?
72-
#
73-
# By default, this is equivalent to {#available?}, {#loaded?}, and {#compatible?}
74-
# all being truthy.
75-
def patchable?
76-
available? && loaded? && compatible?
77-
end
68+
# Can the patch for this integration be applied?
69+
#
70+
# By default, this is equivalent to {#available?}, {#loaded?}, and {#compatible?}
71+
# all being truthy.
72+
def patchable?
73+
available? && loaded? && compatible?
7874
end
7975

80-
module InstanceMethods
81-
# returns the configuration instance.
82-
def configuration
83-
@configuration ||= new_configuration
84-
end
76+
# returns the configuration instance.
77+
def configuration
78+
@configuration ||= new_configuration
79+
end
8580

86-
def configure(options = {}, &block)
87-
configuration.configure(options, &block)
88-
configuration
89-
end
81+
def configure(options = {}, &block)
82+
configuration.configure(options, &block)
83+
configuration
84+
end
9085

91-
# Resets all configuration options
92-
def reset_configuration!
93-
@configuration = nil
94-
end
86+
def enabled
87+
configuration.enabled
88+
end
9589

96-
def enabled
97-
configuration.enabled
98-
end
90+
# The patcher module to inject instrumented objects into the instrumentation target.
91+
#
92+
# {Contrib::Patcher} includes the basic functionality of a patcher. `include`ing
93+
# {Contrib::Patcher} into a new module is the recommend way to create a custom patcher.
94+
#
95+
# @return [Contrib::Patcher] a module that `include`s {Contrib::Patcher}
96+
def patcher
97+
nil
98+
end
9999

100-
# The patcher module to inject instrumented objects into the instrumentation target.
101-
#
102-
# {Contrib::Patcher} includes the basic functionality of a patcher. `include`ing
103-
# {Contrib::Patcher} into a new module is the recommend way to create a custom patcher.
104-
#
105-
# @return [Contrib::Patcher] a module that `include`s {Contrib::Patcher}
106-
def patcher
107-
nil
100+
# @!visibility private
101+
def patch
102+
# @type var patcher_klass: untyped
103+
patcher_klass = patcher
104+
if !patchable? || patcher_klass.nil?
105+
return {
106+
available: available?,
107+
loaded: loaded?,
108+
compatible: compatible?,
109+
patchable: patchable?
110+
}
108111
end
109112

110-
# @!visibility private
111-
def patch
112-
# @type var patcher_klass: untyped
113-
patcher_klass = patcher
114-
if !self.class.patchable? || patcher_klass.nil?
115-
return {
116-
available: self.class.available?,
117-
loaded: self.class.loaded?,
118-
compatible: self.class.compatible?,
119-
patchable: self.class.patchable?
120-
}
121-
end
122-
123-
patcher_klass.patch
124-
true
125-
end
113+
patcher_klass.patch
114+
true
115+
end
126116

127-
# Can the patch for this integration be applied automatically?
128-
# @return [Boolean] can the tracer activate this instrumentation without explicit user input?
129-
def auto_instrument?
130-
true
131-
end
117+
# Can the patch for this integration be applied automatically?
118+
# @return [Boolean] can the tracer activate this instrumentation without explicit user input?
119+
def auto_instrument?
120+
true
121+
end
132122

133-
protected
134-
135-
# Returns a new configuration object for this integration.
136-
#
137-
# This method normally needs to be overridden for each integration
138-
# as their settings, defaults and environment variables are
139-
# specific for each integration.
140-
#
141-
# @return [Datadog::CI::Contrib::Settings] a new, integration-specific settings object
142-
def new_configuration
143-
Datadog::CI::Contrib::Settings.new
144-
end
123+
# Returns a new configuration object for this integration.
124+
#
125+
# This method normally needs to be overridden for each integration
126+
# as their settings, defaults and environment variables are
127+
# specific for each integration.
128+
#
129+
# @return [Datadog::CI::Contrib::Settings] a new, integration-specific settings object
130+
def new_configuration
131+
Datadog::CI::Contrib::Settings.new
145132
end
146133
end
147134
end

lib/datadog/ci/contrib/knapsack/integration.rb

+4-15
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,19 @@ module Contrib
99
module Knapsack
1010
# Knapsack Pro test runner instrumentation
1111
# https://github.com/KnapsackPro/knapsack_pro-ruby
12-
class Integration
13-
include Datadog::CI::Contrib::Integration
14-
15-
Configuration = Struct.new(:enabled)
16-
12+
class Integration < Contrib::Integration
1713
MINIMUM_VERSION = Gem::Version.new("7.0.0")
1814

19-
register_as :knapsack
20-
21-
def self.version
15+
def version
2216
Gem.loaded_specs["knapsack_pro"]&.version
2317
end
2418

25-
def self.loaded?
19+
def loaded?
2620
!defined?(::KnapsackPro).nil? && !defined?(::KnapsackPro::Extensions::RSpecExtension).nil? &&
2721
!defined?(::KnapsackPro::Extensions::RSpecExtension::Runner).nil?
2822
end
2923

30-
def self.compatible?
24+
def compatible?
3125
super && version >= MINIMUM_VERSION
3226
end
3327

@@ -36,11 +30,6 @@ def auto_instrument?
3630
false
3731
end
3832

39-
# TODO: not every integration needs a configuration
40-
def new_configuration
41-
Integration::Configuration.new(true)
42-
end
43-
4433
def patcher
4534
Patcher
4635
end

0 commit comments

Comments
 (0)