From d72791bcd59893035cab37d3686eb5e0f1a0da8a Mon Sep 17 00:00:00 2001 From: Marcel Otto Date: Fri, 14 Apr 2017 13:38:38 +0200 Subject: [PATCH] json_ld: JSON-LD toRdf test suite and various fixes --- lib/json/ld/decoder.ex | 71 +++++++++++++++++++++++++++++--------- test/suite/to_rdf_test.exs | 63 +++++++++++++++++++++++++++++++++ test/unit/decoder_test.exs | 14 ++++---- 3 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 test/suite/to_rdf_test.exs diff --git a/lib/json/ld/decoder.ex b/lib/json/ld/decoder.ex index d68d54c..bc39ec6 100644 --- a/lib/json/ld/decoder.ex +++ b/lib/json/ld/decoder.ex @@ -70,8 +70,12 @@ defmodule JSON.LD.Decoder do rdf_graph end end) - Dataset.add(dataset, rdf_graph, + if Enum.empty?(rdf_graph) do + dataset + else + Dataset.add(dataset, rdf_graph, if(graph_name == "@default", do: nil, else: graph_name)) + end else dataset end @@ -134,21 +138,56 @@ defmodule JSON.LD.Decoder do end defp list_to_rdf(list, node_id_map) do - list - |> Enum.reverse - |> Enum.reduce({[], RDF.NS.RDF.nil}, fn (item, {list_triples, last}) -> - case object_to_rdf(item) do - nil -> {list_triples, last} - object -> - with bnode = node_to_rdf(generate_blank_node_id(node_id_map)) do - { - [{bnode, RDF.NS.RDF.first, object}, - {bnode, RDF.NS.RDF.rest, last } | list_triples], - bnode - } - end - end - end) + {list_triples, first, last} = + list + |> Enum.reduce({[], nil, nil}, fn (item, {list_triples, first, last}) -> + case object_to_rdf(item) do + nil -> {list_triples, first, last} + object -> + with bnode = node_to_rdf(generate_blank_node_id(node_id_map)) do + if last do + { + list_triples ++ + [{last, RDF.NS.RDF.rest, bnode}, + {bnode, RDF.NS.RDF.first, object}], + first, + bnode + } + else + { + list_triples ++ [{bnode, RDF.NS.RDF.first, object}], + bnode, + bnode + } + end + end + end + end) + if last do + {list_triples ++ [{last, RDF.NS.RDF.rest, RDF.NS.RDF.nil}], first} + else + {[], RDF.NS.RDF.nil} + end end +# This is a much nicer and faster version, but the blank node numbering is reversed. +# Although this isn't relevant, I prefer to be more spec conform (for now). +# defp list_to_rdf(list, node_id_map) do +# list +# |> Enum.reverse +# |> Enum.reduce({[], RDF.NS.RDF.nil}, fn (item, {list_triples, last}) -> +# case object_to_rdf(item) do +# nil -> {list_triples, last} +# object -> +# with bnode = node_to_rdf(generate_blank_node_id(node_id_map)) do +# { +# [{bnode, RDF.NS.RDF.first, object}, +# {bnode, RDF.NS.RDF.rest, last } | list_triples], +# bnode +# } +# end +# end +# end) +# end + end diff --git a/test/suite/to_rdf_test.exs b/test/suite/to_rdf_test.exs new file mode 100644 index 0000000..926e788 --- /dev/null +++ b/test/suite/to_rdf_test.exs @@ -0,0 +1,63 @@ +defmodule JSON.LD.TestSuite.ToRdfTest do + use ExUnit.Case, async: false + + import JSON.LD.TestSuite + import RDF.Sigils + + setup_all do + [base_iri: manifest("toRdf")["baseIri"]] + end + + test_cases("toRdf") +# TODO: finish literal implementation +# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0022] end) +# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0035] end) +# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0071] end) +# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0101] end) +# TODO: Ordering problems +# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0118] end) +# TODO: Fixed in Elixir 1.5 +# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0069] end) +# |> Enum.filter(fn %{"@id" => id} -> id in ~w[#t0102] end) + |> Enum.each(fn %{"name" => name, "input" => input} = test_case -> + if input in ~w[toRdf-0022-in.jsonld toRdf-0035-in.jsonld toRdf-0071-in.jsonld toRdf-0101-in.jsonld] do + @tag skip: "finish literal implementation" + end + if input in ~w[toRdf-0069-in.jsonld toRdf-0102-in.jsonld] do + @tag skip: """ + probably caused by a bug in Elixirs URI.merge which should be fixed with Elixir 1.5 + https://github.com/elixir-lang/elixir/pull/5780 + """ + end + if input in ~w[toRdf-0118-in.jsonld] do + @tag skip: """ + Actually an isomorphic graph is generated, but due to different ordering + during expansion the generated blank nodes are named different. + """ + end + @tag :test_suite + @tag :to_rdf_test_suite + @tag data: test_case + test "#{input}: #{name}", + %{data: %{"input" => input, "expect" => output} = test_case, base_iri: base_iri} do + # This requires a special handling, since the N-Quad ouput file is not valid, by using blank nodes as predicates + if input == "toRdf-0118-in.jsonld" do + assert JSON.LD.read_file!(file(input), test_case_options(test_case, base_iri)) == + RDF.Dataset.new([ + {RDF.bnode("b0"), ~I, RDF.bnode("b0")}, + {RDF.bnode("b0"), RDF.bnode("b0"), "plain value"}, + {RDF.bnode("b0"), RDF.bnode("b0"), ~I}, + {RDF.bnode("b0"), RDF.bnode("b0"), RDF.bnode("b0")}, + {RDF.bnode("b0"), RDF.bnode("b0"), RDF.bnode("b1")}, + {RDF.bnode("b0"), RDF.bnode("b0"), RDF.bnode("b2")}, + {RDF.bnode("b0"), RDF.bnode("b0"), RDF.bnode("b3")}, + {RDF.bnode("b1"), RDF.bnode("b0"), "term"}, + {RDF.bnode("b2"), RDF.bnode("b0"), "termId"}, + ]) + else + assert JSON.LD.read_file!(file(input), test_case_options(test_case, base_iri)) == + RDF.NQuads.read_file!(file(output)) + end + end + end) +end diff --git a/test/unit/decoder_test.exs b/test/unit/decoder_test.exs index cfd16fd..9240a55 100644 --- a/test/unit/decoder_test.exs +++ b/test/unit/decoder_test.exs @@ -3,7 +3,7 @@ defmodule JSON.LD.DecoderTest do doctest JSON.LD.Decoder - alias RDF.{Dataset, Graph} + alias RDF.Dataset alias RDF.NS alias RDF.NS.{XSD, RDFS} @@ -19,7 +19,7 @@ defmodule JSON.LD.DecoderTest do test "an empty JSON document is deserialized to an empty graph" do - assert JSON.LD.Decoder.decode!("{}") == Dataset.new(Graph.new) + assert JSON.LD.Decoder.decode!("{}") == Dataset.new end describe "unnamed nodes" do @@ -378,11 +378,11 @@ defmodule JSON.LD.DecoderTest do "foaf:knows": {"@list": ["Manu Sporny", "Dave Longley"]} }), [ - {~I, ~I, RDF.bnode("b1")}, - {RDF.bnode("b1"), NS.RDF.first, RDF.literal("Manu Sporny")}, - {RDF.bnode("b1"), NS.RDF.rest, RDF.bnode("b0")}, - {RDF.bnode("b0"), NS.RDF.first, RDF.literal("Dave Longley")}, - {RDF.bnode("b0"), NS.RDF.rest, NS.RDF.nil}, + {~I, ~I, RDF.bnode("b0")}, + {RDF.bnode("b0"), NS.RDF.first, RDF.literal("Manu Sporny")}, + {RDF.bnode("b0"), NS.RDF.rest, RDF.bnode("b1")}, + {RDF.bnode("b1"), NS.RDF.first, RDF.literal("Dave Longley")}, + {RDF.bnode("b1"), NS.RDF.rest, NS.RDF.nil}, ] }, }