$:.unshift "."
require File.join(File.dirname(__FILE__), 'spec_helper')

describe RDF::RDFa::Format do
  context "should be discover 'rdfa'" do
    [
      [:rdfa,                                        RDF::RDFa::Format],
      ["etc/foaf.html",                              RDF::RDFa::Format],
      [{:file_name      => "etc/foaf.html"},         RDF::RDFa::Format],
      [{:file_extension => "html"},                  RDF::RDFa::Format],
      [{:file_extension => "xhtml"},                 RDF::RDFa::XHTML],
      [{:file_extension => "svg"},                   RDF::RDFa::SVG],
      [{:content_type   => "text/html"},             RDF::RDFa::Format],
      [{:content_type   => "application/xhtml+xml"}, RDF::RDFa::XHTML],
      [{:content_type   => "image/svg+xml"},         RDF::RDFa::SVG],
    ].each do |(arg, format)|
      it "returns #{format} for #{arg.inspect}" do
        RDF::Format.for(arg).should == format
      end
    end
  end
    
  it "should discover 'html'" do
    RDF::Format.for(:html).reader.should == RDF::RDFa::Reader
    RDF::Format.for(:html).writer.should == RDF::RDFa::Writer
  end

  it "should discover 'xhtml'" do
    RDF::Format.for(:xhtml).reader.should == RDF::RDFa::Reader
    RDF::Format.for(:xhtml).writer.should == RDF::RDFa::Writer
  end

  it "should discover 'svg'" do
    RDF::Format.for(:svg).reader.should == RDF::RDFa::Reader
    RDF::Format.for(:svg).writer.should == RDF::RDFa::Writer
  end
end

describe "RDF::RDFa::Reader" do
  describe "discovery" do
    {
      "html" => RDF::Reader.for(:rdfa),
      "etc/foaf.html" => RDF::Reader.for("etc/foaf.html"),
      "foaf.html" => RDF::Reader.for(:file_name      => "foaf.html"),
      ".html" => RDF::Reader.for(:file_extension => "html"),
      "application/xhtml+xml" => RDF::Reader.for(:content_type   => "application/xhtml+xml"),
    }.each_pair do |label, format|
      it "should discover '#{label}'" do
        format.should == RDF::RDFa::Reader
      end
    end
  end

  describe :interface do
    before(:each) do
      @sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml"
              xmlns:dc="http://purl.org/dc/elements/1.1/">
        <head>
          <title>Test 0001</title>
        </head>
        <body>
          <p>This photo was taken by <span class="author" about="photo1.jpg" property="dc:creator">Mark Birbeck</span>.</p>
        </body>
        </html>
        )
    end

    it "should yield reader" do
      inner = mock("inner")
      inner.should_receive(:called).with(RDF::RDFa::Reader)
      RDF::RDFa::Reader.new(@sampledoc) do |reader|
        inner.called(reader.class)
      end
    end

    it "should return reader" do
      RDF::RDFa::Reader.new(@sampledoc).should be_a(RDF::RDFa::Reader)
    end

    it "should yield statements" do
      inner = mock("inner")
      inner.should_receive(:called).with(RDF::Statement)
      RDF::RDFa::Reader.new(@sampledoc).each_statement do |statement|
        inner.called(statement.class)
      end
    end

    it "should yield triples" do
      inner = mock("inner")
      inner.should_receive(:called).with(RDF::URI, RDF::URI, RDF::Literal)
      RDF::RDFa::Reader.new(@sampledoc).each_triple do |subject, predicate, object|
        inner.called(subject.class, predicate.class, object.class)
      end
    end
  end

  describe "parsing a simple doc" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml"
              xmlns:dc="http://purl.org/dc/elements/1.1/">
        <head>
          <title>Test 0001</title>
        </head>
        <body>
          <p>This photo was taken by <span class="author" about="photo1.jpg" property="dc:creator">Mark Birbeck</span>.</p>
        </body>
        </html>
        )

      @graph = parse(sampledoc, :base_uri => "http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/0001.xhtml", :validate => true)
      @statement = @graph.statements.first
    end

    it "should return 1 triple" do
      @graph.size.should == 1
    end

    it "should have a subject with an expanded URI" do
      @statement.subject.should == RDF::URI('http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/photo1.jpg')
    end

    it "should have a predicate of dc:creator" do
      @statement.predicate.should == RDF::DC11.creator
    end

    it "should have an object of type literal and value 'Mark Birkbeck'" do
      @statement.object.should == RDF::Literal("Mark Birbeck")
    end
  end

  describe "parsing a simple doc without a base URI" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml"
              xmlns:dc="http://purl.org/dc/elements/1.1/">
        <body>
          <p>This photo was taken by <span class="author" about="_:photo" property="dc:creator">Mark Birbeck</span>.</p>
        </body>
        </html>
        )

      @graph = parse(sampledoc, :validate => true)
      @statement = @graph.statements.first
    end

    it "should return 1 triple" do
      @graph.size.should == 1
    end

    it "should have a Blank Node named 'photo' as the subject of the triple" do
      @statement.subject.should == RDF::Node('photo')
    end

    it "should have a predicate of dc:creator" do
      @statement.predicate.should == RDF::DC11.creator
    end

    it "should have an object of type literal and value 'Mark Birkbeck'" do
      @statement.object.should == RDF::Literal("Mark Birbeck")
    end
  end

  describe "parsing a document containing an XML Literal" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml"
              xmlns:dc="http://purl.org/dc/elements/1.1/"
              xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
          <head>
            <title>Test 0011</title>
          </head>
          <body>
            <div about="">
              Author: <span property="dc:creator">Albert Einstein</span>
              <h2 property="dc:title" datatype="rdf:XMLLiteral">E = mc<sup>2</sup>: The Most Urgent Problem of Our Time</h2>
          </div>
          </body>
        </html>
        )

      @graph = parse(sampledoc, :base_uri => "http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/0011.xhtml", :validate => true)
    end

    it "should return 2 triples" do
      @graph.size.should == 2
    end

    it "should have a triple for the dc:creator of the document" do
      @graph.should have_triple([
        RDF::URI('http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/0011.xhtml'),
        RDF::DC11.creator,
        "Albert Einstein"
      ])
    end

    it "should have an XML Literal for the dc:title of the document" do
      begin
        @graph.should have_triple([
          RDF::URI('http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/0011.xhtml'),
          RDF::DC11.title,
          RDF::Literal(%(E = mc<sup xmlns="http://www.w3.org/1999/xhtml" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">2</sup>: The Most Urgent Problem of Our Time), :datatype => RDF.XMLLiteral)
        ])
      rescue RSpec::Expectations::ExpectationNotMetError => e
        pending("XMLLiteral canonicalization not implemented yet")
      end
    end
  end

  describe "parsing a document containing sereral bnodes" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa 1.1"
              xmlns:foaf="http://xmlns.com/foaf/0.1/">
          <head>
          <title>Test 0017</title>
          </head>
          <body>
             <p>
                  <span about="[_:a]" property="foaf:name">Manu Sporny</span>
                   <span about="[_:a]" rel="foaf:knows" resource="[_:b]">knows</span>
                   <span about="[_:b]" property="foaf:name">Ralph Swick</span>.
                </p>
          </body>
        </html>
        )

      @graph = parse(sampledoc, :base_uri => "http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/0017.xhtml", :validate => true)
    end

    it "should return 3 triples" do
      @graph.size.should == 3
    end

    it "should have a triple for the foaf:name of BNode A" do
      @graph.should have_triple([
        RDF::Node('a'),
        RDF::FOAF.name,
        "Manu Sporny"
      ])
    end

    it "should have a triple for the foaf:name of BNode B" do
      @graph.should have_triple([
        RDF::Node('b'),
        RDF::FOAF.name,
        "Ralph Swick"
      ])
    end

    it "should have a triple for BNode A knows BNode B" do
      @graph.should have_triple([
        RDF::Node('a'),
        RDF::FOAF.knows,
        RDF::Node('b'),
      ])
    end
  end

  describe "parsing a document that uses the typeof attribute" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa 1.1"
              xmlns:foaf="http://xmlns.com/foaf/0.1/">
          <head>
            <title>Test 0049</title>
          </head>
          <body>
            <div about="http://www.example.org/#me" typeof="foaf:Person">
              <p property="foaf:name">John Doe</p>
            </div>
          </body>
        </html>
        )

      @graph = parse(sampledoc, :base_uri => "http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/0049.xhtml", :validate => true)
    end

    it "should return 2 triples" do
      @graph.size.should == 2
    end

    it "should have a triple stating that #me is of type foaf:Person" do
      @graph.should have_triple([
        RDF::URI('http://www.example.org/#me'),
        RDF.type,
        RDF::FOAF.Person
      ])
    end

    it "should have a triple stating that #me has name 'John Doe'" do
      @graph.should have_triple([
        RDF::URI('http://www.example.org/#me'),
        RDF::FOAF.name,
        RDF::Literal("John Doe")
      ])
    end
  end

  describe "parsing a document with a <base> tag in the <head>" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa 1.1"
            xmlns:dc="http://purl.org/dc/elements/1.1/">
         <head>
            <base href="http://www.example.org/"></base>
            <title>Test 0072</title>
         </head>
         <body>
            <p about="faq">
               Learn more by reading the example.org
               <span property="dc:title">Example FAQ</span>.
            </p>
         </body>
        </html>
        )

      @graph = parse(sampledoc, :base_uri => "http://rdfa.digitalbazaar.com/test-suite/test-cases/xhtml1/0072.xhtml", :validate => true)
    end

    it "should return 1 triple" do
      @graph.size.should == 1
    end

    it "should have the subject of the triple relative to the URI in base" do
      @graph.should have_subject RDF::URI('http://www.example.org/faq')
    end
  end

  describe "parsing a document with a profile containing a prefix mapping" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa 1.1">
          <head>
            <title>Test</title>
            <base href="http://example.org/"/>
          </head>
          <body profile="http://rdfa.digitalbazaar.com/test-suite/test-cases/tests/../../profiles/basic">
          <div about="#me">
            <p>
              <span property="foaf:name">Ivan Herman</span>
            </p>
          </div>
          </body>
        </html>
        )
      
      @graph = parse(sampledoc, :validate => true)
      @statement = @graph.statements.first
    end

    it "should return 1 triple" do
      @graph.size.should == 1
    end

    it "should have a subject of http://example.org/#me" do
      @statement.subject.should == RDF::URI('http://example.org/#me')
    end

    it "should have a predicate of foaf:name" do
      @statement.predicate.should == RDF::FOAF.name
    end

    it "should have an object with the literal 'Ivan Herman'" do
      @statement.object.should == RDF::Literal('Ivan Herman')
    end
  end

  describe "parsing a document with a profile containing a term mapping" do
    before :each do
      sampledoc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.1//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-2.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" version="XHTML+RDFa 1.1">
          <head>
            <title>Test</title>
            <base href="http://example.org/"/>
          </head>
          <body profile="http://rdfa.digitalbazaar.com/test-suite/test-cases/tests/../../profiles/foaf">
          <div about="#me">
            <p>
              <span property="name">Ivan Herman</span>
            </p>
          </div>
          </body>
        </html>
      )
      
      @graph = parse(sampledoc, :validate => true)
      @statement = @graph.statements.first
    end

    it "should return 1 triple" do
      @graph.size.should == 1
    end

    it "should have a subject of http://example.org/#me" do
      @statement.subject.should == RDF::URI('http://example.org/#me')
    end

    it "should have a predicate of foaf:name" do
      @statement.predicate.should == RDF::FOAF.name
    end

    it "should have an object with the literal 'Ivan Herman'" do
      @statement.object.should == RDF::Literal('Ivan Herman')
    end
  end

  describe :profiles do
    before(:each) do
      @profile = StringIO.new(%q(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html>
        <html xmlns="http://www.w3.org/1999/xhtml">
          <head>
            <title>Test mappings</title>
          </head>
          <body prefix="rdfa: http://www.w3.org/ns/rdfa#">
            <p typeof=""><span property="rdfa:uri">http://example.org/</span><span property="rdfa:prefix">foo</span></p>
            <p typeof=""><span property="rdfa:uri">http://example.org/title</span><span property="rdfa:term">title</span></p>
          </body>
        </html>
      ))
      def @profile.content_type; "text/html"; end
      def @profile.base_uri; "http://example.com/profile"; end
      
      @doc = %(<?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE html>
        <html xmlns="http://www.w3.org/1999/xhtml">
          <body profile="http://example.com/profile">
            <div about="http://example.com/doc" typeof="foo:Agent">
              <p property="title">A particular agent</p>
            </div>
          </body>
        </html>
        )

      RDF::Util::File.stub!(:open_file).and_yield(@profile)

      @profile_repository = RDF::Repository.new(:title => "Test Profile Repository")
      @debug = []
      @reader = RDF::RDFa::Reader.new(@doc, :profile_repository => @profile_repository, :debug => @debug, :validate => true)
      
      @expected = RDF::Graph.new
      @expected << [RDF::URI("http://example.com/doc"), RDF.type, RDF::URI("http://example.org/Agent")]
      @expected << [RDF::URI("http://example.com/doc"), RDF::URI("http://example.org/title"), "A particular agent"]
    end

    it "parses profile" do
      RDF::Reader.should_receive(:for).at_least(1).times.and_return(RDF::RDFa::Reader)
      g = RDF::Graph.load(@profile)
      g.count.should == 4
    end

    describe "new profile" do
      subject do
        # Clear vocabulary cache
        RDF::RDFa::Profile.cache.send(:initialize)
        RDF::Graph.new << @reader
      end
      
      it "matches expected" do
        subject.should be_equivalent_graph(@expected, :trace => @debug.join("\n"))
      end
    end
    
    describe "cached profile" do
      before(:each) do
        # Clear vocabulary cache
        RDF::RDFa::Profile.cache.send(:initialize)
        @reader.each {|s|}
      end
      
      it "should not re-parse profile" do
        RDF::RDFa::Profile.cache.send(:initialize)
        RDF::Reader.should_not_receive(:open).with("http://example.com/profile")
        RDF::RDFa::Reader.new(@doc, :profile_repository => @profile_repository).each {|p|}
      end
      
      it "should create vocab_cache" do
        RDF::RDFa::Profile.cache.should be_a(RDF::Util::Cache)
      end
    end
    
    describe "profile content" do
      before(:each) do
        bn_p = RDF::Node.new("prefix")
        bn_t = RDF::Node.new("term")
        ctx = RDF::URI("http://example.com/profile")
        @profile_repository << RDF::Statement.new(bn_p, RDF::RDFA.prefix, RDF::Literal.new("foo"), :context => ctx)
        @profile_repository << RDF::Statement.new(bn_p, RDF::RDFA.uri, RDF::Literal.new("http://example.org/"), :context => ctx)
        @profile_repository << RDF::Statement.new(bn_t, RDF::RDFA.term, RDF::Literal.new("title"), :context => ctx)
        @profile_repository << RDF::Statement.new(bn_t, RDF::RDFA.uri, RDF::Literal.new("http://example.org/title"), :context => ctx)

        # Clear vocabulary cache
        RDF::RDFa::Profile.cache.send(:initialize)
      end

      it "should not recieve RDF::Reader.open" do
        RDF::Reader.should_not_receive(:open).with("http://example.com/profile")
        @reader.each {|s|}
      end

      it "matches expected" do
        graph = RDF::Graph.new << @reader
        graph.should be_equivalent_graph(@expected, :trace => @debug.join("\n"))
      end
    end
  end
  
  # W3C Test suite from http://www.w3.org/2006/07/SWD/RDFa/testsuite/
  describe "w3c test cases" do
    require 'test_helper'
    
    Fixtures::TestCase::HOST_LANGUAGE_VERSION_SETS.each do |(host_language, version)|
      describe "for #{host_language} #{version}" do
        %w(required optional buggy).each do |classification|
          describe "that are #{classification}" do
            Fixtures::TestCase.for_specific(host_language, version, Fixtures::TestCase::Test.send(classification)) do |t|
              specify "test #{t.name}: #{t.title}#{",  (negative test)" if t.expectedResults.false?}" do
                begin
                  t.debug = []
                  reader = RDF::Reader.open(t.input(host_language, version),
                    :base_uri => t.input(host_language, version),
                    :debug => t.debug,
                    :format => :rdfa)
                  reader.should be_a RDF::Reader

                  # Make sure auto-detect works
                  unless host_language =~ /svg/
                    reader.host_language.should == host_language.to_sym
                    reader.version.should == version.to_sym
                  end

                  graph = RDF::Graph.new << reader
                  query = Kernel.open(t.results(host_language, version))
                  graph.should pass_query(query, t)
                rescue RSpec::Expectations::ExpectationNotMetError => e
                  if %w(0198).include?(t.name) || query =~ /XMLLiteral/m
                    pending("XMLLiteral canonicalization not implemented yet")
                  elsif %w(html4 html5).include?(host_language) && Kernel.open(t.input(host_language, version)) {|f| f.read =~ /xmlns/}
                    pending("HTML parsing does not use xmlns")
                  elsif classification != "required"
                    pending("#{classification} test") {  raise }
                  else
                    raise
                  end
                end
              end
            end
          end
        end
      end
    end
  end

  def parse(input, options)
    @debug = options[:debug] || []
    graph = RDF::Graph.new
    RDF::RDFa::Reader.new(input, options.merge(:debug => @debug)).each do |statement|
      graph << statement
    end
    graph
  end

end