Skip to content

Commit 0b2900d

Browse files
authored
PYTHON-5413 Handle flaky tests (#2395)
1 parent 578c6c2 commit 0b2900d

26 files changed

+305
-119
lines changed

.evergreen/generated_configs/functions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ functions:
145145
- MONGODB_API_VERSION
146146
- REQUIRE_API_VERSION
147147
- DEBUG_LOG
148+
- DISABLE_FLAKY
148149
- ORCHESTRATION_FILE
149150
- OCSP_SERVER_TYPE
150151
- VERSION

.evergreen/scripts/generate_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,7 @@ def create_run_tests_func():
10841084
"MONGODB_API_VERSION",
10851085
"REQUIRE_API_VERSION",
10861086
"DEBUG_LOG",
1087+
"DISABLE_FLAKY",
10871088
"ORCHESTRATION_FILE",
10881089
"OCSP_SERVER_TYPE",
10891090
"VERSION",

.evergreen/scripts/setup_tests.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,6 @@ def handle_test_env() -> None:
162162
write_env("PIP_PREFER_BINARY") # Prefer binary dists by default.
163163
write_env("UV_FROZEN") # Do not modify lock files.
164164

165-
# Skip CSOT tests on non-linux platforms.
166-
if PLATFORM != "linux":
167-
write_env("SKIP_CSOT_TESTS")
168-
169165
# Set an environment variable for the test name and sub test name.
170166
write_env(f"TEST_{test_name.upper()}")
171167
write_env("TEST_NAME", test_name)

CONTRIBUTING.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,15 @@ If you are running one of the `no-responder` tests, omit the `run-server` step.
404404
- Regenerate the test variants and tasks using `pre-commit run --all-files generate-config`.
405405
- Make sure to add instructions for running the test suite to `CONTRIBUTING.md`.
406406

407+
## Handling flaky tests
408+
409+
We have a custom `flaky` decorator in [test/asynchronous/utils.py](test/asynchronous/utils.py) that can be used for
410+
tests that are `flaky`. By default the decorator only applies when not running on CPython on Linux, since other
411+
runtimes tend to have more variation. When using the `flaky` decorator, open a corresponding ticket and
412+
a use the ticket number as the "reason" parameter to the decorator, e.g. `@flaky(reason="PYTHON-1234")`.
413+
When running tests locally (not in CI), the `flaky` decorator will be disabled unless `ENABLE_FLAKY` is set.
414+
To disable the `flaky` decorator in CI, you can use `evergreen patch --param DISABLE_FLAKY=1`.
415+
407416
## Specification Tests
408417

409418
The MongoDB [specifications repository](https://github.com/mongodb/specifications)

test/__init__.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import warnings
3333
from asyncio import iscoroutinefunction
3434

35+
from pymongo.errors import AutoReconnect
3536
from pymongo.synchronous.uri_parser import parse_uri
3637

3738
try:
@@ -1219,12 +1220,17 @@ def teardown():
12191220
c = client_context.client
12201221
if c:
12211222
if not client_context.is_data_lake:
1222-
c.drop_database("pymongo-pooling-tests")
1223-
c.drop_database("pymongo_test")
1224-
c.drop_database("pymongo_test1")
1225-
c.drop_database("pymongo_test2")
1226-
c.drop_database("pymongo_test_mike")
1227-
c.drop_database("pymongo_test_bernie")
1223+
try:
1224+
c.drop_database("pymongo-pooling-tests")
1225+
c.drop_database("pymongo_test")
1226+
c.drop_database("pymongo_test1")
1227+
c.drop_database("pymongo_test2")
1228+
c.drop_database("pymongo_test_mike")
1229+
c.drop_database("pymongo_test_bernie")
1230+
except AutoReconnect:
1231+
# PYTHON-4982
1232+
if sys.implementation.name.lower() != "pypy":
1233+
raise
12281234
c.close()
12291235
print_running_clients()
12301236

test/asynchronous/__init__.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from asyncio import iscoroutinefunction
3434

3535
from pymongo.asynchronous.uri_parser import parse_uri
36+
from pymongo.errors import AutoReconnect
3637

3738
try:
3839
import ipaddress
@@ -1235,12 +1236,17 @@ async def async_teardown():
12351236
c = async_client_context.client
12361237
if c:
12371238
if not async_client_context.is_data_lake:
1238-
await c.drop_database("pymongo-pooling-tests")
1239-
await c.drop_database("pymongo_test")
1240-
await c.drop_database("pymongo_test1")
1241-
await c.drop_database("pymongo_test2")
1242-
await c.drop_database("pymongo_test_mike")
1243-
await c.drop_database("pymongo_test_bernie")
1239+
try:
1240+
await c.drop_database("pymongo-pooling-tests")
1241+
await c.drop_database("pymongo_test")
1242+
await c.drop_database("pymongo_test1")
1243+
await c.drop_database("pymongo_test2")
1244+
await c.drop_database("pymongo_test_mike")
1245+
await c.drop_database("pymongo_test_bernie")
1246+
except AutoReconnect:
1247+
# PYTHON-4982
1248+
if sys.implementation.name.lower() != "pypy":
1249+
raise
12441250
await c.close()
12451251
print_running_clients()
12461252

test/asynchronous/test_client_bulk_write.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
async_client_context,
2626
unittest,
2727
)
28+
from test.asynchronous.utils import flaky
2829
from test.utils_shared import (
2930
OvertCommandListener,
3031
)
@@ -619,16 +620,17 @@ async def test_15_unacknowledged_write_across_batches(self):
619620
# https://github.com/mongodb/specifications/blob/master/source/client-side-operations-timeout/tests/README.md#11-multi-batch-bulkwrites
620621
class TestClientBulkWriteCSOT(AsyncIntegrationTest):
621622
async def asyncSetUp(self):
622-
if os.environ.get("SKIP_CSOT_TESTS", ""):
623-
raise unittest.SkipTest("SKIP_CSOT_TESTS is set, skipping...")
624623
await super().asyncSetUp()
625624
self.max_write_batch_size = await async_client_context.max_write_batch_size
626625
self.max_bson_object_size = await async_client_context.max_bson_size
627626
self.max_message_size_bytes = await async_client_context.max_message_size_bytes
628627

629628
@async_client_context.require_version_min(8, 0, 0, -24)
630629
@async_client_context.require_failCommand_fail_point
630+
@flaky(reason="PYTHON-5290", max_runs=3, affects_cpython_linux=True)
631631
async def test_timeout_in_multi_batch_bulk_write(self):
632+
if sys.platform != "linux" and "CI" in os.environ:
633+
self.skipTest("PYTHON-3522 CSOT test runs too slow on Windows and MacOS")
632634
_OVERHEAD = 500
633635

634636
internal_client = await self.async_rs_or_single_client(timeoutMS=None)

test/asynchronous/test_csot.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from test.asynchronous import AsyncIntegrationTest, async_client_context, unittest
2525
from test.asynchronous.unified_format import generate_test_classes
26+
from test.asynchronous.utils import flaky
2627

2728
import pymongo
2829
from pymongo import _csot
@@ -43,9 +44,8 @@
4344
class TestCSOT(AsyncIntegrationTest):
4445
RUN_ON_LOAD_BALANCER = True
4546

47+
@flaky(reason="PYTHON-3522")
4648
async def test_timeout_nested(self):
47-
if os.environ.get("SKIP_CSOT_TESTS", ""):
48-
raise unittest.SkipTest("SKIP_CSOT_TESTS is set, skipping...")
4949
coll = self.db.coll
5050
self.assertEqual(_csot.get_timeout(), None)
5151
self.assertEqual(_csot.get_deadline(), float("inf"))
@@ -82,9 +82,8 @@ async def test_timeout_nested(self):
8282
self.assertEqual(_csot.get_rtt(), 0.0)
8383

8484
@async_client_context.require_change_streams
85+
@flaky(reason="PYTHON-3522")
8586
async def test_change_stream_can_resume_after_timeouts(self):
86-
if os.environ.get("SKIP_CSOT_TESTS", ""):
87-
raise unittest.SkipTest("SKIP_CSOT_TESTS is set, skipping...")
8887
coll = self.db.test
8988
await coll.insert_one({})
9089
async with await coll.watch() as stream:

test/asynchronous/test_cursor.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
sys.path[0:0] = [""]
3232

3333
from test.asynchronous import AsyncIntegrationTest, async_client_context, unittest
34+
from test.asynchronous.utils import flaky
3435
from test.utils_shared import (
3536
AllowListEventListener,
3637
EventListener,
@@ -1406,9 +1407,8 @@ async def test_to_list_length(self):
14061407
docs = await c.to_list(3)
14071408
self.assertEqual(len(docs), 2)
14081409

1410+
@flaky(reason="PYTHON-3522")
14091411
async def test_to_list_csot_applied(self):
1410-
if os.environ.get("SKIP_CSOT_TESTS", ""):
1411-
raise unittest.SkipTest("SKIP_CSOT_TESTS is set, skipping...")
14121412
client = await self.async_single_client(timeoutMS=500, w=1)
14131413
coll = client.pymongo.test
14141414
# Initialize the client with a larger timeout to help make test less flakey
@@ -1449,9 +1449,8 @@ async def test_command_cursor_to_list_length(self):
14491449
self.assertEqual(len(await result.to_list(1)), 1)
14501450

14511451
@async_client_context.require_failCommand_blockConnection
1452+
@flaky(reason="PYTHON-3522")
14521453
async def test_command_cursor_to_list_csot_applied(self):
1453-
if os.environ.get("SKIP_CSOT_TESTS", ""):
1454-
raise unittest.SkipTest("SKIP_CSOT_TESTS is set, skipping...")
14551454
client = await self.async_single_client(timeoutMS=500, w=1)
14561455
coll = client.pymongo.test
14571456
# Initialize the client with a larger timeout to help make test less flakey

test/asynchronous/test_encryption.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import warnings
3333
from test.asynchronous import AsyncIntegrationTest, AsyncPyMongoTestCase, async_client_context
3434
from test.asynchronous.test_bulk import AsyncBulkTestBase
35+
from test.asynchronous.utils import flaky
3536
from test.asynchronous.utils_spec_runner import AsyncSpecRunner, AsyncSpecTestCreator
3637
from threading import Thread
3738
from typing import Any, Dict, Mapping, Optional
@@ -3247,6 +3248,7 @@ async def test_kms_retry(self):
32473248
class TestAutomaticDecryptionKeys(AsyncEncryptionIntegrationTest):
32483249
@async_client_context.require_no_standalone
32493250
@async_client_context.require_version_min(7, 0, -1)
3251+
@flaky(reason="PYTHON-4982")
32503252
async def asyncSetUp(self):
32513253
await super().asyncSetUp()
32523254
self.key1_document = json_data("etc", "data", "keys", "key1-document.json")
@@ -3489,6 +3491,8 @@ async def test_implicit_session_ignored_when_unsupported(self):
34893491

34903492
self.assertNotIn("lsid", self.listener.started_events[1].command)
34913493

3494+
await self.mongocryptd_client.close()
3495+
34923496
async def test_explicit_session_errors_when_unsupported(self):
34933497
self.listener.reset()
34943498
async with self.mongocryptd_client.start_session() as s:
@@ -3501,6 +3505,8 @@ async def test_explicit_session_errors_when_unsupported(self):
35013505
):
35023506
await self.mongocryptd_client.db.test.insert_one({"x": 1}, session=s)
35033507

3508+
await self.mongocryptd_client.close()
3509+
35043510

35053511
if __name__ == "__main__":
35063512
unittest.main()

0 commit comments

Comments
 (0)