datadog-ci 1.33.0 → 1.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -2
- data/lib/datadog/ci/configuration/components.rb +2 -0
- data/lib/datadog/ci/configuration/settings.rb +11 -0
- data/lib/datadog/ci/contrib/cucumber/formatter.rb +51 -1
- data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +5 -0
- data/lib/datadog/ci/contrib/minitest/helpers.rb +54 -6
- data/lib/datadog/ci/contrib/minitest/runnable.rb +5 -0
- data/lib/datadog/ci/contrib/minitest/runnable_minitest_6.rb +5 -0
- data/lib/datadog/ci/contrib/minitest/test.rb +21 -27
- data/lib/datadog/ci/contrib/patcher.rb +0 -4
- data/lib/datadog/ci/contrib/rspec/anonymous_example_name.rb +515 -0
- data/lib/datadog/ci/contrib/rspec/example.rb +23 -5
- data/lib/datadog/ci/contrib/rspec/example_group.rb +11 -1
- data/lib/datadog/ci/contrib/simplecov/result_extractor.rb +9 -1
- data/lib/datadog/ci/ext/settings.rb +1 -0
- data/lib/datadog/ci/ext/telemetry.rb +1 -0
- data/lib/datadog/ci/ext/test.rb +8 -1
- data/lib/datadog/ci/remote/library_settings_client.rb +3 -2
- data/lib/datadog/ci/source_code/method_inspect.rb +0 -3
- data/lib/datadog/ci/test_discovery/component.rb +4 -0
- data/lib/datadog/ci/test_impact_analysis/component.rb +176 -59
- data/lib/datadog/ci/test_impact_analysis/coverage/event.rb +6 -4
- data/lib/datadog/ci/test_impact_analysis/null_component.rb +25 -2
- data/lib/datadog/ci/test_impact_analysis/skippable.rb +20 -5
- data/lib/datadog/ci/test_impact_analysis/skippable_percentage/estimator.rb +3 -3
- data/lib/datadog/ci/test_optimization_cache/readers/legacy.rb +1 -1
- data/lib/datadog/ci/test_suite.rb +20 -0
- data/lib/datadog/ci/test_tracing/component.rb +10 -0
- data/lib/datadog/ci/test_tracing/deprecated_total_coverage_metric.rb +11 -1
- data/lib/datadog/ci/utils/configuration.rb +12 -0
- data/lib/datadog/ci/utils/test_name.rb +112 -0
- data/lib/datadog/ci/version.rb +1 -1
- metadata +3 -1
|
@@ -16,11 +16,8 @@ module Datadog
|
|
|
16
16
|
return nil if target.nil?
|
|
17
17
|
return nil unless LAST_LINE_AVAILABLE
|
|
18
18
|
|
|
19
|
-
# Ruby has outdated RBS for RubyVM::InstructionSequence where method `of` is not defined
|
|
20
|
-
# steep:ignore:start
|
|
21
19
|
iseq = RubyVM::InstructionSequence.of(target)
|
|
22
20
|
return nil unless iseq.is_a?(RubyVM::InstructionSequence)
|
|
23
|
-
# steep:ignore:end
|
|
24
21
|
|
|
25
22
|
# this function is implemented in ext/datadog_ci_native/datadog_method_inspect.c
|
|
26
23
|
_native_last_line_from_iseq(iseq)
|
|
@@ -4,6 +4,7 @@ require "fileutils"
|
|
|
4
4
|
require "json"
|
|
5
5
|
require_relative "../ext/test"
|
|
6
6
|
require_relative "../ext/test_discovery"
|
|
7
|
+
require_relative "../utils/test_name"
|
|
7
8
|
|
|
8
9
|
module Datadog
|
|
9
10
|
module CI
|
|
@@ -76,6 +77,9 @@ module Datadog
|
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
def record_test(name:, suite:, module_name:, parameters:, source_file:)
|
|
80
|
+
name = name.nil? ? nil : Utils::TestName.normalize(name)
|
|
81
|
+
suite = suite.nil? ? nil : Utils::TestName.normalize(suite)
|
|
82
|
+
|
|
79
83
|
test_info = {
|
|
80
84
|
"name" => name,
|
|
81
85
|
"suite" => suite,
|
|
@@ -30,8 +30,8 @@ module Datadog
|
|
|
30
30
|
|
|
31
31
|
FILE_STORAGE_KEY = "test_impact_analysis_component_state"
|
|
32
32
|
|
|
33
|
-
attr_reader :correlation_id, :skippable_tests, :skippable_tests_fetch_error,
|
|
34
|
-
:enabled, :test_skipping_enabled, :code_coverage_enabled
|
|
33
|
+
attr_reader :correlation_id, :skippable_tests, :skippable_suites, :skippable_tests_fetch_error,
|
|
34
|
+
:enabled, :test_skipping_enabled, :code_coverage_enabled, :test_skipping_mode
|
|
35
35
|
|
|
36
36
|
def initialize(
|
|
37
37
|
dd_env:,
|
|
@@ -39,6 +39,7 @@ module Datadog
|
|
|
39
39
|
api: nil,
|
|
40
40
|
coverage_writer: nil,
|
|
41
41
|
enabled: false,
|
|
42
|
+
test_skipping_mode: Ext::Test::TIATestSkippingMode::TEST,
|
|
42
43
|
bundle_location: nil,
|
|
43
44
|
use_single_threaded_coverage: false,
|
|
44
45
|
use_allocation_tracing: true,
|
|
@@ -48,6 +49,7 @@ module Datadog
|
|
|
48
49
|
@api = api
|
|
49
50
|
@dd_env = dd_env
|
|
50
51
|
@config_tags = config_tags || {}
|
|
52
|
+
@test_skipping_mode = test_skipping_mode
|
|
51
53
|
|
|
52
54
|
@bundle_location = if bundle_location && !File.absolute_path?(bundle_location)
|
|
53
55
|
File.join(Git::LocalRepository.root, bundle_location)
|
|
@@ -65,6 +67,7 @@ module Datadog
|
|
|
65
67
|
|
|
66
68
|
@correlation_id = nil
|
|
67
69
|
@skippable_tests = Set.new
|
|
70
|
+
@skippable_suites = Set.new
|
|
68
71
|
|
|
69
72
|
@mutex = Mutex.new
|
|
70
73
|
|
|
@@ -92,8 +95,7 @@ module Datadog
|
|
|
92
95
|
|
|
93
96
|
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_ENABLED, @test_skipping_enabled)
|
|
94
97
|
test_session.set_tag(Ext::Test::TAG_CODE_COVERAGE_ENABLED, @code_coverage_enabled)
|
|
95
|
-
|
|
96
|
-
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_TYPE, Ext::Test::ITR_TEST_SKIPPING_MODE)
|
|
98
|
+
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_TYPE, @test_skipping_mode)
|
|
97
99
|
|
|
98
100
|
if @code_coverage_enabled
|
|
99
101
|
load_datadog_cov!
|
|
@@ -102,10 +104,10 @@ module Datadog
|
|
|
102
104
|
end
|
|
103
105
|
|
|
104
106
|
# Load external cache or component state first, and if successful, skip fetching skippable tests
|
|
105
|
-
if skipping_tests?
|
|
107
|
+
if skipping_tests? || skipping_suites?
|
|
106
108
|
return if load_component_state
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
fetch_skippables(test_session)
|
|
109
111
|
store_component_state if test_session.distributed
|
|
110
112
|
end
|
|
111
113
|
|
|
@@ -117,7 +119,19 @@ module Datadog
|
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
def skipping_tests?
|
|
120
|
-
@test_skipping_enabled
|
|
122
|
+
@test_skipping_enabled && test_skipping_mode?
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def skipping_suites?
|
|
126
|
+
@test_skipping_enabled && suite_skipping_mode?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def test_skipping_mode?
|
|
130
|
+
@test_skipping_mode == Ext::Test::TIATestSkippingMode::TEST
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def suite_skipping_mode?
|
|
134
|
+
@test_skipping_mode == Ext::Test::TIATestSkippingMode::SUITE
|
|
121
135
|
end
|
|
122
136
|
|
|
123
137
|
def code_coverage?
|
|
@@ -174,6 +188,7 @@ module Datadog
|
|
|
174
188
|
# @return [void]
|
|
175
189
|
def on_test_started(test)
|
|
176
190
|
return if !enabled? || !code_coverage?
|
|
191
|
+
return if suite_skipping_mode?
|
|
177
192
|
|
|
178
193
|
# Stop any in-progress context coverage and store it
|
|
179
194
|
stop_context_coverage_and_store
|
|
@@ -200,6 +215,7 @@ module Datadog
|
|
|
200
215
|
# @return [Datadog::CI::TestImpactAnalysis::Coverage::Event, nil] The coverage event or nil
|
|
201
216
|
def on_test_finished(test, context)
|
|
202
217
|
return unless enabled?
|
|
218
|
+
return if suite_skipping_mode?
|
|
203
219
|
|
|
204
220
|
# Handle ITR statistics
|
|
205
221
|
if test.skipped_by_test_impact_analysis?
|
|
@@ -222,33 +238,13 @@ module Datadog
|
|
|
222
238
|
context_ids = test.context_ids || []
|
|
223
239
|
merge_context_coverages_into_test(coverage, context_ids)
|
|
224
240
|
|
|
225
|
-
|
|
226
|
-
Telemetry.code_coverage_is_empty
|
|
227
|
-
return
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# cucumber's gherkin files are not covered by the code coverage collector - we add them here explicitly
|
|
231
|
-
test_source_file = test.source_file
|
|
232
|
-
ensure_test_source_covered(test_source_file, coverage) unless test_source_file.nil?
|
|
233
|
-
|
|
234
|
-
# if we have static dependencies tracking enabled then we can make the coverage
|
|
235
|
-
# more precise by fetching which files we depend on based on constants usage
|
|
236
|
-
enrich_coverage_with_static_dependencies(coverage)
|
|
237
|
-
|
|
238
|
-
Telemetry.code_coverage_files(coverage.size)
|
|
239
|
-
|
|
240
|
-
coverage_event = Coverage::Event.new(
|
|
241
|
+
write_coverage_event(
|
|
241
242
|
test_id: test.id.to_s,
|
|
242
243
|
test_suite_id: test.test_suite_id.to_s,
|
|
243
244
|
test_session_id: test.test_session_id.to_s,
|
|
245
|
+
source_file: test.source_file,
|
|
244
246
|
coverage: coverage
|
|
245
247
|
)
|
|
246
|
-
|
|
247
|
-
Datadog.logger.debug { "Writing coverage event \n #{coverage_event.pretty_inspect}" }
|
|
248
|
-
|
|
249
|
-
write(coverage_event)
|
|
250
|
-
|
|
251
|
-
coverage_event
|
|
252
248
|
end
|
|
253
249
|
|
|
254
250
|
# Clears stored context coverage for a specific context.
|
|
@@ -271,7 +267,7 @@ module Datadog
|
|
|
271
267
|
#
|
|
272
268
|
# @return [Boolean]
|
|
273
269
|
def context_coverage_enabled?
|
|
274
|
-
enabled? && code_coverage? && !@use_single_threaded_coverage
|
|
270
|
+
enabled? && code_coverage? && !suite_skipping_mode? && !@use_single_threaded_coverage
|
|
275
271
|
end
|
|
276
272
|
|
|
277
273
|
def skippable?(datadog_test_id)
|
|
@@ -280,6 +276,12 @@ module Datadog
|
|
|
280
276
|
@mutex.synchronize { @skippable_tests.include?(datadog_test_id) }
|
|
281
277
|
end
|
|
282
278
|
|
|
279
|
+
def skippable_suite?(test_suite_name)
|
|
280
|
+
return false if !enabled? || !skipping_suites?
|
|
281
|
+
|
|
282
|
+
@mutex.synchronize { @skippable_suites.include?(test_suite_name) }
|
|
283
|
+
end
|
|
284
|
+
|
|
283
285
|
def mark_if_skippable(test)
|
|
284
286
|
return if !enabled? || !skipping_tests?
|
|
285
287
|
|
|
@@ -292,6 +294,66 @@ module Datadog
|
|
|
292
294
|
end
|
|
293
295
|
end
|
|
294
296
|
|
|
297
|
+
def mark_if_suite_skippable(test_suite)
|
|
298
|
+
return if !enabled? || !skipping_suites?
|
|
299
|
+
|
|
300
|
+
unskippable = test_suite.itr_unskippable?
|
|
301
|
+
Telemetry.itr_unskippable if unskippable
|
|
302
|
+
|
|
303
|
+
unless skippable_suite?(test_suite.name)
|
|
304
|
+
Datadog.logger.debug { "Test suite is not skippable: #{test_suite.name}" }
|
|
305
|
+
return
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
if unskippable
|
|
309
|
+
Telemetry.itr_forced_run
|
|
310
|
+
test_suite.set_tag(Ext::Test::TAG_ITR_FORCED_RUN, "true")
|
|
311
|
+
|
|
312
|
+
Datadog.logger.debug { "Forced run of skippable test suite: #{test_suite.name}" }
|
|
313
|
+
return
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
test_suite.set_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR, "true")
|
|
317
|
+
test_suite.skipped!(reason: Ext::Test::SkipReason::TEST_IMPACT_ANALYSIS)
|
|
318
|
+
|
|
319
|
+
Datadog.logger.debug { "Marked test suite as skippable: #{test_suite.name}" }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def on_test_suite_started(test_suite)
|
|
323
|
+
return unless enabled? && suite_skipping_mode?
|
|
324
|
+
|
|
325
|
+
mark_if_suite_skippable(test_suite)
|
|
326
|
+
return if test_suite.should_skip?
|
|
327
|
+
return unless code_coverage?
|
|
328
|
+
|
|
329
|
+
Telemetry.code_coverage_started(test_suite)
|
|
330
|
+
coverage_collector&.start
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def on_test_suite_finished(test_suite, context)
|
|
334
|
+
return unless enabled? && suite_skipping_mode?
|
|
335
|
+
|
|
336
|
+
if test_suite.skipped_by_test_impact_analysis?
|
|
337
|
+
Telemetry.itr_skipped
|
|
338
|
+
context.incr_tests_skipped_by_tia_count
|
|
339
|
+
return
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
return unless code_coverage?
|
|
343
|
+
|
|
344
|
+
Telemetry.code_coverage_finished(test_suite)
|
|
345
|
+
|
|
346
|
+
coverage = coverage_collector&.stop
|
|
347
|
+
|
|
348
|
+
write_coverage_event(
|
|
349
|
+
test_id: nil,
|
|
350
|
+
test_suite_id: test_suite.id.to_s,
|
|
351
|
+
test_session_id: test_suite.get_tag(Ext::Test::TAG_TEST_SESSION_ID).to_s,
|
|
352
|
+
source_file: test_suite.source_file,
|
|
353
|
+
coverage: coverage
|
|
354
|
+
)
|
|
355
|
+
end
|
|
356
|
+
|
|
295
357
|
def write_test_session_tags(test_session, skipped_tests_count)
|
|
296
358
|
return if !enabled?
|
|
297
359
|
|
|
@@ -302,8 +364,8 @@ module Datadog
|
|
|
302
364
|
test_session.set_tag(Ext::Test::TAG_ITR_TEST_SKIPPING_COUNT, skipped_tests_count)
|
|
303
365
|
end
|
|
304
366
|
|
|
305
|
-
def
|
|
306
|
-
|
|
367
|
+
def skippables_count
|
|
368
|
+
current_skippables.count
|
|
307
369
|
end
|
|
308
370
|
|
|
309
371
|
def shutdown!
|
|
@@ -314,15 +376,17 @@ module Datadog
|
|
|
314
376
|
def serialize_state
|
|
315
377
|
{
|
|
316
378
|
correlation_id: @correlation_id,
|
|
317
|
-
skippable_tests: @skippable_tests
|
|
379
|
+
skippable_tests: @skippable_tests,
|
|
380
|
+
skippable_suites: @skippable_suites
|
|
318
381
|
}
|
|
319
382
|
end
|
|
320
383
|
|
|
321
384
|
def restore_state(state)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
385
|
+
set_skippables(
|
|
386
|
+
correlation_id: state[:correlation_id],
|
|
387
|
+
tests: state[:skippable_tests] || Set.new,
|
|
388
|
+
suites: state[:skippable_suites] || Set.new
|
|
389
|
+
)
|
|
326
390
|
end
|
|
327
391
|
|
|
328
392
|
def storage_key
|
|
@@ -330,24 +394,20 @@ module Datadog
|
|
|
330
394
|
end
|
|
331
395
|
|
|
332
396
|
def restore_state_from_datadog_test_runner
|
|
333
|
-
Datadog.logger.debug { "Restoring
|
|
397
|
+
Datadog.logger.debug { "Restoring skippables from Test Optimization cache" }
|
|
334
398
|
|
|
335
|
-
|
|
336
|
-
if
|
|
337
|
-
Datadog.logger.debug { "Restoring
|
|
399
|
+
skippables_data = load_cached_skippable_tests
|
|
400
|
+
if skippables_data.nil?
|
|
401
|
+
Datadog.logger.debug { "Restoring skippables failed, will request again" }
|
|
338
402
|
return false
|
|
339
403
|
end
|
|
340
404
|
|
|
341
|
-
Datadog.logger.debug { "Restored
|
|
405
|
+
Datadog.logger.debug { "Restored skippables from Test Optimization: #{skippables_data}" }
|
|
342
406
|
|
|
343
|
-
skippable_response = Skippable::Response.from_json(
|
|
407
|
+
skippable_response = Skippable::Response.from_json(skippables_data)
|
|
408
|
+
apply_skippable_response(skippable_response)
|
|
344
409
|
|
|
345
|
-
|
|
346
|
-
@correlation_id = skippable_response.correlation_id
|
|
347
|
-
@skippable_tests = skippable_response.tests
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
Datadog.logger.debug { "Found [#{@skippable_tests.size}] skippable tests from context" }
|
|
410
|
+
Datadog.logger.debug { "Found [#{skippables_count}] skippable #{@test_skipping_mode}s from context" }
|
|
351
411
|
Datadog.logger.debug { "ITR correlation ID from context: #{@correlation_id}" }
|
|
352
412
|
|
|
353
413
|
true
|
|
@@ -406,27 +466,84 @@ module Datadog
|
|
|
406
466
|
coverage[absolute_test_source_file_path] = true
|
|
407
467
|
end
|
|
408
468
|
|
|
409
|
-
def
|
|
410
|
-
|
|
469
|
+
def write_coverage_event(test_id:, test_suite_id:, test_session_id:, source_file:, coverage:)
|
|
470
|
+
coverage ||= {}
|
|
471
|
+
if coverage.empty?
|
|
472
|
+
Telemetry.code_coverage_is_empty
|
|
473
|
+
return
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
ensure_test_source_covered(source_file, coverage) unless source_file.nil?
|
|
477
|
+
|
|
478
|
+
enrich_coverage_with_static_dependencies(coverage)
|
|
479
|
+
|
|
480
|
+
Telemetry.code_coverage_files(coverage.size)
|
|
481
|
+
|
|
482
|
+
coverage_event = Coverage::Event.new(
|
|
483
|
+
test_id: test_id,
|
|
484
|
+
test_suite_id: test_suite_id,
|
|
485
|
+
test_session_id: test_session_id,
|
|
486
|
+
coverage: coverage
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
Datadog.logger.debug { "Writing coverage event \n #{coverage_event.pretty_inspect}" }
|
|
490
|
+
|
|
491
|
+
write(coverage_event)
|
|
492
|
+
|
|
493
|
+
coverage_event
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def fetch_skippables(test_session)
|
|
497
|
+
return unless skipping_tests? || skipping_suites?
|
|
411
498
|
|
|
412
499
|
# we can only request skippable tests if git metadata is already uploaded
|
|
413
500
|
git_tree_upload_worker.wait_until_done
|
|
414
501
|
|
|
415
502
|
skippable_response =
|
|
416
|
-
Skippable.new(
|
|
417
|
-
|
|
503
|
+
Skippable.new(
|
|
504
|
+
api: @api,
|
|
505
|
+
dd_env: @dd_env,
|
|
506
|
+
config_tags: @config_tags,
|
|
507
|
+
test_skipping_mode: @test_skipping_mode
|
|
508
|
+
)
|
|
509
|
+
.fetch_skippables(test_session)
|
|
418
510
|
@skippable_tests_fetch_error = skippable_response.error_message unless skippable_response.ok?
|
|
419
511
|
|
|
512
|
+
apply_skippable_response(skippable_response)
|
|
513
|
+
|
|
514
|
+
Datadog.logger.debug { "Fetched skippable #{@test_skipping_mode}s: \n #{current_skippables}" }
|
|
515
|
+
Datadog.logger.debug { "Found #{skippables_count} skippable #{@test_skipping_mode}s." }
|
|
516
|
+
Datadog.logger.debug { "ITR correlation ID: #{@correlation_id}" }
|
|
517
|
+
|
|
518
|
+
Utils::Telemetry.inc(skippable_response_metric, skippables_count)
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
def apply_skippable_response(skippable_response)
|
|
522
|
+
set_skippables(
|
|
523
|
+
correlation_id: skippable_response.correlation_id,
|
|
524
|
+
tests: skippable_response.tests,
|
|
525
|
+
suites: skippable_response.suites
|
|
526
|
+
)
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def set_skippables(correlation_id:, tests:, suites:)
|
|
420
530
|
@mutex.synchronize do
|
|
421
|
-
@correlation_id =
|
|
422
|
-
@skippable_tests =
|
|
531
|
+
@correlation_id = correlation_id
|
|
532
|
+
@skippable_tests = tests
|
|
533
|
+
@skippable_suites = suites
|
|
423
534
|
end
|
|
535
|
+
end
|
|
424
536
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
537
|
+
def current_skippables
|
|
538
|
+
suite_skipping_mode? ? @skippable_suites : @skippable_tests
|
|
539
|
+
end
|
|
428
540
|
|
|
429
|
-
|
|
541
|
+
def skippable_response_metric
|
|
542
|
+
if skipping_suites?
|
|
543
|
+
Ext::Telemetry::METRIC_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES
|
|
544
|
+
else
|
|
545
|
+
Ext::Telemetry::METRIC_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS
|
|
546
|
+
end
|
|
430
547
|
end
|
|
431
548
|
|
|
432
549
|
def code_coverage_mode
|
|
@@ -21,7 +21,7 @@ module Datadog
|
|
|
21
21
|
def valid?
|
|
22
22
|
valid = true
|
|
23
23
|
|
|
24
|
-
%i[
|
|
24
|
+
%i[test_suite_id test_session_id coverage].each do |key|
|
|
25
25
|
next unless send(key).nil?
|
|
26
26
|
|
|
27
27
|
Datadog.logger.warn("citestcov event is invalid: [#{key}] is nil. Event: #{self}")
|
|
@@ -34,7 +34,7 @@ module Datadog
|
|
|
34
34
|
def to_msgpack(packer = nil)
|
|
35
35
|
packer ||= MessagePack::Packer.new
|
|
36
36
|
|
|
37
|
-
packer.write_map_header(4)
|
|
37
|
+
packer.write_map_header(test_id.nil? ? 3 : 4)
|
|
38
38
|
|
|
39
39
|
packer.write("test_session_id")
|
|
40
40
|
packer.write(test_session_id.to_i)
|
|
@@ -42,8 +42,10 @@ module Datadog
|
|
|
42
42
|
packer.write("test_suite_id")
|
|
43
43
|
packer.write(test_suite_id.to_i)
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
unless test_id.nil?
|
|
46
|
+
packer.write("span_id")
|
|
47
|
+
packer.write(test_id.to_i)
|
|
48
|
+
end
|
|
47
49
|
|
|
48
50
|
files = coverage.keys
|
|
49
51
|
packer.write("files")
|
|
@@ -2,13 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
require "set"
|
|
4
4
|
|
|
5
|
+
require_relative "../ext/test"
|
|
6
|
+
|
|
5
7
|
module Datadog
|
|
6
8
|
module CI
|
|
7
9
|
module TestImpactAnalysis
|
|
8
10
|
# No-op implementation used when test impact analysis is disabled.
|
|
9
11
|
class NullComponent
|
|
10
12
|
attr_reader :enabled, :skippable_tests_fetch_error, :test_skipping_enabled,
|
|
11
|
-
:code_coverage_enabled, :skippable_tests, :correlation_id
|
|
13
|
+
:code_coverage_enabled, :skippable_tests, :skippable_suites, :correlation_id,
|
|
14
|
+
:test_skipping_mode
|
|
12
15
|
|
|
13
16
|
def initialize
|
|
14
17
|
@enabled = false
|
|
@@ -16,7 +19,9 @@ module Datadog
|
|
|
16
19
|
@code_coverage_enabled = false
|
|
17
20
|
@skippable_tests_fetch_error = nil
|
|
18
21
|
@skippable_tests = Set.new
|
|
22
|
+
@skippable_suites = Set.new
|
|
19
23
|
@correlation_id = nil
|
|
24
|
+
@test_skipping_mode = Ext::Test::TIATestSkippingMode::TEST
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
def configure(_remote_configuration = nil, _test_session = nil)
|
|
@@ -30,6 +35,10 @@ module Datadog
|
|
|
30
35
|
false
|
|
31
36
|
end
|
|
32
37
|
|
|
38
|
+
def skipping_suites?
|
|
39
|
+
false
|
|
40
|
+
end
|
|
41
|
+
|
|
33
42
|
def code_coverage?
|
|
34
43
|
false
|
|
35
44
|
end
|
|
@@ -51,6 +60,13 @@ module Datadog
|
|
|
51
60
|
nil
|
|
52
61
|
end
|
|
53
62
|
|
|
63
|
+
def on_test_suite_started(_test_suite)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def on_test_suite_finished(_test_suite, _context)
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
|
|
54
70
|
def clear_context_coverage(_context_id)
|
|
55
71
|
end
|
|
56
72
|
|
|
@@ -65,10 +81,17 @@ module Datadog
|
|
|
65
81
|
false
|
|
66
82
|
end
|
|
67
83
|
|
|
84
|
+
def skippable_suite?(_test_suite_name)
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
|
|
68
88
|
def write_test_session_tags(_test_session, _skipped_tests_count)
|
|
69
89
|
end
|
|
70
90
|
|
|
71
|
-
def
|
|
91
|
+
def mark_if_suite_skippable(_test_suite)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def skippables_count
|
|
72
95
|
0
|
|
73
96
|
end
|
|
74
97
|
|
|
@@ -36,7 +36,7 @@ module Datadog
|
|
|
36
36
|
|
|
37
37
|
payload.fetch("data", [])
|
|
38
38
|
.each do |test_data|
|
|
39
|
-
next unless test_data["type"] == Ext::Test::
|
|
39
|
+
next unless test_data["type"] == Ext::Test::TIATestSkippingMode::TEST
|
|
40
40
|
|
|
41
41
|
attrs = test_data["attributes"] || {}
|
|
42
42
|
res << Utils::TestRun.datadog_test_id(attrs["name"], attrs["suite"], attrs["parameters"])
|
|
@@ -45,6 +45,20 @@ module Datadog
|
|
|
45
45
|
res
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def suites
|
|
49
|
+
res = Set.new
|
|
50
|
+
|
|
51
|
+
payload.fetch("data", [])
|
|
52
|
+
.each do |test_data|
|
|
53
|
+
next unless test_data["type"] == Ext::Test::TIATestSkippingMode::SUITE
|
|
54
|
+
|
|
55
|
+
suite = (test_data["attributes"] || {})["suite"]
|
|
56
|
+
res << suite unless suite.nil?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
res
|
|
60
|
+
end
|
|
61
|
+
|
|
48
62
|
def error_message
|
|
49
63
|
return nil if ok?
|
|
50
64
|
|
|
@@ -74,18 +88,19 @@ module Datadog
|
|
|
74
88
|
end
|
|
75
89
|
end
|
|
76
90
|
|
|
77
|
-
def initialize(dd_env:, api: nil, config_tags: {})
|
|
91
|
+
def initialize(dd_env:, api: nil, config_tags: {}, test_skipping_mode: Ext::Test::TIATestSkippingMode::TEST)
|
|
78
92
|
@api = api
|
|
79
93
|
@dd_env = dd_env
|
|
80
94
|
@config_tags = config_tags
|
|
95
|
+
@test_skipping_mode = test_skipping_mode
|
|
81
96
|
end
|
|
82
97
|
|
|
83
|
-
def
|
|
98
|
+
def fetch_skippables(test_session)
|
|
84
99
|
api = @api
|
|
85
100
|
return Response.from_http_response(nil) unless api
|
|
86
101
|
|
|
87
102
|
request_payload = payload(test_session)
|
|
88
|
-
Datadog.logger.debug("Fetching skippable
|
|
103
|
+
Datadog.logger.debug("Fetching skippable #{@test_skipping_mode}s with request: #{request_payload}")
|
|
89
104
|
|
|
90
105
|
http_response = api.api_request(
|
|
91
106
|
path: Ext::Transport::DD_API_SKIPPABLE_TESTS_PATH,
|
|
@@ -127,7 +142,7 @@ module Datadog
|
|
|
127
142
|
"data" => {
|
|
128
143
|
"type" => Ext::Transport::DD_API_SKIPPABLE_TESTS_TYPE,
|
|
129
144
|
"attributes" => {
|
|
130
|
-
"test_level" =>
|
|
145
|
+
"test_level" => @test_skipping_mode,
|
|
131
146
|
"service" => test_session.service,
|
|
132
147
|
"env" => @dd_env,
|
|
133
148
|
"repository_url" => test_session.git_repository_url,
|
|
@@ -48,13 +48,13 @@ module Datadog
|
|
|
48
48
|
|
|
49
49
|
# starting and finishing a test session is required to get the skippable tests response
|
|
50
50
|
Datadog::CI.start_test_session(total_tests_count: estimated_tests_count)&.finish
|
|
51
|
-
|
|
51
|
+
skippables_count = test_impact_analysis.skippables_count
|
|
52
52
|
|
|
53
53
|
log("Estimated tests count: #{estimated_tests_count}")
|
|
54
|
-
log("
|
|
54
|
+
log("Skippables count: #{skippables_count}")
|
|
55
55
|
validate_test_impact_analysis_state!
|
|
56
56
|
|
|
57
|
-
[(
|
|
57
|
+
[(skippables_count.to_f / estimated_tests_count).floor(2), 0.99].min || 0.0
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
end
|
|
@@ -49,7 +49,7 @@ module Datadog
|
|
|
49
49
|
tests_hash.each_value.flat_map do |test_configs|
|
|
50
50
|
test_configs.map do |test_config|
|
|
51
51
|
{
|
|
52
|
-
"type" => Ext::Test::
|
|
52
|
+
"type" => Ext::Test::DEFAULT_TIA_TEST_SKIPPING_MODE,
|
|
53
53
|
"attributes" => {
|
|
54
54
|
"suite" => test_config["suite"],
|
|
55
55
|
"name" => test_config["name"],
|
|
@@ -111,6 +111,26 @@ module Datadog
|
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
113
|
|
|
114
|
+
# @internal
|
|
115
|
+
def datadog_skip_reason
|
|
116
|
+
get_tag(Ext::Test::TAG_SKIP_REASON)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @internal
|
|
120
|
+
def should_skip?
|
|
121
|
+
skipped_by_test_impact_analysis?
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# @internal
|
|
125
|
+
def skipped_by_test_impact_analysis?
|
|
126
|
+
get_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR) == "true"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# @internal
|
|
130
|
+
def itr_unskippable?
|
|
131
|
+
get_tag(Ext::Test::TAG_ITR_UNSKIPPABLE) == "true"
|
|
132
|
+
end
|
|
133
|
+
|
|
114
134
|
private
|
|
115
135
|
|
|
116
136
|
def set_status_from_stats!
|
|
@@ -16,6 +16,7 @@ require_relative "../ext/test"
|
|
|
16
16
|
require_relative "../git/local_repository"
|
|
17
17
|
require_relative "../utils/file_storage"
|
|
18
18
|
require_relative "../utils/stateful"
|
|
19
|
+
require_relative "../utils/test_name"
|
|
19
20
|
|
|
20
21
|
require_relative "../worker"
|
|
21
22
|
|
|
@@ -109,6 +110,7 @@ module Datadog
|
|
|
109
110
|
def start_test_suite(test_suite_name, service: nil, tags: {})
|
|
110
111
|
return skip_tracing unless test_suite_level_visibility_enabled
|
|
111
112
|
|
|
113
|
+
test_suite_name = Utils::TestName.normalize(test_suite_name)
|
|
112
114
|
context = @local_test_suites_mode ? @context : maybe_remote_context
|
|
113
115
|
|
|
114
116
|
test_suite = context.start_test_suite(test_suite_name, service: service, tags: tags)
|
|
@@ -117,6 +119,9 @@ module Datadog
|
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
|
|
122
|
+
test_name = Utils::TestName.normalize(test_name)
|
|
123
|
+
test_suite_name = Utils::TestName.normalize(test_suite_name)
|
|
124
|
+
|
|
120
125
|
test_suite = active_test_suite(test_suite_name)
|
|
121
126
|
tags[Ext::Test::TAG_SUITE] ||= test_suite_name
|
|
122
127
|
|
|
@@ -165,6 +170,8 @@ module Datadog
|
|
|
165
170
|
# we return the single active test suite because most of the time there is only one test suite running
|
|
166
171
|
return single_active_test_suite if test_suite_name.nil?
|
|
167
172
|
|
|
173
|
+
test_suite_name = Utils::TestName.normalize(test_suite_name)
|
|
174
|
+
|
|
168
175
|
# when fetching test_suite to use as test's context, try local context instance first
|
|
169
176
|
local_test_suite = @context.active_test_suite(test_suite_name)
|
|
170
177
|
return local_test_suite if local_test_suite
|
|
@@ -194,6 +201,7 @@ module Datadog
|
|
|
194
201
|
end
|
|
195
202
|
|
|
196
203
|
def deactivate_test_suite(test_suite_name)
|
|
204
|
+
test_suite_name = Utils::TestName.normalize(test_suite_name)
|
|
197
205
|
test_suite = active_test_suite(test_suite_name)
|
|
198
206
|
on_test_suite_finished(test_suite) if test_suite
|
|
199
207
|
|
|
@@ -272,6 +280,7 @@ module Datadog
|
|
|
272
280
|
|
|
273
281
|
def on_test_suite_started(test_suite)
|
|
274
282
|
set_codeowners(test_suite)
|
|
283
|
+
test_impact_analysis.on_test_suite_started(test_suite)
|
|
275
284
|
end
|
|
276
285
|
|
|
277
286
|
def on_test_started(test)
|
|
@@ -318,6 +327,7 @@ module Datadog
|
|
|
318
327
|
end
|
|
319
328
|
|
|
320
329
|
def on_test_suite_finished(test_suite)
|
|
330
|
+
test_impact_analysis.on_test_suite_finished(test_suite, maybe_remote_context)
|
|
321
331
|
Telemetry.event_finished(test_suite)
|
|
322
332
|
end
|
|
323
333
|
|