diff --git a/bin/cmdtest.rb b/bin/cmdtest.rb index 02198c9..728b038 100755 --- a/bin/cmdtest.rb +++ b/bin/cmdtest.rb @@ -560,6 +560,7 @@ module Cmdtest pr.add("", "--version", "show version") pr.add("-q", "--quiet", "be more quiet") pr.add("-v", "--verbose", "be more verbose") + pr.add("", "--diff", "experimental diff output") pr.add("", "--fast", "run fast without waiting for unique mtime:s") pr.add("-j", "--parallel", "build in parallel", type: Integer, default: 1, metavar: "N") pr.add("", "--test", "only run named test", type: [String]) diff --git a/lib/cmdtest/lcs.rb b/lib/cmdtest/lcs.rb new file mode 100755 index 0000000..b368a69 --- /dev/null +++ b/lib/cmdtest/lcs.rb @@ -0,0 +1,134 @@ +#---------------------------------------------------------------------- +# lcs.rb +#---------------------------------------------------------------------- +# Copyright 2016 Johan Holmberg. +#---------------------------------------------------------------------- +# This file is part of "cmdtest". +# +# "cmdtest" is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# "cmdtest" is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with "cmdtest". If not, see <http://www.gnu.org/licenses/>. +#---------------------------------------------------------------------- + +module Cmdtest + + class LCS + def print() + i = @m + j = @n + while i > 0 || j > 0 + if @b[i][j] == :XY + acc = [] + while @b[i][j] == :XY + acc << @y[j] + i -= 1 + j -= 1 + end + found_xy(acc) + elsif i > 0 && (j == 0 || @b[i][j] == :X) + acc = [] + while i > 0 && (j == 0 || @b[i][j] == :X) + acc << @x[i] + i -= 1 + end + found_x(acc) + elsif j > 0 && (i == 0 || @b[i][j] == :Y) + acc = [] + while j > 0 && (i == 0 || @b[i][j] == :Y) + acc << @y[j] + j -= 1 + end + found_y(acc) + else + raise "internal error" + end + end + end + + def initialize(x, y) + @m = x.size + @n = y.size + @x = [nil] + x.reverse + @y = [nil] + y.reverse + @c = Array.new(@m+1) { Array.new(@n+1) } + @b = Array.new(@m+1) { Array.new(@n+1) } + + for i in 1..@m + @c[i][0] = 0 + end + + for j in 0..@n + @c[0][j] = 0 + end + + for i in 1..@m + for j in 1..@n + if @x[i] === @y[j] + @c[i][j] = @c[i-1][j-1] + 1 + @b[i][j] = :XY + elsif @c[i-1][j] >= @c[i][j-1] + @c[i][j] = @c[i-1][j] + @b[i][j] = :X + else + @c[i][j] = @c[i][j-1] + @b[i][j] = :Y + end + end + end + end + + def found_xy(arr) + end + + def found_x(arr) + end + + def found_y(arr) + end + end + + class DiffLCS < LCS + include Enumerable + + def found_x(arr) + for e in arr + if Regexp === e + @arr << "- " + e.inspect + else + @arr << "- " + e + end + end + end + + def found_y(arr) + for e in arr + @arr << "+ " + e + end + end + + def found_xy(arr) + for e in arr + @arr << " " + e + end + end + + def each + @arr = [] + print + for e in @arr + yield e + end + end + end + + +end diff --git a/lib/cmdtest/testcase.rb b/lib/cmdtest/testcase.rb index a32f6fc..230c17d 100644 --- a/lib/cmdtest/testcase.rb +++ b/lib/cmdtest/testcase.rb @@ -23,6 +23,8 @@ require "fileutils" require "set" require "stringio" +require "cmdtest/lcs" + module Cmdtest class AssertFailed < RuntimeError ; end @@ -385,9 +387,15 @@ module Cmdtest expected = files.flatten.sort #p [:xxx_files, xxx, actual, expected] _assert0 actual == expected do - _format_output(xxx.to_s.gsub(/_/, " ").gsub(/modified/, "changed"), - actual.inspect + "\n", - expected.inspect + "\n") + if @_runner.opts.diff + _format_output(xxx.to_s.gsub(/_/, " ").gsub(/modified/, "changed"), + actual, + expected) + else + _format_output(xxx.to_s.gsub(/_/, " ").gsub(/modified/, "changed"), + actual.inspect + "\n", + expected.inspect + "\n") + end end end @@ -663,17 +671,26 @@ module Cmdtest end end - def _indented_lines(prefix, output) - case output - when Array - lines = output - when String - lines = output.split(/\n/, -1) + def _to_lines(str) + if Array === str + lines = str + else + lines = str.split(/\n/, -1) if lines[-1] == "" lines.pop elsif ! lines.empty? lines[-1] << "[[missing newline]]" end + end + return lines + end + + def _indented_lines(prefix, output) + case output + when Array + lines = output + when String + lines = _to_lines(output) when Regexp lines = [output] else @@ -696,8 +713,15 @@ module Cmdtest def _format_output(error, actual, expected) res = "" res << "ERROR: #{error}\n" - res << _indented_lines(" actual: ", actual) - res << _indented_lines(" expect: ", expected) + if @_runner.opts.diff && (Array === expected || String === expected) + expected_lines = _to_lines(expected) + actual_lines = _to_lines(actual) + diff = DiffLCS.new(expected_lines, actual_lines) + res << _indented_lines(" ", diff.to_a) + else + res << _indented_lines(" actual: ", actual) + res << _indented_lines(" expect: ", expected) + end return res end