diff options
author | Austin Ziegler <austin@halostatue.ca> | 2011-07-31 03:39:42 -0400 |
---|---|---|
committer | Austin Ziegler <austin@halostatue.ca> | 2011-07-31 03:39:42 -0400 |
commit | 2276593be732e5c91cac144d1fad0310e072613b (patch) | |
tree | ecb9e96942c3cf0990b8ff288c0af2f0b02e1653 /spec | |
parent | 743cff65ed6826c48b967c3064783809da7bc1b7 (diff) | |
download | diff-lcs-2276593be732e5c91cac144d1fad0310e072613b.tar.gz |
Convert to RSpec
Diffstat (limited to 'spec')
-rw-r--r-- | spec/diff_lcs_balanced_spec.rb | 169 | ||||
-rw-r--r-- | spec/diff_lcs_diff_spec.rb | 33 | ||||
-rw-r--r-- | spec/diff_lcs_lcs_spec.rb | 36 | ||||
-rw-r--r-- | spec/diff_lcs_patch_spec.rb | 428 | ||||
-rw-r--r-- | spec/diff_lcs_sdiff_spec.rb | 180 | ||||
-rw-r--r-- | spec/diff_lcs_sequences_spec.rb | 83 | ||||
-rw-r--r-- | spec/spec_helper.rb | 270 |
7 files changed, 1199 insertions, 0 deletions
diff --git a/spec/diff_lcs_balanced_spec.rb b/spec/diff_lcs_balanced_spec.rb new file mode 100644 index 0000000..36e11c9 --- /dev/null +++ b/spec/diff_lcs_balanced_spec.rb @@ -0,0 +1,169 @@ +# -*- ruby encoding: utf-8 -*- + +require 'spec_helper' + +describe "Diff::LCS.traverse_balanced should traverse sequences correctly" do + include Diff::LCS::SpecHelper::Matchers + + def reverse(change_result) + new_result = [] + change_result.each { |line| + line = [ line[0], line[2], line[1] ] + case line[0] + when '<' + line[0] = '>' + when '>' + line[0] = '<' + end + new_result << line + } + new_result.sort_by { |line| line[1] } + end + + def no_change(change_result) + new_result = [] + change_result.each { |line| + case line[0] + when '!' + new_result << [ '<', line[1], line[2] ] + new_result << [ '>', line[1] + 1, line[2] ] + else + new_result << line + end + } + new_result + end + + def traverse(s1, s2, callback_type) + callback = __send__(callback_type) + Diff::LCS.traverse_balanced(s1, s2, callback) + callback + end + + def do_balanced_traversal(s1, s2, result) + balanced_s1_s2 = traverse(s1, s2, :balanced_callback) + balanced_s2_s1 = traverse(s2, s1, :balanced_callback) + balanced_s1_s2_no_change = traverse(s1, s2, :balanced_callback_no_change) + balanced_s2_s1_no_change = traverse(s2, s1, :balanced_callback_no_change) + + balanced_s1_s2.result.should == result + balanced_s2_s1.result.should == reverse(result) + + balanced_s1_s2_no_change.result.should == no_change(result) + balanced_s2_s1_no_change.result.should == no_change(reverse(result)) + end + + it "sequence-a" do + s1 = %w(a b c) + s2 = %w(a x c) + + result = [ + [ '=', 0, 0 ], + [ '!', 1, 1 ], + [ '=', 2, 2 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-b" do + s1 = %w(a x y c) + s2 = %w(a v w c) + + result = [ + [ '=', 0, 0 ], + [ '!', 1, 1 ], + [ '!', 2, 2 ], + [ '=', 3, 3 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-c" do + s1 = %w(x y c) + s2 = %w(v w c) + result = [ + [ '!', 0, 0 ], + [ '!', 1, 1 ], + [ '=', 2, 2 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-d" do + s1 = %w(a x y z) + s2 = %w(b v w) + result = [ + [ '!', 0, 0 ], + [ '!', 1, 1 ], + [ '!', 2, 2 ], + [ '<', 3, 3 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-e" do + s1 = %w(a z) + s2 = %w(a) + result = [ + [ '=', 0, 0 ], + [ '<', 1, 1 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-f" do + s1 = %w(z a) + s2 = %w(a) + result = [ + [ '<', 0, 0 ], + [ '=', 1, 0 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-g" do + s1 = %w(a b c) + s2 = %w(x y z) + result = [ + [ '!', 0, 0 ], + [ '!', 1, 1 ], + [ '!', 2, 2 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-h" do + s1 = %w(abcd efgh ijkl mnopqrstuvwxyz) + s2 = [] + result = [ + [ '<', 0, 0 ], + [ '<', 1, 0 ], + [ '<', 2, 0 ], + [ '<', 3, 0 ] + ] + + do_balanced_traversal(s1, s2, result) + end + + it "sequence-i" do + s1 = [] + s2 = %w(abcd efgh ijkl mnopqrstuvwxyz) + result = [ + [ '>', 0, 0 ], + [ '>', 0, 1 ], + [ '>', 0, 2 ], + [ '>', 0, 3 ] + ] + + do_balanced_traversal(s1, s2, result) + end +end + +# vim: ft=ruby diff --git a/spec/diff_lcs_diff_spec.rb b/spec/diff_lcs_diff_spec.rb new file mode 100644 index 0000000..43ea5c7 --- /dev/null +++ b/spec/diff_lcs_diff_spec.rb @@ -0,0 +1,33 @@ +# -*- ruby encoding: utf-8 -*- + +require 'spec_helper' + +describe "Diff::LCS.diff" do + include Diff::LCS::SpecHelper::Matchers + + it "should correctly diff the sequences" do + diff_s1_s2 = Diff::LCS.diff(seq1, seq2) + diff_s2_s1 = Diff::LCS.diff(seq2, seq1) + + change_diff(correct_forward_diff).should == diff_s1_s2 + change_diff(correct_backward_diff).should == diff_s2_s1 + end + + it "should correctly diff against an empty sequence" do + diff = Diff::LCS.diff(word_sequence, []) + correct_diff = [ + [ [ '-', 0, 'abcd' ], + [ '-', 1, 'efgh' ], + [ '-', 2, 'ijkl' ], + [ '-', 3, 'mnopqrstuvwxyz' ] ] + ] + + change_diff(correct_diff).should == diff + + diff = Diff::LCS.diff([], word_sequence) + correct_diff.each { |hunk| hunk.each { |change| change[0] = '+' } } + change_diff(correct_diff).should == diff + end +end + +# vim: ft=ruby diff --git a/spec/diff_lcs_lcs_spec.rb b/spec/diff_lcs_lcs_spec.rb new file mode 100644 index 0000000..c95ba61 --- /dev/null +++ b/spec/diff_lcs_lcs_spec.rb @@ -0,0 +1,36 @@ +# -*- ruby encoding: utf-8 -*- + +require 'spec_helper' + +describe "Diff::LCS.LCS and Diff::LCS.__lcs" do + include Diff::LCS::SpecHelper::Matchers + + it "should return the correct raw values from Diff::LCS.__lcs" do + res = Diff::LCS.__lcs(seq1, seq2) + # The result of the LCS (less the +nil+ values) must be as long as the + # correct result. + res.compact.size.should == correct_lcs.size + res.should correctly_map_sequence(seq1).to_other_sequence(seq2) + + # Compact these transformations and they should be the correct LCS. + x_seq1 = (0...res.size).map { |ix| res[ix] ? seq1[ix] : nil }.compact + x_seq2 = (0...res.size).map { |ix| res[ix] ? seq2[res[ix]] : nil }.compact + + x_seq1.should == correct_lcs + x_seq2.should == correct_lcs + end + + it "should return the correct compacted values from Diff::LCS.LCS" do + res = Diff::LCS.LCS(seq1, seq2) + res.should == correct_lcs + res.compact.should == res + end + + it "should be transitive" do + res = Diff::LCS.LCS(seq2, seq1) + res.should == correct_lcs + res.compact.should == res + end +end + +# vim: ft=ruby diff --git a/spec/diff_lcs_patch_spec.rb b/spec/diff_lcs_patch_spec.rb new file mode 100644 index 0000000..7e480e0 --- /dev/null +++ b/spec/diff_lcs_patch_spec.rb @@ -0,0 +1,428 @@ +# -*- ruby encoding: utf-8 -*- + +require 'spec_helper' + +describe "Diff::LCS.patch" do + include Diff::LCS::SpecHelper::Matchers + + describe "using a Diff::LCS.diff patchset" do + describe "with default diff callbacks (DiffCallbacks)" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.diff(seq1, seq2) + @patch_set_s2_s1 = Diff::LCS.diff(seq2, seq1) + end + + it "should correctly patch left-to-right (patch autodiscovery)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2, :patch).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1, :patch).should == seq1 + Diff::LCS.patch!(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch!(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch right-to-left (unpatch autodiscovery)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1).should == seq2 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2, :unpatch).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1, :unpatch).should == seq2 + Diff::LCS.unpatch!(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.unpatch!(seq1, @patch_set_s2_s1).should == seq2 + end + end + + describe "with context diff callbacks (ContextDiffCallbacks)" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.diff(seq2, seq1, Diff::LCS::ContextDiffCallbacks) + end + + it "should correctly patch left-to-right (patch autodiscovery)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2, :patch).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1, :patch).should == seq1 + Diff::LCS.patch!(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch!(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch right-to-left (unpatch autodiscovery)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1).should == seq2 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2, :unpatch).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1, :unpatch).should == seq2 + Diff::LCS.unpatch!(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.unpatch!(seq1, @patch_set_s2_s1).should == seq2 + end + end + + describe "with sdiff callbacks (SDiffCallbacks)" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.diff(seq1, seq2, Diff::LCS::SDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.diff(seq2, seq1, Diff::LCS::SDiffCallbacks) + end + + it "should correctly patch left-to-right (patch autodiscovery)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2, :patch).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1, :patch).should == seq1 + Diff::LCS.patch!(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch!(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch right-to-left (unpatch autodiscovery)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1).should == seq2 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2, :unpatch).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1, :unpatch).should == seq2 + Diff::LCS.unpatch!(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.unpatch!(seq1, @patch_set_s2_s1).should == seq2 + end + end + end + + describe "using a Diff::LCS.sdiff patchset" do + describe "with default diff callbacks (DiffCallbacks)" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::DiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.sdiff(seq2, seq1, Diff::LCS::DiffCallbacks) + end + + it "should correctly patch left-to-right (patch autodiscovery)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2, :patch).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1, :patch).should == seq1 + Diff::LCS.patch!(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch!(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch right-to-left (unpatch autodiscovery)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1).should == seq2 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2, :unpatch).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1, :unpatch).should == seq2 + Diff::LCS.unpatch!(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.unpatch!(seq1, @patch_set_s2_s1).should == seq2 + end + end + + describe "with context diff callbacks (ContextDiffCallbacks)" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.sdiff(seq2, seq1, Diff::LCS::ContextDiffCallbacks) + end + + it "should correctly patch left-to-right (patch autodiscovery)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2, :patch).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1, :patch).should == seq1 + Diff::LCS.patch!(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch!(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch right-to-left (unpatch autodiscovery)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1).should == seq2 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2, :unpatch).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1, :unpatch).should == seq2 + Diff::LCS.unpatch!(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.unpatch!(seq1, @patch_set_s2_s1).should == seq2 + end + end + + describe "with sdiff callbacks" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(seq1, seq2) + @patch_set_s2_s1 = Diff::LCS.sdiff(seq2, seq1) + end + + it "should correctly patch left-to-right (patch autodiscovery)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(seq1, @patch_set_s1_s2, :patch).should == seq2 + Diff::LCS.patch(seq2, @patch_set_s2_s1, :patch).should == seq1 + Diff::LCS.patch!(seq1, @patch_set_s1_s2).should == seq2 + Diff::LCS.patch!(seq2, @patch_set_s2_s1).should == seq1 + end + + it "should correctly patch right-to-left (unpatch autodiscovery)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1).should == seq2 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(seq2, @patch_set_s1_s2, :unpatch).should == seq1 + Diff::LCS.patch(seq1, @patch_set_s2_s1, :unpatch).should == seq2 + Diff::LCS.unpatch!(seq2, @patch_set_s1_s2).should == seq1 + Diff::LCS.unpatch!(seq1, @patch_set_s2_s1).should == seq2 + end + end + end + + describe "fix bug 891: patchsets do not contain the last equal part" do + before(:each) do + @s1 = %w(a b c d e f g h i j k) + @s2 = %w(a b c d D e f g h i j k) + end + + describe "using Diff::LCS.diff with default diff callbacks" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2) + @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1) + end + + it "does not autodiscover s1 to s2 patches" do + # It should, but it doesn't. + expect do + Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + + expect do + Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + end + + it "should autodiscover s2 to s1 the left-to-right patches" do + Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1 + Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2 + Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1 + Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2 + Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1 + Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2 + Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1 + Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2 + end + end + + describe "using Diff::LCS.diff with context diff callbacks" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::ContextDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::ContextDiffCallbacks) + end + + it "does not autodiscover s1 to s2 patches" do + # It should, but it doesn't. + expect do + Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + + expect do + Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + end + + it "should autodiscover s2 to s1 the left-to-right patches" do + Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1 + Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2 + Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1 + Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2 + Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1 + Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2 + Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1 + Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2 + end + end + + describe "using Diff::LCS.diff with sdiff callbacks" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.diff(@s1, @s2, Diff::LCS::SDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.diff(@s2, @s1, Diff::LCS::SDiffCallbacks) + end + + it "does not autodiscover s1 to s2 patches" do + # It should, but it doesn't. + expect do + Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + + expect do + Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + end + + it "should autodiscover s2 to s1 the left-to-right patches" do + Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1 + Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2 + Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1 + Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2 + Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1 + Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2 + Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1 + Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2 + end + end + + describe "using Diff::LCS.sdiff with default sdiff callbacks" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2) + @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1) + end + + it "does not autodiscover s1 to s2 patches" do + # It should, but it doesn't. + expect do + Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + + expect do + Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + end + + it "should autodiscover s2 to s1 the left-to-right patches" do + Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1 + Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2 + Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1 + Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2 + Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1 + Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2 + Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1 + Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2 + end + end + + describe "using Diff::LCS.sdiff with context diff callbacks" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::ContextDiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::ContextDiffCallbacks) + end + + it "does not autodiscover s1 to s2 patches" do + # It should, but it doesn't. + expect do + Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + + expect do + Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + end + + it "should autodiscover s2 to s1 the left-to-right patches" do + Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1 + Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2 + Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1 + Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2 + Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1 + Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2 + Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1 + Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2 + end + end + + describe "using Diff::LCS.xdiff with default diff callbacks" do + before(:each) do + @patch_set_s1_s2 = Diff::LCS.sdiff(@s1, @s2, Diff::LCS::DiffCallbacks) + @patch_set_s2_s1 = Diff::LCS.sdiff(@s2, @s1, Diff::LCS::DiffCallbacks) + end + + it "does not autodiscover s1 to s2 patches" do + # It should, but it doesn't. + expect do + Diff::LCS.patch(@s1, @patch_set_s1_s2).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + + expect do + Diff::LCS.patch(@s1, @patch_set_s2_s1).should == @s2 + end.to raise_error(RuntimeError, /provided patchset/) + end + + it "should autodiscover s2 to s1 the left-to-right patches" do + Diff::LCS.patch(@s2, @patch_set_s2_s1).should == @s1 + Diff::LCS.patch(@s2, @patch_set_s1_s2).should == @s1 + end + + it "should correctly patch left-to-right (explicit patch)" do + Diff::LCS.patch(@s1, @patch_set_s1_s2, :patch).should == @s2 + Diff::LCS.patch(@s2, @patch_set_s2_s1, :patch).should == @s1 + Diff::LCS.patch!(@s1, @patch_set_s1_s2).should == @s2 + Diff::LCS.patch!(@s2, @patch_set_s2_s1).should == @s1 + end + + it "should correctly patch right-to-left (explicit unpatch)" do + Diff::LCS.patch(@s2, @patch_set_s1_s2, :unpatch).should == @s1 + Diff::LCS.patch(@s1, @patch_set_s2_s1, :unpatch).should == @s2 + Diff::LCS.unpatch!(@s2, @patch_set_s1_s2).should == @s1 + Diff::LCS.unpatch!(@s1, @patch_set_s2_s1).should == @s2 + end + end + end +end + +# vim: ft=ruby diff --git a/spec/diff_lcs_sdiff_spec.rb b/spec/diff_lcs_sdiff_spec.rb new file mode 100644 index 0000000..b6a3f10 --- /dev/null +++ b/spec/diff_lcs_sdiff_spec.rb @@ -0,0 +1,180 @@ +# -*- ruby encoding: utf-8 -*- + +require 'spec_helper' + +describe "Diff::LCS.sdiff should compare sequences correctly" do + include Diff::LCS::SpecHelper::Matchers + + def do_sdiff_comparison(s1, s2, forward_sdiff) + sdiff_s1_s2 = Diff::LCS.sdiff(s1, s2) + sdiff_s2_s1 = Diff::LCS.sdiff(s2, s1) + + sdiff_s1_s2.should == context_diff(forward_sdiff) + sdiff_s2_s1.should == context_diff(reverse_sdiff(forward_sdiff)) + end + + it "sequence-a" do + do_sdiff_comparison(seq1, seq2, correct_forward_sdiff) + end + + it "sequence-b" do + s1 = %w(abc def yyy xxx ghi jkl) + s2 = %w(abc dxf xxx ghi jkl) + forward_sdiff = [ + [ '=', [ 0, 'abc' ], [ 0, 'abc' ] ], + [ '!', [ 1, 'def' ], [ 1, 'dxf' ] ], + [ '-', [ 2, 'yyy' ], [ 2, nil ] ], + [ '=', [ 3, 'xxx' ], [ 2, 'xxx' ] ], + [ '=', [ 4, 'ghi' ], [ 3, 'ghi' ] ], + [ '=', [ 5, 'jkl' ], [ 4, 'jkl' ] ] + ] + + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-c" do + s1 = %w(a b c d e) + s2 = %w(a e) + forward_sdiff = [ + [ '=', [ 0, 'a' ], [ 0, 'a' ] ], + [ '-', [ 1, 'b' ], [ 1, nil ] ], + [ '-', [ 2, 'c' ], [ 1, nil ] ], + [ '-', [ 3, 'd' ], [ 1, nil ] ], + [ '=', [ 4, 'e' ], [ 1, 'e' ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-d" do + s1 = %w(a e) + s2 = %w(a b c d e) + forward_sdiff = [ + [ '=', [ 0, 'a' ], [ 0, 'a' ] ], + [ '+', [ 1, nil ], [ 1, 'b' ] ], + [ '+', [ 1, nil ], [ 2, 'c' ] ], + [ '+', [ 1, nil ], [ 3, 'd' ] ], + [ '=', [ 1, 'e' ], [ 4, 'e' ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-e" do + s1 = %w(v x a e) + s2 = %w(w y a b c d e) + forward_sdiff = [ + [ '!', [ 0, 'v' ], [ 0, 'w' ] ], + [ '!', [ 1, 'x' ], [ 1, 'y' ] ], + [ '=', [ 2, 'a' ], [ 2, 'a' ] ], + [ '+', [ 3, nil ], [ 3, 'b' ] ], + [ '+', [ 3, nil ], [ 4, 'c' ] ], + [ '+', [ 3, nil ], [ 5, 'd' ] ], + [ '=', [ 3, 'e' ], [ 6, 'e' ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-f" do + s1 = %w(x a e) + s2 = %w(a b c d e) + forward_sdiff = [ + [ '-', [ 0, 'x' ], [ 0, nil ] ], + [ '=', [ 1, 'a' ], [ 0, 'a' ] ], + [ '+', [ 2, nil ], [ 1, 'b' ] ], + [ '+', [ 2, nil ], [ 2, 'c' ] ], + [ '+', [ 2, nil ], [ 3, 'd' ] ], + [ '=', [ 2, 'e' ], [ 4, 'e' ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-g" do + s1 = %w(a e) + s2 = %w(x a b c d e) + forward_sdiff = [ + [ '+', [ 0, nil ], [ 0, 'x' ] ], + [ '=', [ 0, 'a' ], [ 1, 'a' ] ], + [ '+', [ 1, nil ], [ 2, 'b' ] ], + [ '+', [ 1, nil ], [ 3, 'c' ] ], + [ '+', [ 1, nil ], [ 4, 'd' ] ], + [ '=', [ 1, 'e' ], [ 5, 'e' ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-h" do + s1 = %w(a e v) + s2 = %w(x a b c d e w x) + forward_sdiff = [ + [ '+', [ 0, nil ], [ 0, 'x' ] ], + [ '=', [ 0, 'a' ], [ 1, 'a' ] ], + [ '+', [ 1, nil ], [ 2, 'b' ] ], + [ '+', [ 1, nil ], [ 3, 'c' ] ], + [ '+', [ 1, nil ], [ 4, 'd' ] ], + [ '=', [ 1, 'e' ], [ 5, 'e' ] ], + [ '!', [ 2, 'v' ], [ 6, 'w' ] ], + [ '+', [ 3, nil ], [ 7, 'x' ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-i" do + s1 = %w() + s2 = %w(a b c) + forward_sdiff = [ + [ '+', [ 0, nil ], [ 0, 'a' ] ], + [ '+', [ 0, nil ], [ 1, 'b' ] ], + [ '+', [ 0, nil ], [ 2, 'c' ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-j" do + s1 = %w(a b c) + s2 = %w() + forward_sdiff = [ + [ '-', [ 0, 'a' ], [ 0, nil ] ], + [ '-', [ 1, 'b' ], [ 0, nil ] ], + [ '-', [ 2, 'c' ], [ 0, nil ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-k" do + s1 = %w(a b c) + s2 = %w(1) + forward_sdiff = [ + [ '!', [ 0, 'a' ], [ 0, '1' ] ], + [ '-', [ 1, 'b' ], [ 1, nil ] ], + [ '-', [ 2, 'c' ], [ 1, nil ] ] ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-l" do + s1 = %w(a b c) + s2 = %w(c) + forward_sdiff = [ + [ '-', [ 0, 'a' ], [ 0, nil ] ], + [ '-', [ 1, 'b' ], [ 0, nil ] ], + [ '=', [ 2, 'c' ], [ 0, 'c' ] ] + ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-m" do + s1 = %w(abcd efgh ijkl mnop) + s2 = [] + forward_sdiff = [ + [ '-', [ 0, 'abcd' ], [ 0, nil ] ], + [ '-', [ 1, 'efgh' ], [ 0, nil ] ], + [ '-', [ 2, 'ijkl' ], [ 0, nil ] ], + [ '-', [ 3, 'mnop' ], [ 0, nil ] ] + ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end + + it "sequence-n" do + s1 = [] + s2 = %w(abcd efgh ijkl mnop) + forward_sdiff = [ + [ '+', [ 0, nil ], [ 0, 'abcd' ] ], + [ '+', [ 0, nil ], [ 1, 'efgh' ] ], + [ '+', [ 0, nil ], [ 2, 'ijkl' ] ], + [ '+', [ 0, nil ], [ 3, 'mnop' ] ] + ] + do_sdiff_comparison(s1, s2, forward_sdiff) + end +end + +# vim: ft=ruby diff --git a/spec/diff_lcs_sequences_spec.rb b/spec/diff_lcs_sequences_spec.rb new file mode 100644 index 0000000..c7a5558 --- /dev/null +++ b/spec/diff_lcs_sequences_spec.rb @@ -0,0 +1,83 @@ +# -*- ruby encoding: utf-8 -*- + +require 'spec_helper' + +describe "Diff::LCS.traverse_sequences" do + describe "callback with no finishers" do + before(:each) do + @callback_s1_s2 = simple_callback_no_finishers + Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2) + + @callback_s2_s1 = simple_callback_no_finishers + Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1) + end + + it "should have the correct LCS result on left-matches" do + @callback_s1_s2.matched_a.should == correct_lcs + @callback_s2_s1.matched_a.should == correct_lcs + end + + it "should have the correct LCS result on right-matches" do + @callback_s1_s2.matched_b.should == correct_lcs + @callback_s2_s1.matched_b.should == correct_lcs + end + + it "should have the correct skipped sequences for the left sequence" do + @callback_s1_s2.discards_a.should == skipped_seq1 + @callback_s2_s1.discards_a.should == skipped_seq2 + end + + it "should have the correct skipped sequences for the right sequence" do + @callback_s1_s2.discards_b.should == skipped_seq2 + @callback_s2_s1.discards_b.should == skipped_seq1 + end + + it "should not have anything done markers from the left or right sequences" do + @callback_s1_s2.done_a.should be_empty + @callback_s1_s2.done_b.should be_empty + @callback_s2_s1.done_a.should be_empty + @callback_s2_s1.done_b.should be_empty + end + end + + describe "callback with finisher" do + before(:each) do + @callback_s1_s2 = simple_callback + Diff::LCS.traverse_sequences(seq1, seq2, @callback_s1_s2) + @callback_s2_s1 = simple_callback + Diff::LCS.traverse_sequences(seq2, seq1, @callback_s2_s1) + end + + it "should have the correct LCS result on left-matches" do + @callback_s1_s2.matched_a.should == correct_lcs + @callback_s2_s1.matched_a.should == correct_lcs + end + + it "should have the correct LCS result on right-matches" do + @callback_s1_s2.matched_b.should == correct_lcs + @callback_s2_s1.matched_b.should == correct_lcs + end + + it "should have the correct skipped sequences for the left sequence" do + @callback_s1_s2.discards_a.should == skipped_seq1 + @callback_s2_s1.discards_a.should == skipped_seq2 + end + + it "should have the correct skipped sequences for the right sequence" do + @callback_s1_s2.discards_b.should == skipped_seq2 + @callback_s2_s1.discards_b.should == skipped_seq1 + end + + it "should have done markers differently-sized sequences" do + @callback_s1_s2.done_a.should == [[ "p", 9, "s", 10 ]] + @callback_s1_s2.done_b.should be_empty + + # 20110731 I don't yet understand why this particular behaviour + # isn't transitive. + @callback_s2_s1.done_a.should be_empty + @callback_s2_s1.done_b.should be_empty + end + end +end + +# vim: ft=ruby diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..1c934e7 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,270 @@ +# -*- ruby encoding: utf-8 -*- + +require 'rubygems' +require 'bundler' + +require 'pathname' + +file = Pathname.new(__FILE__).expand_path +path = file.parent +parent = path.parent + +$:.unshift parent.join('lib') + +require 'diff-lcs' + +module Diff::LCS::SpecHelper + def seq1 + %w(a b c e h j l m n p) + end + + def skipped_seq1 + %w(a h n p) + end + + def seq2 + %w(b c d e f j k l m r s t) + end + + def skipped_seq2 + %w(d f k r s t) + end + + def word_sequence + %w(abcd efgh ijkl mnopqrstuvwxyz) + end + + def correct_lcs + %w(b c e j l m) + end + + def correct_forward_diff + [ + [ [ '-', 0, 'a' ] ], + [ [ '+', 2, 'd' ] ], + [ [ '-', 4, 'h' ], + [ '+', 4, 'f' ] ], + [ [ '+', 6, 'k' ] ], + [ [ '-', 8, 'n' ], + [ '-', 9, 'p' ], + [ '+', 9, 'r' ], + [ '+', 10, 's' ], + [ '+', 11, 't' ] ] + ] + end + + def correct_backward_diff + [ + [ [ '+', 0, 'a' ] ], + [ [ '-', 2, 'd' ] ], + [ [ '-', 4, 'f' ], + [ '+', 4, 'h' ] ], + [ [ '-', 6, 'k' ] ], + [ + [ '-', 9, 'r' ], + [ '-', 10, 's' ], + [ '+', 8, 'n' ], + [ '-', 11, 't' ], + [ '+', 9, 'p' ] ] + ] + end + + def correct_forward_sdiff + [ + [ '-', [ 0, 'a' ], [ 0, nil ] ], + [ '=', [ 1, 'b' ], [ 0, 'b' ] ], + [ '=', [ 2, 'c' ], [ 1, 'c' ] ], + [ '+', [ 3, nil ], [ 2, 'd' ] ], + [ '=', [ 3, 'e' ], [ 3, 'e' ] ], + [ '!', [ 4, 'h' ], [ 4, 'f' ] ], + [ '=', [ 5, 'j' ], [ 5, 'j' ] ], + [ '+', [ 6, nil ], [ 6, 'k' ] ], + [ '=', [ 6, 'l' ], [ 7, 'l' ] ], + [ '=', [ 7, 'm' ], [ 8, 'm' ] ], + [ '!', [ 8, 'n' ], [ 9, 'r' ] ], + [ '!', [ 9, 'p' ], [ 10, 's' ] ], + [ '+', [ 10, nil ], [ 11, 't' ] ] + ] + end + + def reverse_sdiff(forward_sdiff) + forward_sdiff.map { |line| + line[1], line[2] = line[2], line[1] + case line[0] + when '-' then line[0] = '+' + when '+' then line[0] = '-' + end + line + } + end + + def change_diff(diff) + map_diffs(diff, Diff::LCS::Change) + end + + def context_diff(diff) + map_diffs(diff, Diff::LCS::ContextChange) + end + + def format_diffs(diffs) + diffs.map do |e| + if e.kind_of?(Array) + e.map { |f| f.to_a.join }.join(", ") + else + e.to_a.join + end + end.join("\n") + end + + def map_diffs(diffs, klass = Diff::LCS::ContextChange) + diffs.map do |chunks| + if klass == Diff::LCS::ContextChange + klass.from_a(chunks) + else + chunks.map { |changes| klass.from_a(changes) } + end + end + end + + def simple_callback + callbacks = Object.new + class << callbacks + attr_reader :matched_a + attr_reader :matched_b + attr_reader :discards_a + attr_reader :discards_b + attr_reader :done_a + attr_reader :done_b + + def reset + @matched_a = [] + @matched_b = [] + @discards_a = [] + @discards_b = [] + @done_a = [] + @done_b = [] + end + + def match(event) + @matched_a << event.old_element + @matched_b << event.new_element + end + + def discard_b(event) + @discards_b << event.new_element + end + + def discard_a(event) + @discards_a << event.old_element + end + + def finished_a(event) + @done_a << [event.old_element, event.old_position, + event.new_element, event.new_position] + end + + def finished_b(event) + p "called #finished_b" + @done_b << [event.old_element, event.old_position, + event.new_element, event.new_position] + end + end + callbacks.reset + callbacks + end + + def simple_callback_no_finishers + simple = simple_callback + class << simple + undef :finished_a + undef :finished_b + end + simple + end + + def balanced_callback + cb = Object.new + class << cb + attr_reader :result + + def reset + @result = [] + end + + def match(event) + @result << [ "=", event.old_position, event.new_position ] + end + + def discard_a(event) + @result << [ "<", event.old_position, event.new_position ] + end + + def discard_b(event) + @result << [ ">", event.old_position, event.new_position ] + end + + def change(event) + @result << [ "!", event.old_position, event.new_position ] + end + end + cb.reset + cb + end + + def balanced_callback_no_change + balanced = balanced_callback + class << balanced + undef :change + end + balanced + end + + module Matchers + extend RSpec::Matchers::DSL + + matcher :be_nil_or_match_values do |ii, s1, s2| + match do |ee| + ee.should satisfy { |vee| vee.nil? || s1[ii] == s2[ee] } + end + end + + matcher :correctly_map_sequence do |s1| + match do |actual| + actual.each_with_index { |ee, ii| + ee.should be_nil_or_match_values(ii, s1, @s2) + } + end + + chain :to_other_sequence do |s2| + @s2 = s2 + end + end + end +end + +RSpec.configure do |conf| + conf.include Diff::LCS::SpecHelper +end + + +=begin +RSpec::Matchers.define :be_a_multiple_of do |expected| + match do |actual| + actual % expected == 0 + end + + failure_message_for_should do |actual| + "expected that #{actual} would be a multiple of #{expected}" + end + + failure_message_for_should_not do |actual| + "expected that #{actual} would not be a multiple of #{expected}" + end + + description do + "be multiple of #{expected}" + end +end +=end + +# vim: ft=ruby |