From fedc22de1913a39285d0704a56f3d8b3f8361a00 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 20 Jun 2020 20:30:25 +0200 Subject: [PATCH] =?utf8?q?=E2=99=BB=EF=B8=8F=20Extract=20interface=20from?= =?utf8?q?=20post=20visibility=20filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../sone/notify/DefaultPostVisibilityFilter.java | 92 +++++++++++++++ .../sone/notify/PostVisibilityFilter.java | 82 +------------- .../sone/notify/DefaultPostVisibilityFilterTest.kt | 123 +++++++++++++++++++++ .../sone/notify/ListNotificationFilterTest.kt | 2 +- .../sone/notify/PostVisibilityFilterTest.kt | 123 --------------------- 5 files changed, 222 insertions(+), 200 deletions(-) create mode 100644 src/main/java/net/pterodactylus/sone/notify/DefaultPostVisibilityFilter.java create mode 100644 src/test/kotlin/net/pterodactylus/sone/notify/DefaultPostVisibilityFilterTest.kt delete mode 100644 src/test/kotlin/net/pterodactylus/sone/notify/PostVisibilityFilterTest.kt diff --git a/src/main/java/net/pterodactylus/sone/notify/DefaultPostVisibilityFilter.java b/src/main/java/net/pterodactylus/sone/notify/DefaultPostVisibilityFilter.java new file mode 100644 index 0000000..a1ecfb7 --- /dev/null +++ b/src/main/java/net/pterodactylus/sone/notify/DefaultPostVisibilityFilter.java @@ -0,0 +1,92 @@ +package net.pterodactylus.sone.notify; + +import java.util.function.Predicate; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.inject.Singleton; + +import net.pterodactylus.sone.data.Post; +import net.pterodactylus.sone.data.Sone; +import net.pterodactylus.sone.freenet.wot.OwnIdentity; +import net.pterodactylus.sone.freenet.wot.Trust; +import net.pterodactylus.util.notify.Notification; + +/** + * Filters {@link Notification}s involving {@link Post}s. + */ +@Singleton +public class DefaultPostVisibilityFilter implements PostVisibilityFilter { + + /** + * Checks whether a post is visible to the given Sone. A post is not + * considered visible if one of the following statements is true: + * + *

+ * If {@code post} is not {@code null} more checks are performed, and the + * post will be invisible if: + *

+ * + * If none of these statements is true the post is considered visible. + * + * @param sone + * The Sone that checks for a post’s visibility (may be + * {@code null} to skip Sone-specific checks, such as trust) + * @param post + * The post to check for visibility + * @return {@code true} if the post is considered visible, {@code false} + * otherwise + */ + @Override + public boolean isPostVisible(@Nullable Sone sone, @Nonnull Post post) { + checkNotNull(post, "post must not be null"); + if (!post.isLoaded()) { + return false; + } + Sone postSone = post.getSone(); + if (sone != null) { + Trust trust = postSone.getIdentity().getTrust((OwnIdentity) sone.getIdentity()); + if (trust != null) { + if ((trust.getExplicit() != null) && (trust.getExplicit() < 0)) { + return false; + } + if ((trust.getExplicit() == null) && (trust.getImplicit() != null) && (trust.getImplicit() < 0)) { + return false; + } + } else { + /* + * a null trust means that the trust updater has not yet + * received a trust value for this relation. if we return false, + * the post feed will stay empty until the trust updater has + * received trust values. to prevent this we simply assume that + * posts are visible if there is no trust. + */ + } + if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.getId().equals(post.getRecipientId().orNull())) { + return false; + } + } + return post.getTime() <= System.currentTimeMillis(); + } + + @Override + @Nonnull + public Predicate isVisible(@Nullable final Sone currentSone) { + return post -> (post != null) && isPostVisible(currentSone, post); + } + +} diff --git a/src/main/java/net/pterodactylus/sone/notify/PostVisibilityFilter.java b/src/main/java/net/pterodactylus/sone/notify/PostVisibilityFilter.java index cf84ebf..308074a 100644 --- a/src/main/java/net/pterodactylus/sone/notify/PostVisibilityFilter.java +++ b/src/main/java/net/pterodactylus/sone/notify/PostVisibilityFilter.java @@ -1,90 +1,20 @@ package net.pterodactylus.sone.notify; import java.util.function.Predicate; - -import static com.google.common.base.Preconditions.checkNotNull; - import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.inject.Singleton; import net.pterodactylus.sone.data.Post; import net.pterodactylus.sone.data.Sone; -import net.pterodactylus.sone.freenet.wot.OwnIdentity; -import net.pterodactylus.sone.freenet.wot.Trust; -import net.pterodactylus.util.notify.Notification; -/** - * Filters {@link Notification}s involving {@link Post}s. - */ -@Singleton -public class PostVisibilityFilter { +import com.google.inject.ImplementedBy; + +@ImplementedBy(DefaultPostVisibilityFilter.class) +public interface PostVisibilityFilter { - /** - * Checks whether a post is visible to the given Sone. A post is not - * considered visible if one of the following statements is true: - * - *

- * If {@code post} is not {@code null} more checks are performed, and the - * post will be invisible if: - *

- * - * If none of these statements is true the post is considered visible. - * - * @param sone - * The Sone that checks for a post’s visibility (may be - * {@code null} to skip Sone-specific checks, such as trust) - * @param post - * The post to check for visibility - * @return {@code true} if the post is considered visible, {@code false} - * otherwise - */ - boolean isPostVisible(@Nullable Sone sone, @Nonnull Post post) { - checkNotNull(post, "post must not be null"); - if (!post.isLoaded()) { - return false; - } - Sone postSone = post.getSone(); - if (sone != null) { - Trust trust = postSone.getIdentity().getTrust((OwnIdentity) sone.getIdentity()); - if (trust != null) { - if ((trust.getExplicit() != null) && (trust.getExplicit() < 0)) { - return false; - } - if ((trust.getExplicit() == null) && (trust.getImplicit() != null) && (trust.getImplicit() < 0)) { - return false; - } - } else { - /* - * a null trust means that the trust updater has not yet - * received a trust value for this relation. if we return false, - * the post feed will stay empty until the trust updater has - * received trust values. to prevent this we simply assume that - * posts are visible if there is no trust. - */ - } - if ((!postSone.equals(sone)) && !sone.hasFriend(postSone.getId()) && !sone.getId().equals(post.getRecipientId().orNull())) { - return false; - } - } - return post.getTime() <= System.currentTimeMillis(); - } + boolean isPostVisible(@Nullable Sone sone, @Nonnull Post post); @Nonnull - public Predicate isVisible(@Nullable final Sone currentSone) { - return post -> (post != null) && isPostVisible(currentSone, post); - } + Predicate isVisible(@Nullable Sone currentSone); } diff --git a/src/test/kotlin/net/pterodactylus/sone/notify/DefaultPostVisibilityFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/notify/DefaultPostVisibilityFilterTest.kt new file mode 100644 index 0000000..8a9a699 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/notify/DefaultPostVisibilityFilterTest.kt @@ -0,0 +1,123 @@ +package net.pterodactylus.sone.notify + +import com.google.inject.Guice +import net.pterodactylus.sone.freenet.wot.OwnIdentity +import net.pterodactylus.sone.freenet.wot.Trust +import net.pterodactylus.sone.test.createLocalSone +import net.pterodactylus.sone.test.createPost +import net.pterodactylus.sone.test.createRemoteSone +import net.pterodactylus.sone.test.verifySingletonInstance +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Test + +/** + * Unit test for [DefaultPostVisibilityFilter]. + */ +class DefaultPostVisibilityFilterTest { + + private val postVisibilityFilter = DefaultPostVisibilityFilter() + private val localSone = createLocalSone() + private val remoteSone = createRemoteSone() + + @Test + fun `post visibility filter is only created once`() { + val injector = Guice.createInjector() + injector.verifySingletonInstance() + } + + @Test + fun `post is not visible if it is not loaded`() { + val post = createPost(loaded = false) + assertThat(postVisibilityFilter.isPostVisible(null, post), equalTo(false)) + } + + @Test + fun `loaded post is visible without sone and in the past`() { + val post = createPost(sone = null) + assertThat(postVisibilityFilter.isPostVisible(null, post), equalTo(true)) + } + + @Test + fun `loaded post from the future is not visible`() { + // the offset for the future must be large enough to survive loading freenet.crypt.Util. + val post = createPost(time = System.currentTimeMillis() + 100000, sone = null) + assertThat(postVisibilityFilter.isPostVisible(null, post), equalTo(false)) + } + + @Test + fun `loaded post from explicitely not trusted sone is not visible`() { + remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(-1, null, null)) + val post = createPost(sone = remoteSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(false)) + } + + @Test + fun `loaded post from implicitely untrusted sone is not visible`() { + remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(null, -1, null)) + val post = createPost(sone = remoteSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(false)) + } + + @Test + fun `loaded post from implicitely untrusted but followed sone is visible`() { + localSone.friends.add(remoteSone.id) + remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(1, -1, null)) + val post = createPost(sone = remoteSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) + } + + @Test + fun `loaded post from implicitely trusted and followed sone is visible`() { + localSone.friends.add(remoteSone.id) + remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(null, 1, null)) + val post = createPost(sone = remoteSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) + } + + @Test + fun `loaded post from followed sone with unknown trust is visible`() { + localSone.friends.add(remoteSone.id) + remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(null, null, null)) + val post = createPost(sone = remoteSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) + } + + @Test + fun `loaded post from unfollowed remote sone that is not directed at local sone is not visible`() { + val post = createPost(sone = remoteSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(false)) + } + + @Test + fun `loaded post from local sone is visible`() { + val post = createPost(sone = localSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) + } + + @Test + fun `loaded post from followed remote sone that is not directed at local sone is visible`() { + localSone.friends.add(remoteSone.id) + val post = createPost(sone = remoteSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) + } + + @Test + fun `loaded post from remote sone that is directed at local sone is visible`() { + val post = createPost(sone = remoteSone, recipient = localSone) + assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) + } + + @Test + fun `predicate will correctly recognize visible post`() { + val post = createPost(sone = localSone) + assertThat(postVisibilityFilter.isVisible(null).test(post), equalTo(true)) + } + + @Test + fun `predicate will correctly recognize not visible post`() { + val post = createPost(loaded = false) + assertThat(postVisibilityFilter.isVisible(null).test(post), equalTo(false)) + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/notify/ListNotificationFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/notify/ListNotificationFilterTest.kt index e552ece..327732e 100644 --- a/src/test/kotlin/net/pterodactylus/sone/notify/ListNotificationFilterTest.kt +++ b/src/test/kotlin/net/pterodactylus/sone/notify/ListNotificationFilterTest.kt @@ -213,7 +213,7 @@ private fun matchThisPost(post: Post) = createPostVisibilityFilter { _, p -> p = private val showAllPosts = createPostVisibilityFilter { _, _ -> true } private val showNoPosts = createPostVisibilityFilter { _, _ -> false } -private fun createPostVisibilityFilter(visible: (Sone?, Post) -> Boolean) = object : PostVisibilityFilter() { +private fun createPostVisibilityFilter(visible: (Sone?, Post) -> Boolean) = object : PostVisibilityFilter { override fun isPostVisible(sone: Sone?, post: Post) = visible(sone, post) override fun isVisible(currentSone: Sone?) = Predicate { p -> p != null && isPostVisible(currentSone, p) } } diff --git a/src/test/kotlin/net/pterodactylus/sone/notify/PostVisibilityFilterTest.kt b/src/test/kotlin/net/pterodactylus/sone/notify/PostVisibilityFilterTest.kt deleted file mode 100644 index f89035c..0000000 --- a/src/test/kotlin/net/pterodactylus/sone/notify/PostVisibilityFilterTest.kt +++ /dev/null @@ -1,123 +0,0 @@ -package net.pterodactylus.sone.notify - -import com.google.inject.Guice -import net.pterodactylus.sone.freenet.wot.OwnIdentity -import net.pterodactylus.sone.freenet.wot.Trust -import net.pterodactylus.sone.test.createLocalSone -import net.pterodactylus.sone.test.createPost -import net.pterodactylus.sone.test.createRemoteSone -import net.pterodactylus.sone.test.verifySingletonInstance -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.equalTo -import org.junit.Test - -/** - * Unit test for [PostVisibilityFilterTest]. - */ -class PostVisibilityFilterTest { - - private val postVisibilityFilter = PostVisibilityFilter() - private val localSone = createLocalSone() - private val remoteSone = createRemoteSone() - - @Test - fun `post visibility filter is only created once`() { - val injector = Guice.createInjector() - injector.verifySingletonInstance() - } - - @Test - fun `post is not visible if it is not loaded`() { - val post = createPost(loaded = false) - assertThat(postVisibilityFilter.isPostVisible(null, post), equalTo(false)) - } - - @Test - fun `loaded post is visible without sone and in the past`() { - val post = createPost(sone = null) - assertThat(postVisibilityFilter.isPostVisible(null, post), equalTo(true)) - } - - @Test - fun `loaded post from the future is not visible`() { - // the offset for the future must be large enough to survive loading freenet.crypt.Util. - val post = createPost(time = System.currentTimeMillis() + 100000, sone = null) - assertThat(postVisibilityFilter.isPostVisible(null, post), equalTo(false)) - } - - @Test - fun `loaded post from explicitely not trusted sone is not visible`() { - remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(-1, null, null)) - val post = createPost(sone = remoteSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(false)) - } - - @Test - fun `loaded post from implicitely untrusted sone is not visible`() { - remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(null, -1, null)) - val post = createPost(sone = remoteSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(false)) - } - - @Test - fun `loaded post from implicitely untrusted but followed sone is visible`() { - localSone.friends.add(remoteSone.id) - remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(1, -1, null)) - val post = createPost(sone = remoteSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) - } - - @Test - fun `loaded post from implicitely trusted and followed sone is visible`() { - localSone.friends.add(remoteSone.id) - remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(null, 1, null)) - val post = createPost(sone = remoteSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) - } - - @Test - fun `loaded post from followed sone with unknown trust is visible`() { - localSone.friends.add(remoteSone.id) - remoteSone.identity.setTrust(localSone.identity as OwnIdentity, Trust(null, null, null)) - val post = createPost(sone = remoteSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) - } - - @Test - fun `loaded post from unfollowed remote sone that is not directed at local sone is not visible`() { - val post = createPost(sone = remoteSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(false)) - } - - @Test - fun `loaded post from local sone is visible`() { - val post = createPost(sone = localSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) - } - - @Test - fun `loaded post from followed remote sone that is not directed at local sone is visible`() { - localSone.friends.add(remoteSone.id) - val post = createPost(sone = remoteSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) - } - - @Test - fun `loaded post from remote sone that is directed at local sone is visible`() { - val post = createPost(sone = remoteSone, recipient = localSone) - assertThat(postVisibilityFilter.isPostVisible(localSone, post), equalTo(true)) - } - - @Test - fun `predicate will correctly recognize visible post`() { - val post = createPost(sone = localSone) - assertThat(postVisibilityFilter.isVisible(null).test(post), equalTo(true)) - } - - @Test - fun `predicate will correctly recognize not visible post`() { - val post = createPost(loaded = false) - assertThat(postVisibilityFilter.isVisible(null).test(post), equalTo(false)) - } - -} -- 2.7.4