spectracer 1.2.1 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 69f91e79d4902946517d095ed052d4f5780e7d6be4a433fea8c82bee46d54f78
4
- data.tar.gz: fe1abc2750f426935d646d8f3000f37b759f7a0ec7d1a24ae7ffb6fc19501a7f
3
+ metadata.gz: fbfa5429478965d82098a285c1c6319df7ec0354dc01f5797acb4c7618412bf0
4
+ data.tar.gz: 35fdb890ce1c67257a0cb41cb09a4bfd4f5479c428cc2f477484ba2afb90f919
5
5
  SHA512:
6
- metadata.gz: be6ecf26e9ebfc694d2df58b16fd8f86cf86a0ecc7d06cef002464cbc082d77833d6c64ecb03b3073564114d22dc0e3a1fe16068a598885dd852eaab346f617e
7
- data.tar.gz: 5d6aaa61ab88c7501cfce960aae3be010dc04f9202b741ff40a4035e3aea5359fe822ba96a1bccc26ee90f8d5f09e6148a4cd1bf8191abdec093850e6bb320c6
6
+ metadata.gz: 973f20fc2e7168cb2af8bb865ab5740a590b189dadb8f94635dfe9a4f3c68b033f42b983c15799fe97e84078da40c7d482926d26e9a2faf230e9f2991f4c1db7
7
+ data.tar.gz: ff5a83f41d30a6038ef597b559585897c11c30fbeb2e396a077984fcef7e04f26b9aad4e8718e8fbc4c516e38034df27b18646937041adda63db9f50f5afa9ef
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.3.0] - 2026-02-02
4
+
5
+ ### Added
6
+
7
+ - GlobFactorizer to reduce redundant spec patterns before running tests
8
+ - Removes concrete files already matched by broader globs
9
+ - Eliminates narrower globs subsumed by broader ones
10
+ - Example: `{spec/user/**/*_spec.rb, spec/user/**/one_spec.rb}` → `{spec/user/**/*_spec.rb}`
11
+
3
12
  ## [1.2.0] - 2026-02-02
4
13
 
5
14
  ### Added
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spectracer
4
+ module Core
5
+ class GlobFactorizer
6
+ def call(patterns)
7
+ return patterns.to_a if patterns.size <= 1
8
+
9
+ patterns_array = patterns.to_a.uniq
10
+ globs, files = patterns_array.partition { |p| glob_pattern?(p) }
11
+
12
+ # Remove files that are matched by any glob
13
+ remaining_files = files.reject do |file|
14
+ globs.any? { |glob| File.fnmatch?(glob, file, File::FNM_PATHNAME) }
15
+ end
16
+
17
+ # Remove globs subsumed by other globs (broader patterns win)
18
+ remaining_globs = globs.reject do |glob|
19
+ globs.any? { |other| other != glob && subsumes?(other, glob) }
20
+ end
21
+
22
+ remaining_globs + remaining_files
23
+ end
24
+
25
+ private
26
+
27
+ def glob_pattern?(pattern)
28
+ pattern.include?("*") || pattern.include?("?") || pattern.include?("[")
29
+ end
30
+
31
+ def subsumes?(broader, narrower)
32
+ return false if broader == narrower
33
+
34
+ # Convert the narrower glob to a concrete-ish path by replacing wildcards
35
+ # then check if the broader glob would match it
36
+ test_path = narrower
37
+ .gsub("**", "any/nested/path")
38
+ .gsub("*", "placeholder")
39
+
40
+ File.fnmatch?(broader, test_path, File::FNM_PATHNAME)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -5,6 +5,10 @@ module Spectracer
5
5
  class SpecSelector
6
6
  Result = Struct.new(:specs, :file_to_specs_map, keyword_init: true)
7
7
 
8
+ def initialize(factorizer: GlobFactorizer.new)
9
+ @factorizer = factorizer
10
+ end
11
+
8
12
  def call(changed_files:, inverse_deps:, globs:, on_empty:)
9
13
  spec_set = Set.new
10
14
  file_to_specs = {}
@@ -34,7 +38,7 @@ module Spectracer
34
38
  file_to_specs[file] = matched_specs.uniq.sort unless matched_specs.empty?
35
39
  end
36
40
 
37
- files = spec_set.to_a.sort
41
+ files = @factorizer.call(spec_set).sort
38
42
  specs_result = files.empty? ? on_empty : files.join(",")
39
43
 
40
44
  Result.new(specs: specs_result, file_to_specs_map: file_to_specs)
@@ -24,7 +24,9 @@ module Spectracer
24
24
 
25
25
  inverse_deps.each_value(&:sort!)
26
26
 
27
- @store.write(inverse_deps, @paths.collected_dependencies_file)
27
+ output_path = @paths.collected_dependencies_file
28
+ @logger&.debug("Writing collected dependencies to: #{output_path}")
29
+ @store.write(inverse_deps, output_path)
28
30
 
29
31
  nil
30
32
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Spectracer
4
- VERSION = "1.2.1"
4
+ VERSION = "1.3.0"
5
5
  end
data/lib/spectracer.rb CHANGED
@@ -5,6 +5,7 @@ require_relative "spectracer/logger"
5
5
 
6
6
  require_relative "spectracer/core/paths"
7
7
  require_relative "spectracer/core/path_filter"
8
+ require_relative "spectracer/core/glob_factorizer"
8
9
  require_relative "spectracer/core/spec_selector"
9
10
 
10
11
  require_relative "spectracer/io/command_runner"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spectracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mitch Smith
@@ -38,6 +38,7 @@ files:
38
38
  - README.md
39
39
  - lib/spectracer.default.yml
40
40
  - lib/spectracer.rb
41
+ - lib/spectracer/core/glob_factorizer.rb
41
42
  - lib/spectracer/core/path_filter.rb
42
43
  - lib/spectracer/core/paths.rb
43
44
  - lib/spectracer/core/spec_selector.rb