# coding: utf-8
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" .',
}.each_pair do |contents, triple|
specify "test #{contents}" 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
lambda { parse(n3) }.should raise_error(RDF::ReaderError, "Long Unicode escapes no supported in Ruby 1.8")
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 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, :compare => :array)
end
end
{
%(<#Dürst> a "URI straight in UTF8".) => %( "URI straight in UTF8" .),
%(:a :related :\u3072\u3089\u304C\u306A.) => %( .),
}.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, :compare => :array)
rescue
if defined?(::Encoding)
raise
else
pending("Unicode URIs not supported in Ruby 1.8") { raise }
end
end
end
end
end
it "should create URIRefs" 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 illegal syntax" do
{
%(:y :p1 "xyz"^^xsd:integer .) => %r(Typed literal has an invalid lexical value: .* "xyz"),
%(:y :p1 "12xyz"^^xsd:integer .) => %r(Typed literal has an invalid lexical value: .* "12xyz"),
%(:y :p1 "xy.z"^^xsd:double .) => %r(Typed literal has an invalid lexical value: .* "xy\.z"),
%(:y :p1 "+1.0z"^^xsd:double .) => %r(Typed literal has an invalid lexical value: .* "\+1.0z"),
%(:a :b .) => %r(Illegal statment: ".*" missing object),
%(:a :b 'single quote' .) => RDF::ReaderError,
%(:a "literal value" :b .) => RDF::ReaderError,
%(@keywords prefix. :e prefix :f .) => %r(Keyword ".*" used as expression)
}.each_pair do |n3, error|
it "should raise error for '#{n3}'" do
lambda {
parse("@prefix xsd: . #{n3}", :base_uri => "http://a/b")
}.should raise_error(error)
end
end
end
describe "with n3 grammer" 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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
end
it "should do something for @forAll"
it "should do something for @forSome"
end
describe "namespaces" do
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
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, :compare => :array)
end
end
describe "keywords" do
[
%(prefix :<>.),
%(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(/unqualified keyword '\w+' used without @keyword directive/)
end
end
{
%(:a a :b) => %( .),
%(:a :b true) => %( .),
%(:a :b false) => %( .),
%(c :a :t) => %( .),
%(:c a :t) => %( .),
%(:c :a t) => %( .),
}.each_pair do |n3, nt|
it "should use default_ns for '#{n3}'" do
parse("@keywords . #{n3}", :base_uri => "http://a/b").should be_equivalent_graph(nt, :about => "http://a/b", :trace => @debug, :compare => :array)
end
end
{
%(@keywords true. :a :b true.) => %( "true"^^ .),
%(@keywords false. :a :b false.) => %( "false"^^ .),
%(@keywords a. :a a :b.) => %( .),
%(@keywords is. :a is :b @of :c.) => %(