diff-lcs 2.0.0.beta.1 → 2.0.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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -5
  3. data/CODE_OF_CONDUCT.md +152 -114
  4. data/CONTRIBUTING.md +8 -3
  5. data/CONTRIBUTORS.md +19 -9
  6. data/Manifest.txt +88 -70
  7. data/README.md +30 -17
  8. data/Rakefile +90 -22
  9. data/SECURITY.md +3 -12
  10. data/integration/compare/array_diff_spec.rb +10 -0
  11. data/integration/compare/hash_diff_spec.rb +25 -0
  12. data/integration/compare/string_diff_spec.rb +10 -0
  13. data/integration/rspec_differ_spec.rb +26 -0
  14. data/integration/rspec_expectations_spec.rb +32 -0
  15. data/integration/runner +20 -0
  16. data/lib/diff/lcs/change.rb +21 -16
  17. data/lib/diff/lcs/ldiff.rb +9 -4
  18. data/lib/diff/lcs/version.rb +1 -1
  19. data/spec/hunk_spec.rb +32 -34
  20. data/spec/ldiff_spec.rb +7 -7
  21. data/spec/spec_helper.rb +4 -12
  22. data/test/fixtures/ldiff/output.diff-c +7 -0
  23. data/test/fixtures/ldiff/output.diff-u +5 -0
  24. data/test/fixtures/ldiff/output.diff.bin2 +1 -0
  25. data/test/fixtures/ldiff/output.diff.bin2-c +1 -0
  26. data/test/fixtures/ldiff/output.diff.bin2-e +1 -0
  27. data/test/fixtures/ldiff/output.diff.bin2-f +1 -0
  28. data/test/fixtures/ldiff/output.diff.bin2-u +1 -0
  29. data/{spec → test}/fixtures/ldiff/output.diff.chef-c +2 -2
  30. data/test/fixtures/ldiff/output.diff.chef-u +9 -0
  31. data/{spec → test}/fixtures/ldiff/output.diff.chef2-c +2 -2
  32. data/{spec → test}/fixtures/ldiff/output.diff.chef2-u +2 -2
  33. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
  34. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
  35. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
  36. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
  37. data/test/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
  38. data/test/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
  39. data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-c +2 -2
  40. data/test/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
  41. data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-c +2 -2
  42. data/test/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
  43. data/test/test_block.rb +34 -0
  44. data/test/test_change.rb +234 -0
  45. data/test/test_diff.rb +53 -0
  46. data/test/test_helper.rb +225 -0
  47. data/test/test_hunk.rb +72 -0
  48. data/test/test_issues.rb +168 -0
  49. data/test/test_lcs.rb +47 -0
  50. data/test/test_ldiff.rb +89 -0
  51. data/test/test_patch.rb +362 -0
  52. data/test/test_sdiff.rb +167 -0
  53. data/test/test_traverse_balanced.rb +322 -0
  54. data/test/test_traverse_sequences.rb +187 -0
  55. metadata +136 -105
  56. data/spec/fixtures/ldiff/output.diff-c +0 -7
  57. data/spec/fixtures/ldiff/output.diff-u +0 -5
  58. data/spec/fixtures/ldiff/output.diff.bin2 +0 -1
  59. data/spec/fixtures/ldiff/output.diff.bin2-c +0 -1
  60. data/spec/fixtures/ldiff/output.diff.bin2-e +0 -1
  61. data/spec/fixtures/ldiff/output.diff.bin2-f +0 -1
  62. data/spec/fixtures/ldiff/output.diff.bin2-u +0 -1
  63. data/spec/fixtures/ldiff/output.diff.chef-u +0 -9
  64. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-c +0 -9
  65. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-u +0 -7
  66. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-c +0 -9
  67. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-u +0 -7
  68. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-c +0 -9
  69. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-u +0 -6
  70. data/spec/fixtures/ldiff/output.diff.missing_new_line1-u +0 -9
  71. data/spec/fixtures/ldiff/output.diff.missing_new_line2-u +0 -9
  72. /data/{spec → test}/fixtures/123_x +0 -0
  73. /data/{spec → test}/fixtures/456_x +0 -0
  74. /data/{spec → test}/fixtures/aX +0 -0
  75. /data/{spec → test}/fixtures/bXaX +0 -0
  76. /data/{spec → test}/fixtures/ds1.csv +0 -0
  77. /data/{spec → test}/fixtures/ds2.csv +0 -0
  78. /data/{spec → test}/fixtures/empty +0 -0
  79. /data/{spec → test}/fixtures/file1.bin +0 -0
  80. /data/{spec → test}/fixtures/file2.bin +0 -0
  81. /data/{spec → test}/fixtures/four_lines +0 -0
  82. /data/{spec → test}/fixtures/four_lines_with_missing_new_line +0 -0
  83. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-e +0 -0
  84. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-f +0 -0
  85. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-e +0 -0
  86. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-f +0 -0
  87. /data/{spec → test}/fixtures/ldiff/error.diff.chef-e +0 -0
  88. /data/{spec → test}/fixtures/ldiff/error.diff.chef-f +0 -0
  89. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-e +0 -0
  90. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-f +0 -0
  91. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-e +0 -0
  92. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-f +0 -0
  93. /data/{spec → test}/fixtures/ldiff/output.diff +0 -0
  94. /data/{spec → test}/fixtures/ldiff/output.diff.bin1 +0 -0
  95. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-c +0 -0
  96. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-e +0 -0
  97. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-f +0 -0
  98. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-u +0 -0
  99. /data/{spec → test}/fixtures/ldiff/output.diff.chef +0 -0
  100. /data/{spec → test}/fixtures/ldiff/output.diff.chef2 +0 -0
  101. /data/{spec → test}/fixtures/ldiff/output.diff.chef2-d +0 -0
  102. /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines +0 -0
  103. /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-e +0 -0
  104. /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-f +0 -0
  105. /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty +0 -0
  106. /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-e +0 -0
  107. /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-f +0 -0
  108. /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context +0 -0
  109. /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-e +0 -0
  110. /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-f +0 -0
  111. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1 +0 -0
  112. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
  113. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
  114. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2 +0 -0
  115. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
  116. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
  117. /data/{spec → test}/fixtures/new-chef +0 -0
  118. /data/{spec → test}/fixtures/new-chef2 +0 -0
  119. /data/{spec → test}/fixtures/old-chef +0 -0
  120. /data/{spec → test}/fixtures/old-chef2 +0 -0
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path(__dir__, "../lib")
4
+
5
+ require "minitest/autorun"
6
+ require "simplecov"
7
+ require "simplecov-lcov"
8
+ require "open3"
9
+
10
+ require "diff/lcs"
11
+
12
+ module Diff::LCS::TestHelper
13
+ def hello = "hello"
14
+ def hello_ary = %w[h e l l o]
15
+ def seq1 = %w[a b c e h j l m n p]
16
+ def skipped_seq1 = %w[a h n p]
17
+ def seq2 = %w[b c d e f j k l m r s t]
18
+ def skipped_seq2 = %w[d f k r s t]
19
+ def word_sequence = %w[abcd efgh ijkl mnopqrstuvwxyz]
20
+ def correct_lcs = %w[b c e j l m]
21
+
22
+ private def change(...) = Diff::LCS::Change.new(...)
23
+ private def change_from_a(array, klass = Diff::LCS::Change) = klass.from_a(array)
24
+ private def diff(...) = Diff::LCS.diff(...)
25
+ private def hunk(...) = Diff::LCS::Hunk.new(...)
26
+ private def internal_lcs(...) = Diff::LCS::Internals.lcs(...)
27
+ private def lcs(...) = Diff::LCS.lcs(...)
28
+ private def patch(...) = Diff::LCS.patch(...)
29
+ private def patch!(...) = Diff::LCS.patch!(...)
30
+ private def sdiff(...) = Diff::LCS.sdiff(...)
31
+ private def traverse_balanced(...) = Diff::LCS.traverse_balanced(...)
32
+ private def traverse_sequences(...) = Diff::LCS.traverse_sequences(...)
33
+ private def unpatch(...) = Diff::LCS.unpatch(...)
34
+ private def unpatch!(...) = Diff::LCS.unpatch!(...)
35
+ private def valid_actions = Diff::LCS::Change::VALID_ACTIONS
36
+
37
+ def correct_forward_diff
38
+ [
39
+ [["-", 0, "a"]],
40
+ [["+", 2, "d"]],
41
+ [["-", 4, "h"], ["+", 4, "f"]],
42
+ [["+", 6, "k"]],
43
+ [["-", 8, "n"], ["+", 9, "r"], ["-", 9, "p"], ["+", 10, "s"], ["+", 11, "t"]]
44
+ ]
45
+ end
46
+
47
+ def correct_backward_diff
48
+ [
49
+ [["+", 0, "a"]],
50
+ [["-", 2, "d"]],
51
+ [["-", 4, "f"], ["+", 4, "h"]],
52
+ [["-", 6, "k"]],
53
+ [["-", 9, "r"], ["+", 8, "n"], ["-", 10, "s"], ["+", 9, "p"], ["-", 11, "t"]]
54
+ ]
55
+ end
56
+
57
+ def correct_forward_sdiff
58
+ [
59
+ ["-", [0, "a"], [0, nil]],
60
+ ["=", [1, "b"], [0, "b"]],
61
+ ["=", [2, "c"], [1, "c"]],
62
+ ["+", [3, nil], [2, "d"]],
63
+ ["=", [3, "e"], [3, "e"]],
64
+ ["!", [4, "h"], [4, "f"]],
65
+ ["=", [5, "j"], [5, "j"]],
66
+ ["+", [6, nil], [6, "k"]],
67
+ ["=", [6, "l"], [7, "l"]],
68
+ ["=", [7, "m"], [8, "m"]],
69
+ ["!", [8, "n"], [9, "r"]],
70
+ ["!", [9, "p"], [10, "s"]],
71
+ ["+", [10, nil], [11, "t"]]
72
+ ]
73
+ end
74
+
75
+ def reverse_sdiff(forward_sdiff)
76
+ forward_sdiff.map { |line|
77
+ line[1], line[2] = line[2], line[1]
78
+ case line[0]
79
+ when "-" then line[0] = "+"
80
+ when "+" then line[0] = "-"
81
+ end
82
+ line
83
+ }
84
+ end
85
+
86
+ def change_diff(diff) = map_diffs(diff, Diff::LCS::Change)
87
+ def context_diff(diff) = map_diffs(diff, Diff::LCS::ContextChange)
88
+
89
+ def map_diffs(diffs, klass = Diff::LCS::ContextChange)
90
+ diffs.map do |chunks|
91
+ if klass == Diff::LCS::ContextChange
92
+ klass.from_a(chunks)
93
+ else
94
+ chunks.map { |changes| klass.from_a(changes) }
95
+ end
96
+ end
97
+ end
98
+
99
+ def balanced_traversal(s1, s2, callback_type)
100
+ callback = __send__(callback_type)
101
+ Diff::LCS.traverse_balanced(s1, s2, callback)
102
+ callback
103
+ end
104
+
105
+ def balanced_reverse(change_result)
106
+ new_result = []
107
+ change_result.each do |line|
108
+ line = [line[0], line[2], line[1]]
109
+ case line[0]
110
+ when "<" then line[0] = ">"
111
+ when ">" then line[0] = "<"
112
+ end
113
+ new_result << line
114
+ end
115
+ new_result.sort_by { |line| [line[1], line[2]] }
116
+ end
117
+
118
+ def map_to_no_change(change_result)
119
+ new_result = []
120
+ change_result.each do |line|
121
+ case line[0]
122
+ when "!"
123
+ new_result << ["<", line[1], line[2]]
124
+ new_result << [">", line[1] + 1, line[2]]
125
+ else
126
+ new_result << line
127
+ end
128
+ end
129
+ new_result
130
+ end
131
+
132
+ def assert_nil_or_match_values(ee, ii, s1, s2)
133
+ assert(ee.nil? || s1[ii] == s2[ee])
134
+ end
135
+
136
+ def assert_correctly_maps_sequence(actual, s1, s2)
137
+ actual.each_index { |ii| assert_nil_or_match_values(actual[ii], ii, s1, s2) }
138
+ end
139
+
140
+ class SimpleCallback
141
+ attr_reader :matched_a, :matched_b, :discards_a, :discards_b, :done_a, :done_b
142
+
143
+ def initialize
144
+ reset
145
+ end
146
+
147
+ def reset
148
+ @matched_a = []
149
+ @matched_b = []
150
+ @discards_a = []
151
+ @discards_b = []
152
+ @done_a = []
153
+ @done_b = []
154
+ self
155
+ end
156
+
157
+ def match(event)
158
+ @matched_a << event.old_element
159
+ @matched_b << event.new_element
160
+ end
161
+
162
+ def discard_b(event)
163
+ @discards_b << event.new_element
164
+ end
165
+
166
+ def discard_a(event)
167
+ @discards_a << event.old_element
168
+ end
169
+
170
+ def finished_a(event)
171
+ @done_a << [event.old_element, event.old_position, event.new_element, event.new_position]
172
+ end
173
+
174
+ def finished_b(event)
175
+ @done_b << [event.old_element, event.old_position, event.new_element, event.new_position]
176
+ end
177
+ end
178
+
179
+ def simple_callback = SimpleCallback.new
180
+
181
+ class SimpleCallbackNoFinishers < SimpleCallback
182
+ undef :finished_a
183
+ undef :finished_b
184
+ end
185
+
186
+ def simple_callback_no_finishers = SimpleCallbackNoFinishers.new
187
+
188
+ class BalancedCallback
189
+ attr_reader :result
190
+
191
+ def initialize
192
+ reset
193
+ end
194
+
195
+ def reset
196
+ @result = []
197
+ end
198
+
199
+ def match(event)
200
+ @result << ["=", event.old_position, event.new_position]
201
+ end
202
+
203
+ def discard_a(event)
204
+ @result << ["<", event.old_position, event.new_position]
205
+ end
206
+
207
+ def discard_b(event)
208
+ @result << [">", event.old_position, event.new_position]
209
+ end
210
+
211
+ def change(event)
212
+ @result << ["!", event.old_position, event.new_position]
213
+ end
214
+ end
215
+
216
+ def balanced_callback = BalancedCallback.new
217
+
218
+ class BalancedCallbackNoChange < BalancedCallback
219
+ undef :change
220
+ end
221
+
222
+ def balanced_callback_no_change = BalancedCallbackNoChange.new
223
+
224
+ Minitest::Test.send(:include, self)
225
+ end
data/test/test_hunk.rb ADDED
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ require "diff/lcs/hunk"
6
+
7
+ class TestHunk < Minitest::Test
8
+ def setup
9
+ @old_data = ["Tu a un carté avec {count} itéms".encode("UTF-16LE")]
10
+ @new_data = ["Tu a un carte avec {count} items".encode("UTF-16LE")]
11
+ @pieces = diff(@old_data, @new_data)
12
+ @hunk = hunk(@old_data, @new_data, @pieces[0], 3, 0)
13
+ end
14
+
15
+ def test_produces_a_unified_diff_from_the_two_pieces
16
+ expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
17
+ @@ -1 +1 @@
18
+ -Tu a un carté avec {count} itéms
19
+ +Tu a un carte avec {count} items
20
+ EXPECTED
21
+
22
+ assert_equal expected, @hunk.diff(:unified)
23
+ end
24
+
25
+ def test_produces_a_unified_diff_from_the_two_pieces_last_entry
26
+ expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
27
+ @@ -1 +1 @@
28
+ -Tu a un carté avec {count} itéms
29
+ +Tu a un carte avec {count} items
30
+ \
31
+ EXPECTED
32
+
33
+ assert_equal expected, @hunk.diff(:unified, true)
34
+ end
35
+
36
+ def test_produces_a_context_diff_from_the_two_pieces
37
+ expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
38
+ ***************
39
+ *** 1 ****
40
+ ! Tu a un carté avec {count} itéms
41
+ --- 1 ----
42
+ ! Tu a un carte avec {count} items
43
+ EXPECTED
44
+
45
+ assert_equal expected, @hunk.diff(:context)
46
+ end
47
+
48
+ def test_produces_an_old_diff_from_the_two_pieces
49
+ expected = <<-EXPECTED.gsub(/^ +/, "").encode("UTF-16LE").chomp
50
+ 1c1
51
+ < Tu a un carté avec {count} itéms
52
+ ---
53
+ > Tu a un carte avec {count} items
54
+
55
+ EXPECTED
56
+
57
+ assert_equal expected, @hunk.diff(:old)
58
+ end
59
+
60
+ def test_with_empty_first_data_set_produces_a_unified_diff
61
+ old_data = []
62
+ pieces = diff(old_data, @new_data)
63
+ hunk = hunk(old_data, @new_data, pieces[0], 3, 0)
64
+
65
+ expected = <<-EXPECTED.gsub(/^\s+/, "").encode("UTF-16LE").chomp
66
+ @@ -0,0 +1 @@
67
+ +Tu a un carte avec {count} items
68
+ EXPECTED
69
+
70
+ assert_equal expected, hunk.diff(:unified)
71
+ end
72
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require "diff/lcs/hunk"
5
+
6
+ class TestIssues < Minitest::Test
7
+ def test_issue_1_string_forward_diff
8
+ s1 = "aX"
9
+ s2 = "bXaX"
10
+ forward_diff = [
11
+ [
12
+ ["+", 0, "b"],
13
+ ["+", 1, "X"]
14
+ ]
15
+ ]
16
+
17
+ diff_s1_s2 = diff(s1, s2)
18
+
19
+ assert_equal change_diff(forward_diff), diff_s1_s2
20
+ assert_equal s2, patch(s1, diff_s1_s2)
21
+ assert_equal s1, patch(s2, diff_s1_s2)
22
+ end
23
+
24
+ def test_issue_1_string_reverse_diff
25
+ s1 = "bXaX"
26
+ s2 = "aX"
27
+ forward_diff = [
28
+ [
29
+ ["-", 0, "b"],
30
+ ["-", 1, "X"]
31
+ ]
32
+ ]
33
+
34
+ diff_s1_s2 = diff(s1, s2)
35
+
36
+ assert_equal change_diff(forward_diff), diff_s1_s2
37
+ assert_equal s2, patch(s1, diff_s1_s2)
38
+ assert_equal s1, patch(s2, diff_s1_s2)
39
+ end
40
+
41
+ def test_issue_1_array_forward_diff
42
+ s1 = %w[a X]
43
+ s2 = %w[b X a X]
44
+ forward_diff = [
45
+ [
46
+ ["+", 0, "b"],
47
+ ["+", 1, "X"]
48
+ ]
49
+ ]
50
+
51
+ diff_s1_s2 = diff(s1, s2)
52
+
53
+ assert_equal change_diff(forward_diff), diff_s1_s2
54
+ assert_equal s2, patch(s1, diff_s1_s2)
55
+ assert_equal s1, patch(s2, diff_s1_s2)
56
+ end
57
+
58
+ def test_issue_1_array_reverse_diff
59
+ s1 = %w[b X a X]
60
+ s2 = %w[a X]
61
+ forward_diff = [
62
+ [
63
+ ["-", 0, "b"],
64
+ ["-", 1, "X"]
65
+ ]
66
+ ]
67
+
68
+ diff_s1_s2 = diff(s1, s2)
69
+
70
+ assert_equal change_diff(forward_diff), diff_s1_s2
71
+ assert_equal s2, patch(s1, diff_s1_s2)
72
+ assert_equal s1, patch(s2, diff_s1_s2)
73
+ end
74
+
75
+ def test_issue_57_should_fail_with_correct_error
76
+ assert_raises(Minitest::Assertion) do
77
+ actual = {category: "app.rack.request"}
78
+ expected = {category: "rack.middleware", title: "Anonymous Middleware"}
79
+ assert_equal expected, actual
80
+ end
81
+ end
82
+
83
+ def diff_lines(old_lines, new_lines)
84
+ file_length_difference = 0
85
+ previous_hunk = nil
86
+ output = []
87
+
88
+ diff(old_lines, new_lines).each do |piece|
89
+ hunk = hunk(old_lines, new_lines, piece, 3, file_length_difference)
90
+ file_length_difference = hunk.file_length_difference
91
+ maybe_contiguous_hunks = previous_hunk.nil? || hunk.merge(previous_hunk)
92
+
93
+ output << "#{previous_hunk.diff(:unified)}\n" unless maybe_contiguous_hunks
94
+
95
+ previous_hunk = hunk
96
+ end
97
+ output << "#{previous_hunk.diff(:unified, true)}\n" unless previous_hunk.nil?
98
+ output.join
99
+ end
100
+
101
+ def test_issue_65_should_not_misplace_new_chunk
102
+ old_data = [
103
+ "recipe[a::default]", "recipe[b::default]", "recipe[c::default]",
104
+ "recipe[d::default]", "recipe[e::default]", "recipe[f::default]",
105
+ "recipe[g::default]", "recipe[h::default]", "recipe[i::default]",
106
+ "recipe[j::default]", "recipe[k::default]", "recipe[l::default]",
107
+ "recipe[m::default]", "recipe[n::default]"
108
+ ]
109
+
110
+ new_data = [
111
+ "recipe[a::default]", "recipe[c::default]", "recipe[d::default]",
112
+ "recipe[e::default]", "recipe[f::default]", "recipe[g::default]",
113
+ "recipe[h::default]", "recipe[i::default]", "recipe[j::default]",
114
+ "recipe[k::default]", "recipe[l::default]", "recipe[m::default]",
115
+ "recipe[n::default]", "recipe[o::new]", "recipe[p::new]",
116
+ "recipe[q::new]", "recipe[r::new]"
117
+ ]
118
+
119
+ expected = <<~EODIFF
120
+ @@ -1,5 +1,4 @@
121
+ recipe[a::default]
122
+ -recipe[b::default]
123
+ recipe[c::default]
124
+ recipe[d::default]
125
+ recipe[e::default]
126
+ @@ -12,3 +11,7 @@
127
+ recipe[l::default]
128
+ recipe[m::default]
129
+ recipe[n::default]
130
+ +recipe[o::new]
131
+ +recipe[p::new]
132
+ +recipe[q::new]
133
+ +recipe[r::new]
134
+ EODIFF
135
+
136
+ assert_equal expected, diff_lines(old_data, new_data)
137
+ end
138
+
139
+ def test_issue_107_should_produce_unified_output_with_correct_context
140
+ old_data = <<~DATA_OLD.strip.split("\n").map(&:chomp)
141
+ {
142
+ "name": "x",
143
+ "description": "hi"
144
+ }
145
+ DATA_OLD
146
+
147
+ new_data = <<~DATA_NEW.strip.split("\n").map(&:chomp)
148
+ {
149
+ "name": "x",
150
+ "description": "lo"
151
+ }
152
+ DATA_NEW
153
+
154
+ diff = diff(old_data, new_data)
155
+ hunk = hunk(old_data, new_data, diff.first, 3, 0)
156
+
157
+ expected = <<~EXPECTED.chomp
158
+ @@ -1,4 +1,4 @@
159
+ {
160
+ "name": "x",
161
+ - "description": "hi"
162
+ + "description": "lo"
163
+ }
164
+ EXPECTED
165
+
166
+ assert_equal expected, hunk.diff(:unified)
167
+ end
168
+ end
data/test/test_lcs.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class TestLCSInternals < Minitest::Test
6
+ def test_returns_meaningful_lcs_array_with_seq1_seq2
7
+ res = internal_lcs(seq1, seq2)
8
+ assert_equal correct_lcs.size, res.compact.size
9
+ assert_correctly_maps_sequence(res, seq1, seq2)
10
+
11
+ x_seq1 = (0...res.size).map { |ix| res[ix] ? seq1[ix] : nil }.compact
12
+ x_seq2 = (0...res.size).map { |ix| res[ix] ? seq2[res[ix]] : nil }.compact
13
+
14
+ assert_equal correct_lcs, x_seq1
15
+ assert_equal correct_lcs, x_seq2
16
+ end
17
+
18
+ def test_returns_all_indexes_with_hello_hello
19
+ assert_equal (0...hello.size).to_a, internal_lcs(hello, hello)
20
+ end
21
+
22
+ def test_returns_all_indexes_with_hello_ary_hello_ary
23
+ assert_equal (0...hello_ary.size).to_a, internal_lcs(hello_ary, hello_ary)
24
+ end
25
+ end
26
+
27
+ class TestLCS < Minitest::Test
28
+ def test_returns_correct_compacted_values
29
+ res = lcs(seq1, seq2)
30
+ assert_equal correct_lcs, res
31
+ assert_equal res, res.compact
32
+ end
33
+
34
+ def test_is_transitive
35
+ res = lcs(seq2, seq1)
36
+ assert_equal correct_lcs, res
37
+ assert_equal res, res.compact
38
+ end
39
+
40
+ def test_returns_hello_chars_with_hello_hello
41
+ assert_equal hello.chars, lcs(hello, hello)
42
+ end
43
+
44
+ def test_returns_hello_ary_with_hello_ary_hello_ary
45
+ assert_equal hello_ary, lcs(hello_ary, hello_ary)
46
+ end
47
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class TestLdiff < Minitest::Test
6
+ FIXTURES = [
7
+ {name: "diff", left: "aX", right: "bXaX", diff: 1},
8
+ {name: "diff.missing_new_line1", left: "four_lines", right: "four_lines_with_missing_new_line", diff: 1},
9
+ {name: "diff.missing_new_line2", left: "four_lines_with_missing_new_line", right: "four_lines", diff: 1},
10
+ {name: "diff.issue95_trailing_context", left: "123_x", right: "456_x", diff: 1},
11
+ {name: "diff.four_lines.vs.empty", left: "four_lines", right: "empty", diff: 1},
12
+ {name: "diff.empty.vs.four_lines", left: "empty", right: "four_lines", diff: 1},
13
+ {name: "diff.bin1", left: "file1.bin", right: "file1.bin", diff: 0},
14
+ {name: "diff.bin2", left: "file1.bin", right: "file2.bin", diff: 1},
15
+ {name: "diff.chef", left: "old-chef", right: "new-chef", diff: 1},
16
+ {name: "diff.chef2", left: "old-chef2", right: "new-chef2", diff: 1}
17
+ ].product([nil, "-c", "-u"]).map { |(fixture, flag)|
18
+ fixture = fixture.dup
19
+ fixture[:flag] = flag
20
+ fixture
21
+ }
22
+
23
+ FIXTURES.each do |fixture|
24
+ test_name = "test_ldiff_#{fixture[:name]}_#{fixture[:flag] || "default"}"
25
+ .tr(".", "_")
26
+ .tr("-", "_")
27
+
28
+ define_method(test_name) do
29
+ stdout, stderr, status = run_ldiff(fixture)
30
+ assert_equal fixture[:diff], status
31
+ assert_equal read_fixture(fixture, mode: "error", allow_missing: true), stderr
32
+ assert_equal read_fixture(fixture, mode: "output", allow_missing: false), stdout
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def read_fixture(options, mode: "output", allow_missing: false)
39
+ fixture = options.fetch(:name)
40
+ flag = options.fetch(:flag)
41
+ name = "test/fixtures/ldiff/#{mode}.#{fixture}#{flag}"
42
+
43
+ return "" if !::File.exist?(name) && allow_missing
44
+
45
+ data = IO.__send__(IO.respond_to?(:binread) ? :binread : :read, name)
46
+ clean_data(data, flag)
47
+ end
48
+
49
+ def clean_data(data, flag)
50
+ data =
51
+ case flag
52
+ when "-c", "-u"
53
+ clean_output_timestamp(data)
54
+ else
55
+ data
56
+ end
57
+ data.gsub(/\r\n?/, "\n")
58
+ end
59
+
60
+ def clean_output_timestamp(data)
61
+ data.gsub(
62
+ %r{
63
+ ^
64
+ [-+*]{3}
65
+ \s*
66
+ test/fixtures/(\S+)
67
+ \s*
68
+ \d{4}-\d\d-\d\d
69
+ \s*
70
+ \d\d:\d\d:\d\d(?:\.\d+)
71
+ \s*
72
+ (?:[-+]\d{4}|Z)
73
+ }x,
74
+ '*** test/fixtures/\1 0000-00-00 :00 =>:00 =>00.000000000 -0000'
75
+ )
76
+ end
77
+
78
+ def run_ldiff(options)
79
+ flag = options.fetch(:flag)
80
+ left = options.fetch(:left)
81
+ right = options.fetch(:right)
82
+
83
+ stdout, stderr = capture_subprocess_io do
84
+ system("ruby -Ilib bin/ldiff #{flag} test/fixtures/#{left} test/fixtures/#{right}")
85
+ end
86
+
87
+ [clean_data(stdout, flag), stderr, $?.exitstatus]
88
+ end
89
+ end