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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +32 -5
- data/CODE_OF_CONDUCT.md +152 -114
- data/CONTRIBUTING.md +8 -3
- data/CONTRIBUTORS.md +19 -9
- data/Manifest.txt +88 -70
- data/README.md +30 -17
- data/Rakefile +90 -22
- data/SECURITY.md +3 -12
- data/integration/compare/array_diff_spec.rb +10 -0
- data/integration/compare/hash_diff_spec.rb +25 -0
- data/integration/compare/string_diff_spec.rb +10 -0
- data/integration/rspec_differ_spec.rb +26 -0
- data/integration/rspec_expectations_spec.rb +32 -0
- data/integration/runner +20 -0
- data/lib/diff/lcs/change.rb +21 -16
- data/lib/diff/lcs/ldiff.rb +9 -4
- data/lib/diff/lcs/version.rb +1 -1
- data/spec/hunk_spec.rb +32 -34
- data/spec/ldiff_spec.rb +7 -7
- data/spec/spec_helper.rb +4 -12
- data/test/fixtures/ldiff/output.diff-c +7 -0
- data/test/fixtures/ldiff/output.diff-u +5 -0
- data/test/fixtures/ldiff/output.diff.bin2 +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-c +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-e +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-f +1 -0
- data/test/fixtures/ldiff/output.diff.bin2-u +1 -0
- data/{spec → test}/fixtures/ldiff/output.diff.chef-c +2 -2
- data/test/fixtures/ldiff/output.diff.chef-u +9 -0
- data/{spec → test}/fixtures/ldiff/output.diff.chef2-c +2 -2
- data/{spec → test}/fixtures/ldiff/output.diff.chef2-u +2 -2
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
- data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
- data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
- data/test/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
- data/test/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
- data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-c +2 -2
- data/test/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
- data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-c +2 -2
- data/test/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
- data/test/test_block.rb +34 -0
- data/test/test_change.rb +234 -0
- data/test/test_diff.rb +53 -0
- data/test/test_helper.rb +225 -0
- data/test/test_hunk.rb +72 -0
- data/test/test_issues.rb +168 -0
- data/test/test_lcs.rb +47 -0
- data/test/test_ldiff.rb +89 -0
- data/test/test_patch.rb +362 -0
- data/test/test_sdiff.rb +167 -0
- data/test/test_traverse_balanced.rb +322 -0
- data/test/test_traverse_sequences.rb +187 -0
- metadata +136 -105
- data/spec/fixtures/ldiff/output.diff-c +0 -7
- data/spec/fixtures/ldiff/output.diff-u +0 -5
- data/spec/fixtures/ldiff/output.diff.bin2 +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-c +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-e +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-f +0 -1
- data/spec/fixtures/ldiff/output.diff.bin2-u +0 -1
- data/spec/fixtures/ldiff/output.diff.chef-u +0 -9
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-c +0 -9
- data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-u +0 -7
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-c +0 -9
- data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-u +0 -7
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-c +0 -9
- data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-u +0 -6
- data/spec/fixtures/ldiff/output.diff.missing_new_line1-u +0 -9
- data/spec/fixtures/ldiff/output.diff.missing_new_line2-u +0 -9
- /data/{spec → test}/fixtures/123_x +0 -0
- /data/{spec → test}/fixtures/456_x +0 -0
- /data/{spec → test}/fixtures/aX +0 -0
- /data/{spec → test}/fixtures/bXaX +0 -0
- /data/{spec → test}/fixtures/ds1.csv +0 -0
- /data/{spec → test}/fixtures/ds2.csv +0 -0
- /data/{spec → test}/fixtures/empty +0 -0
- /data/{spec → test}/fixtures/file1.bin +0 -0
- /data/{spec → test}/fixtures/file2.bin +0 -0
- /data/{spec → test}/fixtures/four_lines +0 -0
- /data/{spec → test}/fixtures/four_lines_with_missing_new_line +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-e +0 -0
- /data/{spec → test}/fixtures/ldiff/diff.missing_new_line2-f +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.chef-e +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.chef-f +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-e +0 -0
- /data/{spec → test}/fixtures/ldiff/error.diff.missing_new_line2-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-c +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.bin1-u +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.chef +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.chef2 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.chef2-d +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.empty.vs.four_lines-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.four_lines.vs.empty-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.issue95_trailing_context-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2 +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
- /data/{spec → test}/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
- /data/{spec → test}/fixtures/new-chef +0 -0
- /data/{spec → test}/fixtures/new-chef2 +0 -0
- /data/{spec → test}/fixtures/old-chef +0 -0
- /data/{spec → test}/fixtures/old-chef2 +0 -0
data/test/test_helper.rb
ADDED
|
@@ -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
|
data/test/test_issues.rb
ADDED
|
@@ -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
|
data/test/test_ldiff.rb
ADDED
|
@@ -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
|