From 90993f1fc13288b83996066e726653b4dd011479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 9 Feb 2020 14:03:41 +0100 Subject: [PATCH 01/87] =?UTF-8?q?=F0=9F=9A=9A=20Move=20kotlin=20files=20to?= =?UTF-8?q?=20correct=20source=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/PreferenceChangedEvent.kt | 0 .../net/pterodactylus/sone/core/PreferencesLoader.kt | 0 .../net/pterodactylus/sone/database/memory/MemoryDatabase.kt | 0 .../net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt | 0 .../{java => kotlin}/net/pterodactylus/sone/main/SonePlugin.kt | 0 src/main/{java => kotlin}/net/pterodactylus/sone/web/AllPages.kt | 0 .../net/pterodactylus/sone/freenet/wot/IdentityManagerTest.kt | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename src/main/{java => kotlin}/net/pterodactylus/sone/core/PreferenceChangedEvent.kt (100%) rename src/main/{java => kotlin}/net/pterodactylus/sone/core/PreferencesLoader.kt (100%) rename src/main/{java => kotlin}/net/pterodactylus/sone/database/memory/MemoryDatabase.kt (100%) rename src/main/{java => kotlin}/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt (100%) rename src/main/{java => kotlin}/net/pterodactylus/sone/main/SonePlugin.kt (100%) rename src/main/{java => kotlin}/net/pterodactylus/sone/web/AllPages.kt (100%) rename src/test/{java => kotlin}/net/pterodactylus/sone/freenet/wot/IdentityManagerTest.kt (100%) diff --git a/src/main/java/net/pterodactylus/sone/core/PreferenceChangedEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferenceChangedEvent.kt similarity index 100% rename from src/main/java/net/pterodactylus/sone/core/PreferenceChangedEvent.kt rename to src/main/kotlin/net/pterodactylus/sone/core/PreferenceChangedEvent.kt diff --git a/src/main/java/net/pterodactylus/sone/core/PreferencesLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt similarity index 100% rename from src/main/java/net/pterodactylus/sone/core/PreferencesLoader.kt rename to src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt similarity index 100% rename from src/main/java/net/pterodactylus/sone/database/memory/MemoryDatabase.kt rename to src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt similarity index 100% rename from src/main/java/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt rename to src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityChangeDetector.kt diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.kt b/src/main/kotlin/net/pterodactylus/sone/main/SonePlugin.kt similarity index 100% rename from src/main/java/net/pterodactylus/sone/main/SonePlugin.kt rename to src/main/kotlin/net/pterodactylus/sone/main/SonePlugin.kt diff --git a/src/main/java/net/pterodactylus/sone/web/AllPages.kt b/src/main/kotlin/net/pterodactylus/sone/web/AllPages.kt similarity index 100% rename from src/main/java/net/pterodactylus/sone/web/AllPages.kt rename to src/main/kotlin/net/pterodactylus/sone/web/AllPages.kt diff --git a/src/test/java/net/pterodactylus/sone/freenet/wot/IdentityManagerTest.kt b/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerTest.kt similarity index 100% rename from src/test/java/net/pterodactylus/sone/freenet/wot/IdentityManagerTest.kt rename to src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerTest.kt From d59dcba3dbf5d1f9327dd7b370b96bded9d79ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 18:01:09 +0100 Subject: [PATCH 02/87] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20Sone=E2=80=99s?= =?UTF-8?q?=20identity=20to=20create=20insert=20URI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/core/SoneInserter.java | 13 +++++-- .../sone/core/SoneInserterTest.kt | 35 ++++++++++--------- .../net/pterodactylus/sone/test/Mocks.kt | 1 + 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 7ccb37428..f18776beb 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -27,12 +27,12 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; +import java.net.MalformedURLException; import java.nio.charset.Charset; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; @@ -49,6 +49,7 @@ import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.main.SonePlugin; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.service.AbstractService; @@ -237,7 +238,7 @@ protected void serviceRun() { long insertTime = currentTimeMillis(); eventBus.post(new SoneInsertingEvent(sone)); Stopwatch stopwatch = Stopwatch.createStarted(); - FreenetURI finalUri = freenetInterface.insertDirectory(sone.getInsertUri(), insertInformation.generateManifestEntries(), "index.html"); + FreenetURI finalUri = freenetInterface.insertDirectory(getSoneInsertUri(sone), insertInformation.generateManifestEntries(), "index.html"); stopwatch.stop(); soneInsertDurationHistogram.update(stopwatch.elapsed(MICROSECONDS)); eventBus.post(new SoneInsertedEvent(sone, stopwatch.elapsed(MILLISECONDS), insertInformation.getFingerprint())); @@ -285,6 +286,14 @@ public void insertionDelayChanged(InsertionDelayChangedEvent insertionDelayChang setInsertionDelay(insertionDelayChangedEvent.getInsertionDelay()); } + private FreenetURI getSoneInsertUri(Sone sone) throws MalformedURLException { + return new FreenetURI(((OwnIdentity) sone.getIdentity()).getInsertUri()) + .setKeyType("USK") + .setDocName("Sone") + .setMetaString(new String[0]) + .setSuggestedEdition(sone.getLatestEdition()); + } + /** * Container for information that are required to insert a Sone. This * container merely exists to copy all relevant data without holding a lock diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt index 2f9025095..7ac9e0643 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt @@ -10,6 +10,7 @@ import freenet.keys.* import net.pterodactylus.sone.core.SoneInserter.* import net.pterodactylus.sone.core.event.* import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.main.* import net.pterodactylus.sone.test.* import org.hamcrest.MatcherAssert.* @@ -55,8 +56,9 @@ class SoneInserterTest { } private fun createSone(insertUri: FreenetURI, fingerprint: String = "fingerprint"): Sone { + val ownIdentity = DefaultOwnIdentity("", "", "", insertUri.toString()) val sone = mock() - whenever(sone.insertUri).thenReturn(insertUri) + whenever(sone.identity).thenReturn(ownIdentity) whenever(sone.fingerprint).thenReturn(fingerprint) whenever(sone.rootAlbum).thenReturn(mock()) whenever(core.getSone(anyString())).thenReturn(sone) @@ -94,12 +96,11 @@ class SoneInserterTest { @Test fun `sone inserter inserts a sone if it is eligible`() { - val insertUri = mock() val finalUri = mock() val sone = createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) - whenever(freenetInterface.insertDirectory(eq(insertUri), any>(), eq("index.html"))).thenReturn(finalUri) + whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenReturn(finalUri) val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) doAnswer { soneInserter.stop() @@ -107,7 +108,7 @@ class SoneInserterTest { }.whenever(core).touchConfiguration() soneInserter.serviceRun() val soneEvents = ArgumentCaptor.forClass(SoneEvent::class.java) - verify(freenetInterface).insertDirectory(eq(insertUri), any>(), eq("index.html")) + verify(freenetInterface).insertDirectory(eq(expectedInsertUri), any>(), eq("index.html")) verify(eventBus, times(2)).post(soneEvents.capture()) assertThat(soneEvents.allValues[0], instanceOf(SoneInsertingEvent::class.java)) assertThat(soneEvents.allValues[0].sone, equalTo(sone)) @@ -117,19 +118,18 @@ class SoneInserterTest { @Test fun `sone inserter bails out if it is stopped while inserting`() { - val insertUri = mock() val finalUri = mock() val sone = createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) - whenever(freenetInterface.insertDirectory(eq(insertUri), any>(), eq("index.html"))).thenAnswer { + whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenAnswer { soneInserter.stop() finalUri } soneInserter.serviceRun() val soneEvents = ArgumentCaptor.forClass(SoneEvent::class.java) - verify(freenetInterface).insertDirectory(eq(insertUri), any>(), eq("index.html")) + verify(freenetInterface).insertDirectory(eq(expectedInsertUri), any>(), eq("index.html")) verify(eventBus, times(2)).post(soneEvents.capture()) assertThat(soneEvents.allValues[0], instanceOf(SoneInsertingEvent::class.java)) assertThat(soneEvents.allValues[0].sone, equalTo(sone)) @@ -140,7 +140,6 @@ class SoneInserterTest { @Test fun `sone inserter does not insert sone if it is not eligible`() { - val insertUri = mock() createSone(insertUri) val soneModificationDetector = mock() val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) @@ -154,25 +153,24 @@ class SoneInserterTest { soneInserter.stop() }).start() soneInserter.serviceRun() - verify(freenetInterface, never()).insertDirectory(eq(insertUri), any>(), eq("index.html")) + verify(freenetInterface, never()).insertDirectory(eq(expectedInsertUri), any>(), eq("index.html")) verify(eventBus, never()).post(argThat(org.hamcrest.Matchers.any(SoneEvent::class.java))) } @Test fun `sone inserter posts aborted event if an exception occurs`() { - val insertUri = mock() val sone = createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) val soneException = SoneException(Exception()) - whenever(freenetInterface.insertDirectory(eq(insertUri), any>(), eq("index.html"))).thenAnswer { + whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenAnswer { soneInserter.stop() throw soneException } soneInserter.serviceRun() val soneEvents = ArgumentCaptor.forClass(SoneEvent::class.java) - verify(freenetInterface).insertDirectory(eq(insertUri), any>(), eq("index.html")) + verify(freenetInterface).insertDirectory(eq(expectedInsertUri), any>(), eq("index.html")) verify(eventBus, times(2)).post(soneEvents.capture()) assertThat(soneEvents.allValues[0], instanceOf(SoneInsertingEvent::class.java)) assertThat(soneEvents.allValues[0].sone, equalTo(sone)) @@ -241,12 +239,11 @@ class SoneInserterTest { @Test fun `successful insert updates metrics`() { - val insertUri = mock() val finalUri = mock() createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) - whenever(freenetInterface.insertDirectory(eq(insertUri), any>(), eq("index.html"))).thenReturn(finalUri) + whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenReturn(finalUri) val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry,"SoneId", soneModificationDetector, 1) doAnswer { soneInserter.stop() @@ -259,12 +256,11 @@ class SoneInserterTest { @Test fun `unsuccessful insert does not update histogram but records error`() { - val insertUri = mock() createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) - whenever(freenetInterface.insertDirectory(eq(insertUri), any>(), eq("index.html"))).thenAnswer { + whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenAnswer { soneInserter.stop() throw SoneException(Exception()) } @@ -276,3 +272,10 @@ class SoneInserterTest { } } + +val insertUri = createInsertUri +val expectedInsertUri: FreenetURI = FreenetURI(insertUri.toString()) + .setKeyType("USK") + .setDocName("Sone") + .setMetaString(kotlin.emptyArray()) + .setSuggestedEdition(0) diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index 48fb9bf5f..312f158b3 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -32,6 +32,7 @@ val remoteSone2 = createRemoteSone() val localSone1 = createLocalSone() val localSone2 = createLocalSone() +val createInsertUri: FreenetURI get() = InsertableClientSSK.createRandom(DummyRandomSource(), "").insertURI fun createId() = InsertableClientSSK.createRandom(DummyRandomSource(), "").uri.routingKey.asFreenetBase64 fun createLocalSone(id: String? = createId()) = object : IdOnlySone(id) { From e6fa274b4a3fb8a8ae55c1e31fb940ce5878604e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 18:01:34 +0100 Subject: [PATCH 03/87] =?UTF-8?q?=F0=9F=94=A5=20Replace=20Guava=E2=80=99s?= =?UTF-8?q?=20ByteStream=20method=20with=20Kotlin=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt index 7ac9e0643..487815026 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt @@ -4,7 +4,6 @@ import com.codahale.metrics.* import com.google.common.base.* import com.google.common.base.Optional import com.google.common.eventbus.* -import com.google.common.io.ByteStreams.* import com.google.common.util.concurrent.MoreExecutors.* import freenet.keys.* import net.pterodactylus.sone.core.SoneInserter.* @@ -210,7 +209,7 @@ class SoneInserterTest { val manifestElement = manifestCreator.createManifestElement("test.txt", "plain/text; charset=utf-8", "sone-inserter-manifest.txt") assertThat(manifestElement!!.name, equalTo("test.txt")) assertThat(manifestElement.mimeTypeOverride, equalTo("plain/text; charset=utf-8")) - val templateContent = String(toByteArray(manifestElement.data.inputStream), Charsets.UTF_8) + val templateContent = String(manifestElement.data.inputStream.readBytes(), Charsets.UTF_8) assertThat(templateContent, containsString("Sone Version: ${SonePlugin.getPluginVersion()}\n")) assertThat(templateContent, containsString("Core Startup: $now\n")) assertThat(templateContent, containsString("Sone ID: SoneId\n")) From a9e1e90e3eb8e0aa3bb3e591ce44d77f557f7e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 18:02:00 +0100 Subject: [PATCH 04/87] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20test=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt index 487815026..7d0655275 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt @@ -216,7 +216,7 @@ class SoneInserterTest { } @Test - fun `invalid template returns anull manifest element`() { + fun `invalid template returns a null manifest element`() { val soneProperties = HashMap() val manifestCreator = ManifestCreator(core, soneProperties) assertThat(manifestCreator.createManifestElement("test.txt", From fc66bb049a3de0c3c12d5df53002a32cdf2ce6a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 18:03:45 +0100 Subject: [PATCH 05/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20getInsertUri()=20?= =?UTF-8?q?method=20from=20Sone=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/Sone.java | 8 ------- .../sone/data/impl/IdOnlySone.java | 5 ----- .../sone/data/impl/SoneImpl.java | 21 ------------------- 3 files changed, 34 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index b43d02544..e2a7b5bb3 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -191,14 +191,6 @@ public List apply(@Nullable Sone sone) { @Nonnull FreenetURI getRequestUri(); - /** - * Returns the insert URI of this Sone. - * - * @return The insert URI of this Sone - */ - @Nullable - FreenetURI getInsertUri(); - /** * Returns the latest edition of this Sone. * diff --git a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java index e06e5a706..ddd96b951 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/IdOnlySone.java @@ -52,11 +52,6 @@ public FreenetURI getRequestUri() { return null; } - @Override - public FreenetURI getInsertUri() { - return null; - } - @Override public long getLatestEdition() { return 0; diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index 1069550b7..ceb05b26f 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -46,7 +46,6 @@ import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions; import net.pterodactylus.sone.database.Database; import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; import freenet.keys.FreenetURI; @@ -190,26 +189,6 @@ public FreenetURI getRequestUri() { } } - /** - * Returns the insert URI of this Sone. - * - * @return The insert URI of this Sone - */ - @Nullable - public FreenetURI getInsertUri() { - if (!isLocal()) { - return null; - } - try { - return new FreenetURI(((OwnIdentity) getIdentity()).getInsertUri()) - .setDocName("Sone") - .setMetaString(new String[0]) - .setSuggestedEdition(latestEdition); - } catch (MalformedURLException e) { - throw new IllegalStateException(format("Own identity %s's insert URI is incorrect.", getIdentity()), e); - } - } - /** * Returns the latest edition of this Sone. * From dd5ef478adcf707ccffdea3a6c0e3f1dee5803bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 18:51:43 +0100 Subject: [PATCH 06/87] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Replace=20Sone=20URI?= =?UTF-8?q?=20creator=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/SoneUri.java | 54 ------------- .../pterodactylus/sone/core/SoneUriCreator.kt | 26 ++++++ .../sone/core/SoneUriCreatorTest.kt | 79 +++++++++++++++++++ .../pterodactylus/sone/core/SoneUriTest.java | 36 --------- .../net/pterodactylus/sone/test/Mocks.kt | 1 + 5 files changed, 106 insertions(+), 90 deletions(-) delete mode 100644 src/main/java/net/pterodactylus/sone/core/SoneUri.java create mode 100644 src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt create mode 100644 src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt delete mode 100644 src/test/java/net/pterodactylus/sone/core/SoneUriTest.java diff --git a/src/main/java/net/pterodactylus/sone/core/SoneUri.java b/src/main/java/net/pterodactylus/sone/core/SoneUri.java deleted file mode 100644 index a92995072..000000000 --- a/src/main/java/net/pterodactylus/sone/core/SoneUri.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Sone - SoneUri.java - Copyright © 2013–2020 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.core; - -import static java.util.logging.Logger.getLogger; - -import java.net.MalformedURLException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import freenet.keys.FreenetURI; - -/** - * Helper class that creates {@link FreenetURI}s for Sone to insert to and - * request from. - */ -public class SoneUri { - - /** The logger. */ - private static final Logger logger = getLogger(SoneUri.class.getName()); - - /** - * Generate a Sone URI from the given URI. - * - * @param uri - * The URI to derive the Sone URI from - * @return The derived URI - */ - public static FreenetURI create(String uri) { - try { - return new FreenetURI(uri).setDocName("Sone").setMetaString(new String[0]); - } catch (MalformedURLException mue1) { - /* this should never happen. */ - logger.log(Level.WARNING, String.format("Could not create Sone URI from URI: %s", uri), mue1); - return null; - } - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt b/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt new file mode 100644 index 000000000..115c748bc --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt @@ -0,0 +1,26 @@ +package net.pterodactylus.sone.core + +import freenet.keys.FreenetURI +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.freenet.wot.OwnIdentity + +/** + * Injectable helper class that can create request and insert URIs for [Sones][Sone]. + */ +class SoneUriCreator { + + fun getRequestUri(sone: Sone): FreenetURI = sone.identity.requestUri + .let(::FreenetURI) + .sonify(sone.latestEdition) + + fun getInsertUri(sone: Sone): FreenetURI? = (sone.identity as? OwnIdentity)?.insertUri + ?.let(::FreenetURI) + ?.sonify(sone.latestEdition) + +} + +private fun FreenetURI.sonify(edition: Long): FreenetURI = + setKeyType("USK") + .setDocName("Sone") + .setMetaString(emptyArray()) + .setSuggestedEdition(edition) diff --git a/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt b/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt new file mode 100644 index 000000000..cd7d5418d --- /dev/null +++ b/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt @@ -0,0 +1,79 @@ +package net.pterodactylus.sone.core + +import net.pterodactylus.sone.data.impl.IdOnlySone +import net.pterodactylus.sone.freenet.wot.DefaultIdentity +import net.pterodactylus.sone.freenet.wot.DefaultOwnIdentity +import net.pterodactylus.sone.test.createInsertUri +import net.pterodactylus.sone.test.createRequestUri +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.emptyArray +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.nullValue +import kotlin.test.Test + +/** + * Unit test for [SoneUriCreator]. + */ +class SoneUriCreatorTest { + + private val soneUriCreator = SoneUriCreator() + + private val requestUri = soneUriCreator.getRequestUri(sone) + private val insertUri = soneUriCreator.getInsertUri(sone) + + @Test + fun `generated request URI is a USK`() { + assertThat(requestUri.keyType, equalTo("USK")) + } + + @Test + fun `generated request URI has correct doc name`() { + assertThat(requestUri.docName, equalTo("Sone")) + } + + @Test + fun `generated request URI has no meta strings`() { + assertThat(requestUri.allMetaStrings, emptyArray()) + } + + @Test + fun `generated request URI has correct edition`() { + assertThat(requestUri.suggestedEdition, equalTo(123L)) + } + + @Test + fun `insert URI is null if sone’s identity is not an own identity`() { + val remoteSone = object : IdOnlySone("id") { + override fun getIdentity() = DefaultIdentity("id", "name", createRequestUri.toString()) + } + assertThat(soneUriCreator.getInsertUri(remoteSone), nullValue()) + } + + @Test + fun `generated insert URI is a USK`() { + assertThat(insertUri!!.keyType, equalTo("USK")) + } + + @Test + fun `generated insert URI has correct doc name`() { + assertThat(insertUri!!.docName, equalTo("Sone")) + } + + @Test + fun `generated insert URI has no meta strings`() { + assertThat(insertUri!!.allMetaStrings, emptyArray()) + } + + @Test + fun `generated insert URI has correct edition`() { + assertThat(insertUri!!.suggestedEdition, equalTo(123L)) + } + +} + +private val sone = object : IdOnlySone("id") { + override fun getIdentity() = + DefaultOwnIdentity("id", "name", createRequestUri.toString(), createInsertUri.toString()) + + override fun getLatestEdition() = 123L +} diff --git a/src/test/java/net/pterodactylus/sone/core/SoneUriTest.java b/src/test/java/net/pterodactylus/sone/core/SoneUriTest.java deleted file mode 100644 index 3525c9b6a..000000000 --- a/src/test/java/net/pterodactylus/sone/core/SoneUriTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.pterodactylus.sone.core; - -import static freenet.keys.InsertableClientSSK.createRandom; -import static net.pterodactylus.sone.core.SoneUri.create; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import freenet.crypt.DummyRandomSource; -import freenet.keys.FreenetURI; - -import org.junit.Test; - -/** - * Unit test for {@link SoneUri}. - */ -public class SoneUriTest { - - @Test - public void callConstructorForIncreasedTestCoverage() { - new SoneUri(); - } - - @Test - public void returnedUriHasCorrectDocNameAndMetaStrings() { - FreenetURI uri = createRandom(new DummyRandomSource(), "test-0").getURI().uskForSSK(); - assertThat(create(uri.toString()).getDocName(), is("Sone")); - assertThat(create(uri.toString()).getAllMetaStrings(), is(new String[0])); - } - - @Test - public void malformedUriReturnsNull() { - assertThat(create("not a key"), nullValue()); - } - -} diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index 312f158b3..b273b8258 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -32,6 +32,7 @@ val remoteSone2 = createRemoteSone() val localSone1 = createLocalSone() val localSone2 = createLocalSone() +val createRequestUri: FreenetURI get() = InsertableClientSSK.createRandom(DummyRandomSource(), "").uri val createInsertUri: FreenetURI get() = InsertableClientSSK.createRandom(DummyRandomSource(), "").insertURI fun createId() = InsertableClientSSK.createRandom(DummyRandomSource(), "").uri.routingKey.asFreenetBase64 From e92189c58a2b4eea70faf827d37c08171f4b143d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 19:21:27 +0100 Subject: [PATCH 07/87] =?UTF-8?q?=E2=9C=85=20Add=20test=20for=20URI=20crea?= =?UTF-8?q?tor=20being=20created=20by=20Guice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/SoneUriCreatorTest.kt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt b/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt index cd7d5418d..ae8f25b69 100644 --- a/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt +++ b/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt @@ -1,13 +1,16 @@ package net.pterodactylus.sone.core +import com.google.inject.Guice import net.pterodactylus.sone.data.impl.IdOnlySone import net.pterodactylus.sone.freenet.wot.DefaultIdentity import net.pterodactylus.sone.freenet.wot.DefaultOwnIdentity import net.pterodactylus.sone.test.createInsertUri import net.pterodactylus.sone.test.createRequestUri +import net.pterodactylus.sone.test.getInstance import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.emptyArray import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.notNullValue import org.hamcrest.Matchers.nullValue import kotlin.test.Test @@ -69,6 +72,12 @@ class SoneUriCreatorTest { assertThat(insertUri!!.suggestedEdition, equalTo(123L)) } + @Test + fun `creator can be created by guice`() { + val injector = Guice.createInjector() + assertThat(injector.getInstance(), notNullValue()) + } + } private val sone = object : IdOnlySone("id") { From 4c557b9a7feed99fc00609b91ab59254d93fec97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 19:22:06 +0100 Subject: [PATCH 08/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20useless=20javadoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/core/Core.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 18588a180..ca08ae1a9 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -180,22 +180,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, private final MetricRegistry metricRegistry; private final Histogram configurationSaveTimeHistogram; - /** - * Creates a new core. - * - * @param configuration - * The configuration of the core - * @param freenetInterface - * The freenet interface - * @param identityManager - * The identity manager - * @param webOfTrustUpdater - * The WebOfTrust updater - * @param eventBus - * The event bus - * @param database - * The database - */ @Inject public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database, MetricRegistry metricRegistry) { super("Sone Core"); From 76f80f2904bc44149bda93e7c93e90568f2e4a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 19:23:25 +0100 Subject: [PATCH 09/87] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20URI=20creator?= =?UTF-8?q?=20in=20inserter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/Core.java | 7 ++-- .../pterodactylus/sone/core/SoneInserter.java | 20 ++++------- .../pterodactylus/sone/core/SoneUriCreator.kt | 4 +-- .../net/pterodactylus/sone/main/SoneModule.kt | 2 ++ .../net/pterodactylus/sone/core/CoreTest.kt | 6 ++-- .../sone/core/SoneInserterTest.kt | 35 +++++++++---------- .../pterodactylus/sone/main/SoneModuleTest.kt | 5 +++ 7 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index ca08ae1a9..ce5daa2f0 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -180,8 +180,10 @@ public class Core extends AbstractService implements SoneProvider, PostProvider, private final MetricRegistry metricRegistry; private final Histogram configurationSaveTimeHistogram; + private final SoneUriCreator soneUriCreator; + @Inject - public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database, MetricRegistry metricRegistry) { + public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator) { super("Sone Core"); this.configuration = configuration; this.freenetInterface = freenetInterface; @@ -193,6 +195,7 @@ public Core(Configuration configuration, FreenetInterface freenetInterface, Iden this.eventBus = eventBus; this.database = database; this.metricRegistry = metricRegistry; + this.soneUriCreator = soneUriCreator; preferences = new Preferences(eventBus); this.configurationSaveTimeHistogram = metricRegistry.histogram("configuration.save.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0))); } @@ -610,7 +613,7 @@ public Sone addLocalSone(OwnIdentity ownIdentity) { sone.setLatestEdition(fromNullable(tryParse(property)).or(0L)); sone.setClient(new Client("Sone", SonePlugin.getPluginVersion())); sone.setKnown(true); - SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, metricRegistry, ownIdentity.getId()); + SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, metricRegistry, soneUriCreator, ownIdentity.getId()); soneInserter.insertionDelayChanged(new InsertionDelayChangedEvent(preferences.getInsertionDelay())); eventBus.register(soneInserter); synchronized (soneInserters) { diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index f18776beb..ba13305ec 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -27,7 +27,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; -import java.net.MalformedURLException; import java.nio.charset.Charset; import java.util.HashMap; import java.util.HashSet; @@ -49,7 +48,6 @@ import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.main.SonePlugin; import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.service.AbstractService; @@ -107,6 +105,7 @@ public class SoneInserter extends AbstractService { private final FreenetInterface freenetInterface; private final SoneModificationDetector soneModificationDetector; + private final SoneUriCreator soneUriCreator; private final long delay; private final String soneId; private final Histogram soneInsertDurationHistogram; @@ -124,8 +123,8 @@ public class SoneInserter extends AbstractService { * @param soneId * The ID of the Sone to insert */ - public SoneInserter(final Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, final String soneId) { - this(core, eventBus, freenetInterface, metricRegistry, soneId, new SoneModificationDetector(new LockableFingerprintProvider() { + public SoneInserter(final Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator, final String soneId) { + this(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, soneId, new SoneModificationDetector(new LockableFingerprintProvider() { @Override public boolean isLocked() { Sone sone = core.getSone(soneId); @@ -147,13 +146,14 @@ public String getFingerprint() { } @VisibleForTesting - SoneInserter(Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, String soneId, SoneModificationDetector soneModificationDetector, long delay) { + SoneInserter(Core core, EventBus eventBus, FreenetInterface freenetInterface, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator, String soneId, SoneModificationDetector soneModificationDetector, long delay) { super("Sone Inserter for “" + soneId + "”", false); this.core = core; this.eventBus = eventBus; this.freenetInterface = freenetInterface; this.soneInsertDurationHistogram = metricRegistry.histogram("sone.insert.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0))); this.soneInsertErrorMeter = metricRegistry.meter("sone.insert.errors"); + this.soneUriCreator = soneUriCreator; this.soneId = soneId; this.soneModificationDetector = soneModificationDetector; this.delay = delay; @@ -238,7 +238,7 @@ protected void serviceRun() { long insertTime = currentTimeMillis(); eventBus.post(new SoneInsertingEvent(sone)); Stopwatch stopwatch = Stopwatch.createStarted(); - FreenetURI finalUri = freenetInterface.insertDirectory(getSoneInsertUri(sone), insertInformation.generateManifestEntries(), "index.html"); + FreenetURI finalUri = freenetInterface.insertDirectory(soneUriCreator.getInsertUri(sone), insertInformation.generateManifestEntries(), "index.html"); stopwatch.stop(); soneInsertDurationHistogram.update(stopwatch.elapsed(MICROSECONDS)); eventBus.post(new SoneInsertedEvent(sone, stopwatch.elapsed(MILLISECONDS), insertInformation.getFingerprint())); @@ -286,14 +286,6 @@ public void insertionDelayChanged(InsertionDelayChangedEvent insertionDelayChang setInsertionDelay(insertionDelayChangedEvent.getInsertionDelay()); } - private FreenetURI getSoneInsertUri(Sone sone) throws MalformedURLException { - return new FreenetURI(((OwnIdentity) sone.getIdentity()).getInsertUri()) - .setKeyType("USK") - .setDocName("Sone") - .setMetaString(new String[0]) - .setSuggestedEdition(sone.getLatestEdition()); - } - /** * Container for information that are required to insert a Sone. This * container merely exists to copy all relevant data without holding a lock diff --git a/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt b/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt index 115c748bc..183303a02 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/SoneUriCreator.kt @@ -7,13 +7,13 @@ import net.pterodactylus.sone.freenet.wot.OwnIdentity /** * Injectable helper class that can create request and insert URIs for [Sones][Sone]. */ -class SoneUriCreator { +open class SoneUriCreator { fun getRequestUri(sone: Sone): FreenetURI = sone.identity.requestUri .let(::FreenetURI) .sonify(sone.latestEdition) - fun getInsertUri(sone: Sone): FreenetURI? = (sone.identity as? OwnIdentity)?.insertUri + open fun getInsertUri(sone: Sone): FreenetURI? = (sone.identity as? OwnIdentity)?.insertUri ?.let(::FreenetURI) ?.sonify(sone.latestEdition) diff --git a/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt b/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt index 749de0d73..1f92a93ba 100644 --- a/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt +++ b/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt @@ -7,6 +7,7 @@ import com.google.inject.* import com.google.inject.matcher.* import com.google.inject.name.Names.* import com.google.inject.spi.* +import net.pterodactylus.sone.core.SoneUriCreator import net.pterodactylus.sone.database.* import net.pterodactylus.sone.database.memory.* import net.pterodactylus.sone.freenet.* @@ -61,6 +62,7 @@ open class SoneModule(private val sonePlugin: SonePlugin, private val eventBus: bind(MetricRegistry::class.java).`in`(Singleton::class.java) bind(WebOfTrustConnector::class.java).to(PluginWebOfTrustConnector::class.java).`in`(Singleton::class.java) bind(TickerShutdown::class.java).`in`(Singleton::class.java) + bind(SoneUriCreator::class.java).`in`(Singleton::class.java) bindListener(Matchers.any(), object : TypeListener { override fun hear(typeLiteral: TypeLiteral, typeEncounter: TypeEncounter) { diff --git a/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt index 9eb123c4b..39273d016 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt @@ -52,7 +52,8 @@ class CoreTest { val eventBus = mock() val database = mock() val metricRegistry = MetricRegistry() - val core = Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry) + val soneUriCreator = SoneUriCreator() + val core = Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry, soneUriCreator) val ownIdentity = mock() val identity = mock() whenever(identity.id).thenReturn("sone-id") @@ -169,7 +170,8 @@ class CoreTest { val webOfTrustUpdater = mock() val database = mock() val metricRegistry = MetricRegistry() - return Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry) + val soneUriCreator = SoneUriCreator() + return Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry, soneUriCreator) } } diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt index 7d0655275..3da9e88bc 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/SoneInserterTest.kt @@ -38,6 +38,9 @@ class SoneInserterTest { private val core = mock() private val eventBus = mock() private val freenetInterface = mock() + private val soneUriCreator = object : SoneUriCreator() { + override fun getInsertUri(sone: Sone): FreenetURI = expectedInsertUri + } @Before fun setupCore() { @@ -49,7 +52,7 @@ class SoneInserterTest { @Test fun `insertion delay is forwarded to sone inserter`() { val eventBus = AsyncEventBus(directExecutor()) - eventBus.register(SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId")) + eventBus.register(SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId")) eventBus.post(InsertionDelayChangedEvent(15)) assertThat(SoneInserter.getInsertionDelay().get(), equalTo(15)) } @@ -68,27 +71,27 @@ class SoneInserterTest { fun `isModified is true if modification detector says so`() { val soneModificationDetector = mock() whenever(soneModificationDetector.isModified).thenReturn(true) - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) assertThat(soneInserter.isModified, equalTo(true)) } @Test fun `isModified is false if modification detector says so`() { val soneModificationDetector = mock() - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) assertThat(soneInserter.isModified, equalTo(false)) } @Test fun `last fingerprint is stored correctly`() { - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId") + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId") soneInserter.lastInsertFingerprint = "last-fingerprint" assertThat(soneInserter.lastInsertFingerprint, equalTo("last-fingerprint")) } @Test fun `sone inserter stops when it should`() { - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId") + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId") soneInserter.stop() soneInserter.serviceRun() } @@ -100,7 +103,7 @@ class SoneInserterTest { val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenReturn(finalUri) - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) doAnswer { soneInserter.stop() null @@ -121,7 +124,7 @@ class SoneInserterTest { val sone = createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenAnswer { soneInserter.stop() finalUri @@ -141,7 +144,7 @@ class SoneInserterTest { fun `sone inserter does not insert sone if it is not eligible`() { createSone(insertUri) val soneModificationDetector = mock() - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) Thread(Runnable { try { Thread.sleep(500) @@ -161,7 +164,7 @@ class SoneInserterTest { val sone = createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) val soneException = SoneException(Exception()) whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenAnswer { soneInserter.stop() @@ -181,7 +184,7 @@ class SoneInserterTest { @Test fun `sone inserter exits if sone is unknown`() { val soneModificationDetector = mock() - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) whenever(core.getSone("SoneId")).thenReturn(null) soneInserter.serviceRun() @@ -190,7 +193,7 @@ class SoneInserterTest { @Test fun `sone inserter catches exception and continues`() { val soneModificationDetector = mock() - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) val stopInserterAndThrowException = Answer> { soneInserter.stop() throw NullPointerException() @@ -243,7 +246,7 @@ class SoneInserterTest { val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenReturn(finalUri) - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry,"SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) doAnswer { soneInserter.stop() null @@ -258,7 +261,7 @@ class SoneInserterTest { createSone(insertUri) val soneModificationDetector = mock() whenever(soneModificationDetector.isEligibleForInsert).thenReturn(true) - val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, "SoneId", soneModificationDetector, 1) + val soneInserter = SoneInserter(core, eventBus, freenetInterface, metricRegistry, soneUriCreator, "SoneId", soneModificationDetector, 1) whenever(freenetInterface.insertDirectory(eq(expectedInsertUri), any>(), eq("index.html"))).thenAnswer { soneInserter.stop() throw SoneException(Exception()) @@ -273,8 +276,4 @@ class SoneInserterTest { } val insertUri = createInsertUri -val expectedInsertUri: FreenetURI = FreenetURI(insertUri.toString()) - .setKeyType("USK") - .setDocName("Sone") - .setMetaString(kotlin.emptyArray()) - .setSuggestedEdition(0) +val expectedInsertUri = createInsertUri diff --git a/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt b/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt index c62833474..676ca6b59 100644 --- a/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt @@ -228,4 +228,9 @@ class SoneModuleTest { injector.verifySingletonInstance() } + @Test + fun `sone URI creator is created as singleton`() { + injector.verifySingletonInstance() + } + } From b47ccc9c7796fa160777a03aef49f1168623fbc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 20:20:05 +0100 Subject: [PATCH 10/87] =?UTF-8?q?=F0=9F=94=87=20Silence=20logger=20during?= =?UTF-8?q?=20freenet=20interface=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/core/FreenetInterfaceTest.kt | 4 +++ .../net/pterodactylus/sone/test/Logging.kt | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/test/kotlin/net/pterodactylus/sone/test/Logging.kt diff --git a/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt index e02cddb79..81b4e041c 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt @@ -43,6 +43,10 @@ class FreenetInterfaceTest { @JvmField val expectionException: ExpectedException = ExpectedException.none() + @Rule + @JvmField + val silencedLogging = silencedLogging() + @Suppress("UnstableApiUsage") private val eventBus = mock() private val node = mock() diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Logging.kt b/src/test/kotlin/net/pterodactylus/sone/test/Logging.kt new file mode 100644 index 000000000..2a2c6b7f4 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/test/Logging.kt @@ -0,0 +1,25 @@ +package net.pterodactylus.sone.test + +import org.junit.rules.TestRule +import org.junit.runners.model.Statement +import java.util.logging.Level +import java.util.logging.Logger.getLogger + +/** + * Silences the `net.pterodactylus.sone` [logger][java.util.logging.Logger] during a test. + */ +fun silencedLogging() = TestRule { base, _ -> + object : Statement() { + override fun evaluate() { + getLogger("net.pterodactylus.sone").let { logger -> + val oldLevel = logger.level + logger.level = Level.OFF + try { + base.evaluate() + } finally { + logger.level = oldLevel + } + } + } + } +} From 13d44dc8b6b98fe12a8058e081d665606622d49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 20:22:21 +0100 Subject: [PATCH 11/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20useless=20javadoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/FreenetInterface.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java index a01a2bbaf..7702d846b 100644 --- a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java +++ b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java @@ -104,14 +104,6 @@ public class FreenetInterface { private final RequestClient imageInserts = new RequestClientBuilder().realTime().build(); private final RequestClient imageLoader = new RequestClientBuilder().realTime().build(); - /** - * Creates a new Freenet interface. - * - * @param eventBus - * The event bus - * @param node - * The node to interact with - */ @Inject public FreenetInterface(EventBus eventBus, Node node) { this.eventBus = eventBus; From f3bc759f7876334bdb2184d756f8f1fc279fff52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 11 Feb 2020 20:22:49 +0100 Subject: [PATCH 12/87] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20URI=20creator?= =?UTF-8?q?=20to=20create=20Sone=20URIs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/core/FreenetInterface.java | 9 ++++++--- .../sone/core/FreenetInterfaceTest.kt | 16 ++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java index 7702d846b..35ac6a19f 100644 --- a/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java +++ b/src/main/java/net/pterodactylus/sone/core/FreenetInterface.java @@ -91,6 +91,8 @@ public class FreenetInterface { /** The node to interact with. */ private final Node node; + private final SoneUriCreator soneUriCreator; + /** The high-level client to use for requests. */ private final HighLevelSimpleClient client; private final RequestClient requestClient = new RequestClientBuilder().realTime().build(); @@ -105,9 +107,10 @@ public class FreenetInterface { private final RequestClient imageLoader = new RequestClientBuilder().realTime().build(); @Inject - public FreenetInterface(EventBus eventBus, Node node) { + public FreenetInterface(EventBus eventBus, Node node, SoneUriCreator soneUriCreator) { this.eventBus = eventBus; this.node = node; + this.soneUriCreator = soneUriCreator; this.client = node.clientCore.makeClient(RequestStarter.INTERACTIVE_PRIORITY_CLASS, false, true); } @@ -283,9 +286,9 @@ public void unregisterUsk(Sone sone) { } try { logger.log(Level.FINEST, String.format("Unsubscribing from USK for %s…", sone)); - node.clientCore.uskManager.unsubscribe(USK.create(sone.getRequestUri()), uskCallback); + node.clientCore.uskManager.unsubscribe(USK.create(soneUriCreator.getRequestUri(sone)), uskCallback); } catch (MalformedURLException mue1) { - logger.log(Level.FINE, String.format("Could not unsubscribe USK “%s”!", sone.getRequestUri()), mue1); + logger.log(Level.FINE, String.format("Could not unsubscribe USK “%s”!", soneUriCreator.getRequestUri(sone)), mue1); } } diff --git a/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt index 81b4e041c..a7b187d71 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt @@ -16,6 +16,7 @@ import net.pterodactylus.sone.core.FreenetInterface.* import net.pterodactylus.sone.core.event.* import net.pterodactylus.sone.data.* import net.pterodactylus.sone.data.impl.* +import net.pterodactylus.sone.freenet.wot.DefaultIdentity import net.pterodactylus.sone.test.* import net.pterodactylus.sone.test.Matchers.* import net.pterodactylus.sone.test.TestUtil.* @@ -64,6 +65,7 @@ class FreenetInterfaceTest { private val fetchResult = mock() private val backgroundFetchCallback = mock() private val clientGetter = mock() + private val soneUriCreator = SoneUriCreator() private val freenetInterface: FreenetInterface init { @@ -72,7 +74,7 @@ class FreenetInterfaceTest { setField(node, "random", randomSource) setField(nodeClientCore, "uskManager", uskManager) setField(nodeClientCore, "clientContext", mock()) - freenetInterface = FreenetInterface(eventBus, node) + freenetInterface = FreenetInterface(eventBus, node, soneUriCreator) insertToken = freenetInterface.InsertToken(image) insertToken.setBucket(bucket) } @@ -87,7 +89,7 @@ class FreenetInterfaceTest { fun setupSone() { val insertSsk = createRandom(randomSource, "test-0") whenever(sone.id).thenReturn(insertSsk.uri.routingKey.asFreenetBase64) - whenever(sone.requestUri).thenReturn(insertSsk.uri.uskForSSK()) + whenever(sone.identity).thenReturn(DefaultIdentity("id", "name", insertSsk.uri.toString())) } @Before @@ -188,7 +190,6 @@ class FreenetInterfaceTest { @Test fun `sone with wrong request uri will not be subscribed`() { - whenever(sone.requestUri).thenReturn(FreenetURI("KSK@GPLv3.txt")) freenetInterface.registerUsk(FreenetURI("KSK@GPLv3.txt"), null) verify(uskManager, never()).subscribe(any(USK::class.java), any(USKCallback::class.java), anyBoolean(), any(RequestClient::class.java)) } @@ -262,16 +263,15 @@ class FreenetInterfaceTest { } @Test - fun `unregistering aregistered sone unregisters the sone`() { - freenetInterface.registerActiveUsk(sone.requestUri, mock()) + fun `unregistering a registered sone unregisters the sone`() { + freenetInterface.registerActiveUsk(soneUriCreator.getRequestUri(sone), mock()) freenetInterface.unregisterUsk(sone) verify(uskManager).unsubscribe(any(USK::class.java), any(USKCallback::class.java)) } @Test - fun `unregistering asone with awrong request key will not unsubscribe`() { - whenever(sone.requestUri).thenReturn(FreenetURI("KSK@GPLv3.txt")) - freenetInterface.registerUsk(sone.requestUri, null) + fun `unregistering a sone with a wrong request key will not unsubscribe`() { + freenetInterface.registerUsk(FreenetURI("KSK@GPLv3.txt"), null) freenetInterface.unregisterUsk(sone) verify(uskManager, never()).unsubscribe(any(USK::class.java), any(USKCallback::class.java)) } From 3380cd5d5052c50aacbdc3b767c3ed861a3ea81f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Wed, 12 Feb 2020 21:40:15 +0100 Subject: [PATCH 13/87] =?UTF-8?q?=F0=9F=8E=A8=20Use=20try-with-resources?= =?UTF-8?q?=20instead=20of=20Closer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/core/SoneDownloaderImpl.java | 11 ++----- .../pterodactylus/sone/core/SoneInserter.java | 29 +++++-------------- .../sone/main/DefaultLoaders.java | 19 ++++-------- 3 files changed, 16 insertions(+), 43 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java b/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java index 0538c8b22..43a87fbf4 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneDownloaderImpl.java @@ -17,7 +17,6 @@ package net.pterodactylus.sone.core; -import static freenet.support.io.Closer.close; import static java.lang.String.format; import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.DAYS; @@ -185,11 +184,8 @@ public Sone fetchSone(Sone sone, FreenetURI soneUri, boolean fetchOnly) { private Sone parseSone(Sone originalSone, FetchResult fetchResult, FreenetURI requestUri) { logger.finest(() -> format("Parsing FetchResult (%d bytes, %s) for %s…", fetchResult.size(), fetchResult.getMimeType(), originalSone)); Bucket soneBucket = fetchResult.asBucket(); - InputStream soneInputStream = null; - try { - soneInputStream = soneBucket.getInputStream(); - Sone parsedSone = soneParser.parseSone(originalSone, - soneInputStream); + try (InputStream soneInputStream = soneBucket.getInputStream()) { + Sone parsedSone = soneParser.parseSone(originalSone, soneInputStream); if (parsedSone != null) { logger.finer(() -> format("Sone %s was successfully parsed.", parsedSone)); parsedSone.setLatestEdition(requestUri.getEdition()); @@ -198,8 +194,7 @@ private Sone parseSone(Sone originalSone, FetchResult fetchResult, FreenetURI re } catch (Exception e1) { logger.log(Level.WARNING, e1, () -> format("Could not parse Sone from %s!", requestUri)); } finally { - close(soneInputStream); - close(soneBucket); + soneBucket.free(); } return null; } diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index ba13305ec..69480da09 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -23,10 +23,7 @@ import static java.util.logging.Logger.getLogger; import static net.pterodactylus.sone.data.Album.NOT_EMPTY; -import java.io.Closeable; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; +import java.io.*; import java.nio.charset.Charset; import java.util.HashMap; import java.util.HashSet; @@ -49,7 +46,6 @@ import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; import net.pterodactylus.sone.main.SonePlugin; -import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.util.template.HtmlFilter; import net.pterodactylus.util.template.ReflectionAccessor; @@ -378,19 +374,13 @@ static class ManifestCreator implements Closeable { } public ManifestElement createManifestElement(String name, String contentType, String templateName) { - InputStreamReader templateInputStreamReader = null; - InputStream templateInputStream = null; Template template; - try { - templateInputStream = getClass().getResourceAsStream(templateName); - templateInputStreamReader = new InputStreamReader(templateInputStream, utf8Charset); + try (InputStream templateInputStream = getClass().getResourceAsStream(templateName); + InputStreamReader templateInputStreamReader = new InputStreamReader(templateInputStream, utf8Charset)) { template = TemplateParser.parse(templateInputStreamReader); - } catch (TemplateException te1) { - logger.log(Level.SEVERE, String.format("Could not parse template “%s”!", templateName), te1); + } catch (IOException | TemplateException e1) { + logger.log(Level.SEVERE, String.format("Could not parse template “%s”!", templateName), e1); return null; - } finally { - Closer.close(templateInputStreamReader); - Closer.close(templateInputStream); } TemplateContext templateContext = templateContextFactory.createTemplateContext(); @@ -398,17 +388,14 @@ public ManifestElement createManifestElement(String name, String contentType, St templateContext.set("currentSone", soneProperties); templateContext.set("currentEdition", core.getUpdateChecker().getLatestEdition()); templateContext.set("version", SonePlugin.getPluginVersion()); - StringWriter writer = new StringWriter(); - try { + try (StringWriter writer = new StringWriter()) { template.render(templateContext, writer); RandomAccessBucket bucket = new ArrayBucket(writer.toString().getBytes(Charsets.UTF_8)); buckets.add(bucket); return new ManifestElement(name, bucket, contentType, bucket.size()); - } catch (TemplateException te1) { - logger.log(Level.SEVERE, String.format("Could not render template “%s”!", templateName), te1); + } catch (IOException | TemplateException e1) { + logger.log(Level.SEVERE, String.format("Could not render template “%s”!", templateName), e1); return null; - } finally { - Closer.close(writer); } } diff --git a/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java b/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java index 2a2bd8cfd..507297016 100644 --- a/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java +++ b/src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java @@ -1,13 +1,10 @@ package net.pterodactylus.sone.main; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.io.UnsupportedEncodingException; +import java.io.*; + import javax.annotation.Nonnull; import net.pterodactylus.sone.web.WebInterface; -import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.template.ClassPathTemplateProvider; import net.pterodactylus.util.template.Template; import net.pterodactylus.util.template.TemplateProvider; @@ -25,17 +22,11 @@ public class DefaultLoaders implements Loaders { @Nonnull @Override public Template loadTemplate(@Nonnull String path) { - InputStream templateInputStream = null; - Reader reader = null; - try { - templateInputStream = getClass().getResourceAsStream(path); - reader = new InputStreamReader(templateInputStream, "UTF-8"); + try (InputStream templateInputStream = getClass().getResourceAsStream(path); + Reader reader = new InputStreamReader(templateInputStream, "UTF-8");) { return parse(reader); - } catch (UnsupportedEncodingException uee1) { + } catch (IOException ioe1) { throw new RuntimeException("UTF-8 not supported."); - } finally { - Closer.close(reader); - Closer.close(templateInputStream); } } From 7c659af64fa9597425563f86eed482f4b4523272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Wed, 12 Feb 2020 21:44:01 +0100 Subject: [PATCH 14/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unnecessary=20req?= =?UTF-8?q?uest=20URI=20from=20inserted=20Sone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/core/SoneInserter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 69480da09..311d00615 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -307,7 +307,6 @@ public InsertInformation(Sone sone) { soneProperties.put("id", sone.getId()); soneProperties.put("name", sone.getName()); soneProperties.put("time", currentTimeMillis()); - soneProperties.put("requestUri", sone.getRequestUri()); soneProperties.put("profile", sone.getProfile()); soneProperties.put("posts", Ordering.from(Post.NEWEST_FIRST).sortedCopy(sone.getPosts())); soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); From a587722cd144900aead772ef8ae16efe7445b60e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 14 Feb 2020 18:46:56 +0100 Subject: [PATCH 15/87] =?UTF-8?q?=F0=9F=94=8A=20Implement=20toString()=20o?= =?UTF-8?q?n=20post=20reply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/database/memory/MemoryPostReply.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java index e4a8f30d0..ef21a7596 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java @@ -177,4 +177,17 @@ public boolean equals(Object object) { return memoryPostReply.id.equals(id); } + @Override + public String toString() { + return "MemoryPostReply{" + + "database=" + database + + ", soneProvider=" + soneProvider + + ", id='" + id + '\'' + + ", soneId='" + soneId + '\'' + + ", time=" + time + + ", text='" + text + '\'' + + ", postId='" + postId + '\'' + + '}'; + } + } From 1079e46155f61df9270f6fb8f9aa65bcc86f57c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 13:01:39 +0100 Subject: [PATCH 16/87] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20in=20te?= =?UTF-8?q?st=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/kotlin/net/pterodactylus/sone/utils/OptionalsTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/OptionalsTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/OptionalsTest.kt index 76dd443d5..f60d45fa4 100644 --- a/src/test/kotlin/net/pterodactylus/sone/utils/OptionalsTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/utils/OptionalsTest.kt @@ -47,7 +47,7 @@ class OptionalsTest { } @Test - fun `null as optional is asent optional`() { + fun `null as optional is absent optional`() { val optional = null.asOptional() assertThat(optional.isPresent, equalTo(false)) } From c1f6e1176c57d470eb98ed45cfa268faff8e4009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 13:03:57 +0100 Subject: [PATCH 17/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20predicat?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/Sone.java | 30 ------------------- 1 file changed, 30 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index e2a7b5bb3..90d03555b 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -32,13 +32,11 @@ import javax.annotation.Nullable; import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.template.SoneAccessor; import freenet.keys.FreenetURI; import com.google.common.base.Function; -import com.google.common.base.Predicate; import com.google.common.primitives.Ints; /** @@ -115,34 +113,6 @@ public int compare(Sone leftSone, Sone rightSone) { } }; - /** Filter to remove Sones that have not been downloaded. */ - public static final Predicate EMPTY_SONE_FILTER = new Predicate() { - - @Override - public boolean apply(Sone sone) { - return (sone != null) && (sone.getTime() != 0); - } - }; - - /** Filter that matches all {@link Sone#isLocal() local Sones}. */ - public static final Predicate LOCAL_SONE_FILTER = new Predicate() { - - @Override - public boolean apply(Sone sone) { - return (sone != null) && (sone.getIdentity() instanceof OwnIdentity); - } - - }; - - /** Filter that matches Sones that have at least one album. */ - public static final Predicate HAS_ALBUM_FILTER = new Predicate() { - - @Override - public boolean apply(Sone sone) { - return (sone != null) && !sone.getRootAlbum().getAlbums().isEmpty(); - } - }; - public static final Function> toAllAlbums = new Function>() { @Override public List apply(@Nullable Sone sone) { From f5c68762e5a00d2cdbc433310f5160bd4c811f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 13:22:17 +0100 Subject: [PATCH 18/87] =?UTF-8?q?=F0=9F=92=9A=20Use=20JVM=20target=201.8?= =?UTF-8?q?=20for=20Kotlin=20compiler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/build.gradle b/build.gradle index 3730ead23..d1bf4d994 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,12 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } +compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + } +} + configurations { provided { dependencies.all { dep -> From 7be5373f355b3184e02a61b39238a89be177c040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 13:27:47 +0100 Subject: [PATCH 19/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20nice=20name=20co?= =?UTF-8?q?mparator=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/Sone.java | 15 --------- .../sone/data/impl/SoneImpl.java | 3 +- .../sone/fcp/GetSonesCommand.java | 6 ++-- .../sone/template/CollectionAccessor.java | 7 +++-- .../net/pterodactylus/sone/data/Sone.kt | 31 +++++++++++++++++++ .../sone/web/pages/CreateSonePage.kt | 2 +- .../sone/web/pages/KnownSonesPage.kt | 2 +- .../pterodactylus/sone/web/pages/LoginPage.kt | 2 +- 8 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 src/main/kotlin/net/pterodactylus/sone/data/Sone.kt diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 90d03555b..114350175 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -32,7 +32,6 @@ import javax.annotation.Nullable; import net.pterodactylus.sone.freenet.wot.Identity; -import net.pterodactylus.sone.template.SoneAccessor; import freenet.keys.FreenetURI; @@ -63,20 +62,6 @@ public enum SoneStatus { downloading, } - /** comparator that sorts Sones by their nice name. */ - public static final Comparator NICE_NAME_COMPARATOR = new Comparator() { - - @Override - public int compare(Sone leftSone, Sone rightSone) { - int diff = SoneAccessor.getNiceName(leftSone).compareToIgnoreCase(SoneAccessor.getNiceName(rightSone)); - if (diff != 0) { - return diff; - } - return leftSone.getId().compareToIgnoreCase(rightSone.getId()); - } - - }; - /** Comparator that sorts Sones by last activity (least recent active first). */ public static final Comparator LAST_ACTIVITY_COMPARATOR = new Comparator() { diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index ceb05b26f..093b038f9 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -21,6 +21,7 @@ import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.logging.Logger.getLogger; +import static net.pterodactylus.sone.data.SoneKt.*; import java.net.MalformedURLException; import java.util.ArrayList; @@ -665,7 +666,7 @@ public synchronized String getFingerprint() { /** {@inheritDoc} */ @Override public int compareTo(Sone sone) { - return NICE_NAME_COMPARATOR.compare(this, sone); + return niceNameComparator().compare(this, sone); } // diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java index 83167cdd6..eedf12044 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetSonesCommand.java @@ -17,6 +17,7 @@ package net.pterodactylus.sone.fcp; +import static net.pterodactylus.sone.data.SoneKt.*; import static net.pterodactylus.sone.fcp.AbstractSoneCommandKt.encodeSones; import java.util.ArrayList; @@ -24,7 +25,8 @@ import java.util.List; import net.pterodactylus.sone.core.Core; -import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.data.*; + import freenet.support.SimpleFieldSet; /** @@ -53,7 +55,7 @@ public Response execute(SimpleFieldSet parameters) { if (sones.size() < startSone) { return new Response("Sones", encodeSones(Collections. emptyList(), "Sones.")); } - Collections.sort(sones, Sone.NICE_NAME_COMPARATOR); + sones.sort(niceNameComparator()); return new Response("Sones", encodeSones(sones.subList(startSone, (maxSones == -1) ? sones.size() : Math.min(startSone + maxSones, sones.size())), "Sones.")); } diff --git a/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java b/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java index f17070174..f960b0817 100644 --- a/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/CollectionAccessor.java @@ -17,12 +17,13 @@ package net.pterodactylus.sone.template; +import static net.pterodactylus.sone.data.SoneKt.*; + import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.List; -import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.data.*; import net.pterodactylus.util.template.Accessor; import net.pterodactylus.util.template.ReflectionAccessor; import net.pterodactylus.util.template.TemplateContext; @@ -52,7 +53,7 @@ public Object get(TemplateContext templateContext, Object object, String member) } sones.add((Sone) sone); } - Collections.sort(sones, Sone.NICE_NAME_COMPARATOR); + sones.sort(niceNameComparator()); StringBuilder soneNames = new StringBuilder(); for (Sone sone : sones) { if (soneNames.length() > 0) { diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt new file mode 100644 index 000000000..eb67dff3d --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt @@ -0,0 +1,31 @@ +/** + * Sone - Sone.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +import net.pterodactylus.sone.template.* +import java.util.Comparator.* + +private val caseInsensitiveCompare = { left: String, right: String -> left.compareTo(right, true) } + +/** + * Comparator that sorts Sones by their [nice name][SoneAccessor.getNiceName] + * and, failing that, by [ID][Sone.id]. + */ +@get:JvmName("niceNameComparator") // TODO: remove once Sone is 100% Kotlin +val niceNameComparator: Comparator = + comparing(SoneAccessor::getNiceName, caseInsensitiveCompare).thenComparing(Sone::id) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt index 0b690da51..959788b2d 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt @@ -21,7 +21,7 @@ class CreateSonePage @Inject constructor(webInterface: WebInterface, loaders: Lo private val logger = Logger.getLogger(CreateSonePage::class.java.name) override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) { - templateContext["sones"] = soneRequest.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR) + templateContext["sones"] = soneRequest.core.localSones.sortedWith(niceNameComparator) templateContext["identitiesWithoutSone"] = soneRequest.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}".toLowerCase() } if (soneRequest.isPOST) { val identity = soneRequest.httpRequest.getPartAsStringFailsafe("identity", 43) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt index ae0d7d108..260f35b54 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt @@ -29,7 +29,7 @@ class KnownSonesPage @Inject constructor(webInterface: WebInterface, loaders: Lo .sortedWith( when (soneRequest.parameters["sort"]) { "images" -> Sone.IMAGE_COUNT_COMPARATOR - "name" -> Sone.NICE_NAME_COMPARATOR.reversed() + "name" -> niceNameComparator.reversed() "posts" -> Sone.POST_COUNT_COMPARATOR else -> Sone.LAST_ACTIVITY_COMPARATOR }.let { comparator -> diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt index 5be34ac05..624432578 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt @@ -26,7 +26,7 @@ class LoginPage @Inject constructor(webInterface: WebInterface, loaders: Loaders redirectTo(target) } } - templateContext["sones"] = soneRequest.core.localSones.sortedWith(Sone.NICE_NAME_COMPARATOR) + templateContext["sones"] = soneRequest.core.localSones.sortedWith(niceNameComparator) templateContext["identitiesWithoutSone"] = soneRequest.core.identityManager.allOwnIdentities.filterNot { "Sone" in it.contexts }.sortedBy { "${it.nickname}@${it.id}" } } From afa3d007f883a710a94f74d21c179a006974bc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 20:48:44 +0100 Subject: [PATCH 20/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20last=20activity?= =?UTF-8?q?=20comparator=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/data/Sone.java | 9 --------- src/main/kotlin/net/pterodactylus/sone/data/Sone.kt | 8 ++++++++ .../net/pterodactylus/sone/web/pages/KnownSonesPage.kt | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 114350175..fa6f980ed 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -62,15 +62,6 @@ public enum SoneStatus { downloading, } - /** Comparator that sorts Sones by last activity (least recent active first). */ - public static final Comparator LAST_ACTIVITY_COMPARATOR = new Comparator() { - - @Override - public int compare(Sone firstSone, Sone secondSone) { - return (int) Math.min(Integer.MAX_VALUE, Math.max(Integer.MIN_VALUE, secondSone.getTime() - firstSone.getTime())); - } - }; - /** Comparator that sorts Sones by numbers of posts (descending). */ public static final Comparator POST_COUNT_COMPARATOR = new Comparator() { diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt index eb67dff3d..78a17f55d 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt @@ -29,3 +29,11 @@ private val caseInsensitiveCompare = { left: String, right: String -> left.compa @get:JvmName("niceNameComparator") // TODO: remove once Sone is 100% Kotlin val niceNameComparator: Comparator = comparing(SoneAccessor::getNiceName, caseInsensitiveCompare).thenComparing(Sone::id) + +/** + * Comparator that sorts Sones by their [last activity][Sone.getTime], least + * recently active Sones first. + */ +@get:JvmName("lastActivityComparator") // TODO: remove once Sone is 100% Kotlin +val lastActivityComparator: Comparator = + comparing(Sone::getTime).reversed() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt index 260f35b54..439a163b5 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt @@ -31,7 +31,7 @@ class KnownSonesPage @Inject constructor(webInterface: WebInterface, loaders: Lo "images" -> Sone.IMAGE_COUNT_COMPARATOR "name" -> niceNameComparator.reversed() "posts" -> Sone.POST_COUNT_COMPARATOR - else -> Sone.LAST_ACTIVITY_COMPARATOR + else -> lastActivityComparator }.let { comparator -> when (soneRequest.parameters["order"]) { "asc" -> comparator.reversed() From 9b0b5b760adf41d52603a4b65f3e5f3220da28eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 21:04:33 +0100 Subject: [PATCH 21/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20post=20count=20c?= =?UTF-8?q?omparator=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/Sone.java | 12 ---- .../net/pterodactylus/sone/data/Sone.kt | 11 +++ .../sone/web/pages/KnownSonesPage.kt | 2 +- .../net/pterodactylus/sone/data/SoneTest.kt | 68 +++++++++++++++++++ 4 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index fa6f980ed..4dd9a8e60 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -62,18 +62,6 @@ public enum SoneStatus { downloading, } - /** Comparator that sorts Sones by numbers of posts (descending). */ - public static final Comparator POST_COUNT_COMPARATOR = new Comparator() { - - /** - * {@inheritDoc} - */ - @Override - public int compare(Sone leftSone, Sone rightSone) { - return (leftSone.getPosts().size() != rightSone.getPosts().size()) ? (rightSone.getPosts().size() - leftSone.getPosts().size()) : (rightSone.getReplies().size() - leftSone.getReplies().size()); - } - }; - /** Comparator that sorts Sones by number of images (descending). */ public static final Comparator IMAGE_COUNT_COMPARATOR = new Comparator() { diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt index 78a17f55d..c44d6e8b8 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt @@ -37,3 +37,14 @@ val niceNameComparator: Comparator = @get:JvmName("lastActivityComparator") // TODO: remove once Sone is 100% Kotlin val lastActivityComparator: Comparator = comparing(Sone::getTime).reversed() + +/** + * Comparator that sorts Sones by their [post count][Sone.getPosts] (most posts + * first) and, failing that, by their [reply count][Sone.getReplies] (most + * replies first). + */ +@get:JvmName("postCountComparator") // TODO: remove once Sone is 100% Kotlin +val postCountComparator: Comparator = + comparing { it.posts.size } + .thenComparing { it.replies.size } + .reversed() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt index 439a163b5..392dd93fb 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt @@ -30,7 +30,7 @@ class KnownSonesPage @Inject constructor(webInterface: WebInterface, loaders: Lo when (soneRequest.parameters["sort"]) { "images" -> Sone.IMAGE_COUNT_COMPARATOR "name" -> niceNameComparator.reversed() - "posts" -> Sone.POST_COUNT_COMPARATOR + "posts" -> postCountComparator else -> lastActivityComparator }.let { comparator -> when (soneRequest.parameters["order"]) { diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt new file mode 100644 index 000000000..5cc0ee2cc --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt @@ -0,0 +1,68 @@ +/** + * Sone - SoneTest.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +import net.pterodactylus.sone.data.impl.* +import net.pterodactylus.sone.test.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import kotlin.test.* + +/** + * Unit test for functions in Sone. + */ +class SoneTest { + + @Test + fun `post count comparator sorts sones with different number of posts correctly`() { + val sone1 = object : IdOnlySone("1") { + override fun getPosts() = listOf(createPost(), createPost()) + } + val sone2 = object : IdOnlySone("2") { + override fun getPosts() = listOf(createPost(), createPost(), createPost()) + } + assertThat(postCountComparator.compare(sone1, sone2), greaterThan(0)) + } + + @Test + fun `post count comparator compares replies if posts are not different`() { + val sone1 = object : IdOnlySone("1") { + override fun getPosts() = listOf(createPost(), createPost()) + override fun getReplies() = setOf(emptyPostReply(), emptyPostReply()) + } + val sone2 = object : IdOnlySone("2") { + override fun getPosts() = listOf(createPost(), createPost()) + override fun getReplies() = setOf(emptyPostReply(), emptyPostReply(), emptyPostReply()) + } + assertThat(postCountComparator.compare(sone1, sone2), greaterThan(0)) + } + + @Test + fun `post count comparator sorts sone with same amount of posts and replies as equal`() { + val sone1 = object : IdOnlySone("1") { + override fun getPosts() = listOf(createPost(), createPost()) + override fun getReplies() = setOf(emptyPostReply(), emptyPostReply()) + } + val sone2 = object : IdOnlySone("2") { + override fun getPosts() = listOf(createPost(), createPost()) + override fun getReplies() = setOf(emptyPostReply(), emptyPostReply()) + } + assertThat(postCountComparator.compare(sone1, sone2), equalTo(0)) + } + +} From e03a570dffcf1277952bc03b5e1fb54c50e4394a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 21:10:39 +0100 Subject: [PATCH 22/87] =?UTF-8?q?=E2=9C=85=20Add=20test=20for=20nice-name?= =?UTF-8?q?=20comparator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/SoneTest.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt index 5cc0ee2cc..8866b5128 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt @@ -28,6 +28,39 @@ import kotlin.test.* */ class SoneTest { + @Test + fun `nice name comparator correctly compares Sones by their nice name`() { + val sone1 = object : IdOnlySone("1") { + override fun getProfile() = Profile(this).apply { firstName = "Left" } + } + val sone2 = object : IdOnlySone("2") { + override fun getProfile() = Profile(this).apply { firstName = "Right" } + } + assertThat(niceNameComparator.compare(sone1, sone2), lessThan(0)) + } + + @Test + fun `nice name comparator correctly compares Sones by their ID if nice name is the same`() { + val sone1 = object : IdOnlySone("1") { + override fun getProfile() = Profile(this).apply { firstName = "Left" } + } + val sone2 = object : IdOnlySone("2") { + override fun getProfile() = Profile(this).apply { firstName = "Left" } + } + assertThat(niceNameComparator.compare(sone1, sone2), lessThan(0)) + } + + @Test + fun `nice name comparator treats Sones as equal if nice name and ID are the same`() { + val sone1 = object : IdOnlySone("1") { + override fun getProfile() = Profile(this).apply { firstName = "Left" } + } + val sone2 = object : IdOnlySone("1") { + override fun getProfile() = Profile(this).apply { firstName = "Left" } + } + assertThat(niceNameComparator.compare(sone1, sone2), equalTo(0)) + } + @Test fun `post count comparator sorts sones with different number of posts correctly`() { val sone1 = object : IdOnlySone("1") { From 141f56a62e39c13b1eb14fe6e5c3b8b7bc062168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 21:14:57 +0100 Subject: [PATCH 23/87] =?UTF-8?q?=E2=9C=85=20Add=20test=20for=20last-activ?= =?UTF-8?q?ity=20comparator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/SoneTest.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt index 8866b5128..6b7e51d5b 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt @@ -61,6 +61,28 @@ class SoneTest { assertThat(niceNameComparator.compare(sone1, sone2), equalTo(0)) } + @Test + fun `last activity comparator correctly compares Sones by last activity`() { + val sone1 = object : IdOnlySone("1") { + override fun getTime() = 1000L + } + val sone2 = object : IdOnlySone("2") { + override fun getTime() = 2000L + } + assertThat(lastActivityComparator.compare(sone1, sone2), greaterThan(0)) + } + + @Test + fun `last activity comparator treats Sones as equal if last activity is the same`() { + val sone1 = object : IdOnlySone("1") { + override fun getTime() = 1000L + } + val sone2 = object : IdOnlySone("2") { + override fun getTime() = 1000L + } + assertThat(lastActivityComparator.compare(sone1, sone2), equalTo(0)) + } + @Test fun `post count comparator sorts sones with different number of posts correctly`() { val sone1 = object : IdOnlySone("1") { From 07d3fa7dac96e8a5eff41487d647974df8c26f8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 16 Feb 2020 21:49:23 +0100 Subject: [PATCH 24/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20image=20count=20?= =?UTF-8?q?comparator=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/Sone.java | 18 --------------- .../net/pterodactylus/sone/data/Sone.kt | 3 +++ .../sone/web/pages/KnownSonesPage.kt | 2 +- .../net/pterodactylus/sone/data/SoneTest.kt | 22 +++++++++++++++++++ .../net/pterodactylus/sone/test/Mocks.kt | 3 +++ 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 4dd9a8e60..836baa19d 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -18,13 +18,11 @@ package net.pterodactylus.sone.data; import static com.google.common.collect.FluentIterable.from; -import static java.util.Arrays.asList; import static net.pterodactylus.sone.data.Album.FLATTENER; import static net.pterodactylus.sone.data.Album.IMAGES; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Set; @@ -36,7 +34,6 @@ import freenet.keys.FreenetURI; import com.google.common.base.Function; -import com.google.common.primitives.Ints; /** * A Sone defines everything about a user: her profile, her status updates, her @@ -62,21 +59,6 @@ public enum SoneStatus { downloading, } - /** Comparator that sorts Sones by number of images (descending). */ - public static final Comparator IMAGE_COUNT_COMPARATOR = new Comparator() { - - /** - * {@inheritDoc} - */ - @Override - public int compare(Sone leftSone, Sone rightSone) { - int rightSoneImageCount = from(asList(rightSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size(); - int leftSoneImageCount = from(asList(leftSone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES).size(); - /* sort descending. */ - return Ints.compare(rightSoneImageCount, leftSoneImageCount); - } - }; - public static final Function> toAllAlbums = new Function>() { @Override public List apply(@Nullable Sone sone) { diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt index c44d6e8b8..3f70ad715 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt @@ -48,3 +48,6 @@ val postCountComparator: Comparator = comparing { it.posts.size } .thenComparing { it.replies.size } .reversed() + +val imageCountComparator: Comparator = + comparing { it.rootAlbum.allImages.size }.reversed() diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt index 392dd93fb..01d1d2274 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPage.kt @@ -28,7 +28,7 @@ class KnownSonesPage @Inject constructor(webInterface: WebInterface, loaders: Lo .filterNot { soneRequest.parameters["filter"] == "not-own" && it.isLocal } .sortedWith( when (soneRequest.parameters["sort"]) { - "images" -> Sone.IMAGE_COUNT_COMPARATOR + "images" -> imageCountComparator "name" -> niceNameComparator.reversed() "posts" -> postCountComparator else -> lastActivityComparator diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt index 6b7e51d5b..1429e544e 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt @@ -120,4 +120,26 @@ class SoneTest { assertThat(postCountComparator.compare(sone1, sone2), equalTo(0)) } + @Test + fun `image count comparator sorts Sones correctly if number of images is different`() { + val sone1 = object : IdOnlySone("1") { + override fun getRootAlbum() = AlbumImpl(this).also { it.addImage(createImage(this)) } + } + val sone2 = object : IdOnlySone("2") { + override fun getRootAlbum() = AlbumImpl(this).also { it.addImage(createImage(this)); it.addImage(createImage(this)) } + } + assertThat(imageCountComparator.compare(sone1, sone2), greaterThan(0)) + } + + @Test + fun `image count comparator treats Sones as equal if number of images is the same`() { + val sone1 = object : IdOnlySone("1") { + override fun getRootAlbum() = AlbumImpl(this).also { it.addImage(createImage(this)) } + } + val sone2 = object : IdOnlySone("2") { + override fun getRootAlbum() = AlbumImpl(this).also { it.addImage(createImage(this)) } + } + assertThat(imageCountComparator.compare(sone1, sone2), equalTo(0)) + } + } diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index b273b8258..65f279a30 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -61,3 +61,6 @@ fun emptyPostReply(text: String = "", post: Post? = createPost(), sone: Sone = r override fun isKnown() = known override fun setKnown(known: Boolean): PostReply = this } + +fun createImage(sone: Sone): Image = + ImageImpl().modify().setSone(sone).update() From d98bd42433b94ba26460ca99495805dca3b06481 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 14:37:50 +0100 Subject: [PATCH 25/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20function=20from?= =?UTF-8?q?=20Sone=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/data/Sone.java | 11 ----------- .../sone/database/memory/MemoryDatabase.kt | 8 ++++---- .../net/pterodactylus/sone/core/SoneParserTest.kt | 2 ++ 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 836baa19d..843400ec4 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -17,9 +17,7 @@ package net.pterodactylus.sone.data; -import static com.google.common.collect.FluentIterable.from; import static net.pterodactylus.sone.data.Album.FLATTENER; -import static net.pterodactylus.sone.data.Album.IMAGES; import java.util.Collection; import java.util.Collections; @@ -67,15 +65,6 @@ public List apply(@Nullable Sone sone) { } }; - public static final Function> toAllImages = new Function>() { - @Override - public List apply(@Nullable Sone sone) { - return (sone == null) ? Collections.emptyList() : - from(FLATTENER.apply(sone.getRootAlbum())) - .transformAndConcat(IMAGES).toList(); - } - }; - /** * Returns the identity of this Sone. * diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt index 8722873aa..e62d968d1 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -31,7 +31,7 @@ import net.pterodactylus.sone.data.PostReply import net.pterodactylus.sone.data.Reply.TIME_COMPARATOR import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.Sone.toAllAlbums -import net.pterodactylus.sone.data.Sone.toAllImages +import net.pterodactylus.sone.data.allImages import net.pterodactylus.sone.data.impl.AlbumBuilderImpl import net.pterodactylus.sone.data.impl.ImageBuilderImpl import net.pterodactylus.sone.database.AlbumBuilder @@ -127,9 +127,9 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio for (album in toAllAlbums.apply(sone)!!) { allAlbums[album.id] = album } - soneImages.putAll(sone.id, toAllImages.apply(sone)!!) - for (image in toAllImages.apply(sone)!!) { - allImages[image.id] = image + sone.rootAlbum.allImages.let { images -> + soneImages.putAll(sone.id, images) + images.forEach { image -> allImages[image.id] = image } } } } diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneParserTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneParserTest.kt index 336d8555d..38419a2fb 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/SoneParserTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/SoneParserTest.kt @@ -5,6 +5,7 @@ import com.google.common.base.Optional.* import freenet.crypt.* import freenet.keys.InsertableClientSSK.* import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.data.impl.AlbumImpl import net.pterodactylus.sone.database.memory.* import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.test.* @@ -41,6 +42,7 @@ class SoneParserTest { whenever(sone.identity).thenReturn(identity) whenever(sone.requestUri).thenAnswer { clientSSK.uri.setKeyType("USK").setDocName("Sone") } whenever(sone.time).thenReturn(currentTimeMillis() - DAYS.toMillis(1)) + whenever(sone.rootAlbum).thenReturn(AlbumImpl(sone)) } @Test From 2b7b9baa754ba9d4a41b0abfaf946bd9146a10c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 14:50:45 +0100 Subject: [PATCH 26/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20another=20functio?= =?UTF-8?q?n=20from=20Sone=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/data/Sone.java | 13 ------------- .../net/pterodactylus/sone/data/Albums.kt | 8 ++++++++ .../sone/database/memory/MemoryDatabase.kt | 8 ++++---- .../net/pterodactylus/sone/data/AlbumsTest.kt | 17 +++++++++++++++++ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Sone.java b/src/main/java/net/pterodactylus/sone/data/Sone.java index 843400ec4..2b0a2eb42 100644 --- a/src/main/java/net/pterodactylus/sone/data/Sone.java +++ b/src/main/java/net/pterodactylus/sone/data/Sone.java @@ -17,10 +17,7 @@ package net.pterodactylus.sone.data; -import static net.pterodactylus.sone.data.Album.FLATTENER; - import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Set; @@ -31,8 +28,6 @@ import freenet.keys.FreenetURI; -import com.google.common.base.Function; - /** * A Sone defines everything about a user: her profile, her status updates, her * replies, her likes and dislikes, etc. @@ -57,14 +52,6 @@ public enum SoneStatus { downloading, } - public static final Function> toAllAlbums = new Function>() { - @Override - public List apply(@Nullable Sone sone) { - return (sone == null) ? Collections.emptyList() : FLATTENER.apply( - sone.getRootAlbum()); - } - }; - /** * Returns the identity of this Sone. * diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt index 0c79a8428..081a2fa11 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt @@ -21,3 +21,11 @@ package net.pterodactylus.sone.data val Album.allImages: Collection get() = images + albums.flatMap { it.allImages } + +/** + * Returns this album and all albums contained in this album (recursively). + * A child album is always listed after its parent. + */ +val Album.allAlbums: List + get() = + listOf(this) + albums.flatMap(Album::allAlbums) diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt index e62d968d1..df65a5a35 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -30,7 +30,7 @@ import net.pterodactylus.sone.data.Post import net.pterodactylus.sone.data.PostReply import net.pterodactylus.sone.data.Reply.TIME_COMPARATOR import net.pterodactylus.sone.data.Sone -import net.pterodactylus.sone.data.Sone.toAllAlbums +import net.pterodactylus.sone.data.allAlbums import net.pterodactylus.sone.data.allImages import net.pterodactylus.sone.data.impl.AlbumBuilderImpl import net.pterodactylus.sone.data.impl.ImageBuilderImpl @@ -123,9 +123,9 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio for (postReply in sone.replies) { allPostReplies[postReply.id] = postReply } - soneAlbums.putAll(sone.id, toAllAlbums.apply(sone)!!) - for (album in toAllAlbums.apply(sone)!!) { - allAlbums[album.id] = album + sone.rootAlbum.allAlbums.let { albums -> + soneAlbums.putAll(sone.id, albums) + albums.forEach { album -> allAlbums[album.id] = album } } sone.rootAlbum.allImages.let { images -> soneImages.putAll(sone.id, images) diff --git a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt index 26af4e998..6044fd1fe 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt @@ -45,4 +45,21 @@ class AlbumsTest { private fun createImage(sone: IdOnlySone, id: String) = ImageImpl(id).modify().setSone(sone).update() + @Test + fun `allAlbums returns itself and all its subalbums`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + val firstNestedAlbum = AlbumImpl(sone) + val secondNestedAlbum = AlbumImpl(sone) + val albumNestedInFirst = AlbumImpl(sone) + album.addAlbum(firstNestedAlbum) + album.addAlbum(secondNestedAlbum) + firstNestedAlbum.addAlbum(albumNestedInFirst) + val albums = album.allAlbums + assertThat(albums, containsInAnyOrder(album, firstNestedAlbum, secondNestedAlbum, albumNestedInFirst)) + assertThat(albums.indexOf(firstNestedAlbum), greaterThan(albums.indexOf(album))) + assertThat(albums.indexOf(secondNestedAlbum), greaterThan(albums.indexOf(album))) + assertThat(albums.indexOf(albumNestedInFirst), greaterThan(albums.indexOf(firstNestedAlbum))) + } + } From 509b81185b3a1e82bb78308640d0d7b6b741d3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 15:36:46 +0100 Subject: [PATCH 27/87] =?UTF-8?q?=F0=9F=94=A5=20Replace=20NOT=5FEMPTY=20wi?= =?UTF-8?q?th=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/Core.java | 4 +- .../pterodactylus/sone/core/SoneInserter.java | 4 +- .../net/pterodactylus/sone/data/Album.java | 26 --------- .../sone/data/impl/SoneImpl.java | 3 +- .../net/pterodactylus/sone/data/Albums.kt | 7 +++ .../net/pterodactylus/sone/data/AlbumsTest.kt | 54 ++++++++++++++++++- 6 files changed, 66 insertions(+), 32 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index ce5daa2f0..7a8f5df4a 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -53,6 +53,7 @@ import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound; import net.pterodactylus.sone.core.event.*; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.AlbumsKt; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; @@ -90,7 +91,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; -import com.google.common.collect.FluentIterable; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @@ -1392,7 +1392,7 @@ private synchronized void saveSone(Sone sone) { configuration.getStringValue(sonePrefix + "/Likes/Reply/" + replyLikeCounter + "/ID").setValue(null); /* save albums. first, collect in a flat structure, top-level first. */ - List albums = FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).toList(); + List albums = AlbumsKt.getAllAlbums(sone.getRootAlbum()); int albumCounter = 0; for (Album album : albums) { diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 311d00615..a67c15d35 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -21,7 +21,6 @@ import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.*; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.data.Album.NOT_EMPTY; import java.io.*; import java.nio.charset.Charset; @@ -41,6 +40,7 @@ import net.pterodactylus.sone.core.event.SoneInsertedEvent; import net.pterodactylus.sone.core.event.SoneInsertingEvent; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.AlbumsKt; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; @@ -312,7 +312,7 @@ public InsertInformation(Sone sone) { soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds())); - soneProperties.put("albums", FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).filter(NOT_EMPTY).toList()); + soneProperties.put("albums", FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).filter(AlbumsKt.notEmpty()::invoke).toList()); manifestCreator = new ManifestCreator(core, soneProperties); } diff --git a/src/main/java/net/pterodactylus/sone/data/Album.java b/src/main/java/net/pterodactylus/sone/data/Album.java index 576c73a9a..efaa470bc 100644 --- a/src/main/java/net/pterodactylus/sone/data/Album.java +++ b/src/main/java/net/pterodactylus/sone/data/Album.java @@ -63,32 +63,6 @@ public List apply(Album album) { } }; - /** - * Filter that removes all albums that do not have any images in any album - * below it. - */ - Predicate NOT_EMPTY = new Predicate() { - - @Override - public boolean apply(Album album) { - /* so, we flatten all albums below the given one and check whether at least one album… */ - return FluentIterable.from(asList(album)).transformAndConcat(FLATTENER).anyMatch(new Predicate() { - - @Override - public boolean apply(Album album) { - /* …contains any inserted images. */ - return !album.getImages().isEmpty() && FluentIterable.from(album.getImages()).allMatch(new Predicate() { - - @Override - public boolean apply(Image input) { - return input.isInserted(); - } - }); - } - }); - } - }; - /** * Returns the ID of this album. * diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index 093b038f9..87bd7a6fc 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -37,6 +37,7 @@ import javax.annotation.Nullable; import net.pterodactylus.sone.data.Album; +import net.pterodactylus.sone.data.AlbumsKt; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; @@ -649,7 +650,7 @@ public synchronized String getFingerprint() { hash.putString("Albums(", UTF_8); for (Album album : rootAlbum.getAlbums()) { - if (!Album.NOT_EMPTY.apply(album)) { + if (!AlbumsKt.notEmpty().invoke(album)) { continue; } hash.putString(album.getFingerprint(), UTF_8); diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt index 081a2fa11..da71d6289 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt @@ -29,3 +29,10 @@ val Album.allImages: Collection val Album.allAlbums: List get() = listOf(this) + albums.flatMap(Album::allAlbums) + +@get:JvmName("notEmpty") +val notEmpty: (Album) -> Boolean = { album -> + album.allImages.let { images -> + images.isNotEmpty() && images.any(Image::isInserted) + } +} diff --git a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt index 6044fd1fe..1f938a312 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt @@ -43,7 +43,7 @@ class AlbumsTest { assertThat(images.map(Image::id), containsInAnyOrder("image-1", "image-2", "image-3", "image-4")) } - private fun createImage(sone: IdOnlySone, id: String) = ImageImpl(id).modify().setSone(sone).update() + private fun createImage(sone: IdOnlySone, id: String, key: String? = null) = ImageImpl(id).modify().setSone(sone).setKey(key).update() @Test fun `allAlbums returns itself and all its subalbums`() { @@ -62,4 +62,56 @@ class AlbumsTest { assertThat(albums.indexOf(albumNestedInFirst), greaterThan(albums.indexOf(firstNestedAlbum))) } + @Test + fun `notEmpty finds album without images is empty`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + assertThat(notEmpty(album), equalTo(false)) + } + + @Test + fun `notEmpty finds album with one inserted image is not empty`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + album.addImage(createImage(sone, "1", "key")) + assertThat(notEmpty(album), equalTo(true)) + } + + @Test + fun `notEmpty finds album with one not-inserted image is empty`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + album.addImage(createImage(sone, "1")) + assertThat(notEmpty(album), equalTo(false)) + } + + @Test + fun `notEmpty finds album with empty subalbums is empty`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + val firstNestedAlbum = AlbumImpl(sone) + album.addAlbum(firstNestedAlbum) + assertThat(notEmpty(album), equalTo(false)) + } + + @Test + fun `notEmpty finds album with subalbum with not inserted image is empty`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + val firstNestedAlbum = AlbumImpl(sone) + firstNestedAlbum.addImage(createImage(sone, "1")) + album.addAlbum(firstNestedAlbum) + assertThat(notEmpty(album), equalTo(false)) + } + + @Test + fun `notEmpty finds album with subalbum with inserted image is not empty`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + val firstNestedAlbum = AlbumImpl(sone) + firstNestedAlbum.addImage(createImage(sone, "1", "key")) + album.addAlbum(firstNestedAlbum) + assertThat(notEmpty(album), equalTo(true)) + } + } From 5473139d890c40c85712f59ba534583961c2772e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 16:31:45 +0100 Subject: [PATCH 28/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20FLATTENER=20from?= =?UTF-8?q?=20Album=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/Core.java | 4 +-- .../pterodactylus/sone/core/SoneInserter.java | 6 ++--- .../net/pterodactylus/sone/data/Album.java | 25 ------------------- .../sone/template/SoneAccessor.java | 7 +++--- .../net/pterodactylus/sone/data/Sone.kt | 4 +++ .../sone/database/memory/MemoryDatabase.kt | 2 +- .../sone/web/pages/ImageBrowserPage.kt | 4 +-- .../net/pterodactylus/sone/data/SoneTest.kt | 12 +++++++++ .../sone/web/pages/ImageBrowserPageTest.kt | 19 +++++++------- 9 files changed, 35 insertions(+), 48 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index 7a8f5df4a..e186e6fd2 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -53,7 +53,6 @@ import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound; import net.pterodactylus.sone.core.event.*; import net.pterodactylus.sone.data.Album; -import net.pterodactylus.sone.data.AlbumsKt; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Image; import net.pterodactylus.sone.data.Post; @@ -63,6 +62,7 @@ import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.SoneKt; import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent; import net.pterodactylus.sone.data.TemporaryImage; import net.pterodactylus.sone.database.AlbumBuilder; @@ -1392,7 +1392,7 @@ private synchronized void saveSone(Sone sone) { configuration.getStringValue(sonePrefix + "/Likes/Reply/" + replyLikeCounter + "/ID").setValue(null); /* save albums. first, collect in a flat structure, top-level first. */ - List albums = AlbumsKt.getAllAlbums(sone.getRootAlbum()); + List albums = SoneKt.getAllAlbums(sone); int albumCounter = 0; for (Album album : albums) { diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index a67c15d35..e74c98db0 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -21,6 +21,7 @@ import static java.lang.System.currentTimeMillis; import static java.util.concurrent.TimeUnit.*; import static java.util.logging.Logger.getLogger; +import static java.util.stream.Collectors.toList; import java.io.*; import java.nio.charset.Charset; @@ -39,12 +40,12 @@ import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent; import net.pterodactylus.sone.core.event.SoneInsertedEvent; import net.pterodactylus.sone.core.event.SoneInsertingEvent; -import net.pterodactylus.sone.data.Album; import net.pterodactylus.sone.data.AlbumsKt; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.SoneKt; import net.pterodactylus.sone.main.SonePlugin; import net.pterodactylus.util.service.AbstractService; import net.pterodactylus.util.template.HtmlFilter; @@ -57,7 +58,6 @@ import net.pterodactylus.util.template.XmlFilter; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.FluentIterable; import com.google.common.collect.Ordering; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @@ -312,7 +312,7 @@ public InsertInformation(Sone sone) { soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds())); - soneProperties.put("albums", FluentIterable.from(sone.getRootAlbum().getAlbums()).transformAndConcat(Album.FLATTENER).filter(AlbumsKt.notEmpty()::invoke).toList()); + soneProperties.put("albums", SoneKt.getAllAlbums(sone).stream().filter(AlbumsKt.notEmpty()::invoke).collect(toList())); manifestCreator = new ManifestCreator(core, soneProperties); } diff --git a/src/main/java/net/pterodactylus/sone/data/Album.java b/src/main/java/net/pterodactylus/sone/data/Album.java index efaa470bc..3eefc3e26 100644 --- a/src/main/java/net/pterodactylus/sone/data/Album.java +++ b/src/main/java/net/pterodactylus/sone/data/Album.java @@ -17,42 +17,17 @@ package net.pterodactylus.sone.data; -import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; - -import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.ImmutableList; /** * Container for images that can also contain nested {@link Album}s. */ public interface Album extends Identified, Fingerprintable { - /** Function that flattens the given album and all albums beneath it. */ - Function> FLATTENER = new Function>() { - - @Override - @Nonnull - public List apply(Album album) { - if (album == null) { - return emptyList(); - } - List albums = new ArrayList<>(); - albums.add(album); - for (Album subAlbum : album.getAlbums()) { - albums.addAll(FluentIterable.from(ImmutableList.of(subAlbum)).transformAndConcat(FLATTENER).toList()); - } - return albums; - } - }; - /** Function that transforms an album into the images it contains. */ Function> IMAGES = new Function>() { diff --git a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java index 4bc6a5fe7..06922fc86 100644 --- a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java @@ -17,10 +17,8 @@ package net.pterodactylus.sone.template; -import static com.google.common.collect.FluentIterable.from; -import static java.util.Arrays.asList; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.data.Album.FLATTENER; +import static java.util.stream.Collectors.toList; import static net.pterodactylus.sone.data.Album.IMAGES; import java.util.logging.Level; @@ -30,6 +28,7 @@ import net.pterodactylus.sone.data.Profile; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; +import net.pterodactylus.sone.data.SoneKt; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.freenet.wot.Trust; import net.pterodactylus.sone.text.TimeTextConverter; @@ -116,7 +115,7 @@ public Object get(TemplateContext templateContext, Object object, String member) } return trust; } else if (member.equals("allImages")) { - return from(asList(sone.getRootAlbum())).transformAndConcat(FLATTENER).transformAndConcat(IMAGES); + return SoneKt.getAllAlbums(sone).stream().flatMap(a -> IMAGES.apply(a).stream()).collect(toList()); } else if (member.equals("albums")) { return sone.getRootAlbum().getAlbums(); } diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt index 3f70ad715..46ff3362c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt @@ -51,3 +51,7 @@ val postCountComparator: Comparator = val imageCountComparator: Comparator = comparing { it.rootAlbum.allImages.size }.reversed() + +val Sone.allAlbums: List + get() = + rootAlbum.albums.flatMap(Album::allAlbums) diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt index df65a5a35..5c324866a 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -123,7 +123,7 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio for (postReply in sone.replies) { allPostReplies[postReply.id] = postReply } - sone.rootAlbum.allAlbums.let { albums -> + sone.allAlbums.let { albums -> soneAlbums.putAll(sone.id, albums) albums.forEach { album -> allAlbums[album.id] = album } } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt index 20219d747..a0ca0d781 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt @@ -29,9 +29,7 @@ class ImageBrowserPage @Inject constructor(webInterface: WebInterface, loaders: } else if (soneRequest.parameters["mode"] == "gallery") { templateContext["galleryRequested"] = true soneRequest.core.sones - .map(Sone::getRootAlbum) - .flatMap(Album::getAlbums) - .flatMap { Album.FLATTENER.apply(it)!! } + .flatMap(Sone::allAlbums) .filterNot(Album::isEmpty) .sortedBy(Album::getTitle) .also { albums -> diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt index 1429e544e..42980eeb1 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt @@ -142,4 +142,16 @@ class SoneTest { assertThat(imageCountComparator.compare(sone1, sone2), equalTo(0)) } + @Test + fun `allAlbums returns all albums of a Sone but the root album`() { + val sone = object : IdOnlySone("1") { + private val rootAlbum = AlbumImpl(this) + override fun getRootAlbum() = rootAlbum + } + val album1 = AlbumImpl(sone).also(sone.rootAlbum::addAlbum) + val album11 = AlbumImpl(sone).also(album1::addAlbum) + val album2 = AlbumImpl(sone).also(sone.rootAlbum::addAlbum) + assertThat(sone.allAlbums, contains(album1, album11, album2)) + } + } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt index 1533f4bad..38ecce46d 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt @@ -1,6 +1,8 @@ package net.pterodactylus.sone.web.pages import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.data.impl.AlbumImpl +import net.pterodactylus.sone.data.impl.ImageImpl import net.pterodactylus.sone.test.* import net.pterodactylus.sone.web.* import net.pterodactylus.sone.web.page.* @@ -105,16 +107,13 @@ class ImageBrowserPageTest : WebPageTest(::ImageBrowserPage) { private fun createSone(firstAlbumTitle: String, secondAlbumTitle: String): Sone { return mock().apply { - val rootAlbum = mock() - val firstAlbum = mock() - val firstImage = mock().run { whenever(isInserted).thenReturn(true); this } - whenever(firstAlbum.images).thenReturn(listOf(firstImage)) - val secondAlbum = mock() - val secondImage = mock().run { whenever(isInserted).thenReturn(true); this } - whenever(secondAlbum.images).thenReturn(listOf(secondImage)) - whenever(firstAlbum.title).thenReturn(firstAlbumTitle) - whenever(secondAlbum.title).thenReturn(secondAlbumTitle) - whenever(rootAlbum.albums).thenReturn(listOf(firstAlbum, secondAlbum)) + val rootAlbum = AlbumImpl(this) + val firstAlbum = AlbumImpl(this).modify().setTitle(firstAlbumTitle).update() + firstAlbum.addImage(ImageImpl("1").modify().setSone(this).setKey("key").update()) + val secondAlbum = AlbumImpl(this).modify().setTitle(secondAlbumTitle).update() + secondAlbum.addImage(ImageImpl("2").modify().setSone(this).setKey("key").update()) + rootAlbum.addAlbum(firstAlbum) + rootAlbum.addAlbum(secondAlbum) whenever(this.rootAlbum).thenReturn(rootAlbum) } } From e13cd46faa0404418176f8747d52e8160b2a7068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 16:42:19 +0100 Subject: [PATCH 29/87] =?UTF-8?q?=E2=9C=85=20Add=20tests=20for=20Album.all?= =?UTF-8?q?Images?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/Albums.kt | 2 +- .../net/pterodactylus/sone/data/AlbumsTest.kt | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt index da71d6289..a38e9dd63 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt @@ -20,7 +20,7 @@ package net.pterodactylus.sone.data /** Returns all images contained in this album and all its albums. */ val Album.allImages: Collection get() = - images + albums.flatMap { it.allImages } + images + albums.flatMap(Album::allImages) /** * Returns this album and all albums contained in this album (recursively). diff --git a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt index 1f938a312..6821a24e3 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt @@ -114,4 +114,23 @@ class AlbumsTest { assertThat(notEmpty(album), equalTo(true)) } + @Test + fun `allImages returns images from album`() { + val sone = IdOnlySone("sone") + val album = AlbumImpl(sone) + val image1 = createImage(sone, "1").also(album::addImage) + val image2 = createImage(sone, "2").also(album::addImage) + assertThat(album.allImages, contains(image1, image2)) + } + + @Test + fun `allImages returns images from subalbum`() { + val sone = IdOnlySone("sone") + val album1 = AlbumImpl(sone) + val album2 = AlbumImpl(sone).also(album1::addAlbum) + val image1 = createImage(sone, "1").also(album1::addImage) + val image2 = createImage(sone, "2").also(album2::addImage) + assertThat(album1.allImages, contains(image1, image2)) + } + } From 8e2e2aa6f1c7c4e9b4b994caee11677d4cd41403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 16:43:01 +0100 Subject: [PATCH 30/87] =?UTF-8?q?=F0=9F=8E=A8=20Add=20Sone.allImages=20acc?= =?UTF-8?q?essor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/net/pterodactylus/sone/data/Sone.kt | 4 ++++ .../net/pterodactylus/sone/data/SoneTest.kt | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt index 46ff3362c..34403a16c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Sone.kt @@ -55,3 +55,7 @@ val imageCountComparator: Comparator = val Sone.allAlbums: List get() = rootAlbum.albums.flatMap(Album::allAlbums) + +val Sone.allImages: Collection + get() = + rootAlbum.allImages diff --git a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt index 42980eeb1..fb2312125 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/SoneTest.kt @@ -154,4 +154,19 @@ class SoneTest { assertThat(sone.allAlbums, contains(album1, album11, album2)) } + @Test + fun `allImages returns all images of a Sone`() { + val sone = object : IdOnlySone("1") { + private val rootAlbum = AlbumImpl(this) + override fun getRootAlbum() = rootAlbum + } + val album1 = AlbumImpl(sone).also(sone.rootAlbum::addAlbum) + val album11 = AlbumImpl(sone).also(album1::addAlbum) + val album2 = AlbumImpl(sone).also(sone.rootAlbum::addAlbum) + val image1 = createImage(sone).also(album1::addImage) + val image11 = createImage(sone).also(album11::addImage) + val image2 = createImage(sone).also(album2::addImage) + assertThat(sone.allImages, containsInAnyOrder(image1, image11, image2)) + } + } From 70ed282fe7a7146a8abb92450ceca75f90c20ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 16:45:21 +0100 Subject: [PATCH 31/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20IMAGES=20from=20A?= =?UTF-8?q?lbum=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/data/Album.java | 14 -------------- .../pterodactylus/sone/template/SoneAccessor.java | 4 +--- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Album.java b/src/main/java/net/pterodactylus/sone/data/Album.java index 3eefc3e26..bf8f3ec41 100644 --- a/src/main/java/net/pterodactylus/sone/data/Album.java +++ b/src/main/java/net/pterodactylus/sone/data/Album.java @@ -17,27 +17,13 @@ package net.pterodactylus.sone.data; -import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; - -import com.google.common.base.Function; /** * Container for images that can also contain nested {@link Album}s. */ public interface Album extends Identified, Fingerprintable { - /** Function that transforms an album into the images it contains. */ - Function> IMAGES = new Function>() { - - @Override - @Nonnull - public List apply(Album album) { - return (album != null) ? album.getImages() : Collections.emptyList(); - } - }; - /** * Returns the ID of this album. * diff --git a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java index 06922fc86..b072dc9e6 100644 --- a/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java +++ b/src/main/java/net/pterodactylus/sone/template/SoneAccessor.java @@ -18,8 +18,6 @@ package net.pterodactylus.sone.template; import static java.util.logging.Logger.getLogger; -import static java.util.stream.Collectors.toList; -import static net.pterodactylus.sone.data.Album.IMAGES; import java.util.logging.Level; import java.util.logging.Logger; @@ -115,7 +113,7 @@ public Object get(TemplateContext templateContext, Object object, String member) } return trust; } else if (member.equals("allImages")) { - return SoneKt.getAllAlbums(sone).stream().flatMap(a -> IMAGES.apply(a).stream()).collect(toList()); + return SoneKt.getAllImages(sone); } else if (member.equals("albums")) { return sone.getRootAlbum().getAlbums(); } From c00c2dea5bbe698a46c443d3610855720baa69a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 16:57:00 +0100 Subject: [PATCH 32/87] =?UTF-8?q?=F0=9F=9A=9A=20Rename=20Album=20helper=20?= =?UTF-8?q?file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/core/Core.java | 2 +- src/main/java/net/pterodactylus/sone/core/SoneInserter.java | 4 ++-- .../java/net/pterodactylus/sone/data/impl/SoneImpl.java | 4 ++-- .../net/pterodactylus/sone/data/{Albums.kt => Album.kt} | 2 +- .../pterodactylus/sone/data/{AlbumsTest.kt => AlbumTest.kt} | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) rename src/main/kotlin/net/pterodactylus/sone/data/{Albums.kt => Album.kt} (94%) rename src/test/kotlin/net/pterodactylus/sone/data/{AlbumsTest.kt => AlbumTest.kt} (96%) diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index e186e6fd2..c4cc9cfc6 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -24,7 +24,7 @@ import static java.lang.String.format; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.data.AlbumsKt.getAllImages; +import static net.pterodactylus.sone.data.AlbumKt.getAllImages; import java.util.ArrayList; import java.util.Collection; diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index e74c98db0..651b62e41 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -40,7 +40,7 @@ import net.pterodactylus.sone.core.event.SoneInsertAbortedEvent; import net.pterodactylus.sone.core.event.SoneInsertedEvent; import net.pterodactylus.sone.core.event.SoneInsertingEvent; -import net.pterodactylus.sone.data.AlbumsKt; +import net.pterodactylus.sone.data.AlbumKt; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; @@ -312,7 +312,7 @@ public InsertInformation(Sone sone) { soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds())); - soneProperties.put("albums", SoneKt.getAllAlbums(sone).stream().filter(AlbumsKt.notEmpty()::invoke).collect(toList())); + soneProperties.put("albums", SoneKt.getAllAlbums(sone).stream().filter(AlbumKt.notEmpty()::invoke).collect(toList())); manifestCreator = new ManifestCreator(core, soneProperties); } diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index 87bd7a6fc..8f5a50f41 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -37,7 +37,7 @@ import javax.annotation.Nullable; import net.pterodactylus.sone.data.Album; -import net.pterodactylus.sone.data.AlbumsKt; +import net.pterodactylus.sone.data.AlbumKt; import net.pterodactylus.sone.data.Client; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; @@ -650,7 +650,7 @@ public synchronized String getFingerprint() { hash.putString("Albums(", UTF_8); for (Album album : rootAlbum.getAlbums()) { - if (!AlbumsKt.notEmpty().invoke(album)) { + if (!AlbumKt.notEmpty().invoke(album)) { continue; } hash.putString(album.getFingerprint(), UTF_8); diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt b/src/main/kotlin/net/pterodactylus/sone/data/Album.kt similarity index 94% rename from src/main/kotlin/net/pterodactylus/sone/data/Albums.kt rename to src/main/kotlin/net/pterodactylus/sone/data/Album.kt index a38e9dd63..991c0ec17 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Albums.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Album.kt @@ -1,5 +1,5 @@ /** - * Sone - Albums.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * Sone - Album.kt - Copyright © 2019–2020 David ‘Bombe’ Roden * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/AlbumTest.kt similarity index 96% rename from src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt rename to src/test/kotlin/net/pterodactylus/sone/data/AlbumTest.kt index 6821a24e3..fe11c2c3a 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/AlbumsTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/AlbumTest.kt @@ -1,5 +1,5 @@ /** - * Sone - AlbumsTest.kt - Copyright © 2019–2020 David ‘Bombe’ Roden + * Sone - AlbumTest.kt - Copyright © 2019–2020 David ‘Bombe’ Roden * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +23,9 @@ import org.hamcrest.Matchers.* import kotlin.test.* /** - * Unit test for various helper method in `Albums.kt`. + * Unit test for various helper method in `Album.kt`. */ -class AlbumsTest { +class AlbumTest { @Test fun `recursive list of all images for album is returned correctly`() { From 2ab560633c24940fa49bdaf569635dd673ad7b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 18 Feb 2020 21:32:37 +0100 Subject: [PATCH 33/87] =?UTF-8?q?=F0=9F=8E=A8=20Reduce=20mocking=20of=20al?= =?UTF-8?q?bums=20and=20images?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/core/FreenetInterfaceTest.kt | 2 +- .../sone/core/ImageInserterTest.kt | 4 +-- .../database/memory/MemoryDatabaseTest.kt | 11 +++--- .../sone/template/ImageAccessorTest.kt | 20 +++-------- .../sone/template/SoneAccessorTest.kt | 19 +++++----- .../sone/web/ajax/EditAlbumAjaxPageTest.kt | 35 +++++++++---------- .../sone/web/ajax/EditImageAjaxPageTest.kt | 30 +++++++--------- .../sone/web/pages/DeleteAlbumPageTest.kt | 33 +++++++---------- .../sone/web/pages/DeleteImagePageTest.kt | 9 ++--- .../sone/web/pages/EditAlbumPageTest.kt | 34 +++++++----------- .../sone/web/pages/EditImagePageTest.kt | 32 +++++++---------- .../sone/web/pages/EditProfilePageTest.kt | 10 ++---- .../sone/web/pages/ImageBrowserPageTest.kt | 4 +-- .../sone/web/pages/KnownSonesPageTest.kt | 10 +++--- .../sone/web/pages/UploadImagePageTest.kt | 27 +++++--------- 15 files changed, 115 insertions(+), 165 deletions(-) diff --git a/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt index a7b187d71..9cbaff004 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/FreenetInterfaceTest.kt @@ -57,7 +57,7 @@ class FreenetInterfaceTest { private val uskManager = mock() private val sone = mock() private val callbackCaptor: ArgumentCaptor = forClass(USKCallback::class.java) - private val image = mock() + private val image: Image = ImageImpl() private val insertToken: InsertToken private val bucket = mock() private val clientGetCallback: ArgumentCaptor = forClass(ClientGetCallback::class.java) diff --git a/src/test/kotlin/net/pterodactylus/sone/core/ImageInserterTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/ImageInserterTest.kt index 60bb7e278..6e2c7c5d1 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/ImageInserterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/ImageInserterTest.kt @@ -2,8 +2,8 @@ package net.pterodactylus.sone.core import net.pterodactylus.sone.core.FreenetInterface.InsertToken import net.pterodactylus.sone.core.FreenetInterface.InsertTokenSupplier -import net.pterodactylus.sone.data.Image import net.pterodactylus.sone.data.TemporaryImage +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.test.getInstance import net.pterodactylus.sone.test.mock import net.pterodactylus.sone.test.whenever @@ -23,7 +23,7 @@ import org.mockito.Mockito.verify class ImageInserterTest { private val temporaryImage = mock().apply { whenever(id).thenReturn("image-id") } - private val image = mock().apply { whenever(id).thenReturn("image-id") } + private val image = ImageImpl("image-id") private val freenetInterface = mock() private val insertToken = mock() private val insertTokenSupplier: InsertTokenSupplier = mock().apply { whenever(apply(any())).thenReturn(insertToken) } diff --git a/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt b/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt index cc4778fa6..150336f9a 100644 --- a/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt @@ -57,8 +57,8 @@ class MemoryDatabaseTest { assertThat(memoryDatabase.getPostReply("reply2"), isPostReply("reply2", "post2", 4000L, "reply2")) assertThat(memoryDatabase.getPostReply("reply3"), isPostReply("reply3", "post1", 5000L, "reply3")) assertThat(memoryDatabase.getPostReply("reply4"), nullValue()) - assertThat(memoryDatabase.getAlbum("album1"), isAlbum("album1", null, "album1", "album-description1")) - assertThat(memoryDatabase.getAlbum("album2"), isAlbum("album2", null, "album2", "album-description2")) + assertThat(memoryDatabase.getAlbum("album1"), isAlbum("album1", "root", "album1", "album-description1")) + assertThat(memoryDatabase.getAlbum("album2"), isAlbum("album2", "root", "album2", "album-description2")) assertThat(memoryDatabase.getAlbum("album3"), isAlbum("album3", "album1", "album3", "album-description3")) assertThat(memoryDatabase.getAlbum("album4"), nullValue()) assertThat(memoryDatabase.getImage("image1"), isImage("image1", 1000L, "KSK@image1", "image1", "image-description1", 16, 9)) @@ -123,9 +123,10 @@ class MemoryDatabaseTest { .setDescription("album-description3") .update() firstAlbum.addAlbum(thirdAlbum) - val rootAlbum = mock() - whenever(rootAlbum.id).thenReturn("root") - whenever(rootAlbum.albums).thenReturn(listOf(firstAlbum, secondAlbum)) + val rootAlbum = AlbumImpl(sone, "root").also { + it.addAlbum(firstAlbum) + it.addAlbum(secondAlbum) + } whenever(sone.rootAlbum).thenReturn(rootAlbum) val firstImage = TestImageBuilder().withId("image1") .build() diff --git a/src/test/kotlin/net/pterodactylus/sone/template/ImageAccessorTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/ImageAccessorTest.kt index f7da592af..21a3d00ab 100644 --- a/src/test/kotlin/net/pterodactylus/sone/template/ImageAccessorTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/template/ImageAccessorTest.kt @@ -1,13 +1,9 @@ package net.pterodactylus.sone.template -import net.pterodactylus.sone.data.Album -import net.pterodactylus.sone.data.Image -import net.pterodactylus.sone.test.mock -import net.pterodactylus.sone.test.whenever +import net.pterodactylus.sone.data.impl.* import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.nullValue -import org.junit.Before import org.junit.Test /** @@ -16,16 +12,6 @@ import org.junit.Test class ImageAccessorTest { private val accessor = ImageAccessor() - private val album = mock() - private val images = listOf(mock(), mock()) - - @Before - fun setupImages() { - whenever(album.images).thenReturn(images) - images.forEach { - whenever(it.album).thenReturn(album) - } - } @Test fun `accessor returns next image for first image`() { @@ -53,3 +39,7 @@ class ImageAccessorTest { } } + +private val sone = IdOnlySone("sone") +private val album = AlbumImpl(sone) +private val images = listOf(ImageImpl().modify().setSone(sone).update(), ImageImpl().modify().setSone(sone).update()).onEach(album::addImage) diff --git a/src/test/kotlin/net/pterodactylus/sone/template/SoneAccessorTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/SoneAccessorTest.kt index 87cfce0db..2ddab1530 100644 --- a/src/test/kotlin/net/pterodactylus/sone/template/SoneAccessorTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/template/SoneAccessorTest.kt @@ -10,6 +10,7 @@ import net.pterodactylus.sone.data.Sone.SoneStatus.downloading import net.pterodactylus.sone.data.Sone.SoneStatus.idle import net.pterodactylus.sone.data.Sone.SoneStatus.inserting import net.pterodactylus.sone.data.Sone.SoneStatus.unknown +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.freenet.L10nText import net.pterodactylus.sone.freenet.wot.Identity import net.pterodactylus.sone.freenet.wot.OwnIdentity @@ -217,24 +218,24 @@ class SoneAccessorTest { @Test fun `accessor returns all images in the correct order`() { - val images = listOf(mock(), mock(), mock(), mock(), mock()) - val firstAlbum = createAlbum(listOf(), listOf(images[0], images[3])) - val secondAlbum = createAlbum(listOf(), listOf(images[1], images[4], images[2])) - val rootAlbum = createAlbum(listOf(firstAlbum, secondAlbum), listOf()) + val images = (0 until 5).map { ImageImpl().modify().setSone(sone).update() } + val firstAlbum = createAlbum(emptyList(), listOf(images[0], images[3])) + val secondAlbum = createAlbum(emptyList(), listOf(images[1], images[4], images[2])) + val rootAlbum = createAlbum(listOf(firstAlbum, secondAlbum), emptyList()) whenever(sone.rootAlbum).thenReturn(rootAlbum) assertAccessorReturnValueMatches("allImages", contains(images[0], images[3], images[1], images[4], images[2])) } private fun createAlbum(albums: List, images: List) = - mock().apply { - whenever(this.albums).thenReturn(albums) - whenever(this.images).thenReturn(images) + AlbumImpl(sone).also { + albums.forEach(it::addAlbum) + images.forEach(it::addImage) } @Test fun `accessor returns all albums in the correct order`() { - val albums = listOf(mock(), mock(), mock(), mock(), mock()) - val rootAlbum = createAlbum(albums, listOf()) + val albums = (0 until 5).map { AlbumImpl(sone) } + val rootAlbum = createAlbum(albums, emptyList()) whenever(sone.rootAlbum).thenReturn(rootAlbum) assertAccessorReturnValueMatches("albums", contains(*albums.toTypedArray())) } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPageTest.kt index 1528d60f7..850cc9dfb 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditAlbumAjaxPageTest.kt @@ -1,10 +1,7 @@ package net.pterodactylus.sone.web.ajax -import net.pterodactylus.sone.data.Album -import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.impl.AlbumImpl -import net.pterodactylus.sone.test.deepMock import net.pterodactylus.sone.test.getInstance import net.pterodactylus.sone.test.mock import net.pterodactylus.sone.test.whenever @@ -20,8 +17,7 @@ import org.junit.Test class EditAlbumAjaxPageTest : JsonPageTest("editAlbum.ajax", pageSupplier = ::EditAlbumAjaxPage) { private val sone = mock() - private val localSone = mock().apply { whenever(isLocal).thenReturn(true) } - private val album = mock().apply { whenever(id).thenReturn("album-id") } + private val album = AlbumImpl(sone, "album-id") @Test fun `request without album results in invalid-album-id`() { @@ -30,7 +26,6 @@ class EditAlbumAjaxPageTest : JsonPageTest("editAlbum.ajax", pageSupplier = ::Ed @Test fun `request with non-local album results in not-authorized`() { - whenever(album.sone).thenReturn(sone) addAlbum(album) addRequestParameter("album", "album-id") assertThatJsonFailed("not-authorized") @@ -38,11 +33,11 @@ class EditAlbumAjaxPageTest : JsonPageTest("editAlbum.ajax", pageSupplier = ::Ed @Test fun `request with moveLeft moves album to the left`() { - whenever(album.sone).thenReturn(localSone) - val swappedAlbum = mock().apply { whenever(id).thenReturn("swapped") } - val parentAlbum = mock() - whenever(parentAlbum.moveAlbumUp(album)).thenReturn(swappedAlbum) - whenever(album.parent).thenReturn(parentAlbum) + setupLocalSone() + AlbumImpl(sone).also { + it.addAlbum(AlbumImpl(sone, "swapped")) + it.addAlbum(album) + } addAlbum(album) addRequestParameter("album", "album-id") addRequestParameter("moveLeft", "true") @@ -53,11 +48,11 @@ class EditAlbumAjaxPageTest : JsonPageTest("editAlbum.ajax", pageSupplier = ::Ed @Test fun `request with moveRight moves album to the right`() { - whenever(album.sone).thenReturn(localSone) - val swappedAlbum = mock().apply { whenever(id).thenReturn("swapped") } - val parentAlbum = mock() - whenever(parentAlbum.moveAlbumDown(album)).thenReturn(swappedAlbum) - whenever(album.parent).thenReturn(parentAlbum) + setupLocalSone() + AlbumImpl(sone).also { + it.addAlbum(album) + it.addAlbum(AlbumImpl(sone, "swapped")) + } addAlbum(album) addRequestParameter("album", "album-id") addRequestParameter("moveRight", "true") @@ -68,9 +63,7 @@ class EditAlbumAjaxPageTest : JsonPageTest("editAlbum.ajax", pageSupplier = ::Ed @Test fun `request with missing title results in invalid-title`() { - whenever(album.sone).thenReturn(localSone) - whenever(album.modify()).thenReturn(deepMock()) - whenever(album.modify().setTitle("")).thenThrow(AlbumTitleMustNotBeEmpty::class.java) + setupLocalSone() addAlbum(album) addRequestParameter("album", "album-id") assertThatJsonFailed("invalid-album-title") @@ -95,4 +88,8 @@ class EditAlbumAjaxPageTest : JsonPageTest("editAlbum.ajax", pageSupplier = ::Ed assertThat(baseInjector.getInstance(), notNullValue()) } + private fun setupLocalSone() { + whenever(sone.isLocal).thenReturn(true) + } + } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt index 26436a317..e63bb8a5d 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt @@ -3,7 +3,7 @@ package net.pterodactylus.sone.web.ajax import net.pterodactylus.sone.data.Album import net.pterodactylus.sone.data.Image import net.pterodactylus.sone.data.Sone -import net.pterodactylus.sone.data.impl.ImageImpl +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.template.ParserFilter import net.pterodactylus.sone.template.RenderFilter import net.pterodactylus.sone.template.ShortenFilter @@ -38,9 +38,8 @@ class EditImageAjaxPageTest : JsonPageTest("editImage.ajax") { @Test fun `request with non-local image results in not-authorized`() { - val image = mock() val sone = mock() - whenever(image.sone).thenReturn(sone) + val image = ImageImpl().modify().setSone(sone).update() addImage(image, "image-id") addRequestParameter("image", "image-id") assertThatJsonFailed("not-authorized") @@ -48,13 +47,12 @@ class EditImageAjaxPageTest : JsonPageTest("editImage.ajax") { @Test fun `moving an image to the left returns the correct values`() { - val image = mock().apply { whenever(id).thenReturn("image-id") } val sone = mock().apply { whenever(isLocal).thenReturn(true) } - whenever(image.sone).thenReturn(sone) - val swapped = mock().apply { whenever(id).thenReturn("swapped") } - val album = mock() - whenever(album.moveImageUp(image)).thenReturn(swapped) - whenever(image.album).thenReturn(album) + val image = ImageImpl("image-id").modify().setSone(sone).update() + AlbumImpl(sone).also { + it.addImage(ImageImpl("swapped").modify().setSone(sone).update()) + it.addImage(image) + } addImage(image) addRequestParameter("image", "image-id") addRequestParameter("moveLeft", "true") @@ -66,13 +64,12 @@ class EditImageAjaxPageTest : JsonPageTest("editImage.ajax") { @Test fun `moving an image to the right returns the correct values`() { - val image = mock().apply { whenever(id).thenReturn("image-id") } val sone = mock().apply { whenever(isLocal).thenReturn(true) } - whenever(image.sone).thenReturn(sone) - val swapped = mock().apply { whenever(id).thenReturn("swapped") } - val album = mock() - whenever(album.moveImageDown(image)).thenReturn(swapped) - whenever(image.album).thenReturn(album) + val image = ImageImpl("image-id").modify().setSone(sone).update() + AlbumImpl(sone).also { + it.addImage(image) + it.addImage(ImageImpl("swapped").modify().setSone(sone).update()) + } addImage(image) addRequestParameter("image", "image-id") addRequestParameter("moveRight", "true") @@ -84,9 +81,8 @@ class EditImageAjaxPageTest : JsonPageTest("editImage.ajax") { @Test fun `request with empty title results in invalid-image-title`() { - val image = mock().apply { whenever(id).thenReturn("image-id") } val sone = mock().apply { whenever(isLocal).thenReturn(true) } - whenever(image.sone).thenReturn(sone) + val image = ImageImpl("image-id").modify().setSone(sone).update() addImage(image) addRequestParameter("image", "image-id") assertThatJsonFailed("invalid-image-title") diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPageTest.kt index 55b3d7030..9830ffa52 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteAlbumPageTest.kt @@ -1,6 +1,6 @@ package net.pterodactylus.sone.web.pages -import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.web.* import net.pterodactylus.sone.web.page.* @@ -16,20 +16,14 @@ import org.mockito.Mockito.verify */ class DeleteAlbumPageTest : WebPageTest(::DeleteAlbumPage) { - private val sone = mock() - private val album = mock() - private val parentAlbum = mock() + private val album = AlbumImpl(currentSone, "album-id") + private val parentAlbum = AlbumImpl(currentSone, "parent-id").also { it.addAlbum(album) } @Before fun setupAlbums() { - whenever(sone.id).thenReturn("sone-id") - whenever(sone.isLocal).thenReturn(true) - whenever(parentAlbum.id).thenReturn("parent-id") - whenever(parentAlbum.isRoot).thenReturn(true) - whenever(album.id).thenReturn("album-id") - whenever(album.sone).thenReturn(sone) - whenever(album.parent).thenReturn(parentAlbum) - whenever(sone.rootAlbum).thenReturn(parentAlbum) + whenever(currentSone.id).thenReturn("sone-id") + whenever(currentSone.isLocal).thenReturn(true) + whenever(currentSone.rootAlbum).thenReturn(parentAlbum) } @Test @@ -50,7 +44,6 @@ class DeleteAlbumPageTest : WebPageTest(::DeleteAlbumPage) { @Test fun `get request with valid album ID sets album in template context`() { - val album = mock() addAlbum("album-id", album) addHttpRequestParameter("album", "album-id") page.processTemplate(freenetRequest, templateContext) @@ -66,7 +59,7 @@ class DeleteAlbumPageTest : WebPageTest(::DeleteAlbumPage) { @Test fun `post request redirects to no permissions page if album is not local`() { setMethod(POST) - whenever(sone.isLocal).thenReturn(false) + whenever(currentSone.isLocal).thenReturn(false) addAlbum("album-id", album) addHttpRequestPart("album", "album-id") verifyRedirect("noPermission.html") @@ -94,12 +87,12 @@ class DeleteAlbumPageTest : WebPageTest(::DeleteAlbumPage) { @Test fun `album is deleted and page redirects to album if parent album is not root album`() { setMethod(POST) - whenever(parentAlbum.isRoot).thenReturn(false) - whenever(sone.rootAlbum).thenReturn(mock()) - addAlbum("album-id", album) - addHttpRequestPart("album", "album-id") - verifyRedirect("imageBrowser.html?album=parent-id") { - verify(core).deleteAlbum(album) + val subAlbum = AlbumImpl(currentSone, "sub-album-id") + album.addAlbum(subAlbum) + addAlbum("sub-album-id", subAlbum) + addHttpRequestPart("album", "sub-album-id") + verifyRedirect("imageBrowser.html?album=album-id") { + verify(core).deleteAlbum(subAlbum) } } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePageTest.kt index cecde502f..eccf9491d 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/DeleteImagePageTest.kt @@ -1,6 +1,7 @@ package net.pterodactylus.sone.web.pages import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.web.* import net.pterodactylus.sone.web.page.* @@ -15,16 +16,12 @@ import org.mockito.Mockito.* */ class DeleteImagePageTest : WebPageTest(::DeleteImagePage) { - private val image = mock() private val sone = mock() + private val image = ImageImpl("image-id").modify().setSone(sone).update()!! @Before fun setupImage() { - val album = mock() - whenever(album.id).thenReturn("album-id") - whenever(image.id).thenReturn("image-id") - whenever(image.sone).thenReturn(sone) - whenever(image.album).thenReturn(album) + AlbumImpl(sone, "album-id").also { it.addImage(image) } whenever(sone.isLocal).thenReturn(true) } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPageTest.kt index 72e814b9a..97e88654a 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/EditAlbumPageTest.kt @@ -1,8 +1,6 @@ package net.pterodactylus.sone.web.pages -import net.pterodactylus.sone.data.* -import net.pterodactylus.sone.data.Album.* -import net.pterodactylus.sone.data.Album.Modifier.* +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.web.* import net.pterodactylus.util.web.Method.* @@ -16,20 +14,16 @@ import org.mockito.Mockito.* */ class EditAlbumPageTest : WebPageTest(::EditAlbumPage) { - private val album = mock() - private val parentAlbum = mock() - private val modifier = mockBuilder() - private val sone = mock() + private val album = AlbumImpl(currentSone, "album-id") + private val parentAlbum = AlbumImpl(currentSone, "parent-id").also { + it.addAlbum(AlbumImpl(currentSone)) + it.addAlbum(album) + it.addAlbum(AlbumImpl(currentSone)) + } @Before fun setup() { - whenever(album.id).thenReturn("album-id") - whenever(album.sone).thenReturn(sone) - whenever(album.parent).thenReturn(parentAlbum) - whenever(album.modify()).thenReturn(modifier) - whenever(modifier.update()).thenReturn(album) - whenever(parentAlbum.id).thenReturn("parent-id") - whenever(sone.isLocal).thenReturn(true) + whenever(currentSone.isLocal).thenReturn(true) addHttpRequestHeader("Host", "www.te.st") } @@ -63,7 +57,7 @@ class EditAlbumPageTest : WebPageTest(::EditAlbumPage) { @Test fun `post request with album of non-local sone redirects to no permissions page`() { setMethod(POST) - whenever(sone.isLocal).thenReturn(false) + whenever(currentSone.isLocal).thenReturn(false) addAlbum("album-id", album) addHttpRequestPart("album", "album-id") verifyRedirect("noPermission.html") @@ -76,7 +70,7 @@ class EditAlbumPageTest : WebPageTest(::EditAlbumPage) { addHttpRequestPart("album", "album-id") addHttpRequestPart("moveLeft", "true") verifyRedirect("imageBrowser.html?album=parent-id") { - verify(parentAlbum).moveAlbumUp(album) + assertThat(parentAlbum.albums.indexOf(album), equalTo(0)) verify(core).touchConfiguration() } } @@ -88,7 +82,7 @@ class EditAlbumPageTest : WebPageTest(::EditAlbumPage) { addHttpRequestPart("album", "album-id") addHttpRequestPart("moveRight", "true") verifyRedirect("imageBrowser.html?album=parent-id") { - verify(parentAlbum).moveAlbumDown(album) + assertThat(parentAlbum.albums.indexOf(album), equalTo(2)) verify(core).touchConfiguration() } } @@ -98,7 +92,6 @@ class EditAlbumPageTest : WebPageTest(::EditAlbumPage) { setMethod(POST) addAlbum("album-id", album) addHttpRequestPart("album", "album-id") - whenever(modifier.setTitle("")).thenThrow(AlbumTitleMustNotBeEmpty()) verifyRedirect("emptyAlbumTitle.html") } @@ -110,9 +103,8 @@ class EditAlbumPageTest : WebPageTest(::EditAlbumPage) { addHttpRequestPart("title", "title") addHttpRequestPart("description", "description") verifyRedirect("imageBrowser.html?album=album-id") { - verify(modifier).setTitle("title") - verify(modifier).setDescription("description") - verify(modifier).update() + assertThat(album.title, equalTo("title")) + assertThat(album.description, equalTo("description")) verify(core).touchConfiguration() } } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/EditImagePageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/EditImagePageTest.kt index e43551cae..5f5903150 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/EditImagePageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/EditImagePageTest.kt @@ -1,8 +1,7 @@ package net.pterodactylus.sone.web.pages import net.pterodactylus.sone.data.* -import net.pterodactylus.sone.data.Image.* -import net.pterodactylus.sone.data.Image.Modifier.* +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.web.* import net.pterodactylus.util.web.Method.* @@ -16,19 +15,17 @@ import org.mockito.Mockito.* */ class EditImagePageTest : WebPageTest(::EditImagePage) { - private val image = mock() - private val modifier = mockBuilder() private val sone = mock() - private val album = mock() + private val image = ImageImpl("image-id").modify().setSone(sone).update()!! + private val album = AlbumImpl(sone, "album-id").also { + it.addImage(ImageImpl("1").modify().setSone(sone).update()) + it.addImage(image) + it.addImage(ImageImpl("2").modify().setSone(sone).update()) + } @Before fun setupImage() { whenever(sone.isLocal).thenReturn(true) - whenever(album.id).thenReturn("album-id") - whenever(modifier.update()).thenReturn(image) - whenever(image.sone).thenReturn(sone) - whenever(image.album).thenReturn(album) - whenever(image.modify()).thenReturn(modifier) } @Test @@ -75,7 +72,7 @@ class EditImagePageTest : WebPageTest(::EditImagePage) { addHttpRequestPart("returnPage", "return.html") addHttpRequestPart("moveLeft", "true") verifyRedirect("return.html") { - verify(album).moveImageUp(image) + assertThat(album.images.indexOf(image), equalTo(0)) verify(core).touchConfiguration() } } @@ -88,7 +85,7 @@ class EditImagePageTest : WebPageTest(::EditImagePage) { addHttpRequestPart("returnPage", "return.html") addHttpRequestPart("moveRight", "true") verifyRedirect("return.html") { - verify(album).moveImageDown(image) + assertThat(album.images.indexOf(image), equalTo(2)) verify(core).touchConfiguration() } } @@ -100,7 +97,6 @@ class EditImagePageTest : WebPageTest(::EditImagePage) { addHttpRequestPart("image", "image-id") addHttpRequestPart("returnPage", "return.html") addHttpRequestPart("title", " ") - whenever(modifier.update()).doThrow() verifyRedirect("emptyImageTitle.html") { verify(core, never()).touchConfiguration() } @@ -115,9 +111,8 @@ class EditImagePageTest : WebPageTest(::EditImagePage) { addHttpRequestPart("title", "Title") addHttpRequestPart("description", "Description") verifyRedirect("return.html") { - verify(modifier).setTitle("Title") - verify(modifier).setDescription("Description") - verify(modifier).update() + assertThat(image.title, equalTo("Title")) + assertThat(image.description, equalTo("Description")) verify(core).touchConfiguration() } } @@ -132,9 +127,8 @@ class EditImagePageTest : WebPageTest(::EditImagePage) { addHttpRequestHeader("Host", "www.te.st") addHttpRequestPart("description", "Get http://www.te.st/KSK@GPL.txt") verifyRedirect("return.html") { - verify(modifier).setTitle("Title") - verify(modifier).setDescription("Get KSK@GPL.txt") - verify(modifier).update() + assertThat(image.title, equalTo("Title")) + assertThat(image.description, equalTo("Get KSK@GPL.txt")) verify(core).touchConfiguration() } } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/EditProfilePageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/EditProfilePageTest.kt index 74c385132..ac909ea1e 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/EditProfilePageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/EditProfilePageTest.kt @@ -1,6 +1,7 @@ package net.pterodactylus.sone.web.pages import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.web.* import net.pterodactylus.sone.web.page.* @@ -21,16 +22,13 @@ class EditProfilePageTest : WebPageTest(::EditProfilePage) { @Before fun setupProfile() { - val avatar = mock() - whenever(avatar.id).thenReturn("image-id") - whenever(avatar.sone).thenReturn(currentSone) profile.firstName = "First" profile.middleName = "Middle" profile.lastName = "Last" profile.birthDay = 31 profile.birthMonth = 12 profile.birthYear = 1999 - profile.setAvatar(avatar) + profile.setAvatar(ImageImpl("image-id").modify().setSone(currentSone).update()) whenever(currentSone.profile).thenReturn(profile) } @@ -120,9 +118,7 @@ class EditProfilePageTest : WebPageTest(::EditProfilePage) { @Test fun `post request with new avatar ID and save profile saves the profile and redirects back to profile edit page`() { - val newAvatar = mock() - whenever(newAvatar.sone).thenReturn(currentSone) - whenever(newAvatar.id).thenReturn("avatar-id") + val newAvatar = ImageImpl("avatar-id").modify().setSone(currentSone).update() addImage("avatar-id", newAvatar) verifySingleFieldCanBeChanged("avatarId", "avatar-id") { profile.avatar } } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt index 38ecce46d..4a2304c67 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt @@ -34,7 +34,7 @@ class ImageBrowserPageTest : WebPageTest(::ImageBrowserPage) { @Test fun `get request with album sets album and page in template context`() { - val album = mock() + val album = AlbumImpl(currentSone, "album-id") addAlbum("album-id", album) addHttpRequestParameter("album", "album-id") addHttpRequestParameter("page", "5") @@ -47,7 +47,7 @@ class ImageBrowserPageTest : WebPageTest(::ImageBrowserPage) { @Test fun `get request with image sets image in template context`() { - val image = mock() + val image = ImageImpl() addImage("image-id", image) addHttpRequestParameter("image", "image-id") verifyNoRedirect { diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPageTest.kt index 14cae74bb..23987b64b 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/KnownSonesPageTest.kt @@ -1,6 +1,7 @@ package net.pterodactylus.sone.web.pages import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.utils.* @@ -37,11 +38,12 @@ class KnownSonesPageTest : WebPageTest(::KnownSonesPage) { whenever(this.time).thenReturn(time) whenever(this.posts).thenReturn((0..(posts - 1)).map { mock() }) whenever(this.replies).thenReturn((0..(replies - 1)).map { mock() }.toSet()) - val album = mock() - whenever(album.images).thenReturn(((0..(images - 1)).map { mock() })) - val rootAlbum = mock().apply { - whenever(albums).thenReturn(listOf(album)) + val album = AlbumImpl(this) + repeat(images) { + ImageImpl().modify().setSone(this).update() + .also(album::addImage) } + val rootAlbum = AlbumImpl(this).also { it.addAlbum(album) } whenever(this.rootAlbum).thenReturn(rootAlbum) whenever(this.profile).thenReturn(mock()) whenever(id).thenReturn(name.toLowerCase()) diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/UploadImagePageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/UploadImagePageTest.kt index 370c0a794..4c99759e5 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/UploadImagePageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/UploadImagePageTest.kt @@ -1,10 +1,9 @@ package net.pterodactylus.sone.web.pages import net.pterodactylus.sone.data.* -import net.pterodactylus.sone.data.Image.* +import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.test.getInstance import net.pterodactylus.sone.test.mock -import net.pterodactylus.sone.test.mockBuilder import net.pterodactylus.sone.test.whenever import net.pterodactylus.sone.web.* import net.pterodactylus.sone.web.page.* @@ -20,10 +19,7 @@ import org.mockito.Mockito.eq */ class UploadImagePageTest : WebPageTest(::UploadImagePage) { - private val parentAlbum = mock().apply { - whenever(id).thenReturn("parent-id") - whenever(sone).thenReturn(currentSone) - } + private val parentAlbum = AlbumImpl(currentSone, "parent-id") @Test fun `page returns correct path`() { @@ -58,9 +54,9 @@ class UploadImagePageTest : WebPageTest(::UploadImagePage) { @Test fun `post request with parent that is not the current sone results in no permission error page`() { setMethod(POST) + val remoteAlbum = AlbumImpl(mock(), "parent-id") + addAlbum("parent-id", remoteAlbum) addHttpRequestPart("parent", "parent-id") - whenever(parentAlbum.sone).thenReturn(mock()) - addAlbum("parent-id", parentAlbum) verifyRedirect("noPermission.html") } @@ -97,19 +93,14 @@ class UploadImagePageTest : WebPageTest(::UploadImagePage) { addHttpRequestHeader("Host", "localhost:8888") addUploadedFile("image", "upload-image-value-image.png", "image/png", "upload-image-value-image.png") val temporaryImage = TemporaryImage("temp-image") - val imageModifier = mockBuilder() - val image = mock().apply { - whenever(modify()).thenReturn(imageModifier) - } + val image = ImageImpl() whenever(core.createTemporaryImage(eq("image/png"), any())).thenReturn(temporaryImage) whenever(core.createImage(currentSone, parentAlbum, temporaryImage)).thenReturn(image) verifyRedirect("imageBrowser.html?album=parent-id") { - verify(image).modify() - verify(imageModifier).setWidth(2) - verify(imageModifier).setHeight(1) - verify(imageModifier).setTitle("Title") - verify(imageModifier).setDescription("Description @ KSK@foo") - verify(imageModifier).update() + assertThat(image.width, equalTo(2)) + assertThat(image.height, equalTo(1)) + assertThat(image.title, equalTo("Title")) + assertThat(image.description, equalTo("Description @ KSK@foo")) } } From ee39c03a6a811300728183552afcede1d9f8e79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 21 Feb 2020 12:19:43 +0100 Subject: [PATCH 34/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20FUTURE=5FPOSTS?= =?UTF-8?q?=5FFILTER=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/data/Post.java | 11 -------- .../sone/fcp/GetPostFeedCommand.java | 4 ++- .../net/pterodactylus/sone/data/Post.kt | 8 ++++++ .../sone/web/pages/SearchPage.kt | 2 +- .../net/pterodactylus/sone/data/PostTest.kt | 26 +++++++++++++++++++ .../net/pterodactylus/sone/test/Mocks.kt | 3 ++- 6 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/net/pterodactylus/sone/data/Post.kt create mode 100644 src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt diff --git a/src/main/java/net/pterodactylus/sone/data/Post.java b/src/main/java/net/pterodactylus/sone/data/Post.java index a4a794e6b..991418595 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -22,7 +22,6 @@ import java.util.Comparator; import com.google.common.base.Optional; -import com.google.common.base.Predicate; /** * A post is a short message that a user writes in his Sone to let other users @@ -40,16 +39,6 @@ public int compare(Post leftPost, Post rightPost) { }; - /** Filter for posts with timestamps from the future. */ - public static final Predicate FUTURE_POSTS_FILTER = new Predicate() { - - @Override - public boolean apply(Post post) { - return (post != null) && (post.getTime() <= System.currentTimeMillis()); - } - - }; - // // ACCESSORS // diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java index 821c198b3..8197ba81f 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java @@ -32,6 +32,8 @@ import freenet.support.SimpleFieldSet; +import static net.pterodactylus.sone.data.PostKt.noFuturePost; + /** * Implementation of an FCP interface for other clients or plugins to * communicate with Sone. @@ -67,7 +69,7 @@ public Response execute(SimpleFieldSet parameters) throws FcpException { allPosts.addAll(friendSone.getPosts()); } allPosts.addAll(getCore().getDirectedPosts(sone.getId())); - allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER); + allPosts = Collections2.filter(allPosts, noFuturePost()::invoke); List sortedPosts = new ArrayList<>(allPosts); Collections.sort(sortedPosts, Post.NEWEST_FIRST); diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt new file mode 100644 index 000000000..704156186 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt @@ -0,0 +1,8 @@ +package net.pterodactylus.sone.data + +/** + * Predicate that returns whether a post is _not_ from the future, + * i.e. whether it should be visible now. + */ +@get:JvmName("noFuturePost") +val noFuturePost: (Post) -> Boolean = { it.time <= System.currentTimeMillis() } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt index 19eba1c81..7534bb42c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt @@ -58,7 +58,7 @@ class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: val postPagination = cache.get(phrases) { soneRequest.core.sones .flatMap(Sone::getPosts) - .filter { Post.FUTURE_POSTS_FILTER.apply(it) } + .filter(noFuturePost) .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache, soneRequest.core::getReplies) } }.apply { page = soneRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 } diff --git a/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt new file mode 100644 index 000000000..e5d9e972d --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt @@ -0,0 +1,26 @@ +package net.pterodactylus.sone.data + +import net.pterodactylus.sone.test.createPost +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import java.util.concurrent.TimeUnit.DAYS +import kotlin.test.Test + +/** + * Unit test for the utilities in `Post.kt`. + */ +class PostTest { + + @Test + fun `noFuturePost filter recognizes post from future`() { + val post = createPost(time = System.currentTimeMillis() + DAYS.toMillis(1)) + assertThat(noFuturePost(post), equalTo(false)) + } + + @Test + fun `noFuturePost filter recognizes post not from future`() { + val post = createPost(time = System.currentTimeMillis()) + assertThat(noFuturePost(post), equalTo(true)) + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index 65f279a30..17eae9ad7 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -43,11 +43,12 @@ fun createLocalSone(id: String? = createId()) = object : IdOnlySone(id) { } fun createRemoteSone(id: String? = createId()) = IdOnlySone(id) -fun createPost(text: String = "", sone: Sone = remoteSone1, known: Boolean = false): Post.EmptyPost { +fun createPost(text: String = "", sone: Sone = remoteSone1, known: Boolean = false, time: Long = 1): Post.EmptyPost { return object : Post.EmptyPost("post-id") { override fun getSone() = sone override fun getText() = text override fun isKnown() = known + override fun getTime() = time } } From 37ac474440aec3141cf609af25ee2332724dc7e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 21 Feb 2020 12:57:33 +0100 Subject: [PATCH 35/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20NEWEST=5FFIRST?= =?UTF-8?q?=20comparator=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/core/SoneInserter.java | 3 ++- .../net/pterodactylus/sone/data/Post.java | 12 ---------- .../sone/data/impl/SoneImpl.java | 3 ++- .../sone/fcp/GetPostFeedCommand.java | 3 ++- .../net/pterodactylus/sone/data/Post.kt | 8 +++++++ .../net/pterodactylus/sone/data/PostTest.kt | 23 +++++++++++++++++++ 6 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 651b62e41..fff30529c 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -22,6 +22,7 @@ import static java.util.concurrent.TimeUnit.*; import static java.util.logging.Logger.getLogger; import static java.util.stream.Collectors.toList; +import static net.pterodactylus.sone.data.PostKt.newestFirst; import java.io.*; import java.nio.charset.Charset; @@ -308,7 +309,7 @@ public InsertInformation(Sone sone) { soneProperties.put("name", sone.getName()); soneProperties.put("time", currentTimeMillis()); soneProperties.put("profile", sone.getProfile()); - soneProperties.put("posts", Ordering.from(Post.NEWEST_FIRST).sortedCopy(sone.getPosts())); + soneProperties.put("posts", Ordering.from(newestFirst()).sortedCopy(sone.getPosts())); soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds())); diff --git a/src/main/java/net/pterodactylus/sone/data/Post.java b/src/main/java/net/pterodactylus/sone/data/Post.java index 991418595..d4d34e676 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -19,8 +19,6 @@ import static com.google.common.base.Optional.absent; -import java.util.Comparator; - import com.google.common.base.Optional; /** @@ -29,16 +27,6 @@ */ public interface Post extends Identified { - /** Comparator for posts, sorts descending by time. */ - public static final Comparator NEWEST_FIRST = new Comparator() { - - @Override - public int compare(Post leftPost, Post rightPost) { - return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, rightPost.getTime() - leftPost.getTime())); - } - - }; - // // ACCESSORS // diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index 8f5a50f41..df8832fc7 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -21,6 +21,7 @@ import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.logging.Logger.getLogger; +import static net.pterodactylus.sone.data.PostKt.newestFirst; import static net.pterodactylus.sone.data.SoneKt.*; import java.net.MalformedURLException; @@ -365,7 +366,7 @@ public List getPosts() { synchronized (this) { sortedPosts = new ArrayList<>(posts); } - Collections.sort(sortedPosts, Post.NEWEST_FIRST); + sortedPosts.sort(newestFirst()); return sortedPosts; } diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java index 8197ba81f..d340426d5 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java @@ -32,6 +32,7 @@ import freenet.support.SimpleFieldSet; +import static net.pterodactylus.sone.data.PostKt.newestFirst; import static net.pterodactylus.sone.data.PostKt.noFuturePost; /** @@ -72,7 +73,7 @@ public Response execute(SimpleFieldSet parameters) throws FcpException { allPosts = Collections2.filter(allPosts, noFuturePost()::invoke); List sortedPosts = new ArrayList<>(allPosts); - Collections.sort(sortedPosts, Post.NEWEST_FIRST); + sortedPosts.sort(newestFirst()); if (sortedPosts.size() < startPost) { return new Response("PostFeed", encodePosts(Collections. emptyList(), "Posts.", false)); diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt index 704156186..955196a03 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt @@ -1,8 +1,16 @@ package net.pterodactylus.sone.data +import java.util.Comparator.comparing + /** * Predicate that returns whether a post is _not_ from the future, * i.e. whether it should be visible now. */ @get:JvmName("noFuturePost") val noFuturePost: (Post) -> Boolean = { it.time <= System.currentTimeMillis() } + +/** + * Comparator that orders posts by their time, newest posts first. + */ +@get:JvmName("newestFirst") +val newestFirst: Comparator = comparing(Post::getTime).reversed() diff --git a/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt index e5d9e972d..ebf96b1f6 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt @@ -3,6 +3,8 @@ package net.pterodactylus.sone.data import net.pterodactylus.sone.test.createPost import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.greaterThan +import org.hamcrest.Matchers.lessThan import java.util.concurrent.TimeUnit.DAYS import kotlin.test.Test @@ -23,4 +25,25 @@ class PostTest { assertThat(noFuturePost(post), equalTo(true)) } + @Test + fun `newestFirst comparator returns less-than 0 if first is newer than second`() { + val newerPost = createPost(time = 2000) + val olderPost = createPost(time = 1000) + assertThat(newestFirst.compare(newerPost, olderPost), lessThan(0)) + } + + @Test + fun `newestFirst comparator returns greater-than 0 if first is older than second`() { + val newerPost = createPost(time = 2000) + val olderPost = createPost(time = 1000) + assertThat(newestFirst.compare(olderPost, newerPost), greaterThan(0)) + } + + @Test + fun `newestFirst comparator returns 0 if first and second are the same age`() { + val post1 = createPost(time = 1000) + val post2 = createPost(time = 1000) + assertThat(newestFirst.compare(post2, post1), equalTo(0)) + } + } From f95e5e291e99f09d3b7e813059e7f7b80c3c12ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 21 Feb 2020 13:32:18 +0100 Subject: [PATCH 36/87] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Replace=20=E2=80=9Cn?= =?UTF-8?q?ormal=E2=80=9D=20methods=20with=20extension=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/fcp/AbstractSoneCommand.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt b/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt index 08e059374..f884e3429 100644 --- a/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt +++ b/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt @@ -44,12 +44,12 @@ abstract class AbstractSoneCommand val requiresWriteAccess: Boolean = false) : AbstractCommand() { @Throws(FcpException::class) - protected fun getSone(simpleFieldSet: SimpleFieldSet, parameterName: String, localOnly: Boolean): Sone = - getSone(simpleFieldSet, parameterName, localOnly, true).get() + protected fun SimpleFieldSet.getSone(parameterName: String, localOnly: Boolean): Sone = + getSone(parameterName, localOnly, true).get() @Throws(FcpException::class) - protected fun getSone(simpleFieldSet: SimpleFieldSet, parameterName: String, localOnly: Boolean, mandatory: Boolean): Optional { - val soneId = simpleFieldSet.get(parameterName) + protected fun SimpleFieldSet.getSone(parameterName: String, localOnly: Boolean, mandatory: Boolean): Optional { + val soneId = get(parameterName) .throwOnNullIf(mandatory) { FcpException("Could not load Sone ID from “$parameterName”.") } ?: return Optional.absent() val sone = core.getSone(soneId) @@ -60,9 +60,9 @@ abstract class AbstractSoneCommand } @Throws(FcpException::class) - protected fun getPost(simpleFieldSet: SimpleFieldSet, parameterName: String): Post { + protected fun SimpleFieldSet.getPost(parameterName: String): Post { try { - val postId = simpleFieldSet.getString(parameterName) + val postId = getString(parameterName) return core.getPost(postId) ?: throw FcpException("Could not load post from “$postId”.") } catch (fspe1: FSParseException) { @@ -71,9 +71,9 @@ abstract class AbstractSoneCommand } @Throws(FcpException::class) - protected fun getReply(simpleFieldSet: SimpleFieldSet, parameterName: String): PostReply { + protected fun SimpleFieldSet.getReply(parameterName: String): PostReply { try { - val replyId = simpleFieldSet.getString(parameterName) + val replyId = getString(parameterName) return core.getPostReply(replyId) ?: throw FcpException("Could not load reply from “$replyId”.") } catch (fspe1: FSParseException) { From 9d47ddbac6b14aed85b09d482d3db9a17126d859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 22 Feb 2020 23:13:03 +0100 Subject: [PATCH 37/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20HAS=5FPOST=5FFILT?= =?UTF-8?q?ER=20from=20post=20reply=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/data/PostReply.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/PostReply.java b/src/main/java/net/pterodactylus/sone/data/PostReply.java index f5ffea3f8..6db387649 100644 --- a/src/main/java/net/pterodactylus/sone/data/PostReply.java +++ b/src/main/java/net/pterodactylus/sone/data/PostReply.java @@ -18,7 +18,6 @@ package net.pterodactylus.sone.data; import com.google.common.base.Optional; -import com.google.common.base.Predicate; /** * A reply is like a {@link Post} but can never be posted on its own, it always @@ -26,18 +25,6 @@ */ public interface PostReply extends Reply { - /** - * Filter that selects {@link PostReply}s that have a - * {@link Optional#isPresent() present} {@link #getPost() post}. - */ - public static final Predicate HAS_POST_FILTER = new Predicate() { - - @Override - public boolean apply(PostReply postReply) { - return (postReply != null) && postReply.getPost().isPresent(); - } - }; - /** * Returns the ID of the post this reply refers to. * From be005506d77e048c7de43f08771b072951ab82f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 22 Feb 2020 23:31:52 +0100 Subject: [PATCH 38/87] =?UTF-8?q?=F0=9F=8E=A8=20Rename=20=E2=80=9Cnewest?= =?UTF-8?q?=20first=E2=80=9D=20post=20comparator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/core/SoneInserter.java | 4 ++-- .../java/net/pterodactylus/sone/data/impl/SoneImpl.java | 4 ++-- .../java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java | 4 ++-- src/main/kotlin/net/pterodactylus/sone/data/Post.kt | 4 ++-- src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index fff30529c..ffbed4d25 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -22,7 +22,7 @@ import static java.util.concurrent.TimeUnit.*; import static java.util.logging.Logger.getLogger; import static java.util.stream.Collectors.toList; -import static net.pterodactylus.sone.data.PostKt.newestFirst; +import static net.pterodactylus.sone.data.PostKt.newestPostFirst; import java.io.*; import java.nio.charset.Charset; @@ -309,7 +309,7 @@ public InsertInformation(Sone sone) { soneProperties.put("name", sone.getName()); soneProperties.put("time", currentTimeMillis()); soneProperties.put("profile", sone.getProfile()); - soneProperties.put("posts", Ordering.from(newestFirst()).sortedCopy(sone.getPosts())); + soneProperties.put("posts", Ordering.from(newestPostFirst()).sortedCopy(sone.getPosts())); soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds())); diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index df8832fc7..9baaba94f 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -21,7 +21,7 @@ import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.logging.Logger.getLogger; -import static net.pterodactylus.sone.data.PostKt.newestFirst; +import static net.pterodactylus.sone.data.PostKt.newestPostFirst; import static net.pterodactylus.sone.data.SoneKt.*; import java.net.MalformedURLException; @@ -366,7 +366,7 @@ public List getPosts() { synchronized (this) { sortedPosts = new ArrayList<>(posts); } - sortedPosts.sort(newestFirst()); + sortedPosts.sort(newestPostFirst()); return sortedPosts; } diff --git a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java index d340426d5..05c234965 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java @@ -32,7 +32,7 @@ import freenet.support.SimpleFieldSet; -import static net.pterodactylus.sone.data.PostKt.newestFirst; +import static net.pterodactylus.sone.data.PostKt.newestPostFirst; import static net.pterodactylus.sone.data.PostKt.noFuturePost; /** @@ -73,7 +73,7 @@ public Response execute(SimpleFieldSet parameters) throws FcpException { allPosts = Collections2.filter(allPosts, noFuturePost()::invoke); List sortedPosts = new ArrayList<>(allPosts); - sortedPosts.sort(newestFirst()); + sortedPosts.sort(newestPostFirst()); if (sortedPosts.size() < startPost) { return new Response("PostFeed", encodePosts(Collections. emptyList(), "Posts.", false)); diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt index 955196a03..d87bd3c2e 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Post.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt @@ -12,5 +12,5 @@ val noFuturePost: (Post) -> Boolean = { it.time <= System.currentTimeMillis() } /** * Comparator that orders posts by their time, newest posts first. */ -@get:JvmName("newestFirst") -val newestFirst: Comparator = comparing(Post::getTime).reversed() +@get:JvmName("newestPostFirst") +val newestPostFirst: Comparator = comparing(Post::getTime).reversed() diff --git a/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt index ebf96b1f6..73f849841 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt @@ -29,21 +29,21 @@ class PostTest { fun `newestFirst comparator returns less-than 0 if first is newer than second`() { val newerPost = createPost(time = 2000) val olderPost = createPost(time = 1000) - assertThat(newestFirst.compare(newerPost, olderPost), lessThan(0)) + assertThat(newestPostFirst.compare(newerPost, olderPost), lessThan(0)) } @Test fun `newestFirst comparator returns greater-than 0 if first is older than second`() { val newerPost = createPost(time = 2000) val olderPost = createPost(time = 1000) - assertThat(newestFirst.compare(olderPost, newerPost), greaterThan(0)) + assertThat(newestPostFirst.compare(olderPost, newerPost), greaterThan(0)) } @Test fun `newestFirst comparator returns 0 if first and second are the same age`() { val post1 = createPost(time = 1000) val post2 = createPost(time = 1000) - assertThat(newestFirst.compare(post2, post1), equalTo(0)) + assertThat(newestPostFirst.compare(post2, post1), equalTo(0)) } } From f049280a40ddf05f02400e7f0d93a24dea4545c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 22 Feb 2020 23:37:38 +0100 Subject: [PATCH 39/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20reply=20comparat?= =?UTF-8?q?or=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/core/SoneInserter.java | 3 +- .../net/pterodactylus/sone/data/Reply.java | 13 ----- .../sone/data/impl/SoneImpl.java | 3 +- .../net/pterodactylus/sone/data/Reply.kt | 27 ++++++++++ .../sone/database/memory/MemoryDatabase.kt | 6 +-- .../net/pterodactylus/sone/data/ReplyTest.kt | 50 +++++++++++++++++++ .../net/pterodactylus/sone/test/Mocks.kt | 4 +- 7 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 src/main/kotlin/net/pterodactylus/sone/data/Reply.kt create mode 100644 src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index ffbed4d25..670b2c8b9 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -23,6 +23,7 @@ import static java.util.logging.Logger.getLogger; import static java.util.stream.Collectors.toList; import static net.pterodactylus.sone.data.PostKt.newestPostFirst; +import static net.pterodactylus.sone.data.ReplyKt.newestReplyFirst; import java.io.*; import java.nio.charset.Charset; @@ -310,7 +311,7 @@ public InsertInformation(Sone sone) { soneProperties.put("time", currentTimeMillis()); soneProperties.put("profile", sone.getProfile()); soneProperties.put("posts", Ordering.from(newestPostFirst()).sortedCopy(sone.getPosts())); - soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies())); + soneProperties.put("replies", Ordering.from(newestReplyFirst()).sortedCopy(sone.getReplies())); soneProperties.put("likedPostIds", new HashSet<>(sone.getLikedPostIds())); soneProperties.put("likedReplyIds", new HashSet<>(sone.getLikedReplyIds())); soneProperties.put("albums", SoneKt.getAllAlbums(sone).stream().filter(AlbumKt.notEmpty()::invoke).collect(toList())); diff --git a/src/main/java/net/pterodactylus/sone/data/Reply.java b/src/main/java/net/pterodactylus/sone/data/Reply.java index 120575ead..705b1e417 100644 --- a/src/main/java/net/pterodactylus/sone/data/Reply.java +++ b/src/main/java/net/pterodactylus/sone/data/Reply.java @@ -29,19 +29,6 @@ */ public interface Reply> extends Identified { - /** Comparator that sorts replies ascending by time. */ - public static final Comparator> TIME_COMPARATOR = new Comparator>() { - - /** - * {@inheritDoc} - */ - @Override - public int compare(Reply leftReply, Reply rightReply) { - return (int) Math.max(Integer.MIN_VALUE, Math.min(Integer.MAX_VALUE, leftReply.getTime() - rightReply.getTime())); - } - - }; - /** Filter for replies with timestamps from the future. */ public static final Predicate> FUTURE_REPLY_FILTER = new Predicate>() { diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index 9baaba94f..a197c465f 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -22,6 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.logging.Logger.getLogger; import static net.pterodactylus.sone.data.PostKt.newestPostFirst; +import static net.pterodactylus.sone.data.ReplyKt.newestReplyFirst; import static net.pterodactylus.sone.data.SoneKt.*; import java.net.MalformedURLException; @@ -626,7 +627,7 @@ public synchronized String getFingerprint() { hash.putString(")", UTF_8); List replies = new ArrayList<>(getReplies()); - Collections.sort(replies, Reply.TIME_COMPARATOR); + replies.sort(newestReplyFirst().reversed()); hash.putString("Replies(", UTF_8); for (PostReply reply : replies) { hash.putString("Reply(", UTF_8).putString(reply.getId(), UTF_8).putString(")", UTF_8); diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt new file mode 100644 index 000000000..9a3ec6454 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt @@ -0,0 +1,27 @@ +/** + * Sone - Reply.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +import java.util.Comparator.comparing + +/** + * Comparator that orders replies by their time, newest replies first. + */ +@get:JvmName("newestReplyFirst") +val newestReplyFirst: Comparator> = + comparing(Reply<*>::getTime).reversed() diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt index 5c324866a..e943b38b1 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -28,12 +28,12 @@ import net.pterodactylus.sone.data.Album import net.pterodactylus.sone.data.Image import net.pterodactylus.sone.data.Post import net.pterodactylus.sone.data.PostReply -import net.pterodactylus.sone.data.Reply.TIME_COMPARATOR import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.allAlbums import net.pterodactylus.sone.data.allImages import net.pterodactylus.sone.data.impl.AlbumBuilderImpl import net.pterodactylus.sone.data.impl.ImageBuilderImpl +import net.pterodactylus.sone.data.newestReplyFirst import net.pterodactylus.sone.database.AlbumBuilder import net.pterodactylus.sone.database.Database import net.pterodactylus.sone.database.DatabaseException @@ -62,7 +62,7 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio private val sonePosts: Multimap = HashMultimap.create() private val knownPosts = mutableSetOf() private val allPostReplies = mutableMapOf() - private val sonePostReplies: Multimap = TreeMultimap.create(Comparator { leftString, rightString -> leftString.compareTo(rightString) }, TIME_COMPARATOR) + private val sonePostReplies: Multimap = TreeMultimap.create(Comparator { leftString, rightString -> leftString.compareTo(rightString) }, newestReplyFirst) private val knownPostReplies = mutableSetOf() private val allAlbums = mutableMapOf() private val soneAlbums: Multimap = HashMultimap.create() @@ -227,7 +227,7 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio readLock.withLock { allPostReplies.values .filter { it.postId == postId } - .sortedWith(TIME_COMPARATOR) + .sortedWith(newestReplyFirst.reversed()) } override fun newPostReplyBuilder(): PostReplyBuilder = diff --git a/src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt new file mode 100644 index 000000000..3289b7d9c --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt @@ -0,0 +1,50 @@ +/** + * Sone - ReplyTest.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.data + +import net.pterodactylus.sone.test.emptyPostReply +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.greaterThan +import org.hamcrest.Matchers.lessThan +import kotlin.test.Test + +class ReplyTest { + + @Test + fun `newestReplyFirst comparator returns less-than 0 is first reply is newer than second`() { + val newerReply = emptyPostReply(time = 2000) + val olderReply = emptyPostReply(time = 1000) + assertThat(newestReplyFirst.compare(newerReply, olderReply), lessThan(0)) + } + + @Test + fun `newestReplyFirst comparator returns greater-than 0 is first reply is older than second`() { + val newerReply = emptyPostReply(time = 2000) + val olderReply = emptyPostReply(time = 1000) + assertThat(newestReplyFirst.compare(olderReply, newerReply), greaterThan(0)) + } + + @Test + fun `newestReplyFirst comparator returns 0 is first and second reply have same age`() { + val reply1 = emptyPostReply(time = 1000) + val reply2 = emptyPostReply(time = 1000) + assertThat(newestReplyFirst.compare(reply1, reply2), equalTo(0)) + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index 17eae9ad7..ae2ecd6a8 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -52,12 +52,12 @@ fun createPost(text: String = "", sone: Sone = remoteSone1, known: Boolean = fal } } -fun emptyPostReply(text: String = "", post: Post? = createPost(), sone: Sone = remoteSone1, known: Boolean = false) = object : PostReply { +fun emptyPostReply(text: String = "", post: Post? = createPost(), sone: Sone = remoteSone1, known: Boolean = false, time: Long = 1) = object : PostReply { override val id = "reply-id" override fun getSone() = sone override fun getPostId() = post!!.id override fun getPost(): Optional = Optional.fromNullable(post) - override fun getTime() = 1L + override fun getTime() = time override fun getText() = text override fun isKnown() = known override fun setKnown(known: Boolean): PostReply = this From 0ace0221fc20ef93457b8c62c6ac12286c1aa12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 22 Feb 2020 23:44:47 +0100 Subject: [PATCH 40/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20future=20reply?= =?UTF-8?q?=20filter=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/data/Reply.java | 17 ----------------- .../kotlin/net/pterodactylus/sone/data/Reply.kt | 7 +++++++ .../pterodactylus/sone/template/PostAccessor.kt | 2 +- .../pterodactylus/sone/web/pages/SearchPage.kt | 2 +- .../net/pterodactylus/sone/data/ReplyTest.kt | 13 +++++++++++++ 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/data/Reply.java b/src/main/java/net/pterodactylus/sone/data/Reply.java index 705b1e417..918fd01d9 100644 --- a/src/main/java/net/pterodactylus/sone/data/Reply.java +++ b/src/main/java/net/pterodactylus/sone/data/Reply.java @@ -17,10 +17,6 @@ package net.pterodactylus.sone.data; -import java.util.Comparator; - -import com.google.common.base.Predicate; - /** * Defines methods common for all replies. * @@ -29,19 +25,6 @@ */ public interface Reply> extends Identified { - /** Filter for replies with timestamps from the future. */ - public static final Predicate> FUTURE_REPLY_FILTER = new Predicate>() { - - /** - * {@inheritDoc} - */ - @Override - public boolean apply(Reply reply) { - return (reply != null) && (reply.getTime() <= System.currentTimeMillis()); - } - - }; - /** * Returns the ID of the reply. * diff --git a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt index 9a3ec6454..cfc940a20 100644 --- a/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt +++ b/src/main/kotlin/net/pterodactylus/sone/data/Reply.kt @@ -25,3 +25,10 @@ import java.util.Comparator.comparing @get:JvmName("newestReplyFirst") val newestReplyFirst: Comparator> = comparing(Reply<*>::getTime).reversed() + +/** + * Predicate that returns whether a reply is _not_ from the future, + * i.e. whether it should be visible now. + */ +val noFutureReply: (Reply<*>) -> Boolean = + { it.getTime() <= System.currentTimeMillis() } diff --git a/src/main/kotlin/net/pterodactylus/sone/template/PostAccessor.kt b/src/main/kotlin/net/pterodactylus/sone/template/PostAccessor.kt index ab7f6ba53..42239aa43 100644 --- a/src/main/kotlin/net/pterodactylus/sone/template/PostAccessor.kt +++ b/src/main/kotlin/net/pterodactylus/sone/template/PostAccessor.kt @@ -51,5 +51,5 @@ class PostAccessor(private val core: Core) : ReflectionAccessor() { } -private fun Core.getReplies(post: Post) = getReplies(post.id).filter { Reply.FUTURE_REPLY_FILTER.apply(it) } +private fun Core.getReplies(post: Post) = getReplies(post.id).filter(noFutureReply) private val TemplateContext?.currentSone: Sone? get() = this?.get("currentSone") as? Sone diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt index 7534bb42c..9bbc3e5ba 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt @@ -88,7 +88,7 @@ class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer: private fun Post.allText(soneNameCache: (Sone) -> String, getReplies: (String) -> Collection) = (text + recipient.orNull()?.let { " ${soneNameCache(it)}" } + getReplies(id) - .filter { PostReply.FUTURE_REPLY_FILTER.apply(it) } + .filter(noFutureReply) .map { "${soneNameCache(it.sone)} ${it.text}" }.joinToString(" ", " ")).toLowerCase() private fun Iterable.indicesFor(text: String, predicate: (Phrase) -> Boolean) = diff --git a/src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt b/src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt index 3289b7d9c..d7f7138fb 100644 --- a/src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/data/ReplyTest.kt @@ -22,6 +22,7 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.greaterThan import org.hamcrest.Matchers.lessThan +import java.util.concurrent.TimeUnit.DAYS import kotlin.test.Test class ReplyTest { @@ -47,4 +48,16 @@ class ReplyTest { assertThat(newestReplyFirst.compare(reply1, reply2), equalTo(0)) } + @Test + fun `noFutureReply filter recognizes reply from the future`() { + val futureReply = emptyPostReply(time = System.currentTimeMillis() + DAYS.toMillis(1)) + assertThat(noFutureReply(futureReply), equalTo(false)) + } + + @Test + fun `noFutureReply filter recognizes reply from the present`() { + val futureReply = emptyPostReply(time = System.currentTimeMillis()) + assertThat(noFutureReply(futureReply), equalTo(true)) + } + } From b1e6cda6c077a95eab43ab371a75afd92ef820a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 23 Feb 2020 11:54:09 +0100 Subject: [PATCH 41/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20setKnown()=20meth?= =?UTF-8?q?od=20from=20replies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/core/Core.java | 8 ++++---- src/main/java/net/pterodactylus/sone/data/Reply.java | 9 --------- .../sone/database/memory/MemoryPostReply.java | 9 --------- .../net/pterodactylus/sone/core/UpdatedSoneProcessor.kt | 2 +- .../net/pterodactylus/sone/database/PostReplyStore.kt | 1 + .../pterodactylus/sone/database/memory/MemoryDatabase.kt | 7 ++----- .../pterodactylus/sone/core/UpdatedSoneProcessorTest.kt | 4 ++-- .../sone/database/memory/MemoryDatabaseTest.kt | 6 +++--- src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt | 1 - 9 files changed, 13 insertions(+), 34 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index c4cc9cfc6..da1b2f853 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -797,9 +797,9 @@ private List collectEventsForChangesInSone(Sone oldSone, Sone newSone) { } for (PostReply postReply : soneComparison.getNewPostReplies()) { if (postReply.getSone().equals(newSone)) { - postReply.setKnown(true); + database.setPostReplyKnown(postReply); } else if (postReply.getTime() < database.getFollowingTime(newSone.getId())) { - postReply.setKnown(true); + database.setPostReplyKnown(postReply); } else if (!postReply.isKnown()) { events.add(new NewPostReplyFoundEvent(postReply)); } @@ -976,7 +976,7 @@ public void loadSone(Sone sone) { post.setKnown(true); } for (PostReply reply : replies) { - reply.setKnown(true); + database.setPostReplyKnown(reply); } logger.info(String.format("Sone loaded successfully: %s", sone)); @@ -1118,7 +1118,7 @@ public void deleteReply(PostReply reply) { */ public void markReplyKnown(PostReply reply) { boolean previouslyKnown = reply.isKnown(); - reply.setKnown(true); + database.setPostReplyKnown(reply); eventBus.post(new MarkPostReplyKnownEvent(reply)); if (!previouslyKnown) { touchConfiguration(); diff --git a/src/main/java/net/pterodactylus/sone/data/Reply.java b/src/main/java/net/pterodactylus/sone/data/Reply.java index 918fd01d9..0cf2a52c5 100644 --- a/src/main/java/net/pterodactylus/sone/data/Reply.java +++ b/src/main/java/net/pterodactylus/sone/data/Reply.java @@ -60,13 +60,4 @@ public interface Reply> extends Identified { */ public boolean isKnown(); - /** - * Sets whether this reply is known. - * - * @param known - * {@code true} if this reply is known, {@code false} otherwise - * @return This reply - */ - public T setKnown(boolean known); - } diff --git a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java index ef21a7596..5764622df 100644 --- a/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java +++ b/src/main/java/net/pterodactylus/sone/database/memory/MemoryPostReply.java @@ -124,15 +124,6 @@ public boolean isKnown() { return database.isPostReplyKnown(this); } - /** - * {@inheritDocs} - */ - @Override - public PostReply setKnown(boolean known) { - database.setPostReplyKnown(this, known); - return this; - } - // // POSTREPLY METHODS // diff --git a/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt b/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt index 28bac6d0c..54af30a6c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt @@ -42,7 +42,7 @@ abstract class BasicUpdateSoneProcessor(private val database: Database, private .map { PostRemovedEvent(it) } .forEach(eventBus::post) newPostReplies - .onEach { postReply -> if (postReply.time <= sone.followingTime) postReply.isKnown = true } + .onEach { postReply -> if (postReply.time <= sone.followingTime) database.setPostReplyKnown(postReply) } .mapNotNull { postReply -> postReply.isKnown.ifFalse { NewPostReplyFoundEvent(postReply) } } .forEach(eventBus::post) removedPostReplies diff --git a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt index 671ab532c..859cbb0b1 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/PostReplyStore.kt @@ -26,5 +26,6 @@ interface PostReplyStore { fun storePostReply(postReply: PostReply) fun removePostReply(postReply: PostReply) + fun setPostReplyKnown(postReply: PostReply) } diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt index e943b38b1..fd7ac4861 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -297,12 +297,9 @@ class MemoryDatabase @Inject constructor(private val configuration: Configuratio protected fun isPostReplyKnown(postReply: PostReply) = readLock.withLock { postReply.id in knownPostReplies } - fun setPostReplyKnown(postReply: PostReply, known: Boolean): Unit = + override fun setPostReplyKnown(postReply: PostReply): Unit = writeLock.withLock { - if (known) - knownPostReplies.add(postReply.id) - else - knownPostReplies.remove(postReply.id) + knownPostReplies.add(postReply.id) saveKnownPostReplies() } diff --git a/src/test/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessorTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessorTest.kt index 9e7c71e68..356ea9cd6 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessorTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessorTest.kt @@ -123,14 +123,14 @@ class UpdatedSoneProcessorTest { @Test fun `updated Sone processor does not mark new reply as known if sone was not followed after reply`() { updatedSoneProcessor.updateSone(newSone) - verify(postReplies[2], never()).isKnown = true + verify(database, never()).setPostReplyKnown(postReplies[2]) } @Test fun `updated Sone processor marks new reply as known if sone was followed after reply`() { whenever(database.getFollowingTime("sone")).thenReturn(3500L) updatedSoneProcessor.updateSone(newSone) - verify(postReplies[2]).isKnown = true + verify(database).setPostReplyKnown(postReplies[2]) } @Test diff --git a/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt b/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt index 150336f9a..973047ff7 100644 --- a/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.kt @@ -405,7 +405,7 @@ class MemoryDatabaseTest { prepareConfigurationValues() val postReply = mock() whenever(postReply.id).thenReturn("post-reply-id") - memoryDatabase.setPostReplyKnown(postReply, true) + memoryDatabase.setPostReplyKnown(postReply) assertThat(configuration.getStringValue("KnownReplies/0/ID").value, equalTo("post-reply-id")) assertThat(configuration.getStringValue("KnownReplies/1/ID").value, equalTo(null)) } @@ -435,8 +435,8 @@ class MemoryDatabaseTest { prepareConfigurationValues() val postReply = mock() whenever(postReply.id).thenReturn("post-reply-id") - memoryDatabase.setPostReplyKnown(postReply, true) - memoryDatabase.setPostReplyKnown(postReply, true) + memoryDatabase.setPostReplyKnown(postReply) + memoryDatabase.setPostReplyKnown(postReply) verify(configuration, times(1)).getStringValue("KnownReplies/1/ID") } diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index ae2ecd6a8..d556f6bde 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -60,7 +60,6 @@ fun emptyPostReply(text: String = "", post: Post? = createPost(), sone: Sone = r override fun getTime() = time override fun getText() = text override fun isKnown() = known - override fun setKnown(known: Boolean): PostReply = this } fun createImage(sone: Sone): Image = From 3a7c862787dfdd3d7fe18fa16e1a577a0d89a53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 23 Feb 2020 11:57:41 +0100 Subject: [PATCH 42/87] =?UTF-8?q?=F0=9F=8E=A8=20Clean=20up=20some=20import?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/database/memory/MemoryDatabase.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt index fd7ac4861..3b5a6a934 100644 --- a/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt +++ b/src/main/kotlin/net/pterodactylus/sone/database/memory/MemoryDatabase.kt @@ -21,7 +21,8 @@ import com.google.common.base.Preconditions.checkNotNull import com.google.common.collect.HashMultimap import com.google.common.collect.Multimap import com.google.common.collect.TreeMultimap -import com.google.common.util.concurrent.* +import com.google.common.util.concurrent.AbstractService +import com.google.common.util.concurrent.RateLimiter import com.google.inject.Inject import com.google.inject.Singleton import net.pterodactylus.sone.data.Album @@ -41,7 +42,8 @@ import net.pterodactylus.sone.database.ImageBuilder import net.pterodactylus.sone.database.PostBuilder import net.pterodactylus.sone.database.PostDatabase import net.pterodactylus.sone.database.PostReplyBuilder -import net.pterodactylus.sone.utils.* +import net.pterodactylus.sone.utils.ifTrue +import net.pterodactylus.sone.utils.unit import net.pterodactylus.util.config.Configuration import net.pterodactylus.util.config.ConfigurationException import java.util.concurrent.locks.ReentrantReadWriteLock From ae459549f73eea2b76291e1ed6934034fd2b1f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 3 Mar 2020 21:45:13 +0100 Subject: [PATCH 43/87] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20Kotlin=20t?= =?UTF-8?q?o=201.3.70?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d1bf4d994..3f44ffa37 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '1.3.61' - id 'org.jetbrains.kotlin.plugin.noarg' version '1.3.61' + id 'org.jetbrains.kotlin.jvm' version '1.3.70' + id 'org.jetbrains.kotlin.plugin.noarg' version '1.3.70' id 'info.solidsoft.pitest' version '1.4.5' } From 6918253e33ff37683d3549208bd0b126942a9508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 3 Mar 2020 22:00:16 +0100 Subject: [PATCH 44/87] =?UTF-8?q?=F0=9F=8E=A8=20Use=20different=20way=20to?= =?UTF-8?q?=20set=20kotlin=20compiler=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3f44ffa37..c7c24596e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,4 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id 'org.jetbrains.kotlin.jvm' version '1.3.70' @@ -22,7 +23,7 @@ tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } -compileKotlin { +tasks.withType(KotlinCompile) { kotlinOptions { jvmTarget = "1.8" } From 7625c3f0be6df7e5fe2fc3b58019614f8a915888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 3 Mar 2020 22:00:35 +0100 Subject: [PATCH 45/87] =?UTF-8?q?=F0=9F=8E=A8=20Whitespace=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index c7c24596e..b8ea16cbc 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ configurations { } compile.extendsFrom provided } - + dependencies { provided group: 'org.freenetproject', name: 'fred', version: '0.7.5.1475' provided group: 'org.freenetproject', name: 'freenet-ext', version: '29' From fe1b8d1a73e62d00dfd8cbe85a4defd0adb8615b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 3 Mar 2020 22:00:46 +0100 Subject: [PATCH 46/87] =?UTF-8?q?=F0=9F=8E=A8=20Use=20non-deprecated=20pro?= =?UTF-8?q?perty?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b8ea16cbc..870a07cd1 100644 --- a/build.gradle +++ b/build.gradle @@ -84,7 +84,7 @@ test { } task fatJar(type: Jar) { - archiveName = project.name.toLowerCase() + '-jar-with-dependencies.jar' + archiveFileName = project.name.toLowerCase() + '-jar-with-dependencies.jar' from { (configurations.runtime - configurations.provided).collect { it.isDirectory() ? it : zipTree(it) } } manifest { attributes('Plugin-Main-Class': 'net.pterodactylus.sone.main.SonePlugin') From ef88dd2add0bad9d08dfecbb49bd7577bfafbb50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 6 Apr 2020 22:29:18 +0200 Subject: [PATCH 47/87] =?UTF-8?q?=E2=9C=85=20Add=20test=20for=20page=20toa?= =?UTF-8?q?dlet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/web/page/PageToadletTest.kt | 231 ++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt new file mode 100644 index 000000000..1d6f26c31 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt @@ -0,0 +1,231 @@ +/* + * Sone - PageToadletTest.kt - Copyright © 2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.page + +import freenet.client.HighLevelSimpleClient +import freenet.clients.http.Cookie +import freenet.clients.http.FProxyFetchInProgress.REFILTER_POLICY +import freenet.clients.http.LinkEnabledCallback +import freenet.clients.http.PageMaker +import freenet.clients.http.ReceivedCookie +import freenet.clients.http.SessionManager +import freenet.clients.http.Toadlet +import freenet.clients.http.ToadletContainer +import freenet.clients.http.ToadletContext +import freenet.clients.http.bookmark.BookmarkManager +import freenet.node.useralerts.UserAlertManager +import freenet.support.HTMLNode +import freenet.support.MultiValueTable +import freenet.support.api.Bucket +import freenet.support.api.BucketFactory +import freenet.support.api.HTTPRequest +import freenet.support.io.ArrayBucket +import net.pterodactylus.sone.test.deepMock +import net.pterodactylus.sone.test.mock +import net.pterodactylus.sone.test.whenever +import net.pterodactylus.util.web.Method +import net.pterodactylus.util.web.Page +import net.pterodactylus.util.web.Response +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.arrayContaining +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.sameInstance +import org.junit.Test +import org.mockito.ArgumentMatchers.anyLong +import java.net.URI +import java.util.Date +import kotlin.text.Charsets.UTF_8 + +/** + * Unit test for PageToadletTest. + */ +class PageToadletTest { + + private val highLevelSimpleClient = mock() + private val sessionManager = mock() + private val httpRequest = mock() + private val toadletContext = deepMock() + + init { + whenever(toadletContext.bucketFactory.makeBucket(anyLong())).then { ArrayBucket() } + } + + @Test + fun `get request is forwarded to page correctly`() { + var capturedRequest: FreenetRequest? = null + val page = object : TestPage() { + override fun handleRequest(request: FreenetRequest, response: Response) = + super.handleRequest(request, response) + .also { capturedRequest = request } + } + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext) + assertThat(capturedRequest!!.uri, equalTo(URI("/test"))) + assertThat(capturedRequest!!.method, equalTo(Method.GET)) + } + + @Test + fun `post request is forwarded to page correctly`() { + var capturedRequest: FreenetRequest? = null + val page = object : TestPage() { + override fun handleRequest(request: FreenetRequest, response: Response) = + super.handleRequest(request, response) + .also { capturedRequest = request } + } + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + pageToadlet.handleMethodPOST(URI("/test"), httpRequest, toadletContext) + assertThat(capturedRequest!!.uri, equalTo(URI("/test"))) + assertThat(capturedRequest!!.method, equalTo(Method.POST)) + } + + @Test + fun `content written to response is written to context`() { + val page = object : TestPage() { + override fun handleRequest(request: FreenetRequest, response: Response) = + response.apply { + statusCode = 123 + statusText = "Works" + contentType = "data/test" + addHeader("Test", "Value") + addHeader("More", "true") + addHeader("Test", "Another") + write("Content") + } + } + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + var writtenData: ByteArray? = null + var capturedReply: CapturedReply? = null + val toadletContext = object : DelegatingToadletContext(this.toadletContext) { + override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long) = + sendReplyHeaders(code, desc, mvt, mimeType, length, false) + + override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) { + capturedReply = CapturedReply(code, desc, mvt, mimeType, length) + } + + override fun writeData(data: ByteArray?, offset: Int, length: Int) { + writtenData = data!!.copyOfRange(offset, offset + length) + } + + override fun writeData(data: ByteArray?) = writeData(data, 0, data!!.size) + override fun writeData(data: Bucket?) = writeData(data!!.inputStream.readBytes()) + } + pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext) + assertThat(capturedReply!!.code, equalTo(123)) + assertThat(capturedReply!!.status, equalTo("Works")) + assertThat(capturedReply!!.mimeType, equalTo("data/test")) + assertThat(capturedReply!!.length, equalTo(7L)) + assertThat(capturedReply!!.headers!!.getArray("Test"), arrayContaining("Value", "Another")) + assertThat(capturedReply!!.headers!!.getArray("More"), arrayContaining("true")) + assertThat(writtenData!!.toString(UTF_8), equalTo("Content")) + } + + @Test + fun `link-enabled is true for non-callback pages`() { + val page = TestPage() + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + assertThat(pageToadlet.isEnabled(toadletContext), equalTo(true)) + } + + @Test + fun `link-enabled is passed through for callback pages`() { + var capturedToadletContext: ToadletContext? = null + val page = object : TestPage(), LinkEnabledCallback { + override fun isEnabled(ctx: ToadletContext?) = false.also { capturedToadletContext = toadletContext } + } + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + assertThat(pageToadlet.isEnabled(toadletContext), equalTo(false)) + assertThat(capturedToadletContext, sameInstance(toadletContext)) + } + + @Test + fun `link excemption is false for non-freenet pages`() { + val page = TestPage() + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(false)) + } + + @Test + fun `link excemption is passed through for freenet pages`() { + var capturedUri: URI? = null + val page = object : TestPage(), FreenetPage { + override fun isLinkExcepted(link: URI) = true.also { capturedUri = link } + } + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(true)) + assertThat(capturedUri, equalTo(URI("/test"))) + } + + @Test + fun `path is created correctly from prefix and page path`() { + val page = object : TestPage() { + override fun getPath() = "test-path" + } + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + assertThat(pageToadlet.path(), equalTo("/path/test-path")) + } + + @Test + fun `menu name is returned correctly`() { + val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", TestPage(), "/path/") + assertThat(pageToadlet.menuName, equalTo("MenuName")) + } + +} + +private data class CapturedReply(val code: Int, val status: String?, val headers: MultiValueTable?, val mimeType: String?, val length: Long?) + +private open class TestPage : Page { + override fun getPath() = "" + override fun isPrefixPage() = false + override fun handleRequest(request: FreenetRequest, response: Response) = response +} + +private open class DelegatingToadletContext(private val toadletContext: ToadletContext) : ToadletContext { + override fun activeToadlet(): Toadlet = toadletContext.activeToadlet() + override fun forceDisconnect() = toadletContext.forceDisconnect() + override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length) + override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, forceDisableJavascript) + override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, mTime: Date?) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, mTime) + override fun getUri(): URI = toadletContext.uri + override fun getPageMaker(): PageMaker = toadletContext.pageMaker + override fun getBucketFactory(): BucketFactory = toadletContext.bucketFactory + override fun getHeaders(): MultiValueTable = toadletContext.headers + override fun checkFullAccess(toadlet: Toadlet?): Boolean = toadletContext.checkFullAccess(toadlet) + override fun doRobots(): Boolean = toadletContext.doRobots() + override fun getReFilterPolicy(): REFILTER_POLICY = toadletContext.reFilterPolicy + override fun getAlertManager(): UserAlertManager = toadletContext.alertManager + override fun checkFormPassword(request: HTTPRequest?, redirectTo: String?): Boolean = toadletContext.checkFormPassword(request, redirectTo) + override fun checkFormPassword(request: HTTPRequest?): Boolean = toadletContext.checkFormPassword(request) + override fun addFormChild(parentNode: HTMLNode?, target: String?, id: String?): HTMLNode = toadletContext.addFormChild(parentNode, target, id) + override fun sendReplyHeadersFProxy(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long) = toadletContext.sendReplyHeadersFProxy(code, desc, mvt, mimeType, length) + override fun setCookie(newCookie: Cookie?) = toadletContext.setCookie(newCookie) + override fun isAdvancedModeEnabled(): Boolean = toadletContext.isAdvancedModeEnabled + override fun disableProgressPage(): Boolean = toadletContext.disableProgressPage() + override fun writeData(data: ByteArray?, offset: Int, length: Int) = toadletContext.writeData(data, offset, length) + override fun writeData(data: ByteArray?) = toadletContext.writeData(data) + override fun writeData(data: Bucket?) = toadletContext.writeData(data) + override fun getCookie(domain: URI?, path: URI?, name: String?): ReceivedCookie? = toadletContext.getCookie(domain, path, name) + override fun getUniqueId(): String = toadletContext.uniqueId + override fun sendReplyHeadersStatic(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, mTime: Date?) = toadletContext.sendReplyHeadersStatic(code, desc, mvt, mimeType, length, mTime) + override fun getBookmarkManager(): BookmarkManager = toadletContext.bookmarkManager + override fun isAllowedFullAccess(): Boolean = toadletContext.isAllowedFullAccess + override fun hasFormPassword(request: HTTPRequest?): Boolean = toadletContext.hasFormPassword(request) + override fun getFormPassword(): String = toadletContext.formPassword + override fun getContainer(): ToadletContainer = toadletContext.container +} From 9235105b5767441ce15d7ede26d6ef963a38e51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:11:25 +0200 Subject: [PATCH 48/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20page=20toadlet?= =?UTF-8?q?=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/web/page/PageToadlet.java | 187 ------------------ .../sone/web/page/PageToadlet.kt | 81 ++++++++ 2 files changed, 81 insertions(+), 187 deletions(-) delete mode 100644 src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java create mode 100644 src/main/java/net/pterodactylus/sone/web/page/PageToadlet.kt diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java deleted file mode 100644 index 45d6ffffb..000000000 --- a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Sone - PageToadlet.java - Copyright © 2010–2020 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.web.page; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; - -import net.pterodactylus.sone.utils.AutoCloseableBucket; -import net.pterodactylus.util.web.Header; -import net.pterodactylus.util.web.Method; -import net.pterodactylus.util.web.Page; -import net.pterodactylus.util.web.Response; - -import freenet.client.HighLevelSimpleClient; -import freenet.clients.http.LinkEnabledCallback; -import freenet.clients.http.LinkFilterExceptedToadlet; -import freenet.clients.http.SessionManager; -import freenet.clients.http.Toadlet; -import freenet.clients.http.ToadletContext; -import freenet.clients.http.ToadletContextClosedException; -import freenet.support.MultiValueTable; -import freenet.support.api.HTTPRequest; - -/** - * {@link Toadlet} implementation that is wrapped around a {@link Page}. - */ -public class PageToadlet extends Toadlet implements LinkEnabledCallback, LinkFilterExceptedToadlet { - - private final SessionManager sessionManager; - - /** The name of the menu item. */ - private final String menuName; - - /** The page that handles processing. */ - private final Page page; - - /** The path prefix for the page. */ - private final String pathPrefix; - - /** - * Creates a new toadlet that hands off processing to a {@link Page}. - * - * @param highLevelSimpleClient - * The high-level simple client - * @param menuName - * The name of the menu item - * @param page - * The page to handle processing - * @param pathPrefix - * Prefix that is prepended to all {@link Page#getPath()} return - * values - */ - protected PageToadlet(HighLevelSimpleClient highLevelSimpleClient, SessionManager sessionManager, String menuName, Page page, String pathPrefix) { - super(highLevelSimpleClient); - this.sessionManager = sessionManager; - this.menuName = menuName; - this.page = page; - this.pathPrefix = pathPrefix; - } - - /** - * Returns the name to display in the menu. - * - * @return The name in the menu - */ - public String getMenuName() { - return menuName; - } - - /** - * {@inheritDoc} - */ - @Override - public String path() { - return pathPrefix + page.getPath(); - } - - /** - * Handles a HTTP GET request. - * - * @param uri - * The URI of the request - * @param httpRequest - * The HTTP request - * @param toadletContext - * The toadlet context - * @throws IOException - * if an I/O error occurs - * @throws ToadletContextClosedException - * if the toadlet context is closed - */ - public void handleMethodGET(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException { - handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext, sessionManager)); - } - - /** - * Handles a HTTP POST request. - * - * @param uri - * The URI of the request - * @param httpRequest - * The HTTP request - * @param toadletContext - * The toadlet context - * @throws IOException - * if an I/O error occurs - * @throws ToadletContextClosedException - * if the toadlet context is closed - */ - public void handleMethodPOST(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException { - handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext, sessionManager)); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return getClass().getName() + "[path=" + path() + ",page=" + page + "]"; - } - - /** - * Handles a HTTP request. - * - * @param pageRequest - * The request to handle - * @throws IOException - * if an I/O error occurs - * @throws ToadletContextClosedException - * if the toadlet context is closed - */ - private void handleRequest(FreenetRequest pageRequest) throws IOException, ToadletContextClosedException { - try (AutoCloseableBucket pageBucket = new AutoCloseableBucket(pageRequest.getToadletContext().getBucketFactory().makeBucket(-1)); - OutputStream pageBucketOutputStream = pageBucket.getBucket().getOutputStream()) { - Response pageResponse = page.handleRequest(pageRequest, new Response(pageBucketOutputStream)); - MultiValueTable headers = new MultiValueTable<>(); - if (pageResponse.getHeaders() != null) { - for (Header header : pageResponse.getHeaders()) { - for (String value : header) { - headers.put(header.getName(), value); - } - } - } - writeReply(pageRequest.getToadletContext(), pageResponse.getStatusCode(), pageResponse.getContentType(), pageResponse.getStatusText(), headers, pageBucket.getBucket()); - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean isEnabled(ToadletContext toadletContext) { - if (page instanceof LinkEnabledCallback) { - return ((LinkEnabledCallback) page).isEnabled(toadletContext); - } - return true; - } - - // - // LINKFILTEREXCEPTEDTOADLET METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public boolean isLinkExcepted(URI link) { - return (page instanceof FreenetPage) && ((FreenetPage) page).isLinkExcepted(link); - } - -} diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.kt b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.kt new file mode 100644 index 000000000..a1cddfe4b --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.kt @@ -0,0 +1,81 @@ +/* + * Sone - PageToadlet.kt - Copyright © 2010–2020 David Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web.page + +import freenet.client.HighLevelSimpleClient +import freenet.clients.http.LinkEnabledCallback +import freenet.clients.http.LinkFilterExceptedToadlet +import freenet.clients.http.SessionManager +import freenet.clients.http.Toadlet +import freenet.clients.http.ToadletContext +import freenet.support.MultiValueTable +import freenet.support.api.HTTPRequest +import net.pterodactylus.sone.utils.use +import net.pterodactylus.util.web.Method +import net.pterodactylus.util.web.Page +import net.pterodactylus.util.web.Response +import java.net.URI + +/** + * [Toadlet] implementation that is wrapped around a [Page]. + */ +class PageToadlet( + highLevelSimpleClient: HighLevelSimpleClient, + private val sessionManager: SessionManager, + val menuName: String?, + private val page: Page, + private val pathPrefix: String +) : Toadlet(highLevelSimpleClient), LinkEnabledCallback, LinkFilterExceptedToadlet { + + override fun path() = pathPrefix + page.path + + override fun handleMethodGET(uri: URI, httpRequest: HTTPRequest, toadletContext: ToadletContext) = + handleRequest(FreenetRequest(uri, Method.GET, httpRequest, toadletContext, sessionManager)) + + fun handleMethodPOST(uri: URI?, httpRequest: HTTPRequest?, toadletContext: ToadletContext?) = + handleRequest(FreenetRequest(uri!!, Method.POST, httpRequest!!, toadletContext!!, sessionManager)) + + private fun handleRequest(pageRequest: FreenetRequest) { + pageRequest.toadletContext.bucketFactory.makeBucket(-1).use { pageBucket -> + pageBucket.outputStream.use { pageBucketOutputStream -> + val pageResponse = page.handleRequest(pageRequest, Response(pageBucketOutputStream)) + // according to the javadoc, headers is allowed to return null but that’s stupid and it doesn’t do that. + val headers = pageResponse.headers.fold(MultiValueTable()) { headers, header -> + headers.apply { + header.forEach { put(header.name, it) } + } + } + with(pageResponse) { + writeReply(pageRequest.toadletContext, statusCode, contentType, statusText, headers, pageBucket) + } + } + } + } + + override fun isEnabled(toadletContext: ToadletContext) = + if (page is LinkEnabledCallback) { + page.isEnabled(toadletContext) + } else + true + + override fun isLinkExcepted(link: URI) = + page is FreenetPage && page.isLinkExcepted(link) + + override fun toString() = "${javaClass.name}[path=${path()},page=$page]" + +} From f2f1a24a35a749c562f6ac6c9ffaacc9cdc41513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:13:23 +0200 Subject: [PATCH 49/87] =?UTF-8?q?=F0=9F=92=9A=20Run=20parallel=20tests=20b?= =?UTF-8?q?efore=20non-parallel=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 870a07cd1..c2247d357 100644 --- a/build.gradle +++ b/build.gradle @@ -76,6 +76,7 @@ task notParallelTest(type: Test) { useJUnit { includeCategories 'net.pterodactylus.sone.test.NotParallel' } + dependsOn parallelTest } test { From fbdfcedba2d34ee9a214dd6bb70cc10ed129cbfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:14:17 +0200 Subject: [PATCH 50/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20auto-closeable=20?= =?UTF-8?q?bucket=20wrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/utils/AutoCloseableBucket.kt | 11 -------- .../sone/utils/AutoCloseableBucketTest.kt | 26 ------------------- 2 files changed, 37 deletions(-) delete mode 100644 src/main/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucket.kt delete mode 100644 src/test/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucketTest.kt diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucket.kt b/src/main/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucket.kt deleted file mode 100644 index 58c181f12..000000000 --- a/src/main/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucket.kt +++ /dev/null @@ -1,11 +0,0 @@ -package net.pterodactylus.sone.utils - -import freenet.support.api.Bucket - -class AutoCloseableBucket(val bucket: Bucket) : AutoCloseable { - - override fun close() { - bucket.free() - } - -} diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucketTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucketTest.kt deleted file mode 100644 index a70584400..000000000 --- a/src/test/kotlin/net/pterodactylus/sone/utils/AutoCloseableBucketTest.kt +++ /dev/null @@ -1,26 +0,0 @@ -package net.pterodactylus.sone.utils - -import freenet.support.api.Bucket -import net.pterodactylus.sone.test.mock -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.equalTo -import org.junit.Test -import org.mockito.Mockito.verify - -class AutoCloseableBucketTest { - - private val bucket = mock() - private val autoCloseableBucket = AutoCloseableBucket(bucket) - - @Test - fun `bucket can be retrieved`() { - assertThat(autoCloseableBucket.bucket, equalTo(bucket)) - } - - @Test - fun `bucket will be free’d when close is called`() { - autoCloseableBucket.close() - verify(bucket).free() - } - -} From 3d8fa614b871a03f8940a1be0a98218028306fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:19:10 +0200 Subject: [PATCH 51/87] =?UTF-8?q?=F0=9F=8E=A8=20Don=E2=80=99t=20use=20spec?= =?UTF-8?q?ial=20predicates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/core/Preferences.kt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt index 05b32797a..9914b173c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt @@ -17,14 +17,12 @@ package net.pterodactylus.sone.core -import com.google.common.base.Predicates.* import com.google.common.eventbus.* import net.pterodactylus.sone.core.event.* import net.pterodactylus.sone.fcp.FcpInterface.* import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.* import net.pterodactylus.sone.fcp.event.* import net.pterodactylus.sone.utils.* -import net.pterodactylus.sone.utils.IntegerRangePredicate.* import net.pterodactylus.util.config.* import java.lang.Integer.* @@ -34,7 +32,7 @@ import java.lang.Integer.* */ class Preferences(private val eventBus: EventBus) { - private val _insertionDelay = DefaultOption(60, range(0, MAX_VALUE)) + private val _insertionDelay = DefaultOption(60) { it in 0..MAX_VALUE } val insertionDelay: Int get() = _insertionDelay.get() var newInsertionDelay: Int? get() = unsupported @@ -44,7 +42,7 @@ class Preferences(private val eventBus: EventBus) { eventBus.post(PreferenceChangedEvent("InsertionDelay", insertionDelay)) } - private val _postsPerPage = DefaultOption(10, range(1, MAX_VALUE)) + private val _postsPerPage = DefaultOption(10) { it in 1..MAX_VALUE } val postsPerPage: Int get() = _postsPerPage.get() var newPostsPerPage: Int? get() = unsupported @@ -53,19 +51,19 @@ class Preferences(private val eventBus: EventBus) { eventBus.post(PreferenceChangedEvent("PostsPerPage", postsPerPage)) } - private val _imagesPerPage = DefaultOption(9, range(1, MAX_VALUE)) + private val _imagesPerPage = DefaultOption(9) { it in 1..MAX_VALUE } val imagesPerPage: Int get() = _imagesPerPage.get() var newImagesPerPage: Int? get() = unsupported set (value: Int?) = _imagesPerPage.set(value) - private val _charactersPerPost = DefaultOption(400, or(range(50, MAX_VALUE), equalTo(-1))) + private val _charactersPerPost = DefaultOption(400) { it == -1 || it in 50..MAX_VALUE } val charactersPerPost: Int get() = _charactersPerPost.get() var newCharactersPerPost: Int? get() = unsupported set(value) = _charactersPerPost.set(value) - private val _postCutOffLength = DefaultOption(200, range(50, MAX_VALUE)) + private val _postCutOffLength = DefaultOption(200) { it in 50..MAX_VALUE } val postCutOffLength: Int get() = _postCutOffLength.get() var newPostCutOffLength: Int? get() = unsupported From 0fb2e4be3ce2066ecc5caf03f5ef4af680520a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:20:19 +0200 Subject: [PATCH 52/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20integer=20range?= =?UTF-8?q?=20predicate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/utils/IntegerRangePredicate.java | 64 ------------------- .../sone/utils/IntegerRangePredicateTest.java | 53 --------------- 2 files changed, 117 deletions(-) delete mode 100644 src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java delete mode 100644 src/test/java/net/pterodactylus/sone/utils/IntegerRangePredicateTest.java diff --git a/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java b/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java deleted file mode 100644 index 3a631cfdc..000000000 --- a/src/main/java/net/pterodactylus/sone/utils/IntegerRangePredicate.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Sone - IntegerRangePredicate.java - Copyright © 2013–2020 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.pterodactylus.sone.utils; - -import com.google.common.base.Predicate; - -/** - * {@link Predicate} that verifies that an {@link Integer} value is not - * {@code null} and is between a lower and an upper bound. Both bounds are - * inclusive. - */ -public class IntegerRangePredicate implements Predicate { - - /** The lower bound. */ - private final int lowerBound; - - /** The upper bound. */ - private final int upperBound; - - /** - * Creates a new integer range predicate. - * - * @param lowerBound - * The lower bound - * @param upperBound - * The upper bound - */ - public IntegerRangePredicate(int lowerBound, int upperBound) { - this.lowerBound = lowerBound; - this.upperBound = upperBound; - } - - // - // PREDICATE METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public boolean apply(Integer value) { - return (value != null) && (value >= lowerBound) && (value <= upperBound); - } - - public static IntegerRangePredicate range(int lowerBound, int upperBound) { - return new IntegerRangePredicate(lowerBound, upperBound); - } - -} diff --git a/src/test/java/net/pterodactylus/sone/utils/IntegerRangePredicateTest.java b/src/test/java/net/pterodactylus/sone/utils/IntegerRangePredicateTest.java deleted file mode 100644 index 2bea3f7d8..000000000 --- a/src/test/java/net/pterodactylus/sone/utils/IntegerRangePredicateTest.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.pterodactylus.sone.utils; - -import static net.pterodactylus.sone.utils.IntegerRangePredicate.range; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -import net.pterodactylus.sone.test.TestUtil; - -import org.junit.Test; - -/** - * Unit test for {@link IntegerRangePredicate}. - */ -public class IntegerRangePredicateTest { - - private final IntegerRangePredicate predicate = - new IntegerRangePredicate(-50, 50); - - @Test - public void predicateMatchesNumberWithinBounds() { - assertThat(predicate.apply(17), is(true)); - } - - @Test - public void predicateMatchesLowerBoundary() { - assertThat(predicate.apply(-50), is(true)); - } - - @Test - public void predicateDoesNotMatchOneBelowLowerBoundary() { - assertThat(predicate.apply(-51), is(false)); - } - - @Test - public void predicateMatchesUpperBoundary() { - assertThat(predicate.apply(50), is(true)); - } - - @Test - public void predicateDoesNotMatchesOneAboveUpperBoundary() { - assertThat(predicate.apply(51), is(false)); - } - - @Test - public void staticCreatorMethodCreatesPredicate() { - IntegerRangePredicate predicate = range(-50, 50); - assertThat(TestUtil.getPrivateField(predicate, "lowerBound"), - is(-50)); - assertThat(TestUtil.getPrivateField(predicate, "upperBound"), - is(50)); - } - -} From 5e7cd469678244403a5ca884736261b6d8217f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:20:51 +0200 Subject: [PATCH 53/87] =?UTF-8?q?=F0=9F=8E=A8=20Fix=20formatting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/core/Preferences.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt index 9914b173c..5a7a46497 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt @@ -17,14 +17,17 @@ package net.pterodactylus.sone.core -import com.google.common.eventbus.* -import net.pterodactylus.sone.core.event.* -import net.pterodactylus.sone.fcp.FcpInterface.* -import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.* -import net.pterodactylus.sone.fcp.event.* -import net.pterodactylus.sone.utils.* -import net.pterodactylus.util.config.* -import java.lang.Integer.* +import com.google.common.eventbus.EventBus +import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS +import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent +import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent +import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged +import net.pterodactylus.sone.utils.DefaultOption +import net.pterodactylus.util.config.Configuration +import net.pterodactylus.util.config.ConfigurationException +import java.lang.Integer.MAX_VALUE /** * Convenience interface for external classes that want to access the core’s @@ -55,7 +58,7 @@ class Preferences(private val eventBus: EventBus) { val imagesPerPage: Int get() = _imagesPerPage.get() var newImagesPerPage: Int? get() = unsupported - set (value: Int?) = _imagesPerPage.set(value) + set(value: Int?) = _imagesPerPage.set(value) private val _charactersPerPost = DefaultOption(400) { it == -1 || it in 50..MAX_VALUE } val charactersPerPost: Int get() = _charactersPerPost.get() From 150dafbd9a3f63ca06e6b53002723b56ec7f3d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:28:10 +0200 Subject: [PATCH 54/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20test=20for=20def?= =?UTF-8?q?ault=20option=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/utils/DefaultOptionTest.java | 85 ------------------- .../sone/utils/DefaultOptionTest.kt | 78 +++++++++++++++++ 2 files changed, 78 insertions(+), 85 deletions(-) delete mode 100644 src/test/java/net/pterodactylus/sone/utils/DefaultOptionTest.java create mode 100644 src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt diff --git a/src/test/java/net/pterodactylus/sone/utils/DefaultOptionTest.java b/src/test/java/net/pterodactylus/sone/utils/DefaultOptionTest.java deleted file mode 100644 index 3c82c40ef..000000000 --- a/src/test/java/net/pterodactylus/sone/utils/DefaultOptionTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package net.pterodactylus.sone.utils; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.nullValue; - -import javax.annotation.Nullable; - -import com.google.common.base.Predicate; -import org.junit.Test; - -/** - * Unit test for {@link DefaultOption}. - */ -public class DefaultOptionTest { - - private final Object defaultValue = new Object(); - private final Object acceptedValue = new Object(); - private final Predicate matchesAcceptedValue = new Predicate() { - @Override - public boolean apply(@Nullable Object object) { - return acceptedValue.equals(object); - } - }; - - @Test - public void defaultOptionReturnsDefaultValueWhenUnset() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue); - assertThat(defaultOption.get(), is(defaultValue)); - } - - @Test - public void defaultOptionReturnsNullForRealWhenUnset() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue); - assertThat(defaultOption.getReal(), nullValue()); - } - - @Test - public void defaultOptionWillReturnSetValue() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue); - Object newValue = new Object(); - defaultOption.set(newValue); - assertThat(defaultOption.get(), is(newValue)); - } - - @Test - public void defaultOptionWithValidatorAcceptsValidValues() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue); - defaultOption.set(acceptedValue); - assertThat(defaultOption.get(), is(acceptedValue)); - } - - @Test(expected = IllegalArgumentException.class) - public void defaultOptionWithValidatorRejectsInvalidValues() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue); - defaultOption.set(new Object()); - } - - @Test - public void defaultOptionValidatesObjectsCorrectly() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue); - assertThat(defaultOption.validate(acceptedValue), is(true)); - assertThat(defaultOption.validate(new Object()), is(false)); - } - - @Test - public void settingToNullWillRestoreDefaultValue() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue); - defaultOption.set(null); - assertThat(defaultOption.get(), is(defaultValue)); - } - - @Test - public void validateWithoutValidatorWillValidateNull() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue); - assertThat(defaultOption.validate(null), is(true)); - } - - @Test - public void validateWithValidatorWillValidateNull() { - DefaultOption defaultOption = new DefaultOption<>(defaultValue, matchesAcceptedValue); - assertThat(defaultOption.validate(null), is(true)); - } - -} diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt new file mode 100644 index 000000000..f7f4a4917 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt @@ -0,0 +1,78 @@ +package net.pterodactylus.sone.utils + +import com.google.common.base.Predicate +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.nullValue +import org.hamcrest.Matchers.sameInstance +import org.junit.Test + +/** + * Unit test for [DefaultOption]. + */ +class DefaultOptionTest { + + private val defaultValue = Any() + private val acceptedValue = Any() + private val matchesAcceptedValue = Predicate { it == acceptedValue } + + @Test + fun `default option returns default value when unset`() { + val defaultOption = DefaultOption(defaultValue) + assertThat(defaultOption.get(), sameInstance(defaultValue)) + } + + @Test + fun `default option returns null for real when unset`() { + val defaultOption = DefaultOption(defaultValue) + assertThat(defaultOption.real, nullValue()) + } + + @Test + fun `default option will return set value`() { + val defaultOption = DefaultOption(defaultValue) + val newValue = Any() + defaultOption.set(newValue) + assertThat(defaultOption.get(), sameInstance(newValue)) + } + + @Test + fun `default option with validator accepts valid values`() { + val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue) + defaultOption.set(acceptedValue) + assertThat(defaultOption.get(), sameInstance(acceptedValue)) + } + + @Test(expected = IllegalArgumentException::class) + fun `default option with validator rejects invalid values`() { + val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue) + defaultOption.set(Any()) + } + + @Test + fun `default option validates objects correctly`() { + val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue) + assertThat(defaultOption.validate(acceptedValue), equalTo(true)) + assertThat(defaultOption.validate(Any()), equalTo(false)) + } + + @Test + fun `setting to null will restore default value`() { + val defaultOption = DefaultOption(defaultValue) + defaultOption.set(null) + assertThat(defaultOption.get(), sameInstance(defaultValue)) + } + + @Test + fun `validate without validator will validate null`() { + val defaultOption = DefaultOption(defaultValue) + assertThat(defaultOption.validate(null), equalTo(true)) + } + + @Test + fun `validate with validator will validate null`() { + val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue) + assertThat(defaultOption.validate(null), equalTo(true)) + } + +} From 2d910565950724b0c12230f9874746f5c980ecdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:31:00 +0200 Subject: [PATCH 55/87] =?UTF-8?q?=F0=9F=8E=A8=20Use=20Java=E2=80=99s=20pre?= =?UTF-8?q?dicate=20instead=20of=20Guava=E2=80=99s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/utils/DefaultOption.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java b/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java index d9acaeb86..b46c58e49 100644 --- a/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java +++ b/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java @@ -1,6 +1,6 @@ package net.pterodactylus.sone.utils; -import com.google.common.base.Predicate; +import java.util.function.Predicate; /** * Basic implementation of an {@link Option}. @@ -66,7 +66,7 @@ public T getReal() { */ @Override public boolean validate(T value) { - return (validator == null) || (value == null) || validator.apply(value); + return (validator == null) || (value == null) || validator.test(value); } /** @@ -74,7 +74,7 @@ public boolean validate(T value) { */ @Override public void set(T value) { - if ((value != null) && (validator != null) && (!validator.apply(value))) { + if ((value != null) && (validator != null) && (!validator.test(value))) { throw new IllegalArgumentException("New Value (" + value + ") could not be validated."); } this.value = value; From 80deddc6b581421556370b31aec51d1ae902fa14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:40:07 +0200 Subject: [PATCH 56/87] =?UTF-8?q?=F0=9F=8E=A8=20Replace=20default=20option?= =?UTF-8?q?=20with=20Kotlin=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/utils/DefaultOption.java | 83 ------------------- .../pterodactylus/sone/utils/DefaultOption.kt | 30 +++++++ .../sone/utils/DefaultOptionTest.kt | 3 +- 3 files changed, 31 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/net/pterodactylus/sone/utils/DefaultOption.java create mode 100644 src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt diff --git a/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java b/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java deleted file mode 100644 index b46c58e49..000000000 --- a/src/main/java/net/pterodactylus/sone/utils/DefaultOption.java +++ /dev/null @@ -1,83 +0,0 @@ -package net.pterodactylus.sone.utils; - -import java.util.function.Predicate; - -/** - * Basic implementation of an {@link Option}. - * - * @param - * The type of the option - */ -public class DefaultOption implements Option { - - /** The default value. */ - private final T defaultValue; - - /** The current value. */ - private volatile T value; - - /** The validator. */ - private Predicate validator; - - /** - * Creates a new default option. - * - * @param defaultValue - * The default value of the option - */ - public DefaultOption(T defaultValue) { - this(defaultValue, null); - } - - /** - * Creates a new default option. - * - * @param defaultValue - * The default value of the option - * @param validator - * The validator for value validation (may be {@code null}) - */ - public DefaultOption(T defaultValue, Predicate validator) { - this.defaultValue = defaultValue; - this.validator = validator; - } - - /** - * {@inheritDoc} - */ - @Override - public T get() { - return (value != null) ? value : defaultValue; - } - - /** - * Returns the real value of the option. This will also return an unset - * value (usually {@code null})! - * - * @return The real value of the option - */ - @Override - public T getReal() { - return value; - } - - /** - * {@inheritDoc} - */ - @Override - public boolean validate(T value) { - return (validator == null) || (value == null) || validator.test(value); - } - - /** - * {@inheritDoc} - */ - @Override - public void set(T value) { - if ((value != null) && (validator != null) && (!validator.test(value))) { - throw new IllegalArgumentException("New Value (" + value + ") could not be validated."); - } - this.value = value; - } - -} diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt b/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt new file mode 100644 index 000000000..5f75e1294 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt @@ -0,0 +1,30 @@ +package net.pterodactylus.sone.utils + +import java.util.function.Predicate + +/** + * Basic implementation of an [Option]. + * + * @param The type of the option + */ +class DefaultOption @JvmOverloads constructor( + private val defaultValue: T, + private val validator: ((T) -> Boolean)? = null +) : Option { + + @Volatile + private var value: T? = null + + override fun get() = value ?: defaultValue + + override fun getReal(): T? = value + + override fun validate(value: T?): Boolean = + value == null || validator?.invoke(value) ?: true + + override fun set(value: T?) { + require(validate(value)) { "New Value ($value) could not be validated." } + this.value = value + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt index f7f4a4917..e445b39dc 100644 --- a/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt @@ -1,6 +1,5 @@ package net.pterodactylus.sone.utils -import com.google.common.base.Predicate import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.hamcrest.Matchers.nullValue @@ -14,7 +13,7 @@ class DefaultOptionTest { private val defaultValue = Any() private val acceptedValue = Any() - private val matchesAcceptedValue = Predicate { it == acceptedValue } + private val matchesAcceptedValue = { it: Any -> it == acceptedValue } @Test fun `default option returns default value when unset`() { From fe7976d8e9806d6ad27581e71707a790bc46498e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 10:43:51 +0200 Subject: [PATCH 57/87] =?UTF-8?q?=F0=9F=9A=9A=20Move=20Kotlin=20files=20to?= =?UTF-8?q?=20correct=20source=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/web/page/PageToadlet.kt | 0 .../net/pterodactylus/sone/core/SoneUriCreatorTest.kt | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/main/{java => kotlin}/net/pterodactylus/sone/web/page/PageToadlet.kt (100%) rename src/test/{java => kotlin}/net/pterodactylus/sone/core/SoneUriCreatorTest.kt (100%) diff --git a/src/main/java/net/pterodactylus/sone/web/page/PageToadlet.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt similarity index 100% rename from src/main/java/net/pterodactylus/sone/web/page/PageToadlet.kt rename to src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt diff --git a/src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneUriCreatorTest.kt similarity index 100% rename from src/test/java/net/pterodactylus/sone/core/SoneUriCreatorTest.kt rename to src/test/kotlin/net/pterodactylus/sone/core/SoneUriCreatorTest.kt From f32ec09442b57d34841392fb98a0ef055dd9f349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 11:22:45 +0200 Subject: [PATCH 58/87] =?UTF-8?q?=F0=9F=9A=A8=20Suppress=20deprecation=20w?= =?UTF-8?q?arning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt index 1d6f26c31..24f2b2d72 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt @@ -201,6 +201,7 @@ private open class DelegatingToadletContext(private val toadletContext: ToadletC override fun forceDisconnect() = toadletContext.forceDisconnect() override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length) override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, forceDisableJavascript: Boolean) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, forceDisableJavascript) + @Suppress("DEPRECATION") override fun sendReplyHeaders(code: Int, desc: String?, mvt: MultiValueTable?, mimeType: String?, length: Long, mTime: Date?) = toadletContext.sendReplyHeaders(code, desc, mvt, mimeType, length, mTime) override fun getUri(): URI = toadletContext.uri override fun getPageMaker(): PageMaker = toadletContext.pageMaker From 4f1ab525021ebf8f3d36ff250614bf67e19c2232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 16:28:12 +0200 Subject: [PATCH 59/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20session=20access?= =?UTF-8?q?=20from=20freenet=20request?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/web/page/FreenetRequest.kt | 11 +------- .../sone/web/page/FreenetRequestTest.kt | 28 ------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt index 38e01003a..ce2eacb49 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt @@ -29,13 +29,4 @@ open class FreenetRequest(uri: URI, method: Method, val httpRequest: HTTPRequest, val toadletContext: ToadletContext, val sessionManager: SessionManager -) : Request(uri, method) { - - val session: Session - get() = - sessionManager.useSession(toadletContext) - ?: sessionManager.createSession(randomUUID().toString(), toadletContext) - - val existingSession: Session? get() = sessionManager.useSession(toadletContext) - -} +) : Request(uri, method) diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt index 3295bddec..0c3fad93f 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt @@ -1,14 +1,12 @@ package net.pterodactylus.sone.web.page import freenet.clients.http.* -import freenet.clients.http.SessionManager.* import freenet.support.api.* import net.pterodactylus.sone.test.* import net.pterodactylus.util.web.* import org.hamcrest.MatcherAssert.* import org.hamcrest.Matchers.* import org.junit.* -import org.mockito.* import org.mockito.Mockito.* import java.net.* @@ -41,30 +39,4 @@ class FreenetRequestTest { assertThat(request.toadletContext, equalTo(toadletContext)) } - @Test - fun `null is returned if no session exists`() { - assertThat(request.existingSession, nullValue()) - } - - @Test - fun `existing session can be retrieved`() { - val session = mock() - whenever(sessionManager.useSession(toadletContext)).thenReturn(session) - assertThat(request.existingSession, sameInstance(session)) - } - - @Test - fun `existing session is returned if it exists`() { - val session = mock() - whenever(sessionManager.useSession(toadletContext)).thenReturn(session) - assertThat(request.session, sameInstance(session)) - } - - @Test - fun `new session is returned if none exists`() { - val session = mock() - whenever(sessionManager.createSession(anyString(), ArgumentMatchers.eq(toadletContext))).thenReturn(session) - assertThat(request.session, sameInstance(session)) - } - } From b31d36fc6fa19e9d47677b7450cd96881945b2ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 18:59:26 +0200 Subject: [PATCH 60/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20session=20manager?= =?UTF-8?q?=20from=20freenet=20request?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/web/page/FreenetRequest.kt | 6 +----- .../kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt | 4 ++-- .../kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt | 6 +++--- .../net/pterodactylus/sone/web/pages/SoneTemplatePage.kt | 2 +- .../java/net/pterodactylus/sone/main/DebugLoadersTest.java | 2 +- .../net/pterodactylus/sone/main/DefaultLoadersTest.java | 2 +- .../net/pterodactylus/sone/web/page/FreenetRequestTest.kt | 2 +- .../net/pterodactylus/sone/web/page/SoneRequestTest.kt | 7 ++----- .../kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt | 3 --- 9 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt index ce2eacb49..5729b2553 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/FreenetRequest.kt @@ -18,15 +18,11 @@ package net.pterodactylus.sone.web.page import freenet.clients.http.* -import freenet.clients.http.SessionManager.* import freenet.support.api.* -import net.pterodactylus.sone.freenet.* import net.pterodactylus.util.web.* import java.net.* -import java.util.UUID.* open class FreenetRequest(uri: URI, method: Method, val httpRequest: HTTPRequest, - val toadletContext: ToadletContext, - val sessionManager: SessionManager + val toadletContext: ToadletContext ) : Request(uri, method) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt index a1cddfe4b..32da31351 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt @@ -45,10 +45,10 @@ class PageToadlet( override fun path() = pathPrefix + page.path override fun handleMethodGET(uri: URI, httpRequest: HTTPRequest, toadletContext: ToadletContext) = - handleRequest(FreenetRequest(uri, Method.GET, httpRequest, toadletContext, sessionManager)) + handleRequest(FreenetRequest(uri, Method.GET, httpRequest, toadletContext)) fun handleMethodPOST(uri: URI?, httpRequest: HTTPRequest?, toadletContext: ToadletContext?) = - handleRequest(FreenetRequest(uri!!, Method.POST, httpRequest!!, toadletContext!!, sessionManager)) + handleRequest(FreenetRequest(uri!!, Method.POST, httpRequest!!, toadletContext!!)) private fun handleRequest(pageRequest: FreenetRequest) { pageRequest.toadletContext.bucketFactory.makeBucket(-1).use { pageBucket -> diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt index 703f9539f..fcfc63d9c 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/SoneRequest.kt @@ -7,10 +7,10 @@ import net.pterodactylus.sone.web.* import net.pterodactylus.util.web.* import java.net.* -class SoneRequest(uri: URI, method: Method, httpRequest: HTTPRequest, toadletContext: ToadletContext, sessionManager: SessionManager, +class SoneRequest(uri: URI, method: Method, httpRequest: HTTPRequest, toadletContext: ToadletContext, val core: Core, val webInterface: WebInterface -) : FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager) +) : FreenetRequest(uri, method, httpRequest, toadletContext) fun FreenetRequest.toSoneRequest(core: Core, webInterface: WebInterface) = - SoneRequest(uri, method, httpRequest, toadletContext, sessionManager, core, webInterface) + SoneRequest(uri, method, httpRequest, toadletContext, core, webInterface) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt index 9734a033e..bc00f9984 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt @@ -91,7 +91,7 @@ open class SoneTemplatePage( private val String.urlEncode: String get() = URLEncoder.encode(this, "UTF-8") override fun isEnabled(toadletContext: ToadletContext) = - isEnabled(SoneRequest(toadletContext.uri, Method.GET, HTTPRequestImpl(toadletContext.uri, "GET"), toadletContext, webInterface.sessionManager, core, webInterface)) + isEnabled(SoneRequest(toadletContext.uri, Method.GET, HTTPRequestImpl(toadletContext.uri, "GET"), toadletContext, core, webInterface)) open fun isEnabled(soneRequest: SoneRequest) = when { requiresLogin && getCurrentSone(soneRequest.toadletContext) == null -> false diff --git a/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java b/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java index 72f0bf7a8..53391f63d 100644 --- a/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java +++ b/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java @@ -70,7 +70,7 @@ public void staticPageIsServedFromFilesystem() throws URISyntaxException, IOExce HTTPRequest httpRequest = mock(HTTPRequest.class); ToadletContext toadletContext = mock(ToadletContext.class); SessionManager sessionManager = mock(SessionManager.class); - FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager); + FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext); OutputStream outputStream = new ByteArrayOutputStream(); Response response = new Response(outputStream); page.handleRequest(request, response); diff --git a/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java b/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java index d91e9e3a0..0afefe87b 100644 --- a/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java +++ b/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java @@ -51,7 +51,7 @@ public void staticPageIsServedFromClasspath() throws IOException, URISyntaxExcep HTTPRequest httpRequest = mock(HTTPRequest.class); ToadletContext toadletContext = mock(ToadletContext.class); SessionManager sessionManager = mock(SessionManager.class); - FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager); + FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext); OutputStream outputStream = new ByteArrayOutputStream(); Response response = new Response(outputStream); staticPage.handleRequest(request, response); diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt index 0c3fad93f..feb9b0f43 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt @@ -17,7 +17,7 @@ class FreenetRequestTest { private val httpRequest = mock(HTTPRequest::class.java) private val toadletContext = mock(ToadletContext::class.java) private val sessionManager = mock() - private val request = FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager) + private val request = FreenetRequest(uri, method, httpRequest, toadletContext) @Test fun `uri is retained correctly`() { diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt index 5be6ff129..89b1d701f 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt @@ -19,10 +19,9 @@ class SoneRequestTest { private val method = Method.GET private val httpRequest = Mockito.mock(HTTPRequest::class.java) private val toadletContext = Mockito.mock(ToadletContext::class.java) - private val sessionManager = mock() private val core = mock() private val webInterface = mock() - private val soneRequest = SoneRequest(uri, method, httpRequest, toadletContext, sessionManager, core, webInterface) + private val soneRequest = SoneRequest(uri, method, httpRequest, toadletContext, core, webInterface) @Test fun `freenet request properties are retained correctly`() { @@ -30,7 +29,6 @@ class SoneRequestTest { assertThat(soneRequest.method, equalTo(method)) assertThat(soneRequest.httpRequest, equalTo(httpRequest)) assertThat(soneRequest.toadletContext, equalTo(toadletContext)) - assertThat(soneRequest.sessionManager, equalTo(sessionManager)) } @Test @@ -45,13 +43,12 @@ class SoneRequestTest { @Test fun `freenet request is wrapped correctly`() { - val freenetRequest = FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager) + val freenetRequest = FreenetRequest(uri, method, httpRequest, toadletContext) val wrappedSoneRequest = freenetRequest.toSoneRequest(core, webInterface) assertThat(wrappedSoneRequest.uri, equalTo(uri)) assertThat(wrappedSoneRequest.method, equalTo(method)) assertThat(wrappedSoneRequest.httpRequest, equalTo(httpRequest)) assertThat(wrappedSoneRequest.toadletContext, equalTo(toadletContext)) - assertThat(wrappedSoneRequest.sessionManager, equalTo(sessionManager)) assertThat(wrappedSoneRequest.core, sameInstance(core)) assertThat(wrappedSoneRequest.webInterface, sameInstance(webInterface)) } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt index 07ae727f0..605ab339b 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt @@ -42,7 +42,6 @@ open class WebPageTest(pageSupplier: (WebInterface, Loaders, TemplateRenderer) - val core = webInterface.core val eventBus = mock() val preferences = Preferences(eventBus) - val sessionManager = mock() open val page by lazy { pageSupplier(webInterface, loaders, templateRenderer) } @@ -50,7 +49,6 @@ open class WebPageTest(pageSupplier: (WebInterface, Loaders, TemplateRenderer) - val freenetRequest = mock() init { - whenever(freenetRequest.sessionManager).thenReturn(sessionManager) whenever(freenetRequest.uri).thenReturn(mock()) } @@ -106,7 +104,6 @@ open class WebPageTest(pageSupplier: (WebInterface, Loaders, TemplateRenderer) - } private fun setupWebInterface() { - whenever(webInterface.sessionManager).thenReturn(sessionManager) whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone) whenever(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone) whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone) From 399632073484e68c7664af6b24f6fe45fdbbb6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 20:29:47 +0200 Subject: [PATCH 61/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20=E2=80=9Ccreate?= =?UTF-8?q?=E2=80=9D=20flag=20from=20session=20provider=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/web/WebInterface.java | 4 ++-- src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt | 2 +- src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt | 4 ++-- .../net/pterodactylus/sone/web/pages/SoneTemplatePage.kt | 2 +- .../kotlin/net/pterodactylus/sone/web/ajax/TestObjects.kt | 4 ++-- .../kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index 544805436..d8d5e2613 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -265,8 +265,8 @@ private Sone getCurrentSone(Session session) { @Override @Nullable - public Sone getCurrentSone(@Nonnull ToadletContext toadletContext, boolean createSession) { - return createSession ? getCurrentSoneCreatingSession(toadletContext) : getCurrentSoneWithoutCreatingSession(toadletContext); + public Sone getCurrentSone(@Nonnull ToadletContext toadletContext) { + return getCurrentSoneWithoutCreatingSession(toadletContext); } /** diff --git a/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt b/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt index 463ddaabe..93cd6afed 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/SessionProvider.kt @@ -8,7 +8,7 @@ import net.pterodactylus.sone.data.Sone */ interface SessionProvider { - fun getCurrentSone(toadletContext: ToadletContext, createSession: Boolean = true): Sone? + fun getCurrentSone(toadletContext: ToadletContext): Sone? fun setCurrentSone(toadletContext: ToadletContext, sone: Sone?) } diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt index 99c082876..574a0a862 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt @@ -32,7 +32,7 @@ abstract class JsonPage(protected val webInterface: WebInterface) : Page Date: Tue, 7 Apr 2020 20:47:03 +0200 Subject: [PATCH 62/87] =?UTF-8?q?=F0=9F=9A=A7=20Add=20session=20provider?= =?UTF-8?q?=20implementation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/web/FreenetSessionProvider.kt | 50 +++++++ .../sone/web/FreenetSessionProviderTest.kt | 126 ++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt create mode 100644 src/test/kotlin/net/pterodactylus/sone/web/FreenetSessionProviderTest.kt diff --git a/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt b/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt new file mode 100644 index 000000000..80bacdfe4 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt @@ -0,0 +1,50 @@ +/** + * Sone - FreenetSessionProvider.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web + +import freenet.clients.http.SessionManager +import freenet.clients.http.ToadletContext +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.database.SoneProvider +import java.util.UUID +import javax.inject.Inject + +/** + * [SoneProvider] implementation based on Freenet’s [SessionManager]. + */ +class FreenetSessionProvider @Inject constructor(private val soneProvider: SoneProvider, private val sessionManager: SessionManager) : SessionProvider { + + override fun getCurrentSone(toadletContext: ToadletContext): Sone? = + soneProvider.localSones.singleOrNull() + ?: sessionManager.useSession(toadletContext) + ?.let { it.getAttribute("Sone.CurrentSone") as? String } + ?.let(soneProvider.soneLoader) + ?.takeIf { it.isLocal } + + override fun setCurrentSone(toadletContext: ToadletContext, sone: Sone?) { + if (sone == null) { + sessionManager.useSession(toadletContext) + ?.removeAttribute("Sone.CurrentSone") + } else { + (sessionManager.useSession(toadletContext) + ?: sessionManager.createSession(UUID.randomUUID().toString(), toadletContext)) + ?.setAttribute("Sone.CurrentSone", sone.id) + } + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/web/FreenetSessionProviderTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/FreenetSessionProviderTest.kt new file mode 100644 index 000000000..835b14d5b --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/web/FreenetSessionProviderTest.kt @@ -0,0 +1,126 @@ +/** + * Sone - FreenetSessionProviderTest.kt - Copyright © 2020 David ‘Bombe’ Roden + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.pterodactylus.sone.web + +import com.google.inject.Guice +import freenet.clients.http.SessionManager +import freenet.clients.http.ToadletContext +import net.pterodactylus.sone.data.Sone +import net.pterodactylus.sone.data.impl.IdOnlySone +import net.pterodactylus.sone.database.SoneProvider +import net.pterodactylus.sone.test.deepMock +import net.pterodactylus.sone.test.eq +import net.pterodactylus.sone.test.getInstance +import net.pterodactylus.sone.test.isProvidedByMock +import net.pterodactylus.sone.test.mock +import net.pterodactylus.sone.test.whenever +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.notNullValue +import org.hamcrest.Matchers.nullValue +import org.hamcrest.Matchers.sameInstance +import org.junit.Test +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +/** + * Unit test for FreenetSessionProviderTest. + */ +class FreenetSessionProviderTest { + + private var soneProvider: SoneProvider = DelegatingSoneProvider(mock()) + private val sessionManager: SessionManager = deepMock() + private val provider by lazy { FreenetSessionProvider(soneProvider, sessionManager) } + private val toadletContext = mock() + + @Test + fun `provider returns null for current sone if no sone exists`() { + assertThat(provider.getCurrentSone(toadletContext), nullValue()) + } + + @Test + fun `provider returns singular sone if one sone exists`() { + val localSone: Sone = IdOnlySone("local") + soneProvider = object : DelegatingSoneProvider(mock()) { + override val localSones: Collection = listOf(localSone) + } + assertThat(provider.getCurrentSone(toadletContext), sameInstance(localSone)) + } + + @Test + fun `provider returns null if more than one sones exist but none is stored in the session`() { + soneProvider = object : DelegatingSoneProvider(mock()) { + override val localSones: Collection = listOf(IdOnlySone("1"), IdOnlySone("2")) + } + assertThat(provider.getCurrentSone(toadletContext), nullValue()) + } + + @Test + fun `provider returns sone if more than one sones exist and one is stored in the session`() { + val localSone = object : IdOnlySone("1") { + override fun isLocal() = true + } + soneProvider = object : DelegatingSoneProvider(mock()) { + override val localSones: Collection = listOf(localSone, IdOnlySone("2")) + override val soneLoader: (String) -> Sone? get() = { id -> localSone.takeIf { id == "1" } } + } + whenever(sessionManager.useSession(toadletContext).getAttribute("Sone.CurrentSone")).thenReturn("1") + assertThat(provider.getCurrentSone(toadletContext), equalTo(localSone)) + } + + @Test + fun `provider sets sone ID in existing session`() { + val localSone: Sone = IdOnlySone("local") + provider.setCurrentSone(toadletContext, localSone) + verify(sessionManager.useSession(toadletContext)).setAttribute("Sone.CurrentSone", "local") + } + + @Test + fun `provider sets sone ID in session it created`() { + val localSone: Sone = IdOnlySone("local") + whenever(sessionManager.useSession(toadletContext)).thenReturn(null) + provider.setCurrentSone(toadletContext, localSone) + verify(sessionManager.createSession(anyString(), eq(toadletContext))).setAttribute("Sone.CurrentSone", "local") + } + + @Test + fun `provider removes sone ID in existing session`() { + provider.setCurrentSone(toadletContext, null) + verify(sessionManager.useSession(toadletContext)).removeAttribute("Sone.CurrentSone") + } + + @Test + fun `provider does not create session if sone is to be removed and session does not exist`() { + whenever(sessionManager.useSession(toadletContext)).thenReturn(null) + provider.setCurrentSone(toadletContext, null) + verify(sessionManager.createSession(anyString(), eq(toadletContext)), never()).removeAttribute(anyString()) + } + + @Test + fun `provider can be created by guice`() { + val injector = Guice.createInjector( + SessionManager::class.isProvidedByMock(), + SoneProvider::class.isProvidedByMock() + ) + assertThat(injector.getInstance(), notNullValue()) + } + +} + +private open class DelegatingSoneProvider(private val soneProvider: SoneProvider) : SoneProvider by soneProvider From 52b087e92836e3ac36eab500ebafdd2dcdf11249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 20:49:11 +0200 Subject: [PATCH 63/87] =?UTF-8?q?=F0=9F=9A=A7=20Configure=20session=20prov?= =?UTF-8?q?ider=20in=20Sone=20module?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/main/SoneModule.kt | 3 ++ .../pterodactylus/sone/main/SoneModuleTest.kt | 33 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt b/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt index 1f92a93ba..a95cf78ca 100644 --- a/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt +++ b/src/main/kotlin/net/pterodactylus/sone/main/SoneModule.kt @@ -12,6 +12,8 @@ import net.pterodactylus.sone.database.* import net.pterodactylus.sone.database.memory.* import net.pterodactylus.sone.freenet.* import net.pterodactylus.sone.freenet.wot.* +import net.pterodactylus.sone.web.FreenetSessionProvider +import net.pterodactylus.sone.web.SessionProvider import net.pterodactylus.util.config.* import net.pterodactylus.util.config.ConfigurationException import net.pterodactylus.util.logging.* @@ -63,6 +65,7 @@ open class SoneModule(private val sonePlugin: SonePlugin, private val eventBus: bind(WebOfTrustConnector::class.java).to(PluginWebOfTrustConnector::class.java).`in`(Singleton::class.java) bind(TickerShutdown::class.java).`in`(Singleton::class.java) bind(SoneUriCreator::class.java).`in`(Singleton::class.java) + bind(SessionProvider::class.java).to(FreenetSessionProvider::class.java).`in`(Singleton::class.java) bindListener(Matchers.any(), object : TypeListener { override fun hear(typeLiteral: TypeLiteral, typeEncounter: TypeEncounter) { diff --git a/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt b/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt index 676ca6b59..01d128543 100644 --- a/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt @@ -4,7 +4,9 @@ import com.codahale.metrics.* import com.google.common.base.* import com.google.common.eventbus.* import com.google.inject.Guice.* +import com.google.inject.Injector import com.google.inject.name.Names.* +import freenet.clients.http.SessionManager import freenet.l10n.* import net.pterodactylus.sone.core.* import net.pterodactylus.sone.database.* @@ -13,6 +15,7 @@ import net.pterodactylus.sone.freenet.* import net.pterodactylus.sone.freenet.plugin.* import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.test.* +import net.pterodactylus.sone.web.SessionProvider import net.pterodactylus.util.config.* import net.pterodactylus.util.version.Version import org.hamcrest.MatcherAssert.* @@ -41,14 +44,7 @@ class SoneModuleTest { whenever(l10n()).thenReturn(l10n) } - private val injector by lazy { - createInjector( - SoneModule(sonePlugin, EventBus()), - FreenetInterface::class.isProvidedByDeepMock(), - PluginRespiratorFacade::class.isProvidedByDeepMock(), - PluginConnector::class.isProvidedByDeepMock() - ) - } + private val injector by lazy { createInjector() } @AfterTest fun removePropertiesFromCurrentDirectory() { @@ -198,16 +194,20 @@ class SoneModuleTest { @Test fun `core is registered with event bus`() { val eventBus = mock() - val injector = createInjector( - SoneModule(sonePlugin, eventBus), - FreenetInterface::class.isProvidedByDeepMock(), - PluginRespiratorFacade::class.isProvidedByDeepMock(), - PluginConnector::class.isProvidedByDeepMock() - ) + val injector = createInjector(eventBus) val core = injector.getInstance() verify(eventBus).register(core) } + private fun createInjector(eventBus: EventBus = EventBus()): Injector = + createInjector( + SoneModule(sonePlugin, eventBus), + FreenetInterface::class.isProvidedByDeepMock(), + PluginRespiratorFacade::class.isProvidedByDeepMock(), + PluginConnector::class.isProvidedByDeepMock(), + SessionManager::class.isProvidedByMock() + ) + @Test fun `metrics registry is created as singleton`() { injector.verifySingletonInstance() @@ -233,4 +233,9 @@ class SoneModuleTest { injector.verifySingletonInstance() } + @Test + fun `session provider is created as singleton`() { + injector.verifySingletonInstance() + } + } From 75682f86e69230600e6da4efce9d0f4bf9adf714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 21:12:22 +0200 Subject: [PATCH 64/87] =?UTF-8?q?=F0=9F=94=A5=20Delegate=20session=20provi?= =?UTF-8?q?der=20in=20web=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/web/WebInterface.java | 86 ++----------------- .../sone/web/ajax/TestObjects.kt | 4 - .../sone/web/pages/WebPageTest.kt | 4 - 3 files changed, 8 insertions(+), 86 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/web/WebInterface.java b/src/main/java/net/pterodactylus/sone/web/WebInterface.java index d8d5e2613..40e5708b3 100644 --- a/src/main/java/net/pterodactylus/sone/web/WebInterface.java +++ b/src/main/java/net/pterodactylus/sone/web/WebInterface.java @@ -23,7 +23,6 @@ import java.util.Collection; import java.util.Set; import java.util.TimeZone; -import java.util.UUID; import java.util.logging.Logger; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -88,15 +87,12 @@ import net.pterodactylus.util.web.RedirectPage; import net.pterodactylus.util.web.TemplatePage; -import freenet.clients.http.SessionManager; -import freenet.clients.http.SessionManager.Session; -import freenet.clients.http.ToadletContext; - import com.codahale.metrics.*; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; import com.google.inject.Inject; +import freenet.clients.http.ToadletContext; /** * Bundles functionality that a web interface of a Freenet plugin needs, e.g. @@ -140,6 +136,7 @@ public class WebInterface implements SessionProvider { private final PageToadletRegistry pageToadletRegistry; private final MetricRegistry metricRegistry; private final Translation translation; + private final SessionProvider sessionProvider; /** The “new post” notification. */ private final ListNotification newPostNotification; @@ -162,7 +159,8 @@ public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilt RenderFilter renderFilter, LinkedElementRenderFilter linkedElementRenderFilter, PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter, - NotificationManager notificationManager, @Named("newRemotePost") ListNotification newPostNotification, + NotificationManager notificationManager, SessionProvider sessionProvider, + @Named("newRemotePost") ListNotification newPostNotification, @Named("newRemotePostReply") ListNotification newReplyNotification, @Named("localPost") ListNotification localPostNotification, @Named("localReply") ListNotification localReplyNotification) { @@ -182,6 +180,7 @@ public WebInterface(SonePlugin sonePlugin, Loaders loaders, ListNotificationFilt this.l10nFilter = l10nFilter; this.translation = translation; this.notificationManager = notificationManager; + this.sessionProvider = sessionProvider; this.newPostNotification = newPostNotification; this.newReplyNotification = newReplyNotification; this.localPostNotification = localPostNotification; @@ -216,75 +215,15 @@ public TemplateContextFactory getTemplateContextFactory() { return templateContextFactory; } - private Session getCurrentSessionWithoutCreation(ToadletContext toadletContenxt) { - return getSessionManager().useSession(toadletContenxt); - } - - private Session getOrCreateCurrentSession(ToadletContext toadletContenxt) { - Session session = getCurrentSessionWithoutCreation(toadletContenxt); - if (session == null) { - session = getSessionManager().createSession(UUID.randomUUID().toString(), toadletContenxt); - } - return session; - } - - public Sone getCurrentSoneCreatingSession(ToadletContext toadletContext) { - Collection localSones = getCore().getLocalSones(); - if (localSones.size() == 1) { - return localSones.iterator().next(); - } - return getCurrentSone(getOrCreateCurrentSession(toadletContext)); - } - - public Sone getCurrentSoneWithoutCreatingSession(ToadletContext toadletContext) { - Collection localSones = getCore().getLocalSones(); - if (localSones.size() == 1) { - return localSones.iterator().next(); - } - return getCurrentSone(getCurrentSessionWithoutCreation(toadletContext)); - } - - /** - * Returns the currently logged in Sone. - * - * @param session - * The session - * @return The currently logged in Sone, or {@code null} if no Sone is - * currently logged in - */ - private Sone getCurrentSone(Session session) { - if (session == null) { - return null; - } - String soneId = (String) session.getAttribute("Sone.CurrentSone"); - if (soneId == null) { - return null; - } - return getCore().getLocalSone(soneId); - } - - @Override @Nullable + @Override public Sone getCurrentSone(@Nonnull ToadletContext toadletContext) { - return getCurrentSoneWithoutCreatingSession(toadletContext); + return sessionProvider.getCurrentSone(toadletContext); } - /** - * Sets the currently logged in Sone. - * - * @param toadletContext - * The toadlet context - * @param sone - * The Sone to set as currently logged in - */ @Override public void setCurrentSone(@Nonnull ToadletContext toadletContext, @Nullable Sone sone) { - Session session = getOrCreateCurrentSession(toadletContext); - if (sone == null) { - session.removeAttribute("Sone.CurrentSone"); - } else { - session.setAttribute("Sone.CurrentSone", sone.getId()); - } + sessionProvider.setCurrentSone(toadletContext, sone); } /** @@ -310,15 +249,6 @@ public Translation getTranslation() { return translation; } - /** - * Returns the session manager of the node. - * - * @return The node’s session manager - */ - public SessionManager getSessionManager() { - return sonePlugin.pluginRespirator().getSessionManager("Sone"); - } - /** * Returns the node’s form password. * diff --git a/src/test/kotlin/net/pterodactylus/sone/web/ajax/TestObjects.kt b/src/test/kotlin/net/pterodactylus/sone/web/ajax/TestObjects.kt index 6f77a9ccb..ffd6f15b7 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/ajax/TestObjects.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/ajax/TestObjects.kt @@ -81,8 +81,6 @@ open class TestObjects { init { whenever(webInterface.templateContextFactory).thenReturn(TemplateContextFactory()) whenever(webInterface.getCurrentSone(ArgumentMatchers.eq(toadletContext))).thenReturn(currentSone) - whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(currentSone) - whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(currentSone) whenever(webInterface.core).thenReturn(core) whenever(webInterface.formPassword).then { formPassword } whenever(webInterface.getNotifications(currentSone)).thenAnswer { notifications.values } @@ -140,8 +138,6 @@ open class TestObjects { protected fun unsetCurrentSone() { whenever(webInterface.getCurrentSone(ArgumentMatchers.eq(toadletContext))).thenReturn(null) - whenever(webInterface.getCurrentSoneWithoutCreatingSession(toadletContext)).thenReturn(null) - whenever(webInterface.getCurrentSoneCreatingSession(toadletContext)).thenReturn(null) } protected fun postRequest() { diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt index 096b3e68e..badc2bc16 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt @@ -104,9 +104,7 @@ open class WebPageTest(pageSupplier: (WebInterface, Loaders, TemplateRenderer) - } private fun setupWebInterface() { - whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(currentSone) whenever(webInterface.getCurrentSone(eq(toadletContext))).thenReturn(currentSone) - whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone) whenever(webInterface.getNotifications(currentSone)).then { notifications.values } whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() } whenever(webInterface.translation).thenReturn(translation) @@ -172,9 +170,7 @@ open class WebPageTest(pageSupplier: (WebInterface, Loaders, TemplateRenderer) - } fun unsetCurrentSone() { - whenever(webInterface.getCurrentSoneCreatingSession(eq(toadletContext))).thenReturn(null) whenever(webInterface.getCurrentSone(eq(toadletContext))).thenReturn(null) - whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(null) } fun addOwnIdentity(ownIdentity: OwnIdentity) { From 4edc76e2be95f89c0ee21eee53381237fba124eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 21:29:46 +0200 Subject: [PATCH 65/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20session?= =?UTF-8?q?=20manager=20from=20page=20toadlet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/web/page/PageToadlet.kt | 2 -- .../sone/web/page/PageToadletFactory.kt | 2 +- .../sone/web/page/PageToadletTest.kt | 20 +++++++++---------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt index 32da31351..db7ede0ff 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadlet.kt @@ -20,7 +20,6 @@ package net.pterodactylus.sone.web.page import freenet.client.HighLevelSimpleClient import freenet.clients.http.LinkEnabledCallback import freenet.clients.http.LinkFilterExceptedToadlet -import freenet.clients.http.SessionManager import freenet.clients.http.Toadlet import freenet.clients.http.ToadletContext import freenet.support.MultiValueTable @@ -36,7 +35,6 @@ import java.net.URI */ class PageToadlet( highLevelSimpleClient: HighLevelSimpleClient, - private val sessionManager: SessionManager, val menuName: String?, private val page: Page, private val pathPrefix: String diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt index 4c671ad7c..09cd3a52e 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt @@ -30,6 +30,6 @@ class PageToadletFactory @Inject constructor( @JvmOverloads fun createPageToadlet(page: Page, menuName: String? = null) = - PageToadlet(highLevelSimpleClient, sessionManager, menuName ?: page.menuName, page, pathPrefix) + PageToadlet(highLevelSimpleClient, menuName ?: page.menuName, page, pathPrefix) } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt index 24f2b2d72..cfadfec78 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletTest.kt @@ -23,7 +23,6 @@ import freenet.clients.http.FProxyFetchInProgress.REFILTER_POLICY import freenet.clients.http.LinkEnabledCallback import freenet.clients.http.PageMaker import freenet.clients.http.ReceivedCookie -import freenet.clients.http.SessionManager import freenet.clients.http.Toadlet import freenet.clients.http.ToadletContainer import freenet.clients.http.ToadletContext @@ -57,7 +56,6 @@ import kotlin.text.Charsets.UTF_8 class PageToadletTest { private val highLevelSimpleClient = mock() - private val sessionManager = mock() private val httpRequest = mock() private val toadletContext = deepMock() @@ -73,7 +71,7 @@ class PageToadletTest { super.handleRequest(request, response) .also { capturedRequest = request } } - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") pageToadlet.handleMethodGET(URI("/test"), httpRequest, toadletContext) assertThat(capturedRequest!!.uri, equalTo(URI("/test"))) assertThat(capturedRequest!!.method, equalTo(Method.GET)) @@ -87,7 +85,7 @@ class PageToadletTest { super.handleRequest(request, response) .also { capturedRequest = request } } - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") pageToadlet.handleMethodPOST(URI("/test"), httpRequest, toadletContext) assertThat(capturedRequest!!.uri, equalTo(URI("/test"))) assertThat(capturedRequest!!.method, equalTo(Method.POST)) @@ -107,7 +105,7 @@ class PageToadletTest { write("Content") } } - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") var writtenData: ByteArray? = null var capturedReply: CapturedReply? = null val toadletContext = object : DelegatingToadletContext(this.toadletContext) { @@ -138,7 +136,7 @@ class PageToadletTest { @Test fun `link-enabled is true for non-callback pages`() { val page = TestPage() - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") assertThat(pageToadlet.isEnabled(toadletContext), equalTo(true)) } @@ -148,7 +146,7 @@ class PageToadletTest { val page = object : TestPage(), LinkEnabledCallback { override fun isEnabled(ctx: ToadletContext?) = false.also { capturedToadletContext = toadletContext } } - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") assertThat(pageToadlet.isEnabled(toadletContext), equalTo(false)) assertThat(capturedToadletContext, sameInstance(toadletContext)) } @@ -156,7 +154,7 @@ class PageToadletTest { @Test fun `link excemption is false for non-freenet pages`() { val page = TestPage() - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(false)) } @@ -166,7 +164,7 @@ class PageToadletTest { val page = object : TestPage(), FreenetPage { override fun isLinkExcepted(link: URI) = true.also { capturedUri = link } } - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") assertThat(pageToadlet.isLinkExcepted(URI("/test")), equalTo(true)) assertThat(capturedUri, equalTo(URI("/test"))) } @@ -176,13 +174,13 @@ class PageToadletTest { val page = object : TestPage() { override fun getPath() = "test-path" } - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", page, "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", page, "/path/") assertThat(pageToadlet.path(), equalTo("/path/test-path")) } @Test fun `menu name is returned correctly`() { - val pageToadlet = PageToadlet(highLevelSimpleClient, sessionManager, "MenuName", TestPage(), "/path/") + val pageToadlet = PageToadlet(highLevelSimpleClient, "MenuName", TestPage(), "/path/") assertThat(pageToadlet.menuName, equalTo("MenuName")) } From ed11470b0b9a84a356ead1dade4e33f414aeb838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 21:38:27 +0200 Subject: [PATCH 66/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20more=20session=20?= =?UTF-8?q?managers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/web/page/PageToadletFactory.kt | 2 -- .../pterodactylus/sone/web/page/PageToadletFactoryTest.kt | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt index 09cd3a52e..61d8718dc 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/page/PageToadletFactory.kt @@ -18,13 +18,11 @@ package net.pterodactylus.sone.web.page import freenet.client.* -import freenet.clients.http.* import net.pterodactylus.util.web.* import javax.inject.* class PageToadletFactory @Inject constructor( private val highLevelSimpleClient: HighLevelSimpleClient, - private val sessionManager: SessionManager, @Named("toadletPathPrefix") private val pathPrefix: String ) { diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletFactoryTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletFactoryTest.kt index 7ac8c0160..43b3c1522 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletFactoryTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/PageToadletFactoryTest.kt @@ -2,21 +2,18 @@ package net.pterodactylus.sone.web.page import com.google.inject.* import freenet.client.* -import freenet.clients.http.* import net.pterodactylus.sone.test.* -import net.pterodactylus.sone.web.* import net.pterodactylus.util.web.* import org.hamcrest.MatcherAssert.* import org.hamcrest.Matchers.* import org.junit.* private val highLevelSimpleClient = mock() -private val sessionManager = mock() private const val pathPrefix = "/some/prefix/" class PageToadletFactoryTest { - private val pageToadletFactory = PageToadletFactory(highLevelSimpleClient, sessionManager, pathPrefix) + private val pageToadletFactory = PageToadletFactory(highLevelSimpleClient, pathPrefix) @Test fun `page toadlet without menu name is created without menu name`() { @@ -59,7 +56,6 @@ class PageToadletFactoryTest { fun `page toadlet factory can be created by guice`() { val injector = Guice.createInjector( HighLevelSimpleClient::class.isProvidedBy(highLevelSimpleClient), - SessionManager::class.isProvidedBy(sessionManager), String::class.withNameIsProvidedBy("/Sone/", "toadletPathPrefix") ) assertThat(injector.getInstance(), notNullValue()) From 8e5d39243ef21fa42158b361dcee7361c5e9e4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 21:56:29 +0200 Subject: [PATCH 67/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20more=20session=20?= =?UTF-8?q?manager=20usages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pterodactylus/sone/main/DebugLoadersTest.java | 2 -- .../java/net/pterodactylus/sone/main/DefaultLoadersTest.java | 2 -- .../net/pterodactylus/sone/web/WebInterfaceModuleTest.kt | 4 +--- .../net/pterodactylus/sone/web/page/FreenetRequestTest.kt | 1 - 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java b/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java index 53391f63d..e85c13179 100644 --- a/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java +++ b/src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java @@ -21,7 +21,6 @@ import net.pterodactylus.util.web.Page; import net.pterodactylus.util.web.Response; -import freenet.clients.http.SessionManager; import freenet.clients.http.ToadletContext; import freenet.support.api.HTTPRequest; @@ -69,7 +68,6 @@ public void staticPageIsServedFromFilesystem() throws URISyntaxException, IOExce Method method = Method.GET; HTTPRequest httpRequest = mock(HTTPRequest.class); ToadletContext toadletContext = mock(ToadletContext.class); - SessionManager sessionManager = mock(SessionManager.class); FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext); OutputStream outputStream = new ByteArrayOutputStream(); Response response = new Response(outputStream); diff --git a/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java b/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java index 0afefe87b..5c71b73e5 100644 --- a/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java +++ b/src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java @@ -21,7 +21,6 @@ import net.pterodactylus.util.web.Page; import net.pterodactylus.util.web.Response; -import freenet.clients.http.SessionManager; import freenet.clients.http.ToadletContext; import freenet.support.api.HTTPRequest; @@ -50,7 +49,6 @@ public void staticPageIsServedFromClasspath() throws IOException, URISyntaxExcep Method method = Method.GET; HTTPRequest httpRequest = mock(HTTPRequest.class); ToadletContext toadletContext = mock(ToadletContext.class); - SessionManager sessionManager = mock(SessionManager.class); FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext); OutputStream outputStream = new ByteArrayOutputStream(); Response response = new Response(outputStream); diff --git a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt index 8840caf64..c931aaf64 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt @@ -2,7 +2,6 @@ package net.pterodactylus.sone.web import com.google.inject.Guice.* import freenet.client.* -import freenet.clients.http.* import freenet.support.api.* import net.pterodactylus.sone.core.* import net.pterodactylus.sone.data.* @@ -38,8 +37,7 @@ class WebInterfaceModuleTest { SoneTextParser::class.isProvidedByMock(), ElementLoader::class.isProvidedByMock(), Loaders::class.isProvidedBy(loaders), - HighLevelSimpleClient::class.isProvidedByMock(), - SessionManager::class.isProvidedByMock() + HighLevelSimpleClient::class.isProvidedByMock() ) private val injector = createInjector(webInterfaceModule, *additionalModules)!! private val templateContext by lazy { injector.getInstance().createTemplateContext()!! } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt index feb9b0f43..ce2d7a99a 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt @@ -16,7 +16,6 @@ class FreenetRequestTest { private val method = Method.GET private val httpRequest = mock(HTTPRequest::class.java) private val toadletContext = mock(ToadletContext::class.java) - private val sessionManager = mock() private val request = FreenetRequest(uri, method, httpRequest, toadletContext) @Test From 5e2b2c2467eb01894907082c24e03724070a2158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Tue, 7 Apr 2020 22:41:15 +0200 Subject: [PATCH 68/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unused=20?= =?UTF-8?q?=E2=80=9Ccreate=E2=80=9D=20flag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt | 2 +- .../kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt | 2 +- src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt index 66c8ed2dc..c60fe567b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.kt @@ -21,7 +21,7 @@ class GetNotificationsAjaxPage @Inject constructor(webInterface: WebInterface) : override val requiresLogin = false override fun createJsonObject(request: FreenetRequest) = - getCurrentSone(request.toadletContext, false).let { currentSone -> + getCurrentSone(request.toadletContext).let { currentSone -> webInterface.getNotifications(currentSone) .sortedBy(Notification::getCreatedTime) .let { notifications -> diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt index 75f3c3c7a..5d1726279 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/GetStatusAjaxPage.kt @@ -35,7 +35,7 @@ class GetStatusAjaxPage(webInterface: WebInterface, private val elementLoader: E } override fun createJsonObject(request: FreenetRequest) = - getCurrentSone(request.toadletContext, false).let { currentSone -> + getCurrentSone(request.toadletContext).let { currentSone -> createSuccessJsonObject().apply { this["loggedIn"] = currentSone != null this["options"] = currentSone?.options?.toJsonOptions() ?: jsonObject {} diff --git a/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt index 574a0a862..356aca60d 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt @@ -31,7 +31,7 @@ abstract class JsonPage(protected val webInterface: WebInterface) : Page Date: Wed, 8 Apr 2020 10:08:59 +0200 Subject: [PATCH 69/87] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unnecessary=20imp?= =?UTF-8?q?orts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/net/pterodactylus/sone/core/Options.java | 2 -- src/main/java/net/pterodactylus/sone/core/SoneInserter.java | 2 -- .../net/pterodactylus/sone/core/SoneModificationDetector.java | 3 --- src/main/java/net/pterodactylus/sone/data/Profile.java | 1 - src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java | 1 - .../java/net/pterodactylus/sone/fcp/CreatePostCommand.java | 2 -- .../java/net/pterodactylus/sone/freenet/wot/Identity.java | 4 ---- src/main/java/net/pterodactylus/sone/main/SonePlugin.java | 1 - .../net/pterodactylus/sone/text/SoneTextParserContext.java | 1 - src/main/java/net/pterodactylus/sone/utils/NumberParsers.java | 1 - src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt | 1 - src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt | 2 -- .../net/pterodactylus/sone/core/WebOfTrustUpdaterTest.java | 2 -- src/test/java/net/pterodactylus/sone/test/Matchers.java | 1 - src/test/java/net/pterodactylus/sone/text/TextFilterTest.java | 2 -- .../pterodactylus/sone/core/ConfigurationSoneParserTest.kt | 1 - .../net/pterodactylus/sone/fcp/CreatePostCommandTest.kt | 2 -- .../net/pterodactylus/sone/template/ProfileAccessorTest.kt | 1 - .../net/pterodactylus/sone/template/UnknownDateFilterTest.kt | 1 - src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt | 1 - src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt | 1 - .../net/pterodactylus/sone/web/WebInterfaceModuleTest.kt | 1 - .../net/pterodactylus/sone/web/ajax/CreatePostAjaxPageTest.kt | 1 - .../net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt | 2 -- .../net/pterodactylus/sone/web/page/FreenetRequestTest.kt | 1 - .../kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt | 1 - .../net/pterodactylus/sone/web/pages/CreatePostPageTest.kt | 2 -- 27 files changed, 41 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/core/Options.java b/src/main/java/net/pterodactylus/sone/core/Options.java index 88e33a0fa..398374a04 100644 --- a/src/main/java/net/pterodactylus/sone/core/Options.java +++ b/src/main/java/net/pterodactylus/sone/core/Options.java @@ -23,8 +23,6 @@ import net.pterodactylus.sone.utils.Option; -import com.google.common.base.Predicate; - /** * Stores various options that influence Sone’s behaviour. */ diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 670b2c8b9..fcbe1b50a 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java @@ -43,8 +43,6 @@ import net.pterodactylus.sone.core.event.SoneInsertedEvent; import net.pterodactylus.sone.core.event.SoneInsertingEvent; import net.pterodactylus.sone.data.AlbumKt; -import net.pterodactylus.sone.data.Post; -import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.Sone.SoneStatus; import net.pterodactylus.sone.data.SoneKt; diff --git a/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java b/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java index e5e20d909..793d332be 100644 --- a/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java +++ b/src/main/java/net/pterodactylus/sone/core/SoneModificationDetector.java @@ -1,7 +1,5 @@ package net.pterodactylus.sone.core; -import static com.google.common.base.Optional.absent; -import static com.google.common.base.Optional.of; import static com.google.common.base.Ticker.systemTicker; import static java.util.concurrent.TimeUnit.NANOSECONDS; @@ -11,7 +9,6 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Objects; -import com.google.common.base.Optional; import com.google.common.base.Ticker; /** diff --git a/src/main/java/net/pterodactylus/sone/data/Profile.java b/src/main/java/net/pterodactylus/sone/data/Profile.java index b87ef4fe3..34246cb1b 100644 --- a/src/main/java/net/pterodactylus/sone/data/Profile.java +++ b/src/main/java/net/pterodactylus/sone/data/Profile.java @@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; import static java.nio.charset.StandardCharsets.UTF_8; import java.util.ArrayList; diff --git a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index a197c465f..f2f2ea620 100644 --- a/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java +++ b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java @@ -44,7 +44,6 @@ import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; import net.pterodactylus.sone.data.Profile; -import net.pterodactylus.sone.data.Reply; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.data.SoneOptions; import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions; diff --git a/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java b/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java index 39a6a1e25..3cb1d6bae 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/CreatePostCommand.java @@ -17,8 +17,6 @@ package net.pterodactylus.sone.fcp; -import com.google.common.base.Optional; - import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java index b8d4e6d42..63bceb1f2 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java @@ -17,13 +17,9 @@ package net.pterodactylus.sone.freenet.wot; -import java.util.Collection; -import java.util.Collections; import java.util.Map; import java.util.Set; -import com.google.common.base.Function; - /** * Interface for web of trust identities, defining all functions that can be * performed on an identity. An identity is only a container for identity data diff --git a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java index 25c6dac60..2d3e562f2 100644 --- a/src/main/java/net/pterodactylus/sone/main/SonePlugin.java +++ b/src/main/java/net/pterodactylus/sone/main/SonePlugin.java @@ -42,7 +42,6 @@ import com.google.common.eventbus.*; import com.google.common.cache.*; import com.google.inject.*; -import com.google.inject.Module; import com.google.inject.name.*; import kotlin.jvm.functions.*; diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java index d3a1557ba..598765a3a 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java @@ -18,7 +18,6 @@ package net.pterodactylus.sone.text; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.web.page.FreenetRequest; /** * {@link ParserContext} implementation for the {@link SoneTextParser}. It diff --git a/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java b/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java index 471fc2624..d9091c0fd 100644 --- a/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java +++ b/src/main/java/net/pterodactylus/sone/utils/NumberParsers.java @@ -1,6 +1,5 @@ package net.pterodactylus.sone.utils; -import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.primitives.Ints; diff --git a/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt b/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt index b711ca8fd..24b04faa3 100644 --- a/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt +++ b/src/main/kotlin/net/pterodactylus/sone/text/SoneTextParser.kt @@ -1,7 +1,6 @@ package net.pterodactylus.sone.text import freenet.keys.* -import freenet.support.* import net.pterodactylus.sone.data.* import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.database.* diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt b/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt index 5f75e1294..fd4215f19 100644 --- a/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt +++ b/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt @@ -1,7 +1,5 @@ package net.pterodactylus.sone.utils -import java.util.function.Predicate - /** * Basic implementation of an [Option]. * diff --git a/src/test/java/net/pterodactylus/sone/core/WebOfTrustUpdaterTest.java b/src/test/java/net/pterodactylus/sone/core/WebOfTrustUpdaterTest.java index d7d9b4817..5f8d93d7a 100644 --- a/src/test/java/net/pterodactylus/sone/core/WebOfTrustUpdaterTest.java +++ b/src/test/java/net/pterodactylus/sone/core/WebOfTrustUpdaterTest.java @@ -6,7 +6,6 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import java.util.concurrent.CountDownLatch; @@ -17,7 +16,6 @@ import net.pterodactylus.sone.core.WebOfTrustUpdaterImpl.WebOfTrustContextUpdateJob; import net.pterodactylus.sone.core.WebOfTrustUpdaterImpl.WebOfTrustUpdateJob; import net.pterodactylus.sone.freenet.plugin.PluginException; -import net.pterodactylus.sone.freenet.wot.Identity; import net.pterodactylus.sone.freenet.wot.OwnIdentity; import net.pterodactylus.sone.freenet.wot.WebOfTrustConnector; diff --git a/src/test/java/net/pterodactylus/sone/test/Matchers.java b/src/test/java/net/pterodactylus/sone/test/Matchers.java index 65fb3a6d4..bd571e7fc 100644 --- a/src/test/java/net/pterodactylus/sone/test/Matchers.java +++ b/src/test/java/net/pterodactylus/sone/test/Matchers.java @@ -29,7 +29,6 @@ import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.PostReply; -import com.google.common.base.Optional; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeDiagnosingMatcher; diff --git a/src/test/java/net/pterodactylus/sone/text/TextFilterTest.java b/src/test/java/net/pterodactylus/sone/text/TextFilterTest.java index 35623a74f..0319db93c 100644 --- a/src/test/java/net/pterodactylus/sone/text/TextFilterTest.java +++ b/src/test/java/net/pterodactylus/sone/text/TextFilterTest.java @@ -3,8 +3,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; import org.junit.Test; /** diff --git a/src/test/kotlin/net/pterodactylus/sone/core/ConfigurationSoneParserTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/ConfigurationSoneParserTest.kt index 90e3fa6de..29ade5f61 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/ConfigurationSoneParserTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/ConfigurationSoneParserTest.kt @@ -1,6 +1,5 @@ package net.pterodactylus.sone.core -import com.google.common.base.Optional.* import net.pterodactylus.sone.core.ConfigurationSoneParser.* import net.pterodactylus.sone.data.* import net.pterodactylus.sone.database.* diff --git a/src/test/kotlin/net/pterodactylus/sone/fcp/CreatePostCommandTest.kt b/src/test/kotlin/net/pterodactylus/sone/fcp/CreatePostCommandTest.kt index cfbda94f9..fabdd0943 100644 --- a/src/test/kotlin/net/pterodactylus/sone/fcp/CreatePostCommandTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/fcp/CreatePostCommandTest.kt @@ -1,7 +1,5 @@ package net.pterodactylus.sone.fcp -import com.google.common.base.Optional.absent -import com.google.common.base.Optional.of import net.pterodactylus.sone.core.Core import net.pterodactylus.sone.data.Post import net.pterodactylus.sone.test.mock diff --git a/src/test/kotlin/net/pterodactylus/sone/template/ProfileAccessorTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/ProfileAccessorTest.kt index 4513ce0cf..53d473e98 100644 --- a/src/test/kotlin/net/pterodactylus/sone/template/ProfileAccessorTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/template/ProfileAccessorTest.kt @@ -1,7 +1,6 @@ package net.pterodactylus.sone.template import net.pterodactylus.sone.core.Core -import net.pterodactylus.sone.data.Image import net.pterodactylus.sone.data.Profile import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions diff --git a/src/test/kotlin/net/pterodactylus/sone/template/UnknownDateFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/template/UnknownDateFilterTest.kt index b1c2cc58a..96a3cd52b 100644 --- a/src/test/kotlin/net/pterodactylus/sone/template/UnknownDateFilterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/template/UnknownDateFilterTest.kt @@ -1,7 +1,6 @@ package net.pterodactylus.sone.template import net.pterodactylus.sone.freenet.* -import net.pterodactylus.sone.test.* import org.hamcrest.MatcherAssert.* import org.hamcrest.Matchers.* import org.junit.* diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt b/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt index 85ed9e7ee..1f479f6ec 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Matchers.kt @@ -5,7 +5,6 @@ import net.pterodactylus.sone.freenet.wot.* import net.pterodactylus.sone.utils.* import net.pterodactylus.util.web.* import org.hamcrest.* -import org.hamcrest.Matchers import org.hamcrest.Matchers.* /** diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index d556f6bde..a25fa5da5 100644 --- a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -23,7 +23,6 @@ import freenet.keys.* import net.pterodactylus.sone.data.* import net.pterodactylus.sone.data.SoneOptions.* import net.pterodactylus.sone.data.impl.* -import net.pterodactylus.sone.text.* import net.pterodactylus.sone.utils.* val remoteSone1 = createRemoteSone() diff --git a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt index c931aaf64..9c0860c7d 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/WebInterfaceModuleTest.kt @@ -12,7 +12,6 @@ import net.pterodactylus.sone.main.* import net.pterodactylus.sone.template.* import net.pterodactylus.sone.test.* import net.pterodactylus.sone.text.* -import net.pterodactylus.sone.web.notification.* import net.pterodactylus.sone.web.page.* import net.pterodactylus.util.notify.* import net.pterodactylus.util.template.* diff --git a/src/test/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPageTest.kt index b79e6df2f..e5f1c82a9 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/ajax/CreatePostAjaxPageTest.kt @@ -1,6 +1,5 @@ package net.pterodactylus.sone.web.ajax -import com.google.common.base.Optional import net.pterodactylus.sone.data.Post import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.test.getInstance diff --git a/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt index e63bb8a5d..08b8f455d 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/ajax/EditImageAjaxPageTest.kt @@ -1,7 +1,5 @@ package net.pterodactylus.sone.web.ajax -import net.pterodactylus.sone.data.Album -import net.pterodactylus.sone.data.Image import net.pterodactylus.sone.data.Sone import net.pterodactylus.sone.data.impl.* import net.pterodactylus.sone.template.ParserFilter diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt index ce2d7a99a..1e3caab70 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/FreenetRequestTest.kt @@ -2,7 +2,6 @@ package net.pterodactylus.sone.web.page import freenet.clients.http.* import freenet.support.api.* -import net.pterodactylus.sone.test.* import net.pterodactylus.util.web.* import org.hamcrest.MatcherAssert.* import org.hamcrest.Matchers.* diff --git a/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt index 89b1d701f..0ec86db77 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/page/SoneRequestTest.kt @@ -1,7 +1,6 @@ package net.pterodactylus.sone.web.page import freenet.clients.http.* -import freenet.l10n.* import freenet.support.api.* import net.pterodactylus.sone.core.* import net.pterodactylus.sone.test.* diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/CreatePostPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/CreatePostPageTest.kt index 6212c90ab..a6f5d9d4a 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/CreatePostPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/CreatePostPageTest.kt @@ -1,9 +1,7 @@ package net.pterodactylus.sone.web.pages -import com.google.common.base.Optional.* import net.pterodactylus.sone.data.* import net.pterodactylus.sone.test.* -import net.pterodactylus.sone.utils.* import net.pterodactylus.sone.web.* import net.pterodactylus.sone.web.page.* import net.pterodactylus.util.web.Method.* From ecfd4c5a91d204ce4c001d19fa600cf42ac4cf43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Thu, 9 Apr 2020 19:10:02 +0200 Subject: [PATCH 70/87] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Slightly=20improve?= =?UTF-8?q?=20messy=20expression?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/web/FreenetSessionProvider.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt b/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt index 80bacdfe4..8928f12b0 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/FreenetSessionProvider.kt @@ -41,10 +41,13 @@ class FreenetSessionProvider @Inject constructor(private val soneProvider: SoneP sessionManager.useSession(toadletContext) ?.removeAttribute("Sone.CurrentSone") } else { - (sessionManager.useSession(toadletContext) - ?: sessionManager.createSession(UUID.randomUUID().toString(), toadletContext)) + sessionManager.getOrCreateSession(toadletContext) ?.setAttribute("Sone.CurrentSone", sone.id) } } + private fun SessionManager.getOrCreateSession(toadletContext: ToadletContext) = + useSession(toadletContext) + ?: createSession(UUID.randomUUID().toString(), toadletContext) + } From a6aaf80dbee1c4b6e70159bf6b3c26609d8c3974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 17 Apr 2020 20:14:36 +0200 Subject: [PATCH 71/87] =?UTF-8?q?=F0=9F=94=8A=20Log=20how=20much=20trust-a?= =?UTF-8?q?ll=20would=20reduce=20identity=20amount?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt index 67e70e088..5f087c7eb 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -58,6 +58,13 @@ class IdentityManagerImpl @Inject constructor( try { val currentIdentities = identityLoader.loadIdentities() + val onlyTrustedByAll = currentIdentities.mapValues { (ownIdentity, trustedIdentities) -> + trustedIdentities.filter { trustedIdentity -> + currentIdentities.all { trustedIdentity in it.value } + } + } + logger.log(Level.FINE, "Reduced (${currentIdentities.size},(${currentIdentities.values.joinToString { it.size.toString() }})) identities to (${onlyTrustedByAll.size},(${onlyTrustedByAll.values.joinToString { it.size.toString() }})).") + val identityChangeEventSender = IdentityChangeEventSender(eventBus, oldIdentities) identityChangeEventSender.detectChanges(currentIdentities) From e2eb4417311b9c18e297c2dff2a51d508fffa1d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 01:29:40 +0200 Subject: [PATCH 72/87] =?UTF-8?q?=F0=9F=9A=A7=20Disregard=20identities=20t?= =?UTF-8?q?hat=20don=E2=80=99t=20trust=20anyone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt index 5f087c7eb..f0e08d466 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -60,7 +60,7 @@ class IdentityManagerImpl @Inject constructor( val onlyTrustedByAll = currentIdentities.mapValues { (ownIdentity, trustedIdentities) -> trustedIdentities.filter { trustedIdentity -> - currentIdentities.all { trustedIdentity in it.value } + currentIdentities.filterValues { it.isNotEmpty() }.all { trustedIdentity in it.value } } } logger.log(Level.FINE, "Reduced (${currentIdentities.size},(${currentIdentities.values.joinToString { it.size.toString() }})) identities to (${onlyTrustedByAll.size},(${onlyTrustedByAll.values.joinToString { it.size.toString() }})).") From 69a95e89c8a306713bbe719ce23f49e7536774be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 16:14:41 +0200 Subject: [PATCH 73/87] =?UTF-8?q?=F0=9F=9A=A7=20Try=20to=20only=20allow=20?= =?UTF-8?q?identities=20that=20have=20no=20or=20positive=20trust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/freenet/wot/Identity.java | 2 ++ .../sone/freenet/wot/DefaultIdentity.kt | 8 ++++++- .../sone/freenet/wot/IdentityManagerImpl.kt | 22 +++++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java index 63bceb1f2..e6f4f62b3 100644 --- a/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java +++ b/src/main/java/net/pterodactylus/sone/freenet/wot/Identity.java @@ -131,6 +131,8 @@ public interface Identity { */ public Identity removeProperty(String name); + Map getTrust(); + /** * Retrieves the trust that this identity receives from the given own * identity. If this identity is not in the own identity’s trust tree, a diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultIdentity.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultIdentity.kt index 60979161d..88527b30f 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultIdentity.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/DefaultIdentity.kt @@ -17,7 +17,9 @@ package net.pterodactylus.sone.freenet.wot -import java.util.Collections.* +import java.util.Collections.synchronizedMap +import java.util.Collections.synchronizedSet +import kotlin.collections.set /** * A Web of Trust identity. @@ -77,6 +79,10 @@ open class DefaultIdentity(private val id: String, private val nickname: String? } } + override fun getTrust(): Map = synchronized(trustCache) { + trustCache.toMap() + } + override fun getTrust(ownIdentity: OwnIdentity) = synchronized(trustCache) { trustCache[ownIdentity] } diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt index f0e08d466..676278d61 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -58,9 +58,20 @@ class IdentityManagerImpl @Inject constructor( try { val currentIdentities = identityLoader.loadIdentities() - val onlyTrustedByAll = currentIdentities.mapValues { (ownIdentity, trustedIdentities) -> + val identitiesWithTrust = currentIdentities.values.flatten() + .groupBy { it.id } + .mapValues { (_, identities) -> + identities.reduce { accIdentity, identity -> + identity.trust.forEach { (ownIdentity: OwnIdentity?, trust: Trust?) -> + accIdentity.setTrust(ownIdentity, trust) + } + accIdentity + } + } + + val onlyTrustedByAll = currentIdentities.mapValues { (_, trustedIdentities) -> trustedIdentities.filter { trustedIdentity -> - currentIdentities.filterValues { it.isNotEmpty() }.all { trustedIdentity in it.value } + identitiesWithTrust[trustedIdentity.id]!!.trust.all { it.value.hasZeroOrPositiveTrust() } } } logger.log(Level.FINE, "Reduced (${currentIdentities.size},(${currentIdentities.values.joinToString { it.size.toString() }})) identities to (${onlyTrustedByAll.size},(${onlyTrustedByAll.values.joinToString { it.size.toString() }})).") @@ -96,3 +107,10 @@ private fun notThrowing(action: () -> Unit): Boolean = } catch (e: Exception) { false } + +private fun Trust.hasZeroOrPositiveTrust() = + if (explicit == null) { + implicit == null || implicit >= 0 + } else { + explicit >= 0 + } From 5b7e77d92a7dc57652332c87031c6d638d7588eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 16:33:44 +0200 Subject: [PATCH 74/87] =?UTF-8?q?=F0=9F=9A=9A=20Rename=20method=20to=20bet?= =?UTF-8?q?ter=20reflect=20purpose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/freenet/wot/IdentityLoader.kt | 2 +- .../net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt | 2 +- .../net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt index 474ab5786..c039aaa93 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt @@ -31,7 +31,7 @@ class IdentityLoader @Inject constructor(private val webOfTrustConnector: WebOfT private val logger: Logger = Logger.getLogger(IdentityLoader::class.java.name) @Throws(WebOfTrustException::class) - fun loadIdentities() = + fun loadTrustedIdentities() = time({ stopwatch, identities -> "Loaded ${identities.size} own identities in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) { webOfTrustConnector.loadAllOwnIdentities() }.let(this::loadTrustedIdentitiesForOwnIdentities) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt index 676278d61..36d31fbff 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -56,7 +56,7 @@ class IdentityManagerImpl @Inject constructor( while (!shouldStop()) { try { - val currentIdentities = identityLoader.loadIdentities() + val currentIdentities = identityLoader.loadTrustedIdentities() val identitiesWithTrust = currentIdentities.values.flatten() .groupBy { it.id } diff --git a/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt index b3a844755..8827d77bd 100644 --- a/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt @@ -42,7 +42,7 @@ class IdentityLoaderTest { @Test fun loadingIdentities() { val identityLoader = IdentityLoader(webOfTrustConnector, Context("Test")) - val identities = identityLoader.loadIdentities() + val identities = identityLoader.loadTrustedIdentities() assertThat(identities.keys, hasSize(4)) assertThat(identities.keys, containsInAnyOrder(ownIdentities[0], ownIdentities[1], ownIdentities[2], ownIdentities[3])) verifyIdentitiesForOwnIdentity(identities, ownIdentities[0], createTrustedIdentitiesForFirstOwnIdentity()) @@ -54,7 +54,7 @@ class IdentityLoaderTest { @Test fun loadingIdentitiesWithoutContext() { val identityLoaderWithoutContext = IdentityLoader(webOfTrustConnector) - val identities = identityLoaderWithoutContext.loadIdentities() + val identities = identityLoaderWithoutContext.loadTrustedIdentities() assertThat(identities.keys, hasSize(4)) assertThat(identities.keys, containsInAnyOrder(ownIdentities[0], ownIdentities[1], ownIdentities[2], ownIdentities[3])) verifyIdentitiesForOwnIdentity(identities, ownIdentities[0], createTrustedIdentitiesForFirstOwnIdentity()) From 6ebf42a54cc1ef44bdd762ad06e9b1a84bd7ad25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 16:46:49 +0200 Subject: [PATCH 75/87] =?UTF-8?q?=F0=9F=9A=A7=20Load=20ALL=20identities=20?= =?UTF-8?q?instead=20of=20only=20trusted=20ones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/freenet/wot/IdentityLoader.kt | 21 +++++++++++++++ .../sone/freenet/wot/IdentityManagerImpl.kt | 2 +- .../freenet/wot/PluginWebOfTrustConnector.kt | 26 +++++++++++++------ .../sone/freenet/wot/WebOfTrustConnector.kt | 10 +++++++ .../sone/freenet/wot/IdentityLoaderTest.kt | 1 + 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt index c039aaa93..f6e1d5905 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoader.kt @@ -36,6 +36,11 @@ class IdentityLoader @Inject constructor(private val webOfTrustConnector: WebOfT webOfTrustConnector.loadAllOwnIdentities() }.let(this::loadTrustedIdentitiesForOwnIdentities) + fun loadAllIdentities() = + time({ stopwatch, identities -> "Loaded ${identities.size} own identities in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) { + webOfTrustConnector.loadAllOwnIdentities() + }.let(this::loadAllIdentitiesForOwnIdentities) + @Throws(PluginException::class) private fun loadTrustedIdentitiesForOwnIdentities(ownIdentities: Collection) = ownIdentities @@ -53,6 +58,22 @@ class IdentityLoader @Inject constructor(private val webOfTrustConnector: WebOfT } } + private fun loadAllIdentitiesForOwnIdentities(ownIdentities: Collection) = + ownIdentities + .also { logger.fine { "Getting trusted identities for ${it.size} own identities..." } } + .associateWith { ownIdentity -> + logger.fine { "Getting trusted identities for $ownIdentity..." } + if (ownIdentity.doesNotHaveCorrectContext()) { + logger.fine { "Skipping $ownIdentity because of incorrect context." } + emptySet() + } else { + logger.fine { "Loading trusted identities for $ownIdentity from WoT..." } + time({ stopwatch, identities -> "Loaded ${identities.size} identities for ${ownIdentity.nickname} in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) { + webOfTrustConnector.loadAllIdentities(ownIdentity, context?.context) + } + } + } + private fun OwnIdentity.doesNotHaveCorrectContext() = context?.let { it.context !in contexts } ?: false diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt index 36d31fbff..c8c758dce 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -56,7 +56,7 @@ class IdentityManagerImpl @Inject constructor( while (!shouldStop()) { try { - val currentIdentities = identityLoader.loadTrustedIdentities() + val currentIdentities = identityLoader.loadAllIdentities() val identitiesWithTrust = currentIdentities.values.flatten() .groupBy { it.id } diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/PluginWebOfTrustConnector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/PluginWebOfTrustConnector.kt index 2fad1d10d..d829734f3 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/PluginWebOfTrustConnector.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/PluginWebOfTrustConnector.kt @@ -17,15 +17,17 @@ package net.pterodactylus.sone.freenet.wot -import com.google.inject.* -import freenet.support.* -import kotlinx.coroutines.* -import net.pterodactylus.sone.freenet.* -import net.pterodactylus.sone.freenet.plugin.* -import java.lang.String.* -import java.util.logging.* +import com.google.inject.Inject +import freenet.support.SimpleFieldSet +import kotlinx.coroutines.runBlocking +import net.pterodactylus.sone.freenet.SimpleFieldSetBuilder +import net.pterodactylus.sone.freenet.plugin.PluginConnector +import net.pterodactylus.sone.freenet.plugin.PluginException +import net.pterodactylus.sone.freenet.plugin.PluginReply +import java.lang.String.format +import java.util.logging.Level import java.util.logging.Logger -import java.util.logging.Logger.* +import java.util.logging.Logger.getLogger /** * Connector for the Web of Trust plugin. @@ -46,6 +48,14 @@ class PluginWebOfTrustConnector @Inject constructor(private val pluginConnector: .fields .parseIdentities { parseTrustedIdentity(it, ownIdentity) } + override fun loadAllIdentities(ownIdentity: OwnIdentity, context: String?): Set = + performRequest(SimpleFieldSetBuilder().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.id).put("Selection", "+").put("Context", context ?: "").put("WantTrustValues", "true").get()) + .fields + .parseIdentities { parseTrustedIdentity(it, ownIdentity) } + + performRequest(SimpleFieldSetBuilder().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.id).put("Selection", "-").put("Context", context ?: "").put("WantTrustValues", "true").get()) + .fields + .parseIdentities { parseTrustedIdentity(it, ownIdentity) } + @Throws(PluginException::class) override fun addContext(ownIdentity: OwnIdentity, context: String) { performRequest(SimpleFieldSetBuilder().put("Message", "AddContext").put("Identity", ownIdentity.id).put("Context", context).get()) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.kt index e31dce605..a407e2a04 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/WebOfTrustConnector.kt @@ -28,6 +28,16 @@ interface WebOfTrustConnector { @Throws(PluginException::class) fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String? = null): Set + /** + * Loads all identities known to the given own identity that have the (optional) given context. + * + * @param ownIdentity The own identity + * @param context The context to filter, or `null` + * @return All trusted identities + * @throws PluginException if an error occured talking to the Web of Trust plugin + */ + fun loadAllIdentities(ownIdentity: OwnIdentity, context: String? = null): Set + /** * Adds the given context to the given identity. * diff --git a/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt index 8827d77bd..0e0e33655 100644 --- a/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.kt @@ -106,6 +106,7 @@ private open class TestWebOfTrustConnector : WebOfTrustConnector { override fun loadAllOwnIdentities() = emptySet() override fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String?) = emptySet() + override fun loadAllIdentities(ownIdentity: OwnIdentity, context: String?) = emptySet() override fun addContext(ownIdentity: OwnIdentity, context: String) = Unit override fun removeContext(ownIdentity: OwnIdentity, context: String) = Unit override fun setProperty(ownIdentity: OwnIdentity, name: String, value: String) = Unit From 884606571eff6e4f80af9485695848bfb5e7fa95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 17:13:18 +0200 Subject: [PATCH 76/87] =?UTF-8?q?=F0=9F=9A=A7=20Use=20strictly-filtered=20?= =?UTF-8?q?identities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt index c8c758dce..ae5dd1e9b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -77,13 +77,13 @@ class IdentityManagerImpl @Inject constructor( logger.log(Level.FINE, "Reduced (${currentIdentities.size},(${currentIdentities.values.joinToString { it.size.toString() }})) identities to (${onlyTrustedByAll.size},(${onlyTrustedByAll.values.joinToString { it.size.toString() }})).") val identityChangeEventSender = IdentityChangeEventSender(eventBus, oldIdentities) - identityChangeEventSender.detectChanges(currentIdentities) + identityChangeEventSender.detectChanges(onlyTrustedByAll) - oldIdentities = currentIdentities + oldIdentities = onlyTrustedByAll synchronized(currentOwnIdentities) { currentOwnIdentities.clear() - currentOwnIdentities.addAll(currentIdentities.keys) + currentOwnIdentities.addAll(onlyTrustedByAll.keys) } } catch (wote1: WebOfTrustException) { logger.log(Level.WARNING, "WoT has disappeared!", wote1) From 7fafddbfba3a0fc110c641d9196d14ac1230c56a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 23:32:12 +0200 Subject: [PATCH 77/87] =?UTF-8?q?=E2=9C=85=20Use=20real=20event=20bus=20in?= =?UTF-8?q?=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/core/PreferencesTest.kt | 112 ++++++++++++------ 1 file changed, 73 insertions(+), 39 deletions(-) diff --git a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt index 245ff0361..24bda2143 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt @@ -1,28 +1,32 @@ package net.pterodactylus.sone.core -import com.google.common.eventbus.* -import net.pterodactylus.sone.core.event.* -import net.pterodactylus.sone.fcp.FcpInterface.* -import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.* -import net.pterodactylus.sone.fcp.event.* -import net.pterodactylus.sone.test.* -import net.pterodactylus.util.config.* -import org.hamcrest.MatcherAssert.* -import org.hamcrest.Matchers.* -import org.junit.* -import org.mockito.ArgumentMatchers.any -import org.mockito.Mockito.atLeastOnce -import org.mockito.Mockito.never -import org.mockito.Mockito.verify +import com.google.common.eventbus.EventBus +import com.google.common.eventbus.Subscribe +import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO +import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING +import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent +import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent +import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged +import net.pterodactylus.util.config.Configuration +import net.pterodactylus.util.config.MapConfigurationBackend +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.emptyIterable +import org.hamcrest.Matchers.equalTo +import org.hamcrest.Matchers.hasItem +import org.hamcrest.Matchers.instanceOf +import org.hamcrest.Matchers.nullValue +import org.junit.Test /** * Unit test for [Preferences]. */ class PreferencesTest { - private val eventBus = mock() + private val eventBus = EventBus() private val preferences = Preferences(eventBus) - private val eventsCaptor = capture() @Test fun `preferences retain insertion delay`() { @@ -32,9 +36,14 @@ class PreferencesTest { @Test fun `preferences sends event on setting insertion delay`() { + val events = mutableListOf() + eventBus.register(object { + @Subscribe + fun insertionDelayChangedEvent(event: InsertionDelayChangedEvent) = + events.add(event) + }) preferences.newInsertionDelay = 15 - verify(eventBus, atLeastOnce()).post(eventsCaptor.capture()) - assertThat(eventsCaptor.allValues, hasItem(InsertionDelayChangedEvent(15))) + assertThat(events, hasItem(InsertionDelayChangedEvent(15))) } @Test(expected = IllegalArgumentException::class) @@ -44,13 +53,19 @@ class PreferencesTest { @Test fun `no event is sent when invalid insertion delay is set`() { + val events = mutableListOf() + eventBus.register(object { + @Subscribe + fun insertionDelayChanged(event: InsertionDelayChangedEvent) = + events.add(event) + }) try { preferences.newInsertionDelay = -15 } catch (iae: IllegalArgumentException) { /* ignore. */ } - verify(eventBus, never()).post(any()) + assertThat(events, emptyIterable()) } @Test @@ -189,23 +204,41 @@ class PreferencesTest { @Test fun `preferences retain fcp interface active of true`() { + val events = mutableListOf() + eventBus.register(object { + @Subscribe + fun fcpInterfaceActivatedEvent(event: FcpInterfaceActivatedEvent) = + events.add(event) + }) preferences.newFcpInterfaceActive = true assertThat(preferences.fcpInterfaceActive, equalTo(true)) - verify(eventBus).post(any(FcpInterfaceActivatedEvent::class.java)) + assertThat(events, hasItem(instanceOf(FcpInterfaceActivatedEvent::class.java))) } @Test fun `preferences retain fcp interface active of false`() { + val events = mutableListOf() + eventBus.register(object { + @Subscribe + fun fcpInterfaceDeactivatedEvent(event: FcpInterfaceDeactivatedEvent) = + events.add(event) + }) preferences.newFcpInterfaceActive = false assertThat(preferences.fcpInterfaceActive, equalTo(false)) - verify(eventBus).post(any(FcpInterfaceDeactivatedEvent::class.java)) + assertThat(events, hasItem(instanceOf(FcpInterfaceDeactivatedEvent::class.java))) } @Test fun `preferences return default value when fcp interface active is set to null`() { + val events = mutableListOf() + eventBus.register(object { + @Subscribe + fun fcpInterfaceDeactivatedEvent(event: FcpInterfaceDeactivatedEvent) = + events.add(event) + }) preferences.newFcpInterfaceActive = null assertThat(preferences.fcpInterfaceActive, equalTo(false)) - verify(eventBus).post(any(FcpInterfaceDeactivatedEvent::class.java)) + assertThat(events, hasItem(instanceOf(FcpInterfaceDeactivatedEvent::class.java))) } @Test @@ -215,38 +248,34 @@ class PreferencesTest { @Test fun `preferences retain fcp full access required of no`() { - preferences.newFcpFullAccessRequired = NO - assertThat(preferences.fcpFullAccessRequired, equalTo(NO)) verifyFullAccessRequiredChangedEvent(NO) } - private fun verifyFullAccessRequiredChangedEvent( - fullAccessRequired: FullAccessRequired) { - verify(eventBus).post(eventsCaptor.capture()) - assertThat(eventsCaptor.value, instanceOf(FullAccessRequiredChanged::class.java)) - assertThat((eventsCaptor.value as FullAccessRequiredChanged).fullAccessRequired, - equalTo(fullAccessRequired)) + private fun verifyFullAccessRequiredChangedEvent(set: FullAccessRequired?, expected: FullAccessRequired = set!!) { + val events = mutableListOf() + eventBus.register(object { + @Subscribe + fun fullAccessRequiredChanged(event: FullAccessRequiredChanged) = + events.add(event) + }) + preferences.newFcpFullAccessRequired = set + assertThat(preferences.fcpFullAccessRequired, equalTo(expected)) + assertThat(events.single().fullAccessRequired, equalTo(expected)) } @Test fun `preferences retain fcp full access required of writing`() { - preferences.newFcpFullAccessRequired = WRITING - assertThat(preferences.fcpFullAccessRequired, equalTo(WRITING)) verifyFullAccessRequiredChangedEvent(WRITING) } @Test fun `preferences retain fcp full access required of always`() { - preferences.newFcpFullAccessRequired = ALWAYS - assertThat(preferences.fcpFullAccessRequired, equalTo(ALWAYS)) verifyFullAccessRequiredChangedEvent(ALWAYS) } @Test fun `preferences return default value when fcp full access required is set to null`() { - preferences.newFcpFullAccessRequired = null - assertThat(preferences.fcpFullAccessRequired, equalTo(ALWAYS)) - verifyFullAccessRequiredChangedEvent(ALWAYS) + verifyFullAccessRequiredChangedEvent(null, ALWAYS) } @Test @@ -265,9 +294,14 @@ class PreferencesTest { } private fun testPreferencesChangedEvent(name: String, setter: (T) -> Unit, value: T) { + val events = mutableListOf() + eventBus.register(object { + @Subscribe + fun preferenceChanged(event: PreferenceChangedEvent) = + events.add(event) + }) setter(value) - verify(eventBus, atLeastOnce()).post(eventsCaptor.capture()) - assertThat(eventsCaptor.allValues, hasItem(PreferenceChangedEvent(name, value))) + assertThat(events, hasItem(PreferenceChangedEvent(name, value))) } } From aa82f7eaab5b33951ca7aa0d240f0537c31e3247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 23:47:52 +0200 Subject: [PATCH 78/87] =?UTF-8?q?=F0=9F=9A=A7=20Add=20option=20for=20?= =?UTF-8?q?=E2=80=9Cstrict=20filtering=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pterodactylus/sone/core/Preferences.kt | 14 ++++ .../sone/core/event/StrictFilteringEvents.kt | 13 ++++ .../sone/core/PreferencesTest.kt | 70 ++++++++++++++++++- 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/net/pterodactylus/sone/core/event/StrictFilteringEvents.kt diff --git a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt index 5a7a46497..fe1e3c3ec 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt @@ -19,6 +19,8 @@ package net.pterodactylus.sone.core import com.google.common.eventbus.EventBus import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent +import net.pterodactylus.sone.core.event.StrictFilteringActivatedEvent +import net.pterodactylus.sone.core.event.StrictFilteringDeactivatedEvent import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent @@ -99,6 +101,17 @@ class Preferences(private val eventBus: EventBus) { eventBus.post(FullAccessRequiredChanged(fcpFullAccessRequired)) } + private val _strictFiltering = DefaultOption(false) + val strictFiltering: Boolean get() = _strictFiltering.get() + var newStrictFiltering: Boolean? = false + set(value) { + _strictFiltering.set(value) + when (strictFiltering) { + true -> eventBus.post(StrictFilteringActivatedEvent()) + else -> eventBus.post(StrictFilteringDeactivatedEvent()) + } + } + @Throws(ConfigurationException::class) fun saveTo(configuration: Configuration) { configuration.getIntValue("Option/ConfigurationVersion").value = 0 @@ -110,6 +123,7 @@ class Preferences(private val eventBus: EventBus) { configuration.getBooleanValue("Option/RequireFullAccess").value = _requireFullAccess.real configuration.getBooleanValue("Option/ActivateFcpInterface").value = _fcpInterfaceActive.real configuration.getIntValue("Option/FcpFullAccessRequired").value = toInt(_fcpFullAccessRequired.real) + configuration.getBooleanValue("Option/StrictFiltering").value = _strictFiltering.real } private fun toInt(fullAccessRequired: FullAccessRequired?): Int? { diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/StrictFilteringEvents.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/StrictFilteringEvents.kt new file mode 100644 index 000000000..ea1245923 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/StrictFilteringEvents.kt @@ -0,0 +1,13 @@ +package net.pterodactylus.sone.core.event + +/** + * Event that signals that the “[strict filtering][net.pterodactylus.sone.core.Preferences.strictFiltering]” + * preference was activated. + */ +class StrictFilteringActivatedEvent + +/** + * Event that signals that the “[strict filtering][net.pterodactylus.sone.core.Preferences.strictFiltering]” + * preference was deactivated. + */ +class StrictFilteringDeactivatedEvent diff --git a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt index 24bda2143..dafe7827d 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt @@ -3,6 +3,8 @@ package net.pterodactylus.sone.core import com.google.common.eventbus.EventBus import com.google.common.eventbus.Subscribe import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent +import net.pterodactylus.sone.core.event.StrictFilteringActivatedEvent +import net.pterodactylus.sone.core.event.StrictFilteringDeactivatedEvent import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO @@ -12,6 +14,7 @@ import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged import net.pterodactylus.util.config.Configuration import net.pterodactylus.util.config.MapConfigurationBackend +import org.hamcrest.Matcher import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.emptyIterable import org.hamcrest.Matchers.equalTo @@ -81,9 +84,7 @@ class PreferencesTest { @Test fun `preferences saves null for default insertion delay setting`() { - val configuration = Configuration(MapConfigurationBackend()) - preferences.saveTo(configuration) - assertThat(configuration.getIntValue("Option/InsertionDelay").getValue(null), nullValue()) + verifySavedOption(nullValue()) { it.getIntValue("Option/InsertionDelay").getValue(null) } } @Test @@ -293,6 +294,69 @@ class PreferencesTest { testPreferencesChangedEvent("PostsPerPage", { preferences.newPostsPerPage = it }, 31) } + @Test + fun `default strict filtering is false`() { + assertThat(preferences.strictFiltering, equalTo(false)) + } + + @Test + fun `strict filtering can be set`() { + preferences.newStrictFiltering = true + assertThat(preferences.strictFiltering, equalTo(true)) + } + + @Test + fun `strict filtering returns to default on null`() { + preferences.newStrictFiltering = true + preferences.newStrictFiltering = null + assertThat(preferences.strictFiltering, equalTo(false)) + } + + @Test + fun `event is generated when strict filtering is activated`() { + val events = mutableListOf() + eventBus.register(object { + @Subscribe fun strictFilteringActivatedEvent(event: StrictFilteringActivatedEvent) = + events.add(event) + }) + preferences.newStrictFiltering = true + assertThat(events, hasItem(instanceOf(StrictFilteringActivatedEvent::class.java))) + } + + @Test + fun `event is generated when strict filtering is deactivated`() { + val events = mutableListOf() + eventBus.register(object { + @Subscribe fun strictFilteringDeactivatedEvent(event: StrictFilteringDeactivatedEvent) = + events.add(event) + }) + preferences.newStrictFiltering = false + assertThat(events, hasItem(instanceOf(StrictFilteringDeactivatedEvent::class.java))) + } + + @Test + fun `default strict filtering is saved as null`() { + verifySavedOption(nullValue()) { it.getBooleanValue("Option/StrictFiltering").value } + } + + @Test + fun `activated strict filtering is saved as true`() { + preferences.newStrictFiltering = true + verifySavedOption(equalTo(true)) { it.getBooleanValue("Option/StrictFiltering").value } + } + + @Test + fun `deactivated strict filtering is saved as false`() { + preferences.newStrictFiltering = false + verifySavedOption(equalTo(false)) { it.getBooleanValue("Option/StrictFiltering").value } + } + + private fun verifySavedOption(matcher: Matcher, getter: (Configuration) -> T) { + val configuration = Configuration(MapConfigurationBackend()) + preferences.saveTo(configuration) + assertThat(getter(configuration), matcher) + } + private fun testPreferencesChangedEvent(name: String, setter: (T) -> Unit, value: T) { val events = mutableListOf() eventBus.register(object { From 7e42d9da5be1d45c8a4ed5fc9e178edf403a2c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 23:49:46 +0200 Subject: [PATCH 79/87] =?UTF-8?q?=F0=9F=9A=A7=20Add=20strict=20filtering?= =?UTF-8?q?=20to=20preferences=20loader?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt | 5 +++++ .../net/pterodactylus/sone/core/PreferencesLoaderTest.kt | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt index 32c35cb08..62b60ae9d 100644 --- a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt +++ b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt @@ -17,6 +17,7 @@ class PreferencesLoader(private val preferences: Preferences) { loadRequireFullAccess(configuration) loadFcpInterfaceActive(configuration) loadFcpFullAccessRequired(configuration) + loadStrictFiltering(configuration) } private fun loadInsertionDelay(configuration: Configuration) { @@ -55,4 +56,8 @@ class PreferencesLoader(private val preferences: Preferences) { preferences.newFcpFullAccessRequired = fullAccessRequiredInteger?.let { FullAccessRequired.values()[it] } } + private fun loadStrictFiltering(configuration: Configuration) { + preferences.newStrictFiltering = configuration.getBooleanValue("Option/StrictFiltering").getValue(null) + } + } diff --git a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt index 0cd7e6dc7..cb8df384a 100644 --- a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt @@ -28,6 +28,7 @@ class PreferencesLoaderTest { setupBooleanValue("RequireFullAccess", true) setupBooleanValue("ActivateFcpInterface", true) setupIntValue("FcpFullAccessRequired", 1) + setupBooleanValue("StrictFiltering", true) } private fun setupIntValue(optionName: String, value: Int) { @@ -49,6 +50,7 @@ class PreferencesLoaderTest { assertThat(preferences.requireFullAccess, equalTo(true)) assertThat(preferences.fcpInterfaceActive, equalTo(true)) assertThat(preferences.fcpFullAccessRequired, equalTo(FullAccessRequired.WRITING)) + assertThat(preferences.strictFiltering, equalTo(true)) } @Test From 7a2061d9f8ae6508ffbbac724630ff5696592f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Apr 2020 23:55:05 +0200 Subject: [PATCH 80/87] =?UTF-8?q?=F0=9F=9A=A7=20Add=20processing=20of=20st?= =?UTF-8?q?rict-filtering=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pterodactylus/sone/web/pages/OptionsPage.kt | 3 +++ .../pterodactylus/sone/web/pages/OptionsPageTest.kt | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt index 412258de4..9465a6a28 100644 --- a/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt +++ b/src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt @@ -40,9 +40,11 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade } val fullAccessRequired = "require-full-access" in soneRequest.parameters val fcpInterfaceActive = "fcp-interface-active" in soneRequest.parameters + val strictFiltering = "strict-filtering" in soneRequest.parameters soneRequest.core.preferences.newRequireFullAccess = fullAccessRequired soneRequest.core.preferences.newFcpInterfaceActive = fcpInterfaceActive + soneRequest.core.preferences.newStrictFiltering = strictFiltering val postsPerPage = soneRequest.parameters["posts-per-page"]?.toIntOrNull() val charactersPerPost = soneRequest.parameters["characters-per-post"]?.toIntOrNull() @@ -82,6 +84,7 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade templateContext["require-full-access"] = preferences.requireFullAccess templateContext["post-cut-off-length"] = preferences.postCutOffLength templateContext["posts-per-page"] = preferences.postsPerPage + templateContext["strict-filtering"] = preferences.strictFiltering } } diff --git a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt index d2929d917..d679e85f8 100644 --- a/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt @@ -27,6 +27,7 @@ class OptionsPageTest : WebPageTest(::OptionsPage) { core.preferences.newRequireFullAccess = true core.preferences.newPostCutOffLength = 51 core.preferences.newPostsPerPage = 10 + core.preferences.newStrictFiltering = true } @Before @@ -76,6 +77,7 @@ class OptionsPageTest : WebPageTest(::OptionsPage) { assertThat(templateContext["require-full-access"], equalTo(true)) assertThat(templateContext["post-cut-off-length"], equalTo(51)) assertThat(templateContext["posts-per-page"], equalTo(10)) + assertThat(templateContext["strict-filtering"], equalTo(true)) } } @@ -307,6 +309,16 @@ class OptionsPageTest : WebPageTest(::OptionsPage) { verifyThatPreferencesCanBeSet("posts-per-page", "invalid", 10) { core.preferences.postsPerPage } } + @Test + fun `strict filtering can be set to true`() { + verifyThatPreferencesCanBeSet("strict-filtering", "checked", true) { core.preferences.strictFiltering } + } + + @Test + fun `strict filtering can be set to false`() { + verifyThatPreferencesCanBeSet("strict-filtering", null, false) { core.preferences.strictFiltering } + } + @Test fun `page can be created by dependency injection`() { assertThat(baseInjector.getInstance(), notNullValue()) From 15c7e3e24de47ade817565a752866cef86b82c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 19 Apr 2020 00:11:25 +0200 Subject: [PATCH 81/87] =?UTF-8?q?=E2=9C=A8=20Add=20option=20for=20?= =?UTF-8?q?=E2=80=9Cstrict=20filtering=E2=80=9D=20to=20options=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/i18n/sone.de.properties | 2 ++ src/main/resources/i18n/sone.en.properties | 2 ++ src/main/resources/templates/options.html | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties index e17e99519..293d5ec9d 100644 --- a/src/main/resources/i18n/sone.de.properties +++ b/src/main/resources/i18n/sone.de.properties @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=FCP-Verbindungen nur von Page.Options.Option.FcpFullAccessRequired.Value.No=Nein Page.Options.Option.FcpFullAccessRequired.Value.Writing=Für Schreibzugriffe Page.Options.Option.FcpFullAccessRequired.Value.Always=Immer +Page.Options.Section.WebOfTrustOptions.Title=„Web of Trust“ Optionen +Page.Options.Option.StrictFiltering.Description=Identitäten strenger filtern. Wenn diese Option gewählt ist, werden Identitäten, die von mindestens einer Ihrer lokalen Identitäten einen negativen Vertrauenswert zugewiesen bekommen haben, komplett ignoriert; ansonsten werden Identitäten gezeigt, wenn sie von mindestens einer Ihrer Identitäten einen positiven Vertrauenswert zugewiesen bekommen. Page.Options.Section.Cleaning.Title=Aufräumen Page.Options.Option.ClearOnNextRestart.Description=Setzt die Konfiguration des Sone-Plugins beim nächsten Start zurück. Vorsicht: {strong}Alle Informationen Ihrer Sones werden gelöscht{/strong}, also stellen Sie bitte sicher, dass Sie die notwendigen Sicherungen angefertigt haben! Damit diese Option aktiv wird, muss auch die folgende Option aktiviert werden. Page.Options.Option.ReallyClearOnNextRestart.Description=Diese Option muss auf „ja“ gestellt werden, wenn Sie wirklich {strong}wirklich{/strong} sämtliche Informationen des Sone-Plugins beim nächsten Start entfernen möchten. diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties index 43760259a..07b268b9f 100644 --- a/src/main/resources/i18n/sone.en.properties +++ b/src/main/resources/i18n/sone.en.properties @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=Require FCP connection fro Page.Options.Option.FcpFullAccessRequired.Value.No=No Page.Options.Option.FcpFullAccessRequired.Value.Writing=For Write Access Page.Options.Option.FcpFullAccessRequired.Value.Always=Always +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. Page.Options.Section.Cleaning.Title=Clean Up Page.Options.Option.ClearOnNextRestart.Description=Resets the configuration of the Sone plugin at the next restart. Warning! {strong}This will destroy all of your Sones{/strong} so make sure you have backed up everyhing you still need! Also, you need to set the next option to true to actually do it. Page.Options.Option.ReallyClearOnNextRestart.Description=This option needs to be set to “yes” if you really, {strong}really{/strong} want to clear the plugin configuration on the next restart. diff --git a/src/main/resources/templates/options.html b/src/main/resources/templates/options.html index c4baf2a70..db8e520cf 100644 --- a/src/main/resources/templates/options.html +++ b/src/main/resources/templates/options.html @@ -166,6 +166,10 @@

<%= Page.Options.Section.FcpOptions.Title|l10n|html>

+

<%= Page.Options.Section.WebOfTrustOptions.Title|l10n|html>

+ +

checked="checked"<%/if> /> <%= Page.Options.Option.StrictFiltering.Description|l10n|html>

+ <%include include/tail.html> From ff707421282e6a371feafee669b09ecfd72384ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 19 Apr 2020 00:20:42 +0200 Subject: [PATCH 82/87] =?UTF-8?q?=E2=9C=A8=20Apply=20strict=20filtering=20?= =?UTF-8?q?when=20getting=20identities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sone/freenet/wot/IdentityManagerImpl.kt | 75 ++++++++++++------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt index ae5dd1e9b..829affc5b 100644 --- a/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt +++ b/src/main/kotlin/net/pterodactylus/sone/freenet/wot/IdentityManagerImpl.kt @@ -17,12 +17,18 @@ package net.pterodactylus.sone.freenet.wot -import com.google.common.eventbus.* -import com.google.inject.* -import net.pterodactylus.util.service.* -import java.util.concurrent.TimeUnit.* -import java.util.logging.* -import java.util.logging.Logger.* +import com.google.common.eventbus.EventBus +import com.google.common.eventbus.Subscribe +import com.google.inject.Inject +import com.google.inject.Singleton +import net.pterodactylus.sone.core.event.StrictFilteringActivatedEvent +import net.pterodactylus.sone.core.event.StrictFilteringDeactivatedEvent +import net.pterodactylus.util.service.AbstractService +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.atomic.AtomicBoolean +import java.util.logging.Level +import java.util.logging.Logger +import java.util.logging.Logger.getLogger /** * The identity manager takes care of loading and storing identities, their @@ -42,6 +48,7 @@ class IdentityManagerImpl @Inject constructor( ) : AbstractService("Sone Identity Manager", false), IdentityManager { private val currentOwnIdentities = mutableSetOf() + private val strictFiltering = AtomicBoolean(false) override val isConnected: Boolean get() = notThrowing { webOfTrustConnector.ping() } @@ -56,9 +63,31 @@ class IdentityManagerImpl @Inject constructor( while (!shouldStop()) { try { - val currentIdentities = identityLoader.loadAllIdentities() + val currentIdentities = identityLoader.loadAllIdentities().applyStrictFiltering() - val identitiesWithTrust = currentIdentities.values.flatten() + val identityChangeEventSender = IdentityChangeEventSender(eventBus, oldIdentities) + identityChangeEventSender.detectChanges(currentIdentities) + + oldIdentities = currentIdentities + + synchronized(currentOwnIdentities) { + currentOwnIdentities.clear() + currentOwnIdentities.addAll(currentIdentities.keys) + } + } catch (wote1: WebOfTrustException) { + logger.log(Level.WARNING, "WoT has disappeared!", wote1) + } catch (e: Exception) { + logger.log(Level.SEVERE, "Uncaught exception in IdentityManager thread!", e) + } + + /* wait a minute before checking again. */ + sleep(SECONDS.toMillis(60)) + } + } + + private fun Map>.applyStrictFiltering() = + if (strictFiltering.get()) { + val identitiesWithTrust = values.flatten() .groupBy { it.id } .mapValues { (_, identities) -> identities.reduce { accIdentity, identity -> @@ -69,31 +98,23 @@ class IdentityManagerImpl @Inject constructor( } } - val onlyTrustedByAll = currentIdentities.mapValues { (_, trustedIdentities) -> + mapValues { (_, trustedIdentities) -> trustedIdentities.filter { trustedIdentity -> identitiesWithTrust[trustedIdentity.id]!!.trust.all { it.value.hasZeroOrPositiveTrust() } } } - logger.log(Level.FINE, "Reduced (${currentIdentities.size},(${currentIdentities.values.joinToString { it.size.toString() }})) identities to (${onlyTrustedByAll.size},(${onlyTrustedByAll.values.joinToString { it.size.toString() }})).") - - val identityChangeEventSender = IdentityChangeEventSender(eventBus, oldIdentities) - identityChangeEventSender.detectChanges(onlyTrustedByAll) - - oldIdentities = onlyTrustedByAll - - synchronized(currentOwnIdentities) { - currentOwnIdentities.clear() - currentOwnIdentities.addAll(onlyTrustedByAll.keys) - } - } catch (wote1: WebOfTrustException) { - logger.log(Level.WARNING, "WoT has disappeared!", wote1) - } catch (e: Exception) { - logger.log(Level.SEVERE, "Uncaught exception in IdentityManager thread!", e) + } else { + this } - /* wait a minute before checking again. */ - sleep(SECONDS.toMillis(60)) - } + @Subscribe + fun strictFilteringActivated(event: StrictFilteringActivatedEvent) { + strictFiltering.set(true) + } + + @Subscribe + fun strictFilteringDeactivated(event: StrictFilteringDeactivatedEvent) { + strictFiltering.set(false) } } From 03fecfe9165c16cdc23d3cd9716e2437e91a1918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 19 Apr 2020 15:34:12 +0200 Subject: [PATCH 83/87] =?UTF-8?q?=F0=9F=92=84=20Move=20=E2=80=9CSave?= =?UTF-8?q?=E2=80=9D=20button=20back=20to=20bottom=20of=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/templates/options.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/options.html b/src/main/resources/templates/options.html index db8e520cf..9ef5ceb70 100644 --- a/src/main/resources/templates/options.html +++ b/src/main/resources/templates/options.html @@ -164,12 +164,12 @@

<%= Page.Options.Section.FcpOptions.Title|l10n|html>

-

-

<%= Page.Options.Section.WebOfTrustOptions.Title|l10n|html>

checked="checked"<%/if> /> <%= Page.Options.Option.StrictFiltering.Description|l10n|html>

+

+ <%include include/tail.html> From 38ddcee850a297828efb8a5555d4683d6d1d039a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 19 Apr 2020 15:56:33 +0200 Subject: [PATCH 84/87] =?UTF-8?q?=F0=9F=8C=90=20Update=20translations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/i18n/sone.de.properties | 2 +- src/main/resources/i18n/sone.en.properties | 2 +- src/main/resources/i18n/sone.es.properties | 2 ++ src/main/resources/i18n/sone.fr.properties | 6 ++++-- src/main/resources/i18n/sone.ja.properties | 2 ++ src/main/resources/i18n/sone.no.properties | 2 ++ src/main/resources/i18n/sone.pl.properties | 2 ++ src/main/resources/i18n/sone.ru.properties | 2 ++ 8 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/resources/i18n/sone.de.properties b/src/main/resources/i18n/sone.de.properties index 293d5ec9d..cd42c458a 100644 --- a/src/main/resources/i18n/sone.de.properties +++ b/src/main/resources/i18n/sone.de.properties @@ -75,7 +75,7 @@ Page.Options.Option.FcpFullAccessRequired.Value.No=Nein Page.Options.Option.FcpFullAccessRequired.Value.Writing=Für Schreibzugriffe Page.Options.Option.FcpFullAccessRequired.Value.Always=Immer Page.Options.Section.WebOfTrustOptions.Title=„Web of Trust“ Optionen -Page.Options.Option.StrictFiltering.Description=Identitäten strenger filtern. Wenn diese Option gewählt ist, werden Identitäten, die von mindestens einer Ihrer lokalen Identitäten einen negativen Vertrauenswert zugewiesen bekommen haben, komplett ignoriert; ansonsten werden Identitäten gezeigt, wenn sie von mindestens einer Ihrer Identitäten einen positiven Vertrauenswert zugewiesen bekommen. +Page.Options.Option.StrictFiltering.Description=Identitäten strenger filtern. Wenn diese Option gewählt ist, werden Identitäten, die von mindestens einer Ihrer lokalen Identitäten einen negativen Vertrauenswert zugewiesen bekommen haben, komplett ignoriert; ansonsten werden Identitäten gezeigt, wenn sie von mindestens einer Ihrer Identitäten einen positiven Vertrauenswert zugewiesen bekommen. (Bitte beachten Sie, dass diese Einstellung ein paar Minuten braucht, um Wirkung zu zeigen!) Page.Options.Section.Cleaning.Title=Aufräumen Page.Options.Option.ClearOnNextRestart.Description=Setzt die Konfiguration des Sone-Plugins beim nächsten Start zurück. Vorsicht: {strong}Alle Informationen Ihrer Sones werden gelöscht{/strong}, also stellen Sie bitte sicher, dass Sie die notwendigen Sicherungen angefertigt haben! Damit diese Option aktiv wird, muss auch die folgende Option aktiviert werden. Page.Options.Option.ReallyClearOnNextRestart.Description=Diese Option muss auf „ja“ gestellt werden, wenn Sie wirklich {strong}wirklich{/strong} sämtliche Informationen des Sone-Plugins beim nächsten Start entfernen möchten. diff --git a/src/main/resources/i18n/sone.en.properties b/src/main/resources/i18n/sone.en.properties index 07b268b9f..7c65dcf84 100644 --- a/src/main/resources/i18n/sone.en.properties +++ b/src/main/resources/i18n/sone.en.properties @@ -75,7 +75,7 @@ Page.Options.Option.FcpFullAccessRequired.Value.No=No Page.Options.Option.FcpFullAccessRequired.Value.Writing=For Write Access Page.Options.Option.FcpFullAccessRequired.Value.Always=Always Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings -Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) Page.Options.Section.Cleaning.Title=Clean Up Page.Options.Option.ClearOnNextRestart.Description=Resets the configuration of the Sone plugin at the next restart. Warning! {strong}This will destroy all of your Sones{/strong} so make sure you have backed up everyhing you still need! Also, you need to set the next option to true to actually do it. Page.Options.Option.ReallyClearOnNextRestart.Description=This option needs to be set to “yes” if you really, {strong}really{/strong} want to clear the plugin configuration on the next restart. diff --git a/src/main/resources/i18n/sone.es.properties b/src/main/resources/i18n/sone.es.properties index db20b733c..bc90612fe 100644 --- a/src/main/resources/i18n/sone.es.properties +++ b/src/main/resources/i18n/sone.es.properties @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=Requiere conexión FCP de Page.Options.Option.FcpFullAccessRequired.Value.No=No Page.Options.Option.FcpFullAccessRequired.Value.Writing=Para acceso de escritura Page.Options.Option.FcpFullAccessRequired.Value.Always=Siempre +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) Page.Options.Section.Cleaning.Title=Limpiar Page.Options.Option.ClearOnNextRestart.Description=Reinicia la configuración del plugin Sone en el siguiente reinicio.Cuidado! {strong}Esto destruirá todos tus Sone{/strong} de modo que asegurate de que has hecho una copia de seguridad de todo lo que necesitas! También tendrás que asignar cierto a la siguiente opción para hacerlo. Page.Options.Option.ReallyClearOnNextRestart.Description=Esta opción tiene que ser puesta en "yes" si realmente, {strong}realmente{/strong} quieres limpiar la configuración del plugin en el siguiente reinicio. diff --git a/src/main/resources/i18n/sone.fr.properties b/src/main/resources/i18n/sone.fr.properties index f4ff0d110..bf1eccd9d 100644 --- a/src/main/resources/i18n/sone.fr.properties +++ b/src/main/resources/i18n/sone.fr.properties @@ -26,8 +26,8 @@ Navigation.Menu.Sone.Item.Rescue.Name=Récupération Navigation.Menu.Sone.Item.Rescue.Tooltip=Récupération de votre Sone Navigation.Menu.Sone.Item.About.Name=A propos Navigation.Menu.Sone.Item.About.Tooltip=Informations à propos de Sone -Navigation.Menu.Sone.Item.Metrics.Name=Metrics -Navigation.Menu.Sone.Item.Metrics.Tooltip=Metrics collected by Sone +Navigation.Menu.Sone.Item.Metrics.Name=Métriques +Navigation.Menu.Sone.Item.Metrics.Tooltip=Métriques collectées par Sone Page.About.Title=A propos de - Sone Page.About.Page.Title=A propos @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=Requière une connexion FC Page.Options.Option.FcpFullAccessRequired.Value.No=Non Page.Options.Option.FcpFullAccessRequired.Value.Writing=Pour accès à l'écriture Page.Options.Option.FcpFullAccessRequired.Value.Always=toujours +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) Page.Options.Section.Cleaning.Title=Nettoyer Page.Options.Option.ClearOnNextRestart.Description=Réinitialiser la configuration du plugin Sone au prochain redémarrage. Attention! {strong}Cela détruira tous vos Sones{/strong}. Soyez sûr d'avoir sauvegardé tout ce dont vous avez besoin! Vous devez également choisir "Oui" à l'option suivante pour procéder à la réinitialisation. Page.Options.Option.ReallyClearOnNextRestart.Description=Choisir "Oui" pour cette option si vous voulez vraiment{strong}vraiment{/strong} effacer la configuration au prochain redémarrage. diff --git a/src/main/resources/i18n/sone.ja.properties b/src/main/resources/i18n/sone.ja.properties index 996f7f459..535140f0c 100644 --- a/src/main/resources/i18n/sone.ja.properties +++ b/src/main/resources/i18n/sone.ja.properties @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=許可されたホスト Page.Options.Option.FcpFullAccessRequired.Value.No=いいえ Page.Options.Option.FcpFullAccessRequired.Value.Writing=書き込みのアクセスの場合 Page.Options.Option.FcpFullAccessRequired.Value.Always=常に +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) Page.Options.Section.Cleaning.Title=クリーンアップ Page.Options.Option.ClearOnNextRestart.Description=次回のプラグインの再起動時に設定を初期化する。注意:{strong}これを行うとあなたの全てのSoneが破棄されます{/strong}。実行の前に必要なものは全てバックアップされていることを確認してください。また、次項の設定も同時に有効にする必要があります。 Page.Options.Option.ReallyClearOnNextRestart.Description={strong}本当{/strong}にプラグインの設定を初期化する場合はこの項目も有効にしてください。 diff --git a/src/main/resources/i18n/sone.no.properties b/src/main/resources/i18n/sone.no.properties index 9ddb54d26..967facaa1 100644 --- a/src/main/resources/i18n/sone.no.properties +++ b/src/main/resources/i18n/sone.no.properties @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=Påkrev FCP tilkobling fra Page.Options.Option.FcpFullAccessRequired.Value.No=Nei Page.Options.Option.FcpFullAccessRequired.Value.Writing=For skrivetilgang Page.Options.Option.FcpFullAccessRequired.Value.Always=Alltid +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) Page.Options.Section.Cleaning.Title=Rydd opp Page.Options.Option.ClearOnNextRestart.Description=Nullstill konfigurasjonen til Sone-tillegget ved neste omstart. Advarsel! {strong}Dette vil ødelegge alle dine Soner{/strong} så forsikre deg om at du har tatt backup av alt du fremdeles trenger! Du vil måtte sette det neste valget til 'true' for faktisk å nullstille. Page.Options.Option.ReallyClearOnNextRestart.Description=Denne innstillingen må bli satt til «ja» hvis du virkelig {strong}virkelig{/strong} ønsker å slette Sone-tilleggets innstillinger ved neste omstart. diff --git a/src/main/resources/i18n/sone.pl.properties b/src/main/resources/i18n/sone.pl.properties index cbc8f44aa..253497b0f 100644 --- a/src/main/resources/i18n/sone.pl.properties +++ b/src/main/resources/i18n/sone.pl.properties @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=Wymagane połączenie FCP Page.Options.Option.FcpFullAccessRequired.Value.No=Nie Page.Options.Option.FcpFullAccessRequired.Value.Writing=Dostęp z zapisem Page.Options.Option.FcpFullAccessRequired.Value.Always=Zawsze +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) Page.Options.Section.Cleaning.Title=Wyczyść Page.Options.Option.ClearOnNextRestart.Description=Przy kolejnym uruchomieniu zresetuj ustawienia wtyczki Sone. Uwaga!{strong}Wszystkie twoje profile Sone zostaną zniszczone{/strong}upewnij się, że wykonano kopie zapasowe wszystkich niezbędnych danych! Wymagane wybranie odpowiedzi "tak" w następnej opcji. Page.Options.Option.ReallyClearOnNextRestart.Description=Wymagane wybranie opcji"tak"jeśli {strong}naprawdę{/strong} jesteś zdecydowany zresetować ustawienia wtyczki konfiguracyjnej przy kolejnym uruchomieniu. diff --git a/src/main/resources/i18n/sone.ru.properties b/src/main/resources/i18n/sone.ru.properties index 7e3c113ac..354c0d224 100644 --- a/src/main/resources/i18n/sone.ru.properties +++ b/src/main/resources/i18n/sone.ru.properties @@ -74,6 +74,8 @@ Page.Options.Option.FcpFullAccessRequired.Description=Требовать сое Page.Options.Option.FcpFullAccessRequired.Value.No=Нет Page.Options.Option.FcpFullAccessRequired.Value.Writing=Для доступа на запись Page.Options.Option.FcpFullAccessRequired.Value.Always=Всегда +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) Page.Options.Section.Cleaning.Title=Очистка Page.Options.Option.ClearOnNextRestart.Description=Сбрасывает настройки дополнения Sone во время следующего перезапуска. Предупреждение! {strong}Это уничтожит все ваши Sone{/strong}, так что удостоверьтесь, что вы сохранили резервные копии всего, что вам еще нужно! Кроме того, вам нужно установить следующую настройку в значение true, чтобы действительно это сделать. Page.Options.Option.ReallyClearOnNextRestart.Description=Эта опция должна быть установлена в "yes", если вы действительно, {strong}действительно{/strong} хотите очистить настройки дополнения во время следующего перезапуска. From ba3105b0e4b344c0f069f65217a99f626e70bc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sun, 19 Apr 2020 15:57:00 +0200 Subject: [PATCH 85/87] =?UTF-8?q?=F0=9F=87=AE=F0=9F=87=B9=20Add=20Italian?= =?UTF-8?q?=20translation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/i18n/sone.it.properties | 468 +++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 src/main/resources/i18n/sone.it.properties diff --git a/src/main/resources/i18n/sone.it.properties b/src/main/resources/i18n/sone.it.properties new file mode 100644 index 000000000..d21adaade --- /dev/null +++ b/src/main/resources/i18n/sone.it.properties @@ -0,0 +1,468 @@ +Navigation.Menu.Sone.Name=Sone +Navigation.Menu.Sone.Tooltip=Freenet Social Network Plugin +Navigation.Menu.Sone.Item.Login.Name=Accedi +Navigation.Menu.Sone.Item.Login.Tooltip=Accedi al tuo "Sone" +Navigation.Menu.Sone.Item.Index.Name=Il tuo "Sone" +Navigation.Menu.Sone.Item.Index.Tooltip=Visualizza il tuo "Sone" +Navigation.Menu.Sone.Item.New.Name=Nuovi messaggi e Risposte +Navigation.Menu.Sone.Item.New.Tooltip=Visualizza nuovi messaggi e risposte +Navigation.Menu.Sone.Item.CreateSone.Name=Crea "Sone" +Navigation.Menu.Sone.Item.CreateSone.Tooltip=Crea un nuovo "Sone" +Navigation.Menu.Sone.Item.KnownSones.Name="Sone" conosciuti +Navigation.Menu.Sone.Item.KnownSones.Tooltip=Visualizza tutti i "Sone" conosciuti +Navigation.Menu.Sone.Item.Bookmarks.Name=Preferiti +Navigation.Menu.Sone.Item.Bookmarks.Tooltip=Visualizza messaggi preferiti +Navigation.Menu.Sone.Item.EditProfile.Name=Modifica Profilo +Navigation.Menu.Sone.Item.EditProfile.Tooltip=Modifica il profilo del tuo "Sone" +Navigation.Menu.Sone.Item.ImageBrowser.Name=Immagini +Navigation.Menu.Sone.Item.ImageBrowser.Tooltip=Gestisci le tue immagini +Navigation.Menu.Sone.Item.DeleteSone.Name=Cancella "Sone" +Navigation.Menu.Sone.Item.DeleteSone.Tooltip=Cancella il "Sone" corrente +Navigation.Menu.Sone.Item.Logout.Name=Esci +Navigation.Menu.Sone.Item.Logout.Tooltip=Disconnettiti dal "Sone" corrente +Navigation.Menu.Sone.Item.Options.Name=Opzioni +Navigation.Menu.Sone.Item.Options.Tooltip=Opzioni per il plugin "Sone" +Navigation.Menu.Sone.Item.Rescue.Name=Recupera +Navigation.Menu.Sone.Item.Rescue.Tooltip=Recupera "Sone" +Navigation.Menu.Sone.Item.About.Name=Informazioni +Navigation.Menu.Sone.Item.About.Tooltip=Informazioni su "Sone" +Navigation.Menu.Sone.Item.Metrics.Name=Metriche +Navigation.Menu.Sone.Item.Metrics.Tooltip=Metriche raccolte da "Sone" + +Page.About.Title=Informazioni su Sone +Page.About.Page.Title=Informazioni +Page.About.Flattr.Description=Se ti piace Sone e vuoi ringraziarmi con un premio, puoi usare il pulsante Flattr alla fine di ogni pagina. Flattr è un sistema di micro-pagamenti non anonimo che funziona come un barattolo delle mance e la quantità che ogni utente spende è limitata (minimo 2 € al mese). Maggiori informazioni possono essere trovate su {link}flattr.com{/link}. +Page.About.Homepage.Title=Homepage +Page.About.Homepage.Description=Puoi trovare maggiori informazioni e il codice sorgente di Sone sulla {link}homepage{/link}. +Page.About.License.Title=Licenza + +Page.Options.Title=Opzioni - "Sone" +Page.Options.Page.Title=Opzioni +Page.Options.Page.Description=Queste opzioni influenzano il comportamento del plugin "Sone" in esecuzione +Page.Options.Section.SoneSpecificOptions.Title=Opzioni specifiche di "Sone" +Page.Options.Section.SoneSpecificOptions.NotLoggedIn=Queste opzioni sono disponibili solo se sei {link}collegato{/link} +Page.Options.Section.SoneSpecificOptions.LoggedIn=Queste opzioni sono valide solo se ti sei identificato e sono valide solo per l'account con il quale ti sei identificato. +Page.Options.Option.AutoFollow.Description=Se un novo Sone viene trovato seguilo automaticamente. Da notare che verranno seguiti solo i Sone scoperti dopo l'attivazione di questa opzione! +Page.Options.Option.EnableSoneInsertNotifications.Description=Se abilitato, verrà visualizzato una notifica ogni volta che il tuo Sone sarà in fase di caricamento o sarà terminato il caricamento. +Page.Options.Option.ShowNotificationNewSones.Description=Visualizza notifiche per i nuovi "Sone". +Page.Options.Option.ShowNotificationNewPosts.Description=Visualizza notifiche per i nuovi messaggi. +Page.Options.Option.ShowNotificationNewReplies.Description=Visualizza notifiche per le nuove risposte. +Page.Options.Section.AvatarOptions.Title=Opzioni Avatar +Page.Options.Option.ShowAvatars.Description=Qui puoi disabilitare gli avatar customizzati, in base alla selezione fatta. Se un avatar è disabilitato verrà visualizzato l'avatar autogenerato. +Page.Options.Option.ShowAvatars.Never.Description=Non visualizzare mai gli avatar personalizzati. +Page.Options.Option.ShowAvatars.Followed.Description=Visualizza gli avatar solo per i "Sone" che segui. +Page.Options.Option.ShowAvatars.ManuallyTrusted.Description=Visualizza solo gli avatar per i Sone a cui hai assegnato un valore di trust maggiore di 0. +Page.Options.Option.ShowAvatars.Trusted.Description=Visualizza gli avatar solo per i "Sone" che hanno un livello di fiducia maggiore di 0. +Page.Options.Option.ShowAvatars.Always.Description=Visualizza sempre gli avatar personalizzati. Stai attento: alcuni avatar possono contenere immagini fastidiose od offensive. +Page.Options.Section.LoadLinkedImagesOptions.Title=Carica le immagini collegate +Page.Options.Option.LoadLinkedImages.Description="Sone" può cercare di caricare in automatico le immagini linkate nei messaggi e nelle risposte. Il caricamento avverrà sempre da Freenet, mai da internet! +Page.Options.Option.LoadLinkedImages.Never.Description=Non caricare mai le immagini linkate. +Page.Options.Option.LoadLinkedImages.Followed.Description=Carica le immagini linkate solo per i "Sone" che segui. +Page.Options.Option.LoadLinkedImages.ManuallyTrusted.Description=Visualizza solo le immagini linkate da Sone a cui hai assegnto un valore di trust maggiore di 0. +Page.Options.Option.LoadLinkedImages.Trusted.Description=Visualizza le immagini solo da Sone che hanno un livello di trust maggiore di 0. +Page.Options.Option.LoadLinkedImages.Always.Description=Carica sempre le immaigni. Attenzione: alcune immagini potrebbero essere offensive. +Page.Options.Section.RuntimeOptions.Title=Comportamento durante l'esecuzione +Page.Options.Option.InsertionDelay.Description=Il numero di secondi che il processo di caricamento del Sone aspetta, dopo una modifica, prima di iniziare il caricamento. +Page.Options.Option.PostsPerPage.Description=Il numero di messaggi da visualizzare su una pagina prima di visualizzare i controlli di paginazione. +Page.Options.Option.ImagesPerPage.Description=Il numero di immagini da visualizzare su una pagina prima di visualizzare i controlli di paginazione. +Page.Options.Option.CharactersPerPost.Description=The number of characters to display from a post before cutting it off and showing a link to expand it (-1 to disable). The actual length of the snippet is determined by the option below. +Page.Options.Option.PostCutOffLength.Description=The number of characters that are displayed if a post is deemed too long (see option above). Ignored if “number of characters to display” is disabled (set to -1). +Page.Options.Option.RequireFullAccess.Description=Whether to deny access to Sone to any host that has not been granted full access. +Page.Options.Section.FcpOptions.Title=Settaggi interfaccia FCP +Page.Options.Option.FcpInterfaceActive.Description=Activate the FCP interface to allow other plugins and remote clients to access your Sone plugin. +Page.Options.Option.FcpFullAccessRequired.Description=Require FCP connection from allowed hosts (see your {link}node’s configuration, section “FCP”{/link}) +Page.Options.Option.FcpFullAccessRequired.Value.No=No +Page.Options.Option.FcpFullAccessRequired.Value.Writing=Per l'accesso in scrittura +Page.Options.Option.FcpFullAccessRequired.Value.Always=Sempre +Page.Options.Section.WebOfTrustOptions.Title=Web of Trust Settings +Page.Options.Option.StrictFiltering.Description=Apply stricter filtering of identities. When this is selected, Sone will completely ignore identities that have a negative trust value for any of your local identities, otherwise they will be shown as long as at least one of your local identities has a positive trust value for them. (Note that it will take a couple of minutes for this setting to show effect.) +Page.Options.Section.Cleaning.Title=Pulisci +Page.Options.Option.ClearOnNextRestart.Description=Resets the configuration of the Sone plugin at the next restart. Warning! {strong}This will destroy all of your Sones{/strong} so make sure you have backed up everyhing you still need! Also, you need to set the next option to true to actually do it. +Page.Options.Option.ReallyClearOnNextRestart.Description=This option needs to be set to “yes” if you really, {strong}really{/strong} want to clear the plugin configuration on the next restart. +Page.Options.Warnings.ValueNotChanged=This option was not changed because the value you specified was not valid. +Page.Options.Button.Save=Salva + +Page.Login.Title=Login - Sone +Page.Login.Page.Title=Accedi +Page.Login.Label.SelectSone=Seleziona Sone: +Page.Login.Option.NoSone=Seleziona Sone... + +Page.Login.CreateSone.Title=Crea Sone + +Page.CreateSone.Title=Crea Sone - Sone + +Page.DeleteSone.Title=Cancella Sone - Sone +Page.DeleteSone.Page.Title=Cancellare il Sone "{sone}"? +Page.DeleteSone.Page.Description=This will not delete the Sone from Freenet (because that is impossible), it will merely disconnect your web of trust identity from Sone. +Page.DeleteSone.Button.Yes=Si, cancella. +Page.DeleteSone.Button.No=No, non cancellare. + +Page.Index.Title=Il tuo Sone - Sone +Page.Index.Label.Text=Testo del messaggio: +Page.Index.Label.Sender=Mittente: +Page.Index.Button.Post=Pubblica! +Page.Index.PostList.Title=Feed dei messaggi +Page.Index.PostList.Text.NoPostYet=Nessuno ha anocara scritto un messaggio ancora. Dovresti iniziare adesso! +Page.Index.PostList.Text.FollowSomeSones=Or maybe you are not following any Sones? Take a look at the list of {link}known Sones{/link} and follow whoever looks interesting! +Page.Index.PostList.Text.AutoFollowOption=You also have the option of automatically following newly discovered Sones. Take a look at the {link}options{/link} to activate the auto-follow feature! + +Page.New.Title=Nuovi messaggi e risposte - Sone +Page.New.Page.Title=Nuovi messaggi e risposte +Page.New.NothingNew=Al momento non c'è nulla di nuovo. + +Page.KnownSones.Title=Sone conosciuti - Sone +Page.KnownSones.Page.Title=Sone conosciuti +Page.KnownSones.Text.NoKnownSones=Al momento non ci sono Sone conosciuti che corrispondono al filtro. +Page.KnownSones.Label.Sort=Ordina: +Page.KnownSones.Label.FilterSones=Filtra i Sone: +Page.KnownSones.Sort.Field.Name=Nome +Page.KnownSones.Sort.Field.LastActivity=Ultima attività +Page.KnownSones.Sort.Field.Posts=Numero di messaggi +Page.KnownSones.Sort.Field.Images=Numero di immagini +Page.KnownSones.Sort.Order.Ascending=Ascendente +Page.KnownSones.Sort.Order.Descending=Discendente +Page.KnownSones.Filter.Followed=Visualizza solo i Sone seguiti +Page.KnownSones.Filter.NotFollowed=Nascondi i Sone seguiti +Page.KnownSones.Filter.New=Visualizza solo i nuovi Sone +Page.KnownSones.Filter.NotNew=Nascondi i nuovi Sone +Page.KnownSones.Filter.Own=Visualizza solo i Sone locali +Page.KnownSones.Filter.NotOwn=Visualizza solo i Sone remoti +Page.KnownSones.Button.Apply=Applica +Page.KnownSones.Button.FollowAllSones=Segui tutti i Sone su questa pagina +Page.KnownSones.Button.UnfollowAllSones=Smetti di seguire tutti i Sone su questa pagina + +Page.EditProfile.Title=Modifica profilo - Sone +Page.EditProfile.Page.Title=Modifica profilo +Page.EditProfile.Page.Description=Su questa pagina puoi inserire i dati del tuo profilo +Page.EditProfile.Page.Hint.Optionality=And remember, every single field of this profile is optional! You are not required to enter a single thing here! Also, everything you enter here will probably be stored in Freenet for a very long time! +Page.EditProfile.Label.FirstName=Nome: +Page.EditProfile.Label.MiddleName=Secondo nome(i): +Page.EditProfile.Label.LastName=Cognome: +Page.EditProfile.Birthday.Title=Compleanno: +Page.EditProfile.Birthday.Label.Day=Giorno: +Page.EditProfile.Birthday.Label.Month=Mese: +Page.EditProfile.Birthday.Label.Year=Anno: +Page.EditProfile.Avatar.Title=Avatar +Page.EditProfile.Avatar.Description=You can select one of your uploaded images to be shown as avatar. It should not be larger than 64×64 pixels because that is the largest size shown for other people (80×80 pixels is used for the page header). +Page.EditProfile.Avatar.Delete=Nessun avatar +Page.EditProfile.Fields.Title=Campi personalizzati +Page.EditProfile.Fields.Description=Here you can enter custom fields into your profile. These fields can contain anything you want and be as terse or as verbose as you wish. Just remember that when it comes to anonymity, sometimes less is more. +Page.EditProfile.Fields.Button.Edit=modifica +Page.EditProfile.Fields.Button.MoveUp=sposta sù +Page.EditProfile.Fields.Button.MoveDown=sposta giù +Page.EditProfile.Fields.Button.Delete=cancella +Page.EditProfile.Fields.Button.ReallyDelete=cancella davvero +Page.EditProfile.Fields.AddField.Title=Aggiungi campo +Page.EditProfile.Fields.AddField.Label.Name=Nome: +Page.EditProfile.Fields.AddField.Button.AddField=Aggiungi campo +Page.EditProfile.Button.Save=Salva profilo +Page.EditProfile.Error.DuplicateFieldName=Il nome “{fieldName}” esiste già. + +Page.EditProfileField.Title=Modifica campo del profilo - Sone +Page.EditProfileField.Page.Title=Modifica campo del profilo +Page.EditProfileField.Text=Inserisci un nome per questo campo del profilo. +Page.EditProfileField.Error.DuplicateFieldName=Il nome del campo inserito è già in uso. +Page.EditProfileField.Button.Save=Cambia +Page.EditProfileField.Button.Reset=Rimetti il vecchio nome +Page.EditProfileField.Button.Cancel=Non cambiare nome + +Page.DeleteProfileField.Title=Cancella il campo del profilo - Sone +Page.DeleteProfileField.Page.Title=Cancella il campo del profilo +Page.DeleteProfileField.Text=Vuoi davvero cancellare questo campo del profilo? +Page.DeleteProfileField.Button.Yes=Si, cancella +Page.DeleteProfileField.Button.No=No, non cancellare + +Page.CreatePost.Title=Scrivi un messaggio - Sone +Page.CreatePost.Page.Title=Scrivi un messaggio +Page.CreatePost.Label.Text=Testo del messaggio: +Page.CreatePost.Button.Post=Pubblica! +Page.CreatePost.Error.EmptyText=Non hai inserito alcun messaggio, che è un peccato. Dovresti provare a scrivere di più! + +Page.CreateReply.Title=Rispondi - Sone +Page.CreateReply.Page.Title=Riscpondi +Page.CreateReply.Error.EmptyText=Non hai inserito alcun messaggio, che è un peccato. Dovresti provare a scrivere di più! +Page.CreateReply.Label.Text=Testo della risposta: +Page.CreateReply.Button.Post=Pubblica risposta! + +Page.ViewSone.Title=Visualizza Sone - Sone +Page.ViewSone.Page.TitleWithoutSone=Visualizza Sone sconosciuto +Page.ViewSone.NoSone.Description=There is currently no known Sone with the ID {sone}. If you were looking for a specific Sone, make sure that it is visible in your web of trust: +Page.ViewSone.UnknownSone.Description=This Sone has not yet been retrieved. Please check back in a short time. +Page.ViewSone.UnknownSone.LinkToWebOfTrust=Even though the Sone is still unknown, its Web of Trust profile might already be available: +Page.ViewSone.WriteAMessage=You can write a message to this Sone here. Please note that everybody will be able to read this message! +Page.ViewSone.PostList.Title=Posts by {sone} +Page.ViewSone.PostList.Text.NoPostYet=This Sone has not yet posted anything. +Page.ViewSone.Profile.Title=Profilo +Page.ViewSone.Profile.Label.Name=Nome +Page.ViewSone.Profile.Label.Albums=Album +Page.ViewSone.Profile.Albums.Text.All=Tutti gli album +Page.ViewSone.Profile.Name.WoTLink=web of trust profile +Page.ViewSone.Replies.Title=Messaggi a cui {sone} ha risposto + +Page.ViewPost.Title=Visualizza messaggio - Sone +Page.ViewPost.Page.Title=Visualizza messaggi di {sone} +Page.ViewPost.Page.TitleUnknownSone=Visualizza messaggio sconosciuto +Page.ViewPost.Text.UnknownPost=This post has not yet been downloaded. + +Page.Like.Title=Like Post - Sone +Page.Unlike.Title=Unlike Post - Sone + +Page.DeletePost.Title=Cancella Sone - Sone +Page.DeletePost.Page.Title=Cancella Sone +Page.DeletePost.Text.PostWillBeGone=Deleting a post will remove it from your Sone. It will not remove it from Freenet because that is not possible. Older versions of your Sone will always include the deleted post. +Page.DeletePost.Button.Yes=Si, cancella. +Page.DeletePost.Button.No=No, non cancellare. + +Page.DeleteReply.Title=Cancella risposta - Sone +Page.DeleteReply.Page.Title=Cancella risposta +Page.DeleteReply.Text.PostWillBeGone=Deleting a reply will remove it from your Sone. It will not remove it from Freenet because that is not possible. Older versions of your Sone will always include the deleted reply. +Page.DeleteReply.Button.Yes=Si, cancella. +Page.DeleteReply.Button.No=No, do not delete. + +Page.LockSone.Title=Lock Sone - Sone + +Page.UnlockSone.Title=Unlock Sone - Sone + +Page.FollowSone.Title=Follow Sone - Sone + +Page.UnfollowSone.Title=Unfollow Sone - Sone + +Page.ImageBrowser.Title=Image Browser - Sone +Page.ImageBrowser.Album.Title=Album “{album}” +Page.ImageBrowser.Album.Error.NotFound.Text=The requested album could not be found. It is possible that it has not yet been downloaded, or that it has been deleted. +Page.ImageBrowser.Sone.Title=Albums of {sone} +Page.ImageBrowser.Sone.Error.NotFound.Text=The requested Sone could not be found. It is possible that it has not yet been downloaded. +Page.ImageBrowser.Header.Albums=Albums +Page.ImageBrowser.Header.Images=Images +Page.ImageBrowser.Link.All=All Sones +Page.ImageBrowser.CreateAlbum.Button.CreateAlbum=Create Album +Page.ImageBrowser.Album.Edit.Title=Edit Album +Page.ImageBrowser.Album.Delete.Title=Delete Album +Page.ImageBrowser.Album.Label.AlbumImage=Album Image: +Page.ImageBrowser.Album.Label.Title=Title: +Page.ImageBrowser.Album.Label.Description=Description: +Page.ImageBrowser.Album.AlbumImage.Choose=Choose Album Image… +Page.ImageBrowser.Album.Button.Save=Save Album +Page.ImageBrowser.Album.Button.Delete=Delete Album +Page.ImageBrowser.Image.Edit.Title=Edit Image +Page.ImageBrowser.Image.Title.Label=Title: +Page.ImageBrowser.Image.Description.Label=Description: +Page.ImageBrowser.Image.Button.MoveLeft=◀ +Page.ImageBrowser.Image.Button.Save=Save Image +Page.ImageBrowser.Image.Button.MoveRight=► +Page.ImageBrowser.Image.Delete.Title=Delete Image +Page.ImageBrowser.Image.Button.Delete=Delete Image + +Page.CreateAlbum.Title=Create Album - Sone +Page.CreateAlbum.Page.Title=Create Album +Page.CreateAlbum.Error.NameMissing=Sembra che ti sia dimenticato di inserire un nome per il nuovo album. + +Page.UploadImage.Title=Upload Image - Sone +Page.UploadImage.Error.InvalidImage=The image you were trying to upload could not be recognized. Please upload only JPEG (*.jpg or *.jpeg), or PNG (*.png) images. + +Page.EditImage.Title=Edit Image - Sone + +Page.DeleteImage.Title=Delete Image - Sone +Page.DeleteImage.Page.Title=Delete Image +Page.DeleteImage.Text.ImageWillBeGone=This will remove the image “{image}” from your album “{album}”. If it has already been inserted into Freenet it can not be removed from there forcefully. Do you want to delete the image? +Page.DeleteImage.Button.Yes=Si, cancella immagine. +Page.DeleteImage.Button.No=No, don’t delete image. + +Page.EditAlbum.Title=Edit Album - Sone + +Page.DeleteAlbum.Title=Delete Album - Sone +Page.DeleteAlbum.Page.Title=Delete Album +Page.DeleteAlbum.Text.AlbumWillBeGone=This will remove your album “{title}”. Do you really want to do that? +Page.DeleteAlbum.Button.Yes=Si, cancella album. +Page.DeleteAlbum.Button.No=No, don’t delete album. + +Page.MarkAsKnown.Title=Mark as Known - Sone + +Page.Bookmark.Title=Bookmark - Sone +Page.Unbookmark.Title=Remove Bookmark - Sone +Page.Bookmarks.Title=Bookmarks - Sone +Page.Bookmarks.Page.Title=Bookmarks +Page.Bookmarks.Text.NoBookmarks=You don’t have any bookmarks defined right now. You can bookmark posts by clicking the star below the post. +Page.Bookmarks.Text.PostsNotLoaded=Some of your bookmarked posts have not been shown because they could not be loaded. This can happen if you restarted Sone recently or if the originating Sone has deleted the post. If you are reasonable sure that these posts do not exist anymore, you can {link}unbookmark them{/link}. + +Page.Search.Title=Search - Sone +Page.Search.Page.Title=Search Results +Page.Search.Text.SoneHits=The following Sones match your search terms. +Page.Search.Text.PostHits=The following posts match your search terms. +Page.Search.Text.NoHits=No Sones or posts matched your search terms. + +Page.Rescue.Title=Rescue Sone - Sone +Page.Rescue.Page.Title=Rescue Sone “{0}” +Page.Rescue.Text.Description=The Rescue Mode lets you restore previous versions of your Sone. This can be necessary if your configuration was lost. +Page.Rescue.Text.Procedure=The Rescue Mode works by fetching the latest inserted edition of your Sone. If an edition was successfully fetched it will be loaded into your Sone, letting you control your posts, profile, and other settings (you could do that in a second browser tab or window). If the fetched edition is not the one you want to restore, instruct the Rescue Mode to fetch the next older edition below. +Page.Rescue.Text.Fetching=The Sone Rescuer is currently fetching edition {0} of your Sone. +Page.Rescue.Text.Fetched=The Sone Rescuer has downloaded edition {0} of your Sone. Please check your posts, replies, and profile. If you like what the current Sone contains, just unlock it. +Page.Rescue.Text.FetchedLast=The Sone rescuer has downloaded the last available edition. If it did not manage to restore your Sone you are probably out of luck now. +Page.Rescue.Text.NotFetched=The Sone Rescuer could not download edition {0} of your Sone. Please either try again with edition {0}, or try the next older edition. +Page.Rescue.Label.NextEdition=Next edition +Page.Rescue.Button.Fetch=Fetch edition + +Page.NoPermission.Title=Unauthorized Access - Sone +Page.NoPermission.Page.Title=Unauthorized Access +Page.NoPermission.Text.NoPermission=You tried to do something that you do not have sufficient authorization for. Please refrain from such actions in the future or we will be forced to take counter-measures! + +Page.EmptyImageTitle.Title=Title Must Not Be Empty - Sone +Page.EmptyImageTitle.Page.Title=Title Must Not Be Empty +Page.EmptyImageTitle.Text.EmptyImageTitle=You have to give your image a title. Please go back to the previous page and enter a title. + +Page.EmptyAlbumTitle.Title=Title Must Not Be Empty - Sone +Page.EmptyAlbumTitle.Page.Title=Title Must Not Be Empty +Page.EmptyAlbumTitle.Text.EmptyAlbumTitle=You have to give your album a title. Please go back to the previous page and enter a title. + +Page.DismissNotification.Title=Dismiss Notification - Sone + +Page.WotPluginMissing.Text.WotRequired=Because the Web of Trust is an integral part of Sone, the Web of Trust plugin has to be loaded in order to run Sone. +Page.WotPluginMissing.Text.LoadPlugin=Please load the Web of Trust plugin in the {link}plugin manager{/link}. + +Page.Logout.Title=Logout - Sone + +Page.Invalid.Title=Invalid Action Performed - Sone +Page.Invalid.Page.Title=Invalid Action Performed +Page.Invalid.Text=An invalid action was performed, or the action was valid but the parameters were not. Please go back to the {link}index page{/link} and try again. If the error persists you have probably found a bug. + +Page.Metrics.Title=Metrics +Page.Metrics.Page.Title=Metrics +Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration +Page.Metrics.SoneParseDuration.Title=Sone Parse Duration +Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration + +View.Search.Button.Search=Search + +View.CreateSone.Text.WotIdentityRequired=To create a Sone you need an identity from the {link}Web of Trust plugin{/link}. +View.CreateSone.Select.Default=Select an identity +View.CreateSone.Text.NoIdentities=You do not have any Web of Trust identities. Please head over to the {link}Web of Trust plugin{/link} and create an identity. +View.CreateSone.Text.NoNonSoneIdentities=You do not have any Web of Trust identities that are not already a Sone. Use one of the remaining Web of Trust identities to create a new Sone or head over to the {link}Web of Trust plugin{/link} to create a new identity. +View.CreateSone.Button.Create=Create Sone +View.CreateSone.Text.Error.NoIdentity=You have not selected an identity. + +View.Sone.Label.LastUpdate=Last update: +View.Sone.Text.UnknownDate=unknown +View.Sone.Stats.Posts={0,number} {0,choice,0#posts|1#post|1