2020-05-05 21:58:44 +00:00
|
|
|
defmodule RDF.XSD.Datatype.Test.Case do
|
|
|
|
use ExUnit.CaseTemplate
|
|
|
|
|
|
|
|
alias RDF.XSD
|
|
|
|
|
|
|
|
using(opts) do
|
|
|
|
datatype = Keyword.fetch!(opts, :datatype)
|
|
|
|
datatype_name = Keyword.fetch!(opts, :name)
|
|
|
|
|
|
|
|
datatype_iri =
|
|
|
|
Keyword.get(opts, :iri, RDF.NS.XSD.__base_iri__ <> datatype_name)
|
|
|
|
|
|
|
|
valid = Keyword.get(opts, :valid)
|
|
|
|
invalid = Keyword.get(opts, :invalid)
|
|
|
|
primitive = Keyword.get(opts, :primitive)
|
|
|
|
base = unless primitive, do: Keyword.fetch!(opts, :base)
|
|
|
|
base_primitive = unless primitive, do: Keyword.fetch!(opts, :base_primitive)
|
|
|
|
applicable_facets = Keyword.get(opts, :applicable_facets, [])
|
|
|
|
facets = Keyword.get(opts, :facets)
|
|
|
|
|
|
|
|
quote do
|
|
|
|
alias RDF.XSD
|
|
|
|
alias RDF.XSD.Datatype
|
2020-05-21 13:42:02 +00:00
|
|
|
alias RDF.TestDatatypes.{Age, DecimalUnitInterval, DoubleUnitInterval, FloatUnitInterval}
|
2020-05-05 21:58:44 +00:00
|
|
|
alias unquote(datatype)
|
|
|
|
import unquote(__MODULE__)
|
|
|
|
|
|
|
|
doctest unquote(datatype)
|
|
|
|
|
|
|
|
@moduletag datatype: unquote(datatype)
|
|
|
|
|
|
|
|
if unquote(valid) do
|
|
|
|
@valid unquote(valid)
|
|
|
|
@invalid unquote(invalid)
|
|
|
|
|
|
|
|
test "registration" do
|
2020-05-15 15:13:31 +00:00
|
|
|
assert unquote(datatype) in RDF.Literal.Datatype.Registry.builtin_datatypes()
|
|
|
|
assert unquote(datatype) in RDF.Literal.Datatype.Registry.builtin_xsd_datatypes()
|
|
|
|
|
|
|
|
assert unquote(datatype) |> RDF.Literal.Datatype.Registry.builtin_datatype?()
|
|
|
|
assert unquote(datatype) |> RDF.Literal.Datatype.Registry.builtin_xsd_datatype?()
|
|
|
|
|
|
|
|
assert RDF.Literal.Datatype.get(unquote(datatype_iri)) == unquote(datatype)
|
|
|
|
assert XSD.Datatype.get(unquote(datatype_iri)) == unquote(datatype)
|
2020-05-05 21:58:44 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "primitive/0" do
|
|
|
|
assert unquote(datatype).primitive?() == unquote(!!primitive)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "base/0" do
|
|
|
|
if unquote(primitive) do
|
|
|
|
assert unquote(datatype).base == nil
|
|
|
|
else
|
|
|
|
assert unquote(datatype).base == unquote(base)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "base_primitive/0" do
|
|
|
|
if unquote(primitive) do
|
|
|
|
assert unquote(datatype).base_primitive == unquote(datatype)
|
|
|
|
else
|
|
|
|
assert unquote(datatype).base_primitive == unquote(base_primitive)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "derived_from?/1" do
|
2020-05-15 15:13:31 +00:00
|
|
|
assert unquote(datatype).derived_from?(unquote(datatype)) == false
|
2020-05-05 21:58:44 +00:00
|
|
|
|
|
|
|
unless unquote(primitive) do
|
|
|
|
assert unquote(datatype).derived_from?(unquote(base)) == true
|
|
|
|
assert unquote(datatype).derived_from?(unquote(base_primitive)) == true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-15 15:13:31 +00:00
|
|
|
describe "datatype?/1" do
|
|
|
|
test "with itself" do
|
|
|
|
assert unquote(datatype).datatype?(unquote(datatype)) == true
|
|
|
|
end
|
|
|
|
|
|
|
|
test "with non-RDF values" do
|
|
|
|
assert unquote(datatype).datatype?(self()) == false
|
|
|
|
assert unquote(datatype).datatype?(Elixir.Enum) == false
|
|
|
|
assert unquote(datatype).datatype?(:foo) == false
|
|
|
|
end
|
|
|
|
|
|
|
|
unless unquote(primitive) do
|
|
|
|
test "on a base datatype" do
|
|
|
|
# We're using apply here to suppress "nil.datatype?/1 is undefined" warnings caused by the primitives
|
|
|
|
assert apply(unquote(base), :datatype?, [unquote(datatype)]) == true
|
|
|
|
assert apply(unquote(base_primitive), :datatype?, [unquote(datatype)]) == true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-05-05 21:58:44 +00:00
|
|
|
test "applicable_facets/0" do
|
|
|
|
assert MapSet.new(unquote(datatype).applicable_facets()) ==
|
|
|
|
MapSet.new(unquote(applicable_facets))
|
|
|
|
end
|
|
|
|
|
|
|
|
if unquote(facets) do
|
|
|
|
test "facets" do
|
|
|
|
Enum.each(unquote(facets), fn {facet, value} ->
|
|
|
|
assert apply(unquote(datatype), facet, []) == value
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "name/0" do
|
|
|
|
assert unquote(datatype).name() == unquote(datatype_name)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "id/0" do
|
|
|
|
assert unquote(datatype).id() == RDF.iri(unquote(datatype_iri))
|
|
|
|
end
|
|
|
|
|
2020-05-15 15:13:31 +00:00
|
|
|
describe "general datatype?/1" do
|
|
|
|
test "on the exact same datatype" do
|
|
|
|
assert (unquote(datatype).datatype?(unquote(datatype))) == true
|
|
|
|
Enum.each(@valid, fn {input, _} ->
|
|
|
|
literal = unquote(datatype).new(input)
|
|
|
|
assert (unquote(datatype).datatype?(literal)) == true
|
|
|
|
assert (unquote(datatype).datatype?(literal.literal)) == true
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
unless unquote(primitive) do
|
|
|
|
test "on the base datatype" do
|
|
|
|
assert (unquote(base).datatype?(unquote(datatype))) == true
|
|
|
|
Enum.each(@valid, fn {input, _} ->
|
|
|
|
literal = unquote(datatype).new(input)
|
|
|
|
assert (unquote(base).datatype?(literal)) == true
|
|
|
|
assert (unquote(base).datatype?(literal.literal)) == true
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "on the base primitive datatype" do
|
|
|
|
assert (unquote(base_primitive).datatype?(unquote(datatype))) == true
|
|
|
|
Enum.each(@valid, fn {input, _} ->
|
|
|
|
literal = unquote(datatype).new(input)
|
|
|
|
assert (unquote(base_primitive).datatype?(literal)) == true
|
|
|
|
assert (unquote(base_primitive).datatype?(literal.literal)) == true
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
2020-05-05 21:58:44 +00:00
|
|
|
end
|
|
|
|
|
2020-05-15 20:59:05 +00:00
|
|
|
test "datatype_id/1" do
|
2020-05-05 21:58:44 +00:00
|
|
|
Enum.each(@valid, fn {input, _} ->
|
2020-05-15 20:59:05 +00:00
|
|
|
assert (unquote(datatype).new(input) |> unquote(datatype).datatype_id()) == RDF.iri(unquote(datatype_iri))
|
2020-05-05 21:58:44 +00:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2020-05-15 15:13:31 +00:00
|
|
|
test "language/1" do
|
|
|
|
Enum.each(@valid, fn {input, _} ->
|
|
|
|
assert (unquote(datatype).new(input) |> unquote(datatype).language()) == nil
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2020-05-05 21:58:44 +00:00
|
|
|
describe "general new" do
|
|
|
|
Enum.each(@valid, fn {input, {value, lexical, _}} ->
|
|
|
|
expected = %RDF.Literal{
|
|
|
|
literal: %unquote(datatype){value: value, uncanonical_lexical: lexical}
|
|
|
|
}
|
|
|
|
|
|
|
|
@tag example: %{input: input, output: expected}
|
|
|
|
test "valid: #{unquote(datatype)}.new(#{inspect(input)})", %{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input) == example.output
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
Enum.each(@invalid, fn value ->
|
|
|
|
expected = %RDF.Literal{
|
|
|
|
literal: %unquote(datatype){
|
|
|
|
uncanonical_lexical: unquote(datatype).init_invalid_lexical(value, [])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@tag example: %{input: value, output: expected}
|
|
|
|
test "invalid: #{unquote(datatype)}.new(#{inspect(value)})",
|
|
|
|
%{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input) == example.output
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
test "canonicalize option" do
|
|
|
|
Enum.each(@valid, fn {input, _} ->
|
|
|
|
assert unquote(datatype).new(input, canonicalize: true) ==
|
|
|
|
unquote(datatype).new(input) |> unquote(datatype).canonical()
|
|
|
|
end)
|
|
|
|
|
|
|
|
Enum.each(@invalid, fn input ->
|
|
|
|
assert unquote(datatype).new(input, canonicalize: true) ==
|
|
|
|
unquote(datatype).new(input) |> unquote(datatype).canonical()
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "general new!" do
|
|
|
|
test "with valid values, it behaves the same as new" do
|
|
|
|
Enum.each(@valid, fn {input, _} ->
|
|
|
|
assert unquote(datatype).new!(input) == unquote(datatype).new(input)
|
|
|
|
|
|
|
|
assert unquote(datatype).new!(input) ==
|
|
|
|
unquote(datatype).new(input)
|
|
|
|
|
|
|
|
assert unquote(datatype).new!(input, canonicalize: true) ==
|
|
|
|
unquote(datatype).new(input, canonicalize: true)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "with invalid values, it raises an error" do
|
|
|
|
Enum.each(@invalid, fn value ->
|
|
|
|
assert_raise ArgumentError, fn -> unquote(datatype).new!(value) end
|
|
|
|
|
|
|
|
assert_raise ArgumentError, fn ->
|
|
|
|
unquote(datatype).new!(value, canonicalize: true)
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "general value" do
|
|
|
|
Enum.each(@valid, fn {input, {value, _, canonicalized}} ->
|
|
|
|
@tag example: %{input: input, value: value}
|
|
|
|
test "of valid #{unquote(datatype)}.new(#{inspect(input)})",
|
|
|
|
%{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input) |> unquote(datatype).value() ==
|
|
|
|
example.value
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
Enum.each(@invalid, fn value ->
|
|
|
|
@tag example: %{input: value, value: value}
|
|
|
|
test "of invalid #{unquote(datatype)}.new(#{inspect(value)})", %{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input) |> unquote(datatype).value() == nil
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "general lexical" do
|
|
|
|
Enum.each(@valid, fn {input, {_, lexical, canonicalized}} ->
|
|
|
|
lexical = lexical || canonicalized
|
|
|
|
@tag example: %{input: input, lexical: lexical}
|
|
|
|
test "of valid #{unquote(datatype)}.new(#{inspect(input)})",
|
|
|
|
%{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input) |> unquote(datatype).lexical() ==
|
|
|
|
example.lexical
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
Enum.each(@invalid, fn value ->
|
|
|
|
lexical = unquote(datatype).init_invalid_lexical(value, [])
|
|
|
|
@tag example: %{input: value, lexical: lexical}
|
|
|
|
test "of invalid #{unquote(datatype)}.new(#{inspect(value)}) == #{inspect(lexical)}",
|
|
|
|
%{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input) |> unquote(datatype).lexical() ==
|
|
|
|
example.lexical
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "general canonicalization" do
|
|
|
|
Enum.each(@valid, fn {input, {value, _, _}} ->
|
|
|
|
expected = %RDF.Literal{literal: %unquote(datatype){value: value}}
|
|
|
|
@tag example: %{input: input, output: expected}
|
|
|
|
test "#{unquote(datatype)} #{inspect(input)}", %{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input) |> unquote(datatype).canonical() ==
|
|
|
|
example.output
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
Enum.each(@valid, fn {input, {_, _, canonicalized}} ->
|
|
|
|
@tag example: %{input: input, canonicalized: canonicalized}
|
|
|
|
test "lexical of canonicalized #{unquote(datatype)} #{inspect(input, limit: 4)} is #{
|
|
|
|
inspect(canonicalized, limit: 4)
|
|
|
|
}",
|
|
|
|
%{example: example} do
|
|
|
|
assert unquote(datatype).new(example.input)
|
|
|
|
|> unquote(datatype).canonical()
|
|
|
|
|> unquote(datatype).lexical() ==
|
|
|
|
example.canonicalized
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
Enum.each(@valid, fn {input, {_, _, canonicalized}} ->
|
|
|
|
@tag example: %{input: input, canonicalized: canonicalized}
|
|
|
|
test "canonical? for #{unquote(datatype)} #{inspect(input)}", %{example: example} do
|
|
|
|
literal = unquote(datatype).new(example.input)
|
|
|
|
assert unquote(datatype).canonical?(literal) == (
|
|
|
|
unquote(datatype).lexical(literal) ==example.canonicalized
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
test "does not change the XSD datatype value when it is invalid" do
|
|
|
|
Enum.each(@invalid, fn value ->
|
|
|
|
assert unquote(datatype).new(value) |> unquote(datatype).canonical() ==
|
|
|
|
unquote(datatype).new(value)
|
|
|
|
end)
|
2020-05-06 13:19:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
test "canonical_lexical with valid literals" do
|
|
|
|
Enum.each(@valid, fn {input, {_, _, canonicalized}} ->
|
|
|
|
assert unquote(datatype).new(input) |> unquote(datatype).canonical_lexical() ==
|
|
|
|
canonicalized
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "canonical_lexical with invalid literals" do
|
|
|
|
Enum.each(@invalid, fn value ->
|
|
|
|
assert unquote(datatype).new(value) |> unquote(datatype).canonical_lexical() ==
|
|
|
|
nil
|
|
|
|
end)
|
2020-05-05 21:58:44 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "general validation" do
|
|
|
|
Enum.each(Map.keys(@valid), fn value ->
|
|
|
|
@tag value: value
|
|
|
|
test "#{inspect(value)} as a #{unquote(datatype)} is valid", %{value: value} do
|
|
|
|
assert unquote(datatype).valid?(unquote(datatype).new(value))
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
Enum.each(@invalid, fn value ->
|
|
|
|
@tag value: value
|
|
|
|
test "#{inspect(value)} as a #{unquote(datatype)} is invalid", %{value: value} do
|
|
|
|
refute unquote(datatype).valid?(unquote(datatype).new(value))
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
test "String.Chars protocol implementation" do
|
|
|
|
Enum.each(@valid, fn {input, _} ->
|
|
|
|
assert unquote(datatype).new(input) |> to_string() ==
|
|
|
|
unquote(datatype).new(input) |> unquote(datatype).lexical()
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def dt(value) do
|
|
|
|
{:ok, date, _} = DateTime.from_iso8601(value)
|
|
|
|
date
|
|
|
|
end
|
|
|
|
end
|