From: David ‘Bombe’ Roden Date: Sat, 22 Feb 2020 11:33:24 +0000 (+0100) Subject: 🔀 Merge “work/next” into “next” X-Git-Tag: v82^2~51 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=195de4860177eebe9264b75736a5d34e46738b0d;hp=2ab560633c24940fa49bdaf569635dd673ad7b19 🔀 Merge “work/next” into “next” --- diff --git a/src/main/java/net/pterodactylus/sone/core/SoneInserter.java b/src/main/java/net/pterodactylus/sone/core/SoneInserter.java index 651b62e..fff3052 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.lang.System.currentTimeMillis; 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 class SoneInserter extends AbstractService { 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 a4a794e..d4d34e6 100644 --- a/src/main/java/net/pterodactylus/sone/data/Post.java +++ b/src/main/java/net/pterodactylus/sone/data/Post.java @@ -19,10 +19,7 @@ package net.pterodactylus.sone.data; import static com.google.common.base.Optional.absent; -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 @@ -30,26 +27,6 @@ import com.google.common.base.Predicate; */ 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())); - } - - }; - - /** 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/data/impl/SoneImpl.java b/src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java index 8f5a50f..df8832f 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 com.google.common.base.Preconditions.checkNotNull; 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 class SoneImpl implements Sone { 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 821c198..d340426 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java +++ b/src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java @@ -32,6 +32,9 @@ import com.google.common.collect.Collections2; import freenet.support.SimpleFieldSet; +import static net.pterodactylus.sone.data.PostKt.newestFirst; +import static net.pterodactylus.sone.data.PostKt.noFuturePost; + /** * Implementation of an FCP interface for other clients or plugins to * communicate with Sone. @@ -67,10 +70,10 @@ public class GetPostFeedCommand extends AbstractSoneCommand { 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); + 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 new file mode 100644 index 0000000..955196a --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/data/Post.kt @@ -0,0 +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/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt b/src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt index 08e0593..f884e34 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) { 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 19eba1c..7534bb4 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 0000000..ebf96b1 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt @@ -0,0 +1,49 @@ +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 + +/** + * 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)) + } + + @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)) + } + +} diff --git a/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt b/src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt index 65f279a..17eae9a 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 } }