From: David ‘Bombe’ Roden Date: Mon, 26 Feb 2018 18:45:20 +0000 (+0100) Subject: Add Sone change collector X-Git-Tag: v79^2~134 X-Git-Url: https://git.pterodactylus.net/?p=Sone.git;a=commitdiff_plain;h=f58fae4e96152dfc940f543d7ba07db2233b3293;ds=sidebyside Add Sone change collector This wraps the Sone change detector in order to allow firing off events for new/removed items. --- diff --git a/src/main/kotlin/net/pterodactylus/sone/core/SoneChangeCollector.kt b/src/main/kotlin/net/pterodactylus/sone/core/SoneChangeCollector.kt new file mode 100644 index 0000000..609a55f --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/sone/core/SoneChangeCollector.kt @@ -0,0 +1,49 @@ +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 { + val events = mutableListOf() + 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/test/kotlin/net/pterodactylus/sone/core/SoneChangeCollectorTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/SoneChangeCollectorTest.kt new file mode 100644 index 0000000..3b82b49 --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/sone/core/SoneChangeCollectorTest.kt @@ -0,0 +1,78 @@ +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() + private val newSone = mock() + private val changeCollector = SoneChangeCollector(oldSone) + + @Test + fun `new posts are correctly turned into events`() { + val posts = listOf(mock(), mock(), mock()) + whenever(newSone.posts).thenReturn(posts) + changeCollector.newPostEvent { it.takeIf { it != posts[1] } } + assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder(posts[0], posts[2])) + } + + @Test + fun `actions can be performed on new post without being returned`() { + val posts = listOf(mock(), 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(), mock(), mock()) + whenever(oldSone.posts).thenReturn(posts) + changeCollector.removedPostEvent { it.takeIf { it != posts[1] } } + assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder(posts[0], posts[2])) + } + + @Test + fun `new post replies are correctly turned into events`() { + val postReplies = listOf(mock(), mock(), mock()) + whenever(newSone.replies).thenReturn(postReplies.toSet()) + changeCollector.newPostReplyEvent { it.takeIf { it != postReplies[1] } } + assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder(postReplies[0], postReplies[2])) + } + + @Test + fun `actions can be performed on new replies without being returned`() { + val replies = listOf(mock(), 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(), mock(), mock()) + whenever(oldSone.replies).thenReturn(postReplies.toSet()) + changeCollector.onRemovedPostReply { it.takeIf { it != postReplies[1] } } + assertThat(changeCollector.detectChanges(newSone), containsInAnyOrder(postReplies[0], postReplies[2])) + } + +}