rdf-ex/test/support/xsd_datatype_case.ex
2020-06-29 10:37:42 +02:00

352 lines
13 KiB
Elixir

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
alias RDF.TestDatatypes.{Age, DecimalUnitInterval, DoubleUnitInterval, FloatUnitInterval}
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
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)
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
assert unquote(datatype).derived_from?(unquote(datatype)) == false
unless unquote(primitive) do
assert unquote(datatype).derived_from?(unquote(base)) == true
assert unquote(datatype).derived_from?(unquote(base_primitive)) == true
end
end
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
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
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
end
test "datatype_id/1" do
Enum.each(@valid, fn {input, _} ->
assert unquote(datatype).new(input) |> unquote(datatype).datatype_id() ==
RDF.iri(unquote(datatype_iri))
end)
end
test "language/1" do
Enum.each(@valid, fn {input, _} ->
assert unquote(datatype).new(input) |> unquote(datatype).language() == nil
end)
end
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)
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)
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