Skip to content

Commit

Permalink
Glide integration library (#113)
Browse files Browse the repository at this point in the history
* Initial Glide skeleton project
* Add non-working GlideImage implementation
* Migrate over more of the tests
* Extract test utils to library
* Add Glide samples
* Get tests working
* GlideImage is now fully working!
* Add Glide API file
* Update to main
* Exclude license files from test apk
* Fix Glide docs
* Fix targetSdk for test apps
* Fix GlideTest.loading_slot test
* Apply ktlint's IntelliJ format settings
* Add new awaitNext() and receiveBlocking() test functions
* Document ImageLoad TR type
* Doc EmptyCustomTarget
* Move mock web server delay default to function
* Added GIF to Glide README
  • Loading branch information
chrisbanes committed Oct 15, 2020
1 parent f6d27a9 commit b9880f2
Show file tree
Hide file tree
Showing 51 changed files with 1,702 additions and 259 deletions.
16 changes: 16 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/inspectionProfiles/ktlint.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/inspectionProfiles/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ plugins {
apply plugin: 'binary-compatibility-validator'
apiValidation {
// Ignore the sample from API tracking/checking
ignoredProjects += ["sample"]
ignoredProjects += ["sample", "imageloading-testutils"]
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ object Libs {

const val picasso = "com.squareup.picasso:picasso:2.8"

const val glide = "com.github.bumptech.glide:glide:4.11.0"

const val truth = "com.google.truth:truth:1.0.1"

object OkHttp {
Expand Down
8 changes: 6 additions & 2 deletions coil/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,15 @@ dependencies {
implementation Libs.Kotlin.stdlib
implementation Libs.Coroutines.android

// ======================
// Test dependencies
// ======================

androidTestImplementation project(':imageloading-testutils')

androidTestImplementation Libs.junit
androidTestImplementation Libs.truth

androidTestImplementation Libs.OkHttp.mockWebServer

androidTestImplementation Libs.Coroutines.test

androidTestImplementation Libs.AndroidX.Compose.test
Expand Down
14 changes: 0 additions & 14 deletions coil/src/androidTest/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,8 @@
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="dev.chrisbanes.accompanist.coil.test">

<uses-sdk android:targetSdkVersion="30" />

<!-- Needed for MockWebServer -->
<uses-permission android:name="android.permission.INTERNET" />

<!-- usesCleartextTraffic=true because we use MockWebServer -->
<application
android:name="dev.chrisbanes.accompanist.coil.CoilTestApplication"
android:usesCleartextTraffic="true">
<activity
android:name="androidx.activity.ComponentActivity"
android:theme="@style/Theme.CoilTest"
tools:replace="android:theme" />
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

package dev.chrisbanes.accompanist.coil

import android.content.ContentResolver
import android.net.Uri
import androidx.compose.foundation.Image
import androidx.compose.foundation.Text
import androidx.compose.foundation.layout.preferredSize
Expand All @@ -28,7 +26,6 @@ import androidx.compose.ui.graphics.painter.ColorPainter
import androidx.compose.ui.platform.ContextAmbient
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
Expand All @@ -52,18 +49,16 @@ import coil.request.ImageRequest
import com.google.common.truth.Truth.assertThat
import dev.chrisbanes.accompanist.coil.test.R
import dev.chrisbanes.accompanist.imageloading.ImageLoadState
import dev.chrisbanes.accompanist.imageloading.test.ImageMockWebServer
import dev.chrisbanes.accompanist.imageloading.test.awaitNext
import dev.chrisbanes.accompanist.imageloading.test.receiveBlocking
import dev.chrisbanes.accompanist.imageloading.test.resourceUri
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.withTimeoutOrNull
import okhttp3.mockwebserver.Dispatcher
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import okhttp3.mockwebserver.RecordedRequest
import okio.Buffer
import org.junit.After
import org.junit.Before
import org.junit.Rule
Expand All @@ -80,7 +75,7 @@ class CoilTest {
val composeTestRule = createComposeRule()

// Our MockWebServer. We use a response delay to simulate real-world conditions
private val server = coilTestWebServer(responseDelayMs = 200)
private val server = ImageMockWebServer()

@Before
fun setup() {
Expand All @@ -102,7 +97,7 @@ class CoilTest {
composeTestRule.setContent {
CoilImage(
request = ImageRequest.Builder(ContextAmbient.current)
.data(resourceUri(R.raw.sample))
.data(server.url("/image"))
.listener { _, _ -> latch.countDown() }
.build(),
modifier = Modifier.preferredSize(128.dp, 128.dp),
Expand All @@ -127,7 +122,7 @@ class CoilTest {

composeTestRule.setContent {
CoilImage(
data = resourceUri(R.raw.sample),
data = server.url("/image"),
requestBuilder = {
listener { _, _ -> latch.countDown() }
},
Expand Down Expand Up @@ -212,7 +207,7 @@ class CoilTest {

composeTestRule.setContent {
CoilImage(
data = resourceUri(R.drawable.red_rectangle),
data = server.url("/image"),
modifier = Modifier.preferredSize(128.dp, 128.dp),
imageLoader = imageLoader,
onRequestCompleted = { latch.countDown() }
Expand All @@ -231,23 +226,19 @@ class CoilTest {
@SdkSuppress(minSdkVersion = 26) // captureToBitmap is SDK 26+
fun basicLoad_switchData() {
val loadCompleteSignal = Channel<Unit>(Channel.UNLIMITED)
val drawableResId = MutableStateFlow(R.drawable.red_rectangle)
val data = MutableStateFlow(server.url("/red"))

composeTestRule.setContent {
val resId = drawableResId.collectAsState()
val resId = data.collectAsState()
CoilImage(
data = resourceUri(resId.value),
data = resId.value,
modifier = Modifier.preferredSize(128.dp, 128.dp).testTag(CoilTestTags.Image),
onRequestCompleted = { loadCompleteSignal.offer(Unit) }
)
}

// Await the first load
runBlocking {
withTimeout(5000) {
loadCompleteSignal.receive()
}
}
loadCompleteSignal.awaitNext(5, TimeUnit.SECONDS)

// Assert that the content is completely Red
composeTestRule.onNodeWithTag(CoilTestTags.Image)
Expand All @@ -258,14 +249,10 @@ class CoilTest {
.assertPixels { Color.Red }

// Now switch the data URI to the blue drawable
drawableResId.value = R.drawable.blue_rectangle
data.value = server.url("/blue")

// Await the second load
runBlocking {
withTimeout(5000) {
loadCompleteSignal.receive()
}
}
loadCompleteSignal.awaitNext(5, TimeUnit.SECONDS)

// Assert that the content is completely Blue
composeTestRule.onNodeWithTag(CoilTestTags.Image)
Expand All @@ -282,22 +269,21 @@ class CoilTest {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun basicLoad_changeSize() {
val loadCompleteSignal = Channel<Unit>(Channel.UNLIMITED)
val loadCompleteSignal = Channel<ImageLoadState>(Channel.UNLIMITED)
val sizeFlow = MutableStateFlow(128.dp)

composeTestRule.setContent {
val size = sizeFlow.collectAsState()
CoilImage(
data = resourceUri(R.drawable.red_rectangle),
data = server.url("/red"),
modifier = Modifier.preferredSize(size.value).testTag(CoilTestTags.Image),
onRequestCompleted = { loadCompleteSignal.offer(Unit) }
onRequestCompleted = { loadCompleteSignal.offer(it) }
)
}

// Await the first load
runBlocking {
loadCompleteSignal.receive()
}
assertThat(loadCompleteSignal.receiveBlocking())
.isInstanceOf(ImageLoadState.Success::class.java)

// Now change the size
sizeFlow.value = 256.dp
Expand All @@ -318,7 +304,7 @@ class CoilTest {

composeTestRule.setContent {
CoilImage(
data = resourceUri(R.raw.sample),
data = server.url("/image"),
modifier = Modifier.testTag(CoilTestTags.Image),
onRequestCompleted = { latch.countDown() }
)
Expand Down Expand Up @@ -420,7 +406,7 @@ class CoilTest {

composeTestRule.setContent {
CoilImage(
data = resourceUri(R.raw.sample),
data = server.url("/image"),
modifier = Modifier.preferredSize(128.dp, 128.dp).testTag(CoilTestTags.Image),
onRequestCompleted = { latch.countDown() }
) { _ ->
Expand Down Expand Up @@ -520,40 +506,3 @@ private fun noCacheImageLoader(): ImageLoader {
.diskCachePolicy(CachePolicy.DISABLED)
.build()
}

private fun resourceUri(id: Int): Uri {
val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName
return "${ContentResolver.SCHEME_ANDROID_RESOURCE}://$packageName/$id".toUri()
}

/**
* [MockWebServer] which returns a valid response at the path `/image`, and a 404 for anything else.
* We add a small delay to simulate 'real-world' network conditions.
*/
private fun coilTestWebServer(responseDelayMs: Long = 0): MockWebServer {
val dispatcher = object : Dispatcher() {
override fun dispatch(request: RecordedRequest): MockResponse = when (request.path) {
"/image" -> {
val res = InstrumentationRegistry.getInstrumentation().targetContext.resources

// Load the image into a Buffer
val imageBuffer = Buffer().apply {
readFrom(res.openRawResource(R.raw.sample))
}

MockResponse()
.setHeadersDelay(responseDelayMs, TimeUnit.MILLISECONDS)
.addHeader("Content-Type", "image/jpeg")
.setBody(imageBuffer)
}
else ->
MockResponse()
.setHeadersDelay(responseDelayMs, TimeUnit.MILLISECONDS)
.setResponseCode(404)
}
}

return MockWebServer().apply {
setDispatcher(dispatcher)
}
}
5 changes: 5 additions & 0 deletions generate_docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ mkdir -p $DOCS_ROOT/picasso
cp picasso/images/crossfade.gif $DOCS_ROOT/picasso/crossfade.gif
sed -i.bak 's/images\/crossfade.gif/crossfade.gif/' $DOCS_ROOT/picasso.md

cp glide/README.md $DOCS_ROOT/glide.md
mkdir -p $DOCS_ROOT/glide
cp glide/images/crossfade.gif $DOCS_ROOT/glide/crossfade.gif
sed -i.bak 's/images\/crossfade.gif/crossfade.gif/' $DOCS_ROOT/glide.md

# Convert docs/xxx.md links to just xxx/
sed -i.bak 's/docs\/\([a-zA-Z-]*\).md/\1/' $DOCS_ROOT/index.md

Expand Down
Loading

0 comments on commit b9880f2

Please sign in to comment.