diff-lcs 2.0.0.beta.1 → 2.0.0.beta.2

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -5
  3. data/CONTRIBUTING.md +8 -3
  4. data/CONTRIBUTORS.md +19 -9
  5. data/Manifest.txt +88 -70
  6. data/README.md +13 -9
  7. data/Rakefile +90 -22
  8. data/SECURITY.md +1 -10
  9. data/integration/compare/array_diff_spec.rb +10 -0
  10. data/integration/compare/hash_diff_spec.rb +25 -0
  11. data/integration/compare/string_diff_spec.rb +10 -0
  12. data/integration/rspec_differ_spec.rb +26 -0
  13. data/integration/rspec_expectations_spec.rb +32 -0
  14. data/integration/runner +20 -0
  15. data/lib/diff/lcs/change.rb +21 -16
  16. data/lib/diff/lcs/ldiff.rb +9 -4
  17. data/lib/diff/lcs/version.rb +1 -1
  18. data/spec/hunk_spec.rb +32 -34
  19. data/spec/ldiff_spec.rb +7 -7
  20. data/spec/spec_helper.rb +4 -12
  21. data/test/fixtures/ldiff/output.diff-c +7 -0
  22. data/test/fixtures/ldiff/output.diff-u +5 -0
  23. data/test/fixtures/ldiff/output.diff.bin2 +1 -0
  24. data/test/fixtures/ldiff/output.diff.bin2-c +1 -0
  25. data/test/fixtures/ldiff/output.diff.bin2-e +1 -0
  26. data/test/fixtures/ldiff/output.diff.bin2-f +1 -0
  27. data/test/fixtures/ldiff/output.diff.bin2-u +1 -0
  28. data/{spec → test}/fixtures/ldiff/output.diff.chef-c +2 -2
  29. data/test/fixtures/ldiff/output.diff.chef-u +9 -0
  30. data/{spec → test}/fixtures/ldiff/output.diff.chef2-c +2 -2
  31. data/{spec → test}/fixtures/ldiff/output.diff.chef2-u +2 -2
  32. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
  33. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
  34. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
  35. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
  36. data/test/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
  37. data/test/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
  38. data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-c +2 -2
  39. data/test/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
  40. data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-c +2 -2
  41. data/test/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
  42. data/test/test_block.rb +34 -0
  43. data/test/test_change.rb +234 -0
  44. data/test/test_diff.rb +53 -0
  45. data/test/test_helper.rb +225 -0
  46. data/test/test_hunk.rb +72 -0
  47. data/test/test_issues.rb +168 -0
  48. data/test/test_lcs.rb +47 -0
  49. data/test/test_ldiff.rb +89 -0
  50. data/test/test_patch.rb +362 -0
  51. data/test/test_sdiff.rb +167 -0
  52. data/test/test_traverse_balanced.rb +322 -0
  53. data/test/test_traverse_sequences.rb +187 -0
  54. metadata +130 -96
  55. data/spec/fixtures/ldiff/output.diff-c +0 -7
  56. data/spec/fixtures/ldiff/output.diff-u +0 -5
  57. data/spec/fixtures/ldiff/output.diff.bin2 +0 -1
  58. data/spec/fixtures/ldiff/output.diff.bin2-c +0 -1
  59. data/spec/fixtures/ldiff/output.diff.bin2-e +0 -1
  60. data/spec/fixtures/ldiff/output.diff.bin2-f +0 -1
  61. data/spec/fixtures/ldiff/output.diff.bin2-u +0 -1
  62. data/spec/fixtures/ldiff/output.diff.chef-u +0 -9
  63. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-c +0 -9
  64. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-u +0 -7
  65. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-c +0 -9
  66. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-u +0 -7
  67. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-c +0 -9
  68. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-u +0 -6
  69. data/spec/fixtures/ldiff/output.diff.missing_new_line1-u +0 -9
  70. data/spec/fixtures/ldiff/output.diff.missing_new_line2-u +0 -9
  71. /data/{spec → test}/fixtures/123_x +0 -0
  72. /data/{spec → test}/fixtures/456_x +0 -0
  73. /data/{spec → test}/fixtures/aX +0 -0
  74. /data/{spec → test}/fixtures/bXaX +0 -0
  75. /data/{spec → test}/fixtures/ds1.csv +0 -0
  76. /data/{spec → test}/fixtures/ds2.csv +0 -0
  77. /data/{spec → test}/fixtures/empty +0 -0
  78. /data/{spec → test}/fixtures/file1.bin +0 -0
  79. /data/{spec → test}/fixtures/file2.bin +0 -0
  80. /data/{spec → test}/fixtures/four_lines +0 -0
  81. /data/{spec → test}/fixtures/four_lines_with_missing_new_line +0 -0
  82. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-e +0 -0
  83. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-f +0 -0
  84. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-e +0 -0
  85. /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-f +0 -0
  86. /data/{spec → test}/fixtures/ldiff/error.diff.chef-e +0 -0
  87. /data/{spec → test}/fixtures/ldiff/error.diff.chef-f +0 -0
  88. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-e +0 -0
  89. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-f +0 -0
  90. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-e +0 -0
  91. /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-f +0 -0
  92. /data/{spec → test}/fixtures/ldiff/output.diff +0 -0
  93. /data/{spec → test}/fixtures/ldiff/output.diff.bin1 +0 -0
  94. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-c +0 -0
  95. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-e +0 -0
  96. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-f +0 -0
  97. /data/{spec → test}/fixtures/ldiff/output.diff.bin1-u +0 -0
  98. /data/{spec → test}/fixtures/ldiff/output.diff.chef +0 -0
  99. /data/{spec → test}/fixtures/ldiff/output.diff.chef2 +0 -0
  100. /data/{spec → test}/fixtures/ldiff/output.diff.chef2-d +0 -0
  101. /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines +0 -0
  102. /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-e +0 -0
  103. /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-f +0 -0
  104. /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty +0 -0
  105. /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-e +0 -0
  106. /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-f +0 -0
  107. /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context +0 -0
  108. /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-e +0 -0
  109. /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-f +0 -0
  110. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1 +0 -0
  111. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
  112. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
  113. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2 +0 -0
  114. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
  115. /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
  116. /data/{spec → test}/fixtures/new-chef +0 -0
  117. /data/{spec → test}/fixtures/new-chef2 +0 -0
  118. /data/{spec → test}/fixtures/old-chef +0 -0
  119. /data/{spec → test}/fixtures/old-chef2 +0 -0
@@ -0,0 +1,234 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class TestChange < Minitest::Test
6
+ def test_invalid_change
7
+ assert_raises RuntimeError, /Invalid CHange Action/ do
8
+ change("~", 0, "element")
9
+ end
10
+
11
+ assert_raises RuntimeError, /Invalid Position Type/ do
12
+ change("+", 3.5, "element")
13
+ end
14
+ end
15
+
16
+ def test_from_a
17
+ assert_kind_of Diff::LCS::Change, change_from_a(["+", 0, "element"])
18
+ assert_kind_of Diff::LCS::Change, change_from_a(["+", 0, "element"], Diff::LCS::ContextChange)
19
+ assert_kind_of Diff::LCS::ContextChange,
20
+ change_from_a(["!", [1, "old_element"], [2, "new_element"]])
21
+ assert_kind_of Diff::LCS::ContextChange,
22
+ change_from_a(["!", [1, "old_element"], [2, "new_element"]], Diff::LCS::ContextChange)
23
+
24
+ assert_raises RuntimeError, "Invalid change array format provided." do
25
+ change_from_a(["+", 0])
26
+ end
27
+
28
+ assert_raises RuntimeError, "Invalid change array format provided." do
29
+ change_from_a(["+", 0], Diff::LCS::ContextChange)
30
+ end
31
+ end
32
+
33
+ def test_spaceship_reflexive
34
+ c1 = change("=", 5, "x")
35
+ c2 = change("=", 5, "x")
36
+ assert_equal 0, c1 <=> c2
37
+ end
38
+
39
+ def test_spaceship_position_precedence
40
+ c1 = change("=", 5, "x")
41
+ c2 = change("=", 10, "x")
42
+
43
+ assert_equal(-1, c1 <=> c2)
44
+ assert_equal 1, c2 <=> c1
45
+ end
46
+
47
+ def test_spaceship_action_precedence
48
+ valid_actions.each_cons(2) do |a1, a2|
49
+ c1 = change(a1, 5, "x")
50
+ c2 = change(a2, 5, "x")
51
+
52
+ assert_equal(-1, c1 <=> c2, "#{a1} should sort before #{a2}")
53
+ assert_equal 1, c2 <=> c1
54
+ end
55
+ end
56
+
57
+ def test_spaceship_element_precedence
58
+ c1 = change("=", 5, "a")
59
+ c2 = change("=", 5, "z")
60
+
61
+ assert_equal(-1, c1 <=> c2)
62
+ assert_equal 1, c2 <=> c1
63
+ assert_equal 0, c1 <=> change("=", 5, "a")
64
+ end
65
+
66
+ def test_add
67
+ change = change("+", 0, "element")
68
+
69
+ assert change.adding?
70
+
71
+ refute change.deleting?
72
+ refute change.unchanged?
73
+ refute change.changed?
74
+ refute change.finished_a?
75
+ refute change.finished_b?
76
+ end
77
+
78
+ def test_delete
79
+ change = change("-", 0, "element")
80
+
81
+ assert change.deleting?
82
+
83
+ refute change.adding?
84
+ refute change.unchanged?
85
+ refute change.changed?
86
+ refute change.finished_a?
87
+ refute change.finished_b?
88
+ end
89
+
90
+ def test_unchanged
91
+ change = change("=", 0, "element")
92
+
93
+ assert change.unchanged?
94
+
95
+ refute change.deleting?
96
+ refute change.adding?
97
+ refute change.changed?
98
+ refute change.finished_a?
99
+ refute change.finished_b?
100
+ end
101
+
102
+ def test_changed
103
+ change = change("!", 0, "element")
104
+
105
+ assert change.changed?
106
+
107
+ refute change.deleting?
108
+ refute change.adding?
109
+ refute change.unchanged?
110
+ refute change.finished_a?
111
+ refute change.finished_b?
112
+ end
113
+
114
+ def test_finished_a
115
+ change = change(">", 0, "element")
116
+
117
+ assert change.finished_a?
118
+
119
+ refute change.deleting?
120
+ refute change.adding?
121
+ refute change.unchanged?
122
+ refute change.changed?
123
+ refute change.finished_b?
124
+ end
125
+
126
+ def test_finished_b
127
+ change = change("<", 0, "element")
128
+
129
+ assert change.finished_b?
130
+
131
+ refute change.deleting?
132
+ refute change.adding?
133
+ refute change.unchanged?
134
+ refute change.changed?
135
+ refute change.finished_a?
136
+ end
137
+
138
+ def test_as_array
139
+ action, position, element = change("!", 0, "element")
140
+ assert_equal "!", action
141
+ assert_equal 0, position
142
+ assert_equal "element", element
143
+ end
144
+ end
145
+
146
+ class TestContextChange < Minitest::Test
147
+ private def change(...) = Diff::LCS::ContextChange.new(...)
148
+ private def simplify(...) = Diff::LCS::ContextChange.simplify(change(...))
149
+
150
+ def test_invalid_change
151
+ assert_raises RuntimeError, /Invalid CHange Action/ do
152
+ change("~", 0, "old", 0, "new")
153
+ end
154
+
155
+ assert_raises RuntimeError, /Invalid Position Type/ do
156
+ change("+", 3.5, "old", 0, "new")
157
+ end
158
+
159
+ assert_raises RuntimeError, /Invalid Position Type/ do
160
+ change("+", 0, "old", 3.5, "new")
161
+ end
162
+ end
163
+
164
+ def test_as_array
165
+ action, old, new = change("!", 1, "old_element", 2, "new_element")
166
+
167
+ assert_equal "!", action
168
+ assert_equal [1, "old_element"], old
169
+ assert_equal [2, "new_element"], new
170
+ end
171
+
172
+ def test_spaceship_reflexive
173
+ c1 = change("=", 5, "x", 10, "y")
174
+ c2 = change("=", 5, "x", 10, "y")
175
+ assert_equal 0, c1 <=> c2
176
+ end
177
+
178
+ def test_spaceship_position_precedence
179
+ c1 = change("=", 5, "x", 10, "y")
180
+ c2 = change("=", 15, "x", 10, "y")
181
+
182
+ assert_equal(-1, c1 <=> c2)
183
+ assert_equal 1, c2 <=> c1
184
+
185
+ c1 = change("=", 5, "x", 10, "y")
186
+ c2 = change("=", 5, "x", 20, "y")
187
+
188
+ assert_equal(-1, c1 <=> c2)
189
+ assert_equal 1, c2 <=> c1
190
+ end
191
+
192
+ def test_spaceship_action_precedence
193
+ valid_actions.each_cons(2) do |a1, a2|
194
+ c1 = change(a1, 5, "x", 10, "y")
195
+ c2 = change(a2, 5, "x", 10, "y")
196
+
197
+ assert_equal(-1, c1 <=> c2, "#{a1} should sort before #{a2}")
198
+ assert_equal 1, c2 <=> c1
199
+ end
200
+ end
201
+
202
+ def test_spaceship_element_precedence
203
+ c1 = change("=", 5, "a", 10, "y")
204
+ c2 = change("=", 5, "z", 10, "y")
205
+
206
+ assert_equal(-1, c1 <=> c2, "old_element precedence")
207
+ assert_equal 1, c2 <=> c1
208
+
209
+ c3 = change("=", 5, "x", 10, "a")
210
+ c4 = change("=", 5, "x", 10, "z")
211
+
212
+ assert_equal(-1, c3 <=> c4, "new_element precedence")
213
+ assert_equal 1, c4 <=> c3
214
+ assert_equal 0, c3 <=> Diff::LCS::ContextChange.new("=", 5, "x", 10, "a")
215
+ end
216
+
217
+ def test_simplify
218
+ simplify("-", 5, "old", 10, "new") => {action:, new_element:}
219
+ assert_equal action, "-"
220
+ assert_nil new_element
221
+
222
+ simplify("<", 5, "old", 10, "new") => {action:, new_element:}
223
+ assert_equal action, "-"
224
+ assert_nil new_element
225
+
226
+ simplify("+", 5, "old", 10, "new") => {action:, old_element:}
227
+ assert_equal action, "+"
228
+ assert_nil old_element
229
+
230
+ simplify(">", 5, "old", 10, "new") => {action:, old_element:}
231
+ assert_equal action, "+"
232
+ assert_nil old_element
233
+ end
234
+ end
data/test/test_diff.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class TestDiff < Minitest::Test
6
+ def test_correctly_diffs_seq1_to_seq2
7
+ assert_equal change_diff(correct_forward_diff), diff(seq1, seq2)
8
+ end
9
+
10
+ def test_correctly_diffs_seq2_to_seq1
11
+ assert_equal change_diff(correct_backward_diff), diff(seq2, seq1)
12
+ end
13
+
14
+ def test_correctly_diffs_against_empty_sequence_forward
15
+ correct_diff = [
16
+ [
17
+ ["-", 0, "abcd"],
18
+ ["-", 1, "efgh"],
19
+ ["-", 2, "ijkl"],
20
+ ["-", 3, "mnopqrstuvwxyz"]
21
+ ]
22
+ ]
23
+
24
+ assert_equal change_diff(correct_diff), diff(word_sequence, [])
25
+ end
26
+
27
+ def test_correctly_diffs_against_empty_sequence_backward
28
+ correct_diff = [
29
+ [
30
+ ["+", 0, "abcd"],
31
+ ["+", 1, "efgh"],
32
+ ["+", 2, "ijkl"],
33
+ ["+", 3, "mnopqrstuvwxyz"]
34
+ ]
35
+ ]
36
+
37
+ assert_equal change_diff(correct_diff), diff([], word_sequence)
38
+ end
39
+
40
+ def test_correctly_diffs_xx_and_xaxb
41
+ left = "xx"
42
+ right = "xaxb"
43
+ assert_equal right, patch(left, diff(left, right))
44
+ end
45
+
46
+ def test_returns_empty_diff_with_hello_hello
47
+ assert_empty diff(hello, hello)
48
+ end
49
+
50
+ def test_returns_empty_diff_with_hello_ary_hello_ary
51
+ assert_empty diff(hello_ary, hello_ary)
52
+ end
53
+ end
@@ -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