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

fix: image downloader test #1483

Merged
merged 1 commit into from
Apr 12, 2021
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
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) }
}
}
}