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.



188
189
190
191
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 188

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

#datadog_fqn_test_idObject



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

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.



163
164
165
166
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 163

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)


177
178
179
180
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 177

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.



170
171
172
173
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 170

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

#datadog_test_idObject

Test identification



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

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



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

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

  test_name = full_description.strip
  if [:description].empty?
    # for unnamed it blocks this appends something like "example at ./spec/some_spec.rb:10"
    test_name << " #{description}"
  end

  # 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



137
138
139
140
141
142
143
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 137

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



125
126
127
128
129
130
131
132
133
134
135
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 125

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

  suite_name = "#{datadog_test_suite_description} at #{metadata[: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



153
154
155
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 153

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

#datadog_unskippable?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 145

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

#finish(reporter) ⇒ Object



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

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



27
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
# File 'lib/datadog/ci/contrib/rspec/example.rb', line 27

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