Class: Datadog::CI::Test

Inherits:
Span
  • Object
show all
Defined in:
lib/datadog/ci/test.rb

Overview

Represents a single part of a test run.

Instance Attribute Summary collapse

Attributes inherited from Span

#tracer_span

Instance Method Summary collapse

Methods inherited from Span

#base_commit_sha, #clear_tag, #failed?, #get_metric, #get_tag, #git_branch, #git_commit_message, #git_commit_sha, #git_repository_url, #git_tag, #id, #original_git_commit_message, #original_git_commit_sha, #os_architecture, #os_platform, #os_version, #passed?, #runtime_name, #runtime_version, #service, #set_default_tags, #set_environment_runtime_tags, #set_metric, #set_tag, #set_tags, #skipped?, #source_file, #status, #to_s, #trace_id, #type, #undefined?

Constructor Details

#initialize(tracer_span) ⇒ Test

Returns a new instance of Test.



15
16
17
18
19
20
# File 'lib/datadog/ci/test.rb', line 15

def initialize(tracer_span)
  super

  @custom_impacted_files = []
  @inherited_custom_impacted_files = []
end

Instance Attribute Details

#context_idsArray<String>

Context IDs for this test (used for TIA context coverage merging). Contains list of context identifiers from outermost to innermost.

Returns:

  • (Array<String>)

    list of context IDs



25
26
27
# File 'lib/datadog/ci/test.rb', line 25

def context_ids
  @context_ids
end

Instance Method Details

#add_impacted_files(file_paths) ⇒ void

This method returns an undefined value.

Adds custom files that Test Impact Analysis should consider capable of impacting this test.

This is useful for files that Datadog's native Ruby coverage cannot observe, such as JavaScript executed in a browser during a Capybara test. Paths must resolve inside the Git repository. Relative paths must be relative to the repository root. Absolute paths are also accepted.

If paths are relative to the current working directory, convert them to absolute paths with File.expand_path before calling this method. Submitted paths must be lexically normalized without redundant . or .. components.

Path resolution does not access the filesystem, so files do not need to exist when they are added. Paths outside the repository are ignored when the coverage event is serialized. Calls are incremental: every call adds files for the remainder of the test.

Examples:

Register JavaScript files loaded during an RSpec test

RSpec.configure do |config|
  config.after do
    Datadog::CI.active_test&.add_impacted_files(
      javascript_files_loaded_by_browser.map do |path|
        File.expand_path(path, Dir.pwd)
      end
    )
  end
end

Parameters:

  • file_paths (Array<String>)

    custom paths that can impact this test

Raises:

  • (ArgumentError)

    if file_paths is not an Array



199
200
201
202
203
204
# File 'lib/datadog/ci/test.rb', line 199

def add_impacted_files(file_paths)
  raise ArgumentError, "file_paths must be an Array" unless file_paths.is_a?(Array)

  @custom_impacted_files.concat(file_paths)
  nil
end

#all_executions_failed?Boolean

Returns:

  • (Boolean)


280
281
282
# File 'lib/datadog/ci/test.rb', line 280

def all_executions_failed?
  !!test_suite&.all_executions_failed?(datadog_test_id)
end

#all_executions_passed?Boolean

Returns:

  • (Boolean)


285
286
287
# File 'lib/datadog/ci/test.rb', line 285

def all_executions_passed?
  !!test_suite&.all_executions_passed?(datadog_test_id)
end

#any_retry_passed?Boolean

Returns:

  • (Boolean)


275
276
277
# File 'lib/datadog/ci/test.rb', line 275

def any_retry_passed?
  !!test_suite&.any_test_retry_passed?(datadog_test_id)
end

#attempt_to_fix?Boolean

Returns "true" if this flaky test has fixing attempts (determined by Datadog backend).

Returns:

  • (Boolean)

    true if this test is attempted to be fixed.



135
136
137
# File 'lib/datadog/ci/test.rb', line 135

def attempt_to_fix?
  get_tag(Ext::Test::TAG_IS_ATTEMPT_TO_FIX) == "true"
end

#datadog_skip_reasonObject



290
291
292
293
294
295
296
# File 'lib/datadog/ci/test.rb', line 290

def datadog_skip_reason
  if skipped_by_test_impact_analysis?
    Ext::Test::SkipReason::TEST_IMPACT_ANALYSIS
  elsif disabled? || quarantined?
    Ext::Test::SkipReason::TEST_MANAGEMENT_DISABLED
  end
end

#datadog_test_idString

Returns the test id according to Datadog's test impact analysis.

Returns:

  • (String)

    the test id according to Datadog's test impact analysis.



33
34
35
# File 'lib/datadog/ci/test.rb', line 33

def datadog_test_id
  @datadog_test_id ||= Utils::TestRun.datadog_test_id(name, test_suite_name, parameters)
end

#disabled?Boolean

Returns "true" if this test is disabled by Datadog test management.

Returns:

  • (Boolean)

    true if this test is disabled, false otherwise.



129
130
131
# File 'lib/datadog/ci/test.rb', line 129

def disabled?
  get_tag(Ext::Test::TAG_IS_TEST_DISABLED) == "true"
end

#end_lineInteger?

Returns the ending line number of the test in the source file.

Returns:

  • (Integer)

    the ending line number

  • (nil)

    if the ending line is not available



98
99
100
101
# File 'lib/datadog/ci/test.rb', line 98

def end_line
  line = get_tag(Ext::Test::TAG_SOURCE_END)
  line&.to_i
end

#failed!(exception: nil) ⇒ void

This method returns an undefined value.

Sets the status of the span to "fail".

Parameters:

  • exception (Exception) (defaults to: nil)

    the exception that caused the test to fail.



238
239
240
241
242
# File 'lib/datadog/ci/test.rb', line 238

def failed!(exception: nil)
  super

  record_test_result(Ext::Test::Status::FAIL)
end

#finishvoid

This method returns an undefined value.

Finishes the current test.



39
40
41
42
43
44
45
46
47
# File 'lib/datadog/ci/test.rb', line 39

def finish
  if is_retry? && retry_reason.nil?
    set_tag(Ext::Test::TAG_RETRY_REASON, Ext::Test::RetryReason::RETRY_EXTERNAL)
  end

  test_tracing.deactivate_test

  super
end

#inherit_impacted_files(custom_impacted_files) ⇒ void

This method returns an undefined value.

Inherits suite-level custom impacted files without copying them.



210
211
212
213
# File 'lib/datadog/ci/test.rb', line 210

def inherit_impacted_files(custom_impacted_files)
  @inherited_custom_impacted_files = custom_impacted_files
  nil
end

#is_new?Boolean

Returns "true" if this span represents a test that wasn't known to Datadog before.

Returns:

  • (Boolean)

    true if this test is a new one, false otherwise.



117
118
119
# File 'lib/datadog/ci/test.rb', line 117

def is_new?
  get_tag(Ext::Test::TAG_IS_NEW) == "true"
end

#is_retry?Boolean

Returns "true" if test span represents a retry.

Returns:

  • (Boolean)

    true if this test is a retry, false otherwise.



105
106
107
# File 'lib/datadog/ci/test.rb', line 105

def is_retry?
  get_tag(Ext::Test::TAG_IS_RETRY) == "true"
end

#itr_unskippable!void

This method returns an undefined value.

Marks this test as unskippable by the Test Impact Analysis. This must be done before the test execution starts.

Examples of tests that should be unskippable:

  • tests that read files from disk
  • tests that make network requests
  • tests that call external processes
  • tests that use forking


155
156
157
158
159
160
161
162
163
164
165
# File 'lib/datadog/ci/test.rb', line 155

def itr_unskippable!
  TestImpactAnalysis::Telemetry.itr_unskippable
  set_tag(Ext::Test::TAG_ITR_UNSKIPPABLE, "true")

  if skipped_by_test_impact_analysis?
    clear_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR)

    TestImpactAnalysis::Telemetry.itr_forced_run
    set_tag(Ext::Test::TAG_ITR_FORCED_RUN, "true")
  end
end

#lock_custom_impacted_filesArray<String>

Locks custom impacted files against further changes and returns them to Test Impact Analysis.

Returns:

  • (Array<String>)


220
221
222
223
224
225
# File 'lib/datadog/ci/test.rb', line 220

def lock_custom_impacted_files
  return @custom_impacted_files if @custom_impacted_files.frozen?

  @custom_impacted_files =
    (@inherited_custom_impacted_files | @custom_impacted_files).freeze
end

#modified?Boolean

Returns "true" if this test is marked as modified (e.g., impacted by code changes).

Returns:

  • (Boolean)

    true if this test is modified, false otherwise.



141
142
143
# File 'lib/datadog/ci/test.rb', line 141

def modified?
  get_tag(Ext::Test::TAG_TEST_IS_MODIFIED) == "true"
end

#nameString

Returns the name of the test.

Returns:

  • (String)

    the name of the test.



28
29
30
# File 'lib/datadog/ci/test.rb', line 28

def name
  get_tag(Ext::Test::TAG_NAME)
end

#parametersString?

Gets the parameters for this test (e.g. Cucumber example or RSpec specs) as a serialized JSON.

Returns:

  • (String)

    the serialized JSON of the parameters

  • (nil)

    if this test does not have parameters



270
271
272
# File 'lib/datadog/ci/test.rb', line 270

def parameters
  get_tag(Ext::Test::TAG_PARAMETERS)
end

#passed!void

This method returns an undefined value.

Sets the status of the span to "pass".



229
230
231
232
233
# File 'lib/datadog/ci/test.rb', line 229

def passed!
  super

  record_test_result(Ext::Test::Status::PASS)
end

#peek_durationObject



327
328
329
330
331
332
333
# File 'lib/datadog/ci/test.rb', line 327

def peek_duration
  end_time = Core::Utils::Time.now.utc
  start_time = tracer_span.start_time

  return 0.0 if start_time.nil? || end_time.nil?
  end_time - start_time
end

#quarantined?Boolean

Returns "true" if this test is quarantined by Datadog test management.

Returns:

  • (Boolean)

    true if this test is quarantined, false otherwise.



123
124
125
# File 'lib/datadog/ci/test.rb', line 123

def quarantined?
  get_tag(Ext::Test::TAG_IS_QUARANTINED) == "true"
end

#record_final_statusObject



317
318
319
320
321
322
323
324
# File 'lib/datadog/ci/test.rb', line 317

def record_final_status
  status = get_tag(Ext::Test::TAG_STATUS)
  return if status.nil?

  final_status = compute_final_status(status)
  set_tag(Ext::Test::TAG_FINAL_STATUS, final_status)
  test_suite&.record_test_final_status(datadog_test_id, final_status)
end

#retry_reasonString

Returns string with a reason why test was retried

Returns:

  • (String)

    retry reason



111
112
113
# File 'lib/datadog/ci/test.rb', line 111

def retry_reason
  get_tag(Ext::Test::TAG_RETRY_REASON)
end

#set_parameters(arguments, metadata = {}) ⇒ void

This method returns an undefined value.

Sets the parameters for this test (e.g. Cucumber example or RSpec specs). Parameters are needed to compute test fingerprint to distinguish between different tests having same names.

Parameters:

  • arguments (Hash)

    the arguments that test accepts as key-value hash

  • metadata (Hash) (defaults to: {})

    optional metadata



260
261
262
263
264
# File 'lib/datadog/ci/test.rb', line 260

def set_parameters(arguments,  = {})
  return if arguments.nil?

  set_tag(Ext::Test::TAG_PARAMETERS, Utils::TestRun.test_parameters(arguments: arguments, metadata: ))
end

#should_ignore_failures?Boolean

Returns:

  • (Boolean)


304
305
306
307
308
309
# File 'lib/datadog/ci/test.rb', line 304

def should_ignore_failures?
  return false if attempt_to_fix?
  return true if quarantined? || disabled?

  any_retry_passed?
end

#should_skip?Boolean

Returns:

  • (Boolean)


299
300
301
# File 'lib/datadog/ci/test.rb', line 299

def should_skip?
  skipped_by_test_impact_analysis? || (disabled? && !attempt_to_fix?)
end

#skipped!(exception: nil, reason: nil) ⇒ void

This method returns an undefined value.

Sets the status of the span to "skip".

Parameters:

  • exception (Exception) (defaults to: nil)

    the exception that caused the test to fail.

  • reason (String) (defaults to: nil)

    the reason why the test was skipped.



248
249
250
251
252
# File 'lib/datadog/ci/test.rb', line 248

def skipped!(exception: nil, reason: nil)
  super

  record_test_result(Ext::Test::Status::SKIP)
end

#skipped_by_test_impact_analysis?Boolean

Returns:

  • (Boolean)


312
313
314
# File 'lib/datadog/ci/test.rb', line 312

def skipped_by_test_impact_analysis?
  get_tag(Ext::Test::TAG_ITR_SKIPPED_BY_ITR) == "true"
end

#start_lineInteger?

Returns the starting line number of the test in the source file.

Returns:

  • (Integer)

    the starting line number

  • (nil)

    if the starting line is not available



90
91
92
93
# File 'lib/datadog/ci/test.rb', line 90

def start_line
  line = get_tag(Ext::Test::TAG_SOURCE_START)
  line&.to_i
end

#test_module_idString

Span id of the running test module this test belongs to.

Returns:

  • (String)

    the span id of the test module.



71
72
73
# File 'lib/datadog/ci/test.rb', line 71

def test_module_id
  get_tag(Ext::Test::TAG_TEST_MODULE_ID)
end

#test_module_nameString

Name of the running test module this test belongs to.

Returns:

  • (String)

    the name of the test module.



77
78
79
# File 'lib/datadog/ci/test.rb', line 77

def test_module_name
  get_tag(Ext::Test::TAG_MODULE)
end

#test_session_idString

Span id of the running test session this test belongs to.

Returns:

  • (String)

    the span id of the test session.



83
84
85
# File 'lib/datadog/ci/test.rb', line 83

def test_session_id
  get_tag(Ext::Test::TAG_TEST_SESSION_ID)
end

#test_suiteDatadog::CI::TestSuite?

Running test suite that this test is part of (if any).

Returns:



52
53
54
55
# File 'lib/datadog/ci/test.rb', line 52

def test_suite
  suite_name = test_suite_name
  CI.active_test_suite(suite_name) if suite_name
end

#test_suite_idString

Span id of the running test suite this test belongs to.

Returns:

  • (String)

    the span id of the test suite.



59
60
61
# File 'lib/datadog/ci/test.rb', line 59

def test_suite_id
  get_tag(Ext::Test::TAG_TEST_SUITE_ID)
end

#test_suite_nameString

Name of the running test suite this test belongs to.

Returns:

  • (String)

    the name of the test suite.



65
66
67
# File 'lib/datadog/ci/test.rb', line 65

def test_suite_name
  get_tag(Ext::Test::TAG_SUITE)
end