Skip to content
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
2 changes: 1 addition & 1 deletion android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ detekt { autoCorrect = true }
dependencies {
api(projects.core)

testImplementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui)
Comment thread
MessiasLima marked this conversation as resolved.
testImplementation(libs.junit)
testImplementation(libs.kotlin.test)
testImplementation(libs.robolectric)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dev.appoutlet.some.android.resolver.SizeFResolver
import dev.appoutlet.some.android.resolver.SizeResolver
import dev.appoutlet.some.android.resolver.UriResolver
import dev.appoutlet.some.android.resolver.UserHandleResolver
import dev.appoutlet.some.android.resolver.compose.AnnotatedStringResolver
import dev.appoutlet.some.core.Resolver
import dev.appoutlet.some.core.ResolverProvider
import dev.appoutlet.some.core.StrategyProvider
Expand All @@ -19,8 +20,7 @@ import kotlin.random.Random
/**
* Contributes Android-specific resolvers discovered through `ServiceLoader`.
*
* The returned resolvers extend the shared `some-core` chain with support for common Android framework
* value types such as `Uri`, geometry classes, `Bundle`, `Pair`, `UserHandle`, and `Color`.
* The returned resolvers extend the shared `some-core` chain with Android-specific type support.
*/
class AndroidResolverProvider : ResolverProvider {
/**
Expand All @@ -44,7 +44,8 @@ class AndroidResolverProvider : ResolverProvider {
SizeFResolver(random),
PairResolver(random),
UserHandleResolver(random),
ColorResolver(random)
ColorResolver(random),
AnnotatedStringResolver(random)
)
}
}
Comment thread
MessiasLima marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.appoutlet.some.android.resolver.compose

import androidx.compose.ui.text.AnnotatedString
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_TEXT_LENGTH = 1
private const val MAX_TEXT_LENGTH = 20

/**
* Resolves [AnnotatedString] types with randomized non-blank text content.
*
* @param random Random source used when generating text.
*/
class AnnotatedStringResolver(
private val random: Random
) : Resolver {
override fun canResolve(type: KType): Boolean = type == typeOf<AnnotatedString>()

override fun resolve(type: KType, chain: ResolverChain): Any {
return AnnotatedString(generateText())
}

private fun generateText(): String {
val length = random.nextInt(MIN_TEXT_LENGTH, MAX_TEXT_LENGTH + 1)
val chars = CharArray(length) { ('a'..'z').random(random) }
return String(chars)
}
Comment thread
MessiasLima marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package dev.appoutlet.some.android

import dev.appoutlet.some.android.resolver.BundleResolver
import dev.appoutlet.some.android.resolver.ColorResolver
import dev.appoutlet.some.android.resolver.PairResolver
import dev.appoutlet.some.android.resolver.PointFResolver
import dev.appoutlet.some.android.resolver.PointResolver
import dev.appoutlet.some.android.resolver.RectFResolver
import dev.appoutlet.some.android.resolver.RectResolver
import dev.appoutlet.some.android.resolver.SizeFResolver
import dev.appoutlet.some.android.resolver.SizeResolver
import dev.appoutlet.some.android.resolver.UriResolver
import dev.appoutlet.some.android.resolver.UserHandleResolver
import dev.appoutlet.some.android.resolver.compose.AnnotatedStringResolver
import dev.appoutlet.some.config.DefaultStrategyProvider
import org.junit.Assert.assertEquals
import org.junit.Test
import kotlin.random.Random

class AndroidResolverProviderTest {
@Test
fun `createResolvers returns the full Android resolver list`() {
val resolvers = AndroidResolverProvider().createResolvers(DefaultStrategyProvider(), Random.Default)

assertEquals(
listOf(
UriResolver::class,
BundleResolver::class,
RectResolver::class,
RectFResolver::class,
PointResolver::class,
PointFResolver::class,
SizeResolver::class,
SizeFResolver::class,
PairResolver::class,
UserHandleResolver::class,
ColorResolver::class,
AnnotatedStringResolver::class
),
resolvers.map { it::class }
)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.appoutlet.some.android

import android.graphics.Point
import androidx.compose.ui.text.AnnotatedString
import dev.appoutlet.some.some
import org.junit.Assert.assertTrue
import org.junit.Test
Expand All @@ -17,4 +18,10 @@ class AndroidServiceLoaderTest {
val result: Any = some<Point>()
assertTrue(result is Point)
}

@Test
fun `top-level some resolves AnnotatedString through AndroidResolverProvider`() {
val result: AnnotatedString = some()
assertTrue("Generated AnnotatedString text should not be blank", result.text.isNotBlank())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.appoutlet.some.android.resolver.compose

import androidx.compose.ui.text.AnnotatedString
import dev.appoutlet.some.config.NullableStrategy
import dev.appoutlet.some.core.ResolverChain
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import kotlin.random.Random
import kotlin.reflect.typeOf

class AnnotatedStringResolverTest {
@Test
fun `AnnotatedStringResolver generates AnnotatedString values`() {
val resolver = AnnotatedStringResolver(Random.Default)
val result = resolver.resolve(typeOf<AnnotatedString>(), testChain)
assertTrue(result is AnnotatedString)
}

@Test
fun `AnnotatedStringResolver generates non-blank text`() {
val resolver = AnnotatedStringResolver(Random.Default)

repeat(20) {
val result = resolver.resolve(typeOf<AnnotatedString>(), testChain) as AnnotatedString
assertTrue("Generated AnnotatedString text should not be blank", result.text.isNotBlank())
}
}

@Test
fun `AnnotatedStringResolver canResolve detects AnnotatedString type`() {
val resolver = AnnotatedStringResolver(Random.Default)
assertTrue(resolver.canResolve(typeOf<AnnotatedString>()))
}

@Test
fun `AnnotatedStringResolver rejects non-AnnotatedString types`() {
val resolver = AnnotatedStringResolver(Random.Default)
assertFalse(resolver.canResolve(typeOf<String>()))
assertFalse(resolver.canResolve(typeOf<Int>()))
}

companion object {
private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference)
}
}
1 change: 1 addition & 0 deletions docs/android/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Today, the Android module adds these Android-specific types:
| `android.os.Bundle` | `some<Bundle>()` | One random value each for `String`, `Int`, `Long`, `Float`, `Double`, and `Boolean` |
| `android.os.UserHandle` | `some<UserHandle>()` | API 24+; generated from a random non-system UID |
| `android.graphics.Color` | `some<Color>()` | API 26+; generated from random RGB channel values |
| `androidx.compose.ui.text.AnnotatedString` | `some<AnnotatedString>()` | Non-blank plain text without span styles |

## Platform constraints

Expand Down