Skip to content

Commit

Permalink
+ assert_equal (and must_equal) now tries to diff output where it mak…
Browse files Browse the repository at this point in the history
…es sense.

+ Added Assertions.diff and diff=
+ Added Assertions#diff(exp, act) to be used by assert_equal.
+ Added Assertions#mu_pp_for_diff

[git-p4: depot-paths = "//src/minitest/dev/": change = 6259]
  • Loading branch information
zenspider committed May 25, 2011
1 parent 716c57f commit c6ae48d
Show file tree
Hide file tree
Showing 2 changed files with 224 additions and 12 deletions.
106 changes: 101 additions & 5 deletions lib/minitest/unit.rb
Expand Up @@ -59,16 +59,105 @@ def self.filter_backtrace bt # :nodoc:

module Assertions

WINDOZE = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/

##
# Returns the diff command to use in #diff. Tries to intelligently
# figure out what diff to use.

def self.diff
@diff = if WINDOZE
"diff.exe -u"
else
if system("gdiff", __FILE__, __FILE__)
"gdiff -u" # solaris and kin suck
elsif system("diff", __FILE__, __FILE__)
"diff -u"
else
nil
end
end unless defined? @diff

@diff
end

##
# mu_pp gives a human-readable version of +obj+. By default #inspect is
# called. You can override this to use #pretty_print if you want.
# Set the diff command to use in #diff.

def self.diff= o
@diff = o
end

##
# Returns a diff between +exp+ and +act+. If there is no known
# diff command or if it doesn't make sense to diff the output
# (single line, short output), then it simply returns a basic
# comparison between the two.

def diff exp, act
require "tempfile"

expect = mu_pp_for_diff exp
butwas = mu_pp_for_diff act
result = nil

need_to_diff =
MiniTest::Assertions.diff &&
(expect.include?("\n") ||
butwas.include?("\n") ||
expect.size > 30 ||
butwas.size > 30 ||
expect == butwas)

return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
need_to_diff

Tempfile.open("expect") do |a|
a.puts expect
a.rewind
Tempfile.open("butwas") do |b|
b.puts butwas
b.rewind

result = `#{MiniTest::Assertions.diff} #{a.path} #{b.path}`
result.sub!(/^\-\-\- .+/, "--- expected")
result.sub!(/^\+\+\+ .+/, "+++ actual")

if result.empty? then
klass = exp.class
result = [
"No visible difference.",
"You should look at your implementation of #{klass}#==.",
expect
].join "\n"
end
end
end

result
end

##
# This returns a human-readable version of +obj+. By default
# #inspect is called. You can override this to use #pretty_print
# if you want.

def mu_pp obj
s = obj.inspect
s = s.force_encoding Encoding.default_external if defined? Encoding
s
end

##
# This returns a diff-able human-readable version of +obj+. This
# differs from the regular mu_pp because it expands escaped
# newlines and makes hex-values generic (like object_ids). This
# uses mu_pp to do the first pass and then cleans it up.

def mu_pp_for_diff obj # TODO: possibly rename
mu_pp(obj).gsub(/\\n/, "\n").gsub(/0x[a-f0-9]+/m, '0xXXXXXX')
end

def _assertions= n # :nodoc:
@_assertions = n
end
Expand Down Expand Up @@ -108,12 +197,19 @@ def assert_empty obj, msg = nil
end

##
# Fails unless <tt>exp == act</tt>.
# Fails unless <tt>exp == act</tt> printing the difference between
# the two, if possible.
#
# If there is no visible difference but the assertion fails, you
# should suspect that your #== is buggy, or your inspect output is
# missing crucial details.
#
# For floats use assert_in_delta.
#
# For floats use assert_in_delta
# See also: MiniTest::Assertions.diff

def assert_equal exp, act, msg = nil
msg = message(msg) { "Expected #{mu_pp(exp)}, not #{mu_pp(act)}" }
msg = message(msg) { diff exp, act }
assert(exp == act, msg)
end

Expand Down
130 changes: 123 additions & 7 deletions test/test_minitest_unit.rb
Expand Up @@ -480,12 +480,113 @@ def test_assert_equal
@tc.assert_equal 1, 1
end

def test_assert_equal_different
util_assert_triggered "Expected 1, not 2." do
def test_assert_equal_different_diff_deactivated
without_diff do
util_assert_triggered util_msg("haha" * 10, "blah" * 10) do
o1 = "haha" * 10
o2 = "blah" * 10

@tc.assert_equal o1, o2
end
end
end

def test_assert_equal_different_hex
s = Struct.new :name

o1 = s.new "a"
o2 = s.new "b"
msg = "--- expected
+++ actual
@@ -1 +1 @@
-#{o1.inspect.sub(/0x\w+/, "0xXXXXXX")}
+#{o2.inspect.sub(/0x\w+/, "0xXXXXXX")}
.".gsub(/^ +/, "")

util_assert_triggered msg do
@tc.assert_equal o1, o2
end
end

def test_assert_equal_different_hex_invisible
o1 = Object.new
o2 = Object.new

msg = "No visible difference.
You should look at your implementation of Object#==.
#<Object:0xXXXXXX>.".gsub(/^ +/, "")

util_assert_triggered msg do
@tc.assert_equal o1, o2
end
end

def test_assert_equal_different_long
msg = "--- expected
+++ actual
@@ -1 +1 @@
-\"hahahahahahahahahahahahahahahahahahahaha\"
+\"blahblahblahblahblahblahblahblahblahblah\"
.".gsub(/^ +/, "")

util_assert_triggered msg do
o1 = "haha" * 10
o2 = "blah" * 10

@tc.assert_equal o1, o2
end
end

def test_assert_equal_different_long_invisible
msg = "No visible difference.
You should look at your implementation of String#==.
\"blahblahblahblahblahblahblahblahblahblah\".".gsub(/^ +/, "")

util_assert_triggered msg do
o1 = "blah" * 10
o2 = "blah" * 10
def o1.== o
false
end
@tc.assert_equal o1, o2
end
end

def test_assert_equal_different_long_msg
msg = "message.
--- expected
+++ actual
@@ -1 +1 @@
-\"hahahahahahahahahahahahahahahahahahahaha\"
+\"blahblahblahblahblahblahblahblahblahblah\"
.".gsub(/^ +/, "")

util_assert_triggered msg do
o1 = "haha" * 10
o2 = "blah" * 10
@tc.assert_equal o1, o2, "message"
end
end

def test_assert_equal_different_short
util_assert_triggered util_msg(1, 2) do
@tc.assert_equal 1, 2
end
end

def test_assert_equal_different_short_msg
util_assert_triggered util_msg(1, 2, "message") do
@tc.assert_equal 1, 2, "message"
end
end

def test_assert_equal_different_short_multiline
msg = "--- expected\n+++ actual\n@@ -1,2 +1,2 @@\n \"a\n-b\"\n+c\"\n."
util_assert_triggered msg do
@tc.assert_equal "a\nb", "a\nc"
end
end

def test_assert_in_delta
@tc.assert_in_delta 0.0, 1.0 / 1000, 0.1
end
Expand Down Expand Up @@ -636,7 +737,7 @@ def test_assert_output_out
end

def test_assert_output_triggered_both
util_assert_triggered "In stdout.\nExpected \"yay\", not \"boo\"." do
util_assert_triggered util_msg("yay", "boo", "In stdout") do
@tc.assert_output "yay", "blah" do
print "boo"
$stderr.print "blah blah"
Expand All @@ -645,15 +746,15 @@ def test_assert_output_triggered_both
end

def test_assert_output_triggered_err
util_assert_triggered "In stderr.\nExpected \"blah\", not \"blah blah\"." do
util_assert_triggered util_msg("blah", "blah blah", "In stderr") do
@tc.assert_output nil, "blah" do
$stderr.print "blah blah"
end
end
end

def test_assert_output_triggered_out
util_assert_triggered "In stdout.\nExpected \"blah\", not \"blah blah\"." do
util_assert_triggered util_msg("blah", "blah blah", "In stdout") do
@tc.assert_output "blah" do
print "blah blah"
end
Expand Down Expand Up @@ -832,15 +933,15 @@ def test_assert_silent
def test_assert_silent_triggered_err
@assertion_count = 2

util_assert_triggered "In stderr.\nExpected \"\", not \"blah blah\"." do
util_assert_triggered util_msg("", "blah blah", "In stderr") do
@tc.assert_silent do
$stderr.print "blah blah"
end
end
end

def test_assert_silent_triggered_out
util_assert_triggered "In stdout.\nExpected \"\", not \"blah blah\"." do
util_assert_triggered util_msg("", "blah blah", "In stdout") do
@tc.assert_silent do
print "blah blah"
end
Expand Down Expand Up @@ -1148,4 +1249,19 @@ def util_assert_triggered expected, klass = MiniTest::Assertion

assert_equal expected, msg
end

def util_msg exp, act, msg = nil
s = "Expected: #{exp.inspect}\n Actual: #{act.inspect}."
s = "#{msg}.\n#{s}" if msg
s
end

def without_diff
old_diff = MiniTest::Assertions.diff
MiniTest::Assertions.diff = nil

yield
ensure
MiniTest::Assertions.diff = old_diff
end
end

0 comments on commit c6ae48d

Please sign in to comment.