Browse Source

Reset state on disconnect

tags/v0.6.0
Chris Smith 5 years ago
parent
commit
23bf2faa2c

+ 1
- 0
CHANGELOG View File

11
     * Added sendTagMessage() to send message tags without any content
11
     * Added sendTagMessage() to send message tags without any content
12
     * The reply() utility automatically marks messages as a reply
12
     * The reply() utility automatically marks messages as a reply
13
     * Added react() utility to send a reaction client tag
13
     * Added react() utility to send a reaction client tag
14
+ * State is now reset when the client is disconnected, so you can immediately reconnect
14
  * (Internal) improved how coroutines and channels are used in LineBufferedSocket
15
  * (Internal) improved how coroutines and channels are used in LineBufferedSocket
15
 
16
 
16
 v0.5.0
17
 v0.5.0

+ 13
- 1
src/main/kotlin/com/dmdirc/ktirc/IrcClient.kt View File

5
 import com.dmdirc.ktirc.messages.*
5
 import com.dmdirc.ktirc.messages.*
6
 import com.dmdirc.ktirc.model.*
6
 import com.dmdirc.ktirc.model.*
7
 import com.dmdirc.ktirc.util.currentTimeProvider
7
 import com.dmdirc.ktirc.util.currentTimeProvider
8
+import com.dmdirc.ktirc.util.logger
8
 import io.ktor.util.KtorExperimentalAPI
9
 import io.ktor.util.KtorExperimentalAPI
9
 import kotlinx.coroutines.*
10
 import kotlinx.coroutines.*
10
 import kotlinx.coroutines.channels.map
11
 import kotlinx.coroutines.channels.map
90
 @ExperimentalCoroutinesApi
91
 @ExperimentalCoroutinesApi
91
 class IrcClientImpl(private val server: Server, override val profile: Profile) : IrcClient, CoroutineScope {
92
 class IrcClientImpl(private val server: Server, override val profile: Profile) : IrcClient, CoroutineScope {
92
 
93
 
94
+    private val log by logger()
95
+
93
     override val coroutineContext = GlobalScope.newCoroutineContext(Dispatchers.IO)
96
     override val coroutineContext = GlobalScope.newCoroutineContext(Dispatchers.IO)
94
 
97
 
95
     internal var socketFactory: (CoroutineScope, String, Int, Boolean) -> LineBufferedSocket = ::KtorLineBufferedSocket
98
     internal var socketFactory: (CoroutineScope, String, Int, Boolean) -> LineBufferedSocket = ::KtorLineBufferedSocket
106
     private val connecting = AtomicBoolean(false)
109
     private val connecting = AtomicBoolean(false)
107
 
110
 
108
     override fun send(message: String) {
111
     override fun send(message: String) {
109
-        socket?.sendChannel?.offer(message.toByteArray())
112
+        socket?.sendChannel?.offer(message.toByteArray()) ?: log.warning { "No send channel for message: $message"}
110
     }
113
     }
111
 
114
 
112
     override fun connect() {
115
     override fun connect() {
127
                 // TODO: Send correct host
130
                 // TODO: Send correct host
128
                 sendUser(profile.userName, profile.realName)
131
                 sendUser(profile.userName, profile.realName)
129
                 messageHandler.processMessages(this@IrcClientImpl, receiveChannel.map { parser.parse(it) })
132
                 messageHandler.processMessages(this@IrcClientImpl, receiveChannel.map { parser.parse(it) })
133
+                reset()
130
                 emitEvent(ServerDisconnected(currentTimeProvider()))
134
                 emitEvent(ServerDisconnected(currentTimeProvider()))
131
             }
135
             }
132
         }
136
         }
148
     private fun emitEvent(event: IrcEvent) = messageHandler.emitEvent(this, event)
152
     private fun emitEvent(event: IrcEvent) = messageHandler.emitEvent(this, event)
149
     private fun sendPasswordIfPresent() = server.password?.let(this::sendPassword)
153
     private fun sendPasswordIfPresent() = server.password?.let(this::sendPassword)
150
 
154
 
155
+    internal fun reset() {
156
+        serverState.reset()
157
+        channelState.clear()
158
+        userState.reset()
159
+        socket = null
160
+        connecting.set(false)
161
+    }
162
+
151
 }
163
 }

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

15
     /** The capabilities that we have agreed to enable. */
15
     /** The capabilities that we have agreed to enable. */
16
     val enabledCapabilities = HashMap<Capability, String>()
16
     val enabledCapabilities = HashMap<Capability, String>()
17
 
17
 
18
+    internal fun reset() {
19
+        negotiationState = CapabilitiesNegotiationState.AWAITING_LIST
20
+        advertisedCapabilities.clear()
21
+        enabledCapabilities.clear()
22
+    }
23
+
18
 }
24
 }
19
 
25
 
20
 /**
26
 /**

+ 7
- 0
src/main/kotlin/com/dmdirc/ktirc/model/ChannelState.kt View File

30
      * If [modesDiscovered] is false, this map may be missing modes that the server hasn't told us about.
30
      * If [modesDiscovered] is false, this map may be missing modes that the server hasn't told us about.
31
      */
31
      */
32
     var modes = HashMap<Char, String>()
32
     var modes = HashMap<Char, String>()
33
+
34
+    internal fun reset() {
35
+        receivingUserList = false
36
+        modesDiscovered = false
37
+        users.clear()
38
+        modes.clear()
39
+    }
33
 }
40
 }
34
 
41
 
35
 /**
42
 /**

+ 6
- 2
src/main/kotlin/com/dmdirc/ktirc/model/SaslState.kt View File

14
     var mechanismState: Any? = null
14
     var mechanismState: Any? = null
15
 
15
 
16
     fun getPreferredSaslMechanism(serverMechanisms: String?): SaslMechanism? {
16
     fun getPreferredSaslMechanism(serverMechanisms: String?): SaslMechanism? {
17
-        serverMechanisms ?: return null
18
-        val serverSupported = serverMechanisms.split(',')
17
+        val serverSupported = serverMechanisms?.split(',') ?: return null
19
         return mechanisms
18
         return mechanisms
20
                 .filter { it.priority < currentMechanism?.priority ?: Int.MAX_VALUE }
19
                 .filter { it.priority < currentMechanism?.priority ?: Int.MAX_VALUE }
21
                 .filter { serverMechanisms.isEmpty() || it.ircName in serverSupported }
20
                 .filter { serverMechanisms.isEmpty() || it.ircName in serverSupported }
22
                 .maxBy { it.priority }
21
                 .maxBy { it.priority }
23
     }
22
     }
24
 
23
 
24
+    fun reset() {
25
+        saslBuffer = ""
26
+        currentMechanism = null
27
+    }
28
+
25
 }
29
 }

+ 18
- 6
src/main/kotlin/com/dmdirc/ktirc/model/ServerState.kt View File

10
  * Contains the current state of a single IRC server.
10
  * Contains the current state of a single IRC server.
11
  */
11
  */
12
 class ServerState internal constructor(
12
 class ServerState internal constructor(
13
-        initialNickname: String,
14
-        initialServerName: String,
13
+        private val initialNickname: String,
14
+        private val initialServerName: String,
15
         saslMechanisms: Collection<SaslMechanism> = supportedSaslMechanisms) {
15
         saslMechanisms: Collection<SaslMechanism> = supportedSaslMechanisms) {
16
 
16
 
17
     private val log by logger()
17
     private val log by logger()
79
         return ChannelModeType.NoParameter
79
         return ChannelModeType.NoParameter
80
     }
80
     }
81
 
81
 
82
+    internal fun reset() {
83
+        receivedWelcome = false
84
+        status = ServerStatus.Disconnected
85
+        localNickname = initialNickname
86
+        serverName = initialServerName
87
+        features.clear()
88
+        capabilities.reset()
89
+        sasl.reset()
90
+    }
91
+
82
 }
92
 }
83
 
93
 
84
 /**
94
 /**
104
 
114
 
105
     internal fun setAll(featureMap: ServerFeatureMap) = featureMap.features.forEach { feature, value -> features[feature] = value }
115
     internal fun setAll(featureMap: ServerFeatureMap) = featureMap.features.forEach { feature, value -> features[feature] = value }
106
     internal fun reset(feature: ServerFeature<*>) = features.put(feature, null)
116
     internal fun reset(feature: ServerFeature<*>) = features.put(feature, null)
117
+    internal fun clear() = features.clear()
118
+    internal fun isEmpty() = features.isEmpty()
107
 
119
 
108
 }
120
 }
109
 
121
 
143
     object WhoxSupport : ServerFeature<Boolean>("WHOX", Boolean::class, false)
155
     object WhoxSupport : ServerFeature<Boolean>("WHOX", Boolean::class, false)
144
 }
156
 }
145
 
157
 
146
-internal val serverFeatures: Map<String, ServerFeature<*>> by lazy {
147
-    ServerFeature::class.nestedClasses.map { it.objectInstance as ServerFeature<*> }.associateBy { it.name }
148
-}
149
-
150
 /**
158
 /**
151
  * Enumeration of the possible states of a server.
159
  * Enumeration of the possible states of a server.
152
  */
160
  */
160
     /** We are connected and commands can be sent. */
168
     /** We are connected and commands can be sent. */
161
     Ready,
169
     Ready,
162
 }
170
 }
171
+
172
+internal val serverFeatures: Map<String, ServerFeature<*>> by lazy {
173
+    ServerFeature::class.nestedClasses.map { it.objectInstance as ServerFeature<*> }.associateBy { it.name }
174
+}

+ 4
- 0
src/main/kotlin/com/dmdirc/ktirc/model/UserState.kt View File

35
         }
35
         }
36
     }
36
     }
37
 
37
 
38
+    internal fun reset() {
39
+        users.clear()
40
+    }
41
+
38
 }
42
 }
39
 
43
 
40
 /**
44
 /**

+ 29
- 33
src/test/kotlin/com/dmdirc/ktirc/IrcClientTest.kt View File

49
     }
49
     }
50
 
50
 
51
     @Test
51
     @Test
52
-    fun `IrcClientImpl uses socket factory to create a new socket on connect`() {
52
+    fun `uses socket factory to create a new socket on connect`() {
53
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
53
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
54
         client.socketFactory = mockSocketFactory
54
         client.socketFactory = mockSocketFactory
55
         client.connect()
55
         client.connect()
58
     }
58
     }
59
 
59
 
60
     @Test
60
     @Test
61
-    fun `IrcClientImpl uses socket factory to create a new tls on connect`() {
61
+    fun `uses socket factory to create a new tls on connect`() {
62
         val client = IrcClientImpl(Server(HOST, PORT, true), Profile(NICK, REAL_NAME, USER_NAME))
62
         val client = IrcClientImpl(Server(HOST, PORT, true), Profile(NICK, REAL_NAME, USER_NAME))
63
         client.socketFactory = mockSocketFactory
63
         client.socketFactory = mockSocketFactory
64
         client.connect()
64
         client.connect()
67
     }
67
     }
68
 
68
 
69
     @Test
69
     @Test
70
-    fun `IrcClientImpl throws if socket already exists`() {
70
+    fun `throws if socket already exists`() {
71
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
71
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
72
         client.socketFactory = mockSocketFactory
72
         client.socketFactory = mockSocketFactory
73
         client.connect()
73
         client.connect()
78
     }
78
     }
79
 
79
 
80
     @Test
80
     @Test
81
-    fun `IrcClientImpl emits connection events with local time`() = runBlocking {
81
+    fun `emits connection events with local time`() = runBlocking {
82
         currentTimeProvider = { TestConstants.time }
82
         currentTimeProvider = { TestConstants.time }
83
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
83
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
84
         client.socketFactory = mockSocketFactory
84
         client.socketFactory = mockSocketFactory
96
     }
96
     }
97
 
97
 
98
     @Test
98
     @Test
99
-    fun `IrcClientImpl emits disconnected event with local time when read channel closed`() = runBlocking {
99
+    fun `emits disconnected event with local time when read channel closed`() = runBlocking {
100
         currentTimeProvider = { TestConstants.time }
100
         currentTimeProvider = { TestConstants.time }
101
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
101
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
102
         client.socketFactory = mockSocketFactory
102
         client.socketFactory = mockSocketFactory
103
         client.connect()
103
         client.connect()
104
-        client.blockUntilConnected()
105
 
104
 
106
         client.onEvent(mockEventHandler)
105
         client.onEvent(mockEventHandler)
107
         readLineChannel.close()
106
         readLineChannel.close()
108
 
107
 
109
         val captor = argumentCaptor<ServerDisconnected>()
108
         val captor = argumentCaptor<ServerDisconnected>()
110
-        verify(mockEventHandler, timeout(500)).invoke(captor.capture())
109
+        verify(mockEventHandler, timeout(500).atLeast(2)).invoke(captor.capture())
111
         assertEquals(TestConstants.time, captor.lastValue.time)
110
         assertEquals(TestConstants.time, captor.lastValue.time)
112
     }
111
     }
113
 
112
 
114
     @Test
113
     @Test
115
-    fun `IrcClientImpl sends basic connection strings`() = runBlocking {
114
+    fun `sends basic connection strings`() = runBlocking {
116
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
115
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
117
         client.socketFactory = mockSocketFactory
116
         client.socketFactory = mockSocketFactory
118
         client.connect()
117
         client.connect()
119
 
118
 
120
-        client.blockUntilConnected()
121
-
122
         assertEquals("CAP LS 302", String(sendLineChannel.receive()))
119
         assertEquals("CAP LS 302", String(sendLineChannel.receive()))
123
         assertEquals("NICK :$NICK", String(sendLineChannel.receive()))
120
         assertEquals("NICK :$NICK", String(sendLineChannel.receive()))
124
         assertEquals("USER $USER_NAME 0 * :$REAL_NAME", String(sendLineChannel.receive()))
121
         assertEquals("USER $USER_NAME 0 * :$REAL_NAME", String(sendLineChannel.receive()))
125
     }
122
     }
126
 
123
 
127
     @Test
124
     @Test
128
-    fun `IrcClientImpl sends password first, when present`() = runBlocking {
125
+    fun `sends password first, when present`() = runBlocking {
129
         val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
126
         val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
130
         client.socketFactory = mockSocketFactory
127
         client.socketFactory = mockSocketFactory
131
         client.connect()
128
         client.connect()
132
 
129
 
133
-        client.blockUntilConnected()
134
-
135
         assertEquals("CAP LS 302", String(sendLineChannel.receive()))
130
         assertEquals("CAP LS 302", String(sendLineChannel.receive()))
136
         assertEquals("PASS :$PASSWORD", String(sendLineChannel.receive()))
131
         assertEquals("PASS :$PASSWORD", String(sendLineChannel.receive()))
137
     }
132
     }
138
 
133
 
139
     @Test
134
     @Test
140
-    fun `IrcClientImpl sends events to provided event handler`() {
135
+    fun `sends events to provided event handler`() {
141
         val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
136
         val client = IrcClientImpl(Server(HOST, PORT, password = PASSWORD), Profile(NICK, REAL_NAME, USER_NAME))
142
         client.socketFactory = mockSocketFactory
137
         client.socketFactory = mockSocketFactory
143
         client.onEvent(mockEventHandler)
138
         client.onEvent(mockEventHandler)
152
     }
147
     }
153
 
148
 
154
     @Test
149
     @Test
155
-    fun `IrcClient gets case mapping from server features`() {
150
+    fun `gets case mapping from server features`() {
156
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
151
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
157
         client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.RfcStrict
152
         client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.RfcStrict
158
         assertEquals(CaseMapping.RfcStrict, client.caseMapping)
153
         assertEquals(CaseMapping.RfcStrict, client.caseMapping)
159
     }
154
     }
160
 
155
 
161
     @Test
156
     @Test
162
-    fun `IrcClient indicates if user is local user or not`() {
157
+    fun `indicates if user is local user or not`() {
163
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
158
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
164
         client.serverState.localNickname = "[acidBurn]"
159
         client.serverState.localNickname = "[acidBurn]"
165
 
160
 
168
     }
163
     }
169
 
164
 
170
     @Test
165
     @Test
171
-    fun `IrcClient indicates if nickname is local user or not`() {
166
+    fun `indicates if nickname is local user or not`() {
172
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
167
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
173
         client.serverState.localNickname = "[acidBurn]"
168
         client.serverState.localNickname = "[acidBurn]"
174
 
169
 
177
     }
172
     }
178
 
173
 
179
     @Test
174
     @Test
180
-    fun `IrcClient uses current case mapping to check local user`() {
175
+    fun `uses current case mapping to check local user`() {
181
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
176
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
182
         client.serverState.localNickname = "[acidBurn]"
177
         client.serverState.localNickname = "[acidBurn]"
183
         client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.Ascii
178
         client.serverState.features[ServerFeature.ServerCaseMapping] = CaseMapping.Ascii
185
     }
180
     }
186
 
181
 
187
     @Test
182
     @Test
188
-    fun `IrcClientImpl sends text to socket`() = runBlocking {
183
+    fun `sends text to socket`() = runBlocking {
189
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
184
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
190
         client.socketFactory = mockSocketFactory
185
         client.socketFactory = mockSocketFactory
191
         client.connect()
186
         client.connect()
192
 
187
 
193
-        client.blockUntilConnected()
194
-
195
         client.send("testing 123")
188
         client.send("testing 123")
196
 
189
 
197
         assertEquals(true, withTimeoutOrNull(500) {
190
         assertEquals(true, withTimeoutOrNull(500) {
207
     }
200
     }
208
 
201
 
209
     @Test
202
     @Test
210
-    fun `IrcClientImpl disconnects the socket`() = runBlocking {
203
+    fun `disconnects the socket`() = runBlocking {
211
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
204
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
212
         client.socketFactory = mockSocketFactory
205
         client.socketFactory = mockSocketFactory
213
         client.connect()
206
         client.connect()
214
 
207
 
215
-        client.blockUntilConnected()
216
-
217
         client.disconnect()
208
         client.disconnect()
218
 
209
 
219
         verify(mockSocket, timeout(500)).disconnect()
210
         verify(mockSocket, timeout(500)).disconnect()
220
     }
211
     }
221
 
212
 
222
     @Test
213
     @Test
223
-    fun `IrcClientImpl sends messages in order`() = runBlocking {
214
+    fun `sends messages in order`() = runBlocking {
224
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
215
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
225
         client.socketFactory = mockSocketFactory
216
         client.socketFactory = mockSocketFactory
226
         client.connect()
217
         client.connect()
227
 
218
 
228
-        client.blockUntilConnected()
229
-
230
         (0..100).forEach { client.send("TEST $it") }
219
         (0..100).forEach { client.send("TEST $it") }
231
 
220
 
232
         assertEquals(100, withTimeoutOrNull(500) {
221
         assertEquals(100, withTimeoutOrNull(500) {
242
     }
231
     }
243
 
232
 
244
     @Test
233
     @Test
245
-    fun `IrcClientImpl defaults local nickname to profile`() = runBlocking {
234
+    fun `defaults local nickname to profile`() = runBlocking {
246
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
235
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
247
         assertEquals(NICK, client.serverState.localNickname)
236
         assertEquals(NICK, client.serverState.localNickname)
248
     }
237
     }
249
 
238
 
250
     @Test
239
     @Test
251
-    fun `IrcClientImpl defaults server name to host name`() = runBlocking {
240
+    fun `defaults server name to host name`() = runBlocking {
252
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
241
         val client = IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))
253
         assertEquals(HOST, client.serverState.serverName)
242
         assertEquals(HOST, client.serverState.serverName)
254
     }
243
     }
255
 
244
 
256
-    private suspend fun IrcClientImpl.blockUntilConnected() {
257
-        // Yuck. Maybe connect should be asynchronous?
258
-        while (serverState.status <= ServerStatus.Connecting) {
259
-            delay(50)
245
+    @Test
246
+    fun `reset clears all state`() {
247
+        with (IrcClientImpl(Server(HOST, PORT), Profile(NICK, REAL_NAME, USER_NAME))) {
248
+            userState += User("acidBurn")
249
+            channelState += ChannelState("#thegibson") { CaseMapping.Rfc }
250
+            serverState.serverName = "root.$HOST"
251
+            reset()
252
+
253
+            assertEquals(0, userState.count())
254
+            assertEquals(0, channelState.count())
255
+            assertEquals(HOST, serverState.serverName)
260
         }
256
         }
261
     }
257
     }
262
 
258
 

+ 16
- 4
src/test/kotlin/com/dmdirc/ktirc/model/CapabilitiesStateTest.kt View File

1
 package com.dmdirc.ktirc.model
1
 package com.dmdirc.ktirc.model
2
 
2
 
3
 import org.junit.jupiter.api.Assertions.assertEquals
3
 import org.junit.jupiter.api.Assertions.assertEquals
4
+import org.junit.jupiter.api.Assertions.assertTrue
4
 import org.junit.jupiter.api.Test
5
 import org.junit.jupiter.api.Test
5
 
6
 
6
 internal class CapabilitiesStateTest {
7
 internal class CapabilitiesStateTest {
7
 
8
 
8
     @Test
9
     @Test
9
-    fun `defaults negotiation state to awaiting list`() {
10
-        val capabilitiesState = CapabilitiesState()
10
+    fun `defaults negotiation state to awaiting list`() = with(CapabilitiesState()) {
11
+        assertEquals(CapabilitiesNegotiationState.AWAITING_LIST, negotiationState)
12
+    }
13
+
14
+    @Test
15
+    fun `reset clears all state`() = with(CapabilitiesState()) {
16
+        advertisedCapabilities[Capability.SaslAuthentication] = "foo"
17
+        enabledCapabilities[Capability.SaslAuthentication] = "foo"
18
+        negotiationState = CapabilitiesNegotiationState.FINISHED
19
+
20
+        reset()
11
 
21
 
12
-        assertEquals(CapabilitiesNegotiationState.AWAITING_LIST, capabilitiesState.negotiationState)
22
+        assertTrue(advertisedCapabilities.isEmpty())
23
+        assertTrue(enabledCapabilities.isEmpty())
24
+        assertEquals(CapabilitiesNegotiationState.AWAITING_LIST, negotiationState)
13
     }
25
     }
14
 
26
 
15
-}
27
+}

+ 17
- 2
src/test/kotlin/com/dmdirc/ktirc/model/ChannelStateTest.kt View File

1
 package com.dmdirc.ktirc.model
1
 package com.dmdirc.ktirc.model
2
 
2
 
3
-import org.junit.jupiter.api.Assertions.assertFalse
4
-import org.junit.jupiter.api.Assertions.assertTrue
3
+import com.dmdirc.ktirc.io.CaseMapping
4
+import org.junit.jupiter.api.Assertions.*
5
 import org.junit.jupiter.api.Test
5
 import org.junit.jupiter.api.Test
6
 
6
 
7
 internal class ChannelStateTest {
7
 internal class ChannelStateTest {
22
         assertFalse(ChannelModeType.NoParameter.needsParameterToUnset)
22
         assertFalse(ChannelModeType.NoParameter.needsParameterToUnset)
23
     }
23
     }
24
 
24
 
25
+    @Test
26
+    fun `reset resets all state`() = with(ChannelState("#thegibson") { CaseMapping.Rfc }) {
27
+        receivingUserList = true
28
+        modesDiscovered = true
29
+        modes['a'] = "b"
30
+        users += ChannelUser("acidBurn")
31
+
32
+        reset()
33
+
34
+        assertFalse(receivingUserList)
35
+        assertFalse(modesDiscovered)
36
+        assertTrue(modes.isEmpty())
37
+        assertEquals(0, users.count())
38
+    }
39
+
25
 }
40
 }

+ 14
- 2
src/test/kotlin/com/dmdirc/ktirc/model/SaslStateTest.kt View File

3
 import com.dmdirc.ktirc.sasl.SaslMechanism
3
 import com.dmdirc.ktirc.sasl.SaslMechanism
4
 import com.nhaarman.mockitokotlin2.doReturn
4
 import com.nhaarman.mockitokotlin2.doReturn
5
 import com.nhaarman.mockitokotlin2.mock
5
 import com.nhaarman.mockitokotlin2.mock
6
-import org.junit.jupiter.api.Assertions.assertEquals
7
-import org.junit.jupiter.api.Assertions.assertNull
6
+import org.junit.jupiter.api.Assertions.*
8
 import org.junit.jupiter.api.Test
7
 import org.junit.jupiter.api.Test
9
 
8
 
10
 internal class SaslStateTest {
9
 internal class SaslStateTest {
78
         assertNull(state.mechanismState)
77
         assertNull(state.mechanismState)
79
     }
78
     }
80
 
79
 
80
+    @Test
81
+    fun `reset clears all state`() = with(SaslState(mechanisms)) {
82
+        currentMechanism = mech2
83
+        mechanismState = "in progress"
84
+        saslBuffer = "abcdef"
85
+
86
+        reset()
87
+
88
+        assertNull(currentMechanism)
89
+        assertNull(mechanismState)
90
+        assertTrue(saslBuffer.isEmpty())
91
+    }
92
+
81
 }
93
 }

+ 25
- 9
src/test/kotlin/com/dmdirc/ktirc/model/ServerFeatureMapTest.kt View File

7
 internal class ServerFeatureMapTest {
7
 internal class ServerFeatureMapTest {
8
 
8
 
9
     @Test
9
     @Test
10
-    fun `ServerFeatureMap should return defaults for unspecified features`() {
10
+    fun `should return defaults for unspecified features`() {
11
         val featureMap = ServerFeatureMap()
11
         val featureMap = ServerFeatureMap()
12
         assertEquals(200, featureMap[ServerFeature.MaximumChannelNameLength])
12
         assertEquals(200, featureMap[ServerFeature.MaximumChannelNameLength])
13
     }
13
     }
14
 
14
 
15
     @Test
15
     @Test
16
-    fun `ServerFeatureMap should return null for unspecified features with no default`() {
16
+    fun `should return null for unspecified features with no default`() {
17
         val featureMap = ServerFeatureMap()
17
         val featureMap = ServerFeatureMap()
18
         assertNull(featureMap[ServerFeature.ChannelModes])
18
         assertNull(featureMap[ServerFeature.ChannelModes])
19
     }
19
     }
20
 
20
 
21
     @Test
21
     @Test
22
-    fun `ServerFeatureMap should return previously set value for features`() {
22
+    fun `should return previously set value for features`() {
23
         val featureMap = ServerFeatureMap()
23
         val featureMap = ServerFeatureMap()
24
         featureMap[ServerFeature.MaximumChannels] = 123
24
         featureMap[ServerFeature.MaximumChannels] = 123
25
         assertEquals(123, featureMap[ServerFeature.MaximumChannels])
25
         assertEquals(123, featureMap[ServerFeature.MaximumChannels])
26
     }
26
     }
27
 
27
 
28
     @Test
28
     @Test
29
-    fun `ServerFeatureMap should return default set value for features that were reset`() {
29
+    fun `should return default set value for features that were reset`() {
30
         val featureMap = ServerFeatureMap()
30
         val featureMap = ServerFeatureMap()
31
         featureMap[ServerFeature.MaximumChannels] = 123
31
         featureMap[ServerFeature.MaximumChannels] = 123
32
         featureMap.reset(ServerFeature.MaximumChannels)
32
         featureMap.reset(ServerFeature.MaximumChannels)
34
     }
34
     }
35
 
35
 
36
     @Test
36
     @Test
37
-    fun `ServerFeatureMap should throw if a feature is set with the wrong type`() {
37
+    fun `should throw if a feature is set with the wrong type`() {
38
         val featureMap = ServerFeatureMap()
38
         val featureMap = ServerFeatureMap()
39
         assertThrows(IllegalArgumentException::class.java) {
39
         assertThrows(IllegalArgumentException::class.java) {
40
             featureMap[ServerFeature.MaximumChannels] = "123"
40
             featureMap[ServerFeature.MaximumChannels] = "123"
42
     }
42
     }
43
 
43
 
44
     @Test
44
     @Test
45
-    fun `ServerFeatureMap sets all features from another map`() {
45
+    fun `sets all features from another map`() {
46
         val featureMap1 = ServerFeatureMap()
46
         val featureMap1 = ServerFeatureMap()
47
         val featureMap2 = ServerFeatureMap()
47
         val featureMap2 = ServerFeatureMap()
48
         featureMap2[ServerFeature.WhoxSupport] = true
48
         featureMap2[ServerFeature.WhoxSupport] = true
53
         assertArrayEquals(arrayOf("abc", "def"), featureMap1[ServerFeature.ChannelModes])
53
         assertArrayEquals(arrayOf("abc", "def"), featureMap1[ServerFeature.ChannelModes])
54
     }
54
     }
55
 
55
 
56
-
57
     @Test
56
     @Test
58
-    fun `ServerFeatureMap resets features reset in another map`() {
57
+    fun `resets features reset in another map`() {
59
         val featureMap1 = ServerFeatureMap()
58
         val featureMap1 = ServerFeatureMap()
60
         val featureMap2 = ServerFeatureMap()
59
         val featureMap2 = ServerFeatureMap()
61
         featureMap1[ServerFeature.ServerCaseMapping] = CaseMapping.RfcStrict
60
         featureMap1[ServerFeature.ServerCaseMapping] = CaseMapping.RfcStrict
64
 
63
 
65
         assertEquals(CaseMapping.Rfc, featureMap1[ServerFeature.ServerCaseMapping])
64
         assertEquals(CaseMapping.Rfc, featureMap1[ServerFeature.ServerCaseMapping])
66
     }
65
     }
66
+
67
+    @Test
68
+    fun `clear removes all features`() {
69
+        val featureMap = ServerFeatureMap()
70
+        featureMap[ServerFeature.MaximumChannels] = 123
71
+        featureMap[ServerFeature.Network] = "testnet"
72
+        featureMap.clear()
73
+        assertTrue(featureMap.isEmpty())
74
+    }
75
+
76
+    @Test
77
+    fun `isEmpty returns true if empty`() {
78
+        val featureMap = ServerFeatureMap()
79
+        assertTrue(featureMap.isEmpty())
80
+        featureMap[ServerFeature.MaximumChannels] = 123
81
+        assertFalse(featureMap.isEmpty())
82
+    }
67
     
83
     
68
-}
84
+}

+ 21
- 0
src/test/kotlin/com/dmdirc/ktirc/model/ServerStateTest.kt View File

58
         assertEquals(ChannelModeType.NoParameter, serverState.channelModeType('b'))
58
         assertEquals(ChannelModeType.NoParameter, serverState.channelModeType('b'))
59
     }
59
     }
60
 
60
 
61
+    @Test
62
+    fun `reset clears all state`() = with(ServerState("acidBurn", "")) {
63
+        receivedWelcome = true
64
+        status = ServerStatus.Connecting
65
+        localNickname = "acidBurn3"
66
+        serverName = "root.the.gibson"
67
+        features[ServerFeature.Network] = "gibson"
68
+        capabilities.advertisedCapabilities[Capability.SaslAuthentication] = "sure"
69
+        sasl.saslBuffer = "in progress"
70
+
71
+        reset()
72
+
73
+        assertFalse(receivedWelcome)
74
+        assertEquals(ServerStatus.Disconnected, status)
75
+        assertEquals("acidBurn", localNickname)
76
+        assertEquals("", serverName)
77
+        assertTrue(features.isEmpty())
78
+        assertTrue(capabilities.advertisedCapabilities.isEmpty())
79
+        assertEquals("", sasl.saslBuffer)
80
+    }
81
+
61
 }
82
 }
62
 
83
 
63
 internal class ModePrefixMappingTest {
84
 internal class ModePrefixMappingTest {

+ 16
- 5
src/test/kotlin/com/dmdirc/ktirc/model/UserStateTest.kt View File

9
     private val userState = UserState { CaseMapping.Rfc }
9
     private val userState = UserState { CaseMapping.Rfc }
10
 
10
 
11
     @Test
11
     @Test
12
-    fun `UserState adds and gets new users`() {
12
+    fun `adds and gets new users`() {
13
         userState += User("acidBurn", "libby", "root.localhost")
13
         userState += User("acidBurn", "libby", "root.localhost")
14
         val user = userState["acidburn"]
14
         val user = userState["acidburn"]
15
         assertNotNull(user)
15
         assertNotNull(user)
19
     }
19
     }
20
 
20
 
21
     @Test
21
     @Test
22
-    fun `UserState removes users`() {
22
+    fun `removes users`() {
23
         userState += User("acidBurn", "libby", "root.localhost")
23
         userState += User("acidBurn", "libby", "root.localhost")
24
         userState -= User("ACIDBURN")
24
         userState -= User("ACIDBURN")
25
         assertNull(userState["acidburn"])
25
         assertNull(userState["acidburn"])
26
     }
26
     }
27
 
27
 
28
     @Test
28
     @Test
29
-    fun `UserState removes users by nickname`() {
29
+    fun `removes users by nickname`() {
30
         userState += User("acidBurn", "libby", "root.localhost")
30
         userState += User("acidBurn", "libby", "root.localhost")
31
         userState -= "ACIDBURN"
31
         userState -= "ACIDBURN"
32
         assertNull(userState["acidburn"])
32
         assertNull(userState["acidburn"])
33
     }
33
     }
34
 
34
 
35
     @Test
35
     @Test
36
-    fun `UserState updates existing user with same nickname`() {
36
+    fun `updates existing user with same nickname`() {
37
         userState += User("acidBurn", "libby", "root.localhost")
37
         userState += User("acidBurn", "libby", "root.localhost")
38
         userState.update(User("acidBurn", realName = "Libby", awayMessage = "Hacking"))
38
         userState.update(User("acidBurn", realName = "Libby", awayMessage = "Hacking"))
39
 
39
 
46
     }
46
     }
47
 
47
 
48
     @Test
48
     @Test
49
-    fun `UserState updates existing user with new nickname`() {
49
+    fun `updates existing user with new nickname`() {
50
         userState += User("acidBurn", "libby", "root.localhost")
50
         userState += User("acidBurn", "libby", "root.localhost")
51
         userState.update(User("acidBurn2", realName = "Libby", awayMessage = "Hacking"), "acidBurn")
51
         userState.update(User("acidBurn2", realName = "Libby", awayMessage = "Hacking"), "acidBurn")
52
 
52
 
95
         assertNotNull(userState["zeroCool"])
95
         assertNotNull(userState["zeroCool"])
96
     }
96
     }
97
 
97
 
98
+    @Test
99
+    fun `reset clears all state`() {
100
+        userState += User("acidBurn", "libby", "root.localhost")
101
+        userState += User("zeroCool", "dade", "root.localhost")
102
+        userState += User("acidBurn2", "libby", "root.localhost")
103
+
104
+        userState.reset()
105
+
106
+        assertEquals(0, userState.count())
107
+    }
108
+
98
 }
109
 }

Loading…
Cancel
Save