@@ -19,6 +19,7 @@ import org.junit.jupiter.api.Nested
1919import org.junit.jupiter.api.Test
2020import org.mockito.Mock
2121import org.mockito.MockitoAnnotations
22+ import org.phoenixframework.utilities.copyAndRemove
2223import java.net.URL
2324import java.util.concurrent.TimeUnit
2425
@@ -397,7 +398,6 @@ class SocketTest {
397398 channel1.join().trigger(" ok" , emptyMap())
398399 channel2.join().trigger(" ok" , emptyMap())
399400
400-
401401 var chan1Called = false
402402 channel1.onError { chan1Called = true }
403403
@@ -756,8 +756,8 @@ class SocketTest {
756756 val spy = spy(channel)
757757
758758 // Use the spy instance instead of the Channel instance
759- socket.channels.remove (channel)
760- socket.channels.add (spy)
759+ socket.channels = socket.channels.copyAndRemove (channel)
760+ socket.channels = socket.channels.copyAndAdd (spy)
761761
762762 spy.join()
763763 assertThat(spy.state).isEqualTo(Channel .State .JOINING )
@@ -772,8 +772,8 @@ class SocketTest {
772772 val spy = spy(channel)
773773
774774 // Use the spy instance instead of the Channel instance
775- socket.channels.remove (channel)
776- socket.channels.add (spy)
775+ socket.channels = socket.channels.copyAndRemove (channel)
776+ socket.channels = socket.channels.copyAndAdd (spy)
777777
778778 spy.join().trigger(" ok" , emptyMap())
779779
@@ -789,8 +789,8 @@ class SocketTest {
789789 val spy = spy(channel)
790790
791791 // Use the spy instance instead of the Channel instance
792- socket.channels.remove (channel)
793- socket.channels.add (spy)
792+ socket.channels = socket.channels.copyAndRemove (channel)
793+ socket.channels = socket.channels.copyAndAdd (spy)
794794
795795 spy.join().trigger(" ok" , emptyMap())
796796 spy.leave()
@@ -828,8 +828,8 @@ class SocketTest {
828828 val spy = spy(channel)
829829
830830 // Use the spy instance instead of the Channel instance
831- socket.channels.remove (channel)
832- socket.channels.add (spy)
831+ socket.channels = socket.channels.copyAndRemove (channel)
832+ socket.channels = socket.channels.copyAndAdd (spy)
833833
834834 spy.join()
835835 assertThat(spy.state).isEqualTo(Channel .State .JOINING )
@@ -844,8 +844,8 @@ class SocketTest {
844844 val spy = spy(channel)
845845
846846 // Use the spy instance instead of the Channel instance
847- socket.channels.remove (channel)
848- socket.channels.add (spy)
847+ socket.channels = socket.channels.copyAndRemove (channel)
848+ socket.channels = socket.channels.copyAndAdd (spy)
849849
850850 spy.join().trigger(" ok" , emptyMap())
851851
@@ -861,8 +861,8 @@ class SocketTest {
861861 val spy = spy(channel)
862862
863863 // Use the spy instance instead of the Channel instance
864- socket.channels.remove (channel)
865- socket.channels.add (spy)
864+ socket.channels = socket.channels.copyAndRemove (channel)
865+ socket.channels = socket.channels.copyAndAdd (spy)
866866
867867 spy.join().trigger(" ok" , emptyMap())
868868 spy.leave()
@@ -886,8 +886,8 @@ class SocketTest {
886886 val otherChannel = mock<Channel >()
887887 whenever(otherChannel.isMember(any())).thenReturn(false )
888888
889- socket.channels.add (targetChannel)
890- socket.channels.add (otherChannel)
889+ socket.channels = socket.channels.copyAndAdd (targetChannel)
890+ socket.channels = socket.channels.copyAndRemove (otherChannel)
891891
892892 val rawMessage =
893893 " {\" topic\" :\" topic\" ,\" event\" :\" event\" ,\" payload\" :{\" one\" :\" two\" },\" status\" :\" ok\" }"
@@ -923,4 +923,97 @@ class SocketTest {
923923 /* End OnConnectionMessage */
924924 }
925925
926+
927+ @Nested
928+ @DisplayName(" ConcurrentModificationException" )
929+ inner class ConcurrentModificationExceptionTests {
930+
931+ @Test
932+ internal fun `onOpen does not throw` () {
933+ var oneCalled = 0
934+ var twoCalled = 0
935+ socket.onOpen {
936+ socket.onOpen { twoCalled + = 1 }
937+ oneCalled + = 1
938+ }
939+
940+ socket.onConnectionOpened()
941+ assertThat(oneCalled).isEqualTo(1 )
942+ assertThat(twoCalled).isEqualTo(0 )
943+
944+ socket.onConnectionOpened()
945+ assertThat(oneCalled).isEqualTo(2 )
946+ assertThat(twoCalled).isEqualTo(1 )
947+ }
948+
949+ @Test
950+ internal fun `onClose does not throw` () {
951+ var oneCalled = 0
952+ var twoCalled = 0
953+ socket.onClose {
954+ socket.onClose { twoCalled + = 1 }
955+ oneCalled + = 1
956+ }
957+
958+ socket.onConnectionClosed(1000 )
959+ assertThat(oneCalled).isEqualTo(1 )
960+ assertThat(twoCalled).isEqualTo(0 )
961+
962+ socket.onConnectionClosed(1001 )
963+ assertThat(oneCalled).isEqualTo(2 )
964+ assertThat(twoCalled).isEqualTo(1 )
965+ }
966+
967+ @Test
968+ internal fun `onError does not throw` () {
969+ var oneCalled = 0
970+ var twoCalled = 0
971+ socket.onError { _, _->
972+ socket.onError { _, _ -> twoCalled + = 1 }
973+ oneCalled + = 1
974+ }
975+
976+ socket.onConnectionError(Throwable (), null )
977+ assertThat(oneCalled).isEqualTo(1 )
978+ assertThat(twoCalled).isEqualTo(0 )
979+
980+ socket.onConnectionError(Throwable (), null )
981+ assertThat(oneCalled).isEqualTo(2 )
982+ assertThat(twoCalled).isEqualTo(1 )
983+ }
984+
985+ @Test
986+ internal fun `onMessage does not throw` () {
987+ var oneCalled = 0
988+ var twoCalled = 0
989+ socket.onMessage {
990+ socket.onMessage { twoCalled + = 1 }
991+ oneCalled + = 1
992+ }
993+
994+ socket.onConnectionMessage(" {\" status\" :\" ok\" }" )
995+ assertThat(oneCalled).isEqualTo(1 )
996+ assertThat(twoCalled).isEqualTo(0 )
997+
998+ socket.onConnectionMessage(" {\" status\" :\" ok\" }" )
999+ assertThat(oneCalled).isEqualTo(2 )
1000+ assertThat(twoCalled).isEqualTo(1 )
1001+ }
1002+
1003+ @Test
1004+ internal fun `does not throw when adding channel` () {
1005+ var oneCalled = 0
1006+ socket.onOpen {
1007+ val channel = socket.channel(" foo" )
1008+ oneCalled + = 1
1009+ }
1010+
1011+ socket.onConnectionOpened()
1012+ assertThat(oneCalled).isEqualTo(1 )
1013+ }
1014+
1015+ /* End ConcurrentModificationExceptionTests */
1016+ }
1017+
1018+
9261019}
0 commit comments