From 339671a5532a02cfbc25269b8ac3f13fc6a09295 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 4 Jan 2020 15:40:31 +0100 Subject: [PATCH] =?utf8?q?=F0=9F=9A=A7=20Add=20detector=20for=20mentioned?= =?utf8?q?=20local=20Sones?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../core/event/LocalSoneMentionedInPostEvent.kt | 27 +++++ .../pterodactylus/sone/text/SoneMentionDetector.kt | 44 ++++++++ .../kotlin/net/pterodactylus/sone/test/Mocks.kt | 30 +++++ .../sone/text/SoneMentionDetectorTest.kt | 124 +++++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 src/main/kotlin/net/pterodactylus/sone/core/event/LocalSoneMentionedInPostEvent.kt create mode 100644 src/main/kotlin/net/pterodactylus/sone/text/SoneMentionDetector.kt create mode 100644 src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt create mode 100644 src/test/kotlin/net/pterodactylus/sone/text/SoneMentionDetectorTest.kt diff --git a/src/main/kotlin/net/pterodactylus/sone/core/event/LocalSoneMentionedInPostEvent.kt b/src/main/kotlin/net/pterodactylus/sone/core/event/LocalSoneMentionedInPostEvent.kt new file mode 100644 index 0000000..3e644e0 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/event/LocalSoneMentionedInPostEvent.kt @@ -0,0 +1,27 @@ +/** + * Sone - LocalSoneMentionedInPostEvent.kt - Copyright © 2019 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.core.event + +import net.pterodactylus.sone.data.* + +/** + * Event that signals that a new post or reply was found that mentioned a local + * Sone, which happens if the [SoneTextParser] locates a [SonePart] in a post + * or reply. + */ +data class LocalSoneMentionedInPostEvent(val post: Post) diff --git a/src/main/kotlin/net/pterodactylus/sone/text/SoneMentionDetector.kt b/src/main/kotlin/net/pterodactylus/sone/text/SoneMentionDetector.kt new file mode 100644 index 0000000..7dd8285 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/text/SoneMentionDetector.kt @@ -0,0 +1,44 @@ +/** + * Sone - SoneMentionDetector.kt - Copyright © 2019 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.text + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.utils.* +import javax.inject.* + +/** + * Listens to [NewPostFoundEvent]s and [NewPostReplyFoundEvent], parses the + * texts and emits a [LocalSoneMentionedInPostEvent] if a [SoneTextParser] + * finds a [SonePart] that points to a local [Sone]. + */ +class SoneMentionDetector @Inject constructor(private val eventBus: EventBus, private val soneTextParser: SoneTextParser) { + + @Subscribe + fun onNewPost(newPostFoundEvent: NewPostFoundEvent) { + newPostFoundEvent.post.let { post -> + post.sone.isLocal.onFalse { + val parts = soneTextParser.parse(post.text, null) + if (parts.filterIsInstance().any { it.sone.isLocal }) { + eventBus.post(LocalSoneMentionedInPostEvent(post)) + } + } + } + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt new file mode 100644 index 0000000..f469dd7 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt @@ -0,0 +1,30 @@ +/** + * Sone - Mocks.kt - Copyright © 2019 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.test + +import freenet.crypt.* +import freenet.keys.* +import net.pterodactylus.sone.data.impl.* +import net.pterodactylus.sone.utils.* + +fun createId() = InsertableClientSSK.createRandom(DummyRandomSource(), "").uri.routingKey.asFreenetBase64 + +fun createLocalSone(id: String? = createId()) = object : IdOnlySone(id) { + override fun isLocal() = true +} +fun createRemoteSone(id: String? = createId()) = IdOnlySone(id) diff --git a/src/test/kotlin/net/pterodactylus/sone/text/SoneMentionDetectorTest.kt b/src/test/kotlin/net/pterodactylus/sone/text/SoneMentionDetectorTest.kt new file mode 100644 index 0000000..c319809 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/text/SoneMentionDetectorTest.kt @@ -0,0 +1,124 @@ +/** + * Sone - SoneMentionDetectorTest.kt - Copyright © 2019 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.text + +import com.google.common.eventbus.* +import net.pterodactylus.sone.core.event.* +import net.pterodactylus.sone.data.* +import net.pterodactylus.sone.database.* +import net.pterodactylus.sone.test.* +import org.hamcrest.MatcherAssert.* +import org.hamcrest.Matchers.* +import kotlin.test.* + +/** + * Unit test for [SoneMentionDetector]. + */ +class SoneMentionDetectorTest { + + private val eventBus = EventBus() + private val soneProvider = TestSoneProvider() + private val postProvider = TestPostProvider() + private val soneTextParser = SoneTextParser(soneProvider, postProvider) + private val capturedEvents = mutableListOf() + + init { + eventBus.register(SoneMentionDetector(eventBus, soneTextParser)) + eventBus.register(object : Any() { + @Subscribe + fun captureEvent(localSoneMentionedInPostEvent: LocalSoneMentionedInPostEvent) { + capturedEvents += localSoneMentionedInPostEvent + } + }) + } + + @Test + fun `detector does not emit event on post that does not contain any sones`() { + val post = createPost() + eventBus.post(NewPostFoundEvent(post)) + assertThat(capturedEvents, emptyIterable()) + } + + @Test + fun `detector does not emit event on post that does contain two remote sones`() { + val post = createPost("text mentions sone://${remoteSone1.id} and sone://${remoteSone2.id}.") + eventBus.post(NewPostFoundEvent(post)) + assertThat(capturedEvents, emptyIterable()) + } + + @Test + fun `detector emits event on post that contains links to a remote and a local sone`() { + val post = createPost("text mentions sone://${localSone1.id} and sone://${remoteSone2.id}.") + eventBus.post(NewPostFoundEvent(post)) + assertThat(capturedEvents, contains(LocalSoneMentionedInPostEvent(post))) + } + + @Test + fun `detector emits one event on post that contains two links to the same local sone`() { + val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone1.id}.") + eventBus.post(NewPostFoundEvent(post)) + assertThat(capturedEvents, contains(LocalSoneMentionedInPostEvent(post))) + } + + @Test + fun `detector emits one event on post that contains links to two local sones`() { + val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.") + eventBus.post(NewPostFoundEvent(post)) + assertThat(capturedEvents, contains(LocalSoneMentionedInPostEvent(post))) + } + + @Test + fun `detector does not emit event for post by local sone`() { + val post = createPost("text mentions sone://${localSone1.id} and sone://${localSone2.id}.", localSone1) + eventBus.post(NewPostFoundEvent(post)) + assertThat(capturedEvents, emptyIterable()) + } + +} + +private val remoteSone1 = createRemoteSone() +private val remoteSone2 = createRemoteSone() + +private val localSone1 = createLocalSone() +private val localSone2 = createLocalSone() + +private fun createPost(text: String = "", sone: Sone = remoteSone1): Post.EmptyPost { + return object : Post.EmptyPost("post-id") { + override fun getSone() = sone + override fun getText() = text + } +} + +private class TestSoneProvider : SoneProvider { + + override val sones: Collection get() = remoteSones + localSones + override val localSones: Collection get() = setOf(localSone1, localSone2) + override val remoteSones: Collection get() = setOf(remoteSone1, remoteSone2) + override val soneLoader: (String) -> Sone? get() = this::getSone + override fun getSone(soneId: String): Sone? = + localSones.firstOrNull { it.id == soneId } ?: remoteSones.firstOrNull { it.id == soneId } + +} + +private class TestPostProvider : PostProvider { + + override fun getPost(postId: String): Post? = null + override fun getPosts(soneId: String): Collection = emptyList() + override fun getDirectedPosts(recipientId: String): Collection = emptyList() + +} -- 2.7.4