datadog-ci 1.0.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|