From dbb36e6c37fae10ae450567e9e3e56455e66e485 Mon Sep 17 00:00:00 2001 From: Austin Ziegler Date: Tue, 30 Jun 2020 09:51:08 -0400 Subject: [PATCH] Fix improperly placed chunks Resolve #65 - Also add even more tests for checking `ldiff` results against `diff` results. - Fix issues with diff/ldiff output highlighted by the above tests. - Add a parameter to indicate that the hunk being processed is the _last_ hunk; this results in correct counting of the hunk size. - The misplaced chunks were happening because of an improper `.abs` on `#diff_size`, when the `.abs` needed to be on the finding of the maximum diff size. --- lib/diff/lcs/block.rb | 2 +- lib/diff/lcs/hunk.rb | 118 ++++++++++++++++++------ lib/diff/lcs/ldiff.rb | 33 +++---- spec/fixtures/ldiff/output.diff-c | 4 +- spec/fixtures/ldiff/output.diff-u | 4 +- spec/fixtures/ldiff/output.diff.chef | 4 + spec/fixtures/ldiff/output.diff.chef-c | 15 +++ spec/fixtures/ldiff/output.diff.chef-e | 3 + spec/fixtures/ldiff/output.diff.chef-f | 3 + spec/fixtures/ldiff/output.diff.chef-u | 7 +- spec/fixtures/ldiff/output.diff.chef2-c | 20 ++++ spec/fixtures/ldiff/output.diff.chef2-d | 7 ++ spec/fixtures/ldiff/output.diff.chef2-e | 7 ++ spec/fixtures/ldiff/output.diff.chef2-u | 16 ++++ spec/fixtures/new-chef2 | 17 ++++ spec/fixtures/old-chef2 | 14 +++ spec/hunk_spec.rb | 31 +++++-- spec/issues_spec.rb | 58 ++++++++++++ spec/ldiff_spec.rb | 67 +++++++------- 19 files changed, 330 insertions(+), 100 deletions(-) create mode 100644 spec/fixtures/ldiff/output.diff.chef create mode 100644 spec/fixtures/ldiff/output.diff.chef-c create mode 100644 spec/fixtures/ldiff/output.diff.chef-e create mode 100644 spec/fixtures/ldiff/output.diff.chef-f create mode 100644 spec/fixtures/ldiff/output.diff.chef2-c create mode 100644 spec/fixtures/ldiff/output.diff.chef2-d create mode 100644 spec/fixtures/ldiff/output.diff.chef2-e create mode 100644 spec/fixtures/ldiff/output.diff.chef2-u create mode 100644 spec/fixtures/new-chef2 create mode 100644 spec/fixtures/old-chef2 diff --git a/lib/diff/lcs/block.rb b/lib/diff/lcs/block.rb index fe86793..430702d 100644 --- a/lib/diff/lcs/block.rb +++ b/lib/diff/lcs/block.rb @@ -19,7 +19,7 @@ def initialize(chunk) end def diff_size - (@insert.size - @remove.size).abs + @insert.size - @remove.size end def op diff --git a/lib/diff/lcs/hunk.rb b/lib/diff/lcs/hunk.rb index d884a1b..5b2c64f 100644 --- a/lib/diff/lcs/hunk.rb +++ b/lib/diff/lcs/hunk.rb @@ -20,7 +20,7 @@ def initialize(data_old, data_new, piece, flag_context, file_length_difference) before = after = file_length_difference after += @blocks[0].diff_size @file_length_difference = after # The caller must get this manually - @max_diff_size = @blocks.map { |e| e.diff_size }.max + @max_diff_size = @blocks.map { |e| e.diff_size.abs }.max # Save the start & end of each array. If the array doesn't exist (e.g., # we're only adding items in this block), then figure out the line @@ -101,18 +101,18 @@ def overlaps?(hunk) end # Returns a diff string based on a format. - def diff(format) + def diff(format, last = false) case format when :old - old_diff + old_diff(last) when :unified - unified_diff + unified_diff(last) when :context - context_diff + context_diff(last) when :ed self when :reverse_ed, :ed_finish - ed_diff(format) + ed_diff(format, last) else fail "Unknown diff format #{format}." end @@ -120,7 +120,7 @@ def diff(format) # Note that an old diff can't have any context. Therefore, we know that # there's only one block in the hunk. - def old_diff + def old_diff(last = false) warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1 op_act = { '+' => 'a', '-' => 'd', '!' => 'c' } @@ -129,26 +129,26 @@ def old_diff # Calculate item number range. Old diff range is just like a context # diff range, except the ranges are on one line with the action between # them. - s = encode("#{context_range(:old)}#{op_act[block.op]}#{context_range(:new)}\n") + s = encode("#{context_range(:old, ',')}#{op_act[block.op]}#{context_range(:new, ',')}\n") # If removing anything, just print out all the remove lines in the hunk # which is just all the remove lines in the block. unless block.remove.empty? - @data_old[@start_old..@end_old].each { |e| s << encode('< ') + e + encode("\n") } + @data_old[@start_old..@end_old].each { |e| s << encode('< ') + e.chomp + encode("\n") } end s << encode("---\n") if block.op == '!' unless block.insert.empty? - @data_new[@start_new..@end_new].each { |e| s << encode('> ') + e + encode("\n") } + @data_new[@start_new..@end_new].each { |e| s << encode('> ') + e.chomp + encode("\n") } end s end private :old_diff - def unified_diff + def unified_diff(last = false) # Calculate item number range. - s = encode("@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n") + s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n") # Outlist starts containing the hunk of the old file. Removing an item # just means putting a '-' in front of it. Inserting an item requires @@ -161,7 +161,14 @@ def unified_diff # file -- don't take removed items into account. lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0 - outlist = @data_old[lo..hi].map { |e| e.insert(0, encode(' ')) } + outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") } + + last_block = blocks[-1] + + if last + old_missing_newline = missing_last_newline?(@data_old) + new_missing_newline = missing_last_newline?(@data_new) + end @blocks.each do |block| block.remove.each do |item| @@ -170,67 +177,102 @@ def unified_diff outlist[offset][0, 1] = encode(op) num_removed += 1 end + + if last && block == last_block && old_missing_newline && !new_missing_newline + outlist << encode("\\ No newline at end of file") + num_removed += 1 + end + block.insert.each do |item| op = item.action.to_s # + offset = item.position - @start_new + num_removed - outlist[offset, 0] = encode(op) + @data_new[item.position] + outlist[offset, 0] = encode(op) + @data_new[item.position].chomp num_added += 1 end end + if last && new_missing_newline + outlist << encode("\\ No newline at end of file") + end + s << outlist.join(encode("\n")) + + s end private :unified_diff - def context_diff + def context_diff(last = false) s = encode("***************\n") - s << encode("*** #{context_range(:old)} ****\n") - r = context_range(:new) + s << encode("*** #{context_range(:old, ',', true)} ****\n") + r = context_range(:new, ',', true) + + if last + old_missing_newline = missing_last_newline?(@data_old) + new_missing_newline = missing_last_newline?(@data_new) + end # Print out file 1 part for each block in context diff format if there # are any blocks that remove items lo, hi = @start_old, @end_old removes = @blocks.reject { |e| e.remove.empty? } - if removes - outlist = @data_old[lo..hi].map { |e| e.insert(0, encode(' ')) } + if !removes.empty? + outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") } + + last_block = removes[-1] removes.each do |block| block.remove.each do |item| - outlist[item.position - lo].insert(0, encode(block.op)) # - or ! + outlist[item.position - lo][0, 1] = encode(block.op) # - or ! + end + + if last && block == last_block && old_missing_newline + outlist << encode("\\ No newline at end of file") end end + s << outlist.join("\n") end s << encode("\n--- #{r} ----\n") lo, hi = @start_new, @end_new inserts = @blocks.reject { |e| e.insert.empty? } - if inserts - outlist = @data_new[lo..hi].collect { |e| e.insert(0, encode(' ')) } + + if !inserts.empty? + outlist = @data_new[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") } + + last_block = inserts[-1] + inserts.each do |block| block.insert.each do |item| - outlist[item.position - lo].insert(0, encode(block.op)) # - or ! + outlist[item.position - lo][0, 1] = encode(block.op) # + or ! + end + + if last && block == last_block && new_missing_newline + outlist << encode("\\ No newline at end of file") end end s << outlist.join("\n") end + s end private :context_diff - def ed_diff(format) + def ed_diff(format, last = false) op_act = { '+' => 'a', '-' => 'd', '!' => 'c' } warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1 s = if format == :reverse_ed - encode("#{op_act[@blocks[0].op]}#{context_range(:old)}\n") + encode("#{op_act[@blocks[0].op]}#{context_range(:old, ',')}\n") else encode("#{context_range(:old, ' ')}#{op_act[@blocks[0].op]}\n") end unless @blocks[0].insert.empty? - @data_new[@start_new..@end_new].each do |e| s << e + encode("\n") end + @data_new[@start_new..@end_new].each do |e| + s << e.chomp + encode("\n") + end s << encode(".\n") end s @@ -239,7 +281,7 @@ def ed_diff(format) # Generate a range of item numbers to print. Only print 1 number if the # range has only one item in it. Otherwise, it's 'start,end' - def context_range(mode, op = ',') # rubocop:disable Naming/UncommunicativeMethodParamName + def context_range(mode, op, last = false) case mode when :old s, e = (@start_old + 1), (@end_old + 1) @@ -247,6 +289,9 @@ def context_range(mode, op = ',') # rubocop:disable Naming/UncommunicativeMethod s, e = (@start_new + 1), (@end_new + 1) end + e = e - 1 if last + e = 1 if e.zero? + s < e ? "#{s}#{op}#{e}" : e.to_s end private :context_range @@ -254,7 +299,7 @@ def context_range(mode, op = ',') # rubocop:disable Naming/UncommunicativeMethod # Generate a range of item numbers to print for unified diff. Print number # where block starts, followed by number of lines in the block # (don't print number of lines if it's 1) - def unified_range(mode) + def unified_range(mode, last) case mode when :old s, e = (@start_old + 1), (@end_old + 1) @@ -262,12 +307,25 @@ def unified_range(mode) s, e = (@start_new + 1), (@end_new + 1) end - length = e - s + 1 + length = e - s + (last ? 0 : 1) + first = length < 2 ? e : s # "strange, but correct" - length == 1 ? first.to_s : "#{first},#{length}" + length <= 1 ? first.to_s : "#{first},#{length}" end private :unified_range + def missing_last_newline?(data) + newline = encode("\n") + + if data[-2] + data[-2].end_with?(newline) && !data[-1].end_with?(newline) + elsif data[-1] + !data[-1].end_with?(newline) + else + true + end + end + if String.method_defined?(:encoding) def encode(literal, target_encoding = @preferred_data_encoding) literal.encode target_encoding diff --git a/lib/diff/lcs/ldiff.rb b/lib/diff/lcs/ldiff.rb index 2862267..8ea5f42 100644 --- a/lib/diff/lcs/ldiff.rb +++ b/lib/diff/lcs/ldiff.rb @@ -98,24 +98,19 @@ def run(args, _input = $stdin, output = $stdout, error = $stderr) #:nodoc: # items we've read from each file will differ by FLD (could be 0). file_length_difference = 0 - if @binary.nil? or @binary - data_old = IO.read(file_old) - data_new = IO.read(file_new) - - # Test binary status - if @binary.nil? - old_txt = data_old[0, 4096].scan(/\0/).empty? - new_txt = data_new[0, 4096].scan(/\0/).empty? - @binary = !old_txt or !new_txt - end + data_old = IO.read(file_old) + data_new = IO.read(file_new) + + # Test binary status + if @binary.nil? + old_txt = data_old[0, 4096].scan(/\0/).empty? + new_txt = data_new[0, 4096].scan(/\0/).empty? + @binary = !old_txt or !new_txt + end - unless @binary - data_old = data_old.split($/).map { |e| e.chomp } - data_new = data_new.split($/).map { |e| e.chomp } - end - else - data_old = IO.readlines(file_old).map { |e| e.chomp } - data_new = IO.readlines(file_new).map { |e| e.chomp } + unless @binary + data_old = data_old.lines.to_a + data_new = data_new.lines.to_a end # diff yields lots of pieces, each of which is basically a Block object @@ -163,13 +158,13 @@ def run(args, _input = $stdin, output = $stdout, error = $stderr) #:nodoc: end end - last = oldhunk.diff(@format) + last = oldhunk.diff(@format, true) last << "\n" if last.respond_to?(:end_with?) && !last.end_with?("\n") output << last output.reverse_each { |e| real_output << e.diff(:ed_finish) } if @format == :ed - 1 + return 1 end end diff --git a/spec/fixtures/ldiff/output.diff-c b/spec/fixtures/ldiff/output.diff-c index 92ea5a2..0e1ad99 100644 --- a/spec/fixtures/ldiff/output.diff-c +++ b/spec/fixtures/ldiff/output.diff-c @@ -1,5 +1,5 @@ -*** spec/fixtures/aX 2019-02-01 22:29:34.000000000 -0500 ---- spec/fixtures/bXaX 2019-02-01 22:29:43.000000000 -0500 +*** spec/fixtures/aX 2020-06-23 11:15:32.000000000 -0400 +--- spec/fixtures/bXaX 2020-06-23 11:15:32.000000000 -0400 *************** *** 1 **** ! aX diff --git a/spec/fixtures/ldiff/output.diff-u b/spec/fixtures/ldiff/output.diff-u index c4ba30f..b84f718 100644 --- a/spec/fixtures/ldiff/output.diff-u +++ b/spec/fixtures/ldiff/output.diff-u @@ -1,5 +1,5 @@ ---- spec/fixtures/aX 2019-02-01 22:29:34.000000000 -0500 -+++ spec/fixtures/bXaX 2019-02-01 22:29:43.000000000 -0500 +--- spec/fixtures/aX 2020-06-23 11:15:32.000000000 -0400 ++++ spec/fixtures/bXaX 2020-06-23 11:15:32.000000000 -0400 @@ -1 +1 @@ -aX +bXaX diff --git a/spec/fixtures/ldiff/output.diff.chef b/spec/fixtures/ldiff/output.diff.chef new file mode 100644 index 0000000..8b98efb --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef @@ -0,0 +1,4 @@ +3c3 +< "description": "hi" +--- +> "description": "lo" diff --git a/spec/fixtures/ldiff/output.diff.chef-c b/spec/fixtures/ldiff/output.diff.chef-c new file mode 100644 index 0000000..efbfa19 --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef-c @@ -0,0 +1,15 @@ +*** spec/fixtures/old-chef 2020-06-23 23:18:20.000000000 -0400 +--- spec/fixtures/new-chef 2020-06-23 23:18:20.000000000 -0400 +*************** +*** 1,4 **** + { + "name": "x", +! "description": "hi" + } +\ No newline at end of file +--- 1,4 ---- + { + "name": "x", +! "description": "lo" + } +\ No newline at end of file diff --git a/spec/fixtures/ldiff/output.diff.chef-e b/spec/fixtures/ldiff/output.diff.chef-e new file mode 100644 index 0000000..775d881 --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef-e @@ -0,0 +1,3 @@ +3c + "description": "lo" +. diff --git a/spec/fixtures/ldiff/output.diff.chef-f b/spec/fixtures/ldiff/output.diff.chef-f new file mode 100644 index 0000000..9bf1e67 --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef-f @@ -0,0 +1,3 @@ +c3 + "description": "lo" +. diff --git a/spec/fixtures/ldiff/output.diff.chef-u b/spec/fixtures/ldiff/output.diff.chef-u index ebf2b12..dbacd88 100644 --- a/spec/fixtures/ldiff/output.diff.chef-u +++ b/spec/fixtures/ldiff/output.diff.chef-u @@ -1,8 +1,9 @@ ---- spec/fixtures/old-chef 2019-02-01 21:57:15.000000000 -0400 -+++ spec/fixtures/new-chef 2019-02-01 21:57:29.000000000 -0400 -@@ -1,5 +1,5 @@ +--- spec/fixtures/old-chef 2020-06-23 23:18:20.000000000 -0400 ++++ spec/fixtures/new-chef 2020-06-23 23:18:20.000000000 -0400 +@@ -1,4 +1,4 @@ { "name": "x", - "description": "hi" + "description": "lo" } +\ No newline at end of file diff --git a/spec/fixtures/ldiff/output.diff.chef2-c b/spec/fixtures/ldiff/output.diff.chef2-c new file mode 100644 index 0000000..8349a7a --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef2-c @@ -0,0 +1,20 @@ +*** spec/fixtures/old-chef2 2020-06-30 09:43:35.000000000 -0400 +--- spec/fixtures/new-chef2 2020-06-30 09:44:32.000000000 -0400 +*************** +*** 1,5 **** + recipe[a::default] +- recipe[b::default] + recipe[c::default] + recipe[d::default] + recipe[e::default] +--- 1,4 ---- +*************** +*** 12,14 **** +--- 11,17 ---- + recipe[l::default] + recipe[m::default] + recipe[n::default] ++ recipe[o::new] ++ recipe[p::new] ++ recipe[q::new] ++ recipe[r::new] diff --git a/spec/fixtures/ldiff/output.diff.chef2-d b/spec/fixtures/ldiff/output.diff.chef2-d new file mode 100644 index 0000000..ca32a49 --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef2-d @@ -0,0 +1,7 @@ +d2 +a14 +recipe[o::new] +recipe[p::new] +recipe[q::new] +recipe[r::new] +. diff --git a/spec/fixtures/ldiff/output.diff.chef2-e b/spec/fixtures/ldiff/output.diff.chef2-e new file mode 100644 index 0000000..89f3fa0 --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef2-e @@ -0,0 +1,7 @@ +14a +recipe[o::new] +recipe[p::new] +recipe[q::new] +recipe[r::new] +. +2d diff --git a/spec/fixtures/ldiff/output.diff.chef2-u b/spec/fixtures/ldiff/output.diff.chef2-u new file mode 100644 index 0000000..ef025c7 --- /dev/null +++ b/spec/fixtures/ldiff/output.diff.chef2-u @@ -0,0 +1,16 @@ +--- spec/fixtures/old-chef2 2020-06-30 09:43:35.000000000 -0400 ++++ spec/fixtures/new-chef2 2020-06-30 09:44:32.000000000 -0400 +@@ -1,5 +1,4 @@ + recipe[a::default] +-recipe[b::default] + recipe[c::default] + recipe[d::default] + recipe[e::default] +@@ -12,3 +11,7 @@ + recipe[l::default] + recipe[m::default] + recipe[n::default] ++recipe[o::new] ++recipe[p::new] ++recipe[q::new] ++recipe[r::new] diff --git a/spec/fixtures/new-chef2 b/spec/fixtures/new-chef2 new file mode 100644 index 0000000..8213c73 --- /dev/null +++ b/spec/fixtures/new-chef2 @@ -0,0 +1,17 @@ +recipe[a::default] +recipe[c::default] +recipe[d::default] +recipe[e::default] +recipe[f::default] +recipe[g::default] +recipe[h::default] +recipe[i::default] +recipe[j::default] +recipe[k::default] +recipe[l::default] +recipe[m::default] +recipe[n::default] +recipe[o::new] +recipe[p::new] +recipe[q::new] +recipe[r::new] diff --git a/spec/fixtures/old-chef2 b/spec/fixtures/old-chef2 new file mode 100644 index 0000000..4a23407 --- /dev/null +++ b/spec/fixtures/old-chef2 @@ -0,0 +1,14 @@ +recipe[a::default] +recipe[b::default] +recipe[c::default] +recipe[d::default] +recipe[e::default] +recipe[f::default] +recipe[g::default] +recipe[h::default] +recipe[i::default] +recipe[j::default] +recipe[k::default] +recipe[l::default] +recipe[m::default] +recipe[n::default] diff --git a/spec/hunk_spec.rb b/spec/hunk_spec.rb index 277f739..b3616bf 100644 --- a/spec/hunk_spec.rb +++ b/spec/hunk_spec.rb @@ -6,28 +6,39 @@ require 'diff/lcs/hunk' describe Diff::LCS::Hunk do - let(:old_data) { ['Tu avec carté {count} itém has'.encode('UTF-16LE')] } - let(:new_data) { ['Tu avec carte {count} item has'.encode('UTF-16LE')] } + let(:old_data) { ['Tu a un carté avec {count} itéms'.encode('UTF-16LE')] } + let(:new_data) { ['Tu a un carte avec {count} items'.encode('UTF-16LE')] } let(:pieces) { Diff::LCS.diff old_data, new_data } let(:hunk) { Diff::LCS::Hunk.new(old_data, new_data, pieces[0], 3, 0) } it 'produces a unified diff from the two pieces' do expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp @@ -1 +1 @@ - -Tu avec carté {count} itém has - +Tu avec carte {count} item has + -Tu a un carté avec {count} itéms + +Tu a un carte avec {count} items EXPECTED expect(hunk.diff(:unified)).to eq(expected) end + it 'produces a unified diff from the two pieces (last entry)' do + expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp + @@ -1 +1 @@ + -Tu a un carté avec {count} itéms + +Tu a un carte avec {count} items + \\ No newline at end of file + EXPECTED + + expect(hunk.diff(:unified, true)).to eq(expected) + end + it 'produces a context diff from the two pieces' do expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp *************** *** 1 **** - ! Tu avec carté {count} itém has + ! Tu a un carté avec {count} itéms --- 1 ---- - ! Tu avec carte {count} item has + ! Tu a un carte avec {count} items EXPECTED expect(hunk.diff(:context)).to eq(expected) @@ -36,9 +47,9 @@ it 'produces an old diff from the two pieces' do expected = <<-EXPECTED.gsub(/^ +/, '').encode('UTF-16LE').chomp 1c1 - < Tu avec carté {count} itém has + < Tu a un carté avec {count} itéms --- - > Tu avec carte {count} item has + > Tu a un carte avec {count} items EXPECTED @@ -48,7 +59,7 @@ it 'produces a reverse ed diff from the two pieces' do expected = <<-EXPECTED.gsub(/^ +/, '').encode('UTF-16LE').chomp c1 - Tu avec carte {count} item has + Tu a un carte avec {count} items . EXPECTED @@ -62,7 +73,7 @@ it 'produces a unified diff' do expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp @@ -1 +1,2 @@ - +Tu avec carte {count} item has + +Tu a un carte avec {count} items EXPECTED expect(hunk.diff(:unified)).to eq(expected) diff --git a/spec/issues_spec.rb b/spec/issues_spec.rb index c9a1e53..ad366b3 100644 --- a/spec/issues_spec.rb +++ b/spec/issues_spec.rb @@ -95,4 +95,62 @@ EXPECTED end end + + describe "issue #65" do + def diff_lines(old_lines, new_lines) + file_length_difference = 0 + previous_hunk = nil + output = [] + + require 'byebug' + + Diff::LCS.diff(old_lines, new_lines).each do |piece| + hunk = Diff::LCS::Hunk.new(old_lines, new_lines, piece, 3, file_length_difference) + file_length_difference = hunk.file_length_difference + maybe_contiguous_hunks = (previous_hunk.nil? || hunk.merge(previous_hunk)) + + output << "#{previous_hunk.diff(:unified)}\n" unless maybe_contiguous_hunks + + previous_hunk = hunk + end + output << "#{previous_hunk.diff(:unified, true)}\n" unless previous_hunk.nil? + output.join + end + + it "should not misplace the new chunk" do + old_data = [ + "recipe[a::default]", "recipe[b::default]", "recipe[c::default]", + "recipe[d::default]", "recipe[e::default]", "recipe[f::default]", + "recipe[g::default]", "recipe[h::default]", "recipe[i::default]", + "recipe[j::default]", "recipe[k::default]", "recipe[l::default]", + "recipe[m::default]", "recipe[n::default]" + ] + + new_data = [ + "recipe[a::default]", "recipe[c::default]", "recipe[d::default]", + "recipe[e::default]", "recipe[f::default]", "recipe[g::default]", + "recipe[h::default]", "recipe[i::default]", "recipe[j::default]", + "recipe[k::default]", "recipe[l::default]", "recipe[m::default]", + "recipe[n::default]", "recipe[o::new]", "recipe[p::new]", + "recipe[q::new]", "recipe[r::new]" + ] + + expect(diff_lines(old_data, new_data)).to eq(<<-EOS) +@@ -1,5 +1,4 @@ + recipe[a::default] +-recipe[b::default] + recipe[c::default] + recipe[d::default] + recipe[e::default] +@@ -12,3 +11,7 @@ + recipe[l::default] + recipe[m::default] + recipe[n::default] ++recipe[o::new] ++recipe[p::new] ++recipe[q::new] ++recipe[r::new] +EOS + end + end end diff --git a/spec/ldiff_spec.rb b/spec/ldiff_spec.rb index 2f4c235..5cd355f 100644 --- a/spec/ldiff_spec.rb +++ b/spec/ldiff_spec.rb @@ -1,44 +1,43 @@ -# frozen_string_literal: true +# :frozen_string_literal => true require 'spec_helper' RSpec.describe 'bin/ldiff' do include CaptureSubprocessIO - let(:output_diff) { read_fixture } - let(:output_diff_c) { read_fixture('-c') } - let(:output_diff_e) { read_fixture('-e') } - let(:output_diff_f) { read_fixture('-f') } - let(:output_diff_u) { read_fixture('-u') } - let(:output_diff_chef) { read_fixture('-u', :base => 'output.diff.chef') } + fixtures = [ + { :name => 'output.diff', :left => 'aX', :right => 'bXaX' }, + { :name => 'output.diff.chef', :left => 'old-chef', :right => 'new-chef' }, + # { :name => 'output.diff.chef2', :left => 'old-chef2', :right => 'new-chef2' } + ].product([ nil, '-e', '-f', '-c', '-u']).map { |(fixture, flag)| + fixture = fixture.dup + fixture[:flag] = flag + fixture + } - specify do - expect(run_ldiff('-u', :left => 'old-chef', :right => 'new-chef')).to eq(output_diff_chef) - end - - specify do - expect(run_ldiff).to eq(output_diff) - end - - specify do - expect(run_ldiff('-c')).to eq(output_diff_c) - end + def self.test_ldiff(fixture) + desc = [ + fixture[:flag], + "spec/fixtures/#{fixture[:left]}", + "spec/fixtures/#{fixture[:right]}", + '#', + '=>', + "spec/fixtures/ldiff/#{fixture[:name]}#{fixture[:flag]}" + ].join(' ') - specify do - expect(run_ldiff('-e')).to eq(output_diff_e) - end - - specify do - expect(run_ldiff('-f')).to eq(output_diff_f) + it desc do + expect(run_ldiff(fixture)).to eq(read_fixture(fixture)) + end end - specify do - expect(run_ldiff('-u')).to eq(output_diff_u) + fixtures.each do |fixture| + test_ldiff(fixture) end - def read_fixture(flag = nil, options = {}) - base = options.fetch(:base, 'output.diff') - name = "spec/fixtures/ldiff/#{base}#{flag}" + def read_fixture(options) + fixture = options.fetch(:name) + flag = options.fetch(:flag) + name = "spec/fixtures/ldiff/#{fixture}#{flag}" data = IO.__send__(IO.respond_to?(:binread) ? :binread : :read, name) clean_data(data, flag) end @@ -68,13 +67,15 @@ def clean_output_timestamp(data) \s* (?:[-+]\d{4}|Z) }x, - '*** spec/fixtures/\1 0000-00-00 00:00:00.000000000 -0000' + '*** spec/fixtures/\1 0000-00-00 :00 =>:00 =>00.000000000 -0000' ) end - def run_ldiff(flag = nil, options = {}) - left = options.fetch(:left, 'aX') - right = options.fetch(:right, 'bXaX') + def run_ldiff(options) + flag = options.fetch(:flag) + left = options.fetch(:left) + right = options.fetch(:right) + stdout, stderr = capture_subprocess_io do system("ruby -Ilib bin/ldiff #{flag} spec/fixtures/#{left} spec/fixtures/#{right}") end