require 'rdf/isomorphic' module Matchers class BeEquivalentGraph Info = Struct.new(:about, :information, :trace, :compare, :inputDocument, :outputDocument) def normalize(graph) case @info.compare when :array array = case graph when RDF::Graph raise ":compare => :array used with Graph" when Array graph.sort else graph.to_s.split("\n"). map {|t| t.gsub(/^\s*(.*)\s*$/, '\1')}. reject {|t2| t2.match(/^\s*$/)}. compact. sort. uniq end # Implement to_ntriples on array, to simplify logic later def array.to_ntriples; self.join("\n") + "\n"; end array else case graph when RDF::Graph then graph when IO, StringIO RDF::Graph.new.load(graph, :base_uri => @info.about) else # Figure out which parser to use g = RDF::Graph.new reader_class = RDF::Reader.for(detect_format(graph)) reader_class.new(graph, :base_uri => @info.about).each {|s| g << s} g end end end def initialize(expected, info) @info = if info.respond_to?(:about) info elsif info.is_a?(Hash) identifier = info[:identifier] || expected.is_a?(RDF::Graph) ? expected.context : info[:about] trace = info[:trace] trace = trace.join("\n") if trace.is_a?(Array) Info.new(identifier, info[:information] || "", trace, info[:compare]) else Info.new(expected.is_a?(RDF::Graph) ? expected.context : info, info.to_s) end @expected = normalize(expected) end def matches?(actual) @actual = normalize(actual) if @info.compare == :array @actual == @expected else @actual.isomorphic_with?(@expected) end end def failure_message_for_should info = @info.respond_to?(:information) ? @info.information : @info.inspect if @expected.is_a?(RDF::Graph) && @actual.size != @expected.size "Graph entry count differs:\nexpected: #{@expected.size}\nactual: #{@actual.size}" elsif @expected.is_a?(Array) && @actual.size != @expected.length "Graph entry count differs:\nexpected: #{@expected.length}\nactual: #{@actual.size}" else "Graph differs" end + "\n#{info + "\n" unless info.empty?}" + (@info.inputDocument ? "Input file: #{@info.inputDocument}\n" : "") + (@info.outputDocument ? "Output file: #{@info.outputDocument}\n" : "") + "Unsorted Expected:\n#{@expected.to_ntriples}" + "Unsorted Results:\n#{@actual.to_ntriples}" + # "Unsorted Expected Dump:\n#{@expected.dump}\n" + # "Unsorted Results Dump:\n#{@actual.dump}" + (@info.trace ? "\nDebug:\n#{@info.trace}" : "") end def negative_failure_message "Graphs do not differ\n" end end def be_equivalent_graph(expected, info = nil) BeEquivalentGraph.new(expected, info) end class MatchRE Info = Struct.new(:about, :information, :trace, :inputDocument, :outputDocument) def initialize(expected, info) @info = if info.respond_to?(:about) info elsif info.is_a?(Hash) identifier = info[:identifier] || info[:about] trace = info[:trace] trace = trace.join("\n") if trace.is_a?(Array) Info.new(identifier, info[:information] || "", trace, info[:inputDocument], info[:outputDocument]) else Info.new(info, info.to_s) end @expected = expected end def matches?(actual) @actual = actual @actual.to_s.match(@expected) end def failure_message_for_should info = @info.respond_to?(:information) ? @info.information : @info.inspect "Match failed" "\n#{info + "\n" unless info.empty?}" + (@info.inputDocument ? "Input file: #{@info.inputDocument}\n" : "") + (@info.outputDocument ? "Output file: #{@info.outputDocument}\n" : "") + "Expression: #{@expected}\n" + "Unsorted Results:\n#{@actual}" + (@info.trace ? "\nDebug:\n#{@info.trace}" : "") end def negative_failure_message "Match succeeded\n" end end def match_re(expected, info = nil) MatchRE.new(expected, info) end end