@@ -386,6 +386,41 @@ class SocketTest {
386386 assertThat(socket.channels).contains(channel2)
387387 }
388388
389+ @Test
390+ internal fun `does not throw exception when iterating over channels` () {
391+ val channel1 = socket.channel(" topic-1" )
392+ val channel2 = socket.channel(" topic-2" )
393+
394+ channel1.joinPush.ref = " 1"
395+ channel2.joinPush.ref = " 2"
396+
397+ channel1.join().trigger(" ok" , emptyMap())
398+ channel2.join().trigger(" ok" , emptyMap())
399+
400+
401+ var chan1Called = false
402+ channel1.onError { chan1Called = true }
403+
404+ var chan2Called = false
405+ channel2.onError {
406+ chan2Called = true
407+ socket.remove(channel2)
408+ }
409+
410+ // This will trigger an iteration over the socket.channels list which will trigger
411+ // channel2.onError. That callback will attempt to remove channel2 during iteration
412+ // which would throw a ConcurrentModificationException if the socket.remove method
413+ // is implemented incorrectly.
414+ socket.onConnectionError(IllegalStateException (), null )
415+
416+ // Assert that both on all error's got called even when a channel was removed
417+ assertThat(chan1Called).isTrue()
418+ assertThat(chan2Called).isTrue()
419+
420+ assertThat(socket.channels).doesNotContain(channel2)
421+ assertThat(socket.channels).contains(channel1)
422+ }
423+
389424 /* End Remove */
390425 }
391426
0 commit comments