rdf-ex/test/unit/list_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

418 lines
13 KiB
Elixir

defmodule RDF.ListTest do
use RDF.Test.Case
doctest RDF.List
import RDF.Sigils
alias RDF.{IRI, BlankNode, Literal, Graph}
use RDF.Vocabulary.Namespace
defvocab EX,
base_iri: "http://example.org/#",
terms: [], strict: false
setup do
{:ok,
empty: RDF.List.new(RDF.nil, Graph.new),
one: RDF.List.from([EX.element], head: ~B<one>),
abc: RDF.List.from(~w[a b c], head: ~B<abc>),
ten: RDF.List.from(Enum.to_list(1..10), head: ~B<ten>),
nested: RDF.List.from(["foo", [1, 2], "bar"], head: ~B<nested>),
}
end
describe "new/2" do
#######################################################################
# success cases
test "valid head list node" do
graph = Graph.new(
~B<Foo>
|> RDF.first(1)
|> RDF.rest(~B<Bar>))
|> Graph.add(
~B<Bar>
|> RDF.first(2)
|> RDF.rest(RDF.nil))
assert %RDF.List{} = list = RDF.List.new(~B<Foo>, graph)
assert list.head == ~B<Foo>
assert list.graph == graph
end
test "with non-blank list nodes" do
graph = Graph.new(
EX.Foo
|> RDF.first(1)
|> RDF.rest(RDF.nil))
assert %RDF.List{} = list = RDF.List.new(EX.Foo, graph)
assert list.head == iri(EX.Foo)
end
test "with other properties on its nodes" do
assert RDF.List.new(~B<Foo>,
Graph.new(
~B<Foo>
|> EX.other(EX.Property)
|> RDF.first(1)
|> RDF.rest(~B<Bar>))
|> Graph.add(
~B<Bar>
|> EX.other(EX.Property2)
|> RDF.first(2)
|> RDF.rest(RDF.nil))
)
|> RDF.List.valid? == true
end
#######################################################################
# failure cases
test "when given list node doesn't exist in the given graph" do
assert RDF.List.new(RDF.bnode, RDF.Graph.new) == nil
end
test "When the given head node is not a list" do
assert RDF.List.new(42, RDF.Graph.new) == nil
assert RDF.List.new(EX.Foo, RDF.Graph.new({EX.Foo, EX.bar, EX.Baz})) == nil
assert RDF.List.new(EX.Foo, RDF.Graph.new({EX.Foo, RDF.first, EX.Baz})) == nil
end
test "when list nodes are incomplete" do
assert RDF.List.new(EX.Foo, RDF.Graph.new({EX.Foo, RDF.first, EX.Baz})) == nil
assert RDF.List.new(EX.Foo, RDF.Graph.new({EX.Foo, RDF.rest, RDF.nil})) == nil
end
test "when head node has multiple rdf:first objects" do
assert RDF.List.new(~B<Foo>,
Graph.new(
~B<Foo>
|> RDF.first(1, 2)
|> RDF.rest(RDF.nil))
) == nil
end
test "when later list nodes have multiple rdf:first objects" do
assert RDF.List.new(~B<Foo>,
Graph.new(
~B<Foo>
|> RDF.first(1)
|> RDF.rest(~B<Bar>))
|> Graph.add(
~B<Bar>
|> RDF.first(2, 3)
|> RDF.rest(RDF.nil))
) == nil
end
test "when list nodes have multiple rdf:rest objects" do
assert RDF.List.new(~B<Foo>,
Graph.new(
~B<Foo>
|> RDF.first(1)
|> RDF.rest(~B<Bar>, ~B<Baz>))
|> Graph.add(
~B<Bar>
|> RDF.first(2)
|> RDF.rest(RDF.nil))
|> Graph.add(
~B<Baz>
|> RDF.first(3)
|> RDF.rest(RDF.nil))
) == nil
assert RDF.List.new(~B<Foo>,
Graph.new(
~B<Foo>
|> RDF.first(1)
|> RDF.rest(~B<Bar>))
|> Graph.add(
~B<Bar>
|> RDF.first(2)
|> RDF.rest(RDF.nil, ~B<Baz>))
|> Graph.add(
~B<Baz>
|> RDF.first(3)
|> RDF.rest(RDF.nil))
) == nil
end
test "when the list is cyclic" do
assert RDF.List.new(~B<Foo>,
Graph.new(
~B<Foo>
|> RDF.first(1)
|> RDF.rest(~B<Bar>))
|> Graph.add(
~B<Bar>
|> RDF.first(2)
|> RDF.rest(~B<Foo>))
) == nil
end
end
describe "from/1" do
test "an empty list", %{empty: empty} do
assert RDF.List.from([]) == empty
end
test "an empty list with named head node", %{empty: empty} do
assert RDF.List.from([], name: ~B<foo>) == empty
end
%{
"IRI" => iri(EX.Foo),
"blank node" => ~B<Foo>,
"literal" => ~L<Foo>,
"string" => "Foo",
"integer" => 42,
"float" => 3.14,
"true" => true,
"false" => false,
"unresolved namespace-qualified name" => EX.Foo,
}
|> Enum.each(fn {type, element} ->
@tag element: element
test "list with #{type} element", %{element: element} do
with {bnode, graph_with_list} = one_element_list(element) do
assert RDF.List.from([element], head: bnode) ==
RDF.List.new(bnode, graph_with_list)
end
end
end)
test "nested list" do
assert %RDF.List{head: bnode, graph: graph_with_list} =
RDF.List.from([[1]])
assert [nested] = get_in(graph_with_list, [bnode, RDF.first])
assert get_in(graph_with_list, [bnode, RDF.rest]) == [RDF.nil]
assert get_in(graph_with_list, [nested, RDF.first]) == [XSD.integer(1)]
assert get_in(graph_with_list, [nested, RDF.rest]) == [RDF.nil]
assert %RDF.List{head: bnode, graph: graph_with_list} =
RDF.List.from(["foo", [1, 2], "bar"])
assert get_in(graph_with_list, [bnode, RDF.first]) == [~L"foo"]
assert [second] = get_in(graph_with_list, [bnode, RDF.rest])
assert [nested] = get_in(graph_with_list, [second, RDF.first])
assert get_in(graph_with_list, [nested, RDF.first]) == [XSD.integer(1)]
assert [nested_second] = get_in(graph_with_list, [nested, RDF.rest])
assert get_in(graph_with_list, [nested_second, RDF.first]) == [XSD.integer(2)]
assert get_in(graph_with_list, [nested_second, RDF.rest]) == [RDF.nil]
assert [third] = get_in(graph_with_list, [second, RDF.rest])
assert get_in(graph_with_list, [third, RDF.first]) == [~L"bar"]
assert get_in(graph_with_list, [third, RDF.rest]) == [RDF.nil]
end
%{
"preserve order" => [3, 2, 1],
"different types" => [1, "foo", true, false, 3.14, EX.foo, EX.Foo, ~B<Foo>],
}
|> Enum.each(fn {desc, list} ->
@tag list: list
test "list with multiple elements: #{desc}", %{list: list} do
assert %RDF.List{head: bnode, graph: graph_with_list} =
RDF.List.from(list)
assert RDF.nil ==
Enum.reduce list, bnode, fn element, list_node ->
case element do
%IRI{} ->
assert get_in(graph_with_list, [list_node, RDF.first]) == [element]
%BlankNode{} ->
assert get_in(graph_with_list, [list_node, RDF.first]) == [element]
%Literal{} ->
assert get_in(graph_with_list, [list_node, RDF.first]) == [element]
element when is_boolean(element) ->
assert get_in(graph_with_list, [list_node, RDF.first]) == [RDF.Literal.new(element)]
element when is_atom(element) ->
assert get_in(graph_with_list, [list_node, RDF.first]) == [RDF.iri(element)]
_ ->
assert get_in(graph_with_list, [list_node, RDF.first]) == [RDF.Literal.new(element)]
end
[next] = get_in(graph_with_list, [list_node, RDF.rest])
unless next == RDF.nil do
assert %BlankNode{} = next
end
next
end
end
end)
test "an enumerable" do
assert RDF.List.from(MapSet.new([42]), head: ~B<foo>) ==
RDF.List.from([42], head: ~B<foo>)
end
test "head option with unresolved namespace-qualified name" do
assert RDF.List.from([42], head: EX.Foo).head == iri(EX.Foo)
end
end
describe "values/1" do
test "the empty list", %{empty: empty} do
assert RDF.List.values(empty) == []
end
test "list with one element", %{one: one} do
assert RDF.List.values(one) == [EX.element]
end
test "list with multiple elements", %{abc: abc, ten: ten} do
assert RDF.List.values(abc) == ~w[a b c] |> Enum.map(&Literal.new/1)
assert RDF.List.values(ten) == 1..10 |> Enum.to_list |> Enum.map(&Literal.new/1)
end
test "list with non-blank list nodes" do
assert RDF.List.from([EX.element], head: EX.Foo)
|> RDF.List.values == [EX.element]
end
test "nested list", %{nested: nested} do
assert RDF.List.values(nested) ==
[~L"foo", [XSD.integer(1), XSD.integer(2)], ~L"bar"]
assert RDF.list(["foo", [1, 2]]) |> RDF.List.values ==
[~L"foo", [XSD.integer(1), XSD.integer(2)]]
assert RDF.list([[1, 2], "foo"]) |> RDF.List.values ==
[[XSD.integer(1), XSD.integer(2)], ~L"foo"]
inner_list = RDF.list([1, 2], head: ~B<inner>)
assert RDF.list(["foo", ~B<inner>], graph: inner_list.graph)
|> RDF.List.values == [~L"foo", [XSD.integer(1), XSD.integer(2)]]
end
end
describe "nodes/1" do
test "the empty list", %{empty: empty} do
assert RDF.List.nodes(empty) == []
end
test "non-empty list", %{one: one} do
assert RDF.List.nodes(one) == [~B<one>]
end
test "nested list", %{nested: nested} do
assert RDF.list([[1, 2, 3]], head: ~B<outer>)
|> RDF.List.nodes == [~B<outer>]
assert [~B<nested>, _, _] = RDF.List.nodes(nested)
end
end
describe "valid?/2" do
test "the empty list", %{empty: empty} do
assert RDF.List.valid?(empty)
end
test "valid list with one element", %{one: one} do
assert RDF.List.valid?(one) == true
end
test "valid list with multiple elements", %{abc: abc, ten: ten} do
assert RDF.List.valid?(abc) == true
assert RDF.List.valid?(ten) == true
end
test "valid nested list", %{nested: nested} do
assert RDF.List.valid?(nested) == true
end
test "a non-blank list node is not valid" do
assert RDF.list([EX.element], head: EX.Foo) |> RDF.List.valid? == false
end
test "a non-blank list node on later nodes makes the whole list invalid" do
assert RDF.List.new(~B<Foo>,
Graph.new(
~B<Foo>
|> RDF.first(1)
|> RDF.rest(EX.Foo))
|> Graph.add(
EX.Foo
|> RDF.first(2)
|> RDF.rest(RDF.nil))
)
|> RDF.List.valid? == false
end
end
describe "node?" do
test "the empty list", %{empty: empty} do
assert RDF.List.node?(empty.head, empty.graph) == true
end
test "list with one element", %{one: one} do
assert RDF.List.node?(one.head, one.graph) == true
end
test "list with multiple elements", %{abc: abc, ten: ten} do
assert RDF.List.node?(abc.head, abc.graph) == true
assert RDF.List.node?(ten.head, ten.graph) == true
end
test "nested list", %{nested: nested} do
assert RDF.List.node?(nested.head, nested.graph) == true
end
test "unresolved namespace-qualified name" do
assert RDF.List.node?(EX.Foo,
RDF.List.from([EX.element], head: EX.Foo).graph) == true
end
test "when given list node doesn't exist in the given graph" do
assert RDF.List.node?(RDF.bnode, RDF.Graph.new) == false
end
test "literal" do
assert RDF.List.node?(~L"Foo", RDF.Graph.new) == false
assert RDF.List.node?(42, RDF.Graph.new) == false
assert RDF.List.node?(true, RDF.Graph.new) == false
assert RDF.List.node?(false, RDF.Graph.new) == false
assert RDF.List.node?(nil, RDF.Graph.new) == false
end
test "non-list node" do
assert RDF.List.node?(EX.Foo, RDF.Graph.new({EX.Foo, EX.bar, EX.Baz})) == false
end
test "incomplete list nodes" do
assert RDF.List.node?(EX.Foo, RDF.Graph.new({EX.Foo, RDF.first, EX.Baz})) == false
assert RDF.List.node?(EX.Foo, RDF.Graph.new({EX.Foo, RDF.rest, RDF.nil})) == false
end
end
describe "Enumerable.reduce" do
test "the empty list", %{empty: empty} do
assert Enum.reduce(empty, [], fn description, acc -> [description | acc] end) == []
end
test "a valid list", %{one: one} do
assert [one.graph[one.head]] ==
Enum.reduce(one, [], fn description, acc -> [description | acc] end)
end
end
defp one_element_list(element),
do: one_element_list(element, RDF.bnode)
defp one_element_list(element, bnode) do
{bnode,
Graph.new(
bnode
|> RDF.first(element)
|> RDF.rest(RDF.nil)
)
}
end
end