From 36f8400a738953856d083cbfc73e977cb0522e46 Mon Sep 17 00:00:00 2001 From: Rafael Schouten Date: Sun, 1 Oct 2023 08:22:34 +0200 Subject: [PATCH 1/2] add singleton traits (#15) * add singleton traits * test traits * test that passing an object rather than a type works * Update src/implements.jl Co-authored-by: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> --------- Co-authored-by: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> --- src/implements.jl | 23 ++++++++++++++++++++++- test/runtests.jl | 6 ++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/implements.jl b/src/implements.jl index 3f5406b..9129ade 100644 --- a/src/implements.jl +++ b/src/implements.jl @@ -15,7 +15,8 @@ Without specifying `Options`, the return value specifies that at least all the mandatory components of the interace are implemented. """ function implements end -implements(::Type{<:Interface}, obj) = false +implements(T::Type{<:Interface}, obj) = implements(T, typeof(obj)) +implements(::Type{<:Interface}, obj::Type) = false """ @implements(interface, objtype) @@ -67,3 +68,23 @@ end _all_in(items::Tuple, collection) = all(map(in(collection), items)) _all_in(item::Symbol, collection) = in(item, collection) + +struct Implemented{T<:Interface} end +struct NotImplemented{T<:Interface} end + +""" + implemented_trait(T::Type{<:Interface}, obj) + implemented_trait(T::Type{<:Interface{Option}}, obj) + +Provides a single type for using interface implementation +as a trait. + +Returns `Implemented{T}()` or `NotImplemented{T}()`. +""" +function implemented_trait(::Type{T}, obj) where T<:Interface + if implements(T, obj) + Implemented{T}() + else + NotImplemented{T}() + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 8c3c9da..27970e3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -41,7 +41,13 @@ Animals.talk(::Duck) = :quack @testset "duck" begin ducks = [Duck(1), Duck(2)] @test Interfaces.implements(Animals.AnimalInterface, Duck) == true + @test Interfaces.implements(Animals.AnimalInterface, first(ducks)) == true @test Interfaces.implements(Animals.AnimalInterface{:dig}, Duck) == false + @test Interfaces.implements(Animals.AnimalInterface{:dig}, first(ducks)) == false + @test @inferred Interfaces.implemented_trait(Animals.AnimalInterface{:walk}, Duck) == Interfaces.Implemented{Animals.AnimalInterface{:walk}}() + @test @inferred Interfaces.implemented_trait(Animals.AnimalInterface{:walk}, first(ducks)) == Interfaces.Implemented{Animals.AnimalInterface{:walk}}() + @test @inferred Interfaces.implemented_trait(Animals.AnimalInterface{:dig}, Duck) == Interfaces.NotImplemented{Animals.AnimalInterface{:dig}}() + @test @inferred Interfaces.implemented_trait(Animals.AnimalInterface{:dig}, first(ducks)) == Interfaces.NotImplemented{Animals.AnimalInterface{:dig}}() @test Interfaces.test(Animals.AnimalInterface, Duck, ducks) == true @test Interfaces.test(Animals.AnimalInterface{(:walk,:talk)}, Duck, ducks) == true # TODO wrap errors somehow, or just let Invariants.jl handle that. From e7936228c2e271fb256e4adea279a67cf632d6bb Mon Sep 17 00:00:00 2001 From: Rafael Schouten Date: Sun, 1 Oct 2023 14:52:23 +0200 Subject: [PATCH 2/2] add aqua tests (#19) * add aqua tests * add aqua dep --- Project.toml | 4 +++- src/test.jl | 15 +++++++++------ test/runtests.jl | 3 +++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index 53e524a..b257b01 100644 --- a/Project.toml +++ b/Project.toml @@ -7,7 +7,9 @@ version = "0.1.0" julia = "1.6" [extras] +Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" +Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test"] +test = ["Aqua", "Documenter", "Test"] diff --git a/src/test.jl b/src/test.jl index 51fcbcd..0a8a5da 100644 --- a/src/test.jl +++ b/src/test.jl @@ -18,15 +18,21 @@ If no interface type is passed, Interfaces.jl will find all the interfaces available and test them. """ function test(T::Type{<:Interface{Keys}}, O::Type, test_objects; kw...) where Keys + # Allow passing the keys in the abstract type + # But get them out and put them in the `keys` keyword T1 = _get_type(T).name.wrapper objs = TestObjectWrapper(test_objects) - return test(T1, O, objs; keys=Keys, kw...) + # And run the tests on the parameterless type + return _test(T1, O, objs; keys=Keys, kw...) end function test(T::Type{<:Interface}, O::Type, test_objects; kw...) objs = TestObjectWrapper(test_objects) - return test(T, O, objs; kw...) + return _test(T, O, objs; kw...) end -function test(T::Type{<:Interface}, O::Type, objs::TestObjectWrapper; +# Convenience method for users to test a single object +test(T::Type{<:Interface}, obj; kw...) = test(T, typeof(obj), (obj,); kw...) + +function _test(T::Type{<:Interface}, O::Type, objs::TestObjectWrapper; show=true, keys=nothing ) if show @@ -52,9 +58,6 @@ function test(T::Type{<:Interface}, O::Type, objs::TestObjectWrapper; return all(_bool(results)) end end -# Convenience method for users to test a single object -test(T::Type{<:Interface}, obj; kw...) = - test(T, typeof(obj), TestObjectWrapper((obj,)); kw...) function _test(tests::NamedTuple{K}, objs::TestObjectWrapper) where K map(keys(tests), values(tests)) do k, v diff --git a/test/runtests.jl b/test/runtests.jl index 27970e3..aea9d6d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,8 @@ using Interfaces using Test +using Aqua + +Aqua.test_all(Interfaces) module Animals