Skip to content

Instantly share code, notes, and snippets.

@cjheath
Created October 21, 2010 03:37
Show Gist options
  • Save cjheath/637887 to your computer and use it in GitHub Desktop.
Save cjheath/637887 to your computer and use it in GitHub Desktop.
RSpec differ_from matcher (calls other matchers)
module RSpec
module Matchers
class ArrayMatcher < Matcher
def initialize expected
super(:be_different_array_from, expected) do |*_expected|
match_for_should do |actual|
perform_match(actual, _expected[0])
@extra + @missing != []
end
match_for_should_not do |actual|
perform_match(actual, _expected[0])
@extra + @missing == []
end
def perform_match(actual, expected)
@extra = actual - expected
@missing = expected - actual
end
def failure_message_for_should
"expected a difference in the two lists, but got none"
end
failure_message_for_should_not do |actual|
"expected no difference, but result #{
[ (@missing.empty? ? nil : 'lacks '+@missing.sort.inspect),
(@extra.empty? ? nil : 'has extra '+@extra.sort.inspect)
].compact * ' and '
}"
end
end
end
end
def be_different_array_from(expected)
ArrayMatcher.new(expected)
end
end
end
require 'pathname'
module RSpec
module Matchers
def differ_from(expected)
case expected
when Pathname
FileMatcher.new(expected)
when Array
ArrayMatcher.new(expected)
when String
FileMatcher.new(expected)
else
raise "DiffMatcher doesn't know how to match a #{expected.class}"
end
end
end
end
require 'diff/lcs'
module RSpec
module Matchers
class FileMatcher < Matcher
def initialize expected
super(:have_different_contents, expected) do |*_expected|
match_for_should do |actual|
perform_match(actual, _expected[0])
end
match_for_should_not do |actual|
!perform_match(actual, _expected[0])
end
def perform_match(actual, expected)
expected = File.open(expected).read if expected.is_a?(Pathname)
expected = expected.scan(/[^\n]+/) unless expected.is_a?(Array)
actual = File.open(actual).read if actual.is_a?(Pathname)
actual = actual.scan(/[^\n]+/) unless actual.is_a?(Array)
differences = Diff::LCS::diff(expected, actual)
@diff = differences.map do |chunk|
added_at = (add = chunk.detect{|d| d.action == '+'}) && add.position+1
removed_at = (remove = chunk.detect{|d| d.action == '-'}) && remove.position+1
"Line #{added_at}/#{removed_at}:\n"+
chunk.map do |change|
"#{change.action} #{change.element}"
end*"\n"
end*"\n"
@diff != ''
end
def failure_message_for_should
"expected a difference, but got none"
end
failure_message_for_should_not do |actual|
"expected no difference, but got:\n#{@diff}"
end
end
end
end
def have_different_contents(expected)
FileMatcher.new(expected)
end
end
end
require 'diff/lcs'
module RSpec
module Matchers
def have_different_contents(expected)
Matcher.new :have_different_contents, expected do |*_expected|
match_for_should do |actual|
perform_match(actual, _expected)
end
match_for_should_not do |actual|
!perform_match(actual, _expected)
end
def perform_match(actual, expected)
expected_lines = expected.scan(/[^\n]+/)
actual_lines = actual.scan(/[^\n]+/)
differences = Diff::LCS::diff(expected_lines, actual_lines)
@diff = differences.map do |chunk|
added_at = (add = chunk.detect{|d| d.action == '+'}) && add.position+1
removed_at = (remove = chunk.detect{|d| d.action == '-'}) && remove.position+1
"Line #{added_at}/#{removed_at}:\n"+
chunk.map do |change|
"#{change.action} #{change.element}"
end*"\n"
end*"\n"
@diff != ''
end
def failure_message_for_should
"expected a difference, but got none"
end
failure_message_for_should_not do |actual|
"expected no difference, but got:\n#{@diff}"
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment