Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

laws testing: add insert/delete, generalize modify #136

Merged
merged 6 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions ext/AccessorsTestExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,25 @@ function Accessors.test_getset_laws(lens, obj, val1, val2; cmp=(==))
obj12 = set(obj1, lens, val2)
obj2 = set(obj12, lens, val2)
@test cmp(obj12, obj2)

Accessors.test_modify_law(identity, lens, obj; cmp)
end

function Accessors.test_modify_law(f, lens, obj)
function Accessors.test_modify_law(f, lens, obj; cmp=(==))
obj_modify = modify(f, obj, lens)
old_val = lens(obj)
val = f(old_val)
obj_setfget = set(obj, lens, val)
@test obj_modify == obj_setfget
old_vals = getall(obj, lens)
vals = map(f, old_vals)
obj_setfget = setall(obj, lens, vals)
@test cmp(obj_modify, obj_setfget)
end

function Accessors.test_insertdelete_laws(lens, obj, val; cmp=(==))
obj1 = insert(obj, lens, val)
@test cmp(lens(obj1), val)
obj2 = set(obj1, lens, val)
@test cmp(obj1, obj2)
obj3 = delete(obj1, lens)
@test cmp(obj, obj3)
end

function Accessors.test_getsetall_laws(optic, obj, vals1, vals2; cmp=(==))
Expand All @@ -39,6 +50,8 @@ function Accessors.test_getsetall_laws(optic, obj, vals1, vals2; cmp=(==))
obj12 = setall(obj1, optic, vals2)
obj2 = setall(obj12, optic, vals2)
@test obj12 == obj2

Accessors.test_modify_law(identity, optic, obj; cmp)
end

end
28 changes: 26 additions & 2 deletions src/optics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,20 @@ function modify end

Replace a part according to `optic` of `obj` by `val`.

For a callable `optic`, this law defines the `set` operation: `optic(set(obj, optic, val)) == val` (for an appropriate notion of equality).

```jldoctest
julia> using Accessors

julia> obj = (a=1, b=2); lens=@optic _.a; val = 100;

julia> set(obj, lens, val)
(a = 100, b = 2)

julia> lens = Elements();

julia> set(obj, lens, val)
(a = 100, b = 100)
```
See also [`modify`](@ref).
"""
Expand All @@ -51,14 +58,29 @@ function set end

Delete a part according to `optic` of `obj`.

Note that `optic(delete(obj, optic))` can still have a valid value: for example, when deleting an element from a `Tuple` or `Vector`.

```jldoctest
julia> using Accessors

julia> obj = (a=1, b=2); lens=@optic _.a;

julia> delete(obj, lens)
julia> obj_d = delete(obj, lens)
(b = 2,)

julia> lens(obj_d)
ERROR: type NamedTuple has no field a


julia> obj = (1, 2); lens=first;

julia> obj_d = delete(obj, lens)
(2,)

julia> lens(obj_d)
2
```
See also [`set`](@ref), [`insert`](@ref).
"""
function delete end

Expand All @@ -67,6 +89,8 @@ function delete end

Insert a part according to `optic` into `obj` with the value `val`.

For a callable `optic`, this law defines the `insert` operation: `optic(insert(obj, optic, val)) == val` (for an appropriate notion of equality).

```jldoctest
julia> using Accessors

Expand All @@ -75,7 +99,7 @@ julia> obj = (a=1, b=2); lens=@optic _.c; val = 100;
julia> insert(obj, lens, val)
(a = 1, b = 2, c = 100)
```
See also [`set`](@ref).
See also [`set`](@ref), [`delete`](@ref).
"""
function insert end

Expand Down
1 change: 1 addition & 0 deletions src/testing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
function test_getset_laws end
function test_modify_law end
function test_getsetall_laws end
function test_insertdelete_laws end
3 changes: 1 addition & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ PerformanceTestTools.@include("perf.jl")
include("test_examples.jl")
include("test_core.jl")
include("test_optics.jl")
include("test_delete.jl")
include("test_insert.jl")
include("test_insert_delete.jl")
include("test_extensions.jl")
include("test_quicktypes.jl")
include("test_setmacro.jl")
Expand Down
7 changes: 6 additions & 1 deletion test/test_extensions.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module TestExtensions
using Test
using Accessors
using Accessors: test_getset_laws
using Accessors: test_getset_laws, test_insertdelete_laws
using AxisKeys
using IntervalSets
using StaticArrays, StaticNumbers
Expand Down Expand Up @@ -122,6 +122,9 @@ end
end
test_getset_laws(SVector, (0, 1), SVector('x', 'y'), SVector(1, 2); cmp=cmp)
test_getset_laws(MVector, (0, 1), MVector('x', 'y'), MVector(1, 2); cmp=cmp)

test_insertdelete_laws((@optic _[1]), SVector(1), 2)
test_insertdelete_laws((@optic _[2]), SVector(1), 2)
end


Expand All @@ -139,6 +142,7 @@ VERSION >= v"1.9-" && @testset "StructArrays" begin
@test sb.:2 === 10:12
ss = @delete sb.:2
@test ss.:1 === s.:1
test_insertdelete_laws((@optic _.:2), s, 10:12)

s = StructArray(a=[1, 2, 3])
sb = @insert StructArrays.components(s).b = 10:12
Expand All @@ -161,6 +165,7 @@ VERSION >= v"1.9-" && @testset "StructArrays" begin
@test @insert(s.b = 10:11)::StructArray == [(a=(x=1, y=:abc), b=10), (a=(x=2, y=:def), b=11)]
@test @insert(s.a.z = 10:11)::StructArray == [(a=(x=1, y=:abc, z=10),), (a=(x=2, y=:def, z=11),)]
@test @delete(s.a.y)::StructArray == [(a=(x=1,),), (a=(x=2,),)]
test_insertdelete_laws((@optic _.a.z), s, ["a", "b"])

s = StructArray([S(1, 2), S(3, 4)])
@test @inferred(set(s, PropertyLens(:a), 10:11))::StructArray == StructArray([S(10, 2), S(11, 4)])
Expand Down
3 changes: 2 additions & 1 deletion test/test_functionlenses.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ using Unitful
using LinearAlgebra: norm, diag
using AxisKeys
using InverseFunctions: inverse
using Accessors: test_getset_laws, test_modify_law
using Accessors: test_getset_laws, test_modify_law, test_insertdelete_laws
using Accessors


Expand Down Expand Up @@ -159,6 +159,7 @@ end
B = @insert size(A)[2] = 1
@test reshape(A, (2, 1, 3)) == B
@test A == @delete size(B)[2]
test_insertdelete_laws((@optic size(_)[2]), A, 1)
@test_throws Exception @set size(A)[1] = 1
@test_throws Exception @insert size(A)[2] = 2

Expand Down
9 changes: 5 additions & 4 deletions test/test_getsetall.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module TestGetSetAll
using Test
using Accessors
using Accessors: test_getsetall_laws
using Accessors: test_getsetall_laws, test_modify_law
using StaticNumbers
using StaticArrays

Expand Down Expand Up @@ -155,14 +155,15 @@ end
@test_broken ([1, 2], [3.0, 4.0, 5.0], ("6",)) == @inferred setall(obj, @optic(_ |> Elements() |> Elements()), (1, 2, 3., 4., 5., "6"))
end

@testset "getall/setall consistency" begin
@testset "getall-setall laws" begin
for (optic, obj, vals1, vals2) in [
(Elements(), (1, "2"), (2, 3), (4, 5)),
(Properties(), (a=1, b="2"), (2, 3), (4, 5)),
(Elements(), (1, false), (2, 3), (4, 5)),
(Properties(), (a=1, b=false), (2, 3), (4, 5)),
(If(x -> x isa Number) ∘ Properties(), (a=1, b="2"), (2,), (4,)),
(@optic(_.b |> Elements() |> Properties() |> _ * 3), (a=1, b=((c=3, d=4), (c=5, d=6))), 1:4, (-9, -12, -15, -18)),
]
test_getsetall_laws(optic, obj, vals1, vals2)
test_modify_law(x -> x + 1, optic, obj)
end
end

Expand Down
70 changes: 0 additions & 70 deletions test/test_insert.jl

This file was deleted.

81 changes: 79 additions & 2 deletions test/test_delete.jl → test/test_insert_delete.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,73 @@
module TestDelete
module TestInsertDelete
using Test
using Accessors
using StaticArrays
using Accessors
using Accessors: insert
using Accessors: test_insertdelete_laws


@testset "test insert" begin
@testset "function" begin
@test @inferred(insert( (b=2, c=3), @optic(_.a), 1 )) == (b=2, c=3, a=1)
@test insert( (b=2, c=3), @optic(_[:a]), 1 ) == (b=2, c=3, a=1)
let A = [1, 2]
@test insert(A, @optic(_[2]), 3) == [1, 3, 2]
@test_throws BoundsError insert(A, @optic(_[4]), 3)
@test_throws Exception insert(A, @optic(_[1, 3]), 3)
@test insert(A, first, 3) == [3, 1, 2]
@test insert(A, @optic(first(_, 2)), [3, 4]) == [3, 4, 1, 2]
@test insert(A, @optic(last(_, 2)), [3, 4]) == [1, 2, 3, 4]
@test A == [1, 2] # not changed
end
@test @inferred(insert(CartesianIndex(1, 2, 3), @optic(_[2]), 4)) == CartesianIndex(1, 4, 2, 3)
@test insert((1,2), last, 3) == (1, 2, 3)
@inferred(insert((1,2), last, 3))
@test @inferred(insert(SVector(1,2), @optic(_[1]), 3)) == SVector(3, 1, 2)
@test @inferred(insert(SVector(1,2), last, 3)) == SVector(1, 2, 3)
let D = Dict(:a => 1)
@test insert(D, @optic(_[:b]), 2) == Dict(:a => 1, :b => 2)
@test D == Dict(:a => 1) # not changed
end
@test insert((a=1, b=(2, 3)), @optic(_.b[2]), "xxx") === (a=1, b=(2, "xxx", 3))
@test_broken begin
@inferred insert((a=1, b=(2, 3)), @optic(_.b[2]), "xxx")
true
end
@test @inferred(insert((1, 2), @optic(_[1]), 3)) == (3, 1, 2)
end

@testset "macro" begin
x = (b=2, c=3)
@test @insert(x.a = 1) === (b=2, c=3, a=1)
@test @insert(x[(:a, :x)] = (1, :xyz)) === (b=2, c=3, a=1, x=:xyz)
@test @insert(x[(:a, :x)] = (x=:xyz, a=1)) === (b=2, c=3, a=1, x=:xyz)
x = [1, 2]
@test @insert(x[3] = 3) == [1, 2, 3]
x = (a=(b=(1, 2),), c=1)
@test @insert(x.a.b[1] = 0) == (a=(b=(0, 1, 2),), c=1)

# inferred & constant-propagated:
function doit(nt)
nt = @delete nt[1]
nt = @insert nt[:a] =1
nt = @delete nt[(:a, :c)]
nt = @insert nt[(:x, :y)] = ("def", :abc)
return nt
end
@test @inferred(doit((a='3', b=2, c="1"))) === (b=2, x="def", y=:abc)

x = (1, 2)
@test [@insert(x[3] = 3)] == [(1, 2, 3)]

A = [(x=1, y=2), (x=3, y=4)]
@test @insert(Elements()(A).z = 5) == [(x=1, y=2, z=5), (x=3, y=4, z=5)]
end

@testset "friendly error" begin
res = @test_throws ArgumentError Accessors.insertmacro(identity, :(obj.prop == val))
@test occursin("obj.prop == val", res.value.msg)
end
end

@testset "test delete" begin
@testset "function" begin
Expand Down Expand Up @@ -73,4 +139,15 @@ using StaticArrays
end
end

@testset "insert-delete laws" begin
test_insertdelete_laws((@o _.c), (a=1, b=2), "3")
@testset for o in ((@o _[2]), (@o _[3]), first, last), obj in ((1, 2), [1, 2])
test_insertdelete_laws(o, obj, 3)
end
@testset for o in ((@o _.a[2]), (@o _.a[3]), (@o first(_.a)), (@o last(_.a)))
test_insertdelete_laws(o, (a=(1, 2),), "3")
end
test_insertdelete_laws((@o first(_, 2)), [1, 2, 3], [4, 5])
end

end
Loading