Module: Verity::Fingerprint

Defined in:
lib/verity/fingerprint.rb

Overview

Public: Content-addressed fingerprinting for test bodies. Parses source files with Prism to produce stable identifiers that survive line-number shifts when unrelated code changes, while disambiguating tests with identical bodies by appending the line number.

Constant Summary collapse

HEX_LENGTH =
16
THREAD_KEY =
:__verity_fp_plan__

Class Method Summary collapse

Class Method Details

.clear_plan!Object

Internal: Remove the current thread’s fingerprint plan. Called after a test file finishes loading.

Returns nil.



32
33
34
# File 'lib/verity/fingerprint.rb', line 32

def clear_plan!
  Thread.current[THREAD_KEY] = nil
end

.derive_method_suffix(fingerprint) ⇒ Object

Raises:

  • (ArgumentError)


98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/verity/fingerprint.rb', line 98

def derive_method_suffix(fingerprint)
  parts = fingerprint.split(":")
  hex =
    if parts.size >= 3 && parts.last.match?(/\A\d+\z/)
      parts[-2]
    else
      parts[-1]
    end
  raise ArgumentError, "invalid fingerprint (expected ...:#{HEX_LENGTH} hex chars): #{fingerprint}" unless /\A[a-f0-9]{#{HEX_LENGTH}}\z/.match?(hex)

  hex
end

.fallback_fingerprint(file, line) ⇒ Object

Public: Generate a location-based fingerprint when the Prism plan does not cover a given line (e.g. dynamically generated tests).

file - String file path. line - Integer line number.

Returns a String in the form “relative/path:hex”.



54
55
56
57
58
# File 'lib/verity/fingerprint.rb', line 54

def fallback_fingerprint(file, line)
  rel = relative_source_path(file)
  sha = Digest::SHA1.hexdigest("#{file}:#{line}")[0, HEX_LENGTH]
  "#{rel}:#{sha}"
end

.install_plan!(absolute_path) ⇒ Object

Internal: Parse the given source file and install its line-to-fingerprint mapping on the current thread. Must be called before loading a test file.

absolute_path - String absolute filesystem path to the source file.

Returns the plan Hash (line => fingerprint).



24
25
26
# File 'lib/verity/fingerprint.rb', line 24

def install_plan!(absolute_path)
  Thread.current[THREAD_KEY] = plan_file(absolute_path)
end

.lookup(line) ⇒ Object

Internal: Look up the fingerprint for a source line from the current thread’s installed plan.

line - Integer source line number.

Returns a String fingerprint, or nil if no plan is active or the line has no entry.



43
44
45
# File 'lib/verity/fingerprint.rb', line 43

def lookup(line)
  Thread.current[THREAD_KEY]&.[](line)
end

.plan_file(absolute_path) ⇒ Object

Internal: Parse a source file and build a Hash mapping each ‘test` call’s line number to a content-addressed fingerprint string. Duplicate body hashes within the same file are disambiguated by appending the line number.

absolute_path - String absolute path to the Ruby source file.

Returns a Hash { Integer => String }.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/verity/fingerprint.rb', line 68

def plan_file(absolute_path)
  source = File.read(absolute_path, encoding: "UTF-8")
  result = Prism.parse(source, filepath: File.expand_path(absolute_path))
  return {} unless result.success?

  program = result.value
  rows = []
  each_load_time_test(program) do |call|
    body = call.block.body
    canon = canonical(body)
    body_hex = Digest::SHA1.hexdigest(canon)[0, HEX_LENGTH]
    rows << { line: call.location.start_line, body_hex: body_hex }
  end

  relative = relative_source_path(absolute_path)
  by_hex = rows.group_by { _1[:body_hex] }
  plan = {}
  rows.each do |row|
    line = row[:line]
    body_hex = row[:body_hex]
    plan[line] =
      if by_hex[body_hex].length > 1
        "#{relative}:#{body_hex}:#{line}"
      else
        "#{relative}:#{body_hex}"
      end
  end
  plan
end