🔀 Merge “work/next” into “next”
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 22 Feb 2020 11:33:24 +0000 (12:33 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sat, 22 Feb 2020 11:33:24 +0000 (12:33 +0100)
src/main/java/net/pterodactylus/sone/core/SoneInserter.java
src/main/java/net/pterodactylus/sone/data/Post.java
src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java
src/main/kotlin/net/pterodactylus/sone/data/Post.kt [new file with mode: 0644]
src/main/kotlin/net/pterodactylus/sone/fcp/AbstractSoneCommand.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt
src/test/kotlin/net/pterodactylus/sone/data/PostTest.kt [new file with mode: 0644]
src/test/kotlin/net/pterodactylus/sone/test/Mocks.kt

index 651b62e..fff3052 100644 (file)
@@ -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()));
index a4a794e..d4d34e6 100644 (file)
@@ -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<Post> NEWEST_FIRST = new Comparator<Post>() {
-
-               @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<Post> FUTURE_POSTS_FILTER = new Predicate<Post>() {
-
-               @Override
-               public boolean apply(Post post) {
-                       return (post != null) && (post.getTime() <= System.currentTimeMillis());
-               }
-
-       };
-
        //
        // ACCESSORS
        //
index 8f5a50f..df8832f 100644 (file)
@@ -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;
        }
 
index 821c198..d340426 100644 (file)
@@ -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<Post> sortedPosts = new ArrayList<>(allPosts);
-               Collections.sort(sortedPosts, Post.NEWEST_FIRST);
+               sortedPosts.sort(newestFirst());
 
                if (sortedPosts.size() < startPost) {
                        return new Response("PostFeed", encodePosts(Collections.<Post> 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 (file)
index 0000000..955196a
--- /dev/null
@@ -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<Post> = comparing(Post::getTime).reversed()
index 08e0593..f884e34 100644 (file)
@@ -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<Sone> {
-               val soneId = simpleFieldSet.get(parameterName)
+       protected fun SimpleFieldSet.getSone(parameterName: String, localOnly: Boolean, mandatory: Boolean): Optional<Sone> {
+               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) {
index 19eba1c..7534bb4 100644 (file)
@@ -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 (file)
index 0000000..ebf96b1
--- /dev/null
@@ -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))
+       }
+
+}
index 65f279a..17eae9a 100644 (file)
@@ -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
        }
 }