datadog-ci 1.0.1 → 1.2.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 +16 -2
- data/ext/datadog_cov/datadog_cov.c +259 -67
- data/lib/datadog/ci/configuration/components.rb +121 -79
- data/lib/datadog/ci/configuration/settings.rb +6 -0
- data/lib/datadog/ci/contrib/rspec/example.rb +1 -1
- data/lib/datadog/ci/contrib/rspec/patcher.rb +3 -3
- data/lib/datadog/ci/ext/settings.rb +1 -0
- data/lib/datadog/ci/span.rb +3 -3
- data/lib/datadog/ci/test.rb +1 -1
- data/lib/datadog/ci/test_module.rb +1 -1
- data/lib/datadog/ci/{itr/runner.rb → test_optimisation/component.rb} +13 -10
- data/lib/datadog/ci/{itr → test_optimisation}/coverage/ddcov.rb +1 -1
- data/lib/datadog/ci/{itr → test_optimisation}/coverage/event.rb +1 -1
- data/lib/datadog/ci/{itr → test_optimisation}/coverage/transport.rb +1 -1
- data/lib/datadog/ci/{itr → test_optimisation}/coverage/writer.rb +1 -1
- data/lib/datadog/ci/{itr → test_optimisation}/skippable.rb +1 -1
- data/lib/datadog/ci/test_session.rb +1 -1
- data/lib/datadog/ci/test_suite.rb +1 -1
- data/lib/datadog/ci/test_visibility/{recorder.rb → component.rb} +10 -10
- data/lib/datadog/ci/test_visibility/{null_recorder.rb → null_component.rb} +6 -4
- data/lib/datadog/ci/test_visibility/transport.rb +1 -1
- data/lib/datadog/ci/transport/adapters/net.rb +138 -0
- data/lib/datadog/ci/transport/api/agentless.rb +2 -2
- data/lib/datadog/ci/transport/api/evp_proxy.rb +1 -1
- data/lib/datadog/ci/transport/http.rb +7 -57
- data/lib/datadog/ci/version.rb +2 -2
- data/lib/datadog/ci.rb +15 -15
- metadata +12 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba0975da7283a037f3ea9c70954f2868cf387a0e86608882d2ebefdd097e0cf7
|
4
|
+
data.tar.gz: 7842e2e30802411616d987bcbae9be620befa27ba90206a733646a469225f835
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89bce02785793b2c1f5b5438f6c9cfbf63d3ff4cb065788b4884063341bdc23c289e29715f846ce24e5a4ab939c9cdeb05e755a216e92cee6f20b9954462b05f
|
7
|
+
data.tar.gz: 6adac19cff9d74fc081d22654a8b0db93152c8c5b6706fee82263e6f219edd96bb473f9595c85418a904110bfcde430b7ddcabd034279cb45d24374a55c5b170
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.2.0] - 2024-07-16
|
4
|
+
|
5
|
+
### Changed
|
6
|
+
* Expand test impact analysis with allocation tracing ([#197][])
|
7
|
+
|
8
|
+
## [1.1.0] - 2024-07-01
|
9
|
+
|
10
|
+
### Added
|
11
|
+
* Ignore Webmock automatically when making HTTP calls ([#193][])
|
12
|
+
|
3
13
|
## [1.0.1] - 2024-06-11
|
4
14
|
|
5
15
|
### Fixed
|
@@ -267,7 +277,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
267
277
|
|
268
278
|
- Ruby versions < 2.7 no longer supported ([#8][])
|
269
279
|
|
270
|
-
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.0
|
280
|
+
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.2.0...main
|
281
|
+
[1.2.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.1.0...v1.2.0
|
282
|
+
[1.1.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.0.1...v1.1.0
|
271
283
|
[1.0.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.0.0...v1.0.1
|
272
284
|
[1.0.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.0.0.beta6...v1.0.0
|
273
285
|
[1.0.0.beta6]: https://github.com/DataDog/datadog-ci-rb/compare/v1.0.0.beta5...v1.0.0.beta6
|
@@ -377,4 +389,6 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
377
389
|
[#183]: https://github.com/DataDog/datadog-ci-rb/issues/183
|
378
390
|
[#185]: https://github.com/DataDog/datadog-ci-rb/issues/185
|
379
391
|
[#189]: https://github.com/DataDog/datadog-ci-rb/issues/189
|
380
|
-
[#190]: https://github.com/DataDog/datadog-ci-rb/issues/190
|
392
|
+
[#190]: https://github.com/DataDog/datadog-ci-rb/issues/190
|
393
|
+
[#193]: https://github.com/DataDog/datadog-ci-rb/issues/193
|
394
|
+
[#197]: https://github.com/DataDog/datadog-ci-rb/issues/197
|
@@ -1,13 +1,26 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <ruby/debug.h>
|
3
|
+
#include <ruby/st.h>
|
4
|
+
|
5
|
+
#include <stdbool.h>
|
6
|
+
|
7
|
+
// This is a native extension that collects a list of Ruby files that were executed during the test run.
|
8
|
+
// It is used to optimize the test suite by running only the tests that are affected by the changes.
|
3
9
|
|
4
10
|
#define PROFILE_FRAMES_BUFFER_SIZE 1
|
5
11
|
|
6
12
|
// threading modes
|
7
|
-
|
8
|
-
|
13
|
+
enum threading_mode
|
14
|
+
{
|
15
|
+
single,
|
16
|
+
multi
|
17
|
+
};
|
18
|
+
|
19
|
+
// functions declarations
|
20
|
+
static void on_newobj_event(VALUE tracepoint_data, void *data);
|
9
21
|
|
10
|
-
|
22
|
+
// utility functions
|
23
|
+
static char *ruby_strndup(const char *str, size_t size)
|
11
24
|
{
|
12
25
|
char *dup;
|
13
26
|
|
@@ -18,30 +31,84 @@ char *ruby_strndup(const char *str, size_t size)
|
|
18
31
|
return dup;
|
19
32
|
}
|
20
33
|
|
34
|
+
// Equivalent to Ruby "begin/rescue nil" call, where we call a C function and
|
35
|
+
// swallow the exception if it occurs - const_source_location often fails with
|
36
|
+
// exceptions for classes that are defined in C or for anonymous classes.
|
37
|
+
static VALUE rescue_nil(VALUE (*function_to_call_safely)(VALUE), VALUE function_to_call_safely_arg)
|
38
|
+
{
|
39
|
+
int exception_state;
|
40
|
+
// rb_protect sets exception_state to non-zero if an exception occurs
|
41
|
+
// see https://github.com/ruby/ruby/blob/3219ecf4f659908674f534491d8934ba54e1143d/include/ruby/internal/intern/proc.h#L349
|
42
|
+
VALUE result = rb_protect(function_to_call_safely, function_to_call_safely_arg, &exception_state);
|
43
|
+
if (exception_state != 0)
|
44
|
+
{
|
45
|
+
return Qnil;
|
46
|
+
}
|
47
|
+
|
48
|
+
return result;
|
49
|
+
}
|
50
|
+
|
51
|
+
static int mark_key_for_gc_i(st_data_t key, st_data_t _value, st_data_t _data)
|
52
|
+
{
|
53
|
+
VALUE klass = (VALUE)key;
|
54
|
+
// mark klass link for GC as non-movable to avoid changing hashtable's keys
|
55
|
+
rb_gc_mark(klass);
|
56
|
+
return ST_CONTINUE;
|
57
|
+
}
|
58
|
+
|
21
59
|
// Data structure
|
22
60
|
struct dd_cov_data
|
23
61
|
{
|
62
|
+
// Ruby hash with filenames impacted by the test.
|
63
|
+
VALUE impacted_files;
|
64
|
+
|
65
|
+
// Root is the path to the root folder of the project under test.
|
66
|
+
// Files located outside of the root are ignored.
|
24
67
|
char *root;
|
25
68
|
long root_len;
|
26
69
|
|
70
|
+
// Ignored path contains path to the folder where bundled gems are located if
|
71
|
+
// gems are installed in the project folder.
|
27
72
|
char *ignored_path;
|
28
73
|
long ignored_path_len;
|
29
74
|
|
30
|
-
|
31
|
-
|
75
|
+
// Line tracepoint optimisation: cache last seen filename pointer to avoid
|
76
|
+
// unnecessary string comparison if we stay in the same file.
|
32
77
|
uintptr_t last_filename_ptr;
|
33
78
|
|
79
|
+
// Line tracepoint can work in two modes: single threaded and multi threaded
|
80
|
+
//
|
81
|
+
// In single threaded mode line tracepoint will only cover the thread that started the coverage.
|
82
|
+
// This mode is useful for testing frameworks that run tests in multiple threads.
|
83
|
+
// Do not use single threaded mode for Rails applications unless you know that you
|
84
|
+
// don't run any background threads.
|
85
|
+
//
|
86
|
+
// In multi threaded mode line tracepoint will cover all threads. This mode is enabled by default
|
87
|
+
// and is recommended for most applications.
|
88
|
+
enum threading_mode threading_mode;
|
34
89
|
// for single threaded mode: thread that is being covered
|
35
90
|
VALUE th_covered;
|
36
91
|
|
37
|
-
|
92
|
+
// Allocation tracing is used to track test impact for objects that do not
|
93
|
+
// contain any methods that could be covered by line tracepoint.
|
94
|
+
//
|
95
|
+
// Allocation tracing works only in multi threaded mode.
|
96
|
+
VALUE object_allocation_tracepoint;
|
97
|
+
st_table *klasses_table; // { (VALUE) -> int } hashmap with class names that were covered by allocation during the test run
|
38
98
|
};
|
39
99
|
|
40
100
|
static void dd_cov_mark(void *ptr)
|
41
101
|
{
|
42
102
|
struct dd_cov_data *dd_cov_data = ptr;
|
43
|
-
rb_gc_mark_movable(dd_cov_data->
|
103
|
+
rb_gc_mark_movable(dd_cov_data->impacted_files);
|
44
104
|
rb_gc_mark_movable(dd_cov_data->th_covered);
|
105
|
+
rb_gc_mark_movable(dd_cov_data->object_allocation_tracepoint);
|
106
|
+
|
107
|
+
// if GC starts withing dd_cov_allocate() call, klasses_table might not be initialized yet
|
108
|
+
if (dd_cov_data->klasses_table != NULL)
|
109
|
+
{
|
110
|
+
st_foreach(dd_cov_data->klasses_table, mark_key_for_gc_i, 0);
|
111
|
+
}
|
45
112
|
}
|
46
113
|
|
47
114
|
static void dd_cov_free(void *ptr)
|
@@ -49,17 +116,20 @@ static void dd_cov_free(void *ptr)
|
|
49
116
|
struct dd_cov_data *dd_cov_data = ptr;
|
50
117
|
xfree(dd_cov_data->root);
|
51
118
|
xfree(dd_cov_data->ignored_path);
|
119
|
+
st_free_table(dd_cov_data->klasses_table);
|
52
120
|
xfree(dd_cov_data);
|
53
121
|
}
|
54
122
|
|
55
123
|
static void dd_cov_compact(void *ptr)
|
56
124
|
{
|
57
125
|
struct dd_cov_data *dd_cov_data = ptr;
|
58
|
-
dd_cov_data->
|
126
|
+
dd_cov_data->impacted_files = rb_gc_location(dd_cov_data->impacted_files);
|
59
127
|
dd_cov_data->th_covered = rb_gc_location(dd_cov_data->th_covered);
|
128
|
+
dd_cov_data->object_allocation_tracepoint = rb_gc_location(dd_cov_data->object_allocation_tracepoint);
|
129
|
+
// keys for dd_cov_data->klasses_table are not moved by GC, so we don't need to update them
|
60
130
|
}
|
61
131
|
|
62
|
-
const rb_data_type_t dd_cov_data_type = {
|
132
|
+
static const rb_data_type_t dd_cov_data_type = {
|
63
133
|
.wrap_struct_name = "dd_cov",
|
64
134
|
.function = {
|
65
135
|
.dmark = dd_cov_mark,
|
@@ -71,64 +141,48 @@ const rb_data_type_t dd_cov_data_type = {
|
|
71
141
|
static VALUE dd_cov_allocate(VALUE klass)
|
72
142
|
{
|
73
143
|
struct dd_cov_data *dd_cov_data;
|
74
|
-
VALUE
|
144
|
+
VALUE dd_cov = TypedData_Make_Struct(klass, struct dd_cov_data, &dd_cov_data_type, dd_cov_data);
|
75
145
|
|
76
|
-
dd_cov_data->
|
146
|
+
dd_cov_data->impacted_files = rb_hash_new();
|
77
147
|
dd_cov_data->root = NULL;
|
78
148
|
dd_cov_data->root_len = 0;
|
79
149
|
dd_cov_data->ignored_path = NULL;
|
80
150
|
dd_cov_data->ignored_path_len = 0;
|
81
151
|
dd_cov_data->last_filename_ptr = 0;
|
82
|
-
dd_cov_data->threading_mode =
|
152
|
+
dd_cov_data->threading_mode = multi;
|
83
153
|
|
84
|
-
|
85
|
-
|
154
|
+
dd_cov_data->object_allocation_tracepoint = Qnil;
|
155
|
+
// numtable type is needed to store VALUE as a key
|
156
|
+
dd_cov_data->klasses_table = st_init_numtable();
|
86
157
|
|
87
|
-
|
88
|
-
|
89
|
-
{
|
90
|
-
VALUE opt;
|
158
|
+
return dd_cov;
|
159
|
+
}
|
91
160
|
|
92
|
-
|
93
|
-
VALUE rb_root = rb_hash_lookup(opt, ID2SYM(rb_intern("root")));
|
94
|
-
if (!RTEST(rb_root))
|
95
|
-
{
|
96
|
-
rb_raise(rb_eArgError, "root is required");
|
97
|
-
}
|
98
|
-
VALUE rb_ignored_path = rb_hash_lookup(opt, ID2SYM(rb_intern("ignored_path")));
|
161
|
+
// Helper functions (available in C only)
|
99
162
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
{
|
108
|
-
threading_mode = SINGLE_THREADED_COVERAGE_MODE;
|
109
|
-
}
|
110
|
-
else
|
163
|
+
// Checks if the filename is located under the root folder of the project (but not
|
164
|
+
// in the ignored folder) and adds it to the impacted_files hash.
|
165
|
+
static void record_impacted_file(struct dd_cov_data *dd_cov_data, VALUE filename)
|
166
|
+
{
|
167
|
+
char *filename_ptr = RSTRING_PTR(filename);
|
168
|
+
// if the current filename is not located under the root, we skip it
|
169
|
+
if (strncmp(dd_cov_data->root, filename_ptr, dd_cov_data->root_len) != 0)
|
111
170
|
{
|
112
|
-
|
171
|
+
return;
|
113
172
|
}
|
114
173
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
dd_cov_data->threading_mode = threading_mode;
|
119
|
-
dd_cov_data->root_len = RSTRING_LEN(rb_root);
|
120
|
-
dd_cov_data->root = ruby_strndup(RSTRING_PTR(rb_root), dd_cov_data->root_len);
|
121
|
-
|
122
|
-
if (RTEST(rb_ignored_path))
|
174
|
+
// if ignored_path is provided and the current filename is located under the ignored_path, we skip it too
|
175
|
+
// this is useful for ignoring bundled gems location
|
176
|
+
if (dd_cov_data->ignored_path_len != 0 && strncmp(dd_cov_data->ignored_path, filename_ptr, dd_cov_data->ignored_path_len) == 0)
|
123
177
|
{
|
124
|
-
|
125
|
-
dd_cov_data->ignored_path = ruby_strndup(RSTRING_PTR(rb_ignored_path), dd_cov_data->ignored_path_len);
|
178
|
+
return;
|
126
179
|
}
|
127
180
|
|
128
|
-
|
181
|
+
rb_hash_aset(dd_cov_data->impacted_files, filename, Qtrue);
|
129
182
|
}
|
130
183
|
|
131
|
-
|
184
|
+
// Executed on RUBY_EVENT_LINE event and captures the filename from rb_profile_frames.
|
185
|
+
static void on_line_event(rb_event_flag_t event, VALUE data, VALUE self, ID id, VALUE klass)
|
132
186
|
{
|
133
187
|
struct dd_cov_data *dd_cov_data;
|
134
188
|
TypedData_Get_Struct(data, struct dd_cov_data, &dd_cov_data_type, dd_cov_data);
|
@@ -161,54 +215,182 @@ static void dd_cov_update_coverage(rb_event_flag_t event, VALUE data, VALUE self
|
|
161
215
|
return;
|
162
216
|
}
|
163
217
|
|
164
|
-
|
165
|
-
|
166
|
-
|
218
|
+
record_impacted_file(dd_cov_data, filename);
|
219
|
+
}
|
220
|
+
|
221
|
+
// Get source location for a given class name
|
222
|
+
static VALUE get_source_location(VALUE klass_name)
|
223
|
+
{
|
224
|
+
return rb_funcall(rb_cObject, rb_intern("const_source_location"), 1, klass_name);
|
225
|
+
}
|
226
|
+
|
227
|
+
// Get source location for a given class name and swallow any exceptions
|
228
|
+
static VALUE safely_get_source_location(VALUE klass_name)
|
229
|
+
{
|
230
|
+
return rescue_nil(get_source_location, klass_name);
|
231
|
+
}
|
232
|
+
|
233
|
+
// This function is called for each class that was instantiated during the test run.
|
234
|
+
static int process_instantiated_klass(st_data_t key, st_data_t _value, st_data_t data)
|
235
|
+
{
|
236
|
+
VALUE klass = (VALUE)key;
|
237
|
+
struct dd_cov_data *dd_cov_data = (struct dd_cov_data *)data;
|
238
|
+
|
239
|
+
VALUE klass_name = rb_class_name(klass);
|
240
|
+
if (klass_name == Qnil)
|
241
|
+
{
|
242
|
+
return ST_CONTINUE;
|
243
|
+
}
|
244
|
+
|
245
|
+
VALUE source_location = safely_get_source_location(klass_name);
|
246
|
+
if (source_location == Qnil || RARRAY_LEN(source_location) == 0)
|
247
|
+
{
|
248
|
+
return ST_CONTINUE;
|
249
|
+
}
|
250
|
+
|
251
|
+
VALUE filename = RARRAY_AREF(source_location, 0);
|
252
|
+
if (filename == Qnil)
|
253
|
+
{
|
254
|
+
return ST_CONTINUE;
|
255
|
+
}
|
256
|
+
|
257
|
+
record_impacted_file(dd_cov_data, filename);
|
258
|
+
return ST_CONTINUE;
|
259
|
+
}
|
260
|
+
|
261
|
+
// Executed on RUBY_INTERNAL_EVENT_NEWOBJ event and captures the source file for the
|
262
|
+
// allocated object's class.
|
263
|
+
static void on_newobj_event(VALUE tracepoint_data, void *data)
|
264
|
+
{
|
265
|
+
rb_trace_arg_t *tracearg = rb_tracearg_from_tracepoint(tracepoint_data);
|
266
|
+
VALUE new_object = rb_tracearg_object(tracearg);
|
267
|
+
|
268
|
+
// To keep things fast and practical, we only care about objects that extend
|
269
|
+
// either Object or Struct.
|
270
|
+
enum ruby_value_type type = rb_type(new_object);
|
271
|
+
if (type != RUBY_T_OBJECT && type != RUBY_T_STRUCT)
|
167
272
|
{
|
168
273
|
return;
|
169
274
|
}
|
170
275
|
|
171
|
-
|
172
|
-
|
173
|
-
|
276
|
+
VALUE klass = rb_class_of(new_object);
|
277
|
+
if (klass == Qnil || klass == 0)
|
278
|
+
{
|
279
|
+
return;
|
280
|
+
}
|
281
|
+
// Skip anonymous classes starting with "#<Class".
|
282
|
+
// it allows us to skip the source location lookup that will always fail
|
283
|
+
//
|
284
|
+
// rb_mod_name returns nil for anonymous classes
|
285
|
+
if (rb_mod_name(klass) == Qnil)
|
174
286
|
{
|
175
287
|
return;
|
176
288
|
}
|
177
289
|
|
178
|
-
|
290
|
+
struct dd_cov_data *dd_cov_data = (struct dd_cov_data *)data;
|
291
|
+
|
292
|
+
// We use VALUE directly as a key for the hashmap
|
293
|
+
// Ruby itself does it too:
|
294
|
+
// https://github.com/ruby/ruby/blob/94b87084a689a3bc732dcaee744508a708223d6c/ext/objspace/object_tracing.c#L113
|
295
|
+
st_insert(dd_cov_data->klasses_table, (st_data_t)klass, 1);
|
179
296
|
}
|
180
297
|
|
181
|
-
|
298
|
+
// DDCov instance methods available in Ruby
|
299
|
+
static VALUE dd_cov_initialize(int argc, VALUE *argv, VALUE self)
|
182
300
|
{
|
301
|
+
VALUE opt;
|
302
|
+
|
303
|
+
rb_scan_args(argc, argv, "10", &opt);
|
304
|
+
VALUE rb_root = rb_hash_lookup(opt, ID2SYM(rb_intern("root")));
|
305
|
+
if (!RTEST(rb_root))
|
306
|
+
{
|
307
|
+
rb_raise(rb_eArgError, "root is required");
|
308
|
+
}
|
309
|
+
VALUE rb_ignored_path = rb_hash_lookup(opt, ID2SYM(rb_intern("ignored_path")));
|
310
|
+
|
311
|
+
VALUE rb_threading_mode = rb_hash_lookup(opt, ID2SYM(rb_intern("threading_mode")));
|
312
|
+
enum threading_mode threading_mode;
|
313
|
+
if (rb_threading_mode == ID2SYM(rb_intern("multi")))
|
314
|
+
{
|
315
|
+
threading_mode = multi;
|
316
|
+
}
|
317
|
+
else if (rb_threading_mode == ID2SYM(rb_intern("single")))
|
318
|
+
{
|
319
|
+
threading_mode = single;
|
320
|
+
}
|
321
|
+
else
|
322
|
+
{
|
323
|
+
rb_raise(rb_eArgError, "threading mode is invalid");
|
324
|
+
}
|
325
|
+
|
326
|
+
VALUE rb_allocation_tracing_enabled = rb_hash_lookup(opt, ID2SYM(rb_intern("use_allocation_tracing")));
|
327
|
+
if (rb_allocation_tracing_enabled == Qtrue && threading_mode == single)
|
328
|
+
{
|
329
|
+
rb_raise(rb_eArgError, "allocation tracing is not supported in single threaded mode");
|
330
|
+
}
|
183
331
|
|
184
332
|
struct dd_cov_data *dd_cov_data;
|
185
333
|
TypedData_Get_Struct(self, struct dd_cov_data, &dd_cov_data_type, dd_cov_data);
|
186
334
|
|
335
|
+
dd_cov_data->threading_mode = threading_mode;
|
336
|
+
dd_cov_data->root_len = RSTRING_LEN(rb_root);
|
337
|
+
dd_cov_data->root = ruby_strndup(RSTRING_PTR(rb_root), dd_cov_data->root_len);
|
338
|
+
|
339
|
+
if (RTEST(rb_ignored_path))
|
340
|
+
{
|
341
|
+
dd_cov_data->ignored_path_len = RSTRING_LEN(rb_ignored_path);
|
342
|
+
dd_cov_data->ignored_path = ruby_strndup(RSTRING_PTR(rb_ignored_path), dd_cov_data->ignored_path_len);
|
343
|
+
}
|
344
|
+
|
345
|
+
if (rb_allocation_tracing_enabled == Qtrue)
|
346
|
+
{
|
347
|
+
dd_cov_data->object_allocation_tracepoint = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, on_newobj_event, (void *)dd_cov_data);
|
348
|
+
}
|
349
|
+
|
350
|
+
return Qnil;
|
351
|
+
}
|
352
|
+
|
353
|
+
// starts test impact collection, executed before the start of each test
|
354
|
+
static VALUE dd_cov_start(VALUE self)
|
355
|
+
{
|
356
|
+
struct dd_cov_data *dd_cov_data;
|
357
|
+
TypedData_Get_Struct(self, struct dd_cov_data, &dd_cov_data_type, dd_cov_data);
|
358
|
+
|
187
359
|
if (dd_cov_data->root_len == 0)
|
188
360
|
{
|
189
361
|
rb_raise(rb_eRuntimeError, "root is required");
|
190
362
|
}
|
191
363
|
|
192
|
-
|
364
|
+
// add line tracepoint
|
365
|
+
if (dd_cov_data->threading_mode == single)
|
193
366
|
{
|
194
367
|
VALUE thval = rb_thread_current();
|
195
|
-
rb_thread_add_event_hook(thval,
|
368
|
+
rb_thread_add_event_hook(thval, on_line_event, RUBY_EVENT_LINE, self);
|
196
369
|
dd_cov_data->th_covered = thval;
|
197
370
|
}
|
198
371
|
else
|
199
372
|
{
|
200
|
-
rb_add_event_hook(
|
373
|
+
rb_add_event_hook(on_line_event, RUBY_EVENT_LINE, self);
|
374
|
+
}
|
375
|
+
|
376
|
+
// add object allocation tracepoint
|
377
|
+
if (dd_cov_data->object_allocation_tracepoint != Qnil)
|
378
|
+
{
|
379
|
+
rb_tracepoint_enable(dd_cov_data->object_allocation_tracepoint);
|
201
380
|
}
|
202
381
|
|
203
382
|
return self;
|
204
383
|
}
|
205
384
|
|
385
|
+
// stops test impact collection, executed after the end of each test
|
386
|
+
// returns the hash with impacted files and resets the internal state
|
206
387
|
static VALUE dd_cov_stop(VALUE self)
|
207
388
|
{
|
208
389
|
struct dd_cov_data *dd_cov_data;
|
209
390
|
TypedData_Get_Struct(self, struct dd_cov_data, &dd_cov_data_type, dd_cov_data);
|
210
391
|
|
211
|
-
|
392
|
+
// stop line tracepoint
|
393
|
+
if (dd_cov_data->threading_mode == single)
|
212
394
|
{
|
213
395
|
VALUE thval = rb_thread_current();
|
214
396
|
if (!rb_equal(thval, dd_cov_data->th_covered))
|
@@ -216,17 +398,27 @@ static VALUE dd_cov_stop(VALUE self)
|
|
216
398
|
rb_raise(rb_eRuntimeError, "Coverage was not started by this thread");
|
217
399
|
}
|
218
400
|
|
219
|
-
rb_thread_remove_event_hook(dd_cov_data->th_covered,
|
401
|
+
rb_thread_remove_event_hook(dd_cov_data->th_covered, on_line_event);
|
220
402
|
dd_cov_data->th_covered = Qnil;
|
221
403
|
}
|
222
404
|
else
|
223
405
|
{
|
224
|
-
rb_remove_event_hook(
|
406
|
+
rb_remove_event_hook(on_line_event);
|
225
407
|
}
|
226
408
|
|
227
|
-
|
409
|
+
// stop object allocation tracepoint
|
410
|
+
if (dd_cov_data->object_allocation_tracepoint != Qnil)
|
411
|
+
{
|
412
|
+
rb_tracepoint_disable(dd_cov_data->object_allocation_tracepoint);
|
413
|
+
}
|
414
|
+
|
415
|
+
// process classes covered by allocation tracing
|
416
|
+
st_foreach(dd_cov_data->klasses_table, process_instantiated_klass, (st_data_t)dd_cov_data);
|
417
|
+
st_clear(dd_cov_data->klasses_table);
|
418
|
+
|
419
|
+
VALUE res = dd_cov_data->impacted_files;
|
228
420
|
|
229
|
-
dd_cov_data->
|
421
|
+
dd_cov_data->impacted_files = rb_hash_new();
|
230
422
|
dd_cov_data->last_filename_ptr = 0;
|
231
423
|
|
232
424
|
return res;
|
@@ -236,8 +428,8 @@ void Init_datadog_cov(void)
|
|
236
428
|
{
|
237
429
|
VALUE mDatadog = rb_define_module("Datadog");
|
238
430
|
VALUE mCI = rb_define_module_under(mDatadog, "CI");
|
239
|
-
VALUE
|
240
|
-
VALUE mCoverage = rb_define_module_under(
|
431
|
+
VALUE mTestOptimisation = rb_define_module_under(mCI, "TestOptimisation");
|
432
|
+
VALUE mCoverage = rb_define_module_under(mTestOptimisation, "Coverage");
|
241
433
|
VALUE cDatadogCov = rb_define_class_under(mCoverage, "DDCov", rb_cObject);
|
242
434
|
|
243
435
|
rb_define_alloc_func(cDatadogCov, dd_cov_allocate);
|