From 854ad142c8b6bc82e7792861a9eaeba35364a36d Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 24 Jul 2026 16:03:56 +0200 Subject: [PATCH] =?utf8?q?=F0=9F=9A=A7=20Expose=20more=20information=20abo?= =?utf8?q?ut=20not-loaded=20elements?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../sone/core/DefaultElementLoader.kt | 21 ++++++ .../sone/core/DefaultElementLoaderTest.kt | 80 +++++++++++++++++++--- 2 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt index 83178ae..7615776 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/DefaultElementLoader.kt @@ -32,10 +32,12 @@ class DefaultElementLoader(private val freenetInterface: FreenetInterface, ticke override fun shouldCancel(uri: FreenetURI, mimeType: String, size: Long): Boolean { if (size > 2097152) { logger.fine { "Canceling download of $uri because it’s > 2 MiB." } + rejectedElements += uri.toString() to RejectionReason.SizeTooLarge return true } if (!mimeType.startsWith("image/") && !mimeType.startsWith("text/html")) { logger.fine { "Canceling download of $uri because of its MIME type, $mimeType." } + rejectedElements += uri.toString() to RejectionReason.InvalidMediaType return true } return false @@ -103,6 +105,25 @@ class DefaultElementLoader(private val freenetInterface: FreenetInterface, ticke return LinkedElement(link, loading = true) } + val loadingElements: Set + get() = loadingLinks.asMap().keys + + val loadedElements: Set + get() = elementCache.asMap().values.toSet() + + val rejectedElements: Map + field = mutableMapOf() + + val failedElements: Set + get() = failureCache.asMap().keys + + enum class RejectionReason { + + InvalidMediaType, + SizeTooLarge, + + } + } private fun String.decode() = try { URLDecoder.decode(this, "UTF-8")!! } catch (e: RuntimeException) { freenet.support.Logger.error(DefaultElementLoader::class.java, "Could not decode %s!".format(this), e); throw e } diff --git a/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt index f108371..eb9ee15 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/DefaultElementLoaderTest.kt @@ -2,28 +2,35 @@ package net.pterodactylus.sone.core import com.google.common.base.Ticker import freenet.keys.FreenetURI +import java.io.ByteArrayOutputStream +import java.util.concurrent.TimeUnit.MINUTES +import java.util.concurrent.atomic.AtomicReference +import java.util.logging.Handler +import java.util.logging.Level.ALL +import java.util.logging.LogRecord +import java.util.logging.Logger +import kotlin.math.min +import net.pterodactylus.sone.core.DefaultElementLoader.RejectionReason.InvalidMediaType +import net.pterodactylus.sone.core.DefaultElementLoader.RejectionReason.SizeTooLarge import net.pterodactylus.sone.core.FreenetInterface.BackgroundFetchCallback -import net.pterodactylus.sone.test.* +import net.pterodactylus.sone.test.dummyHighLevelSimpleClientCreator +import net.pterodactylus.sone.test.everything +import net.pterodactylus.sone.test.handleMatcher +import net.pterodactylus.sone.test.silencedLogging import org.hamcrest.Description import org.hamcrest.Matcher import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.contains +import org.hamcrest.Matchers.containsInAnyOrder import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.hasEntry +import org.hamcrest.Matchers.hasItem import org.hamcrest.Matchers.not import org.hamcrest.TypeSafeDiagnosingMatcher import org.junit.Rule import org.junit.Test -import java.io.ByteArrayOutputStream -import java.util.concurrent.TimeUnit.MINUTES -import java.util.concurrent.atomic.AtomicReference -import java.util.logging.Handler -import java.util.logging.Level.ALL -import java.util.logging.LogRecord -import java.util.logging.Logger -import kotlin.math.min /** * Unit test for [DefaultElementLoaderTest]. @@ -223,6 +230,59 @@ class DefaultElementLoaderTest { } } + @Test + fun `element loader exposes all currently loading elements`() { + runWithCallback("KSK@test") { elementLoader, _, _, _ -> + elementLoader.loadElement("KSK@test") + assertThat(elementLoader.loadingElements, containsInAnyOrder("KSK@test")) + } + } + + @Test + fun `element loader exposes all currently loaded elements`() { + runWithCallback("KSK@test") { elementLoader, _, callback, _ -> + callback.loaded(FreenetURI("KSK@test"), "image/png", read("/static/images/unknown-image-0.png")) + assertThat(elementLoader.loadedElements, containsInAnyOrder( + isLinkedElement(equalTo("KSK@test"), allOf( + hasEntry("type", "image"), hasEntry("size", 2451), hasEntry("sizeHuman", "2 KiB"), + )) + )) + } + } + + @Test + fun `element loader no longer exposes an element as loading once it has been loaded`() { + runWithCallback("KSK@test") { elementLoader, _, callback, _ -> + elementLoader.loadElement("KSK@test") + callback.loaded(FreenetURI("KSK@test"), "image/png", read("/static/images/unknown-image-0.png")) + assertThat(elementLoader.loadingElements, not(hasItem("KSK@test"))) + } + } + + @Test + fun `element loader exposes rejected element when media type does not match`() { + runWithCallback("KSK@test") { elementLoader, _, callback, _ -> + callback.shouldCancel(FreenetURI("KSK@test"), "application/x-test-data", 1234) + assertThat(elementLoader.rejectedElements, hasEntry("KSK@test", InvalidMediaType)) + } + } + + @Test + fun `element loader exposes rejected element when data is too large`() { + runWithCallback("KSK@test") { elementLoader, _, callback, _ -> + callback.shouldCancel(FreenetURI("KSK@test"), "image/png", 12345678) + assertThat(elementLoader.rejectedElements, hasEntry("KSK@test", SizeTooLarge)) + } + } + + @Test + fun `element loader exposes failed element when element cannot be loaded`() { + runWithCallback("KSK@test") { elementLoader, _, callback, _ -> + callback.failed(FreenetURI("KSK@test")) + assertThat(elementLoader.failedElements, hasItem("KSK@test")) + } + } + private fun read(resource: String): ByteArray = javaClass.getResourceAsStream(resource)?.use { input -> ByteArrayOutputStream().use { @@ -253,7 +313,7 @@ class DefaultElementLoaderTest { } -private fun runWithCallback(requestUri: String, ticker: Ticker = createTicker(), callbackAction: (elementLoader: ElementLoader, linkedElement: LinkedElement, callback: BackgroundFetchCallback, fetchedUris: List) -> Unit) { +private fun runWithCallback(requestUri: String, ticker: Ticker = createTicker(), callbackAction: (elementLoader: DefaultElementLoader, linkedElement: LinkedElement, callback: BackgroundFetchCallback, fetchedUris: List) -> Unit) { val fetchedUris = mutableListOf() val callback = AtomicReference() val freenetInterface = overrideStartFetch { uri, backgroundFetchCallback -> -- 2.7.4