Skip to content

Commit 33c833f

Browse files
committed
more interface tests
1 parent 6ac42c2 commit 33c833f

File tree

4 files changed

+177
-69
lines changed

4 files changed

+177
-69
lines changed

BaseInterfaces/src/BaseInterfaces.jl

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,24 @@ include("set.jl")
1010
include("array.jl")
1111

1212
# Some example interface delarations.
13-
@implements ArrayInterface Base.LogicalIndex # No getindex
14-
@implements ArrayInterface{(:getindex,)} UnitRange
15-
@implements ArrayInterface{(:getindex,)} StepRange
16-
@implements ArrayInterface{(:getindex,:setindex,)} Array
17-
@implements ArrayInterface{(:getindex,:setindex,)} SubArray
18-
@implements ArrayInterface{(:getindex,:setindex,)} PermutedDimsArray
19-
@implements ArrayInterface{(:getindex,:setindex,)} Base.ReshapedArray
13+
14+
# @implements ArrayInterface Base.LogicalIndex # No getindex
15+
@implements ArrayInterface UnitRange
16+
@implements ArrayInterface StepRange
17+
@implements ArrayInterface LinRange
18+
@implements ArrayInterface Base.OneTo
19+
@implements ArrayInterface Base.Slice
20+
@implements ArrayInterface Base.IdentityUnitRange
21+
@implements ArrayInterface Base.CodeUnits
22+
@implements ArrayInterface{(:setindex!,:similar_type,:similar_eltype,:similar_size)} Array
23+
@implements ArrayInterface{(:setindex!,:similar_type,:similar_size)} BitArray
24+
@implements ArrayInterface{(:setindex!,)} SubArray
25+
@implements ArrayInterface{(:setindex!,)} PermutedDimsArray
26+
@implements ArrayInterface{(:setindex!,)} Base.ReshapedArray
2027

2128
@implements DictInterface{(:setindex,)} Dict
2229
@implements DictInterface{(:setindex,)} IdDict
23-
# @implements DictInterface GenericDict
24-
# @implements DictInterface{(:setindex,)} WeakKeyDict
30+
@implements DictInterface{(:setindex,)} WeakKeyDict
2531
@implements DictInterface Base.EnvDict
2632
@implements DictInterface Base.ImmutableDict
2733
@implements DictInterface Base.Pairs
@@ -32,7 +38,9 @@ include("array.jl")
3238
@implements IterationInterface{(:reverse,)} Base.Generator
3339
@implements IterationInterface{(:reverse,:indexing,)} Tuple
3440

35-
@implements SetInterface Set
36-
@implements SetInterface BitSet
41+
# TODO add grouping to reduce the number of options
42+
@implements SetInterface{(:copy,:empty,:emptymutable,:hasfastin,:setdiff,:intersect,:empty!,:delete!,:push!,:copymutable,:sizehint)} Set
43+
@implements SetInterface{(:copy,:empty,:emptymutable,:hasfastin,:setdiff,:intersect,:empty!,:delete!,:push!,:copymutable,:sizehint)} BitSet
44+
@implements SetInterface{(:empty,:emptymutable,:hasfastin,:intersect,:union,:sizehint)} Base.KeySet
3745

3846
end

BaseInterfaces/src/array.jl

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,93 @@
1-
@interface ArrayInterface (
2-
mandatory = (
3-
iterate = A -> Interfaces.test(IterationInterface, A; show=false),
4-
ndims = A -> ndims(A) isa Int,
1+
#= Abstract Arrays
2+
3+
Methods to implement Brief description
4+
size(A) Returns a tuple containing the dimensions of A
5+
getindex(A, i::Int) (if IndexLinear) Linear scalar indexing
6+
getindex(A, I::Vararg{Int, N}) (if IndexCartesian, where N = ndims(A)) N-dimensional scalar indexing
7+
8+
Optional methods Default definition Brief description
9+
IndexStyle(::Type) IndexCartesian() Returns either IndexLinear() or IndexCartesian(). See the description below.
10+
setindex!(A, v, i::Int) (if IndexLinear) Scalar indexed assignment
11+
setindex!(A, v, I::Vararg{Int, N}) (if IndexCartesian, where N = ndims(A)) N-dimensional scalar indexed assignment
12+
getindex(A, I...) defined in terms of scalar getindex Multidimensional and nonscalar indexing
13+
setindex!(A, X, I...) defined in terms of scalar setindex! Multidimensional and nonscalar indexed assignment
14+
iterate defined in terms of scalar getindex Iteration
15+
length(A) prod(size(A)) Number of elements
16+
similar(A) similar(A, eltype(A), size(A)) Return a mutable array with the same shape and element type
17+
similar(A, ::Type{S}) similar(A, S, size(A)) Return a mutable array with the same shape and the specified element type
18+
similar(A, dims::Dims) similar(A, eltype(A), dims) Return a mutable array with the same element type and size dims
19+
similar(A, ::Type{S}, dims::Dims) Array{S}(undef, dims) Return a mutable array with the specified element type and size
20+
21+
Non-traditional indices Default definition Brief description
22+
axes(A) map(OneTo, size(A)) Return a tuple of AbstractUnitRange{<:Integer} of valid indices
23+
similar(A, ::Type{S}, inds) similar(A, S, Base.to_shape(inds)) Return a mutable array with the specified indices inds (see below)
24+
similar(T::Union{Type,Function}, inds) T(Base.to_shape(inds)) Return an array similar to T with the specified indices inds (see below)
25+
=#
26+
27+
# And arbitrary new type for array values
28+
struct ArrayTestVal
29+
a::Int
30+
end
31+
32+
# In case `eltype` and `ndims` have custom methods
33+
# We should always be able to use these to mean the same thing
34+
_eltype(::AbstractArray{T}) where T = T
35+
_ndims(::AbstractArray{<:Any,N}) where N = N
36+
37+
array_components = (;
38+
mandatory = (;
39+
type = A -> A isa AbstractArray,
40+
eltype = (
41+
A -> eltype(A) isa Type,
42+
A -> eltype(A) == _eltype(A),
43+
),
44+
ndims = (
45+
A -> ndims(A) isa Int,
46+
A -> ndims(A) == _ndims(A),
47+
),
548
size = (
649
A -> size(A) isa NTuple{<:Any,Int},
750
A -> length(size(A)) == ndims(A),
851
),
9-
eltype = A -> typeof(first(iterate(A))) <: eltype(A)
52+
getindex = (
53+
A -> A[begin] isa eltype(A),
54+
A -> A[map(first, axes(A))...] isa eltype(A),
55+
),
56+
indexstyle = A -> IndexStyle(A) in (IndexCartesian(), IndexLinear()),
1057
),
1158
# TODO implement all the optional conditions
1259
optional = (;
13-
getindex = A -> true,
14-
setindex = A -> true,
15-
),
16-
) "Base Julia AbstractArray interface"
60+
setindex! = (
61+
A -> length(A) > 1 || throw(ArgumentError("Test arrays must have more than one element to test setindex!")),
62+
A -> begin
63+
# Tests setindex! by simply swapping the first and last elements
64+
x1 = A[begin]; x2 = A[end]
65+
A[begin] = x2
66+
A[end] = x1
67+
A[begin] == x2 && A[end] == x1
68+
end,
69+
A -> begin
70+
fs = map(first, axes(A))
71+
ls = map(last, axes(A))
72+
x1 = A[fs...];
73+
x2 = A[ls...]
74+
A[fs...] = x2
75+
A[ls...] = x1
76+
A[fs...] == x2 && A[ls...] == x1
77+
end,
78+
),
79+
similar_type = A -> similar(A) isa typeof(A),
80+
similar_eltype = A -> begin
81+
A1 = similar(A, ArrayTestVal)
82+
eltype(A1) == ArrayTestVal && _wrappertype(A) == _wrappertype(A1)
83+
end,
84+
similar_size = A -> begin
85+
A1 = similar(A, (2, 3))
86+
size(A1) == (2, 3) && _wrappertype(A) == _wrappertype(A1)
87+
end,
88+
)
89+
)
90+
91+
_wrappertype(A) = Base.typename(typeof(A)).wrapper
92+
93+
@interface ArrayInterface array_components "Base Julia AbstractArray interface"

BaseInterfaces/src/set.jl

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,60 @@
22

33
# Requirements for AbstractSet subtypes:
44

5-
mandatory = (
6-
type = s -> s isa AbstractSet,
7-
eltype = "elements eltype of set `s` are subtypes of `eltype(s)`" => s -> typeof(first(iterate(s))) <: eltype(s),
8-
length = "set defines length and test object has length larger than zero" => s -> length(s) isa Int && length(s) > 0,
9-
iteration = "follows the IterationInterface" => x -> Interfaces.test(IterationInterface, x; show=false),
10-
copy = s -> begin
11-
s1 = copy(s)
12-
s1 !== s && s1 isa typeof(s) && collect(s) == collect(s1)
13-
end,
14-
in = "`in` is true for elements in set" => s -> begin
15-
all(x -> in(x, s), s)
16-
end,
17-
)
18-
19-
optional = (
20-
empty = "returns a empty set able to hold elements of type U" => s -> begin
21-
s1 = Base.empty(s)
22-
eltype(s1) == eltype(s) || return false
23-
s1 = Base.empty(s, Int)
24-
eltype(s1) == Int || return false
25-
end,
26-
emptymutable = "returns a empty set able to hold elements of type U" => s -> begin
27-
s1 = Base.empty(s)
28-
s1 isa typeof(s)
29-
eltype(s1) == eltype(s) || return false
30-
s1 = Base.empty(s, Int)
31-
eltype(s1) == Int || return false
32-
end,
33-
hasfastin = s -> Base.hasfastin(s) isa Bool,
34-
setdiff = s -> begin
35-
sd = setdiff(s, collect(s))
36-
typeof(sd) == typeof(s) && length(sd) == 0
37-
end,
38-
intersect = s -> intesect(s, itr),
39-
union = s -> union(s, itr),
40-
mutable = (
41-
"empty! removes all elements from the set" => s -> empty!(s),
42-
"delete! removes element valued x of the set" => s -> begin
5+
set_components = (;
6+
mandatory = (;
7+
type = s -> s isa AbstractSet,
8+
eltype = "elements eltype of set `s` are subtypes of `eltype(s)`" => s -> typeof(first(iterate(s))) <: eltype(s),
9+
length = "set defines length and test object has length larger than zero" => s -> length(s) isa Int && length(s) > 0,
10+
iteration = "follows the IterationInterface" => x -> Interfaces.test(IterationInterface, x; show=false),
11+
in = "`in` is true for elements in set" => s -> begin
12+
all(x -> in(x, s), s)
13+
end,
14+
),
15+
optional = (;
16+
copy = s -> begin
17+
s1 = copy(s)
18+
s1 !== s && s1 isa typeof(s) && collect(s) == collect(s1)
19+
end,
20+
empty = "returns a empty set able to hold elements of type U" => s -> begin
21+
s1 = Base.empty(s)
22+
eltype(s1) == eltype(s) || return false
23+
s1 = Base.empty(s, Int)
24+
eltype(s1) == Int || return false
25+
end,
26+
emptymutable = "returns a empty set able to hold elements of type U" => s -> begin
27+
s1 = Base.empty(s)
28+
s1 isa typeof(s)
29+
eltype(s1) == eltype(s) || return false
30+
s1 = Base.empty(s, Int)
31+
eltype(s1) == Int || return false
32+
end,
33+
hasfastin = s -> Base.hasfastin(s) isa Bool,
34+
setdiff = s -> begin
35+
sd = setdiff(s, collect(s))
36+
typeof(sd) == typeof(s) && length(sd) == 0
37+
end,
38+
intersect = s -> intersect(s, s) == s, # TODO
39+
union = s -> union(s, s) == s, # TODO
40+
empty! = "empty! removes all elements from the set" => s -> length(empty!(s)) == 0,
41+
delete! = "delete! removes element valued x of the set" => s -> begin
4342
x = first(iterate(s))
4443
!in(delete!(s, x), x)
4544
end,
46-
"push! adds element x to the set" => s -> begin
45+
push! = "push! adds element x to the set" => s -> begin
46+
# TODO do this without delete!
4747
x = first(iterate(s))
4848
delete!(s, x)
4949
push!(s, x)
50-
in(s, x)
50+
in(x, s)
5151
end,
52-
),
53-
coppymutable = s -> begin
54-
s1 = Base.copymutable(s)
55-
s1 isa typeof(s) && s1 !== s
56-
end,
57-
sizehint = s -> (sizehint!(s, n); true),
52+
copymutable = s -> begin
53+
s1 = Base.copymutable(s)
54+
s1 isa typeof(s) && s1 !== s
55+
end,
56+
# TODO is there anything we can actually test here?
57+
sizehint = s -> (sizehint!(s, 10); true),
58+
)
5859
)
5960

60-
@interface SetInterface (; mandatory, optional) "The `AbstractSet` interface"
61+
@interface SetInterface set_components "The `AbstractSet` interface"

BaseInterfaces/test/runtests.jl

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,25 @@ using BaseInterfaces
22
using Interfaces
33
using Test
44

5+
@implements SetInterface{(:empty,:emptymutable,:hasfastin,:intersect,:union,:sizehint)} Test.GenericSet
6+
@implements DictInterface Test.GenericDict
7+
58
@testset "ArrayInterface" begin
6-
@test Interfaces.test(ArrayInterface, Array, [[1, 2]])
7-
@test Interfaces.test(ArrayInterface, SubArray, [view([1, 2], 1:2)])
9+
@test Interfaces.test(ArrayInterface, Array, [[3, 2], ['a' 'b'; 'n' 'm']])
10+
@test Interfaces.test(ArrayInterface, BitArray, [BitArray([false true; true false])])
11+
@test Interfaces.test(ArrayInterface, SubArray, [view([7, 2], 1:2)])
12+
@test Interfaces.test(ArrayInterface, PermutedDimsArray, [PermutedDimsArray([7 2], (2, 1))])
13+
@test Interfaces.test(ArrayInterface, Base.ReshapedArray, [reshape(view([7, 2], 1:2), 2, 1)])
14+
@test Interfaces.test(ArrayInterface, UnitRange, [2:10])
15+
@test Interfaces.test(ArrayInterface, StepRange, [2:1:10])
16+
@test Interfaces.test(ArrayInterface, Base.OneTo, [Base.OneTo(10)])
17+
@test Interfaces.test(ArrayInterface, Base.Slice, [Base.Slice(100:150)])
18+
@test Interfaces.test(ArrayInterface, Base.IdentityUnitRange, [Base.IdentityUnitRange(100:150)])
19+
@test Interfaces.test(ArrayInterface, Base.CodeUnits, [codeunits("abcde")])
20+
# No `getindex` defined for LogicalIndex
21+
@test_broken Interfaces.test(ArrayInterface, Base.LogicalIndex, [to_indices([1, 2, 3], ([false, true, true],))[1]])
22+
23+
# TODO test LinearAlgebra arrays and SparseArrays
824
end
925

1026
@testset "DictInterface" begin
@@ -13,6 +29,9 @@ end
1329
@test Interfaces.test(DictInterface, Base.EnvDict, [Arguments(d=Base.EnvDict())])
1430
@test Interfaces.test(DictInterface, Base.ImmutableDict, [Arguments(d=Base.ImmutableDict(:a => 1, :b => 2))])
1531
@test Interfaces.test(DictInterface, Base.Pairs, [Arguments(d=Base.pairs((a = 1, b = 2)))])
32+
@test Interfaces.test(DictInterface, Test.GenericDict, [Arguments(d=Test.GenericDict(Dict(:a => 1, :b => 2)), k=:c, v=3)])
33+
a = Ref(1); b = Ref(2)
34+
@test Interfaces.test(DictInterface, WeakKeyDict, [Arguments(d= d = WeakKeyDict(a => 1, b => 2), k=Ref(3), v=3)])
1635
end
1736

1837
@testset "IterationInterface" begin
@@ -26,4 +45,7 @@ end
2645
@testset "SetInterface" begin
2746
@test Interfaces.test(SetInterface, Set, [Set((1, 2))])
2847
@test Interfaces.test(SetInterface, BitSet, [BitSet((1, 2))])
48+
@test Interfaces.test(SetInterface, Base.KeySet, [Base.KeySet(Dict(:a=>1, :b=>2))])
49+
@test Interfaces.test(SetInterface, Test.GenericSet, [Test.GenericSet(Set((1, 2)))])
50+
# @test Interfaces.test(SetInterface, Base.IdSet, ?)
2951
end

0 commit comments

Comments
 (0)