diff --git a/android/src/main/java/dev/appoutlet/some/android/resolver/BundleResolver.kt b/android/src/main/java/dev/appoutlet/some/android/resolver/BundleResolver.kt new file mode 100644 index 00000000..6b049cc2 --- /dev/null +++ b/android/src/main/java/dev/appoutlet/some/android/resolver/BundleResolver.kt @@ -0,0 +1,42 @@ +package dev.appoutlet.some.android.resolver + +import android.os.Bundle +import dev.appoutlet.some.core.Resolver +import dev.appoutlet.some.core.ResolverChain +import kotlin.random.Random +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +private const val MIN_STRING_LENGTH = 1 +private const val MAX_STRING_LENGTH = 10 + +/** + * Resolves [Bundle] types by creating a new [Bundle] and populating it with + * one entry for each supported primitive value type. + * + * Generated bundles always contain one [String], [Int], [Long], [Float], + * [Double], and [Boolean] entry with random values. + * + * @param random Random source used when generating values. + */ +class BundleResolver( + private val random: Random +) : Resolver { + override fun canResolve(type: KType): Boolean = type == typeOf() + + override fun resolve(type: KType, chain: ResolverChain): Any { + return Bundle().apply { + putString("string", generateString()) + putInt("int", random.nextInt()) + putLong("long", random.nextLong()) + putFloat("float", random.nextFloat()) + putDouble("double", random.nextDouble()) + putBoolean("boolean", random.nextBoolean()) + } + } + + private fun generateString(): String { + val length = random.nextInt(MIN_STRING_LENGTH, MAX_STRING_LENGTH + 1) + return List(length) { ('a'..'z').random(random) }.joinToString("") + } +} diff --git a/android/src/test/java/dev/appoutlet/some/android/resolver/BundleResolverTest.kt b/android/src/test/java/dev/appoutlet/some/android/resolver/BundleResolverTest.kt new file mode 100644 index 00000000..77c6bb6f --- /dev/null +++ b/android/src/test/java/dev/appoutlet/some/android/resolver/BundleResolverTest.kt @@ -0,0 +1,65 @@ +package dev.appoutlet.some.android.resolver + +import android.os.Bundle +import dev.appoutlet.some.config.NullableStrategy +import dev.appoutlet.some.core.ResolverChain +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.random.Random +import kotlin.reflect.typeOf + +@RunWith(RobolectricTestRunner::class) +class BundleResolverTest { + @Test + fun `BundleResolver canResolve detects Bundle type`() { + val resolver = BundleResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `BundleResolver rejects non-Bundle types`() { + val resolver = BundleResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } + + @Test + fun `BundleResolver generates non-null Bundle`() { + val resolver = BundleResolver(Random.Default) + val result = resolver.resolve(typeOf(), testChain) + assertNotNull(result) + assertTrue(result is Bundle) + } + + @Test + fun `BundleResolver generates one entry for each supported type`() { + val resolver = BundleResolver(Random.Default) + + repeat(20) { + val result = resolver.resolve(typeOf(), testChain) as Bundle + + assertEquals(6, result.size()) + assertEquals(setOf("string", "int", "long", "float", "double", "boolean"), result.keySet()) + assertNotNull(result.getString("string")) + assertTrue(result.getString("string")!!.isNotEmpty()) + assertTrue(result.getValue("string") is String) + assertTrue(result.getValue("int") is Int) + assertTrue(result.getValue("long") is Long) + assertTrue(result.getValue("float") is Float) + assertTrue(result.getValue("double") is Double) + assertTrue(result.getValue("boolean") is Boolean) + } + } + + @Suppress("DEPRECATION") + private fun Bundle.getValue(key: String): Any? = get(key) + + companion object { + private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference) + } +} diff --git a/docs/android/index.md b/docs/android/index.md index ac5e4a41..1934bfe6 100644 --- a/docs/android/index.md +++ b/docs/android/index.md @@ -38,15 +38,16 @@ All shared types from [Supported Types](../supported-types.md) are available her Today, the Android module adds these Android-specific types: -| Type | Usage | Notes | -|------|-------|-------| -| `android.net.Uri` | `some()` | See [Uri](uri.md) and [Uri Strategy](uri.md#uri-strategy) | -| `android.graphics.Rect` | `some()` | Random integer bounds with `left < right` and `top < bottom` | -| `android.graphics.RectF` | `some()` | Random float bounds with `left < right` and `top < bottom` | -| `android.graphics.Point` | `some()` | Random integer `x` and `y` | +| Type | Usage | Notes | +|------|------------------|-------| +| `android.net.Uri` | `some()` | See [Uri](uri.md) and [Uri Strategy](uri.md#uri-strategy) | +| `android.graphics.Rect` | `some()` | Random integer bounds with `left < right` and `top < bottom` | +| `android.graphics.RectF` | `some()` | Random float bounds with `left < right` and `top < bottom` | +| `android.graphics.Point` | `some()` | Random integer `x` and `y` | | `android.graphics.PointF` | `some()` | Random float `x` and `y` | -| `android.util.Size` | `some()` | Random positive integer `width` and `height` | -| `android.util.SizeF` | `some()` | Random positive float `width` and `height` | +| `android.util.Size` | `some()` | Random positive integer `width` and `height` | +| `android.util.SizeF` | `some()` | Random positive float `width` and `height` | +| `android.os.Bundle` | `some()` | One random value each for `String`, `Int`, `Long`, `Float`, `Double`, and `Boolean` | ## Platform constraints