minitest-holdify 1.0.3 → 1.1.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: 06f80be32b494ec786f8d873556a8046d6e051854ce38051c1f9a41ecce91fe7
4
- data.tar.gz: 95efd3e040696f2eacdbcf61b378db9c51bb33889f73b54c418d8bb0417e6244
3
+ metadata.gz: c91100bb90de5695419fdb279520529462f67f37d2e580ce6d61c496cf43cbcb
4
+ data.tar.gz: 48b9d63b74bdea68de55c7a2790e8fb1fef59dc1eb956bce96b02d7ef0b141b3
5
5
  SHA512:
6
- metadata.gz: 3174aad64ba870444147f629ca38dddbbafce4b57d94d72d32febc1c32afdc56c6f2415337e43fab7be44a45a7d7edae7197aabc06f5ea82504bc868ca3ab123
7
- data.tar.gz: a336655be1c76832e50c00b919bbb7ecc8a735b1b49e8067a8df3506fa7a6ef2f7b1553b10356bca2fd54025a0fb22a936bdc70f390eaaa124e244f978407df9
6
+ metadata.gz: c17c6cfea957cbad41b2f272da130226a1c878546b46559ec80ccb1f939cb8c395c3b91a91e98ada8a1760bf3b661568364f6ca22f2e34ac957961c6011b4a40
7
+ data.tar.gz: 89f7c53a3ae05ef7e302efff24398e1579067d246a913c2ca91029185e08af9d8b4dae35da3a6686def351ce6fa89b0b9da1a34d388526dfa1455f5a5950e170
data/lib/holdify/hold.rb CHANGED
@@ -9,26 +9,35 @@ 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 = []
14
- @added = []
12
+ @session = Hash.new { |h, k| h[k] = [] } # { lineno => [values] }
13
+ @forced = [] # [ "file:lineno" ]
14
+ @added = [] # [ "file:lineno" ]
15
+ @index = {} # { lineno => index }
16
+ @counts = Hash.new(0) # { id => count }
15
17
  end
16
18
 
17
19
  def call(actual, force: false)
18
20
  location = find_location
19
- id = @store.id_at(location.lineno)
20
- raise "Could not find holdify statement at line #{location.lineno}" unless id
21
+ lineno = location.lineno
22
+ id = @store.id_at(lineno)
23
+ raise "Could not find holdify statement at line #{lineno}" unless id
21
24
 
22
- @session[id] << actual
23
- @forced << "#{location.path}:#{location.lineno}" if force
25
+ unless @index.key?(lineno)
26
+ @index[lineno] = @counts[id]
27
+ @counts[id] += 1
28
+ end
29
+ index = @index[lineno]
30
+
31
+ @session[lineno] << actual
32
+ @forced << "#{location.path}:#{lineno}" if force
24
33
 
25
34
  return actual if force || Holdify.reconcile
26
35
 
27
- stored = @store.stored(id)
28
- index = @session[id].size - 1
36
+ stored = @store.stored(id, index)
37
+ index = @session[lineno].size - 1
29
38
  return stored[index] if stored && index < stored.size
30
39
 
31
- @added << "#{location.path}:#{location.lineno}"
40
+ @added << "#{location.path}:#{lineno}"
32
41
  actual
33
42
  end
34
43
 
@@ -36,7 +45,11 @@ module Holdify
36
45
  return unless @test.failures.empty?
37
46
 
38
47
  @added.each { |loc| warn "[holdify] Held new value for #{loc}" } unless Holdify.quiet
39
- @session.each { |id, values| @store.update(id, values) }
48
+ @session.each do |lineno, values|
49
+ id = @store.id_at(lineno)
50
+ index = @index[lineno]
51
+ @store.update(lineno, id, values, index)
52
+ end
40
53
  @store.save
41
54
  end
42
55
 
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'tempfile'
5
+ require 'open3'
6
+
7
+ module Holdify
8
+ # Generates a pretty diff using git
9
+ module Pretty
10
+ RESET = "\e[0m"
11
+ RED_FG = "\e[31m"
12
+ GREEN_FG = "\e[32m"
13
+
14
+ G_BASE = "\e[100m #{RESET} ".freeze
15
+ G_EXP = "\e[41m\e[97m\e[1m-#{RESET} ".freeze
16
+ G_ACT = "\e[42m\e[97m\e[1m+#{RESET} ".freeze
17
+
18
+ class << self
19
+ def call(expected, actual)
20
+ exp_file = create_tempfile(expected)
21
+ act_file = create_tempfile(actual)
22
+
23
+ cmd = %W[git diff --no-index --word-diff=porcelain --unified=1000 #{exp_file.path} #{act_file.path}]
24
+ stdout, _stderr, _status = Open3.capture3(*cmd)
25
+
26
+ return nil if stdout.empty?
27
+
28
+ format(stdout)
29
+ rescue Errno::ENOENT
30
+ nil
31
+ ensure
32
+ exp_file&.close!
33
+ act_file&.close!
34
+ end
35
+
36
+ private
37
+
38
+ def format(diff)
39
+ lines = diff.lines.map(&:chomp)
40
+ start = lines.index { |l| l.start_with?('@@') }
41
+ return nil unless start
42
+
43
+ output = ["#{G_EXP}#{RED_FG}Held (Expected)#{RESET}", "#{G_ACT}#{GREEN_FG}Current (Actual)#{RESET}"]
44
+ exp_buf = +''
45
+ act_buf = +''
46
+
47
+ lines[(start + 1)..].each do |line|
48
+ char = line[0]
49
+ text = line[1..]
50
+
51
+ # :nocov:
52
+ case char
53
+ # :nocov:
54
+ when ' '
55
+ exp_buf << text
56
+ act_buf << text
57
+ when '-'
58
+ exp_buf << RED_FG << text << RESET
59
+ when '+'
60
+ act_buf << GREEN_FG << text << RESET
61
+ when '~'
62
+ flush_buffers(output, exp_buf, act_buf)
63
+ exp_buf.clear
64
+ act_buf.clear
65
+ end
66
+ end
67
+
68
+ flush_buffers(output, exp_buf, act_buf) unless exp_buf.empty? && act_buf.empty?
69
+
70
+ output.join("\n")
71
+ end
72
+
73
+ def flush_buffers(output, exp, act)
74
+ if exp == act
75
+ output << "#{G_BASE}#{exp}"
76
+ else
77
+ output << "#{G_EXP}#{exp}"
78
+ output << "#{G_ACT}#{act}"
79
+ end
80
+ end
81
+
82
+ def create_tempfile(obj)
83
+ Tempfile.new(%w[holdify .yaml]).tap do |file|
84
+ file.write(YAML.dump(obj, line_width: 78)) # Ensure 80 columns (including gutter)
85
+ file.flush
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
data/lib/holdify/store.rb CHANGED
@@ -17,38 +17,52 @@ module Holdify
17
17
  @source[lineno] = Digest::SHA1.hexdigest(content) unless content.empty?
18
18
  end
19
19
 
20
+ @index = Hash.new { |h, k| h[k] = [] } # { id => ["L123 id", "L124 id"] }
20
21
  @data = (File.exist?(@path) && YAML.unsafe_load_file(@path)) || {} # { key => [values] }
21
- @index = {} # { id => "L123 id" }
22
22
 
23
- valid_ids = @source.values
24
- @data.keep_if do |key, _|
25
- id = key.split.last
26
- next false unless valid_ids.include?(id)
27
-
28
- @index[id] = key
29
- true
30
- end
23
+ organize_data
31
24
  end
32
25
 
33
- def id_at(lineno) = @source[lineno]
34
- def stored(id) = @data[@index[id]]
26
+ def id_at(lineno) = @source[lineno]
27
+ def stored(id, index) = @data[@index[id][index]]
35
28
 
36
29
  # Overwrite the entry for a given ID with a new list of values
37
- def update(id, values)
38
- new_key = "L#{@source.key(id)} #{id}"
39
- old_key = @index[id]
30
+ def update(lineno, id, values, index)
31
+ new_key = "L#{lineno} #{id}"
32
+ old_key = @index[id][index]
40
33
  @data.delete(old_key) if old_key && old_key != new_key
41
- @data[@index[id] = new_key] = values
34
+ @data[new_key] = values
35
+ @index[id][index] = new_key
42
36
  end
43
37
 
44
38
  def save
45
39
  return FileUtils.rm_f(@path) if @data.empty?
46
40
 
47
41
  sorted = @data.sort_by { |k, _| k[/\d+/].to_i }.to_h
48
- content = YAML.dump(sorted, line_width: -1)
42
+ content = YAML.dump(sorted, line_width: 78)
49
43
  return if File.exist?(@path) && File.read(@path) == content
50
44
 
51
45
  File.write(@path, content)
52
46
  end
47
+
48
+ private
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
53
67
  end
54
68
  end
data/lib/holdify.rb CHANGED
@@ -5,12 +5,20 @@ require_relative 'holdify/hold'
5
5
 
6
6
  # Add description
7
7
  module Holdify
8
- VERSION = '1.0.3'
8
+ VERSION = '1.1.0'
9
9
  CONFIG = { ext: '.yaml' }.freeze
10
10
 
11
11
  class << self
12
12
  attr_accessor :reconcile, :quiet
13
+ attr_writer :pretty
13
14
 
14
15
  def stores = @stores ||= {}
16
+
17
+ def pretty
18
+ return @pretty unless @pretty.nil?
19
+
20
+ @pretty = $stdout.tty? && !ENV.key?('NO_COLOR') && ENV['TERM'] != 'dumb' &&
21
+ system('git --version', out: File::NULL, err: File::NULL)
22
+ end
15
23
  end
16
24
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'holdify'
4
+ require 'holdify/pretty'
4
5
 
5
6
  # Implement the minitest plugin
6
7
  module Minitest
@@ -13,6 +14,9 @@ module Minitest
13
14
  opts.on '--holdify-quiet', 'Skip the warning on storing a new value' do
14
15
  Holdify.quiet = true
15
16
  end
17
+ opts.on '--holdify-pretty', 'Format the stored values with pretty print' do
18
+ Holdify.pretty = true
19
+ end
16
20
  end
17
21
 
18
22
  # Reopen the minitest class
@@ -40,10 +44,20 @@ module Minitest
40
44
  assertion, message = message, assertion unless assertion.nil? || assertion.is_a?(Symbol)
41
45
  expected = @hold.(actual, **)
42
46
 
43
- if actual.nil?
44
- assert_nil expected, message
45
- else
46
- send(assertion || :assert_equal, expected, actual, message)
47
+ begin
48
+ if actual.nil?
49
+ assert_nil expected, message
50
+ else
51
+ send(assertion || :assert_equal, expected, actual, message)
52
+ end
53
+ rescue Minitest::Assertion
54
+ raise unless Holdify.pretty
55
+
56
+ diff = Holdify::Pretty.call(expected, actual)
57
+ raise unless diff
58
+
59
+ msg = message ? "#{message}\n#{diff}" : diff
60
+ raise Minitest::Assertion, msg
47
61
  end
48
62
 
49
63
  if inspect
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.0.3
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Domizio Demichelis
@@ -34,6 +34,7 @@ files:
34
34
  - LICENSE.txt
35
35
  - lib/holdify.rb
36
36
  - lib/holdify/hold.rb
37
+ - lib/holdify/pretty.rb
37
38
  - lib/holdify/store.rb
38
39
  - lib/minitest-holdify.rb
39
40
  - lib/minitest/holdify_plugin.rb
@@ -59,7 +60,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
60
  - !ruby/object:Gem::Version
60
61
  version: '0'
61
62
  requirements: []
62
- rubygems_version: 3.6.9
63
+ rubygems_version: 4.0.3
63
64
  specification_version: 4
64
65
  summary: Hardcoded values suck! Holdify them.
65
66
  test_files: []