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

Additional tests for copy in Optics #3089

Merged
merged 1 commit into from
Jul 10, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package arrow.optics.plugin

import org.junit.jupiter.api.Test

// from https://kotlinlang.slack.com/archives/C5UPMM0A0/p1688822411819599
// and https://github.com/overfullstack/my-lab/blob/master/arrow/src/test/kotlin/ga/overfullstack/optics/OpticsLab.kt

val copyCode = """
@optics data class Person(val name: String, val age: Int, val address: Address) {
companion object
}
@optics data class Address(val street: Street, val city: City, val coordinates: List<Int>) {
companion object
}
@optics data class Street(val name: String, val number: Int?) {
companion object
}
@optics data class City(val name: String, val country: String) {
companion object
}

fun Person.moveToAmsterdamCopy(): Person = copy {
Person.address.city.name set "Amsterdam"
Person.address.city.country set "Netherlands"
Person.address .coordinates set listOf(2, 3)
}

fun Person.moveToAmsterdamInside(): Person = copy {
inside(Person.address.city) {
City.name set "Amsterdam"
City.country set "Netherlands"
}
}

val me =
Person(
"Gopal",
99,
Address(Street("Kotlinstraat", 1), City("Hilversum", "Netherlands"), listOf(1, 2))
)
"""

class CopyTest {
@Test
fun `code compiles`() {
"""
|package PersonTest
|$imports
|$copyCode
""".compilationSucceeds()
}

@Test
fun `birthday increments`() {
"""
|package PersonTest
|$imports
|$copyCode
|val meAfterBirthdayParty = Person.age.modify(me) { it + 1 }
|val r = Person.age.get(meAfterBirthdayParty)
""".evals("r" to 100)
}

@Test
fun `moving to another city`() {
"""
|package PersonTest
|$imports
|$copyCode
|val newAddress =
| Address(Street("Kotlinplein", null), City("Amsterdam", "Netherlands"), listOf(1, 2))
|val meAfterMoving = Person.address.set(me, newAddress)
|val r = Person.address.get(meAfterMoving).street.name
""".evals("r" to "Kotlinplein")
}

@Test
fun `optics composition`() {
"""
|package PersonTest
|$imports
|$copyCode
|val personCity: Lens<Person, String> = Person.address compose Address.city compose City.name
|val meAtTheCapital = personCity.set(me, "Amsterdam")
|val r = meAtTheCapital.address.city.name
""".evals("r" to "Amsterdam")
}

@Test
fun `optics copy to modify multiple fields`() {
"""
|package PersonTest
|$imports
|$copyCode
|val meAfterMoving = me.moveToAmsterdamInside()
|val r = meAfterMoving.address.city.name
""".evals("r" to "Amsterdam")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package arrow.optics

import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe

// from https://kotlinlang.slack.com/archives/C5UPMM0A0/p1688822411819599
// and https://github.com/overfullstack/my-lab/blob/master/arrow/src/test/kotlin/ga/overfullstack/optics/OpticsLab.kt

data class Person(val name: String, val age: Int, val address: Address) {
companion object {
val name: Lens<Person, String> = Lens(
get = { it.name },
set = { p, x -> p.copy(name = x) }
)
val age: Lens<Person, Int> = Lens(
get = { it.age },
set = { p, x -> p.copy(age = x) }
)
val address: Lens<Person, Address> = Lens(
get = { it.address },
set = { p, x -> p.copy(address = x) }
)
}
}
data class Address(val street: Street, val city: City, val coordinates: List<Int>) {
companion object {
val city: Lens<Address, City> = Lens(
get = { it.city },
set = { a, x -> a.copy(city = x) }
)
val coordinates: Lens<Address, List<Int>> = Lens(
get = { it.coordinates },
set = { a, x -> a.copy(coordinates = x) }
)
}
}
data class Street(val name: String, val number: Int?)
data class City(val name: String, val country: String) {
companion object {
val name: Lens<City, String> = Lens(
get = { it.name },
set = { c, x -> c.copy(name = x) }
)
val country: Lens<City, String> = Lens(
get = { it.country },
set = { c, x -> c.copy(country = x) }
)
}
}

fun Person.moveToAmsterdamCopy(): Person = copy {
Person.address + Address.city + City.name set "Amsterdam"
Person.address + Address.city + City.country set "Netherlands"
Person.address + Address.coordinates set listOf(2, 3)
}

fun Person.moveToAmsterdamInside(): Person = copy {
inside(Person.address + Address.city) {
City.name set "Amsterdam"
City.country set "Netherlands"
}
}

class CopyTest : StringSpec({
"optics" {
val me =
Person(
"Gopal",
99,
Address(Street("Kotlinstraat", 1), City("Hilversum", "Netherlands"), listOf(1, 2))
)

Person.name.get(me) shouldBe "Gopal"

val meAfterBirthdayParty = Person.age.modify(me) { it + 1 }
Person.age.get(meAfterBirthdayParty) shouldBe 100

val newAddress =
Address(Street("Kotlinplein", null), City("Amsterdam", "Netherlands"), listOf(1, 2))
val meAfterMoving = Person.address.set(me, newAddress)
Person.address.get(meAfterMoving) shouldBe newAddress
}

"optics composition" {
val personCity: Lens<Person, String> = Person.address compose Address.city compose City.name

val me =
Person(
"Alejandro",
35,
Address(Street("Kotlinstraat", 1), City("Hilversum", "Netherlands"), listOf(1, 2))
)

personCity.get(me) shouldBe "Hilversum"
val meAtTheCapital = personCity.set(me, "Amsterdam")
meAtTheCapital.address.city.name shouldBe "Amsterdam"
}

"optics copy to modify multiple fields" {
val me =
Person(
"Alejandro",
35,
Address(Street("Kotlinstraat", 1), City("Hilversum", "Netherlands"), listOf(1, 2))
)
val meAfterMoving1 = me.moveToAmsterdamInside()
val meAfterMoving2 = me.moveToAmsterdamInside()
meAfterMoving1 shouldBe meAfterMoving2
meAfterMoving1.address.city.name shouldBe "Amsterdam"
}
})