From cae4dc3436a6a05fcc8e9d67e058b4a75cd7e9d7 Mon Sep 17 00:00:00 2001 From: otariidae Date: Tue, 25 Oct 2022 00:32:17 +0900 Subject: [PATCH 1/5] test connect_sender and connect_receiver --- .../test_one_to_multiple_cast_skyway.py | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 backend/multicaster/test_one_to_multiple_cast_skyway.py diff --git a/backend/multicaster/test_one_to_multiple_cast_skyway.py b/backend/multicaster/test_one_to_multiple_cast_skyway.py new file mode 100644 index 00000000..361a5c1d --- /dev/null +++ b/backend/multicaster/test_one_to_multiple_cast_skyway.py @@ -0,0 +1,159 @@ +import asyncio +import json +from typing import cast +from unittest import IsolatedAsyncioTestCase +from unittest.mock import patch +from websockets.server import serve, WebSocketServer +from websockets.client import connect, WebSocketClientProtocol +from one_to_multiple_cast_skyway import ( + handler, + ConnectSenderMessage, + ConnectReceiverMessage, + RemoteAddress, +) + +DUMMY_SENDER_TOKEN = "abcdefg" +WAIT_STATE_CHANGE_SEC = 0.5 + + +class OneToMultipleCastSkywayTest(IsolatedAsyncioTestCase): + server: WebSocketServer + ws_uri: str + + async def asyncSetUp(self) -> None: + self.server = await serve(handler, "127.0.0.1", 0) + addr = cast(RemoteAddress, next(iter(self.server.sockets)).getsockname()) + if addr is None: + raise Exception("cannot get server host and port") + host, port = addr + self.ws_uri = f"ws://{host}:{port}/" + + async def asyncTearDown(self) -> None: + self.server.close() + await self.server.wait_closed() + + @patch("one_to_multiple_cast_skyway.SENDER_TOKEN", DUMMY_SENDER_TOKEN) + async def test_connect_sender_with_invalid_sender_token(self) -> None: + from one_to_multiple_cast_skyway import rooms + + DUMMY_ROOM_ID = "A101" + DUMMY_SKYWAY_ROOM_ID = "B101" + DUMMY_PEER_ID = "you-write-code-slowly-because-you-don't-write-test" + async with connect(self.ws_uri) as sender_socket: + await sender_socket.send( + json.dumps( + ConnectSenderMessage( + msg_type="connect_sender", + room_id=DUMMY_ROOM_ID, + skyway_room_id=DUMMY_SKYWAY_ROOM_ID, + peer_id=DUMMY_PEER_ID, + sender_token="invalid_token", + ) + ) + ) + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + self.assertIn(DUMMY_ROOM_ID, rooms) + self.assertIsNone(rooms[DUMMY_ROOM_ID]["sender_socket"]) + self.assertEqual(len(rooms[DUMMY_ROOM_ID]["connections"]), 0) + self.assertEqual( + rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0 + ) + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + self.assertNotIn(DUMMY_ROOM_ID, rooms) + + @patch("one_to_multiple_cast_skyway.SENDER_TOKEN", DUMMY_SENDER_TOKEN) + async def test_connect_sender_with_valid_sender_token(self) -> None: + from one_to_multiple_cast_skyway import rooms + + DUMMY_ROOM_ID = "A102" + DUMMY_SKYWAY_ROOM_ID = "B102" + DUMMY_PEER_ID = "you-would-go-slow-if-you-don't-write-test" + async with connect(self.ws_uri) as sender_socket: + await sender_socket.send( + json.dumps( + ConnectSenderMessage( + msg_type="connect_sender", + room_id=DUMMY_ROOM_ID, + skyway_room_id=DUMMY_SKYWAY_ROOM_ID, + peer_id=DUMMY_PEER_ID, + sender_token=DUMMY_SENDER_TOKEN, + ) + ) + ) + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + self.assertIn(DUMMY_ROOM_ID, rooms) + self.assertIsNotNone(rooms[DUMMY_ROOM_ID]["sender_socket"]) + self.assertEqual( + rooms[DUMMY_ROOM_ID]["skyway_room_id"], DUMMY_SKYWAY_ROOM_ID + ) + self.assertEqual(rooms[DUMMY_ROOM_ID]["peer_id"], DUMMY_PEER_ID) + + @patch("one_to_multiple_cast_skyway.SENDER_TOKEN", DUMMY_SENDER_TOKEN) + async def test_connect_receiver(self) -> None: + from one_to_multiple_cast_skyway import rooms + + DUMMY_ROOM_ID = "A201" + async with connect(self.ws_uri) as receiver_socket: + await receiver_socket.send( + json.dumps( + ConnectReceiverMessage( + msg_type="connect_receiver", + room_id=DUMMY_ROOM_ID, + ) + ) + ) + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + self.assertIn(DUMMY_ROOM_ID, rooms) + self.assertIsNone(rooms[DUMMY_ROOM_ID]["sender_socket"]) + self.assertEqual(len(rooms[DUMMY_ROOM_ID]["connections"]), 1) + self.assertEqual( + rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0 + ) + + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + self.assertNotIn(DUMMY_ROOM_ID, rooms) + + @patch("one_to_multiple_cast_skyway.SENDER_TOKEN", DUMMY_SENDER_TOKEN) + async def test_connect_multiple_receiver(self) -> None: + from one_to_multiple_cast_skyway import rooms + + DUMMY_ROOM_ID = "B202" + CONNECTIONS_LEN = 10 + receiver_sockets: set[WebSocketClientProtocol] = set() + for i in range(CONNECTIONS_LEN): + receiver_socket = await connect(self.ws_uri) + receiver_sockets.add(receiver_socket) + await receiver_socket.send( + json.dumps( + ConnectReceiverMessage( + msg_type="connect_receiver", + room_id=DUMMY_ROOM_ID, + ) + ) + ) + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + self.assertIn(DUMMY_ROOM_ID, rooms) + self.assertIsNone(rooms[DUMMY_ROOM_ID]["sender_socket"]) + connections_len_expected = i + 1 + self.assertEqual( + len(rooms[DUMMY_ROOM_ID]["connections"]), connections_len_expected + ) + self.assertEqual( + rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0 + ) + + self.assertEqual(len(rooms[DUMMY_ROOM_ID]["connections"]), CONNECTIONS_LEN) + + for i, receiver_socket in enumerate(receiver_sockets, start=1): + await receiver_socket.close() + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + connections_len_expected = CONNECTIONS_LEN - i + if connections_len_expected != 0: + self.assertEqual( + len(rooms[DUMMY_ROOM_ID]["connections"]), connections_len_expected + ) + self.assertEqual( + rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0 + ) + else: + self.assertNotIn(DUMMY_ROOM_ID, rooms) From 8e1c681ccb249f6ad316f376aa65c1f3edc3c343 Mon Sep 17 00:00:00 2001 From: otariidae Date: Tue, 25 Oct 2022 00:59:58 +0900 Subject: [PATCH 2/5] run unittest on GitHub Actions --- .github/workflows/test-multicaster.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-multicaster.yml b/.github/workflows/test-multicaster.yml index 70473986..ea4a32d7 100644 --- a/.github/workflows/test-multicaster.yml +++ b/.github/workflows/test-multicaster.yml @@ -20,3 +20,5 @@ jobs: - run: pip install -r requirements-dev.txt - name: Type check run: mypy . + - name: Test + run: python -m unittest From ce523100a199cae5445402993f7f7bee1399877b Mon Sep 17 00:00:00 2001 From: otariidae Date: Tue, 25 Oct 2022 01:06:15 +0900 Subject: [PATCH 3/5] delete room_id in place --- .../multicaster/one_to_multiple_cast_skyway.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/backend/multicaster/one_to_multiple_cast_skyway.py b/backend/multicaster/one_to_multiple_cast_skyway.py index 74fb635e..9c4ba97e 100644 --- a/backend/multicaster/one_to_multiple_cast_skyway.py +++ b/backend/multicaster/one_to_multiple_cast_skyway.py @@ -211,13 +211,19 @@ async def handler(websocket: WebSocketServerProtocol, path: str) -> None: room["sender_socket"] = None room["peer_id"] = None - async with lock: - rooms = { - room_id: room - for room_id, room in rooms.items() - if len(room["connections"]) != 0 - } # ルームに人がいなくなったら削除 + # ただしroomsに再代入すると + # from one_to_multiple_cast_skyway import rooms + # してる別の個所が壊れることがあるので + # in placeな操作をするためにdel 文を使う + room_ids_to_delete = set() + for room_id in rooms.keys(): + room = rooms[room_id] + if len(room["connections"]) == 0: + room_ids_to_delete.add(room_id) + async with lock: + for room_id in room_ids_to_delete: + del rooms[room_id] async with lock: connections.remove(websocket) From de53fe4876a4700d6812866fdf8467a71bbf26f1 Mon Sep 17 00:00:00 2001 From: otariidae Date: Tue, 25 Oct 2022 01:21:53 +0900 Subject: [PATCH 4/5] improve tests --- .../multicaster/test_one_to_multiple_cast_skyway.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/multicaster/test_one_to_multiple_cast_skyway.py b/backend/multicaster/test_one_to_multiple_cast_skyway.py index 361a5c1d..7574a23c 100644 --- a/backend/multicaster/test_one_to_multiple_cast_skyway.py +++ b/backend/multicaster/test_one_to_multiple_cast_skyway.py @@ -52,12 +52,7 @@ async def test_connect_sender_with_invalid_sender_token(self) -> None: ) ) await asyncio.sleep(WAIT_STATE_CHANGE_SEC) - self.assertIn(DUMMY_ROOM_ID, rooms) - self.assertIsNone(rooms[DUMMY_ROOM_ID]["sender_socket"]) - self.assertEqual(len(rooms[DUMMY_ROOM_ID]["connections"]), 0) - self.assertEqual( - rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0 - ) + self.assertNotIn(DUMMY_ROOM_ID, rooms) await asyncio.sleep(WAIT_STATE_CHANGE_SEC) self.assertNotIn(DUMMY_ROOM_ID, rooms) @@ -83,10 +78,14 @@ async def test_connect_sender_with_valid_sender_token(self) -> None: await asyncio.sleep(WAIT_STATE_CHANGE_SEC) self.assertIn(DUMMY_ROOM_ID, rooms) self.assertIsNotNone(rooms[DUMMY_ROOM_ID]["sender_socket"]) + self.assertEqual(len(rooms[DUMMY_ROOM_ID]["connections"]), 1) + self.assertEqual(rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0) self.assertEqual( rooms[DUMMY_ROOM_ID]["skyway_room_id"], DUMMY_SKYWAY_ROOM_ID ) self.assertEqual(rooms[DUMMY_ROOM_ID]["peer_id"], DUMMY_PEER_ID) + await asyncio.sleep(WAIT_STATE_CHANGE_SEC) + self.assertNotIn(DUMMY_ROOM_ID, rooms) @patch("one_to_multiple_cast_skyway.SENDER_TOKEN", DUMMY_SENDER_TOKEN) async def test_connect_receiver(self) -> None: From ccf4b097c6c589d58b112d7455dc76d3f439885b Mon Sep 17 00:00:00 2001 From: format BOT Date: Wed, 2 Nov 2022 14:04:10 +0000 Subject: [PATCH 5/5] format by black --- backend/multicaster/test_one_to_multiple_cast_skyway.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/multicaster/test_one_to_multiple_cast_skyway.py b/backend/multicaster/test_one_to_multiple_cast_skyway.py index 7574a23c..dbd12da2 100644 --- a/backend/multicaster/test_one_to_multiple_cast_skyway.py +++ b/backend/multicaster/test_one_to_multiple_cast_skyway.py @@ -79,7 +79,9 @@ async def test_connect_sender_with_valid_sender_token(self) -> None: self.assertIn(DUMMY_ROOM_ID, rooms) self.assertIsNotNone(rooms[DUMMY_ROOM_ID]["sender_socket"]) self.assertEqual(len(rooms[DUMMY_ROOM_ID]["connections"]), 1) - self.assertEqual(rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0) + self.assertEqual( + rooms[DUMMY_ROOM_ID]["cumulative_activated_connect_num"], 0 + ) self.assertEqual( rooms[DUMMY_ROOM_ID]["skyway_room_id"], DUMMY_SKYWAY_ROOM_ID )