fast_cov 0.3.0 → 0.3.1
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/ext/fast_cov/fast_cov.c +0 -78
- data/lib/fast_cov/trackers/abstract_tracker.rb +8 -4
- data/lib/fast_cov/trackers/const_get_tracker.rb +1 -2
- data/lib/fast_cov/trackers/factory_bot_tracker.rb +1 -1
- data/lib/fast_cov/trackers/file_tracker.rb +32 -7
- data/lib/fast_cov/utils.rb +44 -0
- data/lib/fast_cov/version.rb +1 -1
- data/lib/fast_cov.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ba4073b332a0c7f0837d5cf954b9ad98353721e6350c24969541819a42a27c65
|
|
4
|
+
data.tar.gz: c85de804a17e0f1bd7b21a370e61dbe0d59dc2bb5ce02d84f827c500d6816b5a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f080c26c0a0813a10555885ac162e7662519641501d5d499bebbb1315414ba3469770ac66d95576d6209cc7e9c8b4181fa43c6a729c3d4ccd2c282a1394a86f6
|
|
7
|
+
data.tar.gz: 0ff5f9c3053015028416a2d5190785542abf1bfe0ce6fe2c0d8dae5ab16e555419f5a8b350980119ebf87065b1166b5f700f6131c582c4df9c36b5eb3a0b5f8e
|
data/ext/fast_cov/fast_cov.c
CHANGED
|
@@ -140,80 +140,6 @@ static void on_line_event(rb_event_flag_t event, VALUE self_data, VALUE self,
|
|
|
140
140
|
record_impacted_file(data, filename);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
// ---- Utils module methods (FastCov::Utils) ------------------------------
|
|
144
|
-
|
|
145
|
-
// Utils.path_within?(path, directory) -> true/false
|
|
146
|
-
// Check if path is within directory, correctly handling:
|
|
147
|
-
// - Trailing slashes on directory
|
|
148
|
-
// - Sibling directories with longer names (e.g., /a/b/c vs /a/b/cd)
|
|
149
|
-
static VALUE utils_path_within(VALUE self, VALUE path, VALUE directory) {
|
|
150
|
-
Check_Type(path, T_STRING);
|
|
151
|
-
Check_Type(directory, T_STRING);
|
|
152
|
-
|
|
153
|
-
// Freeze strings to prevent GC compaction from moving them
|
|
154
|
-
rb_str_freeze(path);
|
|
155
|
-
rb_str_freeze(directory);
|
|
156
|
-
|
|
157
|
-
bool result = fast_cov_is_within_root(
|
|
158
|
-
RSTRING_PTR(path), RSTRING_LEN(path),
|
|
159
|
-
RSTRING_PTR(directory), RSTRING_LEN(directory));
|
|
160
|
-
|
|
161
|
-
return result ? Qtrue : Qfalse;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Utils.relativize_paths(set, root) -> set
|
|
165
|
-
// Mutates set in place: converts absolute paths to relative paths from root.
|
|
166
|
-
// Paths not within root are left unchanged.
|
|
167
|
-
static VALUE utils_relativize_paths(VALUE self, VALUE set, VALUE root) {
|
|
168
|
-
Check_Type(root, T_STRING);
|
|
169
|
-
|
|
170
|
-
// Freeze root to prevent GC from moving it during compaction
|
|
171
|
-
rb_str_freeze(root);
|
|
172
|
-
|
|
173
|
-
const char *root_ptr = RSTRING_PTR(root);
|
|
174
|
-
long root_len = RSTRING_LEN(root);
|
|
175
|
-
|
|
176
|
-
// Normalize: strip trailing slash for offset calculation
|
|
177
|
-
long effective_root_len = root_len;
|
|
178
|
-
if (effective_root_len > 0 && root_ptr[effective_root_len - 1] == '/') {
|
|
179
|
-
effective_root_len--;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Collect paths to transform (can't modify set while iterating)
|
|
183
|
-
VALUE paths = rb_funcall(set, rb_intern("to_a"), 0);
|
|
184
|
-
long num_paths = RARRAY_LEN(paths);
|
|
185
|
-
|
|
186
|
-
for (long i = 0; i < num_paths; i++) {
|
|
187
|
-
VALUE abs_path = rb_ary_entry(paths, i);
|
|
188
|
-
if (!RB_TYPE_P(abs_path, T_STRING)) continue;
|
|
189
|
-
|
|
190
|
-
// Freeze to prevent GC moving it
|
|
191
|
-
rb_str_freeze(abs_path);
|
|
192
|
-
|
|
193
|
-
const char *path_ptr = RSTRING_PTR(abs_path);
|
|
194
|
-
long path_len = RSTRING_LEN(abs_path);
|
|
195
|
-
|
|
196
|
-
// Use proper within_root check
|
|
197
|
-
if (!fast_cov_is_within_root(path_ptr, path_len, root_ptr, root_len)) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Calculate offset (skip root + separator)
|
|
202
|
-
long offset = effective_root_len;
|
|
203
|
-
if (offset < path_len && path_ptr[offset] == '/') offset++;
|
|
204
|
-
|
|
205
|
-
// Create relative path
|
|
206
|
-
VALUE rel_path = rb_str_substr(abs_path, offset, path_len - offset);
|
|
207
|
-
|
|
208
|
-
// Delete old path, add new path
|
|
209
|
-
rb_funcall(set, rb_intern("delete"), 1, abs_path);
|
|
210
|
-
rb_funcall(set, rb_intern("add"), 1, rel_path);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
RB_GC_GUARD(paths);
|
|
214
|
-
return set;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
143
|
// ---- Ruby instance methods ----------------------------------------------
|
|
218
144
|
|
|
219
145
|
static VALUE fast_cov_initialize(int argc, VALUE *argv, VALUE self) {
|
|
@@ -366,8 +292,4 @@ void Init_fast_cov(void) {
|
|
|
366
292
|
rb_define_method(cCoverage, "start", fast_cov_start, 0);
|
|
367
293
|
rb_define_method(cCoverage, "stop", fast_cov_stop, 0);
|
|
368
294
|
|
|
369
|
-
// FastCov::Utils module (C-defined methods)
|
|
370
|
-
VALUE mUtils = rb_define_module_under(mFastCov, "Utils");
|
|
371
|
-
rb_define_module_function(mUtils, "path_within?", utils_path_within, 2);
|
|
372
|
-
rb_define_module_function(mUtils, "relativize_paths", utils_relativize_paths, 2);
|
|
373
295
|
}
|
|
@@ -21,6 +21,10 @@ module FastCov
|
|
|
21
21
|
@started_thread = nil
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
def root
|
|
25
|
+
@coverage_map.root
|
|
26
|
+
end
|
|
27
|
+
|
|
24
28
|
# Public API - called by FastCov framework
|
|
25
29
|
|
|
26
30
|
def start
|
|
@@ -70,12 +74,12 @@ module FastCov
|
|
|
70
74
|
class << self
|
|
71
75
|
attr_accessor :active
|
|
72
76
|
|
|
73
|
-
def record(to: nil)
|
|
77
|
+
def record(path, to: nil)
|
|
74
78
|
return unless active
|
|
75
|
-
return unless
|
|
79
|
+
return unless path
|
|
76
80
|
|
|
77
|
-
|
|
78
|
-
active.record(path, to: to)
|
|
81
|
+
to ||= Utils.resolve_caller(caller_locations(1, 20), active.root)
|
|
82
|
+
active.record(path, to: to)
|
|
79
83
|
end
|
|
80
84
|
|
|
81
85
|
def reset
|
|
@@ -23,9 +23,8 @@ module FastCov
|
|
|
23
23
|
|
|
24
24
|
module ConstGetPatch
|
|
25
25
|
def const_get(name, inherit = true)
|
|
26
|
-
source = caller_locations(1, 1).first&.absolute_path
|
|
27
26
|
result = super
|
|
28
|
-
FastCov::ConstGetTracker.record(
|
|
27
|
+
FastCov::ConstGetTracker.record(const_source_location(name, inherit)&.first)
|
|
29
28
|
result
|
|
30
29
|
end
|
|
31
30
|
end
|
|
@@ -4,21 +4,27 @@ require_relative "abstract_tracker"
|
|
|
4
4
|
|
|
5
5
|
module FastCov
|
|
6
6
|
# Tracks files read from disk during coverage (JSON, YAML, .rb templates, etc.)
|
|
7
|
-
# via File.read
|
|
7
|
+
# via File.read, File.open, and YAML load methods.
|
|
8
|
+
#
|
|
9
|
+
# YAML methods are patched separately because Bootsnap's compile cache
|
|
10
|
+
# overrides YAML.load_file/safe_load_file to bypass File.open entirely.
|
|
8
11
|
#
|
|
9
12
|
# Register via: coverage_map.use(FastCov::FileTracker)
|
|
10
13
|
class FileTracker < AbstractTracker
|
|
11
14
|
def install
|
|
12
|
-
|
|
15
|
+
unless File.singleton_class.ancestors.include?(FilePatch)
|
|
16
|
+
File.singleton_class.prepend(FilePatch)
|
|
17
|
+
end
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
if defined?(::YAML) && !::YAML.singleton_class.ancestors.include?(YamlPatch)
|
|
20
|
+
::YAML.singleton_class.prepend(YamlPatch)
|
|
21
|
+
end
|
|
15
22
|
end
|
|
16
23
|
|
|
17
24
|
module FilePatch
|
|
18
25
|
def read(name, *args, **kwargs, &block)
|
|
19
|
-
source = caller_locations(1, 1).first&.absolute_path
|
|
20
26
|
super.tap do
|
|
21
|
-
FastCov::FileTracker.record(
|
|
27
|
+
FastCov::FileTracker.record(File.expand_path(name))
|
|
22
28
|
end
|
|
23
29
|
end
|
|
24
30
|
|
|
@@ -26,9 +32,28 @@ module FastCov
|
|
|
26
32
|
mode = args[0]
|
|
27
33
|
is_read = mode.nil? || (mode.is_a?(String) && mode.start_with?("r")) ||
|
|
28
34
|
(mode.is_a?(Integer) && (mode & (File::WRONLY | File::RDWR)).zero?)
|
|
29
|
-
source = caller_locations(1, 1).first&.absolute_path
|
|
30
35
|
super.tap do
|
|
31
|
-
FastCov::FileTracker.record(
|
|
36
|
+
FastCov::FileTracker.record(File.expand_path(name)) if is_read
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module YamlPatch
|
|
42
|
+
def load_file(path, *args, **kwargs)
|
|
43
|
+
super.tap do
|
|
44
|
+
FastCov::FileTracker.record(File.expand_path(path))
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def safe_load_file(path, *args, **kwargs)
|
|
49
|
+
super.tap do
|
|
50
|
+
FastCov::FileTracker.record(File.expand_path(path))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def unsafe_load_file(path, *args, **kwargs)
|
|
55
|
+
super.tap do
|
|
56
|
+
FastCov::FileTracker.record(File.expand_path(path))
|
|
32
57
|
end
|
|
33
58
|
end
|
|
34
59
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FastCov
|
|
4
|
+
module Utils
|
|
5
|
+
# Check if path is within directory, correctly handling:
|
|
6
|
+
# - Trailing slashes on directory
|
|
7
|
+
# - Sibling directories with longer names (e.g., /a/b/c vs /a/b/cd)
|
|
8
|
+
def self.path_within?(path, directory)
|
|
9
|
+
dir = directory.end_with?("/") ? directory.chop : directory
|
|
10
|
+
return true if path == dir
|
|
11
|
+
|
|
12
|
+
path.start_with?("#{dir}/")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Mutates set in place: converts absolute paths to relative paths from root.
|
|
16
|
+
# Paths not within root are left unchanged.
|
|
17
|
+
def self.relativize_paths(set, root)
|
|
18
|
+
prefix = root.end_with?("/") ? root : "#{root}/"
|
|
19
|
+
|
|
20
|
+
set.to_a.each do |abs_path|
|
|
21
|
+
next unless abs_path.is_a?(String)
|
|
22
|
+
next unless abs_path.start_with?(prefix) || abs_path == root.chomp("/")
|
|
23
|
+
|
|
24
|
+
set.delete(abs_path)
|
|
25
|
+
set.add(abs_path.delete_prefix(prefix))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
set
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Walk caller locations to find the first frame whose file is within root.
|
|
32
|
+
# Handles indirect calls (e.g., YAML.load_file -> File.open) where the
|
|
33
|
+
# immediate caller is a stdlib/gem file outside the project.
|
|
34
|
+
def self.resolve_caller(locations, root)
|
|
35
|
+
locations.each do |loc|
|
|
36
|
+
path = loc.absolute_path
|
|
37
|
+
next unless path
|
|
38
|
+
|
|
39
|
+
return path if path_within?(path, root)
|
|
40
|
+
end
|
|
41
|
+
nil
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
data/lib/fast_cov/version.rb
CHANGED
data/lib/fast_cov.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "fast_cov/fast_cov.#{RUBY_VERSION}"
|
|
4
4
|
|
|
5
5
|
module FastCov
|
|
6
|
+
autoload :Utils, File.expand_path("fast_cov/utils", __dir__)
|
|
6
7
|
autoload :VERSION, File.expand_path("fast_cov/version", __dir__)
|
|
7
8
|
autoload :ConnectedDependencies, File.expand_path("fast_cov/connected_dependencies", __dir__)
|
|
8
9
|
autoload :CoverageMap, File.expand_path("fast_cov/coverage_map", __dir__)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fast_cov
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ngan Pham
|
|
@@ -91,6 +91,7 @@ files:
|
|
|
91
91
|
- lib/fast_cov/trackers/const_get_tracker.rb
|
|
92
92
|
- lib/fast_cov/trackers/factory_bot_tracker.rb
|
|
93
93
|
- lib/fast_cov/trackers/file_tracker.rb
|
|
94
|
+
- lib/fast_cov/utils.rb
|
|
94
95
|
- lib/fast_cov/version.rb
|
|
95
96
|
homepage: https://github.com/Gusto/fast_cov
|
|
96
97
|
licenses:
|