diff --git a/kotest-property/src/commonMain/kotlin/io/kotest/property/arbitrary/array.kt b/kotest-property/src/commonMain/kotlin/io/kotest/property/arbitrary/array.kt
index f7d97ae735f..5a1b15d2253 100644
--- a/kotest-property/src/commonMain/kotlin/io/kotest/property/arbitrary/array.kt
+++ b/kotest-property/src/commonMain/kotlin/io/kotest/property/arbitrary/array.kt
@@ -12,3 +12,15 @@ internal inline fun
Arb.Companion.toPrimitiveArray(
is Arb -> generateArrayLength
is Exhaustive -> generateArrayLength.toArb()
}.flatMap { length -> Arb.list(generateContents, length..length) }.map { it.toArray() }
+
+inline fun Arb.Companion.array(
+ gen: Gen,
+ range: IntRange = 0..100,
+ crossinline toArray: Collection.() -> Array = { this.toTypedArray() }
+): Arb> {
+ check(!range.isEmpty())
+ check(range.first >= 0)
+ return arbitrary {
+ list(gen, range).bind().toArray()
+ }
+}
diff --git a/kotest-property/src/jvmMain/kotlin/io/kotest/property/arbitrary/targetDefaultForClassName.kt b/kotest-property/src/jvmMain/kotlin/io/kotest/property/arbitrary/targetDefaultForClassName.kt
index 718a946da6c..0b30a1a0dbe 100644
--- a/kotest-property/src/jvmMain/kotlin/io/kotest/property/arbitrary/targetDefaultForClassName.kt
+++ b/kotest-property/src/jvmMain/kotlin/io/kotest/property/arbitrary/targetDefaultForClassName.kt
@@ -1,5 +1,6 @@
package io.kotest.property.arbitrary
+import io.kotest.mpp.bestName
import io.kotest.property.Arb
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
@@ -17,6 +18,8 @@ import java.util.Date
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.full.isSubclassOf
+import kotlin.reflect.javaType
+import kotlin.reflect.jvm.jvmErasure
import kotlin.reflect.typeOf
@Suppress("UNCHECKED_CAST")
@@ -45,6 +48,17 @@ fun targetDefaultForType(providedArbs: Map, Arb<*>> = emptyMap(), type
val upperBound = type.arguments.first().type ?: error("No bound for List")
Arb.list(Arb.forType(providedArbs, upperBound) as Arb<*>)
}
+ clazz.java.isArray -> {
+ val upperBound = type.arguments.first().type ?: error("No bound for Array")
+ Arb.array(Arb.forType(providedArbs, upperBound) as Arb<*>) {
+ val upperBoundKClass = (upperBound.classifier as? KClass<*>) ?: error("No classifier for $upperBound")
+ val array = java.lang.reflect.Array.newInstance(upperBoundKClass.javaObjectType, this.size) as Array
+ for ((i, item) in this.withIndex()) {
+ java.lang.reflect.Array.set(array, i, item)
+ }
+ array
+ }
+ }
clazz.isSubclassOf(Set::class) -> {
val upperBound = type.arguments.first().type ?: error("No bound for Set")
val upperBoundKClass = (upperBound.classifier as? KClass<*>)
diff --git a/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ArrayTest.kt b/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ArrayTest.kt
index 09c0e187523..8462a273c94 100644
--- a/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ArrayTest.kt
+++ b/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ArrayTest.kt
@@ -1,24 +1,95 @@
package com.sksamuel.kotest.property.arbitrary
-import io.kotest.core.spec.style.FunSpec
+import io.kotest.core.spec.style.DescribeSpec
import io.kotest.inspectors.forAll
+import io.kotest.matchers.collections.shouldContain
+import io.kotest.matchers.collections.shouldHaveAtLeastSize
+import io.kotest.matchers.collections.shouldHaveAtMostSize
+import io.kotest.matchers.collections.shouldNotBeEmpty
import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual
import io.kotest.matchers.ints.shouldBeLessThanOrEqual
import io.kotest.matchers.shouldBe
import io.kotest.property.Arb
-import io.kotest.property.arbitrary.*
+import io.kotest.property.Exhaustive
+import io.kotest.property.PropTestConfig
+import io.kotest.property.arbitrary.array
+import io.kotest.property.arbitrary.byte
+import io.kotest.property.arbitrary.byteArray
+import io.kotest.property.arbitrary.constant
+import io.kotest.property.arbitrary.double
+import io.kotest.property.arbitrary.edgecases
+import io.kotest.property.arbitrary.int
+import io.kotest.property.arbitrary.positiveInt
+import io.kotest.property.arbitrary.take
+import io.kotest.property.checkAll
+import io.kotest.property.exhaustive.constant
+import io.kotest.property.forAll
-class ArrayTest : FunSpec({
- test("ByteArray should generate specified lengths") {
- Arb.byteArray(Arb.int(5, 15), Arb.byte()).take(1000).toList().forAll {
- it.size.shouldBeGreaterThanOrEqual(5)
- it.size.shouldBeLessThanOrEqual(15)
+class ArrayTest : DescribeSpec({
+ describe("ByteArray should") {
+ it("generate specified lengths") {
+ Arb.byteArray(Arb.int(5, 15), Arb.byte()).take(1000).toList().forAll {
+ it.size.shouldBeGreaterThanOrEqual(5)
+ it.size.shouldBeLessThanOrEqual(15)
+ }
+ }
+
+ it("populate random byte values") {
+ Arb.byteArray(Arb.constant(1000000), Arb.byte()).take(10).toList().forAll {
+ it.toSet().size shouldBe 256
+ }
}
}
- test("ByteArray should populate random byte values") {
- Arb.byteArray(Arb.constant(1000000), Arb.byte()).take(10).toList().forAll {
- it.toSet().size shouldBe 256
+ describe("Arb.array should") {
+ it("not include empty edge cases as first sample") {
+ val numGen = Arb.array(Arb.int(), 1..10)
+ forAll(1, numGen) { it.isNotEmpty() }
+ }
+
+ it("return arrays of underlying generators") {
+ val gen = Arb.array(Exhaustive.constant(1), 2..10)
+ checkAll(gen) {
+ it.shouldHaveAtLeastSize(2)
+ it.shouldHaveAtMostSize(10)
+ it.toSet() shouldBe setOf(1)
+ }
+ }
+
+ it("include repeated elements in edge cases") {
+ val edgeCase = Arb.positiveInt().edgecases().firstOrNull()
+ Arb.array(Arb.positiveInt()).edgecases() shouldContain listOf(edgeCase, edgeCase)
+ Arb.array(Arb.positiveInt(), 4..6).edgecases() shouldContain listOf(edgeCase, edgeCase, edgeCase, edgeCase)
+ }
+
+ it("include empty array in edge cases") {
+ Arb.array(Arb.positiveInt()).edgecases() shouldContain emptyArray()
+ }
+
+ it("respect bounds in edge cases") {
+ val edges = Arb.array(Arb.positiveInt(), 2..10).edgecases().toSet()
+ edges.forAll { it.shouldNotBeEmpty() }
+ }
+
+ it("generate arrays of length up to 100 by default") {
+ checkAll(10_000, Arb.array(Arb.double())) {
+ it.shouldHaveAtMostSize(100)
+ }
+
+ checkAll>(PropTestConfig(iterations = 10_000)) {
+ it.shouldHaveAtMostSize(100)
+ }
+
+ forAll>(PropTestConfig(iterations = 10_000)) {
+ it.size <= 100
+ }
+ }
+
+ it("generate arrays in the given range") {
+ checkAll(1000, Arb.array(Arb.double(), 250..500)) {
+ it.shouldHaveAtLeastSize(250)
+ it.shouldHaveAtMostSize(500)
+ }
}
}
})
diff --git a/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ReflectiveBindTest.kt b/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ReflectiveBindTest.kt
index 2bb0503bf2c..dea51c5a90c 100644
--- a/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ReflectiveBindTest.kt
+++ b/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/arbitrary/ReflectiveBindTest.kt
@@ -159,6 +159,12 @@ class ReflectiveBindTest : StringSpec(
val listArb = Arb.bind>()
listArb.next().shouldBeInstanceOf>()
+ val arrayArb = Arb.bind>()
+ arrayArb.next().shouldBeInstanceOf>()
+
+ val arrayArbWithClass = Arb.bind>()
+ arrayArbWithClass.next().shouldBeInstanceOf>()
+
val bigDecimalArb = Arb.bind()
bigDecimalArb.next().shouldBeInstanceOf()
}