rdf-ex/test/unit/turtle_decoder_test.exs
Marcel Otto 5819eec0cf Re-integrate XSD.ex
It turned out that the costs of separating the XSD datatypes are too high
and probably not worth the effort, since with its limited scope
probably nobody would want to use XSD.ex outside of the RDF.ex context
anyway.
2020-05-05 23:58:44 +02:00

404 lines
15 KiB
Elixir

defmodule RDF.Turtle.DecoderTest do
use ExUnit.Case, async: false
doctest RDF.Turtle.Decoder
import RDF.Sigils
alias RDF.{Turtle, Graph, NS, XSD}
use RDF.Vocabulary.Namespace
defvocab EX,
base_iri: "http://example.org/#",
terms: [], strict: false
defvocab P,
base_iri: "http://www.perceive.net/schemas/relationship/",
terms: [], strict: false
test "an empty string is deserialized to an empty graph" do
assert Turtle.Decoder.decode!("") == Graph.new
assert Turtle.Decoder.decode!(" \n\r\r\n ") == Graph.new
end
test "comments" do
assert Turtle.Decoder.decode!("# just a comment") == Graph.new
assert Turtle.Decoder.decode!("""
<http://example.org/#S> <http://example.org/#p> _:1 . # a comment
""") == Graph.new({EX.S, EX.p, RDF.bnode("1")})
assert Turtle.Decoder.decode!("""
# a comment
<http://example.org/#S> <http://example.org/#p> <http://example.org/#O> .
""") == Graph.new({EX.S, EX.p, EX.O})
assert Turtle.Decoder.decode!("""
<http://example.org/#S> <http://example.org/#p> <http://example.org/#O> .
# a comment
""") == Graph.new({EX.S, EX.p, EX.O})
assert Turtle.Decoder.decode!("""
# Header line 1
# Header line 2
<http://example.org/#S1> <http://example.org/#p1> <http://example.org/#O1> .
# 1st comment
<http://example.org/#S1> <http://example.org/#p2> <http://example.org/#O2> . # 2nd comment
# last comment
""") == Graph.new([
{EX.S1, EX.p1, EX.O1},
{EX.S1, EX.p2, EX.O2},
])
end
test "empty lines" do
assert Turtle.Decoder.decode!("""
<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green_goblin> .
""") == Graph.new({EX.spiderman, P.enemyOf, EX.green_goblin})
assert Turtle.Decoder.decode!("""
<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/enemyOf> <http://example.org/#green_goblin> .
""") == Graph.new({EX.spiderman, P.enemyOf, EX.green_goblin})
assert Turtle.Decoder.decode!("""
<http://example.org/#S1> <http://example.org/#p1> <http://example.org/#O1> .
<http://example.org/#S1> <http://example.org/#p2> <http://example.org/#O2> .
""") == Graph.new([
{EX.S1, EX.p1, EX.O1},
{EX.S1, EX.p2, EX.O2},
])
end
describe "statements" do
test "a N-Triple-style statement" do
assert Turtle.Decoder.decode!(
"<http://example.org/#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/#Person> ."
) == Graph.new({EX.Aaron, RDF.type, EX.Person})
end
test "a statement with the 'a' keyword" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Aaron> a <http://example.org/#Person> .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person})
end
test "multiple N-Triple-style statement" do
assert Turtle.Decoder.decode!("""
<http://example.org/#S1> <http://example.org/#p1> <http://example.org/#O1> .
<http://example.org/#S1> <http://example.org/#p2> <http://example.org/#O2> .
""") == Graph.new([
{EX.S1, EX.p1, EX.O1},
{EX.S1, EX.p2, EX.O2},
])
assert Turtle.Decoder.decode!("""
<http://example.org/#S1> <http://example.org/#p1> <http://example.org/#O1> .
<http://example.org/#S1> <http://example.org/#p2> <http://example.org/#O2> .
<http://example.org/#S2> <http://example.org/#p3> <http://example.org/#O3> .
""") == Graph.new([
{EX.S1, EX.p1, EX.O1},
{EX.S1, EX.p2, EX.O2},
{EX.S2, EX.p3, EX.O3}
])
end
test "statement with multiple objects" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> "baz", 1, true .
""") == Graph.new([
{EX.Foo, EX.bar, "baz"},
{EX.Foo, EX.bar, 1},
{EX.Foo, EX.bar, true},
])
end
test "statement with multiple predications" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> "baz";
<http://example.org/#baz> 42 .
""") == Graph.new([
{EX.Foo, EX.bar, "baz"},
{EX.Foo, EX.baz, 42},
])
end
end
describe "blank node property lists" do
test "blank node property list on object position" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> [ <http://example.org/#baz> 42 ] .
""") == Graph.new([
{EX.Foo, EX.bar, RDF.bnode("b0")},
{RDF.bnode("b0"), EX.baz, 42},
])
end
test "blank node property list on subject position" do
assert Turtle.Decoder.decode!("""
[ <http://example.org/#baz> 42 ] <http://example.org/#bar> false .
""") == Graph.new([
{RDF.bnode("b0"), EX.baz, 42},
{RDF.bnode("b0"), EX.bar, false},
])
end
test "a single blank node property list" do
assert Turtle.Decoder.decode!("[ <http://example.org/#foo> 42 ] .") ==
Graph.new([{RDF.bnode("b0"), EX.foo, 42}])
end
test "nested blank node property list" do
assert Turtle.Decoder.decode!("""
[ <http://example.org/#p1> [ <http://example.org/#p2> <http://example.org/#o2> ] ; <http://example.org/#p> <http://example.org/#o> ].
""") == Graph.new([
{RDF.bnode("b0"), EX.p1, RDF.bnode("b1")},
{RDF.bnode("b1"), EX.p2, EX.o2},
{RDF.bnode("b0"), EX.p, EX.o},
])
end
test "blank node via []" do
assert Turtle.Decoder.decode!("""
[] <http://xmlns.com/foaf/0.1/name> "Aaron Swartz" .
""") == Graph.new({RDF.bnode("b0"), ~I<http://xmlns.com/foaf/0.1/name>, "Aaron Swartz"})
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> [] .
""") == Graph.new({EX.Foo, EX.bar, RDF.bnode("b0")})
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> [ ] .
""") == Graph.new({EX.Foo, EX.bar, RDF.bnode("b0")})
end
end
test "blank node" do
assert Turtle.Decoder.decode!("""
_:foo <http://example.org/#p> <http://example.org/#O> .
""") == Graph.new({RDF.bnode("foo"), EX.p, EX.O})
assert Turtle.Decoder.decode!("""
<http://example.org/#S> <http://example.org/#p> _:1 .
""") == Graph.new({EX.S, EX.p, RDF.bnode("1")})
assert Turtle.Decoder.decode!("""
_:foo <http://example.org/#p> _:bar .
""") == Graph.new({RDF.bnode("foo"), EX.p, RDF.bnode("bar")})
end
describe "quoted literals" do
test "an untyped string literal" do
assert Turtle.Decoder.decode!("""
<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/realname> "Peter Parker" .
""") == Graph.new({EX.spiderman, P.realname, RDF.literal("Peter Parker")})
end
test "an untyped long quoted string literal" do
assert Turtle.Decoder.decode!("""
<http://example.org/#spiderman> <http://www.perceive.net/schemas/relationship/realname> '''Peter Parker''' .
""") == Graph.new({EX.spiderman, P.realname, RDF.literal("Peter Parker")})
end
test "a typed literal" do
assert Turtle.Decoder.decode!("""
<http://example.org/#spiderman> <http://example.org/#p> "42"^^<http://www.w3.org/2001/XMLSchema#integer> .
""") == Graph.new({EX.spiderman, EX.p, RDF.literal(42)})
end
test "a typed literal with type as a prefixed name" do
assert Turtle.Decoder.decode!("""
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
<http://example.org/#spiderman> <http://example.org/#p> "42"^^xsd:integer .
""") == Graph.new({EX.spiderman, EX.p, RDF.literal(42)}, prefixes: %{xsd: NS.XSD})
end
test "a language tagged literal" do
assert Turtle.Decoder.decode!("""
<http://example.org/#S> <http://example.org/#p> "foo"@en .
""") == Graph.new({EX.S, EX.p, RDF.literal("foo", language: "en")})
end
test "a '@prefix' or '@base' language tagged literal" do
assert Turtle.Decoder.decode!("""
<http://example.org/#S> <http://example.org/#p> "foo"@prefix .
""") == Graph.new({EX.S, EX.p, RDF.literal("foo", language: "prefix")})
assert Turtle.Decoder.decode!("""
<http://example.org/#S> <http://example.org/#p> "foo"@base .
""") == Graph.new({EX.S, EX.p, RDF.literal("foo", language: "base")})
end
end
describe "shorthand literals" do
test "boolean" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> true .
""") == Graph.new({EX.Foo, EX.bar, XSD.true})
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> false .
""") == Graph.new({EX.Foo, EX.bar, XSD.false})
end
test "integer" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> 42 .
""") == Graph.new({EX.Foo, EX.bar, XSD.integer(42)})
end
test "decimal" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> 3.14 .
""") == Graph.new({EX.Foo, EX.bar, XSD.decimal("3.14")})
end
test "double" do
assert Turtle.Decoder.decode!("""
<http://example.org/#Foo> <http://example.org/#bar> 1.2e3 .
""") == Graph.new({EX.Foo, EX.bar, XSD.double("1.2e3")})
end
end
describe "prefixed names" do
test "non-empty prefixed names" do
prefixes = RDF.PrefixMap.new(ex: ~I<http://example.org/#>)
assert Turtle.Decoder.decode!("""
@prefix ex: <http://example.org/#> .
ex:Aaron <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ex:Person .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes)
assert Turtle.Decoder.decode!("""
@prefix ex: <http://example.org/#> .
ex:Aaron <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ex:Person .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes)
assert Turtle.Decoder.decode!("""
PREFIX ex: <http://example.org/#>
ex:Aaron <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ex:Person .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes)
assert Turtle.Decoder.decode!("""
prefix ex: <http://example.org/#>
ex:Aaron <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ex:Person .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes)
end
test "empty prefixed name" do
prefixes = RDF.PrefixMap.new("": ~I<http://example.org/#>)
assert Turtle.Decoder.decode!("""
@prefix : <http://example.org/#> .
:Aaron <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> :Person .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes)
assert Turtle.Decoder.decode!("""
PREFIX : <http://example.org/#>
:Aaron <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> :Person .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes)
end
end
describe "collections" do
test "non-empty collection" do
assert Turtle.Decoder.decode!("""
@prefix : <http://example.org/#> .
:subject :predicate ( :a :b :c ) .
""") == Graph.new([
{EX.subject, EX.predicate, RDF.bnode("b0")},
{RDF.bnode("b0"), RDF.first, EX.a},
{RDF.bnode("b0"), RDF.rest, RDF.bnode("b1")},
{RDF.bnode("b1"), RDF.first, EX.b},
{RDF.bnode("b1"), RDF.rest, RDF.bnode("b2")},
{RDF.bnode("b2"), RDF.first, EX.c},
{RDF.bnode("b2"), RDF.rest, RDF.nil},
], prefixes: %{"": ~I<http://example.org/#>})
end
test "empty collection" do
assert Turtle.Decoder.decode!("""
@prefix : <http://example.org/#> .
:subject :predicate () .
""") == Graph.new({EX.subject, EX.predicate, RDF.nil}, prefixes: %{"": ~I<http://example.org/#>})
end
test "nested collection" do
assert Turtle.Decoder.decode!("""
@prefix : <http://example.org/#> .
:subject :predicate ( :a (:b :c) ) .
""") == Graph.new([
{EX.subject, EX.predicate, RDF.bnode("b0")},
{RDF.bnode("b0"), RDF.first, EX.a},
{RDF.bnode("b0"), RDF.rest, RDF.bnode("b3")},
{RDF.bnode("b3"), RDF.first, RDF.bnode("b1")},
{RDF.bnode("b3"), RDF.rest, RDF.nil},
{RDF.bnode("b1"), RDF.first, EX.b},
{RDF.bnode("b1"), RDF.rest, RDF.bnode("b2")},
{RDF.bnode("b2"), RDF.first, EX.c},
{RDF.bnode("b2"), RDF.rest, RDF.nil},
], prefixes: %{"": ~I<http://example.org/#>})
end
end
describe "relative IRIs" do
test "without explicit in-doc base and no document_base option option given" do
assert_raise RuntimeError, fn ->
Turtle.Decoder.decode!(
"<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .")
end
end
test "without explicit in-doc base, but document_base option given" do
assert Turtle.Decoder.decode!("""
<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .
""", base: "http://example.org/") ==
Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I<http://example.org/>)
end
test "with @base given" do
assert Turtle.Decoder.decode!("""
@base <http://example.org/> .
<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I<http://example.org/>)
assert Turtle.Decoder.decode!("""
@base <http://example.org/#> .
<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I<http://example.org/#>)
end
test "with BASE given" do
assert Turtle.Decoder.decode!("""
BASE <http://example.org/>
<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I<http://example.org/>)
assert Turtle.Decoder.decode!("""
base <http://example.org/#>
<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .
""") == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I<http://example.org/#>)
end
test "when a given base is itself relative" do
assert_raise RuntimeError, fn ->
Turtle.Decoder.decode!("""
@base <foo> .
<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .
""")
end
assert_raise RuntimeError, fn ->
Turtle.Decoder.decode!(
"<#Aaron> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <#Person> .",
base: "foo")
end
end
end
end