From: David ‘Bombe’ Roden Date: Fri, 24 Jul 2026 14:03:56 +0000 (+0200) Subject: 🚧 Expose more information about not-loaded elements X-Git-Tag: v83^2~5^2~13 X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=854ad142c8b6bc82e7792861a9eaeba35364a36d;p=Sone.git 🚧 Expose more information about not-loaded elements --- 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 ->