|
| 1 | +import asyncio |
1 | 2 | import unittest |
2 | 3 | import _thread |
3 | 4 | import time |
|
7 | 8 |
|
8 | 9 | from websocket import WebSocketApp |
9 | 10 |
|
10 | | -class TestWebsocket(unittest.TestCase): |
| 11 | +class TestMutlipleWebsocketsAsyncio(unittest.TestCase): |
11 | 12 |
|
12 | | - ws = None |
| 13 | + max_allowed_connections = 3 # max that echo.websocket.org allows |
13 | 14 |
|
14 | | - on_open_called = None |
15 | | - on_message_called = None |
16 | | - on_ping_called = None |
17 | | - on_close_called = None |
| 15 | + #relays = ["wss://echo.websocket.org", "wss://echo.websocket.org", "wss://echo.websocket.org" ] # more gives "too many requests" error |
| 16 | + relays = ["wss://echo.websocket.org", "wss://echo.websocket.org", "wss://echo.websocket.org", "wss://echo.websocket.org", "wss://echo.websocket.org" ] # more might give "too many requests" error |
| 17 | + wslist = [] |
| 18 | + |
| 19 | + on_open_called = 0 |
| 20 | + on_message_called = 0 |
| 21 | + on_ping_called = 0 |
| 22 | + on_close_called = 0 |
| 23 | + on_error_called = 0 |
18 | 24 |
|
19 | 25 | def on_message(self, wsapp, message: str): |
20 | 26 | print(f"on_message received: {message}") |
21 | 27 | self.on_message_called = True |
22 | 28 |
|
23 | 29 | def on_open(self, wsapp): |
24 | 30 | print(f"on_open called: {wsapp}") |
25 | | - self.on_open_called = True |
26 | | - self.ws.send('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker_batch"]}') |
| 31 | + self.on_open_called += 1 |
| 32 | + #wsapp.send('{"type": "subscribe","product_ids": ["BTC-USD"],"channels": ["ticker_batch"]}') |
27 | 33 |
|
28 | 34 | def on_ping(wsapp, message): |
29 | 35 | print("Got a ping!") |
30 | 36 | self.on_ping_called = True |
31 | 37 |
|
32 | 38 | def on_close(self, wsapp, close_status_code, close_msg): |
33 | 39 | print(f"on_close called: {wsapp}") |
34 | | - self.on_close_called = True |
| 40 | + self.on_close_called += 1 |
35 | 41 |
|
36 | | - def websocket_thread(self): |
37 | | - wsurl = "wss://ws-feed.exchange.coinbase.com" |
| 42 | + def on_error(self, wsapp, arg1): |
| 43 | + print(f"on_error called: {wsapp}, {arg1}") |
| 44 | + self.on_error_called += 1 |
38 | 45 |
|
39 | | - self.ws = WebSocketApp( |
40 | | - wsurl, |
41 | | - on_open=self.on_open, |
42 | | - on_close=self.on_close, |
43 | | - on_message=self.on_message, |
44 | | - on_ping=self.on_ping |
45 | | - ) # maybe add other callbacks to reconnect when disconnected etc. |
46 | | - self.ws.run_forever() |
| 46 | + async def closeall(self): |
| 47 | + await asyncio.sleep(1) |
47 | 48 |
|
48 | | - def wait_for_ping(self): |
49 | | - self.on_ping_called = False |
50 | | - for _ in range(60): |
51 | | - print("Waiting for on_ping to be called...") |
52 | | - if self.on_ping_called: |
53 | | - print("yes, it was called!") |
54 | | - break |
55 | | - time.sleep(1) |
56 | | - self.assertTrue(self.on_ping_called) |
| 49 | + self.on_close_called = 0 |
| 50 | + print("disconnecting...") |
| 51 | + for ws in self.wslist: |
| 52 | + await ws.close() |
57 | 53 |
|
58 | | - def test_it(self): |
59 | | - on_open_called = False |
60 | | - _thread.stack_size(mpos.apps.good_stack_size()) |
61 | | - _thread.start_new_thread(self.websocket_thread, ()) |
62 | | - |
63 | | - self.on_open_called = False |
64 | | - self.on_message_called = False # message might be received very quickly, before we expect it |
65 | | - for _ in range(5): |
| 54 | + async def main(self) -> None: |
| 55 | + tasks = [] |
| 56 | + self.wslist = [] |
| 57 | + for idx, wsurl in enumerate(self.relays): |
| 58 | + print(f"creating WebSocketApp for {wsurl}") |
| 59 | + ws = WebSocketApp( |
| 60 | + wsurl, |
| 61 | + on_open=self.on_open, |
| 62 | + on_close=self.on_close, |
| 63 | + on_message=self.on_message, |
| 64 | + on_ping=self.on_ping, |
| 65 | + on_error=self.on_error |
| 66 | + ) |
| 67 | + print(f"creating task for {wsurl}") |
| 68 | + tasks.append(asyncio.create_task(ws.run_forever(),)) |
| 69 | + print(f"created task for {wsurl}") |
| 70 | + self.wslist.append(ws) |
| 71 | + |
| 72 | + print(f"Starting {len(tasks)} concurrent WebSocket connections…") |
| 73 | + await asyncio.sleep(2) |
| 74 | + await self.closeall() |
| 75 | + |
| 76 | + for _ in range(10): |
66 | 77 | print("Waiting for on_open to be called...") |
67 | | - if self.on_open_called: |
| 78 | + if self.on_open_called == min(len(self.relays),self.max_allowed_connections): |
68 | 79 | print("yes, it was called!") |
69 | 80 | break |
70 | | - time.sleep(1) |
71 | | - self.assertTrue(self.on_open_called) |
| 81 | + await asyncio.sleep(1) |
| 82 | + self.assertTrue(self.on_open_called == min(len(self.relays),self.max_allowed_connections)) |
72 | 83 |
|
73 | | - self.on_message_called = False # message might be received very quickly, before we expect it |
74 | | - for _ in range(5): |
75 | | - print("Waiting for on_message to be called...") |
76 | | - if self.on_message_called: |
| 84 | + for _ in range(10): |
| 85 | + print("Waiting for on_close to be called...") |
| 86 | + if self.on_close_called == min(len(self.relays),self.max_allowed_connections): |
77 | 87 | print("yes, it was called!") |
78 | 88 | break |
79 | | - time.sleep(1) |
80 | | - self.assertTrue(self.on_message_called) |
| 89 | + await asyncio.sleep(1) |
| 90 | + self.assertTrue(self.on_close_called == min(len(self.relays),self.max_allowed_connections)) |
81 | 91 |
|
82 | | - # Disabled because not all servers send pings: |
83 | | - # self.wait_for_ping() |
| 92 | + self.assertTrue(self.on_error_called == min(len(self.relays),self.max_allowed_connections)) |
84 | 93 |
|
85 | | - self.on_close_called = False |
86 | | - self.ws.close() |
87 | | - for _ in range(5): |
88 | | - print("Waiting for on_close to be called...") |
89 | | - if self.on_close_called: |
| 94 | + # Wait for *all* of them to finish (or be cancelled) |
| 95 | + # If this hangs, it's also a failure: |
| 96 | + await asyncio.gather(*tasks, return_exceptions=True) |
| 97 | + |
| 98 | + def wait_for_ping(self): |
| 99 | + self.on_ping_called = False |
| 100 | + for _ in range(60): |
| 101 | + print("Waiting for on_ping to be called...") |
| 102 | + if self.on_ping_called: |
90 | 103 | print("yes, it was called!") |
91 | 104 | break |
92 | 105 | time.sleep(1) |
93 | | - self.assertTrue(self.on_close_called) |
| 106 | + self.assertTrue(self.on_ping_called) |
| 107 | + |
| 108 | + def test_it_loop(self): |
| 109 | + for testnr in range(1): |
| 110 | + print(f"starting iteration {testnr}") |
| 111 | + asyncio.run(self.do_two()) |
| 112 | + print(f"finished iteration {testnr}") |
| 113 | + |
| 114 | + def do_two(self): |
| 115 | + await self.main() |
| 116 | + |
0 commit comments