From 329cfa999c0814d337900b5be19b5ff4f9ec8579 Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Sat, 18 Oct 2014 21:31:37 +0200 Subject: [PATCH] Add unit test for CTCP handler; refactor CTCP handler. --- .../pterodactylus/irc/connection/CtcpHandler.java | 102 ++++++++++-- .../irc/connection/CtcpHandlerTest.java | 181 +++++++++++++++++++++ .../net/pterodactylus/irc/connection/Replies.java | 19 ++- 3 files changed, 285 insertions(+), 17 deletions(-) create mode 100644 src/test/java/net/pterodactylus/irc/connection/CtcpHandlerTest.java diff --git a/src/main/java/net/pterodactylus/irc/connection/CtcpHandler.java b/src/main/java/net/pterodactylus/irc/connection/CtcpHandler.java index ff9757e..d5e07cf 100644 --- a/src/main/java/net/pterodactylus/irc/connection/CtcpHandler.java +++ b/src/main/java/net/pterodactylus/irc/connection/CtcpHandler.java @@ -66,26 +66,57 @@ public class CtcpHandler implements Handler { String ctcpCommand = messageWords[0]; if (ctcpCommand.equalsIgnoreCase("DCC")) { if (messageWords[1].equalsIgnoreCase("SEND")) { - Optional inetAddress = parseInetAddress(messageWords[3]); - Optional port = fromNullable(Ints.tryParse(messageWords[4])); - long fileSize = fromNullable(Longs.tryParse(messageWords[5])).or(-1L); - if (inetAddress.isPresent() && port.isPresent()) { - eventBus.post(new DccSendReceived(connection, client, messageWords[2], inetAddress.get(), port.get(), fileSize)); - } else { - logger.warn(format("Received malformed DCC SEND: “%s”", message)); - } + processDccSend(client, message, messageWords); } else if (messageWords[1].equalsIgnoreCase("ACCEPT")) { - Optional port = fromNullable(Ints.tryParse(messageWords[3])); - long position = (messageWords.length > 4) ? fromNullable(Longs.tryParse(messageWords[4])).or(-1L) : -1; - if (port.isPresent()) { - eventBus.post(new DccAcceptReceived(connection, client, messageWords[2], port.get(), position)); - } else { - logger.warn(format("Received malformed DCC ACCEPT: “%s”", message)); - } + processDccAccept(client, message, messageWords); } } } + private void processDccSend(Source client, String message, + String[] messageWords) { + Optional dccSendInformation = parseDccSendInformation(messageWords); + if (dccSendInformation.isPresent()) { + eventBus.post(new DccSendReceived(connection, client, + dccSendInformation.get().filename, + dccSendInformation.get().internetAddress, + dccSendInformation.get().port, + dccSendInformation.get().size)); + } else { + logger.warn(format("Received malformed DCC SEND: “%s”", message)); + } + } + + private Optional parseDccSendInformation(String[] messageWords) { + if (messageWords.length <5) { + return absent(); + } + Optional internetAddress = parseInetAddress(messageWords[3]); + Optional port = fromNullable(Ints.tryParse(messageWords[4])); + long fileSize = (messageWords.length > 5) ? fromNullable(Longs.tryParse(messageWords[5])).or(-1L) : -1; + if (!internetAddress.isPresent() || !port.isPresent()) { + return absent(); + } + return of(new DccSendInformation(messageWords[2], internetAddress.get(), port.get(), fileSize)); + } + + private static class DccSendInformation { + + private final String filename; + private final InetAddress internetAddress; + private final int port; + private final long size; + + private DccSendInformation(String filename, + InetAddress internetAddress, int port, long size) { + this.filename = filename; + this.internetAddress = internetAddress; + this.port = port; + this.size = size; + } + + } + private Optional parseInetAddress(String ip) { Long ipNumber = Longs.tryParse(ip); if (ipNumber == null) { @@ -104,4 +135,45 @@ public class CtcpHandler implements Handler { } } + private void processDccAccept(Source client, String message, + String[] messageWords) { + Optional dccAcceptInformation = parseDccAcceptInformation(messageWords); + if (dccAcceptInformation.isPresent()) { + eventBus.post(new DccAcceptReceived(connection, client, + dccAcceptInformation.get().filename, + dccAcceptInformation.get().port, + dccAcceptInformation.get().position)); + } else { + logger.warn(format("Received malformed DCC ACCEPT: “%s”", message)); + } + } + + private Optional parseDccAcceptInformation( + String[] messageWords) { + if (messageWords.length < 4) { + return absent(); + } + Optional port = fromNullable(Ints.tryParse(messageWords[3])); + long position = (messageWords.length > 4) ? fromNullable( + Longs.tryParse(messageWords[4])).or(-1L) : -1; + if (!port.isPresent()) { + return absent(); + } + return of(new DccAcceptInformation(messageWords[2], port.get(), position)); + } + + private static class DccAcceptInformation { + + private final String filename; + private final int port; + private final long position; + + private DccAcceptInformation(String filename, int port, long position) { + this.filename = filename; + this.port = port; + this.position = position; + } + + } + } diff --git a/src/test/java/net/pterodactylus/irc/connection/CtcpHandlerTest.java b/src/test/java/net/pterodactylus/irc/connection/CtcpHandlerTest.java new file mode 100644 index 0000000..e1463c9 --- /dev/null +++ b/src/test/java/net/pterodactylus/irc/connection/CtcpHandlerTest.java @@ -0,0 +1,181 @@ +package net.pterodactylus.irc.connection; + +import static net.pterodactylus.irc.Source.parseSource; +import static net.pterodactylus.irc.connection.Replies.createReply; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentCaptor.forClass; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import net.pterodactylus.irc.Connection; +import net.pterodactylus.irc.event.DccAcceptReceived; +import net.pterodactylus.irc.event.DccSendReceived; + +import com.google.common.eventbus.EventBus; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +/** + * Unit test for {@link CtcpHandler}. + * + * @author David ‘Bombe’ Roden + */ +public class CtcpHandlerTest { + + private final EventBus eventBus = mock(EventBus.class); + private final Connection connection = mock(Connection.class); + private final CtcpHandler handler = new CtcpHandler(eventBus, connection); + + @Test + public void handlerWillHandleCtcpMessagesSentAsNotice() { + assertThat(handler.willHandle(createReply("NOTICE", "\u0001ACTION\u0001")), is(true)); + } + + @Test + public void handlerWillHandleCtcpMessagesSentAsPrivmsg() { + assertThat(handler.willHandle(createReply("PRIVMSG", "\u0001ACTION\u0001")), is(true)); + } + + @Test + public void handlerWillNotHandleNormalMessages() { + assertThat(handler.willHandle(createReply("NOTICE", "ACTION")), is(false)); + } + + @Test + public void handlerWillNotHandleMalformedMessages() { + assertThat(handler.willHandle(createReply("NOTICE", "\u0001ACTION")), is(false)); + } + + @Test + public void dccSendCommandAsNoticeIsRecognizedCorrectly() { + dccSendCommandIsRecognizedCorrectly("NOTICE", "23456", 23456L); + } + + private void dccSendCommandIsRecognizedCorrectly(String command, + Object sizeAsString, long expectedSize) { + handler.handleReply(createReply(parseSource("User!user@host"), + command, + "\u0001DCC SEND test.dat 2130706433 12345 " + sizeAsString + "\u0001")); + ArgumentCaptor eventCaptor = forClass( + DccSendReceived.class); + verify(eventBus).post(eventCaptor.capture()); + DccSendReceived dccSendReceived = eventCaptor.getValue(); + assertThat(dccSendReceived.connection(), is(connection)); + assertThat(dccSendReceived.filename(), is("test.dat")); + assertThat(dccSendReceived.inetAddress().getHostAddress(), + is("127.0.0.1")); + assertThat(dccSendReceived.port(), is(12345)); + assertThat(dccSendReceived.filesize(), is(expectedSize)); + assertThat(dccSendReceived.source().nick().get(), is("User")); + } + + @Test + public void dccSendCommandAsPrivmsgIsRecognizedCorrectly() { + dccSendCommandIsRecognizedCorrectly("PRIVMSG", "23456", 23456L); + } + + @Test + public void nonDccCommandDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001FOO SEND test.dat 2130706433 12345 23456\u0001")); + verify(eventBus, never()).post(anyObject()); + } + + @Test + public void nonSendDccCommandDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC FOO test.dat 2130706433 12345 23456\u0001")); + verify(eventBus, never()).post(anyObject()); + } + + @Test + public void dccCommandWithoutPortNumberDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC SEND test.dat 2130706433\u0001")); + verify(eventBus, never()).post(anyObject()); + } + + @Test + public void dccCommandWithInvalidPortNumberDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC SEND test.dat 2130706433 abc\u0001")); + verify(eventBus, never()).post(anyObject()); + } + + @Test + public void dccCommandWithoutIpAddressDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC SEND test.dat\u0001")); + verify(eventBus, never()).post(anyObject()); + } + + @Test + public void dccCommandWithInvalidIpAddressDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC SEND test.dat abc abc\u0001")); + verify(eventBus, never()).post(anyObject()); + } + + @Test + public void dccCommandWithoutLengthIsRecognizedCorrectly() { + dccSendCommandIsRecognizedCorrectly("PRIVMSG", "", -1); + } + + @Test + public void dccAcceptCommandIsRecognized() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC ACCEPT test.dat 12345 23456\u0001")); + ArgumentCaptor eventCaptor = forClass( + DccAcceptReceived.class); + verify(eventBus).post(eventCaptor.capture()); + DccAcceptReceived dccAcceptReceived = eventCaptor.getValue(); + assertThat(dccAcceptReceived.connection(), is(connection)); + assertThat(dccAcceptReceived.filename(), is("test.dat")); + assertThat(dccAcceptReceived.port(), is(12345)); + assertThat(dccAcceptReceived.position(), is(23456L)); + assertThat(dccAcceptReceived.source().nick().get(), is("User")); + } + + @Test + public void dccAcceptCommandWithoutParametersDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC ACCEPT\u0001")); + verify(eventBus,never()).post(anyObject()); + } + + @Test + public void dccAcceptCommandWithInvalidPortDoesNotCauseEvent() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC ACCEPT test.dat abc\u0001")); + verify(eventBus,never()).post(anyObject()); + } + + @Test + public void dccAcceptCommandWithMissingPositionIsRecognized() { + handler.handleReply(createReply(parseSource("User!user@host"), + "NOTICE", + "\u0001DCC ACCEPT test.dat 12345\u0001")); + ArgumentCaptor eventCaptor = forClass( + DccAcceptReceived.class); + verify(eventBus).post(eventCaptor.capture()); + DccAcceptReceived dccAcceptReceived = eventCaptor.getValue(); + assertThat(dccAcceptReceived.connection(), is(connection)); + assertThat(dccAcceptReceived.filename(), is("test.dat")); + assertThat(dccAcceptReceived.port(), is(12345)); + assertThat(dccAcceptReceived.position(), is(-1L)); + assertThat(dccAcceptReceived.source().nick().get(), is("User")); + } + +} diff --git a/src/test/java/net/pterodactylus/irc/connection/Replies.java b/src/test/java/net/pterodactylus/irc/connection/Replies.java index 214c3d4..841d3af 100644 --- a/src/test/java/net/pterodactylus/irc/connection/Replies.java +++ b/src/test/java/net/pterodactylus/irc/connection/Replies.java @@ -1,10 +1,15 @@ package net.pterodactylus.irc.connection; +import static com.google.common.base.Optional.fromNullable; import static java.util.Arrays.asList; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.ArrayList; +import java.util.List; + import net.pterodactylus.irc.Reply; +import net.pterodactylus.irc.Source; /** * Helper class to mock {@link Reply}s for testing {@link Handler}s. @@ -13,10 +18,20 @@ import net.pterodactylus.irc.Reply; */ public class Replies { - public static Reply createReply(String command, String channel) { + public static Reply createReply(Source source, String command, + String... parameters) { + Reply reply = createReply(command, parameters); + when(reply.source()).thenReturn(fromNullable(source)); + return reply; + } + + public static Reply createReply(String command, String... parameters) { final Reply reply = mock(Reply.class); when(reply.command()).thenReturn(command); - when(reply.parameters()).thenReturn(asList(":some.server", channel)); + List allParameters = new ArrayList<>(); + allParameters.add(":some.server"); + allParameters.addAll(asList(parameters)); + when(reply.parameters()).thenReturn(allParameters); return reply; } -- 2.7.4