Browse Source

Tag parsing support

tags/v0.1.0
Chris Smith 5 years ago
parent
commit
b0941e1047
26 changed files with 166 additions and 72 deletions
  1. 4
    4
      build.gradle.kts
  2. 3
    2
      src/main/kotlin/com/dmdirc/ktirc/io/LineBufferedSocket.kt
  3. 1
    0
      src/main/kotlin/com/dmdirc/ktirc/io/MessageHandler.kt
  4. 58
    5
      src/main/kotlin/com/dmdirc/ktirc/io/MessageParser.kt
  5. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/CapabilityProcessor.kt
  6. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/ISupportProcessor.kt
  7. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/JoinProcessor.kt
  8. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/MessageProcessor.kt
  9. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/NamesProcessor.kt
  10. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/PartProcessor.kt
  11. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/PingProcessor.kt
  12. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessor.kt
  13. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/QuitProcessor.kt
  14. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/WelcomeProcessor.kt
  15. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/model/CapabilitiesState.kt
  16. 12
    0
      src/main/kotlin/com/dmdirc/ktirc/model/IrcMessage.kt
  17. 6
    5
      src/test/kotlin/com/dmdirc/ktirc/io/MessageHandlerTest.kt
  18. 37
    11
      src/test/kotlin/com/dmdirc/ktirc/io/MessageParserTest.kt
  19. 8
    8
      src/test/kotlin/com/dmdirc/ktirc/messages/CapabilityProcessorTest.kt
  20. 7
    7
      src/test/kotlin/com/dmdirc/ktirc/messages/ISupportProcessorTest.kt
  21. 3
    3
      src/test/kotlin/com/dmdirc/ktirc/messages/JoinProcessorTest.kt
  22. 3
    3
      src/test/kotlin/com/dmdirc/ktirc/messages/NamesProcessorTest.kt
  23. 4
    4
      src/test/kotlin/com/dmdirc/ktirc/messages/PartProcessorTest.kt
  24. 3
    3
      src/test/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessorTest.kt
  25. 4
    4
      src/test/kotlin/com/dmdirc/ktirc/messages/QuitProcessorTest.kt
  26. 2
    2
      src/test/kotlin/com/dmdirc/ktirc/messages/WelcomeProcessorTest.kt

+ 4
- 4
build.gradle.kts View File

@@ -1,7 +1,7 @@
1 1
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
2 2
 
3 3
 plugins {
4
-    kotlin("jvm") version "1.3.0-rc-80"
4
+    kotlin("jvm") version "1.3.0-rc-190"
5 5
 }
6 6
 
7 7
 repositories {
@@ -13,8 +13,8 @@ repositories {
13 13
 
14 14
 dependencies {
15 15
     implementation(kotlin("stdlib-jdk8"))
16
-    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.30.0-eap13")
17
-    implementation("io.ktor:ktor-network:0.9.6-alpha-1-rc13")
16
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:0.30.2-eap13")
17
+    implementation("io.ktor:ktor-network:1.0.0-beta-1")
18 18
 
19 19
     testCompile("org.junit.jupiter:junit-jupiter-api:5.3.1")
20 20
     testCompile("org.junit.jupiter:junit-jupiter-params:5.3.1")
@@ -43,7 +43,7 @@ tasks.withType<Test> {
43 43
 configurations.all {
44 44
     resolutionStrategy.eachDependency {
45 45
         if (requested.group == "org.jetbrains.kotlin") {
46
-            useVersion("1.3.0-rc-80")
46
+            useVersion("1.3.0-rc-190")
47 47
         }
48 48
     }
49 49
 }

+ 3
- 2
src/main/kotlin/com/dmdirc/ktirc/io/LineBufferedSocket.kt View File

@@ -6,8 +6,8 @@ import io.ktor.network.sockets.Socket
6 6
 import io.ktor.network.sockets.aSocket
7 7
 import io.ktor.network.sockets.openReadChannel
8 8
 import io.ktor.network.sockets.openWriteChannel
9
-import io.ktor.network.util.ioCoroutineDispatcher
10 9
 import kotlinx.coroutines.CoroutineScope
10
+import kotlinx.coroutines.Dispatchers
11 11
 import kotlinx.coroutines.channels.ReceiveChannel
12 12
 import kotlinx.coroutines.channels.produce
13 13
 import kotlinx.coroutines.io.ByteReadChannel
@@ -43,9 +43,10 @@ class KtorLineBufferedSocket(private val host: String, private val port: Int): L
43 43
     private lateinit var readChannel: ByteReadChannel
44 44
     private lateinit var writeChannel: ByteWriteChannel
45 45
 
46
+    @Suppress("EXPERIMENTAL_API_USAGE")
46 47
     override suspend fun connect() {
47 48
         log.info { "Connecting..." }
48
-        socket = aSocket(ActorSelectorManager(ioCoroutineDispatcher)).tcp().connect(InetSocketAddress(host, port))
49
+        socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(InetSocketAddress(host, port))
49 50
         readChannel = socket.openReadChannel()
50 51
         writeChannel = socket.openWriteChannel()
51 52
     }

+ 1
- 0
src/main/kotlin/com/dmdirc/ktirc/io/MessageHandler.kt View File

@@ -3,6 +3,7 @@ package com.dmdirc.ktirc.io
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.events.EventHandler
5 5
 import com.dmdirc.ktirc.messages.MessageProcessor
6
+import com.dmdirc.ktirc.model.IrcMessage
6 7
 import com.dmdirc.ktirc.util.logger
7 8
 import kotlinx.coroutines.channels.ReceiveChannel
8 9
 import kotlinx.coroutines.channels.consumeEach

+ 58
- 5
src/main/kotlin/com/dmdirc/ktirc/io/MessageParser.kt View File

@@ -1,5 +1,9 @@
1 1
 package com.dmdirc.ktirc.io
2 2
 
3
+import com.dmdirc.ktirc.model.IrcMessage
4
+import com.dmdirc.ktirc.model.messageTags
5
+import com.dmdirc.ktirc.util.logger
6
+
3 7
 /**
4 8
  * Parses a message received from an IRC server.
5 9
  *
@@ -30,14 +34,16 @@ class MessageParser {
30 34
         private const val COLON = ':'.toByte()
31 35
     }
32 36
 
37
+    private val log by logger()
38
+
33 39
     fun parse(message: ByteArray) = CursorByteArray(message).run {
34
-        IrcMessage(takeTags(), takePrefix(), String(takeWord()), takeParams().toList())
40
+        IrcMessage(takeTags(), takePrefix(), String(takeWord()), takeParams())
35 41
     }
36 42
 
37 43
     /**
38 44
      * Attempts to read IRCv3 tags from the message.
39 45
      */
40
-    private fun CursorByteArray.takeTags() = takeOptionalPrefixedSection(AT)
46
+    private fun CursorByteArray.takeTags() = takeOptionalPrefixedSection(AT).toTagMap()
41 47
 
42 48
     /**
43 49
      * Attempts to read a prefix from the message.
@@ -60,17 +66,64 @@ class MessageParser {
60 66
         while (!exhausted()) {
61 67
             yield(takeParam())
62 68
         }
63
-    }
69
+    }.toList()
64 70
 
71
+    /**
72
+     * If the next word starts with the given prefix, takes and returns it, otherwise returns null.
73
+     */
65 74
     private fun CursorByteArray.takeOptionalPrefixedSection(prefix: Byte) = when {
66 75
         exhausted() -> null
67 76
         peek() == prefix -> takeWord(skip = 1)
68 77
         else -> null
69 78
     }
70 79
 
71
-}
80
+    /**
81
+     * Parses the bytes as a list of message tags. Unknown tags are discarded.
82
+     */
83
+    private fun ByteArray?.toTagMap() = sequence {
84
+        forEachPart(';') { tag ->
85
+            val index = tag.indexOf('=')
86
+            val name = if (index == -1) tag else tag.substring(0 until index)
87
+            messageTags[name]?.let {
88
+                yield(Pair(it, if (index == -1) "" else tag.substring(index + 1).unescapeTagValue()))
89
+            } ?: log.severe { "Unknown message tag: $name"}
90
+        }
91
+    }.toMap()
72 92
 
73
-class IrcMessage(val tags: ByteArray?, val prefix: ByteArray?, val command: String, val params: List<ByteArray>)
93
+    /**
94
+     * Resolves any backslash escaped characters in a tag value.
95
+     */
96
+    private fun String.unescapeTagValue() = String(sequence {
97
+        var escaped = false
98
+        forEach { char ->
99
+            when {
100
+                escaped -> {
101
+                    char.unescaped()?.let { yield(it) }
102
+                    escaped = false
103
+                }
104
+                char == '\\' -> escaped = true
105
+                else -> yield(char)
106
+            }
107
+        }
108
+    }.toList().toCharArray())
109
+
110
+    /**
111
+     * Maps an escaped character in a tag value back to its real form. Returns null if the sequence is invalid.
112
+     */
113
+    private fun Char.unescaped() = when (this) {
114
+        ':' -> ';'
115
+        'n' -> '\n'
116
+        'r' -> '\r'
117
+        's' -> ' '
118
+        '\\' -> '\\'
119
+        else -> null
120
+    }
121
+
122
+    private inline fun ByteArray?.forEachPart(delimiter: Char, action: (String) -> Unit) = this?.let {
123
+        String(it).split(delimiter).forEach(action)
124
+    }
125
+
126
+}
74 127
 
75 128
 /**
76 129
  * A ByteArray with a 'cursor' that tracks the current read position.

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/CapabilityProcessor.kt View File

@@ -3,7 +3,7 @@ package com.dmdirc.ktirc.messages
3 3
 import com.dmdirc.ktirc.events.ServerCapabilitiesAcknowledged
4 4
 import com.dmdirc.ktirc.events.ServerCapabilitiesFinished
5 5
 import com.dmdirc.ktirc.events.ServerCapabilitiesReceived
6
-import com.dmdirc.ktirc.io.IrcMessage
6
+import com.dmdirc.ktirc.model.IrcMessage
7 7
 import com.dmdirc.ktirc.model.capabilities
8 8
 import com.dmdirc.ktirc.util.logger
9 9
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/ISupportProcessor.kt View File

@@ -2,7 +2,7 @@ package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ServerFeaturesUpdated
4 4
 import com.dmdirc.ktirc.io.CaseMapping
5
-import com.dmdirc.ktirc.io.IrcMessage
5
+import com.dmdirc.ktirc.model.IrcMessage
6 6
 import com.dmdirc.ktirc.model.ModePrefixMapping
7 7
 import com.dmdirc.ktirc.model.ServerFeatureMap
8 8
 import com.dmdirc.ktirc.model.serverFeatures

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/JoinProcessor.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ChannelJoined
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.asUser
6 6
 
7 7
 internal class JoinProcessor : MessageProcessor {

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/MessageProcessor.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.IrcEvent
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 
6 6
 interface MessageProcessor {
7 7
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/NamesProcessor.kt View File

@@ -2,7 +2,7 @@ package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ChannelNamesFinished
4 4
 import com.dmdirc.ktirc.events.ChannelNamesReceived
5
-import com.dmdirc.ktirc.io.IrcMessage
5
+import com.dmdirc.ktirc.model.IrcMessage
6 6
 
7 7
 internal class NamesProcessor : MessageProcessor {
8 8
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/PartProcessor.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ChannelParted
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.asUser
6 6
 
7 7
 internal class PartProcessor : MessageProcessor {

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/PingProcessor.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.PingReceived
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 
6 6
 internal class PingProcessor : MessageProcessor {
7 7
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessor.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.MessageReceived
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.asUser
6 6
 
7 7
 internal class PrivmsgProcessor : MessageProcessor {

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/QuitProcessor.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.UserQuit
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.asUser
6 6
 
7 7
 internal class QuitProcessor : MessageProcessor {

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/WelcomeProcessor.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ServerWelcome
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 
6 6
 internal class WelcomeProcessor : MessageProcessor {
7 7
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/model/CapabilitiesState.kt View File

@@ -23,7 +23,7 @@ sealed class Capability(val name: String) {
23 23
     object UserAccountMessageTag : Capability("account-tag") // TODO: Add accounts to user info
24 24
 
25 25
     // Capabilities that extend existing commands to supply extra information:
26
-    object HostsInNamesReply : Capability("userhost-in-names") // TODO: Parse these hosts
26
+    object HostsInNamesReply : Capability("userhost-in-names")
27 27
     object MultipleUserModePrefixes : Capability("multi-prefix")
28 28
     object AccountAndRealNameInJoinMessages : Capability("extended-join") // TODO: Parse this
29 29
 

+ 12
- 0
src/main/kotlin/com/dmdirc/ktirc/model/IrcMessage.kt View File

@@ -0,0 +1,12 @@
1
+package com.dmdirc.ktirc.model
2
+
3
+class IrcMessage(val tags: Map<MessageTag, String>, val prefix: ByteArray?, val command: String, val params: List<ByteArray>)
4
+
5
+sealed class MessageTag(val name: String) {
6
+    object AccountName: MessageTag("account")
7
+    object ServerTime : MessageTag("time")
8
+}
9
+
10
+val messageTags: Map<String, MessageTag> by lazy {
11
+    MessageTag::class.nestedClasses.map { it.objectInstance as MessageTag }.associateBy { it.name }
12
+}

+ 6
- 5
src/test/kotlin/com/dmdirc/ktirc/io/MessageHandlerTest.kt View File

@@ -5,6 +5,7 @@ import com.dmdirc.ktirc.events.EventHandler
5 5
 import com.dmdirc.ktirc.events.ServerConnected
6 6
 import com.dmdirc.ktirc.events.ServerWelcome
7 7
 import com.dmdirc.ktirc.messages.MessageProcessor
8
+import com.dmdirc.ktirc.model.IrcMessage
8 9
 import com.nhaarman.mockitokotlin2.*
9 10
 import kotlinx.coroutines.channels.Channel
10 11
 import kotlinx.coroutines.runBlocking
@@ -25,7 +26,7 @@ internal class MessageHandlerTest {
25 26
     @Test
26 27
     fun `MessageHandler passes message on to correct processor`() = runBlocking {
27 28
         val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf())
28
-        val message = IrcMessage(null, null, "JOIN", emptyList())
29
+        val message = IrcMessage(emptyMap(), null, "JOIN", emptyList())
29 30
 
30 31
         with(Channel<IrcMessage>(1)) {
31 32
             send(message)
@@ -40,9 +41,9 @@ internal class MessageHandlerTest {
40 41
     @Test
41 42
     fun `MessageHandler reads multiple messages`() = runBlocking {
42 43
         val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf())
43
-        val joinMessage = IrcMessage(null, null, "JOIN", emptyList())
44
-        val nickMessage = IrcMessage(null, null, "NICK", emptyList())
45
-        val otherMessage = IrcMessage(null, null, "OTHER", emptyList())
44
+        val joinMessage = IrcMessage(emptyMap(), null, "JOIN", emptyList())
45
+        val nickMessage = IrcMessage(emptyMap(), null, "NICK", emptyList())
46
+        val otherMessage = IrcMessage(emptyMap(), null, "OTHER", emptyList())
46 47
 
47 48
         with(Channel<IrcMessage>(3)) {
48 49
             send(joinMessage)
@@ -64,7 +65,7 @@ internal class MessageHandlerTest {
64 65
         val eventHandler1 = mock<EventHandler>()
65 66
         val eventHandler2 = mock<EventHandler>()
66 67
         val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf(eventHandler1, eventHandler2))
67
-        val joinMessage = IrcMessage(null, null, "JOIN", emptyList())
68
+        val joinMessage = IrcMessage(emptyMap(), null, "JOIN", emptyList())
68 69
         whenever(joinProcessor.process(any())).thenReturn(listOf(ServerConnected, ServerWelcome("abc")))
69 70
 
70 71
         with(Channel<IrcMessage>(1)) {

+ 37
- 11
src/test/kotlin/com/dmdirc/ktirc/io/MessageParserTest.kt View File

@@ -1,5 +1,6 @@
1 1
 package com.dmdirc.ktirc.io
2 2
 
3
+import com.dmdirc.ktirc.model.MessageTag
3 4
 import org.junit.jupiter.api.Assertions.*
4 5
 import org.junit.jupiter.api.Test
5 6
 import org.junit.jupiter.params.ParameterizedTest
@@ -15,29 +16,54 @@ internal class MessageParserTest {
15 16
         @JvmStatic
16 17
         @Suppress("unused")
17 18
         fun ircMessageArgumentsProvider(): Stream<Arguments> = Stream.of(
18
-                arguments("test", null, null, "test", emptyList<String>()),
19
-                arguments("test 1 2", null, null, "test", listOf("1", "2")),
20
-                arguments("test    1     2     ", null, null, "test", listOf("1", "2")),
21
-                arguments("test :1 2", null, null, "test", listOf("1 2")),
22
-                arguments("test :1 2    ", null, null, "test", listOf("1 2    ")),
23
-                arguments("123 :1 2    ", null, null, "123", listOf("1 2    ")),
24
-                arguments(":test abc 1 2    ", null, "test", "abc", listOf("1", "2")),
25
-                arguments("@tags :test abc 1 2 :three four", "tags", "test", "abc", listOf("1", "2", "three four")),
26
-                arguments("@tags abc 1 2 : three four ", "tags", null, "abc", listOf("1", "2", " three four "))
19
+                arguments("test", null, "test", emptyList<String>()),
20
+                arguments("test 1 2", null, "test", listOf("1", "2")),
21
+                arguments("test    1     2     ", null, "test", listOf("1", "2")),
22
+                arguments("test :1 2", null, "test", listOf("1 2")),
23
+                arguments("test :1 2    ", null, "test", listOf("1 2    ")),
24
+                arguments("123 :1 2    ", null, "123", listOf("1 2    ")),
25
+                arguments(":test abc 1 2    ", "test", "abc", listOf("1", "2")),
26
+                arguments("@tags :test abc 1 2 :three four", "test", "abc", listOf("1", "2", "three four")),
27
+                arguments("@tags abc 1 2 : three four ", null, "abc", listOf("1", "2", " three four "))
27 28
         )
28 29
     }
29 30
 
30 31
     @ParameterizedTest
31 32
     @MethodSource("ircMessageArgumentsProvider")
32
-    fun `Parses IRC messages`(input: String, tags: String?, prefix: String?, command: String, params: List<String>) {
33
+    fun `Parses basic IRC messages`(input: String, prefix: String?, command: String, params: List<String>) {
33 34
         val parsed = MessageParser().parse(input.toByteArray())
34 35
 
35
-        assertEquals(tags, parsed.tags?.let { String(it) }) { "Expected '$input' to have tags '$tags'" }
36 36
         assertEquals(prefix, parsed.prefix?.let { String(it) }) { "Expected '$input' to have prefix '$prefix'" }
37 37
         assertEquals(command, parsed.command) { "Expected '$input' to have command '$command'" }
38 38
         assertEquals(params, parsed.params.map { String(it) }) { "Expected '$input' to have params '$params'" }
39 39
     }
40 40
 
41
+    @Test
42
+    fun `Parses tags without values`() {
43
+        val parsed = MessageParser().parse("@time;account= :zeroCool!dade@root.localhost PRIVMSG #chat :Hack the planet!".toByteArray())
44
+
45
+        assertEquals(2, parsed.tags.size)
46
+        assertEquals("", parsed.tags[MessageTag.ServerTime])
47
+        assertEquals("", parsed.tags[MessageTag.AccountName])
48
+    }
49
+
50
+    @Test
51
+    fun `Parses tags with values`() {
52
+        val parsed = MessageParser().parse("@time=2011-10-19T16:40:51.620Z;account=zeroCool :zeroCool!dade@root.localhost PRIVMSG #chat :Hack the planet!".toByteArray())
53
+
54
+        assertEquals(2, parsed.tags.size)
55
+        assertEquals("2011-10-19T16:40:51.620Z", parsed.tags[MessageTag.ServerTime])
56
+        assertEquals("zeroCool", parsed.tags[MessageTag.AccountName])
57
+    }
58
+
59
+    @Test
60
+    fun `Parses tags with escape sequences`() {
61
+        val parsed = MessageParser().parse("""@account=\\hack\sthe\r\nplanet\: :zeroCool!dade@root.localhost PRIVMSG #chat :Hack the planet!""".toByteArray())
62
+
63
+        assertEquals(1, parsed.tags.size)
64
+        assertEquals("\\hack the\r\nplanet;", parsed.tags[MessageTag.AccountName])
65
+    }
66
+
41 67
 }
42 68
 
43 69
 internal class CursorByteArrayTest {

+ 8
- 8
src/test/kotlin/com/dmdirc/ktirc/messages/CapabilityProcessorTest.kt View File

@@ -3,8 +3,8 @@ package com.dmdirc.ktirc.messages
3 3
 import com.dmdirc.ktirc.events.ServerCapabilitiesAcknowledged
4 4
 import com.dmdirc.ktirc.events.ServerCapabilitiesFinished
5 5
 import com.dmdirc.ktirc.events.ServerCapabilitiesReceived
6
-import com.dmdirc.ktirc.io.IrcMessage
7 6
 import com.dmdirc.ktirc.model.Capability
7
+import com.dmdirc.ktirc.model.IrcMessage
8 8
 import org.junit.jupiter.api.Assertions.assertEquals
9 9
 import org.junit.jupiter.api.Assertions.assertTrue
10 10
 import org.junit.jupiter.api.Test
@@ -15,14 +15,14 @@ internal class CapabilityProcessorTest {
15 15
 
16 16
     @Test
17 17
     fun `CapabilityProcessor does nothing for unknown subcommand`() {
18
-        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*".toByteArray(), "FOO".toByteArray())))
18
+        val events = processor.process(IrcMessage(emptyMap(), "the.gibson".toByteArray(), "CAP", listOf("*".toByteArray(), "FOO".toByteArray())))
19 19
 
20 20
         assertTrue(events.isEmpty())
21 21
     }
22 22
 
23 23
     @Test
24 24
     fun `CapabilityProcessor raises ServerCapabilitiesReceived event with known capabilities`() {
25
-        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost extended-join invalid").map { it.toByteArray() }))
25
+        val events = processor.process(IrcMessage(emptyMap(), "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost extended-join invalid").map { it.toByteArray() }))
26 26
 
27 27
         val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
28 28
         assertEquals(2, receivedEvent.capabilities.size)
@@ -32,7 +32,7 @@ internal class CapabilityProcessorTest {
32 32
 
33 33
     @Test
34 34
     fun `CapabilityProcessor raises ServerCapabilitiesReceived event with values for capabilities`() {
35
-        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost=test123 extended-join=abc=def invalid").map { it.toByteArray() }))
35
+        val events = processor.process(IrcMessage(emptyMap(), "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost=test123 extended-join=abc=def invalid").map { it.toByteArray() }))
36 36
 
37 37
         val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
38 38
         assertEquals(2, receivedEvent.capabilities.size)
@@ -42,7 +42,7 @@ internal class CapabilityProcessorTest {
42 42
 
43 43
     @Test
44 44
     fun `CapabilityProcessor overwrites earlier values with later ones for identical capabilities`() {
45
-        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost=test123 chghost chghost=456").map { it.toByteArray() }))
45
+        val events = processor.process(IrcMessage(emptyMap(), "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost=test123 chghost chghost=456").map { it.toByteArray() }))
46 46
 
47 47
         val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
48 48
         assertEquals(1, receivedEvent.capabilities.size)
@@ -52,7 +52,7 @@ internal class CapabilityProcessorTest {
52 52
 
53 53
     @Test
54 54
     fun `CapabilityProcessor raises ServerCapabilitiesReceived event with known capabilities for multi-line responses`() {
55
-        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "*", "chghost extended-join invalid").map { it.toByteArray() }))
55
+        val events = processor.process(IrcMessage(emptyMap(), "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "*", "chghost extended-join invalid").map { it.toByteArray() }))
56 56
 
57 57
         val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
58 58
         assertEquals(2, receivedEvent.capabilities.size)
@@ -62,7 +62,7 @@ internal class CapabilityProcessorTest {
62 62
 
63 63
     @Test
64 64
     fun `CapabilityProcessor raises ServerCapabilitiesFinished event for final LS responses`() {
65
-        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost extended-join invalid").map { it.toByteArray() }))
65
+        val events = processor.process(IrcMessage(emptyMap(), "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost extended-join invalid").map { it.toByteArray() }))
66 66
 
67 67
         assertEquals(2, events.size)
68 68
         assertTrue(events[1] is ServerCapabilitiesFinished)
@@ -70,7 +70,7 @@ internal class CapabilityProcessorTest {
70 70
 
71 71
     @Test
72 72
     fun `CapabilityProcessor raises ServerCapabilitiesAcknowledged event`() {
73
-        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "ACK", "chghost=test123 extended-join=abc=def invalid").map { it.toByteArray() }))
73
+        val events = processor.process(IrcMessage(emptyMap(), "the.gibson".toByteArray(), "CAP", listOf("*", "ACK", "chghost=test123 extended-join=abc=def invalid").map { it.toByteArray() }))
74 74
 
75 75
         val receivedEvent = events.filterIsInstance<ServerCapabilitiesAcknowledged>()[0]
76 76
         assertEquals(2, receivedEvent.capabilities.size)

+ 7
- 7
src/test/kotlin/com/dmdirc/ktirc/messages/ISupportProcessorTest.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.io.CaseMapping
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.ModePrefixMapping
6 6
 import com.dmdirc.ktirc.model.ServerFeature
7 7
 import com.dmdirc.ktirc.model.ServerFeatureMap
@@ -19,7 +19,7 @@ internal class ISupportProcessorTest {
19 19
 
20 20
     @Test
21 21
     fun `ISupportProcessor handles multiple numeric arguments`() {
22
-        val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
22
+        val events = processor.process(IrcMessage(emptyMap(), "server.com".toByteArray(), "005",
23 23
                 listOf("nickname", "MAXCHANNELS=123", "CHANNELLEN=456", "are supported blah blah").map { it.toByteArray() }))
24 24
 
25 25
         assertEquals(123, events[0].serverFeatures[ServerFeature.MaximumChannels])
@@ -28,7 +28,7 @@ internal class ISupportProcessorTest {
28 28
 
29 29
     @Test
30 30
     fun `ISupportProcessor handles string arguments`() {
31
-        val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
31
+        val events = processor.process(IrcMessage(emptyMap(), "server.com".toByteArray(), "005",
32 32
                 listOf("nickname", "CHANMODES=abcd", "are supported blah blah").map { it.toByteArray() }))
33 33
 
34 34
         assertEquals("abcd", events[0].serverFeatures[ServerFeature.ChannelModes])
@@ -36,7 +36,7 @@ internal class ISupportProcessorTest {
36 36
 
37 37
     @Test
38 38
     fun `ISupportProcessor handles resetting arguments`() {
39
-        val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
39
+        val events = processor.process(IrcMessage(emptyMap(), "server.com".toByteArray(), "005",
40 40
                 listOf("nickname", "-CHANMODES", "are supported blah blah").map { it.toByteArray() }))
41 41
 
42 42
         val oldFeatures = ServerFeatureMap()
@@ -47,7 +47,7 @@ internal class ISupportProcessorTest {
47 47
 
48 48
     @Test
49 49
     fun `ISupportProcessor handles case mapping arguments`() {
50
-        val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
50
+        val events = processor.process(IrcMessage(emptyMap(), "server.com".toByteArray(), "005",
51 51
                 listOf("nickname", "CASEMAPPING=rfc1459-strict", "are supported blah blah").map { it.toByteArray() }))
52 52
 
53 53
         assertEquals(CaseMapping.RfcStrict, events[0].serverFeatures[ServerFeature.ServerCaseMapping])
@@ -55,7 +55,7 @@ internal class ISupportProcessorTest {
55 55
 
56 56
     @Test
57 57
     fun `ISupportProcessor handles mode prefix arguments`() {
58
-        val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
58
+        val events = processor.process(IrcMessage(emptyMap(), "server.com".toByteArray(), "005",
59 59
                 listOf("nickname", "PREFIX=(ovd)@+%", "are supported blah blah").map { it.toByteArray() }))
60 60
 
61 61
         assertEquals(ModePrefixMapping("ovd", "@+%"), events[0].serverFeatures[ServerFeature.ModePrefixes])
@@ -63,7 +63,7 @@ internal class ISupportProcessorTest {
63 63
 
64 64
     @Test
65 65
     fun `ISupportProcessor handles boolean features with no arguments`() {
66
-        val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
66
+        val events = processor.process(IrcMessage(emptyMap(), "server.com".toByteArray(), "005",
67 67
                 listOf("nickname", "WHOX", "are supported blah blah").map { it.toByteArray() }))
68 68
 
69 69
         assertEquals(true, events[0].serverFeatures[ServerFeature.WhoxSupport])

+ 3
- 3
src/test/kotlin/com/dmdirc/ktirc/messages/JoinProcessorTest.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ChannelJoined
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.User
6 6
 import org.junit.jupiter.api.Assertions.assertEquals
7 7
 import org.junit.jupiter.api.Test
@@ -11,7 +11,7 @@ internal class JoinProcessorTest {
11 11
     @Test
12 12
     fun `JoinProcessor raises join event`() {
13 13
         val events = JoinProcessor().process(
14
-                IrcMessage(null, "acidburn!libby@root.localhost".toByteArray(), "JOIN", listOf("#crashandburn".toByteArray())))
14
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "JOIN", listOf("#crashandburn".toByteArray())))
15 15
         assertEquals(1, events.size)
16 16
         assertEquals(ChannelJoined(User("acidburn", "libby", "root.localhost"), "#crashandburn"), events[0])
17 17
     }
@@ -19,7 +19,7 @@ internal class JoinProcessorTest {
19 19
     @Test
20 20
     fun `JoinProcessor does nothing if prefix missing`() {
21 21
         val events = JoinProcessor().process(
22
-                IrcMessage(null, null, "JOIN", listOf("#crashandburn".toByteArray())))
22
+                IrcMessage(emptyMap(), null, "JOIN", listOf("#crashandburn".toByteArray())))
23 23
         assertEquals(0, events.size)
24 24
     }
25 25
 

+ 3
- 3
src/test/kotlin/com/dmdirc/ktirc/messages/NamesProcessorTest.kt View File

@@ -2,7 +2,7 @@ package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ChannelNamesFinished
4 4
 import com.dmdirc.ktirc.events.ChannelNamesReceived
5
-import com.dmdirc.ktirc.io.IrcMessage
5
+import com.dmdirc.ktirc.model.IrcMessage
6 6
 import org.junit.jupiter.api.Assertions.assertEquals
7 7
 import org.junit.jupiter.api.Test
8 8
 
@@ -12,7 +12,7 @@ internal class NamesProcessorTest {
12 12
 
13 13
     @Test
14 14
     fun `NamesProcessor handles end of names reply`() {
15
-        val events = processor.process(IrcMessage(null, ":the.gibson".toByteArray(), "366", listOf("AcidBurn", "#root", "End of /NAMES list").map { it.toByteArray() }))
15
+        val events = processor.process(IrcMessage(emptyMap(), ":the.gibson".toByteArray(), "366", listOf("AcidBurn", "#root", "End of /NAMES list").map { it.toByteArray() }))
16 16
 
17 17
         assertEquals(1, events.size)
18 18
         assertEquals("#root", (events[0] as ChannelNamesFinished).channel)
@@ -20,7 +20,7 @@ internal class NamesProcessorTest {
20 20
 
21 21
     @Test
22 22
     fun `NamesProcessor handles names reply`() {
23
-        val events = processor.process(IrcMessage(null, ":the.gibson".toByteArray(), "353", listOf("AcidBurn", "@", "#root", "AcidBurn @ZeroCool +ThePlague").map { it.toByteArray() }))
23
+        val events = processor.process(IrcMessage(emptyMap(), ":the.gibson".toByteArray(), "353", listOf("AcidBurn", "@", "#root", "AcidBurn @ZeroCool +ThePlague").map { it.toByteArray() }))
24 24
 
25 25
         assertEquals(1, events.size)
26 26
         val event = events[0] as ChannelNamesReceived

+ 4
- 4
src/test/kotlin/com/dmdirc/ktirc/messages/PartProcessorTest.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.ChannelParted
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.User
6 6
 import org.junit.jupiter.api.Assertions
7 7
 import org.junit.jupiter.api.Test
@@ -11,7 +11,7 @@ internal class PartProcessorTest {
11 11
     @Test
12 12
     fun `PartProcessor raises part event without message`() {
13 13
         val events = PartProcessor().process(
14
-                IrcMessage(null, "acidburn!libby@root.localhost".toByteArray(), "PART", listOf("#crashandburn".toByteArray())))
14
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "PART", listOf("#crashandburn".toByteArray())))
15 15
         Assertions.assertEquals(1, events.size)
16 16
         Assertions.assertEquals(ChannelParted(User("acidburn", "libby", "root.localhost"), "#crashandburn"), events[0])
17 17
     }
@@ -19,7 +19,7 @@ internal class PartProcessorTest {
19 19
     @Test
20 20
     fun `PartProcessor raises part event with message`() {
21 21
         val events = PartProcessor().process(
22
-                IrcMessage(null, "acidburn!libby@root.localhost".toByteArray(), "PART", listOf("#crashandburn".toByteArray(), "Hack the planet!".toByteArray())))
22
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "PART", listOf("#crashandburn".toByteArray(), "Hack the planet!".toByteArray())))
23 23
         Assertions.assertEquals(1, events.size)
24 24
         Assertions.assertEquals(ChannelParted(User("acidburn", "libby", "root.localhost"), "#crashandburn", "Hack the planet!"), events[0])
25 25
     }
@@ -27,7 +27,7 @@ internal class PartProcessorTest {
27 27
     @Test
28 28
     fun `PartProcessor does nothing if prefix missing`() {
29 29
         val events = JoinProcessor().process(
30
-                IrcMessage(null, null, "PART", listOf("#crashandburn".toByteArray())))
30
+                IrcMessage(emptyMap(), null, "PART", listOf("#crashandburn".toByteArray())))
31 31
         Assertions.assertEquals(0, events.size)
32 32
     }
33 33
 

+ 3
- 3
src/test/kotlin/com/dmdirc/ktirc/messages/PrivmsgProcessorTest.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.MessageReceived
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.User
6 6
 import org.junit.jupiter.api.Assertions
7 7
 import org.junit.jupiter.api.Test
@@ -11,7 +11,7 @@ internal class PrivmsgProcessorTest {
11 11
     @Test
12 12
     fun `PrivsgProcessor raises message received event`() {
13 13
         val events = PrivmsgProcessor().process(
14
-                IrcMessage(null, "acidburn!libby@root.localhost".toByteArray(), "PRIVMSG", listOf("#crashandburn".toByteArray(), "hack the planet!".toByteArray())))
14
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "PRIVMSG", listOf("#crashandburn".toByteArray(), "hack the planet!".toByteArray())))
15 15
         Assertions.assertEquals(1, events.size)
16 16
         Assertions.assertEquals(MessageReceived(User("acidburn", "libby", "root.localhost"), "#crashandburn", "hack the planet!"), events[0])
17 17
     }
@@ -19,7 +19,7 @@ internal class PrivmsgProcessorTest {
19 19
     @Test
20 20
     fun `PrivsgProcessor does nothing if prefix missing`() {
21 21
         val events = PrivmsgProcessor().process(
22
-                IrcMessage(null, null, "PRIVMSG", listOf("#crashandburn".toByteArray(), "hack the planet!".toByteArray())))
22
+                IrcMessage(emptyMap(), null, "PRIVMSG", listOf("#crashandburn".toByteArray(), "hack the planet!".toByteArray())))
23 23
         Assertions.assertEquals(0, events.size)
24 24
     }
25 25
 }

+ 4
- 4
src/test/kotlin/com/dmdirc/ktirc/messages/QuitProcessorTest.kt View File

@@ -1,7 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.UserQuit
4
-import com.dmdirc.ktirc.io.IrcMessage
4
+import com.dmdirc.ktirc.model.IrcMessage
5 5
 import com.dmdirc.ktirc.model.User
6 6
 import org.junit.jupiter.api.Assertions
7 7
 import org.junit.jupiter.api.Test
@@ -11,7 +11,7 @@ internal class QuitProcessorTest {
11 11
     @Test
12 12
     fun `QuitProcessor raises quit event without message`() {
13 13
         val events = QuitProcessor().process(
14
-                IrcMessage(null, "acidburn!libby@root.localhost".toByteArray(), "QUIT", emptyList()))
14
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "QUIT", emptyList()))
15 15
         Assertions.assertEquals(1, events.size)
16 16
         Assertions.assertEquals(UserQuit(User("acidburn", "libby", "root.localhost")), events[0])
17 17
     }
@@ -19,7 +19,7 @@ internal class QuitProcessorTest {
19 19
     @Test
20 20
     fun `QuitProcessor raises quit event with message`() {
21 21
         val events = QuitProcessor().process(
22
-                IrcMessage(null, "acidburn!libby@root.localhost".toByteArray(), "QUIT", listOf("Hack the planet!".toByteArray())))
22
+                IrcMessage(emptyMap(), "acidburn!libby@root.localhost".toByteArray(), "QUIT", listOf("Hack the planet!".toByteArray())))
23 23
         Assertions.assertEquals(1, events.size)
24 24
         Assertions.assertEquals(UserQuit(User("acidburn", "libby", "root.localhost"), "Hack the planet!"), events[0])
25 25
     }
@@ -27,7 +27,7 @@ internal class QuitProcessorTest {
27 27
     @Test
28 28
     fun `QuitProcessor does nothing if prefix missing`() {
29 29
         val events = QuitProcessor().process(
30
-                IrcMessage(null, null, "QUIT", listOf("Hack the planet!".toByteArray())))
30
+                IrcMessage(emptyMap(), null, "QUIT", listOf("Hack the planet!".toByteArray())))
31 31
         Assertions.assertEquals(0, events.size)
32 32
     }
33 33
 

+ 2
- 2
src/test/kotlin/com/dmdirc/ktirc/messages/WelcomeProcessorTest.kt View File

@@ -2,7 +2,7 @@ package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.IrcEvent
4 4
 import com.dmdirc.ktirc.events.ServerWelcome
5
-import com.dmdirc.ktirc.io.IrcMessage
5
+import com.dmdirc.ktirc.model.IrcMessage
6 6
 import org.junit.jupiter.api.Assertions.assertEquals
7 7
 import org.junit.jupiter.api.Assertions.assertTrue
8 8
 import org.junit.jupiter.api.Test
@@ -18,7 +18,7 @@ internal class WelcomeProcessorTest {
18 18
 
19 19
     @Test
20 20
     fun `WelcomeProcessor returns server welcome event`() {
21
-        val events = processor.process(IrcMessage(null, ":thegibson.com".toByteArray(), "001", listOf(
21
+        val events = processor.process(IrcMessage(emptyMap(), ":thegibson.com".toByteArray(), "001", listOf(
22 22
                 "acidBurn".toByteArray(),
23 23
                 "Welcome to the Internet Relay Network, acidBurn!burn@hacktheplanet.com".toByteArray())))
24 24
         assertEquals(listOf<IrcEvent>(ServerWelcome("acidBurn")), events)

Loading…
Cancel
Save