mirror of
https://github.com/IBM/fp-go.git
synced 2025-08-10 22:31:32 +02:00
266 lines
6.3 KiB
Go
266 lines
6.3 KiB
Go
// Copyright (c) 2023 IBM Corp.
|
|
// All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package testing
|
|
|
|
import (
|
|
"testing"
|
|
|
|
EQT "github.com/IBM/fp-go/eq/testing"
|
|
F "github.com/IBM/fp-go/function"
|
|
I "github.com/IBM/fp-go/identity"
|
|
L "github.com/IBM/fp-go/optics/lens"
|
|
LI "github.com/IBM/fp-go/optics/lens/iso"
|
|
O "github.com/IBM/fp-go/option"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type (
|
|
Street struct {
|
|
num int
|
|
name string
|
|
}
|
|
|
|
Address struct {
|
|
city string
|
|
street *Street
|
|
}
|
|
|
|
Inner struct {
|
|
Value int
|
|
Foo string
|
|
}
|
|
|
|
InnerOpt struct {
|
|
Value *int
|
|
Foo *string
|
|
}
|
|
|
|
Outer struct {
|
|
inner *Inner
|
|
}
|
|
|
|
OuterOpt struct {
|
|
inner *InnerOpt
|
|
}
|
|
)
|
|
|
|
func (outer *OuterOpt) GetInner() *InnerOpt {
|
|
return outer.inner
|
|
}
|
|
|
|
func (outer *OuterOpt) SetInner(inner *InnerOpt) *OuterOpt {
|
|
outer.inner = inner
|
|
return outer
|
|
}
|
|
|
|
func (inner *InnerOpt) GetValue() *int {
|
|
return inner.Value
|
|
}
|
|
|
|
func (inner *InnerOpt) SetValue(value *int) *InnerOpt {
|
|
inner.Value = value
|
|
return inner
|
|
}
|
|
|
|
func (outer *Outer) GetInner() *Inner {
|
|
return outer.inner
|
|
}
|
|
|
|
func (outer *Outer) SetInner(inner *Inner) *Outer {
|
|
outer.inner = inner
|
|
return outer
|
|
}
|
|
|
|
func (inner *Inner) GetValue() int {
|
|
return inner.Value
|
|
}
|
|
|
|
func (inner *Inner) SetValue(value int) *Inner {
|
|
inner.Value = value
|
|
return inner
|
|
}
|
|
|
|
func (street *Street) GetName() string {
|
|
return street.name
|
|
}
|
|
|
|
func (street *Street) SetName(name string) *Street {
|
|
street.name = name
|
|
return street
|
|
}
|
|
|
|
func (addr *Address) GetStreet() *Street {
|
|
return addr.street
|
|
}
|
|
|
|
func (addr *Address) SetStreet(s *Street) *Address {
|
|
addr.street = s
|
|
return addr
|
|
}
|
|
|
|
var (
|
|
streetLens = L.MakeLensRef((*Street).GetName, (*Street).SetName)
|
|
addrLens = L.MakeLensRef((*Address).GetStreet, (*Address).SetStreet)
|
|
outerLens = L.FromNillableRef(L.MakeLensRef((*Outer).GetInner, (*Outer).SetInner))
|
|
valueLens = L.MakeLensRef((*Inner).GetValue, (*Inner).SetValue)
|
|
|
|
outerOptLens = L.FromNillableRef(L.MakeLensRef((*OuterOpt).GetInner, (*OuterOpt).SetInner))
|
|
valueOptLens = L.MakeLensRef((*InnerOpt).GetValue, (*InnerOpt).SetValue)
|
|
|
|
sampleStreet = Street{num: 220, name: "Schönaicherstr"}
|
|
sampleAddress = Address{city: "Böblingen", street: &sampleStreet}
|
|
sampleStreet2 = Street{num: 220, name: "Neue Str"}
|
|
|
|
defaultInner = Inner{
|
|
Value: -1,
|
|
Foo: "foo",
|
|
}
|
|
|
|
emptyOuter = Outer{}
|
|
|
|
defaultInnerOpt = InnerOpt{
|
|
Value: &defaultInner.Value,
|
|
Foo: &defaultInner.Foo,
|
|
}
|
|
|
|
emptyOuterOpt = OuterOpt{}
|
|
)
|
|
|
|
func TestStreetLensLaws(t *testing.T) {
|
|
// some comparison
|
|
eqs := EQT.Eq[*Street]()
|
|
eqa := EQT.Eq[string]()
|
|
|
|
laws := AssertLaws(
|
|
t,
|
|
eqa,
|
|
eqs,
|
|
)(streetLens)
|
|
|
|
cpy := sampleStreet
|
|
assert.True(t, laws(&sampleStreet, "Neue Str."))
|
|
assert.Equal(t, cpy, sampleStreet)
|
|
}
|
|
|
|
func TestAddrLensLaws(t *testing.T) {
|
|
// some comparison
|
|
eqs := EQT.Eq[*Address]()
|
|
eqa := EQT.Eq[*Street]()
|
|
|
|
laws := AssertLaws(
|
|
t,
|
|
eqa,
|
|
eqs,
|
|
)(addrLens)
|
|
|
|
cpyAddr := sampleAddress
|
|
cpyStreet := sampleStreet2
|
|
assert.True(t, laws(&sampleAddress, &sampleStreet2))
|
|
assert.Equal(t, cpyAddr, sampleAddress)
|
|
assert.Equal(t, cpyStreet, sampleStreet2)
|
|
}
|
|
|
|
func TestCompose(t *testing.T) {
|
|
// some comparison
|
|
eqs := EQT.Eq[*Address]()
|
|
eqa := EQT.Eq[string]()
|
|
|
|
streetName := L.Compose[*Address](streetLens)(addrLens)
|
|
|
|
laws := AssertLaws(
|
|
t,
|
|
eqa,
|
|
eqs,
|
|
)(streetName)
|
|
|
|
cpyAddr := sampleAddress
|
|
cpyStreet := sampleStreet
|
|
assert.True(t, laws(&sampleAddress, "Neue Str."))
|
|
assert.Equal(t, cpyAddr, sampleAddress)
|
|
assert.Equal(t, cpyStreet, sampleStreet)
|
|
}
|
|
|
|
func TestOuterLensLaws(t *testing.T) {
|
|
// some equal predicates
|
|
eqValue := EQT.Eq[int]()
|
|
eqOptValue := O.Eq(eqValue)
|
|
// lens to access a value from outer
|
|
valueFromOuter := L.ComposeOption[*Outer, int](&defaultInner)(valueLens)(outerLens)
|
|
// try to access the value, this should get an option
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]()))
|
|
// update the object
|
|
withValue := valueFromOuter.Set(O.Some(1))(&emptyOuter)
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuter), O.None[int]()))
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1)))
|
|
// updating with none should remove the inner
|
|
nextValue := valueFromOuter.Set(O.None[int]())(withValue)
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]()))
|
|
// check if this meets the laws
|
|
|
|
eqOuter := EQT.Eq[*Outer]()
|
|
|
|
laws := AssertLaws(
|
|
t,
|
|
eqOptValue,
|
|
eqOuter,
|
|
)(valueFromOuter)
|
|
|
|
assert.True(t, laws(&emptyOuter, O.Some(2)))
|
|
assert.True(t, laws(&emptyOuter, O.None[int]()))
|
|
|
|
assert.True(t, laws(withValue, O.Some(2)))
|
|
assert.True(t, laws(withValue, O.None[int]()))
|
|
}
|
|
|
|
func TestOuterOptLensLaws(t *testing.T) {
|
|
// some equal predicates
|
|
eqValue := EQT.Eq[int]()
|
|
eqOptValue := O.Eq(eqValue)
|
|
intIso := LI.FromNillable[int]()
|
|
// lens to access a value from outer
|
|
valueFromOuter := F.Pipe3(
|
|
valueOptLens,
|
|
LI.Compose[*InnerOpt](intIso),
|
|
L.ComposeOptions[*OuterOpt, int](&defaultInnerOpt),
|
|
I.Ap[L.Lens[*OuterOpt, O.Option[int]]](outerOptLens),
|
|
)
|
|
|
|
// try to access the value, this should get an option
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]()))
|
|
// update the object
|
|
withValue := valueFromOuter.Set(O.Some(1))(&emptyOuterOpt)
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(&emptyOuterOpt), O.None[int]()))
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(withValue), O.Some(1)))
|
|
// updating with none should remove the inner
|
|
nextValue := valueFromOuter.Set(O.None[int]())(withValue)
|
|
assert.True(t, eqOptValue.Equals(valueFromOuter.Get(nextValue), O.None[int]()))
|
|
// check if this meets the laws
|
|
|
|
eqOuter := EQT.Eq[*OuterOpt]()
|
|
|
|
laws := AssertLaws(
|
|
t,
|
|
eqOptValue,
|
|
eqOuter,
|
|
)(valueFromOuter)
|
|
|
|
assert.True(t, laws(&emptyOuterOpt, O.Some(2)))
|
|
assert.True(t, laws(&emptyOuterOpt, O.None[int]()))
|
|
|
|
assert.True(t, laws(withValue, O.Some(2)))
|
|
assert.True(t, laws(withValue, O.None[int]()))
|
|
}
|