Module: Datadog::CI::Contrib::RSpec::Example::InstanceMethods

Defined in:
lib/datadog/ci/contrib/rspec/example.rb

Instance Method Summary collapse

Instance Method Details

#datadog_context_idsObject

Returns list of context IDs for this example, from outermost to innermost. Used for merging context-level coverage into test coverage.



185
186
187
188
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 185

def datadog_context_ids
  traverse_example_group_hierarchy unless defined?(@datadog_context_ids)
  @datadog_context_ids
end

#datadog_fqn_test_idObject



104
105
106
107
108
109
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 104

def datadog_fqn_test_id
  @datadog_fqn_test_id ||= Utils::TestRun.datadog_test_id(
    datadog_test_name,
    datadog_test_suite_name
  )
end

#datadog_source_fileObject

Returns the relative source file path for this example.

Some test frameworks (like rswag) dynamically generate examples inside gem code, which causes metadata to point to the gem's internal file instead of the actual spec file. In such cases, we traverse the example_group hierarchy to find the correct source file.



160
161
162
163
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 160

def datadog_source_file
  resolve_source_location unless defined?(@datadog_source_file)
  @datadog_source_file
end

#datadog_source_location_from_parent?Boolean

Returns true if the source location was resolved from a parent example_group rather than the example's own metadata.

Returns:

  • (Boolean)


174
175
176
177
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 174

def datadog_source_location_from_parent?
  resolve_source_location unless defined?(@datadog_source_location_from_parent)
  @datadog_source_location_from_parent
end

#datadog_source_startObject

Returns the source line number for this example. This corresponds to the same location as datadog_source_file.



167
168
169
170
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 167

def datadog_source_start
  resolve_source_location unless defined?(@datadog_source_start)
  @datadog_source_start
end

#datadog_test_idObject

============================================

Test identification



96
97
98
99
100
101
102
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 96

def datadog_test_id
  @datadog_test_id ||= Utils::TestRun.datadog_test_id(
    datadog_test_name,
    datadog_test_suite_name,
    datadog_test_parameters
  )
end

#datadog_test_nameObject



111
112
113
114
115
116
117
118
119
120
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 111

def datadog_test_name
  return @datadog_test_name if defined?(@datadog_test_name)

  test_name = datadog_unnamed_example? ? datadog_unnamed_example_name : full_description.strip

  # remove example group description from test name to avoid duplication
  test_name = test_name.sub(datadog_test_suite_description, "").strip

  @datadog_test_name = test_name
end

#datadog_test_parametersObject



134
135
136
137
138
139
140
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 134

def datadog_test_parameters
  return @datadog_test_parameters if defined?(@datadog_test_parameters)

  @datadog_test_parameters = Utils::TestRun.test_parameters(
    metadata: {"scoped_id" => [:scoped_id]}
  )
end

#datadog_test_suite_nameObject



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 122

def datadog_test_suite_name
  return @datadog_test_suite_name if defined?(@datadog_test_suite_name)

  suite_name = "#{datadog_test_suite_description} at #{[:example_group][:rerun_file_path]}"

  if ci_queue?
    suite_name = "#{suite_name} (ci-queue running example [#{datadog_test_name}])"
  end

  @datadog_test_suite_name = suite_name
end

#datadog_test_suite_source_file_pathObject

============================================

Source location



150
151
152
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 150

def datadog_test_suite_source_file_path
  Git::LocalRepository.relative_to_root([:rerun_file_path])
end

#datadog_unskippable?Boolean

Returns:

  • (Boolean)


142
143
144
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 142

def datadog_unskippable?
  !![CI::Ext::Test::ITR_UNSKIPPABLE_OPTION]
end

#finish(reporter) ⇒ Object



81
82
83
84
85
86
87
88
89
90
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 81

def finish(reporter)
  # By default finish test but do not report it to RSpec::Core::Reporter
  # it is going to be reported once after retries are done.
  #
  # We need to do this because RSpec breaks when we try to report the same example multiple times with different
  # results.
  return super unless @skip_reporting

  super(::RSpec::Core::NullReporter)
end

#run(*args) ⇒ Object

============================================

Main entry points



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 28

def run(*args)
  return super unless datadog_configuration[:enabled]
  return super if ::RSpec.configuration.dry_run? && !datadog_configuration[:dry_run_enabled]

  test_suite_span = test_tracing_component.start_test_suite(datadog_test_suite_name) if ci_queue?

  # don't report test to RSpec::Core::Reporter until retries are done
  @skip_reporting = true

  # we keep track of the last test failure if we encounter any
  test_failure = nil

  test_retries_component.with_retries do
    test_tracing_component.trace_test(
      datadog_test_name,
      datadog_test_suite_name,
      tags: build_test_tags,
      service: datadog_configuration[:service_name]
    ) do |test_span|
      # Set context IDs on the test span for TIA context coverage merging
      test_span&.context_ids = datadog_context_ids

      # Process TIA status of the test and eventually tell RSpec to skip the test
      test_span&.itr_unskippable! if datadog_unskippable?
      [:skip] = test_span&.datadog_skip_reason if test_span&.should_skip?

      # before each run remove any previous exception
      @exception = nil

      result = super

      # When test job is canceled and RSpec is quitting we don't want to report the last test
      # before RSpec context unwinds. This test might have some unrelated errors that we don't want to
      # see in Datadog.
      return result if ::RSpec.world.wants_to_quit

      test_failure = handle_test_result(test_span, test_failure)
      # Formatter metadata here is a set of properties that will be used by Datadog's custom rspec formatter
      (test_span)

      # In attempt-to-fix flow we need to treat test as failed if any of the retries failed
      restore_failure_state(test_span, test_failure)
    end
  end

  # this is a special case for ci-queue test runner, we need to finish the test suite span created for a single test
  test_suite_span&.finish

  # after retries are done, we must report the test to RSpec
  @skip_reporting = false
  finish(reporter)
end