defmodule RDF.Turtle.DecoderTest do use ExUnit.Case, async: false doctest RDF.Turtle.Decoder import RDF.Sigils alias RDF.{Turtle, Graph} alias RDF.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!(""" _:1 . # a comment """) == Graph.new({EX.S, EX.p, RDF.bnode("1")}) assert Turtle.Decoder.decode!(""" # a comment . """) == Graph.new({EX.S, EX.p, EX.O}) assert Turtle.Decoder.decode!(""" . # a comment """) == Graph.new({EX.S, EX.p, EX.O}) assert Turtle.Decoder.decode!(""" # Header line 1 # Header line 2 . # 1st comment . # 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!(""" . """) == Graph.new({EX.spiderman, P.enemyOf, EX.green_goblin}) assert Turtle.Decoder.decode!(""" . """) == Graph.new({EX.spiderman, P.enemyOf, EX.green_goblin}) assert Turtle.Decoder.decode!(""" . . """) == 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!( " ." ) == Graph.new({EX.Aaron, RDF.type, EX.Person}) end test "a statement with the 'a' keyword" do assert Turtle.Decoder.decode!(""" a . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}) end test "multiple N-Triple-style statement" do assert Turtle.Decoder.decode!(""" . . """) == Graph.new([ {EX.S1, EX.p1, EX.O1}, {EX.S1, EX.p2, EX.O2}, ]) assert Turtle.Decoder.decode!(""" . . . """) == 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!(""" "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!(""" "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!(""" [ 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!(""" [ 42 ] 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!("[ 42 ] .") == Graph.new([{RDF.bnode("b0"), EX.foo, 42}]) end test "nested blank node property list" do assert Turtle.Decoder.decode!(""" [ [ ] ; ]. """) == 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!(""" [] "Aaron Swartz" . """) == Graph.new({RDF.bnode("b0"), ~I, "Aaron Swartz"}) assert Turtle.Decoder.decode!(""" [] . """) == Graph.new({EX.Foo, EX.bar, RDF.bnode("b0")}) assert Turtle.Decoder.decode!(""" [ ] . """) == Graph.new({EX.Foo, EX.bar, RDF.bnode("b0")}) end end test "blank node" do assert Turtle.Decoder.decode!(""" _:foo . """) == Graph.new({RDF.bnode("foo"), EX.p, EX.O}) assert Turtle.Decoder.decode!(""" _:1 . """) == Graph.new({EX.S, EX.p, RDF.bnode("1")}) assert Turtle.Decoder.decode!(""" _:foo _: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!(""" "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!(""" '''Peter Parker''' . """) == Graph.new({EX.spiderman, P.realname, RDF.literal("Peter Parker")}) end test "a typed literal" do assert Turtle.Decoder.decode!(""" "42"^^ . """) == 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: "42"^^xsd:integer . """) == Graph.new({EX.spiderman, EX.p, RDF.literal(42)}, prefixes: %{xsd: XSD}) end test "a language tagged literal" do assert Turtle.Decoder.decode!(""" "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!(""" "foo"@prefix . """) == Graph.new({EX.S, EX.p, RDF.literal("foo", language: "prefix")}) assert Turtle.Decoder.decode!(""" "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!(""" true . """) == Graph.new({EX.Foo, EX.bar, RDF.Boolean.new(true)}) assert Turtle.Decoder.decode!(""" false . """) == Graph.new({EX.Foo, EX.bar, RDF.Boolean.new(false)}) end test "integer" do assert Turtle.Decoder.decode!(""" 42 . """) == Graph.new({EX.Foo, EX.bar, RDF.Integer.new(42)}) end test "decimal" do assert Turtle.Decoder.decode!(""" 3.14 . """) == Graph.new({EX.Foo, EX.bar, RDF.Literal.new("3.14", datatype: XSD.decimal)}) end test "double" do assert Turtle.Decoder.decode!(""" 1.2e3 . """) == Graph.new({EX.Foo, EX.bar, RDF.Double.new("1.2e3")}) end end describe "prefixed names" do test "non-empty prefixed names" do prefixes = RDF.PrefixMap.new(ex: ~I) assert Turtle.Decoder.decode!(""" @prefix ex: . ex:Aaron ex:Person . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes) assert Turtle.Decoder.decode!(""" @prefix ex: . ex:Aaron ex:Person . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes) assert Turtle.Decoder.decode!(""" PREFIX ex: ex:Aaron ex:Person . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes) assert Turtle.Decoder.decode!(""" prefix ex: ex:Aaron ex:Person . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes) end test "empty prefixed name" do prefixes = RDF.PrefixMap.new("": ~I) assert Turtle.Decoder.decode!(""" @prefix : . :Aaron :Person . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, prefixes: prefixes) assert Turtle.Decoder.decode!(""" PREFIX : :Aaron :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 : . :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}) end test "empty collection" do assert Turtle.Decoder.decode!(""" @prefix : . :subject :predicate () . """) == Graph.new({EX.subject, EX.predicate, RDF.nil}, prefixes: %{"": ~I}) end test "nested collection" do assert Turtle.Decoder.decode!(""" @prefix : . :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}) 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> <#Person> .") end end test "without explicit in-doc base, but document_base option given" do assert Turtle.Decoder.decode!(""" <#Aaron> <#Person> . """, base: "http://example.org/") == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I) end test "with @base given" do assert Turtle.Decoder.decode!(""" @base . <#Aaron> <#Person> . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I) assert Turtle.Decoder.decode!(""" @base . <#Aaron> <#Person> . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I) end test "with BASE given" do assert Turtle.Decoder.decode!(""" BASE <#Aaron> <#Person> . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I) assert Turtle.Decoder.decode!(""" base <#Aaron> <#Person> . """) == Graph.new({EX.Aaron, RDF.type, EX.Person}, base_iri: ~I) end test "when a given base is itself relative" do assert_raise RuntimeError, fn -> Turtle.Decoder.decode!(""" @base . <#Aaron> <#Person> . """) end assert_raise RuntimeError, fn -> Turtle.Decoder.decode!( "<#Aaron> <#Person> .", base: "foo") end end end end