Browse Source

Add names handling and channel users

tags/v0.1.0
Chris Smith 5 years ago
parent
commit
ead8e9321b

+ 31
- 2
src/main/kotlin/com/dmdirc/ktirc/events/ChannelStateHandler.kt View File

@@ -2,6 +2,8 @@ package com.dmdirc.ktirc.events
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.model.ChannelState
5
+import com.dmdirc.ktirc.model.ChannelUser
6
+import com.dmdirc.ktirc.model.ServerFeature
5 7
 import com.dmdirc.ktirc.util.logger
6 8
 
7 9
 class ChannelStateHandler : EventHandler {
@@ -11,15 +13,42 @@ class ChannelStateHandler : EventHandler {
11 13
     override suspend fun processEvent(client: IrcClient, event: IrcEvent) {
12 14
         when (event) {
13 15
             is ChannelJoined -> handleJoin(client, event)
16
+            is ChannelNamesReceived -> handleNamesReceived(client, event)
17
+            is ChannelNamesFinished -> handleNamesFinished(client, event)
14 18
         }
15 19
     }
16 20
 
17 21
     private fun handleJoin(client: IrcClient, event: ChannelJoined) {
18 22
         if (client.isLocalUser(event.user)) {
19 23
             log.info { "Joined new channel: ${event.channel}" }
20
-            client.channelState += ChannelState(event.channel)
24
+            client.channelState += ChannelState(event.channel) { client.caseMapping }
25
+        }
26
+
27
+        client.channelState[event.channel]?.let { it.users += ChannelUser(event.user.nickname) }
28
+    }
29
+
30
+    private fun handleNamesReceived(client: IrcClient, event: ChannelNamesReceived) {
31
+        val channel = client.channelState[event.channel] ?: return
32
+
33
+        if (!channel.receivingUserList) {
34
+            log.finer { "Started receiving names list for ${channel.name}" }
35
+            channel.users.clear()
36
+            channel.receivingUserList = true
37
+        }
38
+
39
+        val modePrefixes = client.serverState.features[ServerFeature.ModePrefixes]!!
40
+        for (user in event.names) {
41
+            user.takeWhile { modePrefixes.isPrefix(it) }.let { prefix ->
42
+                channel.users += ChannelUser(user.substring(prefix.length), modePrefixes.getModes(prefix))
43
+            }
44
+        }
45
+    }
46
+
47
+    private fun handleNamesFinished(client: IrcClient, event: ChannelNamesFinished) {
48
+        client.channelState[event.channel]?.let {
49
+            it.receivingUserList = false
50
+            log.finest { "Finished receiving names in ${event.channel}. Users: ${it.users.toList()}" }
21 51
         }
22
-        // TODO: Add user to channel
23 52
     }
24 53
 
25 54
 }

+ 1
- 0
src/main/kotlin/com/dmdirc/ktirc/events/EventHandler.kt View File

@@ -9,6 +9,7 @@ interface EventHandler {
9 9
 }
10 10
 
11 11
 val eventHandlers = setOf(
12
+        ChannelStateHandler(),
12 13
         PingHandler(),
13 14
         ServerStateHandler()
14 15
 )

+ 16
- 20
src/main/kotlin/com/dmdirc/ktirc/events/Events.kt View File

@@ -7,28 +7,24 @@ import com.dmdirc.ktirc.model.User
7 7
 
8 8
 sealed class IrcEvent
9 9
 
10
-/**
11
- * Raised when the server initially welcomes us to the IRC network.
12
- */
13
-data class ServerWelcome(val localNick: String): IrcEvent()
14
-
15
-/**
16
- * Raised when the features supported by the server have changed. This may occur numerous times during the
17
- * connection phase.
18
- */
10
+/** Raised when the server initially welcomes us to the IRC network. */
11
+data class ServerWelcome(val localNick: String) : IrcEvent()
12
+
13
+/** Raised when the features supported by the server have changed. This may occur numerous times. */
19 14
 data class ServerFeaturesUpdated(val serverFeatures: ServerFeatureMap) : IrcEvent()
20 15
 
21
-/**
22
- * Raised when the connection to the server has been established, configuration information has been received, etc.
23
- */
16
+/** Raised when the connection to the server has been established, configuration information has been received, etc. */
17
+// TODO: Implement
24 18
 object ServerConnected : IrcEvent()
25 19
 
26
-/**
27
- * Raised whenever a PING is received from the server.
28
- */
29
-data class PingReceived(val nonce: ByteArray): IrcEvent()
20
+/** Raised whenever a PING is received from the server. */
21
+data class PingReceived(val nonce: ByteArray) : IrcEvent()
22
+
23
+/** Raised when a user joins a channel. */
24
+data class ChannelJoined(val user: User, val channel: String) : IrcEvent()
25
+
26
+/** Raised when a batch of the channel's member list has been received. More batches may follow. */
27
+data class ChannelNamesReceived(val channel: String, val names: List<String>) : IrcEvent()
30 28
 
31
-/**
32
- * Raised when a user joins a channel.
33
- */
34
-data class ChannelJoined(val user: User, val channel: String): IrcEvent()
29
+/** Raised when the entirety of the channel's member list has been received. */
30
+data class ChannelNamesFinished(val channel: String) : IrcEvent()

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

@@ -3,6 +3,7 @@ package com.dmdirc.ktirc.messages
3 3
 import com.dmdirc.ktirc.events.ServerFeaturesUpdated
4 4
 import com.dmdirc.ktirc.io.CaseMapping
5 5
 import com.dmdirc.ktirc.io.IrcMessage
6
+import com.dmdirc.ktirc.model.ModePrefixMapping
6 7
 import com.dmdirc.ktirc.model.ServerFeatureMap
7 8
 import com.dmdirc.ktirc.model.serverFeatures
8 9
 import com.dmdirc.ktirc.util.logger
@@ -57,11 +58,14 @@ class ISupportProcessor : MessageProcessor {
57 58
                 null
58 59
             }
59 60
 
60
-    private fun ByteArray.cast(to: KClass<out Any>): Any = when (to) {
61
-        Int::class -> String(this).toInt()
62
-        String::class -> String(this)
63
-        CaseMapping::class -> CaseMapping.fromName(String(this))
64
-        else -> TODO("not implemented")
61
+    private fun ByteArray.cast(to: KClass<out Any>): Any = with (String(this)) {
62
+        when (to) {
63
+            Int::class -> toInt()
64
+            String::class -> this
65
+            CaseMapping::class -> CaseMapping.fromName(this)
66
+            ModePrefixMapping::class -> indexOf(')').let { ModePrefixMapping(substring(1 until it), substring(it + 1)) }
67
+            else -> TODO("not implemented")
68
+        }
65 69
     }
66 70
 
67 71
 }

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

@@ -20,6 +20,7 @@ interface MessageProcessor {
20 20
 val messageProcessors = setOf(
21 21
         ISupportProcessor(),
22 22
         JoinProcessor(),
23
+        NamesProcessor(),
23 24
         PingProcessor(),
24 25
         WelcomeProcessor()
25 26
 )

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

@@ -0,0 +1,17 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.events.ChannelNamesFinished
4
+import com.dmdirc.ktirc.events.ChannelNamesReceived
5
+import com.dmdirc.ktirc.io.IrcMessage
6
+
7
+class NamesProcessor : MessageProcessor {
8
+
9
+    override val commands = arrayOf("353", "366")
10
+
11
+    override fun process(message: IrcMessage) = when (message.command) {
12
+        "353" -> listOf(ChannelNamesReceived(String(message.params[2]), String(message.params[3]).split(' ')))
13
+        "366" -> listOf(ChannelNamesFinished(String(message.params[1])))
14
+        else -> emptyList()
15
+    }
16
+
17
+}

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

@@ -1,3 +1,12 @@
1 1
 package com.dmdirc.ktirc.model
2 2
 
3
-class ChannelState(val name: String)
3
+import com.dmdirc.ktirc.io.CaseMapping
4
+
5
+class ChannelState(val name: String, caseMappingProvider: () -> CaseMapping) {
6
+
7
+    var receivingUserList = false
8
+    val users = ChannelUserMap(caseMappingProvider)
9
+
10
+}
11
+
12
+data class ChannelUser(var nickname: String, var modes: String = "")

+ 0
- 24
src/main/kotlin/com/dmdirc/ktirc/model/ChannelStateMap.kt View File

@@ -1,24 +0,0 @@
1
-package com.dmdirc.ktirc.model
2
-
3
-import com.dmdirc.ktirc.io.CaseMapping
4
-
5
-class ChannelStateMap(private val caseMappingProvider: () -> CaseMapping) : Iterable<ChannelState> {
6
-
7
-    private val channels = HashSet<ChannelState>()
8
-
9
-    operator fun get(name: String) = channels.find { caseMappingProvider().areEquivalent(it.name, name) }
10
-
11
-    operator fun plusAssign(state: ChannelState) {
12
-        require(get(state.name) == null) { "Channel state already registered: ${state.name}"}
13
-        channels.add(state)
14
-    }
15
-
16
-    operator fun minusAssign(state: ChannelState) {
17
-        channels.removeIf { caseMappingProvider().areEquivalent(it.name, state.name) }
18
-    }
19
-
20
-    operator fun contains(name: String) = get(name) != null
21
-
22
-    override fun iterator() = channels.iterator()
23
-
24
-}

+ 29
- 0
src/main/kotlin/com/dmdirc/ktirc/model/Maps.kt View File

@@ -0,0 +1,29 @@
1
+package com.dmdirc.ktirc.model
2
+
3
+import com.dmdirc.ktirc.io.CaseMapping
4
+
5
+abstract class CaseInsensitiveMap<T>(private val caseMappingProvider: () -> CaseMapping, private val nameOf: (T) -> String) : Iterable<T> {
6
+
7
+    private val values = HashSet<T>()
8
+
9
+    operator fun get(name: String) = values.find { caseMappingProvider().areEquivalent(nameOf(it), name) }
10
+
11
+    operator fun plusAssign(value: T) {
12
+        require(get(nameOf(value)) == null) { "Value already registered: ${nameOf(value)}"}
13
+        values.add(value)
14
+    }
15
+
16
+    operator fun minusAssign(state: T) {
17
+        values.removeIf { caseMappingProvider().areEquivalent(nameOf(it), nameOf(state)) }
18
+    }
19
+
20
+    operator fun contains(name: String) = get(name) != null
21
+
22
+    override fun iterator() = values.iterator()
23
+
24
+    fun clear() = values.clear()
25
+
26
+}
27
+
28
+class ChannelStateMap(caseMappingProvider: () -> CaseMapping) : CaseInsensitiveMap<ChannelState>(caseMappingProvider, ChannelState::name)
29
+class ChannelUserMap(caseMappingProvider: () -> CaseMapping) : CaseInsensitiveMap<ChannelUser>(caseMappingProvider, ChannelUser::nickname)

+ 9
- 0
src/main/kotlin/com/dmdirc/ktirc/model/ServerState.kt View File

@@ -27,8 +27,17 @@ class ServerFeatureMap {
27 27
 
28 28
 }
29 29
 
30
+data class ModePrefixMapping(val modes: String, val prefixes: String) {
31
+
32
+    fun isPrefix(char: Char) = prefixes.contains(char)
33
+    fun getMode(prefix: Char) = modes[prefixes.indexOf(prefix)]
34
+    fun getModes(prefixes: String) = String(prefixes.map(this::getMode).toCharArray())
35
+
36
+}
37
+
30 38
 sealed class ServerFeature<T : Any>(val name: String, val type: KClass<T>, val default: T? = null) {
31 39
     object ServerCaseMapping : ServerFeature<CaseMapping>("CASEMAPPING", CaseMapping::class, CaseMapping.Rfc)
40
+    object ModePrefixes : ServerFeature<ModePrefixMapping>("PREFIX", ModePrefixMapping::class, ModePrefixMapping("ov", "@+"))
32 41
     object MaximumChannels : ServerFeature<Int>("CHANLIMIT", Int::class)
33 42
     object ChannelModes : ServerFeature<String>("CHANMODES", String::class)
34 43
     object MaximumChannelNameLength : ServerFeature<Int>("CHANNELLEN", Int::class, 200)

+ 77
- 10
src/test/kotlin/com/dmdirc/ktirc/events/ChannelStateHandlerTest.kt View File

@@ -2,35 +2,102 @@ package com.dmdirc.ktirc.events
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.io.CaseMapping
5
-import com.dmdirc.ktirc.model.ChannelStateMap
6
-import com.dmdirc.ktirc.model.User
5
+import com.dmdirc.ktirc.model.*
7 6
 import com.nhaarman.mockitokotlin2.doReturn
8 7
 import com.nhaarman.mockitokotlin2.mock
9 8
 import kotlinx.coroutines.runBlocking
10
-import org.junit.jupiter.api.Assertions.assertFalse
11
-import org.junit.jupiter.api.Assertions.assertTrue
9
+import org.junit.jupiter.api.Assertions.*
12 10
 import org.junit.jupiter.api.Test
13 11
 
14 12
 internal class ChannelStateHandlerTest {
15 13
 
14
+    private val handler = ChannelStateHandler()
16 15
     private val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
16
+    private val serverState = ServerState("")
17 17
     private val ircClient = mock<IrcClient> {
18
+        on { serverState } doReturn serverState
18 19
         on { channelState } doReturn channelStateMap
19 20
         on { isLocalUser(User("acidburn", "libby", "root.localhost")) } doReturn true
20 21
     }
21 22
 
22 23
     @Test
23 24
     fun `ChannelStateHandler creates new state object for local joins`() = runBlocking {
24
-        val handler = ChannelStateHandler()
25
-        handler.processEvent(ircClient, ChannelJoined(User("acidburn", "libby", "root.localhost"), "#newchannel"))
26
-        assertTrue("#newchannel" in channelStateMap)
25
+        handler.processEvent(ircClient, ChannelJoined(User("acidburn", "libby", "root.localhost"), "#thegibson"))
26
+        assertTrue("#thegibson" in channelStateMap)
27 27
     }
28 28
 
29 29
     @Test
30 30
     fun `ChannelStateHandler does not create new state object for remote joins`() = runBlocking {
31
-        val handler = ChannelStateHandler()
32
-        handler.processEvent(ircClient, ChannelJoined(User("zerocool", "dade", "root.localhost"), "#newchannel"))
33
-        assertFalse("#newchannel" in channelStateMap)
31
+        handler.processEvent(ircClient, ChannelJoined(User("zerocool", "dade", "root.localhost"), "#thegibson"))
32
+        assertFalse("#thegibson" in channelStateMap)
33
+    }
34
+
35
+    @Test
36
+    fun `ChannelStateHandler adds joiners to channel state`() = runBlocking {
37
+        channelStateMap += ChannelState("#thegibson") { CaseMapping.Rfc }
38
+
39
+        handler.processEvent(ircClient, ChannelJoined(User("zerocool", "dade", "root.localhost"), "#thegibson"))
40
+
41
+        assertTrue("zerocool" in channelStateMap["#thegibson"]?.users!!)
42
+    }
43
+
44
+    @Test
45
+    fun `ChannelStateHandler clears existing users when getting a new list`() = runBlocking {
46
+        val channel = ChannelState("#thegibson") { CaseMapping.Rfc }
47
+        channel.users += ChannelUser("acidBurn")
48
+        channel.users += ChannelUser("thePlague")
49
+        channelStateMap += channel
50
+
51
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("zeroCool")))
52
+
53
+        assertEquals(1, channel.users.count())
54
+        assertNotNull(channel.users["zeroCool"])
55
+    }
56
+
57
+    @Test
58
+    fun `ChannelStateHandler adds users from multiple name received events`() = runBlocking {
59
+        val channel = ChannelState("#thegibson") { CaseMapping.Rfc }
60
+        channelStateMap += channel
61
+
62
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("zeroCool")))
63
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("acidBurn")))
64
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("thePlague")))
65
+
66
+        assertEquals(3, channel.users.count())
67
+        assertNotNull(channel.users["zeroCool"])
68
+        assertNotNull(channel.users["acidBurn"])
69
+        assertNotNull(channel.users["thePlague"])
70
+    }
71
+
72
+    @Test
73
+    fun `ChannelStateHandler clears and readds users on additional names received`() = runBlocking {
74
+        val channel = ChannelState("#thegibson") { CaseMapping.Rfc }
75
+        channelStateMap += channel
76
+
77
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("zeroCool")))
78
+        handler.processEvent(ircClient, ChannelNamesFinished("#thegibson"))
79
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("acidBurn")))
80
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("thePlague")))
81
+
82
+        assertEquals(2, channel.users.count())
83
+        assertNotNull(channel.users["acidBurn"])
84
+        assertNotNull(channel.users["thePlague"])
85
+    }
86
+
87
+    @Test
88
+    fun `ChannelStateHandler adds users with mode prefixes`() = runBlocking {
89
+        val channel = ChannelState("#thegibson") { CaseMapping.Rfc }
90
+        channelStateMap += channel
91
+        serverState.features[ServerFeature.ModePrefixes] = ModePrefixMapping("ov", "@+")
92
+
93
+        handler.processEvent(ircClient, ChannelNamesReceived("#thegibson", listOf("@zeroCool", "@+acidBurn", "+thePlague", "cerealKiller")))
94
+        handler.processEvent(ircClient, ChannelNamesFinished("#thegibson"))
95
+
96
+        assertEquals(4, channel.users.count())
97
+        assertEquals("o", channel.users["zeroCool"]?.modes)
98
+        assertEquals("ov", channel.users["acidBurn"]?.modes)
99
+        assertEquals("v", channel.users["thePlague"]?.modes)
100
+        assertEquals("", channel.users["cerealKiller"]?.modes)
34 101
     }
35 102
 
36 103
 }

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

@@ -2,6 +2,7 @@ package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.io.CaseMapping
4 4
 import com.dmdirc.ktirc.io.IrcMessage
5
+import com.dmdirc.ktirc.model.ModePrefixMapping
5 6
 import com.dmdirc.ktirc.model.ServerFeature
6 7
 import com.dmdirc.ktirc.model.ServerFeatureMap
7 8
 import org.junit.jupiter.api.Assertions.*
@@ -52,6 +53,14 @@ internal class ISupportProcessorTest {
52 53
         assertEquals(CaseMapping.RfcStrict, events[0].serverFeatures[ServerFeature.ServerCaseMapping])
53 54
     }
54 55
 
56
+    @Test
57
+    fun `ISupportProcessor handles mode prefix arguments`() {
58
+        val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
59
+                listOf("nickname", "PREFIX=(ovd)@+%", "are supported blah blah").map { it.toByteArray() }))
60
+
61
+        assertEquals(ModePrefixMapping("ovd", "@+%"), events[0].serverFeatures[ServerFeature.ModePrefixes])
62
+    }
63
+
55 64
     @Test
56 65
     fun `ISupportProcessor handles boolean features with no arguments`() {
57 66
         val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",

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

@@ -0,0 +1,31 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.events.ChannelNamesFinished
4
+import com.dmdirc.ktirc.events.ChannelNamesReceived
5
+import com.dmdirc.ktirc.io.IrcMessage
6
+import org.junit.jupiter.api.Assertions.assertEquals
7
+import org.junit.jupiter.api.Test
8
+
9
+internal class NamesProcessorTest {
10
+
11
+    private val processor = NamesProcessor()
12
+
13
+    @Test
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() }))
16
+
17
+        assertEquals(1, events.size)
18
+        assertEquals("#root", (events[0] as ChannelNamesFinished).channel)
19
+    }
20
+
21
+    @Test
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() }))
24
+
25
+        assertEquals(1, events.size)
26
+        val event = events[0] as ChannelNamesReceived
27
+        assertEquals("#root", event.channel)
28
+        assertEquals(listOf("AcidBurn", "@ZeroCool", "+ThePlague"), event.names)
29
+    }
30
+
31
+}

+ 0
- 78
src/test/kotlin/com/dmdirc/ktirc/model/ChannelStateMapTest.kt View File

@@ -1,78 +0,0 @@
1
-package com.dmdirc.ktirc.model
2
-
3
-import com.dmdirc.ktirc.io.CaseMapping
4
-import org.junit.jupiter.api.Assertions.*
5
-import org.junit.jupiter.api.Test
6
-import org.junit.jupiter.api.assertThrows
7
-
8
-internal class ChannelStateMapTest {
9
-
10
-    @Test
11
-    fun `ChannelStateMap stores channel state`() {
12
-        val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
13
-        val channelState = ChannelState("#dumpsterdiving")
14
-        channelStateMap += channelState
15
-
16
-        assertSame(channelState, channelStateMap["#dumpsterdiving"])
17
-    }
18
-
19
-    @Test
20
-    fun `ChannelStateMap disallows setting the same channel twice`() {
21
-        val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
22
-        channelStateMap += ChannelState("#dumpsterdiving")
23
-
24
-        assertThrows<IllegalArgumentException> {
25
-            channelStateMap += ChannelState("#DumpsterDiving")
26
-        }
27
-    }
28
-
29
-    @Test
30
-    fun `ChannelStateMap retrieves channels in different cases`() {
31
-        val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
32
-        val channelState = ChannelState("#dumpsterdiving[]")
33
-        channelStateMap += channelState
34
-
35
-        assertSame(channelState, channelStateMap["#dumpsterdiving{}"])
36
-    }
37
-
38
-    @Test
39
-    fun `ChannelStateMap returns null if channel not found`() {
40
-        val channelStateMap = ChannelStateMap { CaseMapping.Ascii }
41
-        val channelState = ChannelState("#dumpsterdiving[]")
42
-        channelStateMap += channelState
43
-
44
-        assertNull(channelStateMap["#dumpsterdiving{}"])
45
-    }
46
-
47
-    @Test
48
-    fun `ChannelStateMap removes channels`() {
49
-        val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
50
-        val channelState = ChannelState("#dumpsterdiving")
51
-        channelStateMap += channelState
52
-        channelStateMap -= ChannelState("#dumpsterDIVING")
53
-
54
-        assertNull(channelStateMap["#dumpsterdiving"])
55
-    }
56
-
57
-    @Test
58
-    fun `ChannelStateMap can be iterated`() {
59
-        val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
60
-        channelStateMap += ChannelState("#dumpsterdiving")
61
-        channelStateMap += ChannelState("#gibson")
62
-
63
-        val names = channelStateMap.map { it.name }.toList()
64
-        assertEquals(2, names.size)
65
-        assertTrue(names.contains("#dumpsterdiving"))
66
-        assertTrue(names.contains("#gibson"))
67
-    }
68
-
69
-    @Test
70
-    fun `ChannelStateMap indicates if it contains a channel or not`() {
71
-        val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
72
-        channelStateMap += ChannelState("#dumpsterdiving")
73
-
74
-        assertTrue("#dumpsterDIVING" in channelStateMap)
75
-        assertFalse("#crashandburn" in channelStateMap)
76
-    }
77
-
78
-}

+ 127
- 0
src/test/kotlin/com/dmdirc/ktirc/model/MapsTest.kt View File

@@ -0,0 +1,127 @@
1
+package com.dmdirc.ktirc.model
2
+
3
+import com.dmdirc.ktirc.io.CaseMapping
4
+import org.junit.jupiter.api.Assertions.*
5
+import org.junit.jupiter.api.Test
6
+import org.junit.jupiter.api.assertThrows
7
+
8
+internal class CaseInsensitiveMapTest {
9
+
10
+    private var caseMapping = CaseMapping.Rfc
11
+    private val map = object : CaseInsensitiveMap<String>({ caseMapping }, { str -> str.substring(0, 4) }) {}
12
+
13
+    @Test
14
+    fun `CaseInsensitiveMap stores values`() {
15
+        val value = "acidBurn"
16
+
17
+        map += value
18
+
19
+        assertSame(value, map["acid"])
20
+    }
21
+
22
+    @Test
23
+    fun `CaseInsensitiveMap disallows the same value twice`() {
24
+        val value = "acidBurn"
25
+
26
+        map += value
27
+
28
+        assertThrows<IllegalArgumentException> {
29
+            map += value
30
+        }
31
+    }
32
+
33
+    @Test
34
+    fun `CaseInsensitiveMap retrieves values using differently cased keys`() {
35
+        val value = "[acidBurn]"
36
+        map += value
37
+
38
+        assertSame(value, map["{ACI"])
39
+    }
40
+
41
+    @Test
42
+    fun `CaseInsensitiveMap retrieves values if the casemapping changes`() {
43
+        val value = "[acidBurn]"
44
+        map += value
45
+
46
+        caseMapping = CaseMapping.Ascii
47
+
48
+        assertSame(value, map["[ACI"])
49
+        assertNull(map["{aci"])
50
+    }
51
+
52
+    @Test
53
+    fun `CaseInsensitiveMap retrieves null if value not found`() {
54
+        val value = "[acidBurn]"
55
+        map += value
56
+
57
+        assertNull(map["acidBurn"])
58
+        assertNull(map["thePlague"])
59
+    }
60
+
61
+    @Test
62
+    fun `CaseInsensitiveMap removes values`() {
63
+        map += "acidBurn"
64
+        map -= "ACIDburn"
65
+
66
+        assertNull(map["acidBurn"])
67
+        assertNull(map["ACIDburn"])
68
+    }
69
+
70
+    @Test
71
+    fun `CaseInsensitiveMap can be iterated`() {
72
+        map += "acidBurn"
73
+        map += "zeroCool"
74
+
75
+        val names = map.toList()
76
+        assertEquals(2, names.size)
77
+        assertTrue(names.contains("acidBurn"))
78
+        assertTrue(names.contains("zeroCool"))
79
+    }
80
+
81
+    @Test
82
+    fun `ChannelInsensitiveMap indicates if it contains a value or not`() {
83
+        map += "acidBurn"
84
+
85
+        assertTrue("acid" in map)
86
+        assertFalse("theP" in map)
87
+    }
88
+
89
+    @Test
90
+    fun `ChannelInsensitiveMap can be cleared`() {
91
+        map += "acidBurn"
92
+        map += "zeroCool"
93
+
94
+        map.clear()
95
+
96
+        assertFalse("acid" in map)
97
+        assertFalse("zero" in map)
98
+        assertEquals(0, map.count())
99
+    }
100
+
101
+}
102
+
103
+internal class ChannelStateMapTest {
104
+
105
+    @Test
106
+    fun `ChannelStateMap maps channels on name`() {
107
+        val channelUserMap = ChannelStateMap { CaseMapping.Rfc }
108
+        channelUserMap += ChannelState("#dumpsterDiving") { CaseMapping.Rfc }
109
+        assertTrue("#dumpsterDiving" in channelUserMap)
110
+        assertTrue("#dumpsterdiving" in channelUserMap)
111
+        assertFalse("#thegibson" in channelUserMap)
112
+    }
113
+
114
+}
115
+
116
+internal class ChannelUserMapTest {
117
+
118
+    @Test
119
+    fun `ChannelUserMap maps users on nickname`() {
120
+        val channelUserMap = ChannelUserMap { CaseMapping.Rfc }
121
+        channelUserMap += ChannelUser("acidBurn")
122
+        assertTrue("acidBurn" in channelUserMap)
123
+        assertTrue("acidburn" in channelUserMap)
124
+        assertFalse("zerocool" in channelUserMap)
125
+    }
126
+
127
+}

+ 31
- 1
src/test/kotlin/com/dmdirc/ktirc/model/ServerStateTest.kt View File

@@ -1,6 +1,6 @@
1 1
 package com.dmdirc.ktirc.model
2 2
 
3
-import org.junit.jupiter.api.Assertions.assertEquals
3
+import org.junit.jupiter.api.Assertions.*
4 4
 import org.junit.jupiter.api.Test
5 5
 
6 6
 internal class ServerStateTest {
@@ -11,4 +11,34 @@ internal class ServerStateTest {
11 11
         assertEquals("acidBurn", serverState.localNickname)
12 12
     }
13 13
 
14
+}
15
+
16
+internal class ModePrefixMappingTest {
17
+
18
+    @Test
19
+    fun `ModePrefixMapping identifies which chars are prefixes`() {
20
+        val mapping = ModePrefixMapping("oav", "+@-")
21
+        assertTrue(mapping.isPrefix('+'))
22
+        assertTrue(mapping.isPrefix('@'))
23
+        assertFalse(mapping.isPrefix('!'))
24
+        assertFalse(mapping.isPrefix('o'))
25
+    }
26
+
27
+    @Test
28
+    fun `ModePrefixMapping maps prefixes to modes`() {
29
+        val mapping = ModePrefixMapping("oav", "+@-")
30
+        assertEquals('o', mapping.getMode('+'))
31
+        assertEquals('a', mapping.getMode('@'))
32
+        assertEquals('v', mapping.getMode('-'))
33
+    }
34
+
35
+    @Test
36
+    fun `ModePrefixMapping maps prefix strings to modes`() {
37
+        val mapping = ModePrefixMapping("oav", "+@-")
38
+        assertEquals("oa", mapping.getModes("+@"))
39
+        assertEquals("o", mapping.getModes("+"))
40
+        assertEquals("", mapping.getModes(""))
41
+        assertEquals("vao", mapping.getModes("-@+"))
42
+    }
43
+
14 44
 }

Loading…
Cancel
Save