PYTHON-4520 Add test-async tox command and automated async testing (#1699)
This commit is contained in:
parent
3617b5cf51
commit
b035c9ce65
@ -258,6 +258,10 @@ if [ -z "$GREEN_FRAMEWORK" ]; then
|
||||
# Use --capture=tee-sys so pytest prints test output inline:
|
||||
# https://docs.pytest.org/en/stable/how-to/capture-stdout-stderr.html
|
||||
python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 $TEST_ARGS
|
||||
if [ -z "$TEST_ARGS" ]; then # TODO: remove this in PYTHON-4528
|
||||
python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 test/synchronous/ $TEST_ARGS
|
||||
fi
|
||||
python -m pytest -v --capture=tee-sys --durations=5 --maxfail=10 test/asynchronous/ $TEST_ARGS
|
||||
else
|
||||
python green_framework_test.py $GREEN_FRAMEWORK -v $TEST_ARGS
|
||||
fi
|
||||
|
||||
5
.github/workflows/test-python.yml
vendored
5
.github/workflows/test-python.yml
vendored
@ -71,6 +71,9 @@ jobs:
|
||||
- name: Run tests
|
||||
run: |
|
||||
tox -m test
|
||||
- name: Run async tests
|
||||
run: |
|
||||
tox -m test-async
|
||||
|
||||
doctest:
|
||||
runs-on: ubuntu-latest
|
||||
@ -203,3 +206,5 @@ jobs:
|
||||
which python
|
||||
pip install -e ".[test]"
|
||||
pytest -v
|
||||
pytest -v test/synchronous/
|
||||
pytest -v test/asynchronous/
|
||||
|
||||
@ -28,7 +28,10 @@ from pymongo.asynchronous.database import AsyncDatabase
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from test import unittest
|
||||
from test.asynchronous import AsyncIntegrationTest, async_client_context
|
||||
from test.asynchronous import ( # TODO: fix sync imports in PYTHON-4528
|
||||
AsyncIntegrationTest,
|
||||
async_client_context,
|
||||
)
|
||||
from test.utils import (
|
||||
IMPOSSIBLE_WRITE_CONCERN,
|
||||
EventListener,
|
||||
@ -472,8 +475,8 @@ class AsyncTestCollection(AsyncIntegrationTest):
|
||||
async def test_index_haystack(self):
|
||||
db = self.db
|
||||
await db.test.drop()
|
||||
_id = await db.test.insert_one(
|
||||
{"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"}
|
||||
_id = (
|
||||
await db.test.insert_one({"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"})
|
||||
).inserted_id
|
||||
await db.test.insert_one({"pos": {"long": 34.2, "lat": 37.3}, "type": "restaurant"})
|
||||
await db.test.insert_one({"pos": {"long": 59.1, "lat": 87.2}, "type": "office"})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from test.synchronous import setup, teardown
|
||||
from test import setup, teardown
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@ -27,8 +27,11 @@ from pymongo.synchronous.database import Database
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from test import unittest
|
||||
from test.synchronous import IntegrationTest, client_context
|
||||
from test import ( # TODO: fix sync imports in PYTHON-4528
|
||||
IntegrationTest,
|
||||
client_context,
|
||||
unittest,
|
||||
)
|
||||
from test.utils import (
|
||||
IMPOSSIBLE_WRITE_CONCERN,
|
||||
EventListener,
|
||||
@ -461,8 +464,8 @@ class TestCollection(IntegrationTest):
|
||||
def test_index_haystack(self):
|
||||
db = self.db
|
||||
db.test.drop()
|
||||
_id = db.test.insert_one(
|
||||
{"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"}
|
||||
_id = (
|
||||
db.test.insert_one({"pos": {"long": 34.2, "lat": 33.3}, "type": "restaurant"})
|
||||
).inserted_id
|
||||
db.test.insert_one({"pos": {"long": 34.2, "lat": 37.3}, "type": "restaurant"})
|
||||
db.test.insert_one({"pos": {"long": 59.1, "lat": 87.2}, "type": "office"})
|
||||
|
||||
@ -92,6 +92,8 @@ docstring_replacements: dict[tuple[str, str], str] = {
|
||||
|
||||
type_replacements = {"_Condition": "threading.Condition"}
|
||||
|
||||
import_replacements = {"test.synchronous": "test"}
|
||||
|
||||
_pymongo_base = "./pymongo/asynchronous/"
|
||||
_gridfs_base = "./gridfs/asynchronous/"
|
||||
_test_base = "./test/asynchronous/"
|
||||
@ -136,23 +138,31 @@ def process_files(files: list[str]) -> None:
|
||||
if "__init__" not in file or "__init__" and "test" in file:
|
||||
with open(file, "r+") as f:
|
||||
lines = f.readlines()
|
||||
lines = apply_is_sync(lines)
|
||||
lines = apply_is_sync(lines, file)
|
||||
lines = translate_coroutine_types(lines)
|
||||
lines = translate_async_sleeps(lines)
|
||||
if file in docstring_translate_files:
|
||||
lines = translate_docstrings(lines)
|
||||
translate_locks(lines)
|
||||
translate_types(lines)
|
||||
if file in sync_test_files:
|
||||
translate_imports(lines)
|
||||
f.seek(0)
|
||||
f.writelines(lines)
|
||||
f.truncate()
|
||||
|
||||
|
||||
def apply_is_sync(lines: list[str]) -> list[str]:
|
||||
is_sync = next(iter([line for line in lines if line.startswith("_IS_SYNC = ")]))
|
||||
index = lines.index(is_sync)
|
||||
is_sync = is_sync.replace("False", "True")
|
||||
lines[index] = is_sync
|
||||
def apply_is_sync(lines: list[str], file: str) -> list[str]:
|
||||
try:
|
||||
is_sync = next(iter([line for line in lines if line.startswith("_IS_SYNC = ")]))
|
||||
index = lines.index(is_sync)
|
||||
is_sync = is_sync.replace("False", "True")
|
||||
lines[index] = is_sync
|
||||
except StopIteration as e:
|
||||
print(
|
||||
f"Missing _IS_SYNC at top of async file {file.replace('synchronous', 'asynchronous')}"
|
||||
)
|
||||
raise e
|
||||
return lines
|
||||
|
||||
|
||||
@ -196,6 +206,15 @@ def translate_types(lines: list[str]) -> list[str]:
|
||||
return lines
|
||||
|
||||
|
||||
def translate_imports(lines: list[str]) -> list[str]:
|
||||
for k, v in import_replacements.items():
|
||||
matches = [line for line in lines if k in line and "import" in line]
|
||||
for line in matches:
|
||||
index = lines.index(line)
|
||||
lines[index] = line.replace(k, v)
|
||||
return lines
|
||||
|
||||
|
||||
def translate_async_sleeps(lines: list[str]) -> list[str]:
|
||||
blocking_sleeps = [line for line in lines if "asyncio.sleep(0)" in line]
|
||||
lines = [line for line in lines if line not in blocking_sleeps]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/bash -eu
|
||||
|
||||
python ./tools/synchro.py
|
||||
python -m ruff check pymongo/synchronous/ gridfs/synchronous/ test/synchronous --fix --silent
|
||||
|
||||
11
tox.ini
11
tox.ini
@ -5,6 +5,8 @@ requires =
|
||||
envlist =
|
||||
# Test using the system Python.
|
||||
test,
|
||||
# Test async tests using the system Python.
|
||||
test-async,
|
||||
# Test using the run-tests Evergreen script.
|
||||
test-eg,
|
||||
# Set up encryption files and services.
|
||||
@ -34,6 +36,7 @@ envlist =
|
||||
|
||||
labels = # Use labels and -m instead of -e so that tox -m <label> fails instantly if the label does not exist
|
||||
test = test
|
||||
test-async = test-async
|
||||
test-eg = test-eg
|
||||
setup-encryption = setup-encryption
|
||||
teardown-encryption = teardown-encryption
|
||||
@ -63,6 +66,14 @@ extras =
|
||||
test
|
||||
commands =
|
||||
pytest -v --durations=5 --maxfail=10 {posargs}
|
||||
pytest -v --durations=5 --maxfail=10 test/synchronous/ {posargs}
|
||||
|
||||
[testenv:test-async]
|
||||
description = run base set of async unit tests with no extra functionality
|
||||
extras =
|
||||
test
|
||||
commands =
|
||||
pytest -v --durations=5 --maxfail=10 test/asynchronous/ {posargs}
|
||||
|
||||
[testenv:test-eg]
|
||||
description = run tests using run-tests.sh Evergreen script
|
||||
|
||||
Loading…
Reference in New Issue
Block a user