Skip to content

Commit 16ffed7

Browse files
Rework autobahn tests (aio-libs#12173)
1 parent b420a45 commit 16ffed7

8 files changed

Lines changed: 249 additions & 125 deletions

File tree

.github/workflows/ci-cd.yml

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ jobs:
237237
PIP_USER: 1
238238
run: >-
239239
PATH="${HOME}/Library/Python/3.11/bin:${HOME}/.local/bin:${PATH}"
240-
pytest --junitxml=junit.xml
240+
pytest --junitxml=junit.xml -m 'not dev_mode and not autobahn'
241241
shell: bash
242242
- name: Re-run the failing tests with maximum verbosity
243243
if: failure()
@@ -280,6 +280,98 @@ jobs:
280280
with:
281281
token: ${{ secrets.CODECOV_TOKEN }}
282282

283+
autobahn:
284+
permissions:
285+
contents: read # to fetch code (actions/checkout)
286+
287+
name: Autobahn testsuite
288+
needs: gen_llhttp
289+
strategy:
290+
matrix:
291+
pyver: ['3.14']
292+
no-extensions: ['']
293+
os: [ubuntu]
294+
fail-fast: true
295+
runs-on: ${{ matrix.os }}-latest
296+
steps:
297+
- name: Checkout
298+
uses: actions/checkout@v6
299+
with:
300+
submodules: true
301+
- name: Setup Python ${{ matrix.pyver }}
302+
id: python-install
303+
uses: actions/setup-python@v6
304+
with:
305+
allow-prereleases: true
306+
python-version: ${{ matrix.pyver }}
307+
- name: Get pip cache dir
308+
id: pip-cache
309+
run: |
310+
echo "dir=$(pip cache dir)" >> "${GITHUB_OUTPUT}"
311+
shell: bash
312+
- name: Cache PyPI
313+
uses: actions/cache@v5.0.3
314+
with:
315+
key: pip-ci-${{ runner.os }}-${{ matrix.pyver }}-${{ matrix.no-extensions }}-${{ hashFiles('requirements/*.txt') }}
316+
path: ${{ steps.pip-cache.outputs.dir }}
317+
restore-keys: |
318+
pip-ci-${{ runner.os }}-${{ matrix.pyver }}-${{ matrix.no-extensions }}-
319+
- name: Update pip, wheel, setuptools, build, twine
320+
run: |
321+
python -m pip install -U pip wheel setuptools build twine
322+
- name: Install dependencies
323+
env:
324+
DEPENDENCY_GROUP: test${{ endsWith(matrix.pyver, 't') && '-ft' || '' }}
325+
run: |
326+
python -Im pip install -r requirements/${{ env.DEPENDENCY_GROUP }}.in -c requirements/${{ env.DEPENDENCY_GROUP }}.txt
327+
- name: Restore llhttp generated files
328+
if: ${{ matrix.no-extensions == '' }}
329+
uses: actions/download-artifact@v8
330+
with:
331+
name: llhttp
332+
path: vendor/llhttp/build/
333+
- name: Cythonize
334+
if: ${{ matrix.no-extensions == '' }}
335+
run: |
336+
make cythonize
337+
- name: Install self
338+
env:
339+
AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }}
340+
run: python -m pip install -e .
341+
- name: Run unittests
342+
env:
343+
COLOR: yes
344+
AIOHTTP_NO_EXTENSIONS: ${{ matrix.no-extensions }}
345+
PIP_USER: 1
346+
run: >-
347+
PATH="${HOME}/Library/Python/3.11/bin:${HOME}/.local/bin:${PATH}"
348+
pytest --junitxml=junit.xml --numprocesses=0 -m autobahn
349+
shell: bash
350+
- name: Turn coverage into xml
351+
env:
352+
COLOR: 'yes'
353+
PIP_USER: 1
354+
run: |
355+
python -m coverage xml
356+
- name: Upload coverage
357+
uses: codecov/codecov-action@v5
358+
with:
359+
files: ./coverage.xml
360+
flags: >-
361+
CI-GHA,OS-${{
362+
runner.os
363+
}},VM-${{
364+
matrix.os
365+
}},Py-${{
366+
steps.python-install.outputs.python-version
367+
}}
368+
token: ${{ secrets.CODECOV_TOKEN }}
369+
- name: Upload test results to Codecov
370+
if: ${{ !cancelled() }}
371+
uses: codecov/test-results-action@v1
372+
with:
373+
token: ${{ secrets.CODECOV_TOKEN }}
374+
283375
benchmark:
284376
name: Benchmark
285377
needs:
@@ -330,20 +422,21 @@ jobs:
330422
needs:
331423
- lint
332424
- test
425+
- autobahn
333426

334427
runs-on: ubuntu-latest
335428

336429
steps:
430+
- name: Decide whether the needed jobs succeeded or failed
431+
uses: re-actors/alls-green@release/v1
432+
with:
433+
jobs: ${{ toJSON(needs) }}
337434
- name: Trigger codecov notification
338435
uses: codecov/codecov-action@v5
339436
with:
340437
token: ${{ secrets.CODECOV_TOKEN }}
341438
fail_ci_if_error: true
342439
run_command: send-notifications
343-
- name: Decide whether the needed jobs succeeded or failed
344-
uses: re-actors/alls-green@release/v1
345-
with:
346-
jobs: ${{ toJSON(needs) }}
347440

348441
pre-deploy:
349442
name: Pre-Deploy

CHANGES/12173.contrib.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed and reworked ``autobahn`` tests -- by :user:`Dreamsorcerer`.

setup.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ addopts =
6161
--cov=aiohttp
6262
--cov=tests/
6363

64-
# run tests that are not marked with dev_mode
65-
-m "not dev_mode"
64+
-m "not dev_mode and not autobahn and not internal"
6665
filterwarnings =
6766
error
6867
ignore:module 'ssl' has no attribute 'OP_NO_COMPRESSION'. The Python interpreter is compiled against OpenSSL < 1.0.0. Ref. https.//docs.python.org/3/library/ssl.html#ssl.OP_NO_COMPRESSION:UserWarning
@@ -95,6 +94,7 @@ minversion = 3.8.2
9594
testpaths = tests/
9695
xfail_strict = true
9796
markers =
97+
autobahn: Autobahn testsuite. Should be run as a separate job.
9898
dev_mode: mark test to run in dev mode.
9999
internal: tests which may cause issues for packagers, but should be run in aiohttp's CI.
100100
skip_blockbuster: mark test to skip the blockbuster fixture.

tests/autobahn/client/client.py

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,31 @@
22

33
import asyncio
44

5-
import aiohttp
5+
from aiohttp import ClientSession, WSMsgType
66

77

88
async def client(url: str, name: str) -> None:
9-
async with aiohttp.ClientSession() as session:
10-
async with session.ws_connect(url + "/getCaseCount") as ws:
9+
async with ClientSession(base_url=url) as session:
10+
async with session.ws_connect("/getCaseCount") as ws:
1111
msg = await ws.receive()
12-
assert msg.type is aiohttp.WSMsgType.TEXT
12+
assert msg.type is WSMsgType.TEXT
1313
num_tests = int(msg.data)
14-
print("running %d cases" % num_tests)
1514

1615
for i in range(1, num_tests + 1):
17-
print("running test case:", i)
18-
text_url = url + "/runCase?case=%d&agent=%s" % (i, name)
19-
async with session.ws_connect(text_url) as ws:
16+
async with session.ws_connect(
17+
"/runCase", params={"case": i, "agent": name}
18+
) as ws:
2019
async for msg in ws:
21-
if msg.type is aiohttp.WSMsgType.TEXT:
20+
if msg.type is WSMsgType.TEXT:
2221
await ws.send_str(msg.data)
23-
elif msg.type is aiohttp.WSMsgType.BINARY:
22+
elif msg.type is WSMsgType.BINARY:
2423
await ws.send_bytes(msg.data)
2524
else:
2625
break
2726

28-
url = url + "/updateReports?agent=%s" % name
29-
async with session.ws_connect(url) as ws:
30-
print("finally requesting %s" % url)
27+
async with session.ws_connect("/updateReports", params={"agent": name}) as ws:
28+
pass
3129

3230

33-
async def run(url: str, name: str) -> None:
34-
try:
35-
await client(url, name)
36-
except Exception:
37-
import traceback
38-
39-
traceback.print_exc()
40-
41-
42-
if __name__ == "__main__":
43-
asyncio.run(run("http://localhost:9001", "aiohttp"))
31+
if __name__ == "__main__": # pragma: no branch
32+
asyncio.run(client("http://localhost:9001", "aiohttp"))
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
21
{
32
"url": "ws://localhost:9001",
43

5-
"options": {"failByDrop": false},
4+
"options": {"failByDrop": true},
65
"outdir": "./reports/clients",
76
"webport": 8080,
87

98
"cases": ["*"],
10-
"exclude-cases": ["12.*", "13.*"],
9+
"exclude-cases": [],
1110
"exclude-agent-cases": {}
1211
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
{
2-
"options": { "failByDrop": false },
2+
"options": {"failByDrop": true},
33
"outdir": "./reports/servers",
44

55
"servers": [
66
{
77
"agent": "AutobahnServer",
88
"url": "ws://localhost:9001",
9-
"options": { "version": 18 }
9+
"options": {"version": 18}
1010
}
1111
],
1212

1313
"cases": ["*"],
14-
"exclude-cases": ["12.*", "13.*"],
14+
"exclude-cases": [],
1515
"exclude-agent-cases": {}
1616
}

tests/autobahn/server/server.py

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,47 +9,33 @@
99

1010
async def wshandler(request: web.Request) -> web.WebSocketResponse:
1111
ws = web.WebSocketResponse(autoclose=False)
12-
is_ws = ws.can_prepare(request)
13-
if not is_ws:
14-
raise web.HTTPBadRequest()
15-
1612
await ws.prepare(request)
1713

1814
request.app[websockets].append(ws)
1915

20-
while True:
21-
msg = await ws.receive()
22-
16+
async for msg in ws:
2317
if msg.type is web.WSMsgType.TEXT:
2418
await ws.send_str(msg.data)
2519
elif msg.type is web.WSMsgType.BINARY:
2620
await ws.send_bytes(msg.data)
27-
elif msg.type is web.WSMsgType.CLOSE:
28-
await ws.close()
29-
break
3021
else:
3122
break
3223

3324
return ws
3425

3526

3627
async def on_shutdown(app: web.Application) -> None:
37-
ws_list = app[websockets]
38-
for ws in set(ws_list):
28+
for ws in app[websockets]:
3929
await ws.close(code=WSCloseCode.GOING_AWAY, message=b"Server shutdown")
4030

4131

42-
if __name__ == "__main__":
32+
if __name__ == "__main__": # pragma: no branch
4333
logging.basicConfig(
4434
level=logging.DEBUG, format="%(asctime)s %(levelname)s %(message)s"
4535
)
4636

4737
app = web.Application()
48-
l: list[web.WebSocketResponse] = []
49-
app[websockets] = l
38+
app[websockets] = []
5039
app.router.add_route("GET", "/", wshandler)
5140
app.on_shutdown.append(on_shutdown)
52-
try:
53-
web.run_app(app, port=9001)
54-
except KeyboardInterrupt:
55-
print("Server stopped at http://127.0.0.1:9001")
41+
web.run_app(app, port=9001)

0 commit comments

Comments
 (0)