Skip to content
This repository has been archived by the owner on Jun 1, 2024. It is now read-only.

Commit

Permalink
fix: image downloader test (#1483)
Browse files Browse the repository at this point in the history
Signed-off-by: Matheus Ribeiro <[email protected]>
  • Loading branch information
matheusribeirozup authored Apr 12, 2021
1 parent 0b569ae commit fbac822
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,29 @@ import kotlinx.coroutines.withContext
import java.io.InputStream
import java.net.URL

internal object URLFactory {
fun createURL(url: String?) = URL(url)
}

internal class ImageDownloader {

suspend fun getRemoteImage(url: String, contentWidth: Int, contentHeight: Int) : Bitmap? {
suspend fun getRemoteImage(url: String, contentWidth: Int, contentHeight: Int) : Bitmap {
val cacheId = LruImageCache.generateBitmapId(url, contentWidth, contentHeight)

return withContext(CoroutineDispatchers.IO) {
val bitmapCached = LruImageCache.get(cacheId)

bitmapCached
?: url.let {
downloadBitmap(it, contentWidth, contentHeight).apply {
downloadBitmap(it).apply {
LruImageCache.put(cacheId, this)
}
}
}
}

private fun downloadBitmap(url: String?, contentWidth: Int, contentHeight: Int) : Bitmap {
val inputStream: InputStream = URL(url).openStream()
private fun downloadBitmap(url: String?) : Bitmap {
val inputStream: InputStream = URLFactory.createURL(url).openStream()
return BitmapFactory.decodeStream(inputStream)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,3 @@ internal object LruImageCache {
contentHeight: Int
) = StringBuilder().append(url).append(contentWidth).append(contentHeight).toString()
}


Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import android.widget.ImageView
import androidx.lifecycle.lifecycleScope
import br.com.zup.beagle.android.cache.imagecomponent.ImageDownloader
import br.com.zup.beagle.android.data.formatUrl
import br.com.zup.beagle.android.logger.BeagleLoggerProxy
import br.com.zup.beagle.android.logger.BeagleMessageLogs
import br.com.zup.beagle.android.utils.CoroutineDispatchers
import br.com.zup.beagle.android.widget.RootView
import kotlinx.coroutines.launch
Expand All @@ -34,18 +34,18 @@ internal class DefaultImageDownloader : BeagleImageDownloader {

override fun download(url: String, imageView: ImageView, rootView: RootView) {
imageView.post {
rootView.getLifecycleOwner().lifecycleScope.launch(CoroutineDispatchers.IO) {
val bitmap = try {
imageDownloader.getRemoteImage(url.formatUrl() ?: url, imageView.width, imageView.height)
} catch (e: Exception) {
BeagleLoggerProxy.error(e.message ?: "Error when try to download Image")
null
}
rootView.getLifecycleOwner().lifecycleScope.launch(CoroutineDispatchers.IO) {
val bitmap = try {
imageDownloader.getRemoteImage(url.formatUrl() ?: url, imageView.width, imageView.height)
} catch (e: Exception) {
BeagleMessageLogs.errorWhileTryingToDownloadImage(url, e)
null
}

bitmap?.let {
setImage(imageView, bitmap)
}
bitmap?.let {
setImage(imageView, bitmap)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,9 @@ internal object BeagleMessageLogs {
val warningMessage = "Cannot get some attributes of property $propertyName."
BeagleLoggerProxy.warning(warningMessage)
}

fun errorWhileTryingToDownloadImage(image: String, ex: Exception) {
val errorMessage = "Error while trying to download image: $image"
BeagleLoggerProxy.error(errorMessage, ex)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,38 @@ import android.graphics.BitmapFactory
import android.util.LruCache
import io.mockk.*
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.*
import org.junit.jupiter.api.Assertions.assertEquals
import java.io.InputStream
import java.net.URL

internal class ImageDownloaderTest {
@DisplayName("Given ImageDownloader")
class ImageDownloaderTest {

private val imageDownloader = ImageDownloader()
private val bitmap: Bitmap = mockk(relaxed = true)
private val bitmapImproved: Bitmap = mockk(relaxed = true)
private val contentWidth = 300
private val contentHeight = 200
private val url = "https://vitafelice.com.br/wp-content/uploads/2019/01/beagle.jpg"
private val url = "https://www.stub.com/image.png"
private val bitmapId = url + contentWidth + contentHeight
private val inputStream: InputStream = mockk(relaxed = true)
private val javaUrl: URL = mockk(relaxed = true)

@BeforeEach
fun setUp() {
mockkStatic(BitmapFactory::class)
mockkObject(LruImageCache)
mockkStatic(LruCache::class)
mockkStatic(Bitmap::class)
mockkObject(URLFactory)
every { bitmapImproved.width } returns contentWidth
every { bitmapImproved.height } returns contentHeight
every { LruImageCache.get(any()) } returns null
every { LruImageCache.put(any(), any()) } just runs
every { BitmapFactory.decodeStream(any()) } returns bitmap
every { URLFactory.createURL(any()) } returns javaUrl
every { javaUrl.openStream() } returns inputStream
every { BitmapFactory.decodeStream(inputStream) } returns bitmap
every { Bitmap.createScaledBitmap(
any(),
contentWidth,
Expand All @@ -54,33 +61,68 @@ internal class ImageDownloaderTest {
) } returns bitmapImproved
}

@Test
fun `GIVEN url and ImageViewSize WHEN download image bitmap THEN keep size and save on cache`() = runBlocking {
// Given
every { bitmap.width } returns contentWidth
every { bitmap.height } returns contentHeight
@AfterEach
fun tearDown() {
unmockkAll()
}

@DisplayName("When download image")
@Nested
inner class Download {

@DisplayName("Then should keep passed size and save the bitmap on cache")
@Test
fun keepSizeAndSaveOnCache() = runBlocking {
// Given
every { bitmap.width } returns contentWidth
every { bitmap.height } returns contentHeight

// When
val bitmap = imageDownloader.getRemoteImage(url, contentWidth, contentHeight)

// When
val bitmap = imageDownloader.getRemoteImage(url, contentWidth, contentHeight)
// Then
verify(exactly = 1) { LruImageCache.put(bitmapId, bitmap) }
verify(exactly = 1) { LruImageCache.get(bitmapId) }
assertEquals(contentWidth, bitmap.width)
assertEquals(contentHeight, bitmap.height)
}

// Then
verify(exactly = 1) { LruImageCache.put(bitmapId, bitmap) }
verify(exactly = 1) { LruImageCache.get(bitmapId) }
assertEquals(contentWidth, bitmap?.width)
assertEquals(contentHeight, bitmap?.height)
@DisplayName("Then should load bitmap from cache when exists")
@Test
fun loadBitmapFromCache() = runBlocking {
// Given
every { LruImageCache.get(any()) } returns bitmapImproved

// When
val bitmap = imageDownloader.getRemoteImage(url, contentWidth, contentHeight)

// Then
verify(exactly = 1) { LruImageCache.get(bitmapId) }
assertEquals(contentWidth, bitmap.width)
assertEquals(contentHeight, bitmap.height)
}
}
}

@DisplayName("Given URLFactory")
class ImageDownloaderUrlFactory {

@DisplayName("When createURL method in URLFactory is called")
@Nested
inner class Factory {

@Test
fun `GIVEN bitmap data WHEN download image THEN should load image bitmap from cache`() = runBlocking {
// Given
every { LruImageCache.get(any()) } returns bitmapImproved
@DisplayName("Then should return a new instance of URL")
@Test
fun factoryUrl() {
// Given
val url = "https://www.stub.com"

// When
val bitmap = imageDownloader.getRemoteImage(url, contentWidth, contentHeight)
// When
val result = URLFactory.createURL(url)

// Then
verify(exactly = 1) { LruImageCache.get(bitmapId) }
assertEquals(contentWidth, bitmap?.width)
assertEquals(contentHeight, bitmap?.height)
// Then
assertEquals("https", result.protocol)
assertEquals("www.stub.com", result.host)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -418,4 +418,23 @@ internal class BeagleMessageLogsTest {
verify(exactly = 1) { BeagleLoggerProxy.warning(expectedMessage) }
}
}

@DisplayName("When try to download an invalid image")
@Nested
inner class DownloadImage {

@DisplayName("Then should call BeagleLoggerProxy.error with image and exception")
@Test
fun downloadInvalidImage() {
// Given
val image = "/image"
val exceptionMessage = "Error while trying to download image: $image"

// When
BeagleMessageLogs.errorWhileTryingToDownloadImage(image, exception)

// Then
verify(exactly = 1) { BeagleLoggerProxy.error(exceptionMessage, exception) }
}
}
}

0 comments on commit fbac822

Please sign in to comment.