minitest-holdify 1.1.0 → 1.1.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/lib/holdify/hold.rb +15 -26
- data/lib/holdify/ledger.rb +60 -0
- data/lib/holdify/pretty.rb +3 -3
- data/lib/holdify/source.rb +32 -0
- data/lib/holdify/store.rb +10 -58
- data/lib/holdify.rb +2 -2
- data/lib/minitest/holdify_plugin.rb +7 -5
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48b28eeeaa320f75630822394f0f4b91a3025e081f085e39dee88ce264a5b2ab
|
|
4
|
+
data.tar.gz: fbc6c3b837a70c44d7359495c1e58809376022a6a458f504db4d7f06ff8585ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0accd428f401a337bbdbd8f9537b952c42e40508425d3353213b9795d2b0ecd4b617087d41f0abb79af508e156e7aa7149a6fe30e9cdff7b135c062f3b3caf32
|
|
7
|
+
data.tar.gz: c71c3caff7e00b1f8fb40ec5e871d3697cec7925a76559b1c8064a512bcc5a9285c150836a013edfa3c33325da9e3256096dfc27161251467ca817cfffbf8c19
|
data/lib/holdify/hold.rb
CHANGED
|
@@ -9,50 +9,39 @@ module Holdify
|
|
|
9
9
|
@test = test
|
|
10
10
|
@path, = test.method(test.name).source_location
|
|
11
11
|
@store = Holdify.stores[@path] ||= Store.new(@path)
|
|
12
|
-
@session = Hash.new { |h, k| h[k] = [] } # {
|
|
13
|
-
@forced = [] # [ "file:
|
|
14
|
-
@added = [] # [ "file:
|
|
15
|
-
@index = {} # { lineno => index }
|
|
16
|
-
@counts = Hash.new(0) # { id => count }
|
|
12
|
+
@session = Hash.new { |h, k| h[k] = [] } # { line => [values] }
|
|
13
|
+
@forced = [] # [ "file:line" ]
|
|
14
|
+
@added = [] # [ "file:line" ]
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
def call(actual, force: false)
|
|
20
18
|
location = find_location
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
raise "Could not find holdify statement at line #{lineno}" unless id
|
|
19
|
+
line = location.lineno
|
|
20
|
+
raise "Could not find holdify statement at line #{line}" unless @store.sha_at(line)
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@counts[id] += 1
|
|
28
|
-
end
|
|
29
|
-
index = @index[lineno]
|
|
30
|
-
|
|
31
|
-
@session[lineno] << actual
|
|
32
|
-
@forced << "#{location.path}:#{lineno}" if force
|
|
22
|
+
@session[line] << actual
|
|
23
|
+
@forced << "#{@path}:#{line}" if force
|
|
33
24
|
|
|
34
25
|
return actual if force || Holdify.reconcile
|
|
35
26
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
# Expected value
|
|
28
|
+
values = @store.get(line)
|
|
29
|
+
index = @session[line].size - 1
|
|
30
|
+
return values[index] if values && index < values.size
|
|
39
31
|
|
|
40
|
-
@added << "#{
|
|
32
|
+
@added << "#{@path}:#{line}"
|
|
41
33
|
actual
|
|
42
34
|
end
|
|
43
35
|
|
|
44
36
|
def save
|
|
45
37
|
return unless @test.failures.empty?
|
|
46
38
|
|
|
47
|
-
@added.each
|
|
48
|
-
@session.each
|
|
49
|
-
id = @store.id_at(lineno)
|
|
50
|
-
index = @index[lineno]
|
|
51
|
-
@store.update(lineno, id, values, index)
|
|
52
|
-
end
|
|
39
|
+
@added.each { |loc| warn "[holdify] Held new value for #{loc}" } unless Holdify.quiet
|
|
40
|
+
@session.each { |line, values| @store.set(line, values) }
|
|
53
41
|
@store.save
|
|
54
42
|
end
|
|
55
43
|
|
|
44
|
+
# Find the location in the test that triggered the hold
|
|
56
45
|
def find_location
|
|
57
46
|
caller_locations.find do |location|
|
|
58
47
|
next unless location.path == @path
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'yaml'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
|
|
6
|
+
module Holdify
|
|
7
|
+
# Manages the persistence and alignment of stored values
|
|
8
|
+
class Ledger
|
|
9
|
+
def initialize(path, source)
|
|
10
|
+
@path = "#{path}#{CONFIG[:ext]}"
|
|
11
|
+
@source = source
|
|
12
|
+
File.delete(@path) if Holdify.reconcile && File.exist?(@path)
|
|
13
|
+
@data = load_and_align
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def get(line) = @data[line]
|
|
17
|
+
|
|
18
|
+
def set(line, values) = (@data[line] = values)
|
|
19
|
+
|
|
20
|
+
def save
|
|
21
|
+
return FileUtils.rm_f(@path) if @data.empty?
|
|
22
|
+
|
|
23
|
+
output = {}
|
|
24
|
+
@data.keys.sort.each do |line|
|
|
25
|
+
sha = @source.sha_at(line)
|
|
26
|
+
next unless sha
|
|
27
|
+
|
|
28
|
+
output["L#{line} #{sha}"] = @data[line]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
content = YAML.dump(output, line_width: 78) # Ensure 80 columns (including pretty gutter)
|
|
32
|
+
return if File.exist?(@path) && File.read(@path) == content
|
|
33
|
+
|
|
34
|
+
File.write(@path, content)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def load_and_align
|
|
40
|
+
{}.tap do |aligned|
|
|
41
|
+
raw_data = (File.exist?(@path) && YAML.unsafe_load_file(@path)) || {}
|
|
42
|
+
raw_data.group_by { |k, _| k.split.last }.each do |sha, entries|
|
|
43
|
+
target_lines = @source.lines_with(sha)
|
|
44
|
+
next if target_lines.empty?
|
|
45
|
+
|
|
46
|
+
# Old data
|
|
47
|
+
candidates = entries.map { |key, values| { line: key[/\d+/].to_i, values: values } }
|
|
48
|
+
exact, moved = candidates.partition { |c| target_lines.include?(c[:line]) }
|
|
49
|
+
moved.sort_by! { |c| c[:line] }
|
|
50
|
+
|
|
51
|
+
# New aligned data
|
|
52
|
+
target_lines.each do |line|
|
|
53
|
+
match = exact.find { |c| c[:line] == line } || moved.shift
|
|
54
|
+
aligned[line] = match[:values] if match
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
data/lib/holdify/pretty.rb
CHANGED
|
@@ -55,9 +55,9 @@ module Holdify
|
|
|
55
55
|
exp_buf << text
|
|
56
56
|
act_buf << text
|
|
57
57
|
when '-'
|
|
58
|
-
exp_buf << RED_FG
|
|
58
|
+
exp_buf << "#{RED_FG}#{text}#{RESET}"
|
|
59
59
|
when '+'
|
|
60
|
-
act_buf << GREEN_FG
|
|
60
|
+
act_buf << "#{GREEN_FG}#{text}#{RESET}"
|
|
61
61
|
when '~'
|
|
62
62
|
flush_buffers(output, exp_buf, act_buf)
|
|
63
63
|
exp_buf.clear
|
|
@@ -81,7 +81,7 @@ module Holdify
|
|
|
81
81
|
|
|
82
82
|
def create_tempfile(obj)
|
|
83
83
|
Tempfile.new(%w[holdify .yaml]).tap do |file|
|
|
84
|
-
file.write(YAML.dump(obj, line_width: 78)) # Ensure 80 columns (including gutter)
|
|
84
|
+
file.write(YAML.dump(obj, line_width: 78)) # Ensure 80 columns (including pretty gutter)
|
|
85
85
|
file.flush
|
|
86
86
|
end
|
|
87
87
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'digest/sha1'
|
|
4
|
+
|
|
5
|
+
module Holdify
|
|
6
|
+
# Represents the current state of the source file
|
|
7
|
+
class Source
|
|
8
|
+
def initialize(path) = (@line_sha, @sha_lines = parse(path))
|
|
9
|
+
|
|
10
|
+
def sha_at(line) = @line_sha[line]
|
|
11
|
+
|
|
12
|
+
def lines_with(sha) = @sha_lines[sha]
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def parse(path)
|
|
17
|
+
line_sha = {}
|
|
18
|
+
sha_lines = Hash.new { |h, k| h[k] = [] }
|
|
19
|
+
|
|
20
|
+
File.foreach(path).with_index(1) do |text, line|
|
|
21
|
+
content = text.strip
|
|
22
|
+
next if content.empty?
|
|
23
|
+
|
|
24
|
+
sha = Digest::SHA1.hexdigest(content)
|
|
25
|
+
line_sha[line] = sha
|
|
26
|
+
sha_lines[sha] << line
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
[line_sha, sha_lines]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/holdify/store.rb
CHANGED
|
@@ -1,68 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
require_relative 'source'
|
|
5
|
+
require_relative 'ledger'
|
|
6
6
|
|
|
7
7
|
module Holdify
|
|
8
|
-
#
|
|
8
|
+
# Interface to the Source (code) and the Ledger (data)
|
|
9
9
|
class Store
|
|
10
|
-
|
|
11
|
-
@path = "#{source_path}#{CONFIG[:ext]}"
|
|
12
|
-
File.delete(@path) if Holdify.reconcile && File.exist?(@path)
|
|
10
|
+
extend Forwardable
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@source[lineno] = Digest::SHA1.hexdigest(content) unless content.empty?
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
@index = Hash.new { |h, k| h[k] = [] } # { id => ["L123 id", "L124 id"] }
|
|
21
|
-
@data = (File.exist?(@path) && YAML.unsafe_load_file(@path)) || {} # { key => [values] }
|
|
22
|
-
|
|
23
|
-
organize_data
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def id_at(lineno) = @source[lineno]
|
|
27
|
-
def stored(id, index) = @data[@index[id][index]]
|
|
28
|
-
|
|
29
|
-
# Overwrite the entry for a given ID with a new list of values
|
|
30
|
-
def update(lineno, id, values, index)
|
|
31
|
-
new_key = "L#{lineno} #{id}"
|
|
32
|
-
old_key = @index[id][index]
|
|
33
|
-
@data.delete(old_key) if old_key && old_key != new_key
|
|
34
|
-
@data[new_key] = values
|
|
35
|
-
@index[id][index] = new_key
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def save
|
|
39
|
-
return FileUtils.rm_f(@path) if @data.empty?
|
|
40
|
-
|
|
41
|
-
sorted = @data.sort_by { |k, _| k[/\d+/].to_i }.to_h
|
|
42
|
-
content = YAML.dump(sorted, line_width: 78)
|
|
43
|
-
return if File.exist?(@path) && File.read(@path) == content
|
|
44
|
-
|
|
45
|
-
File.write(@path, content)
|
|
12
|
+
def initialize(path)
|
|
13
|
+
@source = Source.new(path)
|
|
14
|
+
@ledger = Ledger.new(path, @source)
|
|
46
15
|
end
|
|
47
16
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def organize_data
|
|
51
|
-
source_counts = @source.values.tally
|
|
52
|
-
sorted_keys = @data.keys.sort_by { |k| k[/\d+/].to_i }
|
|
53
|
-
|
|
54
|
-
sorted_keys.each do |key|
|
|
55
|
-
id = key.split.last
|
|
56
|
-
next unless source_counts[id]
|
|
57
|
-
|
|
58
|
-
if @index[id].size < source_counts[id]
|
|
59
|
-
@index[id] << key
|
|
60
|
-
else
|
|
61
|
-
@data.delete(key)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
@data.keep_if { |key, _| source_counts[key.split.last] }
|
|
66
|
-
end
|
|
17
|
+
def_delegator :@source, :sha_at
|
|
18
|
+
def_delegators :@ledger, :get, :set, :save
|
|
67
19
|
end
|
|
68
20
|
end
|
data/lib/holdify.rb
CHANGED
|
@@ -11,12 +11,14 @@ module Minitest
|
|
|
11
11
|
Holdify.reconcile = true
|
|
12
12
|
Holdify.quiet = true
|
|
13
13
|
end
|
|
14
|
-
|
|
15
|
-
Holdify.quiet = true
|
|
16
|
-
end
|
|
14
|
+
|
|
17
15
|
opts.on '--holdify-pretty', 'Format the stored values with pretty print' do
|
|
18
16
|
Holdify.pretty = true
|
|
19
17
|
end
|
|
18
|
+
|
|
19
|
+
opts.on '--holdify-quiet', 'Skip the warning on storing a new value' do
|
|
20
|
+
Holdify.quiet = true
|
|
21
|
+
end
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
# Reopen the minitest class
|
|
@@ -68,10 +70,10 @@ module Minitest
|
|
|
68
70
|
expected
|
|
69
71
|
end
|
|
70
72
|
|
|
71
|
-
#
|
|
73
|
+
# Force store the current value
|
|
72
74
|
def assert_hold!(*, **) = assert_hold(*, **, force: true)
|
|
73
75
|
|
|
74
|
-
#
|
|
76
|
+
# Print to STDERR the actual value
|
|
75
77
|
def assert_hold?(*, **) = assert_hold(*, **, inspect: true)
|
|
76
78
|
end
|
|
77
79
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: minitest-holdify
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Domizio Demichelis
|
|
@@ -34,7 +34,9 @@ files:
|
|
|
34
34
|
- LICENSE.txt
|
|
35
35
|
- lib/holdify.rb
|
|
36
36
|
- lib/holdify/hold.rb
|
|
37
|
+
- lib/holdify/ledger.rb
|
|
37
38
|
- lib/holdify/pretty.rb
|
|
39
|
+
- lib/holdify/source.rb
|
|
38
40
|
- lib/holdify/store.rb
|
|
39
41
|
- lib/minitest-holdify.rb
|
|
40
42
|
- lib/minitest/holdify_plugin.rb
|