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

arrow-core: speedup 'Arb.string()' related slow tests #3005

Merged
merged 9 commits into from
Mar 30, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import arrow.core.test.any
import arrow.core.test.either
import arrow.core.test.intSmall
import arrow.core.test.laws.MonoidLaws
import arrow.core.test.nonEmptyList
import arrow.core.test.suspendFunThatReturnsAnyLeft
import arrow.core.test.suspendFunThatReturnsAnyRight
import arrow.core.test.suspendFunThatReturnsEitherAnyOrAnyOrThrows
Expand All @@ -25,7 +26,6 @@ import io.kotest.property.arbitrary.float
import io.kotest.property.arbitrary.int
import io.kotest.property.arbitrary.list
import io.kotest.property.arbitrary.long
import io.kotest.property.arbitrary.map
import io.kotest.property.arbitrary.string
import io.kotest.property.arbitrary.nonPositiveInt
import io.kotest.property.arbitrary.short
Expand Down Expand Up @@ -666,19 +666,16 @@ class EitherTest : StringSpec({
}

"zipOrAccumulate EitherNel results in all Right transformed, or all Left in a NonEmptyList" {
fun <A> Arb.Companion.nonEmptyList(arb: Arb<A>): Arb<NonEmptyList<A>> =
Arb.list(arb, 1..100).map { it.toNonEmptyListOrNull()!! }

checkAll(
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.short()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.byte()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.int()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.long()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.float()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.double()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.char()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.string()),
Arb.either(Arb.nonEmptyList(Arb.string()), Arb.boolean())
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.short()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.byte()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.int()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.long()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.float()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.double()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.char()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.string()),
Arb.either(Arb.nonEmptyList(Arb.int()), Arb.boolean())
) { a, b, c, d, e, f, g, h, i ->
val res = Either.zipOrAccumulate(a, b, c, d, e, f, g, h, i, ::Tuple9)
val all = listOf(a, b, c, d, e, f, g, h, i)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ class IterableTest : StringSpec({
}

"flattenOrAccumulate" {
checkAll(Arb.list(Arb.either(Arb.string(), Arb.int()))) { list ->
checkAll(Arb.list(Arb.either(Arb.int(), Arb.int()))) { list ->
val expected =
if (list.any { it.isLeft() }) list.filterIsInstance<Either.Left<String>>()
if (list.any { it.isLeft() }) list.filterIsInstance<Either.Left<Int>>()
.map { it.value }.toNonEmptyListOrNull().shouldNotBeNull().left()
else list.filterIsInstance<Either.Right<Int>>().map { it.value }.right()

Expand Down Expand Up @@ -266,13 +266,13 @@ class IterableTest : StringSpec({
"sequence Either traverse Nullable interoperate - and proof map + sequence equality with traverse" {
checkAll(
Arb.list(Arb.int()),
Arb.functionAToB<Int, Either<String, Int>?>(Arb.either(Arb.string(), Arb.int()).orNull())
Arb.functionAToB<Int, Either<Int, Int>?>(Arb.either(Arb.int(), Arb.int()).orNull())
) { ints, f ->

val res: Either<String, List<Int>>? =
val res: Either<Int, List<Int>>? =
ints.traverse(f)?.sequence()

val expected: Either<String, List<Int>>? =
val expected: Either<Int, List<Int>>? =
ints.map(f).sequence()?.sequence()

res shouldBe expected
Expand Down Expand Up @@ -565,26 +565,26 @@ class IterableTest : StringSpec({
}

"unzip(fn)" {
checkAll(Arb.list(Arb.pair(Arb.int(), Arb.string()))) { xs ->
checkAll(Arb.list(Arb.pair(Arb.int(), Arb.int()))) { xs ->
xs.unzip { it } shouldBe xs.unzip()
}
}

"unalign is the inverse of align" {
checkAll(Arb.list(Arb.int()), Arb.list(Arb.string())) { a, b ->
checkAll(Arb.list(Arb.int()), Arb.list(Arb.int())) { a, b ->
a.align(b).unalign() shouldBe (a to b)
}
}

"align is the inverse of unalign" {
checkAll(Arb.list(Arb.ior(Arb.int(), Arb.string()))) { xs ->
checkAll(Arb.list(Arb.ior(Arb.int(), Arb.int()))) { xs ->
val (a, b) = xs.unalign()
a.align(b) shouldBe xs
}
}

"unalign(fn)" {
checkAll(Arb.list(Arb.ior(Arb.int(), Arb.string()))) { xs ->
checkAll(Arb.list(Arb.ior(Arb.int(), Arb.int()))) { xs ->
xs.unalign { it } shouldBe xs.unalign()
}
}
Expand All @@ -596,7 +596,7 @@ class IterableTest : StringSpec({
}

"reduceOrNull is compatible with reduce from stdlib" {
checkAll(Arb.list(Arb.string())) { xs ->
checkAll(Arb.list(Arb.int())) { xs ->

val rs = xs.reduceOrNull({ it }) { a, b ->
a + b
Expand All @@ -606,14 +606,14 @@ class IterableTest : StringSpec({
rs.shouldBeNull()
} else {
rs shouldBe xs.reduce {
a,b -> a +b
a,b -> a + b
}
}
}
}

"reduceRightNull is compatible with reduce from stdlib" {
checkAll(Arb.list(Arb.string())) { xs ->
checkAll(Arb.list(Arb.int())) { xs ->

val rs = xs.reduceRightNull({ it }) { a, b ->
a + b
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ class MapKTest : StringSpec({
"zip with" {
checkAll(
Arb.map2(Arb.int(), Arb.int(), Arb.int()),
Arb.functionABCToD<Int, Int, Int, String>(Arb.string())
Arb.functionABCToD<Int, Int, Int, Int>(Arb.int())
) { (a, b), fn ->
a.zip(b, fn) shouldBe a.zip(b).mapValues { fn(it.key, it.value.first, it.value.second) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ class NonEmptyListTest : StringSpec({
}

"unzip with split function" {
checkAll(Arb.nonEmptyList(Arb.pair(Arb.int(), Arb.string()))) { nel ->
checkAll(Arb.nonEmptyList(Arb.pair(Arb.int(), Arb.int()))) { nel ->
val unzipped = nel.unzip(::identity)

unzipped.first shouldBe nel.map { it.first }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import arrow.core.test.laws.MonoidLaws
import arrow.core.test.option
import arrow.core.test.sequence
import arrow.core.test.testLaws
import arrow.core.test.unit
import arrow.typeclasses.Semigroup
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.sequences.shouldBeEmpty
Expand Down Expand Up @@ -239,40 +240,40 @@ class SequenceKTest : StringSpec({
}

"crosswalk the sequence to a List function" {
checkAll(Arb.list(Arb.string())){ strList ->
val obtained = strList.asSequence().crosswalk { listOf(it.length) }
val expected = if (strList.isEmpty()) emptyList()
else listOf(strList.map { it.length })
checkAll(Arb.list(Arb.int())){ list ->
val obtained = list.asSequence().crosswalk { listOf(it) }
val expected = if (list.isEmpty()) emptyList()
else listOf(list.map { it })
obtained.map{ it.sorted() } shouldBe expected.map { it.sorted() }
}
}

"crosswalk the sequence to a nullable function" {
checkAll(Arb.list(Arb.string())){ strList ->
checkAll(Arb.list(Arb.int())){ list ->
fun nullEvens(i: Int): Int? = if(i % 2 == 0) i else null

val obtained = strList.asSequence().crosswalkNullList { nullEvens(it.length) }
val expected = strList.map { nullEvens(it.length) }
val obtained = list.asSequence().crosswalkNullList { nullEvens(it) }
val expected = list.map { nullEvens(it) }
obtained?.size shouldBe expected.filterNotNull().size
}
}

"can align sequences - 1" {
checkAll(Arb.sequence(Arb.int()), Arb.sequence(Arb.string())) { a, b ->
checkAll(Arb.sequence(Arb.unit()), Arb.sequence(Arb.unit())) { a, b ->
a.align(b).toList().size shouldBe max(a.toList().size, b.toList().size)
}
}

"can align sequences - 2" {
checkAll(Arb.sequence(Arb.int()), Arb.sequence(Arb.string())) { a, b ->
checkAll(Arb.sequence(Arb.unit()), Arb.sequence(Arb.unit())) { a, b ->
a.align(b).take(min(a.toList().size, b.toList().size)).forEach {
it.isBoth() shouldBe true
}
}
}

"can align sequences - 3" {
checkAll(Arb.sequence(Arb.int()), Arb.sequence(Arb.string())) { a, b ->
checkAll(Arb.sequence(Arb.unit()), Arb.sequence(Arb.unit())) { a, b ->
val ls = a.toList()
val rs = b.toList()
a.align(b).drop(min(ls.size, rs.size)).forEach {
Expand Down Expand Up @@ -322,17 +323,17 @@ class SequenceKTest : StringSpec({
}

"unzipToPair should unzip values in a Pair in a Sequence of Pairs" {
checkAll(Arb.list(Arb.pair(Arb.string(), Arb.int()))){ pairList ->
checkAll(Arb.list(Arb.pair(Arb.int(), Arb.int()))){ pairList ->
val obtained = pairList.asSequence().unzipToPair()
val expected = pairList.unzip()
obtained shouldBe expected
}
}

"unzipToPair should unzip values in a Pair in a Sequence" {
checkAll(Arb.list(Arb.string())){ strList ->
val obtained = strList.asSequence().unzipToPair { str -> Pair(str, str.length)}
val expected = strList.unzip { str -> Pair(str, str.length) }
checkAll(Arb.list(Arb.int())){ list ->
val obtained = list.asSequence().unzipToPair { n -> Pair(n, n)}
val expected = list.unzip { n -> Pair(n, n) }
obtained shouldBe expected
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ package arrow.core.continuations

import arrow.core.Either
import arrow.core.Ior
import arrow.core.test.nonEmptyList
import arrow.typeclasses.Semigroup
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.arbitrary.filter
import io.kotest.property.arbitrary.int
import io.kotest.property.arbitrary.list
import io.kotest.property.arbitrary.string
import io.kotest.property.checkAll
Expand All @@ -32,11 +34,11 @@ class IorSpec :
}

"Concurrent - arrow.ior bind" {
checkAll(Arb.list(Arb.string()).filter(List<String>::isNotEmpty)) { strs ->
checkAll(Arb.nonEmptyList(Arb.int())) { xs ->
ior(Semigroup.list()) {
strs.mapIndexed { index, s -> async { Ior.Both(listOf(s), index).bind() } }.awaitAll()
xs.mapIndexed { index, s -> async { Ior.Both(listOf(s), index).bind() } }.awaitAll()
}
.mapLeft { it.toSet() } shouldBe Ior.Both(strs.toSet(), strs.indices.toList())
.mapLeft { it.toSet() } shouldBe Ior.Both(xs.toSet(), xs.indices.toList())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,8 +608,8 @@ class EffectSpec : StringSpec({
}

"accumulate, returns no error" {
checkAll(Arb.list(Arb.string())) { elements ->
either<NonEmptyList<Int>, List<String>> {
checkAll(Arb.list(Arb.int())) { elements ->
either<NonEmptyList<Int>, List<Int>> {
mapOrAccumulate(elements) { it }
} shouldBe elements.right()
}
Expand All @@ -624,8 +624,8 @@ class EffectSpec : StringSpec({
}

"NonEmptyList - mapOrAccumulate, returns no error" {
checkAll(Arb.nonEmptyList(Arb.string())) { elements ->
either<NonEmptyList<Int>, NonEmptyList<String>> {
checkAll(Arb.nonEmptyList(Arb.int())) { elements ->
either<NonEmptyList<Int>, NonEmptyList<Int>> {
mapOrAccumulate(elements) { it }
} shouldBe elements.right()
}
Expand All @@ -640,15 +640,15 @@ class EffectSpec : StringSpec({
}

"NonEmptySet - mapOrAccumulate, returns no error" {
checkAll(Arb.nonEmptySet(Arb.string())) { elements ->
either<NonEmptyList<Int>, NonEmptySet<String>> {
checkAll(Arb.nonEmptySet(Arb.int())) { elements ->
either<NonEmptyList<Int>, NonEmptySet<Int>> {
mapOrAccumulate(elements) { it }
} shouldBe elements.right()
}
}

"bindAll fails on first error" {
checkAll(Arb.list(Arb.either(Arb.string(), Arb.int()))) { eithers ->
checkAll(Arb.list(Arb.either(Arb.int(), Arb.int()))) { eithers ->
val expected = eithers.firstOrNull { it.isLeft() } ?: eithers.mapNotNull { it.getOrNull() }.right()
either {
eithers.bindAll()
Expand All @@ -659,11 +659,11 @@ class EffectSpec : StringSpec({
fun <E, A> Either<E, A>.leftOrNull(): E? = fold(::identity) { null }

"accumulate - bindAll" {
checkAll(Arb.list(Arb.either(Arb.string(), Arb.int()))) { eithers ->
checkAll(Arb.list(Arb.either(Arb.int(), Arb.int()))) { eithers ->
val expected =
eithers.mapNotNull { it.leftOrNull() }.toNonEmptyListOrNull()?.left() ?: eithers.mapNotNull { it.getOrNull() }.right()

either<NonEmptyList<String>, List<Int>> {
either<NonEmptyList<Int>, List<Int>> {
zipOrAccumulate(
{ eithers.bindAll() },
{ emptyList<Int>() }
Expand All @@ -673,7 +673,7 @@ class EffectSpec : StringSpec({
}

"NonEmptyList - bindAll fails on first error" {
checkAll(Arb.nonEmptyList(Arb.either(Arb.string(), Arb.int()))) { eithers ->
checkAll(Arb.nonEmptyList(Arb.either(Arb.int(), Arb.int()))) { eithers ->
val expected = eithers.firstOrNull { it.isLeft() } ?: eithers.mapNotNull { it.getOrNull() }.right()
either {
eithers.bindAll()
Expand All @@ -682,11 +682,11 @@ class EffectSpec : StringSpec({
}

"NonEmptyList - bindAll accumulate errors" {
checkAll(Arb.nonEmptyList(Arb.either(Arb.string(), Arb.int()))) { eithers ->
checkAll(Arb.nonEmptyList(Arb.either(Arb.int(), Arb.int()))) { eithers ->
val expected =
eithers.mapNotNull { it.leftOrNull() }.toNonEmptyListOrNull()?.left() ?: eithers.mapNotNull { it.getOrNull() }.right()

either<NonEmptyList<String>, NonEmptyList<Int>> {
either<NonEmptyList<Int>, NonEmptyList<Int>> {
zipOrAccumulate(
{ eithers.bindAll() },
{ emptyList<Int>() }
Expand All @@ -696,7 +696,7 @@ class EffectSpec : StringSpec({
}

"NonEmptySet - bindAll fails on first error" {
checkAll(Arb.nonEmptySet(Arb.either(Arb.string(), Arb.int()))) { eithers ->
checkAll(Arb.nonEmptySet(Arb.either(Arb.int(), Arb.int()))) { eithers ->
val expected = eithers.firstOrNull { it.isLeft() } ?: eithers.mapNotNull { it.getOrNull() }.toSet().right()
either {
eithers.bindAll()
Expand All @@ -705,11 +705,11 @@ class EffectSpec : StringSpec({
}

"NonEmptySet - bindAll accumulate errors" {
checkAll(Arb.nonEmptySet(Arb.either(Arb.string(), Arb.int()))) { eithers ->
checkAll(Arb.nonEmptySet(Arb.either(Arb.int(), Arb.int()))) { eithers ->
val expected =
eithers.mapNotNull { it.leftOrNull() }.toNonEmptyListOrNull()?.left() ?: eithers.mapNotNull { it.getOrNull() }.toSet().right()

either<NonEmptyList<String>, NonEmptySet<Int>> {
either<NonEmptyList<Int>, NonEmptySet<Int>> {
zipOrAccumulate(
{ eithers.bindAll() },
{ emptySet<Int>() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package arrow.core.raise

import arrow.core.Either
import arrow.core.Ior
import arrow.core.test.nonEmptyList
import arrow.typeclasses.Semigroup
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
import io.kotest.property.arbitrary.filter
import io.kotest.property.arbitrary.int
import io.kotest.property.arbitrary.list
import io.kotest.property.arbitrary.string
import io.kotest.property.checkAll
Expand Down Expand Up @@ -45,11 +47,11 @@ class IorSpec : StringSpec({
}

"Concurrent - arrow.ior bind" {
checkAll(Arb.list(Arb.string()).filter(List<String>::isNotEmpty)) { strs ->
ior(List<String>::plus) {
strs.mapIndexed { index, s -> async { Ior.Both(listOf(s), index).bind() } }.awaitAll()
checkAll(Arb.nonEmptyList(Arb.int())) { xs ->
ior(List<Int>::plus) {
xs.mapIndexed { index, s -> async { Ior.Both(listOf(s), index).bind() } }.awaitAll()
}
.mapLeft { it.toSet() } shouldBe Ior.Both(strs.toSet(), strs.indices.toList())
.mapLeft { it.toSet() } shouldBe Ior.Both(xs.toSet(), xs.indices.toList())
}
}

Expand Down