diff-lcs 1.3 → 1.5.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.
Files changed (56) hide show
  1. checksums.yaml +5 -5
  2. data/Contributing.md +86 -48
  3. data/History.md +370 -159
  4. data/License.md +6 -4
  5. data/Manifest.txt +23 -1
  6. data/README.rdoc +10 -10
  7. data/Rakefile +109 -36
  8. data/bin/htmldiff +9 -6
  9. data/bin/ldiff +4 -1
  10. data/lib/diff/lcs/array.rb +2 -2
  11. data/lib/diff/lcs/backports.rb +9 -0
  12. data/lib/diff/lcs/block.rb +5 -5
  13. data/lib/diff/lcs/callbacks.rb +22 -17
  14. data/lib/diff/lcs/change.rb +42 -49
  15. data/lib/diff/lcs/htmldiff.rb +21 -12
  16. data/lib/diff/lcs/hunk.rb +160 -73
  17. data/lib/diff/lcs/internals.rb +57 -56
  18. data/lib/diff/lcs/ldiff.rb +63 -57
  19. data/lib/diff/lcs/string.rb +1 -1
  20. data/lib/diff/lcs.rb +226 -210
  21. data/lib/diff-lcs.rb +2 -2
  22. data/spec/change_spec.rb +58 -34
  23. data/spec/diff_spec.rb +13 -9
  24. data/spec/fixtures/aX +1 -0
  25. data/spec/fixtures/bXaX +1 -0
  26. data/spec/fixtures/ldiff/output.diff +4 -0
  27. data/spec/fixtures/ldiff/output.diff-c +7 -0
  28. data/spec/fixtures/ldiff/output.diff-e +3 -0
  29. data/spec/fixtures/ldiff/output.diff-f +3 -0
  30. data/spec/fixtures/ldiff/output.diff-u +5 -0
  31. data/spec/fixtures/ldiff/output.diff.chef +4 -0
  32. data/spec/fixtures/ldiff/output.diff.chef-c +15 -0
  33. data/spec/fixtures/ldiff/output.diff.chef-e +3 -0
  34. data/spec/fixtures/ldiff/output.diff.chef-f +3 -0
  35. data/spec/fixtures/ldiff/output.diff.chef-u +9 -0
  36. data/spec/fixtures/ldiff/output.diff.chef2 +7 -0
  37. data/spec/fixtures/ldiff/output.diff.chef2-c +20 -0
  38. data/spec/fixtures/ldiff/output.diff.chef2-d +7 -0
  39. data/spec/fixtures/ldiff/output.diff.chef2-e +7 -0
  40. data/spec/fixtures/ldiff/output.diff.chef2-f +7 -0
  41. data/spec/fixtures/ldiff/output.diff.chef2-u +16 -0
  42. data/spec/fixtures/new-chef +4 -0
  43. data/spec/fixtures/new-chef2 +17 -0
  44. data/spec/fixtures/old-chef +4 -0
  45. data/spec/fixtures/old-chef2 +14 -0
  46. data/spec/hunk_spec.rb +48 -37
  47. data/spec/issues_spec.rb +132 -21
  48. data/spec/lcs_spec.rb +3 -3
  49. data/spec/ldiff_spec.rb +74 -32
  50. data/spec/patch_spec.rb +14 -20
  51. data/spec/sdiff_spec.rb +83 -81
  52. data/spec/spec_helper.rb +146 -91
  53. data/spec/traverse_balanced_spec.rb +138 -136
  54. data/spec/traverse_sequences_spec.rb +7 -9
  55. metadata +76 -48
  56. data/autotest/discover.rb +0 -1
data/spec/issues_spec.rb CHANGED
@@ -1,49 +1,160 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
3
+ require "spec_helper"
4
+ require "diff/lcs/hunk"
4
5
 
5
6
  describe "Diff::LCS Issues" do
6
7
  include Diff::LCS::SpecHelper::Matchers
7
8
 
8
- describe 'issue #1' do
9
- shared_examples 'handles simple diffs' do |s1, s2, forward_diff|
9
+ describe "issue #1" do
10
+ shared_examples "handles simple diffs" do |s1, s2, forward_diff|
10
11
  before do
11
12
  @diff_s1_s2 = Diff::LCS.diff(s1, s2)
12
13
  end
13
14
 
14
- it 'creates the correct diff' do
15
+ it "creates the correct diff" do
15
16
  expect(change_diff(forward_diff)).to eq(@diff_s1_s2)
16
17
  end
17
18
 
18
- it 'creates the correct patch s1->s2' do
19
+ it "creates the correct patch s1->s2" do
19
20
  expect(Diff::LCS.patch(s1, @diff_s1_s2)).to eq(s2)
20
21
  end
21
22
 
22
- it 'creates the correct patch s2->s1' do
23
+ it "creates the correct patch s2->s1" do
23
24
  expect(Diff::LCS.patch(s2, @diff_s1_s2)).to eq(s1)
24
25
  end
25
26
  end
26
27
 
27
- describe 'string' do
28
- it_has_behavior 'handles simple diffs', 'aX', 'bXaX', [
29
- [ [ '+', 0, 'b' ],
30
- [ '+', 1, 'X' ] ],
28
+ describe "string" do
29
+ it_has_behavior "handles simple diffs", "aX", "bXaX", [
30
+ [
31
+ ["+", 0, "b"],
32
+ ["+", 1, "X"]
33
+ ]
31
34
  ]
32
- it_has_behavior 'handles simple diffs', 'bXaX', 'aX', [
33
- [ [ '-', 0, 'b' ],
34
- [ '-', 1, 'X' ] ],
35
+ it_has_behavior "handles simple diffs", "bXaX", "aX", [
36
+ [
37
+ ["-", 0, "b"],
38
+ ["-", 1, "X"]
39
+ ]
35
40
  ]
36
41
  end
37
42
 
38
- describe 'array' do
39
- it_has_behavior 'handles simple diffs', %w(a X), %w(b X a X), [
40
- [ [ '+', 0, 'b' ],
41
- [ '+', 1, 'X' ] ],
43
+ describe "array" do
44
+ it_has_behavior "handles simple diffs", %w[a X], %w[b X a X], [
45
+ [
46
+ ["+", 0, "b"],
47
+ ["+", 1, "X"]
48
+ ]
42
49
  ]
43
- it_has_behavior 'handles simple diffs', %w(b X a X), %w(a X), [
44
- [ [ '-', 0, 'b' ],
45
- [ '-', 1, 'X' ] ],
50
+ it_has_behavior "handles simple diffs", %w[b X a X], %w[a X], [
51
+ [
52
+ ["-", 0, "b"],
53
+ ["-", 1, "X"]
54
+ ]
46
55
  ]
47
56
  end
48
57
  end
58
+
59
+ describe "issue #57" do
60
+ it "should fail with a correct error" do
61
+ # standard:disable Style/HashSyntax
62
+ expect {
63
+ actual = {:category => "app.rack.request"}
64
+ expected = {:category => "rack.middleware", :title => "Anonymous Middleware"}
65
+ expect(actual).to eq(expected)
66
+ }.to raise_error(RSpec::Expectations::ExpectationNotMetError)
67
+ # standard:enable Style/HashSyntax
68
+ end
69
+ end
70
+
71
+ describe "issue #60" do
72
+ it "should produce unified output with correct context" do
73
+ # standard:disable Layout/HeredocIndentation
74
+ old_data = <<-DATA_OLD.strip.split("\n").map(&:chomp)
75
+ {
76
+ "name": "x",
77
+ "description": "hi"
78
+ }
79
+ DATA_OLD
80
+
81
+ new_data = <<-DATA_NEW.strip.split("\n").map(&:chomp)
82
+ {
83
+ "name": "x",
84
+ "description": "lo"
85
+ }
86
+ DATA_NEW
87
+
88
+ diff = ::Diff::LCS.diff(old_data, new_data)
89
+ hunk = ::Diff::LCS::Hunk.new(old_data, new_data, diff.first, 3, 0)
90
+
91
+ expect(hunk.diff(:unified)).to eq(<<-EXPECTED.chomp)
92
+ @@ -1,5 +1,5 @@
93
+ {
94
+ "name": "x",
95
+ - "description": "hi"
96
+ + "description": "lo"
97
+ }
98
+ EXPECTED
99
+ # standard:enable Layout/HeredocIndentation
100
+ end
101
+ end
102
+
103
+ describe "issue #65" do
104
+ def diff_lines(old_lines, new_lines)
105
+ file_length_difference = 0
106
+ previous_hunk = nil
107
+ output = []
108
+
109
+ Diff::LCS.diff(old_lines, new_lines).each do |piece|
110
+ hunk = Diff::LCS::Hunk.new(old_lines, new_lines, piece, 3, file_length_difference)
111
+ file_length_difference = hunk.file_length_difference
112
+ maybe_contiguous_hunks = previous_hunk.nil? || hunk.merge(previous_hunk)
113
+
114
+ output << "#{previous_hunk.diff(:unified)}\n" unless maybe_contiguous_hunks
115
+
116
+ previous_hunk = hunk
117
+ end
118
+ output << "#{previous_hunk.diff(:unified, true)}\n" unless previous_hunk.nil?
119
+ output.join
120
+ end
121
+
122
+ it "should not misplace the new chunk" do
123
+ old_data = [
124
+ "recipe[a::default]", "recipe[b::default]", "recipe[c::default]",
125
+ "recipe[d::default]", "recipe[e::default]", "recipe[f::default]",
126
+ "recipe[g::default]", "recipe[h::default]", "recipe[i::default]",
127
+ "recipe[j::default]", "recipe[k::default]", "recipe[l::default]",
128
+ "recipe[m::default]", "recipe[n::default]"
129
+ ]
130
+
131
+ new_data = [
132
+ "recipe[a::default]", "recipe[c::default]", "recipe[d::default]",
133
+ "recipe[e::default]", "recipe[f::default]", "recipe[g::default]",
134
+ "recipe[h::default]", "recipe[i::default]", "recipe[j::default]",
135
+ "recipe[k::default]", "recipe[l::default]", "recipe[m::default]",
136
+ "recipe[n::default]", "recipe[o::new]", "recipe[p::new]",
137
+ "recipe[q::new]", "recipe[r::new]"
138
+ ]
139
+
140
+ # standard:disable Layout/HeredocIndentation
141
+ expect(diff_lines(old_data, new_data)).to eq(<<-EODIFF)
142
+ @@ -1,5 +1,4 @@
143
+ recipe[a::default]
144
+ -recipe[b::default]
145
+ recipe[c::default]
146
+ recipe[d::default]
147
+ recipe[e::default]
148
+ @@ -12,3 +11,7 @@
149
+ recipe[l::default]
150
+ recipe[m::default]
151
+ recipe[n::default]
152
+ +recipe[o::new]
153
+ +recipe[p::new]
154
+ +recipe[q::new]
155
+ +recipe[r::new]
156
+ EODIFF
157
+ # standard:enable Layout/HeredocIndentation
158
+ end
159
+ end
49
160
  end
data/spec/lcs_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
3
+ require "spec_helper"
4
4
 
5
5
  describe Diff::LCS::Internals, ".lcs" do
6
6
  include Diff::LCS::SpecHelper::Matchers
@@ -47,7 +47,7 @@ describe Diff::LCS, ".LCS" do
47
47
  end
48
48
 
49
49
  it "returns %W(h e l l o) with (hello, hello)" do
50
- expect(Diff::LCS.LCS(hello, hello)).to eq(hello.split(//))
50
+ expect(Diff::LCS.LCS(hello, hello)).to eq(hello.chars)
51
51
  end
52
52
 
53
53
  it "returns hello_ary with (hello_ary, hello_ary)" do
data/spec/ldiff_spec.rb CHANGED
@@ -1,47 +1,89 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
3
+ require "spec_helper"
4
4
 
5
- describe "Diff::LCS.diff" do
6
- include Diff::LCS::SpecHelper::Matchers
5
+ RSpec.describe "bin/ldiff" do
6
+ include CaptureSubprocessIO
7
7
 
8
- it 'correctly diffs seq1 to seq2' do
9
- diff_s1_s2 = Diff::LCS.diff(seq1, seq2)
10
- expect(change_diff(correct_forward_diff)).to eq(diff_s1_s2)
11
- end
8
+ # standard:disable Style/HashSyntax
9
+ fixtures = [
10
+ {:name => "output.diff", :left => "aX", :right => "bXaX"},
11
+ {:name => "output.diff.chef", :left => "old-chef", :right => "new-chef"},
12
+ {:name => "output.diff.chef2", :left => "old-chef2", :right => "new-chef2"}
13
+ ].product([nil, "-e", "-f", "-c", "-u"]).map { |(fixture, flag)|
14
+ fixture = fixture.dup
15
+ fixture[:flag] = flag
16
+ fixture
17
+ }
18
+ # standard:enable Style/HashSyntax
12
19
 
13
- it 'correctly diffs seq2 to seq1' do
14
- diff_s2_s1 = Diff::LCS.diff(seq2, seq1)
15
- expect(change_diff(correct_backward_diff)).to eq(diff_s2_s1)
16
- end
20
+ def self.test_ldiff(fixture)
21
+ desc = [
22
+ fixture[:flag],
23
+ "spec/fixtures/#{fixture[:left]}",
24
+ "spec/fixtures/#{fixture[:right]}",
25
+ "#",
26
+ "=>",
27
+ "spec/fixtures/ldiff/#{fixture[:name]}#{fixture[:flag]}"
28
+ ].join(" ")
17
29
 
18
- it 'correctly diffs against an empty sequence' do
19
- diff = Diff::LCS.diff(word_sequence, [])
20
- correct_diff = [
21
- [ [ '-', 0, 'abcd' ],
22
- [ '-', 1, 'efgh' ],
23
- [ '-', 2, 'ijkl' ],
24
- [ '-', 3, 'mnopqrstuvwxyz' ] ]
25
- ]
30
+ it desc do
31
+ expect(run_ldiff(fixture)).to eq(read_fixture(fixture))
32
+ end
33
+ end
26
34
 
27
- expect(change_diff(correct_diff)).to eq(diff)
35
+ fixtures.each do |fixture|
36
+ test_ldiff(fixture)
37
+ end
28
38
 
29
- diff = Diff::LCS.diff([], word_sequence)
30
- correct_diff.each { |hunk| hunk.each { |change| change[0] = '+' } }
31
- expect(change_diff(correct_diff)).to eq(diff)
39
+ def read_fixture(options)
40
+ fixture = options.fetch(:name)
41
+ flag = options.fetch(:flag)
42
+ name = "spec/fixtures/ldiff/#{fixture}#{flag}"
43
+ data = IO.__send__(IO.respond_to?(:binread) ? :binread : :read, name)
44
+ clean_data(data, flag)
32
45
  end
33
46
 
34
- it "correctly diffs 'xx' and 'xaxb'" do
35
- left = 'xx'
36
- right = 'xaxb'
37
- expect(Diff::LCS.patch(left, Diff::LCS.diff(left, right))).to eq(right)
47
+ def clean_data(data, flag)
48
+ data =
49
+ case flag
50
+ when "-c", "-u"
51
+ clean_output_timestamp(data)
52
+ else
53
+ data
54
+ end
55
+ data.gsub(/\r\n?/, "\n")
38
56
  end
39
57
 
40
- it "returns an empty diff with (hello, hello)" do
41
- expect(Diff::LCS.diff(hello, hello)).to eq([])
58
+ def clean_output_timestamp(data)
59
+ data.gsub(
60
+ %r{
61
+ ^
62
+ [-+*]{3}
63
+ \s*
64
+ spec/fixtures/(\S+)
65
+ \s*
66
+ \d{4}-\d\d-\d\d
67
+ \s*
68
+ \d\d:\d\d:\d\d(?:\.\d+)
69
+ \s*
70
+ (?:[-+]\d{4}|Z)
71
+ }x,
72
+ '*** spec/fixtures/\1 0000-00-00 :00 =>:00 =>00.000000000 -0000'
73
+ )
42
74
  end
43
75
 
44
- it "returns an empty diff with (hello_ary, hello_ary)" do
45
- expect(Diff::LCS.diff(hello_ary, hello_ary)).to eq([])
76
+ def run_ldiff(options)
77
+ flag = options.fetch(:flag)
78
+ left = options.fetch(:left)
79
+ right = options.fetch(:right)
80
+
81
+ stdout, stderr = capture_subprocess_io do
82
+ system("ruby -Ilib bin/ldiff #{flag} spec/fixtures/#{left} spec/fixtures/#{right}")
83
+ end
84
+
85
+ expect(stderr).to be_empty if RUBY_VERSION >= "1.9"
86
+ expect(stdout).not_to be_empty
87
+ clean_data(stdout, flag)
46
88
  end
47
89
  end
data/spec/patch_spec.rb CHANGED
@@ -1,6 +1,6 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
3
+ require "spec_helper"
4
4
 
5
5
  describe "Diff::LCS.patch" do
6
6
  include Diff::LCS::SpecHelper::Matchers
@@ -29,12 +29,12 @@ describe "Diff::LCS.patch" do
29
29
  describe "an empty patchset returns the source" do
30
30
  it "works on a string (hello)" do
31
31
  diff = Diff::LCS.diff(hello, hello)
32
- expect(Diff::LCS::patch(hello, diff)).to eq(hello)
32
+ expect(Diff::LCS.patch(hello, diff)).to eq(hello)
33
33
  end
34
34
 
35
35
  it "works on an array %W(h e l l o)" do
36
36
  diff = Diff::LCS.diff(hello_ary, hello_ary)
37
- expect(Diff::LCS::patch(hello_ary, diff)).to eq(hello_ary)
37
+ expect(Diff::LCS.patch(hello_ary, diff)).to eq(hello_ary)
38
38
  end
39
39
  end
40
40
 
@@ -104,11 +104,11 @@ describe "Diff::LCS.patch" do
104
104
  describe "using a Diff::LCS.sdiff patchset" do
105
105
  describe "an empty patchset returns the source" do
106
106
  it "works on a string (hello)" do
107
- expect(Diff::LCS::patch(hello, Diff::LCS.sdiff(hello, hello))).to eq(hello)
107
+ expect(Diff::LCS.patch(hello, Diff::LCS.sdiff(hello, hello))).to eq(hello)
108
108
  end
109
109
 
110
110
  it "works on an array %W(h e l l o)" do
111
- expect(Diff::LCS::patch(hello_ary, Diff::LCS.sdiff(hello_ary, hello_ary))).to eq(hello_ary)
111
+ expect(Diff::LCS.patch(hello_ary, Diff::LCS.sdiff(hello_ary, hello_ary))).to eq(hello_ary)
112
112
  end
113
113
  end
114
114
 
@@ -181,8 +181,8 @@ describe "Diff::LCS.patch" do
181
181
  # above.
182
182
  describe "fix bug 891: patchsets do not contain the last equal part" do
183
183
  before :each do
184
- @s1 = %w(a b c d e f g h i j k)
185
- @s2 = %w(a b c d D e f g h i j k)
184
+ @s1 = %w[a b c d e f g h i j k] # rubocop:disable Layout/SpaceInsideArrayPercentLiteral
185
+ @s2 = %w[a b c d D e f g h i j k]
186
186
  end
187
187
 
188
188
  describe "using Diff::LCS.diff with default diff callbacks" do
@@ -225,10 +225,8 @@ describe "Diff::LCS.patch" do
225
225
 
226
226
  describe "using Diff::LCS.diff with context diff callbacks" do
227
227
  before :each do
228
- @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2,
229
- Diff::LCS::ContextDiffCallbacks)
230
- @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1,
231
- Diff::LCS::ContextDiffCallbacks)
228
+ @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::ContextDiffCallbacks)
229
+ @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::ContextDiffCallbacks)
232
230
  end
233
231
 
234
232
  it "autodiscovers s1 to s2 patches" do
@@ -265,10 +263,8 @@ describe "Diff::LCS.patch" do
265
263
 
266
264
  describe "using Diff::LCS.diff with sdiff callbacks" do
267
265
  before(:each) do
268
- @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2,
269
- Diff::LCS::SDiffCallbacks)
270
- @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1,
271
- Diff::LCS::SDiffCallbacks)
266
+ @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::SDiffCallbacks)
267
+ @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::SDiffCallbacks)
272
268
  end
273
269
 
274
270
  it "autodiscovers s1 to s2 patches" do
@@ -343,10 +339,8 @@ describe "Diff::LCS.patch" do
343
339
 
344
340
  describe "using Diff::LCS.sdiff with context diff callbacks" do
345
341
  before(:each) do
346
- @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2,
347
- Diff::LCS::ContextDiffCallbacks)
348
- @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1,
349
- Diff::LCS::ContextDiffCallbacks)
342
+ @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::ContextDiffCallbacks)
343
+ @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::ContextDiffCallbacks)
350
344
  end
351
345
 
352
346
  it "autodiscovers s1 to s2 patches" do