Replace Sone change detector with simple comparison helper
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 22 Feb 2019 20:07:16 +0000 (21:07 +0100)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Fri, 22 Feb 2019 21:16:16 +0000 (22:16 +0100)
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/SoneChangeDetector.java [deleted file]
src/main/kotlin/net/pterodactylus/sone/core/SoneChangeCollector.kt [deleted file]
src/main/kotlin/net/pterodactylus/sone/core/SoneComparison.kt [new file with mode: 0644]
src/main/kotlin/net/pterodactylus/sone/core/UpdatedSoneProcessor.kt
src/test/java/net/pterodactylus/sone/core/SoneChangeDetectorTest.java [deleted file]
src/test/kotlin/net/pterodactylus/sone/core/SoneChangeCollectorTest.kt [deleted file]
src/test/kotlin/net/pterodactylus/sone/core/SoneComparisonTest.kt [new file with mode: 0644]

index 4319d8b..8e20176 100644 (file)
@@ -48,8 +48,6 @@ import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidImageFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidParentAlbumFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostFound;
 import net.pterodactylus.sone.core.ConfigurationSoneParser.InvalidPostReplyFound;
-import net.pterodactylus.sone.core.SoneChangeDetector.PostProcessor;
-import net.pterodactylus.sone.core.SoneChangeDetector.PostReplyProcessor;
 import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent;
 import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
 import net.pterodactylus.sone.core.event.MarkPostKnownEvent;
 import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent;
 import net.pterodactylus.sone.core.event.InsertionDelayChangedEvent;
 import net.pterodactylus.sone.core.event.MarkPostKnownEvent;
@@ -851,48 +849,33 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                }
        }
 
                }
        }
 
-       private List<Object> collectEventsForChangesInSone(Sone oldSone,
-                       final Sone newSone) {
-               final List<Object> events = new ArrayList<Object>();
-               SoneChangeDetector soneChangeDetector = new SoneChangeDetector(
-                               oldSone);
-               soneChangeDetector.onNewPosts(new PostProcessor() {
-                       @Override
-                       public void processPost(Post post) {
-                               if (post.getSone().equals(newSone)) {
-                                       post.setKnown(true);
-                               } else if (post.getTime() < database.getFollowingTime(newSone.getId())) {
-                                       post.setKnown(true);
-                               } else if (!post.isKnown()) {
-                                       events.add(new NewPostFoundEvent(post));
-                               }
-                       }
-               });
-               soneChangeDetector.onRemovedPosts(new PostProcessor() {
-                       @Override
-                       public void processPost(Post post) {
-                               events.add(new PostRemovedEvent(post));
-                       }
-               });
-               soneChangeDetector.onNewPostReplies(new PostReplyProcessor() {
-                       @Override
-                       public void processPostReply(PostReply postReply) {
-                               if (postReply.getSone().equals(newSone)) {
-                                       postReply.setKnown(true);
-                               } else if (postReply.getTime() < database.getFollowingTime(newSone.getId())) {
-                                       postReply.setKnown(true);
-                               } else if (!postReply.isKnown()) {
-                                       events.add(new NewPostReplyFoundEvent(postReply));
-                               }
+       private List<Object> collectEventsForChangesInSone(Sone oldSone, Sone newSone) {
+               List<Object> events = new ArrayList<>();
+               SoneComparison soneComparison = new SoneComparison(oldSone, newSone);
+               for (Post newPost : soneComparison.getNewPosts()) {
+                       if (newPost.getSone().equals(newSone)) {
+                               newPost.setKnown(true);
+                       } else if (newPost.getTime() < database.getFollowingTime(newSone.getId())) {
+                               newPost.setKnown(true);
+                       } else if (!newPost.isKnown()) {
+                               events.add(new NewPostFoundEvent(newPost));
                        }
                        }
-               });
-               soneChangeDetector.onRemovedPostReplies(new PostReplyProcessor() {
-                       @Override
-                       public void processPostReply(PostReply postReply) {
-                               events.add(new PostReplyRemovedEvent(postReply));
+               }
+               for (Post post : soneComparison.getRemovedPosts()) {
+                       events.add(new PostRemovedEvent(post));
+               }
+               for (PostReply postReply : soneComparison.getNewPostReplies()) {
+                       if (postReply.getSone().equals(newSone)) {
+                               postReply.setKnown(true);
+                       } else if (postReply.getTime() < database.getFollowingTime(newSone.getId())) {
+                               postReply.setKnown(true);
+                       } else if (!postReply.isKnown()) {
+                               events.add(new NewPostReplyFoundEvent(postReply));
                        }
                        }
-               });
-               soneChangeDetector.detectChanges(newSone);
+               }
+               for (PostReply postReply : soneComparison.getRemovedPostReplies()) {
+                       events.add(new PostReplyRemovedEvent(postReply));
+               }
                return events;
        }
 
                return events;
        }
 
diff --git a/src/main/java/net/pterodactylus/sone/core/SoneChangeDetector.java b/src/main/java/net/pterodactylus/sone/core/SoneChangeDetector.java
deleted file mode 100644 (file)
index 7dd882d..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-package net.pterodactylus.sone.core;
-
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.fromNullable;
-import static com.google.common.collect.FluentIterable.from;
-
-import java.util.Collection;
-
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-
-/**
- * Compares the contents of two {@link Sone}s and fires events for new and
- * removed elements.
- */
-public class SoneChangeDetector {
-
-       private final Sone oldSone;
-       private Optional<PostProcessor> newPostProcessor = absent();
-       private Optional<PostProcessor> removedPostProcessor = absent();
-       private Optional<PostReplyProcessor> newPostReplyProcessor = absent();
-       private Optional<PostReplyProcessor> removedPostReplyProcessor = absent();
-
-       public SoneChangeDetector(Sone oldSone) {
-               this.oldSone = oldSone;
-       }
-
-       public void onNewPosts(PostProcessor newPostProcessor) {
-               this.newPostProcessor = fromNullable(newPostProcessor);
-       }
-
-       public void onRemovedPosts(PostProcessor removedPostProcessor) {
-               this.removedPostProcessor = fromNullable(removedPostProcessor);
-       }
-
-       public void onNewPostReplies(PostReplyProcessor newPostReplyProcessor) {
-               this.newPostReplyProcessor = fromNullable(newPostReplyProcessor);
-       }
-
-       public void onRemovedPostReplies(
-                       PostReplyProcessor removedPostReplyProcessor) {
-               this.removedPostReplyProcessor = fromNullable(removedPostReplyProcessor);
-       }
-
-       public void detectChanges(Sone newSone) {
-               processPosts(from(newSone.getPosts()).filter(
-                               notContainedIn(oldSone.getPosts())), newPostProcessor);
-               processPosts(from(oldSone.getPosts()).filter(
-                               notContainedIn(newSone.getPosts())), removedPostProcessor);
-               processPostReplies(from(newSone.getReplies()).filter(
-                               notContainedIn(oldSone.getReplies())), newPostReplyProcessor);
-               processPostReplies(from(oldSone.getReplies()).filter(
-                               notContainedIn(newSone.getReplies())), removedPostReplyProcessor);
-       }
-
-       private void processPostReplies(FluentIterable<PostReply> postReplies,
-                       Optional<PostReplyProcessor> postReplyProcessor) {
-               for (PostReply postReply : postReplies) {
-                       notifyPostReplyProcessor(postReplyProcessor, postReply);
-               }
-       }
-
-       private void notifyPostReplyProcessor(
-                       Optional<PostReplyProcessor> postReplyProcessor,
-                       PostReply postReply) {
-               if (postReplyProcessor.isPresent()) {
-                       postReplyProcessor.get()
-                                       .processPostReply(postReply);
-               }
-       }
-
-       private void processPosts(FluentIterable<Post> posts,
-                       Optional<PostProcessor> newPostProcessor) {
-               for (Post post : posts) {
-                       notifyPostProcessor(newPostProcessor, post);
-               }
-       }
-
-       private void notifyPostProcessor(Optional<PostProcessor> postProcessor,
-                       Post newPost) {
-               if (postProcessor.isPresent()) {
-                       postProcessor.get().processPost(newPost);
-               }
-       }
-
-       private <T> Predicate<T> notContainedIn(final Collection<T> posts) {
-               return new Predicate<T>() {
-                       @Override
-                       public boolean apply(T element) {
-                               return !posts.contains(element);
-                       }
-               };
-       }
-
-       public interface PostProcessor {
-
-               void processPost(Post post);
-
-       }
-
-       public interface PostReplyProcessor {
-
-               void processPostReply(PostReply postReply);
-
-       }
-
-}
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/SoneChangeCollector.kt b/src/main/kotlin/net/pterodactylus/sone/core/SoneChangeCollector.kt
deleted file mode 100644 (file)
index 609a55f..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-package net.pterodactylus.sone.core
-
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-
-/**
- * Wrapper around a [SoneChangeDetector] that can turn changed elements into
- * different elements which are then being returned. This can be used to turn
- * changed elements into events for further processing.
- */
-class SoneChangeCollector(private val oldSone: Sone) {
-
-       private val newPostEventCreators = mutableListOf<(Post) -> Any?>()
-       private val removedPostEventCreators = mutableListOf<(Post) -> Any?>()
-       private val newPostReplyEventCreators = mutableListOf<(PostReply) -> Any?>()
-       private val removedPostReplyEventCreators = mutableListOf<(PostReply) -> Any?>()
-
-       fun onNewPost(postProcessor: (Post) -> Unit) =
-                       newPostEventCreators.add { postProcessor(it).let { null } }.let { this }
-
-       fun newPostEvent(postEventCreator: (Post) -> Any?) =
-                       newPostEventCreators.add(postEventCreator).let { this }
-
-       fun onNewPostReply(postReplyProcessor: (PostReply) -> Unit) =
-                       newPostReplyEventCreators.add { postReplyProcessor(it).let { null } }.let { this }
-
-       fun newPostReplyEvent(postReplyEventCreator: (PostReply) -> Any?) =
-                       newPostReplyEventCreators.add(postReplyEventCreator).let { this }
-
-       fun removedPostEvent(postEventCreator: (Post) -> Any?) =
-                       removedPostEventCreators.add(postEventCreator).let { this }
-
-       fun onRemovedPostReply(postReplyEventCreator: (PostReply) -> Any?) =
-                       removedPostReplyEventCreators.add(postReplyEventCreator).let { this }
-
-       fun detectChanges(newSone: Sone): List<Any> {
-               val events = mutableListOf<Any>()
-               SoneChangeDetector(oldSone).apply {
-                       onNewPosts { post -> newPostEventCreators.mapNotNull { it(post) }.forEach { events.add(it) } }
-                       onRemovedPosts { post -> removedPostEventCreators.mapNotNull { it(post) }.forEach { events.add(it) } }
-                       onNewPostReplies { reply -> newPostReplyEventCreators.mapNotNull { it(reply) }.forEach { events.add(it) } }
-                       onRemovedPostReplies { reply -> removedPostReplyEventCreators.mapNotNull { it(reply) }.forEach { events.add(it) } }
-                       detectChanges(newSone)
-               }
-               return events
-       }
-
-}
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/SoneComparison.kt b/src/main/kotlin/net/pterodactylus/sone/core/SoneComparison.kt
new file mode 100644 (file)
index 0000000..9a67f80
--- /dev/null
@@ -0,0 +1,12 @@
+package net.pterodactylus.sone.core
+
+import net.pterodactylus.sone.data.*
+
+class SoneComparison(private val oldSone: Sone, private val newSone: Sone) {
+
+       val newPosts: Collection<Post> get() = newSone.posts - oldSone.posts
+       val removedPosts: Collection<Post> get() = oldSone.posts - newSone.posts
+       val newPostReplies: Collection<PostReply> get() = newSone.replies - oldSone.replies
+       val removedPostReplies: Collection<PostReply> get() = oldSone.replies - newSone.replies
+
+}
index 580f227..ebaa3c8 100644 (file)
@@ -1,16 +1,13 @@
 package net.pterodactylus.sone.core
 
 package net.pterodactylus.sone.core
 
-import com.google.common.eventbus.EventBus
-import com.google.inject.ImplementedBy
-import net.pterodactylus.sone.core.event.NewPostFoundEvent
-import net.pterodactylus.sone.core.event.NewPostReplyFoundEvent
-import net.pterodactylus.sone.core.event.PostRemovedEvent
-import net.pterodactylus.sone.core.event.PostReplyRemovedEvent
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.data.Sone.SoneStatus
-import net.pterodactylus.sone.database.Database
-import net.pterodactylus.sone.utils.ifFalse
-import net.pterodactylus.util.logging.Logging
+import com.google.common.eventbus.*
+import com.google.inject.*
+import net.pterodactylus.sone.core.event.*
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.data.Sone.*
+import net.pterodactylus.sone.database.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.util.logging.*
 import javax.inject.Inject
 
 /**
 import javax.inject.Inject
 
 /**
@@ -35,9 +32,23 @@ abstract class BasicUpdateSoneProcessor(private val database: Database, private
                        logger.fine("Downloaded Sone $sone can not update stored Sone $storedSone.")
                        return
                }
                        logger.fine("Downloaded Sone $sone can not update stored Sone $storedSone.")
                        return
                }
-               collectEventsForChanges(storedSone, sone)
-                               .also { database.storeSone(sone) }
-                               .forEach(eventBus::post)
+
+               SoneComparison(storedSone, sone).apply {
+                       newPosts
+                                       .onEach { post -> if (post.time <= sone.followingTime) post.isKnown = true }
+                                       .mapNotNull { post -> post.isKnown.ifFalse { NewPostFoundEvent(post) } }
+                                       .forEach(eventBus::post)
+                       removedPosts
+                                       .map { PostRemovedEvent(it) }
+                                       .forEach(eventBus::post)
+                       newPostReplies
+                                       .onEach { postReply -> if (postReply.time <= sone.followingTime) postReply.isKnown = true }
+                                       .mapNotNull { postReply -> postReply.isKnown.ifFalse { NewPostReplyFoundEvent(postReply) } }
+                                       .forEach(eventBus::post)
+                       removedPostReplies
+                                       .map { PostReplyRemovedEvent(it) }
+                                       .forEach(eventBus::post)
+               }
                sone.options = storedSone.options
                sone.isKnown = storedSone.isKnown
                sone.status = if (sone.time != 0L) SoneStatus.idle else SoneStatus.unknown
                sone.options = storedSone.options
                sone.isKnown = storedSone.isKnown
                sone.status = if (sone.time != 0L) SoneStatus.idle else SoneStatus.unknown
@@ -47,16 +58,6 @@ abstract class BasicUpdateSoneProcessor(private val database: Database, private
 
        private val Sone.followingTime get() = database.getFollowingTime(id) ?: 0
 
 
        private val Sone.followingTime get() = database.getFollowingTime(id) ?: 0
 
-       private fun collectEventsForChanges(oldSone: Sone, newSone: Sone): List<Any> =
-                       SoneChangeCollector(oldSone)
-                                       .onNewPost { post -> if (post.time <= newSone.followingTime) post.isKnown = true }
-                                       .newPostEvent { post -> post.isKnown.ifFalse { NewPostFoundEvent(post) } }
-                                       .removedPostEvent { PostRemovedEvent(it) }
-                                       .onNewPostReply { postReply -> if (postReply.time <= newSone.followingTime) postReply.isKnown = true }
-                                       .newPostReplyEvent { postReply -> postReply.isKnown.ifFalse { NewPostReplyFoundEvent(postReply) } }
-                                       .onRemovedPostReply { PostReplyRemovedEvent(it) }
-                                       .detectChanges(newSone)
-
 }
 
 class DefaultUpdateSoneProcessor @Inject constructor(database: Database, eventBus: EventBus) :
 }
 
 class DefaultUpdateSoneProcessor @Inject constructor(database: Database, eventBus: EventBus) :
diff --git a/src/test/java/net/pterodactylus/sone/core/SoneChangeDetectorTest.java b/src/test/java/net/pterodactylus/sone/core/SoneChangeDetectorTest.java
deleted file mode 100644 (file)
index eaa9637..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-package net.pterodactylus.sone.core;
-
-import static java.util.Arrays.asList;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.HashSet;
-
-import net.pterodactylus.sone.core.SoneChangeDetector.PostProcessor;
-import net.pterodactylus.sone.core.SoneChangeDetector.PostReplyProcessor;
-import net.pterodactylus.sone.data.Post;
-import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Unit test for {@link SoneChangeDetector}.
- */
-public class SoneChangeDetectorTest {
-
-       private final Sone oldSone = mock(Sone.class);
-       private final Sone newSone = mock(Sone.class);
-       private final SoneChangeDetector soneChangeDetector =
-                       new SoneChangeDetector(oldSone);
-       private final Post oldPost = mock(Post.class);
-       private final Post removedPost = mock(Post.class);
-       private final Post newPost = mock(Post.class);
-       private final PostProcessor newPostProcessor = mock(PostProcessor.class);
-       private final PostProcessor removedPostProcessor =
-                       mock(PostProcessor.class);
-       private final PostReply oldPostReply = mock(PostReply.class);
-       private final PostReply removedPostReply = mock(PostReply.class);
-       private final PostReply newPostReply = mock(PostReply.class);
-       private final PostReplyProcessor newPostReplyProcessor =
-                       mock(PostReplyProcessor.class);
-       private final PostReplyProcessor removedPostReplyProcessor =
-                       mock(PostReplyProcessor.class);
-
-       @Before
-       public void setupPosts() {
-               when(oldSone.getPosts()).thenReturn(asList(oldPost, removedPost));
-               when(newSone.getPosts()).thenReturn(asList(oldPost, newPost));
-       }
-
-       @Before
-       public void setupPostProcessors() {
-               soneChangeDetector.onNewPosts(newPostProcessor);
-               soneChangeDetector.onRemovedPosts(removedPostProcessor);
-       }
-
-       @Before
-       public void setupPostReplies() {
-               when(oldSone.getReplies()).thenReturn(
-                               new HashSet<PostReply>(
-                                               asList(oldPostReply, removedPostReply)));
-               when(newSone.getReplies()).thenReturn(
-                               new HashSet<PostReply>(asList(oldPostReply, newPostReply)));
-       }
-
-       @Before
-       public void setupPostReplyProcessors() {
-               soneChangeDetector.onNewPostReplies(newPostReplyProcessor);
-               soneChangeDetector.onRemovedPostReplies(removedPostReplyProcessor);
-       }
-
-       @Test
-       public void changeDetectorDetectsChanges() {
-               soneChangeDetector.detectChanges(newSone);
-
-               verify(newPostProcessor).processPost(newPost);
-               verify(newPostProcessor, never()).processPost(oldPost);
-               verify(newPostProcessor, never()).processPost(removedPost);
-               verify(removedPostProcessor).processPost(removedPost);
-               verify(removedPostProcessor, never()).processPost(oldPost);
-               verify(removedPostProcessor, never()).processPost(newPost);
-
-               verify(newPostReplyProcessor).processPostReply(newPostReply);
-               verify(newPostReplyProcessor, never()).processPostReply(oldPostReply);
-               verify(newPostReplyProcessor, never()).processPostReply(
-                               removedPostReply);
-               verify(removedPostReplyProcessor).processPostReply(removedPostReply);
-               verify(removedPostReplyProcessor, never()).processPostReply(
-                               oldPostReply);
-               verify(removedPostReplyProcessor, never()).processPostReply(
-                               newPostReply);
-       }
-
-       @Test
-       public void changeDetectorDoesNotNotifyAnyProcessorIfProcessorsUnset() {
-           soneChangeDetector.onNewPosts(null);
-           soneChangeDetector.onRemovedPosts(null);
-           soneChangeDetector.onNewPostReplies(null);
-           soneChangeDetector.onRemovedPostReplies(null);
-               soneChangeDetector.detectChanges(newSone);
-               verify(newPostProcessor, never()).processPost(any(Post.class));
-               verify(removedPostProcessor, never()).processPost(any(Post.class));
-               verify(newPostReplyProcessor, never()).processPostReply(any(PostReply.class));
-               verify(removedPostReplyProcessor, never()).processPostReply(any(PostReply.class));
-       }
-
-}
diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneChangeCollectorTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneChangeCollectorTest.kt
deleted file mode 100644 (file)
index 3b82b49..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-package net.pterodactylus.sone.core
-
-import net.pterodactylus.sone.data.Post
-import net.pterodactylus.sone.data.PostReply
-import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.containsInAnyOrder
-import org.hamcrest.Matchers.emptyIterable
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
-import java.util.concurrent.atomic.AtomicInteger
-
-/**
- * Unit test for [SoneChangeCollectorTest].
- */
-class SoneChangeCollectorTest {
-
-       private val oldSone = mock<Sone>()
-       private val newSone = mock<Sone>()
-       private val changeCollector = SoneChangeCollector(oldSone)
-
-       @Test
-       fun `new posts are correctly turned into events`() {
-               val posts = listOf(mock<Post>(), mock(), mock())
-               whenever(newSone.posts).thenReturn(posts)
-               changeCollector.newPostEvent { it.takeIf { it != posts[1] } }
-               assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder<Any>(posts[0], posts[2]))
-       }
-
-       @Test
-       fun `actions can be performed on new post without being returned`() {
-               val posts = listOf(mock<Post>(), mock(), mock())
-               val counter = AtomicInteger(0)
-               whenever(newSone.posts).thenReturn(posts.slice(0..2))
-               whenever(oldSone.posts).thenReturn(posts.slice(2..2))
-               changeCollector.onNewPost { counter.incrementAndGet() }
-               assertThat(changeCollector.detectChanges(newSone), emptyIterable())
-               assertThat(counter.get(), equalTo(2))
-       }
-
-       @Test
-       fun `removed posts are correctly turned into events`() {
-               val posts = listOf(mock<Post>(), mock(), mock())
-               whenever(oldSone.posts).thenReturn(posts)
-               changeCollector.removedPostEvent { it.takeIf { it != posts[1] } }
-               assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder<Any>(posts[0], posts[2]))
-       }
-
-       @Test
-       fun `new post replies are correctly turned into events`() {
-               val postReplies = listOf(mock<PostReply>(), mock(), mock())
-               whenever(newSone.replies).thenReturn(postReplies.toSet())
-               changeCollector.newPostReplyEvent { it.takeIf { it != postReplies[1] } }
-               assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder<Any>(postReplies[0], postReplies[2]))
-       }
-
-       @Test
-       fun `actions can be performed on new replies without being returned`() {
-               val replies = listOf(mock<PostReply>(), mock(), mock())
-               val counter = AtomicInteger(0)
-               whenever(newSone.replies).thenReturn(replies.slice(0..2).toSet())
-               whenever(oldSone.replies).thenReturn(replies.slice(2..2).toSet())
-               changeCollector.onNewPostReply { counter.incrementAndGet() }
-               assertThat(changeCollector.detectChanges(newSone), emptyIterable())
-               assertThat(counter.get(), equalTo(2))
-       }
-
-       @Test
-       fun `removed post replies are correctly turned into events`() {
-               val postReplies = listOf(mock<PostReply>(), mock(), mock())
-               whenever(oldSone.replies).thenReturn(postReplies.toSet())
-               changeCollector.onRemovedPostReply { it.takeIf { it != postReplies[1] } }
-               assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder<Any>(postReplies[0], postReplies[2]))
-       }
-
-}
diff --git a/src/test/kotlin/net/pterodactylus/sone/core/SoneComparisonTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneComparisonTest.kt
new file mode 100644 (file)
index 0000000..f79d736
--- /dev/null
@@ -0,0 +1,50 @@
+package net.pterodactylus.sone.core
+
+import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+class SoneComparsisonTest {
+
+       private val oldSone = mock<Sone>()
+       private val newSone = mock<Sone>()
+
+       private val oldPost = mock<Post>()
+       private val removedPost = mock<Post>()
+       private val newPost = mock<Post>()
+       private val oldPostReply = mock<PostReply>()
+       private val removedPostReply = mock<PostReply>()
+       private val newPostReply = mock<PostReply>()
+
+       init {
+               whenever(oldSone.posts).thenReturn(listOf(oldPost, removedPost))
+               whenever(newSone.posts).thenReturn(listOf(oldPost, newPost))
+               whenever(oldSone.replies).thenReturn(setOf(oldPostReply, removedPostReply))
+               whenever(newSone.replies).thenReturn(setOf(oldPostReply, newPostReply))
+       }
+
+       private val soneComparison = SoneComparison(oldSone, newSone)
+
+       @Test
+       fun `new posts are identified correctly`() {
+               assertThat(soneComparison.newPosts, contains(newPost))
+       }
+
+       @Test
+       fun `removed posts are identified correctly`() {
+               assertThat(soneComparison.removedPosts, contains(removedPost))
+       }
+
+       @Test
+       fun `new post replies are identified correctly`() {
+               assertThat(soneComparison.newPostReplies, contains(newPostReply))
+       }
+
+       @Test
+       fun `removed post replies are identified correctly`() {
+               assertThat(soneComparison.removedPostReplies, contains(removedPostReply))
+       }
+
+}