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

Add tests for FakeImageLoaderEngine and rememberAsyncImagePainter. #1909

Merged
merged 5 commits into from
Oct 30, 2023
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
18 changes: 10 additions & 8 deletions coil-compose-base/src/main/java/coil/compose/AsyncImagePainter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ fun rememberAsyncImagePainter(
@Stable
class AsyncImagePainter internal constructor(
request: ImageRequest,
imageLoader: ImageLoader
imageLoader: ImageLoader,
) : Painter(), RememberObserver {

private var rememberScope: CoroutineScope? = null
Expand Down Expand Up @@ -263,9 +263,11 @@ class AsyncImagePainter internal constructor(
/** Update the [request] to work with [AsyncImagePainter]. */
private fun updateRequest(request: ImageRequest): ImageRequest {
return request.newBuilder()
.target(onStart = { placeholder ->
updateState(State.Loading(placeholder?.toPainter()))
})
.target(
onStart = { placeholder ->
updateState(State.Loading(placeholder?.toPainter()))
},
)
.apply {
if (request.defined.sizeResolver == null) {
// If no other size resolver is set, suspend until the canvas size is positive.
Expand Down Expand Up @@ -318,7 +320,7 @@ class AsyncImagePainter internal constructor(
contentScale = contentScale,
durationMillis = transition.durationMillis,
fadeStart = result !is SuccessResult || !result.isPlaceholderCached,
preferExactIntrinsicSize = transition.preferExactIntrinsicSize
preferExactIntrinsicSize = transition.preferExactIntrinsicSize,
)
} else {
return null
Expand Down Expand Up @@ -379,7 +381,7 @@ private fun validateRequest(request: ImageRequest) {
when (request.data) {
is ImageRequest.Builder -> unsupportedData(
name = "ImageRequest.Builder",
description = "Did you forget to call ImageRequest.Builder.build()?"
description = "Did you forget to call ImageRequest.Builder.build()?",
)
is ImageBitmap -> unsupportedData("ImageBitmap")
is ImageVector -> unsupportedData("ImageVector")
Expand All @@ -390,7 +392,7 @@ private fun validateRequest(request: ImageRequest) {

private fun unsupportedData(
name: String,
description: String = "If you wish to display this $name, use androidx.compose.foundation.Image."
description: String = "If you wish to display this $name, use androidx.compose.foundation.Image.",
): Nothing = throw IllegalArgumentException("Unsupported type: $name. $description")

private val Size.isPositive get() = width >= 0.5 && height >= 0.5
Expand All @@ -399,7 +401,7 @@ private fun Size.toSizeOrNull() = when {
isUnspecified -> CoilSize.ORIGINAL
isPositive -> CoilSize(
width = if (width.isFinite()) Dimension(width.roundToInt()) else Dimension.Undefined,
height = if (height.isFinite()) Dimension(height.roundToInt()) else Dimension.Undefined
height = if (height.isFinite()) Dimension(height.roundToInt()) else Dimension.Undefined,
)
else -> null
}
Expand Down
49 changes: 44 additions & 5 deletions coil-test-paparazzi/src/test/java/coil/test/PaparazziTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ package coil.test
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.widget.ImageView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import app.cash.paparazzi.DeviceConfig.Companion.PIXEL_6
import app.cash.paparazzi.DeviceConfig
import app.cash.paparazzi.Paparazzi
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import coil.decode.ImageSource
import coil.request.ImageRequest
import coil.size.Size
import kotlin.test.assertTrue
import okio.Buffer
import org.junit.Rule
Expand All @@ -19,13 +24,16 @@ class PaparazziTest {

@get:Rule
val paparazzi = Paparazzi(
deviceConfig = PIXEL_6,
deviceConfig = DeviceConfig(
screenWidth = 320,
screenHeight = 470,
),
theme = "android:Theme.Material.Light.NoActionBar.Fullscreen",
showSystemUi = false,
)

@Test
fun loadView() {
fun imageView() {
val url = "https://www.example.com/image.jpg"
val drawable = object : ColorDrawable(Color.RED) {
override fun getIntrinsicWidth() = 100
Expand All @@ -51,9 +59,8 @@ class PaparazziTest {
}

@Test
fun loadCompose() {
fun asyncImage() {
val url = "https://www.example.com/image.jpg"
// Wrap the color drawable so it isn't automatically converted into a ColorPainter.
val drawable = object : ColorDrawable(Color.RED) {
override fun getIntrinsicWidth() = 100
override fun getIntrinsicHeight() = 100
Expand All @@ -71,6 +78,38 @@ class PaparazziTest {
contentDescription = null,
imageLoader = imageLoader,
contentScale = ContentScale.None,
modifier = Modifier.fillMaxSize(),
)
}
}

@Test
fun rememberAsyncImagePainter() {
val url = "https://www.example.com/image.jpg"
val drawable = object : ColorDrawable(Color.RED) {
override fun getIntrinsicWidth() = 100
override fun getIntrinsicHeight() = 100
}
val engine = FakeImageLoaderEngine.Builder()
.intercept(url, drawable)
.build()
val imageLoader = ImageLoader.Builder(paparazzi.context)
.components { add(engine) }
.build()

paparazzi.snapshot {
Image(
painter = rememberAsyncImagePainter(
// TODO: Figure out how to avoid having to specify an immediate size.
model = ImageRequest.Builder(paparazzi.context)
.data(url)
.size(Size.ORIGINAL)
.build(),
imageLoader = imageLoader,
),
contentDescription = null,
contentScale = ContentScale.None,
modifier = Modifier.fillMaxSize(),
)
}
}
Expand Down
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package coil.test

import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
Expand All @@ -10,6 +11,9 @@ import androidx.compose.ui.test.onRoot
import androidx.test.ext.junit.runners.AndroidJUnit4
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import coil.size.Size
import coil.util.ComposeTestActivity
import com.github.takahirom.roborazzi.RoborazziRule
import org.junit.Rule
Expand All @@ -29,14 +33,14 @@ class RoborazziComposeTest {
composeRule = composeTestRule,
captureRoot = composeTestRule.onRoot(),
options = RoborazziRule.Options(
captureType = RoborazziRule.CaptureType.LastImage(),
outputDirectoryPath = "src/test/snapshots/images",
)
)

@Test
fun loadCompose() {
fun asyncImage() {
val url = "https://www.example.com/image.jpg"
// Wrap the color drawable so it isn't automatically converted into a ColorPainter.
val drawable = object : ColorDrawable(Color.RED) {
override fun getIntrinsicWidth() = 100
override fun getIntrinsicHeight() = 100
Expand All @@ -58,4 +62,35 @@ class RoborazziComposeTest {
)
}
}

@Test
fun rememberAsyncImagePainter() {
val url = "https://www.example.com/image.jpg"
val drawable = object : ColorDrawable(Color.RED) {
override fun getIntrinsicWidth() = 100
override fun getIntrinsicHeight() = 100
}
val engine = FakeImageLoaderEngine.Builder()
.intercept(url, drawable)
.build()
val imageLoader = ImageLoader.Builder(composeTestRule.activity)
.components { add(engine) }
.build()

composeTestRule.setContent {
Image(
// TODO: Figure out how to avoid having to specify an immediate size.
painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(composeTestRule.activity)
.data(url)
.size(Size.ORIGINAL)
.build(),
imageLoader = imageLoader,
),
contentDescription = null,
contentScale = ContentScale.None,
modifier = Modifier.fillMaxSize(),
)
}
}
}
17 changes: 11 additions & 6 deletions coil-test-roborazzi/src/test/java/coil/test/RoborazziViewTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import coil.ImageLoader
import coil.request.ImageRequest
import coil.util.ViewTestActivity
import coil.util.activity
import com.github.takahirom.roborazzi.captureRoboImage
import com.github.takahirom.roborazzi.RoborazziRule
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -24,8 +24,17 @@ class RoborazziViewTest {
@get:Rule
val activityRule = activityScenarioRule<ViewTestActivity>()

@get:Rule
val roborazziRule = RoborazziRule(
captureRoot = onView(isRoot()),
options = RoborazziRule.Options(
captureType = RoborazziRule.CaptureType.LastImage(),
outputDirectoryPath = "src/test/snapshots/images",
)
)

@Test
fun loadView() {
fun imageView() {
val url = "https://www.example.com/image.jpg"
val drawable = object : ColorDrawable(Color.RED) {
override fun getIntrinsicWidth() = 100
Expand All @@ -47,9 +56,5 @@ class RoborazziViewTest {

// Don't suspend to test that the image view is updated synchronously.
imageLoader.enqueue(request)

// https://github.com/takahirom/roborazzi/issues/9
onView(isRoot())
.captureRoboImage("src/test/snapshots/images/coil.test.RoborazziViewTest.loadView.png")
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion coil-test/src/main/java/coil/test/FakeImageLoaderEngine.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ class FakeImageLoaderEngine private constructor(
error("No interceptors handled this request and no fallback is set: ${chain.request.data}")
}
requestTransformer = RequestTransformer { request ->
request.newBuilder().transitionFactory(Transition.Factory.NONE).build()
request.newBuilder()
.transitionFactory(Transition.Factory.NONE)
.build()
}
}

Expand Down
Loading