diff --git a/android/src/main/java/dev/appoutlet/some/android/resolver/PointFResolver.kt b/android/src/main/java/dev/appoutlet/some/android/resolver/PointFResolver.kt new file mode 100644 index 00000000..bde01b7f --- /dev/null +++ b/android/src/main/java/dev/appoutlet/some/android/resolver/PointFResolver.kt @@ -0,0 +1,27 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.PointF +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 COORDINATE_BOUND = 1_000_000f + +/** + * Resolves [PointF] types with randomized float coordinates. + * + * @param random Random source used when generating coordinates. + */ +class PointFResolver( + private val random: Random +) : Resolver { + override fun canResolve(type: KType): Boolean = type == typeOf() + + override fun resolve(type: KType, chain: ResolverChain): Any { + val x = random.nextFloat() * (COORDINATE_BOUND * 2) - COORDINATE_BOUND + val y = random.nextFloat() * (COORDINATE_BOUND * 2) - COORDINATE_BOUND + return PointF(x, y) + } +} diff --git a/android/src/main/java/dev/appoutlet/some/android/resolver/PointResolver.kt b/android/src/main/java/dev/appoutlet/some/android/resolver/PointResolver.kt new file mode 100644 index 00000000..14ccfe1d --- /dev/null +++ b/android/src/main/java/dev/appoutlet/some/android/resolver/PointResolver.kt @@ -0,0 +1,23 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Point +import dev.appoutlet.some.core.Resolver +import dev.appoutlet.some.core.ResolverChain +import kotlin.random.Random +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +/** + * Resolves [Point] types with randomized integer coordinates. + * + * @param random Random source used when generating coordinates. + */ +class PointResolver( + private val random: Random +) : Resolver { + override fun canResolve(type: KType): Boolean = type == typeOf() + + override fun resolve(type: KType, chain: ResolverChain): Any { + return Point(random.nextInt(), random.nextInt()) + } +} diff --git a/android/src/main/java/dev/appoutlet/some/android/resolver/RectFResolver.kt b/android/src/main/java/dev/appoutlet/some/android/resolver/RectFResolver.kt new file mode 100644 index 00000000..591d7e37 --- /dev/null +++ b/android/src/main/java/dev/appoutlet/some/android/resolver/RectFResolver.kt @@ -0,0 +1,31 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.RectF +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 COORDINATE_BOUND = 1_000_000f + +/** + * Resolves [RectF] types with randomized float bounds. + * + * Generated rectangles always satisfy `left < right` and `top < bottom`. + * + * @param random Random source used when generating coordinates. + */ +class RectFResolver( + private val random: Random +) : Resolver { + override fun canResolve(type: KType): Boolean = type == typeOf() + + override fun resolve(type: KType, chain: ResolverChain): Any { + val left = random.nextFloat() * (COORDINATE_BOUND * 2) - COORDINATE_BOUND + val top = random.nextFloat() * (COORDINATE_BOUND * 2) - COORDINATE_BOUND + val right = left + random.nextFloat() * (COORDINATE_BOUND - 1) + 1 + val bottom = top + random.nextFloat() * (COORDINATE_BOUND - 1) + 1 + return RectF(left, top, right, bottom) + } +} diff --git a/android/src/main/java/dev/appoutlet/some/android/resolver/RectResolver.kt b/android/src/main/java/dev/appoutlet/some/android/resolver/RectResolver.kt new file mode 100644 index 00000000..19242cc8 --- /dev/null +++ b/android/src/main/java/dev/appoutlet/some/android/resolver/RectResolver.kt @@ -0,0 +1,31 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Rect +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 COORDINATE_BOUND = 1_000_000 + +/** + * Resolves [Rect] types with randomized integer bounds. + * + * Generated rectangles always satisfy `left < right` and `top < bottom`. + * + * @param random Random source used when generating coordinates. + */ +class RectResolver( + private val random: Random +) : Resolver { + override fun canResolve(type: KType): Boolean = type == typeOf() + + override fun resolve(type: KType, chain: ResolverChain): Any { + val left = random.nextInt(-COORDINATE_BOUND, COORDINATE_BOUND) + val top = random.nextInt(-COORDINATE_BOUND, COORDINATE_BOUND) + val right = left + random.nextInt(1, COORDINATE_BOUND) + val bottom = top + random.nextInt(1, COORDINATE_BOUND) + return Rect(left, top, right, bottom) + } +} diff --git a/android/src/main/java/dev/appoutlet/some/android/resolver/SizeFResolver.kt b/android/src/main/java/dev/appoutlet/some/android/resolver/SizeFResolver.kt new file mode 100644 index 00000000..e7b8cf73 --- /dev/null +++ b/android/src/main/java/dev/appoutlet/some/android/resolver/SizeFResolver.kt @@ -0,0 +1,27 @@ +package dev.appoutlet.some.android.resolver + +import android.util.SizeF +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 DIMENSION_BOUND = 1_000_000f + +/** + * Resolves [SizeF] types with randomized positive float dimensions. + * + * @param random Random source used when generating dimensions. + */ +class SizeFResolver( + private val random: Random +) : Resolver { + override fun canResolve(type: KType): Boolean = type == typeOf() + + override fun resolve(type: KType, chain: ResolverChain): Any { + val width = random.nextFloat() * (DIMENSION_BOUND - 1) + 1 + val height = random.nextFloat() * (DIMENSION_BOUND - 1) + 1 + return SizeF(width, height) + } +} diff --git a/android/src/main/java/dev/appoutlet/some/android/resolver/SizeResolver.kt b/android/src/main/java/dev/appoutlet/some/android/resolver/SizeResolver.kt new file mode 100644 index 00000000..ece307e4 --- /dev/null +++ b/android/src/main/java/dev/appoutlet/some/android/resolver/SizeResolver.kt @@ -0,0 +1,23 @@ +package dev.appoutlet.some.android.resolver + +import android.util.Size +import dev.appoutlet.some.core.Resolver +import dev.appoutlet.some.core.ResolverChain +import kotlin.random.Random +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +/** + * Resolves [Size] types with randomized positive integer dimensions. + * + * @param random Random source used when generating dimensions. + */ +class SizeResolver( + private val random: Random +) : Resolver { + override fun canResolve(type: KType): Boolean = type == typeOf() + + override fun resolve(type: KType, chain: ResolverChain): Any { + return Size(random.nextInt(1, Int.MAX_VALUE), random.nextInt(1, Int.MAX_VALUE)) + } +} diff --git a/android/src/test/java/dev/appoutlet/some/android/resolver/PointFResolverTest.kt b/android/src/test/java/dev/appoutlet/some/android/resolver/PointFResolverTest.kt new file mode 100644 index 00000000..7ec4c3d1 --- /dev/null +++ b/android/src/test/java/dev/appoutlet/some/android/resolver/PointFResolverTest.kt @@ -0,0 +1,49 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Point +import android.graphics.PointF +import android.graphics.Rect +import android.graphics.RectF +import android.util.Size +import android.util.SizeF +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 org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.random.Random +import kotlin.reflect.typeOf + +@RunWith(RobolectricTestRunner::class) +class PointFResolverTest { + @Test + fun `PointFResolver generates PointF values`() { + val resolver = PointFResolver(Random.Default) + val result = resolver.resolve(typeOf(), testChain) + assertTrue(result is PointF) + } + + @Test + fun `PointFResolver canResolve detects PointF type`() { + val resolver = PointFResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `PointFResolver rejects non-PointF types`() { + val resolver = PointFResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } + + companion object { + private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference) + } +} diff --git a/android/src/test/java/dev/appoutlet/some/android/resolver/PointResolverTest.kt b/android/src/test/java/dev/appoutlet/some/android/resolver/PointResolverTest.kt new file mode 100644 index 00000000..56a2a453 --- /dev/null +++ b/android/src/test/java/dev/appoutlet/some/android/resolver/PointResolverTest.kt @@ -0,0 +1,49 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Point +import android.graphics.PointF +import android.graphics.Rect +import android.graphics.RectF +import android.util.Size +import android.util.SizeF +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 org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.random.Random +import kotlin.reflect.typeOf + +@RunWith(RobolectricTestRunner::class) +class PointResolverTest { + @Test + fun `PointResolver generates Point values`() { + val resolver = PointResolver(Random.Default) + val result = resolver.resolve(typeOf(), testChain) + assertTrue(result is Point) + } + + @Test + fun `PointResolver canResolve detects Point type`() { + val resolver = PointResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `PointResolver rejects non-Point types`() { + val resolver = PointResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } + + companion object { + private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference) + } +} diff --git a/android/src/test/java/dev/appoutlet/some/android/resolver/RectFResolverTest.kt b/android/src/test/java/dev/appoutlet/some/android/resolver/RectFResolverTest.kt new file mode 100644 index 00000000..c1ad226b --- /dev/null +++ b/android/src/test/java/dev/appoutlet/some/android/resolver/RectFResolverTest.kt @@ -0,0 +1,59 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Point +import android.graphics.PointF +import android.graphics.Rect +import android.graphics.RectF +import android.util.Size +import android.util.SizeF +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 org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.random.Random +import kotlin.reflect.typeOf + +@RunWith(RobolectricTestRunner::class) +class RectFResolverTest { + @Test + fun `RectFResolver generates RectF values`() { + val resolver = RectFResolver(Random.Default) + val result = resolver.resolve(typeOf(), testChain) + assertTrue(result is RectF) + } + + @Test + fun `RectFResolver canResolve detects RectF type`() { + val resolver = RectFResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `RectFResolver rejects non-RectF types`() { + val resolver = RectFResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } + + @Test + fun `RectFResolver generates rects with valid bounds`() { + val resolver = RectFResolver(Random.Default) + repeat(20) { + val result = resolver.resolve(typeOf(), testChain) as RectF + assertTrue("left should be less than right", result.left < result.right) + assertTrue("top should be less than bottom", result.top < result.bottom) + } + } + + companion object { + private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference) + } +} diff --git a/android/src/test/java/dev/appoutlet/some/android/resolver/RectResolverTest.kt b/android/src/test/java/dev/appoutlet/some/android/resolver/RectResolverTest.kt new file mode 100644 index 00000000..e80a8222 --- /dev/null +++ b/android/src/test/java/dev/appoutlet/some/android/resolver/RectResolverTest.kt @@ -0,0 +1,59 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Point +import android.graphics.PointF +import android.graphics.Rect +import android.graphics.RectF +import android.util.Size +import android.util.SizeF +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 org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.random.Random +import kotlin.reflect.typeOf + +@RunWith(RobolectricTestRunner::class) +class RectResolverTest { + @Test + fun `RectResolver generates Rect values`() { + val resolver = RectResolver(Random.Default) + val result = resolver.resolve(typeOf(), testChain) + assertTrue(result is Rect) + } + + @Test + fun `RectResolver canResolve detects Rect type`() { + val resolver = RectResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `RectResolver rejects non-Rect types`() { + val resolver = RectResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } + + @Test + fun `RectResolver generates rects with valid bounds`() { + val resolver = RectResolver(Random.Default) + repeat(20) { + val result = resolver.resolve(typeOf(), testChain) as Rect + assertTrue("left should be less than right", result.left < result.right) + assertTrue("top should be less than bottom", result.top < result.bottom) + } + } + + companion object { + private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference) + } +} diff --git a/android/src/test/java/dev/appoutlet/some/android/resolver/SizeFResolverTest.kt b/android/src/test/java/dev/appoutlet/some/android/resolver/SizeFResolverTest.kt new file mode 100644 index 00000000..4588b7cb --- /dev/null +++ b/android/src/test/java/dev/appoutlet/some/android/resolver/SizeFResolverTest.kt @@ -0,0 +1,59 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Point +import android.graphics.PointF +import android.graphics.Rect +import android.graphics.RectF +import android.util.Size +import android.util.SizeF +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 org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.random.Random +import kotlin.reflect.typeOf + +@RunWith(RobolectricTestRunner::class) +class SizeFResolverTest { + @Test + fun `SizeFResolver generates SizeF values`() { + val resolver = SizeFResolver(Random.Default) + val result = resolver.resolve(typeOf(), testChain) + assertTrue(result is SizeF) + } + + @Test + fun `SizeFResolver canResolve detects SizeF type`() { + val resolver = SizeFResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `SizeFResolver rejects non-SizeF types`() { + val resolver = SizeFResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } + + @Test + fun `SizeFResolver generates sizes with positive dimensions`() { + val resolver = SizeFResolver(Random.Default) + repeat(20) { + val result = resolver.resolve(typeOf(), testChain) as SizeF + assertTrue("width should be positive", result.width > 0) + assertTrue("height should be positive", result.height > 0) + } + } + + companion object { + private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference) + } +} diff --git a/android/src/test/java/dev/appoutlet/some/android/resolver/SizeResolverTest.kt b/android/src/test/java/dev/appoutlet/some/android/resolver/SizeResolverTest.kt new file mode 100644 index 00000000..9ece0e0c --- /dev/null +++ b/android/src/test/java/dev/appoutlet/some/android/resolver/SizeResolverTest.kt @@ -0,0 +1,59 @@ +package dev.appoutlet.some.android.resolver + +import android.graphics.Point +import android.graphics.PointF +import android.graphics.Rect +import android.graphics.RectF +import android.util.Size +import android.util.SizeF +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 org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import kotlin.random.Random +import kotlin.reflect.typeOf + +@RunWith(RobolectricTestRunner::class) +class SizeResolverTest { + @Test + fun `SizeResolver generates Size values`() { + val resolver = SizeResolver(Random.Default) + val result = resolver.resolve(typeOf(), testChain) + assertTrue(result is Size) + } + + @Test + fun `SizeResolver canResolve detects Size type`() { + val resolver = SizeResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `SizeResolver rejects non-Size types`() { + val resolver = SizeResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } + + @Test + fun `SizeResolver generates sizes with positive dimensions`() { + val resolver = SizeResolver(Random.Default) + repeat(20) { + val result = resolver.resolve(typeOf(), testChain) as Size + assertTrue("width should be positive", result.width > 0) + assertTrue("height should be positive", result.height > 0) + } + } + + companion object { + private val testChain = ResolverChain(emptyList(), NullableStrategy.NullOnCircularReference) + } +} diff --git a/docs/android/index.md b/docs/android/index.md index bfab06bf..ac5e4a41 100644 --- a/docs/android/index.md +++ b/docs/android/index.md @@ -29,18 +29,24 @@ Use `dev.appoutlet:some-android` in Android projects when you want the shared So ## When to use it - Android unit tests and Robolectric tests -- Android libraries and apps that need generated `android.net.Uri` values +- Android libraries and apps that need generated `Uri`, `Rect`, `Point`, `Size`, or similar Android framework values - Projects that want one dependency for both the shared Some API and Android-specific support ## Supported Android types All shared types from [Supported Types](../supported-types.md) are available here too. -Today, the Android module adds one Android-specific type: +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` | +| `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` | ## Platform constraints