# coding: utf-8
$:.unshift "."
require File.join(File.dirname(__FILE__), 'spec_helper')
describe "RDF::N3::Reader" do
context "discovery" do
{
"n3" => RDF::Reader.for(:n3),
"etc/foaf.n3" => RDF::Reader.for("etc/foaf.n3"),
"etc/foaf.ttl" => RDF::Reader.for("etc/foaf.ttl"),
"foaf.n3" => RDF::Reader.for(:file_name => "foaf.n3"),
"foaf.ttl" => RDF::Reader.for(:file_name => "foaf.ttl"),
".n3" => RDF::Reader.for(:file_extension => "n3"),
".ttl" => RDF::Reader.for(:file_extension => "ttl"),
"text/n3" => RDF::Reader.for(:content_type => "text/n3"),
"text/turtle" => RDF::Reader.for(:content_type => "text/turtle"),
}.each_pair do |label, format|
it "should discover '#{label}'" do
format.should == RDF::N3::Reader
end
end
end
context :interface do
before(:each) do
@sampledoc = <<-EOF;
@prefix dc: .
@prefix po: .
@prefix rdf: .
_:broadcast
a po:Broadcast;
po:schedule_date """2008-06-24T12:00:00Z""";
po:broadcast_of _:version;
po:broadcast_on ;
.
_:version
a po:Version;
.
dc:title """Nemone""";
a po:Brand;
.
a po:Episode;
po:episode ;
po:version _:version;
po:long_synopsis """Actor and comedian Rhys Darby chats to Nemone.""";
dc:title """Nemone""";
po:synopsis """Actor and comedian Rhys Darby chats to Nemone.""";
.
a po:Service;
dc:title """BBC 6 Music""";
.
#_:abcd a po:Episode.
EOF
end
it "should yield reader" do
inner = mock("inner")
inner.should_receive(:called).with(RDF::N3::Reader)
RDF::N3::Reader.new(@sampledoc) do |reader|
inner.called(reader.class)
end
end
it "should return reader" do
RDF::N3::Reader.new(@sampledoc).should be_a(RDF::N3::Reader)
end
it "should yield statements" do
inner = mock("inner")
inner.should_receive(:called).with(RDF::Statement).exactly(15)
RDF::N3::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).exactly(15)
RDF::N3::Reader.new(@sampledoc).each_triple do |subject, predicate, object|
inner.called(subject.class, predicate.class, object.class)
end
end
end
describe "with simple ntriples" do
context "simple triple" do
before(:each) do
n3_string = %( "Gregg Kellogg" .)
@graph = parse(n3_string)
@statement = @graph.statements.first
end
it "should have a single triple" do
@graph.size.should == 1
end
it "should have subject" do
@statement.subject.to_s.should == "http://example.org/"
end
it "should have predicate" do
@statement.predicate.to_s.should == "http://xmlns.com/foaf/0.1/name"
end
it "should have object" do
@statement.object.to_s.should == "Gregg Kellogg"
end
end
# NTriple tests from http://www.w3.org/2000/10/rdf-tests/rdfcore/ntriples/test.nt
describe "with blank lines" do
{
"comment" => "# comment lines",
"comment after whitespace" => " # comment after whitespace",
"empty line" => "",
"line with spaces" => " "
}.each_pair do |name, statement|
specify "test #{name}" do
parse(statement).size.should == 0
end
end
end
describe "with literal encodings" do
{
'Dürst' => ':a :b "D\u00FCrst" .',
'simple literal' => ':a :b "simple literal" .',
'backslash:\\' => ':a :b "backslash:\\\\" .',
'dquote:"' => ':a :b "dquote:\"" .',
"newline:\n" => ':a :b "newline:\n" .',
"return\r" => ':a :b "return\r" .',
"tab:\t" => ':a :b "tab:\t" .',
"é" => ':a :b "\u00E9" .',
"€" => ':a :b "\u20AC" .',
"resumé" => ':a :resume "resum\u00E9" .',
}.each_pair do |contents, triple|
specify "test #{triple}" do
graph = parse(triple, :base_uri => "http://a/b")
statement = graph.statements.first
graph.size.should == 1
statement.object.value.should == contents
end
end
{
'Dürst' => ':a :b "Dürst" .',
"é" => ':a :b "é" .',
"€" => ':a :b "€" .',
"resumé" => ':a :resume "resumé" .',
}.each_pair do |contents, triple|
specify "test #{triple}" do
graph = parse(triple, :base_uri => "http://a/b")
statement = graph.statements.first
graph.size.should == 1
statement.object.value.should == contents
end
end
it "should parse long literal with escape" do
n3 = %(@prefix : . :a :b "\\U00015678another" .)
if defined?(::Encoding)
statement = parse(n3).statements.first
statement.object.value.should == "\u{15678}another"
else
pending("Not supported in Ruby 1.8")
end
end
it "should parse multi-line literal" do
graph = parse(%(
"""
Foo
barhere
baz
more
""" .
))
graph.size.should == 1
graph.statements.first.object.value.should == %(
Foo
barhere
baz
more
)
end
it "should parse long literal ending in double quote" do
graph = parse(%(:a :b """ \\"""" .), :base_uri => "http://a/b")
graph.size.should == 1
graph.statements.first.object.value.should == ' "'
end
end
it "should create named subject bnode" do
graph = parse("_:anon .")
graph.size.should == 1
statement = graph.statements.first
statement.subject.should be_a(RDF::Node)
statement.subject.id.should =~ /anon/
statement.predicate.to_s.should == "http://example.org/property"
statement.object.to_s.should == "http://example.org/resource2"
end
it "should create named predicate bnode" do
graph = parse(" _:anon .")
graph.size.should == 1
statement = graph.statements.first
statement.subject.to_s.should == "http://example.org/resource2"
statement.predicate.should be_a(RDF::Node)
statement.predicate.id.should =~ /anon/
statement.object.to_s.should == "http://example.org/object"
end
it "should create named object bnode" do
graph = parse(" _:anon .")
graph.size.should == 1
statement = graph.statements.first
statement.subject.to_s.should == "http://example.org/resource2"
statement.predicate.to_s.should == "http://example.org/property"
statement.object.should be_a(RDF::Node)
statement.object.id.should =~ /anon/
end
{
"three uris" => " .",
"spaces and tabs throughout" => " . ",
"line ending with CR NL" => " .\r\n",
"literal escapes (1)" => ' "simple literal" .',
"literal escapes (2)" => ' "backslash:\\\\" .',
"literal escapes (3)" => ' "dquote:\"" .',
"literal escapes (4)" => ' "newline:\n" .',
"literal escapes (5)" => ' "return:\r" .',
"literal escapes (6)" => ' "tab:\t" .',
"Space is optional before final . (1)" => [' .', ' .'],
"Space is optional before final . (2)" => [' "x".', ' "x" .'],
"XML Literals as Datatyped Literals (1)" => ' ""^^ .',
"XML Literals as Datatyped Literals (2)" => ' " "^^ .',
"XML Literals as Datatyped Literals (3)" => ' "x"^^ .',
"XML Literals as Datatyped Literals (4)" => ' "\""^^ .',
"XML Literals as Datatyped Literals (5)" => ' ""^^ .',
"XML Literals as Datatyped Literals (6)" => ' "a "^^ .',
"XML Literals as Datatyped Literals (7)" => ' "a c"^^ .',
"XML Literals as Datatyped Literals (8)" => ' "a\n\nc"^^ .',
"XML Literals as Datatyped Literals (9)" => ' "chat"^^ .',
"Plain literals with languages (1)" => ' "chat"@fr .',
"Plain literals with languages (2)" => ' "chat"@en .',
"Typed Literals" => ' "abc"^^ .',
}.each_pair do |name, statement|
specify "test #{name}" do
graph = parse([statement].flatten.first)
graph.size.should == 1
graph.to_ntriples.chomp.should == [statement].flatten.last.gsub(/\s+/, " ").strip
end
end
it "should allow mixed-case language" do
n3doc = %(:x2 :p "xyz"@EN .)
statement = parse(n3doc).statements.first
statement.object.to_ntriples.should == %("xyz"@EN)
end
it "should create typed literals" do
n3doc = " \"Joe\"^^ ."
statement = parse(n3doc).statements.first
statement.object.class.should == RDF::Literal
end
it "should create BNodes" do
n3doc = "_:a a _:c ."
statement = parse(n3doc).statements.first
statement.subject.class.should == RDF::Node
statement.object.class.should == RDF::Node
end
describe "should create URIs" do
{
%( .) => %( .),
%( .) => %( .),
%(:joe :knows :jane .) => %( .),
%(<#D%C3%BCrst> a "URI percent ^encoded as C3, BC".) => %( "URI percent ^encoded as C3, BC" .),
}.each_pair do |n3, nt|
it "for '#{n3}'" do
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
end
{
%(<#Dürst> :knows :jane.) => ' .',
%(:Dürst :knows :jane.) => ' .',
%(:bob :resumé "Bob's non-normalized resumé".) => ' "Bob\'s non-normalized resumé" .',
%(:alice :resumé "Alice's normalized resumé".) => ' "Alice\'s normalized resumé" .',
}.each_pair do |n3, nt|
it "for '#{n3}'" do
begin
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
rescue
if defined?(::Encoding)
raise
else
pending("Unicode URIs not supported in Ruby 1.8") { raise }
end
end
end
end
{
%(<#Dürst> a "URI straight in UTF8".) => %( "URI straight in UTF8" .),
#%(:a :related :ひらがな .) => %( .),
}.each_pair do |n3, nt|
it "for '#{n3}'" do
begin
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
rescue
if defined?(::Encoding)
raise
else
pending("Unicode URIs not supported in Ruby 1.8") { raise }
end
end
end
end
end
it "should create URIs" do
n3doc = " ."
statement = parse(n3doc).statements.first
statement.subject.class.should == RDF::URI
statement.object.class.should == RDF::URI
end
it "should create literals" do
n3doc = " \"Joe\"."
statement = parse(n3doc).statements.first
statement.object.class.should == RDF::Literal
end
end
describe "with n3 grammar" do
describe "syntactic expressions" do
it "should create typed literals with qname" do
n3doc = %(
@prefix rdf: .
@prefix foaf: .
@prefix xsd: .
foaf:name \"Joe\"^^xsd:string .
)
statement = parse(n3doc).statements.first
statement.object.class.should == RDF::Literal
end
it "should use <> as a prefix and as a triple node" do
n3 = %(@prefix : <> . <> a :a.)
nt = %(
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should use <#> as a prefix and as a triple node" do
n3 = %(@prefix : <#> . <#> a :a.)
nt = %(
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should generate rdf:type for 'a'" do
n3 = %(@prefix a: . a:b a .)
nt = %( .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should generate rdf:type for '@a'" do
n3 = %(@prefix a: . a:b @a .)
nt = %( .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should generate inverse predicate for 'is xxx of'" do
n3 = %("value" is :prop of :b . :b :prop "value" .)
nt = %( "value" .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should generate inverse predicate for '@is xxx @of'" do
n3 = %("value" @is :prop @of :b . :b :prop "value" .)
nt = %( "value" .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should generate inverse predicate for 'is xxx of' with object list" do
n3 = %("value" is :prop of :b, :c . )
nt = %(
"value" .
"value" .
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should generate predicate for 'has xxx'" do
n3 = %(@prefix a: . a:b has :pred a:c .)
nt = %( .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should generate predicate for '@has xxx'" do
n3 = %(@prefix a: . a:b @has :pred a:c .)
nt = %( .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should create log:implies predicate for '=>'" do
n3 = %(@prefix a: . _:a => a:something .)
nt = %(_:a .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should create log:implies inverse predicate for '<='" do
n3 = %(@prefix a: . _:a <= a:something .)
nt = %( _:a .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should create owl:sameAs predicate for '='" do
n3 = %(@prefix a: . _:a = a:something .)
nt = %(_:a .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
{
%(:a :b @true) => %( "true"^^ .),
%(:a :b @false) => %( "false"^^ .),
%(:a :b 1) => %( "1"^^ .),
%(:a :b -1) => %( "-1"^^ .),
%(:a :b +1) => %( "+1"^^ .),
%(:a :b 1.0) => %( "1.0"^^ .),
%(:a :b 1.0e1) => %( "1.0e1"^^ .),
%(:a :b 1.0e-1) => %( "1.0e-1"^^ .),
%(:a :b 1.0e+1) => %( "1.0e+1"^^ .),
}.each_pair do |n3, nt|
it "should create typed literal for '#{n3}'" do
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
end
it "should accept empty localname" do
n3 = %(: : : .)
nt = %( .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should accept prefix with empty local name" do
n3 = %(@prefix foo: . foo: foo: foo: .)
nt = %( .)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "substitutes variable for URI with @forAll" do
n3 = %(@forAll :x . :x :y :z .)
g = parse(n3, :base_uri => "http://a/b")
statement = g.statements.first
statement.subject.should be_a(RDF::Query::Variable)
statement.predicate.to_s.should == "http://a/b#y"
statement.object.to_s.should == "http://a/b#z"
end
it "substitutes variable for URIs with @forAll" do
n3 = %(@forAll :x, :y, :z . :x :y :z .)
g = parse(n3, :base_uri => "http://a/b")
statement = g.statements.first
statement.subject.should be_a(RDF::Query::Variable)
statement.predicate.should be_a(RDF::Query::Variable)
statement.object.should be_a(RDF::Query::Variable)
statement.subject.should_not == statement.predicate
statement.object.should_not == statement.predicate
statement.predicate.should_not == statement.object
end
it "substitutes node for URI with @forEach" do
n3 = %(@forSome :x . :x :y :z .)
g = parse(n3, :base_uri => "http://a/b")
statement = g.statements.first
statement.subject.should be_a(RDF::Node)
statement.predicate.to_s.should == "http://a/b#y"
statement.object.to_s.should == "http://a/b#z"
end
it "substitutes node for URIs with @forEach" do
n3 = %(@forSome :x, :y, :z . :x :y :z .)
g = parse(n3, :base_uri => "http://a/b")
statement = g.statements.first
statement.subject.should be_a(RDF::Node)
statement.predicate.should be_a(RDF::Node)
statement.object.should be_a(RDF::Node)
statement.subject.should_not == statement.predicate
statement.object.should_not == statement.predicate
statement.predicate.should_not == statement.object
end
end
describe "prefixes" do
it "should not append # for http://foo/bar" do
n3 = %(@prefix : . :a : :b .)
nt = %(
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should not append # for http://foo/bar/" do
n3 = %(@prefix : . :a : :b .)
nt = %(
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should not append # for http://foo/bar#" do
n3 = %(@prefix : . :a : :b .)
nt = %(
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should set absolute base" do
n3 = %(@base . <> :a . <#c> :d .)
nt = %(
.
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should set absolute base (trailing /)" do
n3 = %(@base . <> :a . <#c> :d .)
nt = %(
.
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should set absolute base (trailing #)" do
n3 = %(@base . <> :a . <#c> :d .)
nt = %(
.
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "should set relative base" do
n3 = %(
@base .
<> :a , <#c>.
@base .
<> :a , <#c>.
@base <../>.
<> :a , <#e>.
)
nt = %(
.
.
.
.
.
.
)
parse(n3, :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug)
end
it "returns defined prefixes" do
n3 = %(
@prefix rdf: .
@prefix rdfs: .
@prefix : .
:foo a rdfs:Class.
:bar :d :c.
:a :d :c.
)
reader = RDF::N3::Reader.new(n3)
reader.each {|statement|}
reader.prefixes.should == {
:rdf => "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
:rdfs => "http://www.w3.org/2000/01/rdf-schema#",
nil => "http://test/"}
end
end
describe "keywords" do
[
%(base <>.),
%(keywords a.),
%(:a is :b of :c.),
%(:a @is :b of :c.),
%(:a is :b @of :c.),
%(:a has :b :c.),
].each do |n3|
it "should require @ if keywords set to empty for '#{n3}'" do
lambda do
parse("@keywords . #{n3}", :base_uri => "http://a/b")
end.should raise_error(RDF::ReaderError)
end
end
[
%(prefix :<>.),
].each do |n3|
it "parses as local name if keywords set to empty for '#{n3}'" do
lambda do
parse("@keywords . #{n3}", :base_uri => "http://a/b")
end.should_not raise_error(RDF::ReaderError)
end
end
{
%(:a a :b) => %( .),
%(:a :b true) => %( .),
%(:a :b false) => %(