Add image loader
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 11 Nov 2016 07:11:27 +0000 (08:11 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 11 Nov 2016 07:11:27 +0000 (08:11 +0100)
src/main/kotlin/net/pterodactylus/sone/core/DefaultImageLoader.kt [new file with mode: 0644]
src/main/kotlin/net/pterodactylus/sone/core/ImageLoader.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/core/DefaultImageLoaderTest.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/test/Mockotlin.kt

diff --git a/src/main/kotlin/net/pterodactylus/sone/core/DefaultImageLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/DefaultImageLoader.kt
new file mode 100644 (file)
index 0000000..469feb8
--- /dev/null
@@ -0,0 +1,38 @@
+package net.pterodactylus.sone.core
+
+import com.google.common.cache.CacheBuilder
+import freenet.keys.FreenetURI
+import java.io.ByteArrayInputStream
+import javax.imageio.ImageIO
+
+/**
+ * [ImageLoader] implementation that uses a simple Guava [com.google.common.cache.Cache].
+ */
+class DefaultImageLoader(private val freenetInterface: FreenetInterface) : ImageLoader {
+
+       private val imageCache = CacheBuilder.newBuilder().build<String, LoadedImage>()
+       private val callback = object : FreenetInterface.BackgroundFetchCallback {
+               override fun loaded(uri: FreenetURI, mimeType: String, data: ByteArray) {
+                       if (!mimeType.startsWith("image/")) {
+                               return
+                       }
+                       val image = ByteArrayInputStream(data).use {
+                               ImageIO.read(it)
+                       }
+                       val loadedImage = LoadedImage(uri.toString(), mimeType, image.width, image.height)
+                       imageCache.get(uri.toString()) { loadedImage }
+               }
+
+               override fun failed(uri: FreenetURI) {
+               }
+       }
+
+       override fun toLoadedImage(link: String): LoadedImage? {
+               imageCache.getIfPresent(link)?.run {
+                       return this
+               }
+               freenetInterface.startFetch(FreenetURI(link), callback)
+               return null
+       }
+
+}
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/ImageLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/ImageLoader.kt
new file mode 100644 (file)
index 0000000..6d7fb36
--- /dev/null
@@ -0,0 +1,12 @@
+package net.pterodactylus.sone.core
+
+/**
+ * Component that loads images and supplies information about them.
+ */
+interface ImageLoader {
+
+       fun toLoadedImage(link: String): LoadedImage?
+
+}
+
+data class LoadedImage(val link: String, val mimeType: String, val width: Int, val height: Int)
diff --git a/src/test/kotlin/net/pterodactylus/sone/core/DefaultImageLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/DefaultImageLoaderTest.kt
new file mode 100644 (file)
index 0000000..cc31665
--- /dev/null
@@ -0,0 +1,59 @@
+package net.pterodactylus.sone.core
+
+import com.google.common.io.ByteStreams
+import com.google.common.io.Files
+import freenet.keys.FreenetURI
+import net.pterodactylus.sone.core.FreenetInterface.BackgroundFetchCallback
+import net.pterodactylus.sone.test.capture
+import net.pterodactylus.sone.test.mock
+import org.hamcrest.MatcherAssert
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers
+import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.nullValue
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.verify
+import java.io.ByteArrayOutputStream
+
+/**
+ * Unit test for [DefaultImageLoaderTest].
+ */
+class DefaultImageLoaderTest {
+
+       companion object {
+               private const val IMAGE_ID = "KSK@gpl.png"
+       }
+
+       private val freenetInterface = mock<FreenetInterface>()
+       private val imageLoader = DefaultImageLoader(freenetInterface)
+       private val callback = capture<BackgroundFetchCallback>()
+
+       @Test
+       fun `image loader starts request for link that is not known`() {
+               assertThat(imageLoader.toLoadedImage(IMAGE_ID), nullValue())
+               verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), any<BackgroundFetchCallback>())
+       }
+
+       @Test
+       fun `image loader can load image`() {
+               assertThat(imageLoader.toLoadedImage(IMAGE_ID), nullValue())
+               verify(freenetInterface).startFetch(eq(FreenetURI(IMAGE_ID)), callback.capture())
+           callback.value.loaded(FreenetURI(IMAGE_ID), "image/png", read("/static/images/unknown-image-0.png"))
+               val loadedImage = imageLoader.toLoadedImage(IMAGE_ID)!!
+               assertThat(loadedImage.link, `is`(IMAGE_ID))
+               assertThat(loadedImage.mimeType, `is`("image/png"))
+               assertThat(loadedImage.width, `is`(200))
+               assertThat(loadedImage.height, `is`(150))
+       }
+
+       private fun read(resource: String): ByteArray =
+                       javaClass.getResourceAsStream(resource)?.use { input ->
+                               ByteArrayOutputStream().use {
+                                       ByteStreams.copy(input, it)
+                                       it
+                               }.toByteArray()
+                       } ?: ByteArray(0)
+
+}
index ed2f3fc..5ce82df 100644 (file)
@@ -1,5 +1,7 @@
 package net.pterodactylus.sone.test
 
+import org.mockito.ArgumentCaptor
 import org.mockito.Mockito
 
 inline fun <reified T : Any> mock(): T = Mockito.mock<T>(T::class.java)!!
+inline fun <reified T : Any> capture(): ArgumentCaptor<T> = ArgumentCaptor.forClass(T::class.java)