diff --git a/dev_utils/dev_utils/service_helper.py b/dev_utils/dev_utils/service_helper.py index 066dfdf4c..0e69ca4d2 100644 --- a/dev_utils/dev_utils/service_helper.py +++ b/dev_utils/dev_utils/service_helper.py @@ -12,24 +12,22 @@ def __init__( iothub_connection_string, eventhub_connection_string, eventhub_consumer_group, - event_loop=None, executor=None, ): - self._event_loop = event_loop or asyncio.get_event_loop() self._executor = executor or concurrent.futures.ThreadPoolExecutor() self._inner_object = ServiceHelperSync( iothub_connection_string, eventhub_connection_string, eventhub_consumer_group ) + async def _run_in_executor(self, func, *args): + loop = asyncio.get_running_loop() + return await loop.run_in_executor(self._executor, func, *args) + def set_identity(self, device_id, module_id): return self._inner_object.set_identity(device_id, module_id) async def set_desired_properties(self, desired_props): - return await self._event_loop.run_in_executor( - self._executor, - self._inner_object.set_desired_properties, - desired_props, - ) + return await self._run_in_executor(self._inner_object.set_desired_properties, desired_props) async def invoke_method( self, @@ -38,8 +36,7 @@ async def invoke_method( connect_timeout_in_seconds=None, response_timeout_in_seconds=None, ): - return await self._event_loop.run_in_executor( - self._executor, + return await self._run_in_executor( self._inner_object.invoke_method, method_name, payload, @@ -52,25 +49,21 @@ async def send_c2d( payload, properties, ): - return await self._event_loop.run_in_executor( - self._executor, self._inner_object.send_c2d, payload, properties - ) + return await self._run_in_executor(self._inner_object.send_c2d, payload, properties) async def wait_for_eventhub_arrival(self, message_id, timeout=60): - return await self._event_loop.run_in_executor( - self._executor, + return await self._run_in_executor( self._inner_object.wait_for_eventhub_arrival, message_id, timeout, ) async def get_next_reported_patch_arrival(self, block=True, timeout=240): - return await self._event_loop.run_in_executor( - self._executor, + return await self._run_in_executor( self._inner_object.get_next_reported_patch_arrival, block, timeout, ) async def shutdown(self): - return await self._event_loop.run_in_executor(self._executor, self._inner_object.shutdown) + return await self._run_in_executor(self._inner_object.shutdown) diff --git a/pytest.ini b/pytest.ini index 07ac113a3..23108d61e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,5 @@ [pytest] +asyncio_mode=auto testdox_format = plaintext addopts = --testdox --timeout 20 --ignore e2e --ignore tests/e2e norecursedirs=__pycache__, *.egg-info diff --git a/requirements_test.txt b/requirements_test.txt index 899d56576..26789d3a1 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,5 +1,5 @@ -pytest < 8.0.0 -pytest-asyncio <= 0.16 +pytest>=8,<9 +pytest-asyncio~=1.2 pytest-mock pytest-testdox>=1.1.1 pytest-cov diff --git a/tests/e2e/iothub_e2e/aio/conftest.py b/tests/e2e/iothub_e2e/aio/conftest.py index e6ff8f52c..5c76eb570 100644 --- a/tests/e2e/iothub_e2e/aio/conftest.py +++ b/tests/e2e/iothub_e2e/aio/conftest.py @@ -2,7 +2,6 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. import pytest -import asyncio from dev_utils import test_env, ServiceHelper import logging import datetime @@ -56,13 +55,6 @@ def pytest_sessionfinish(session, exitstatus): print("-----------------------------------") -@pytest.fixture(scope="session") -def event_loop(): - loop = asyncio.get_event_loop() - yield loop - loop.close() - - @pytest.fixture(scope="function") async def brand_new_client(device_identity, client_kwargs, service_helper, device_id, module_id): service_helper.set_identity(device_id, module_id) @@ -104,12 +96,11 @@ async def client(brand_new_client): @pytest.fixture(scope="session") -async def service_helper(event_loop, executor): +async def service_helper(executor): service_helper = ServiceHelper( iothub_connection_string=test_env.IOTHUB_CONNECTION_STRING, eventhub_connection_string=test_env.EVENTHUB_CONNECTION_STRING, eventhub_consumer_group=test_env.EVENTHUB_CONSUMER_GROUP, - event_loop=event_loop, executor=executor, ) yield service_helper diff --git a/tests/e2e/iothub_e2e/aio/test_c2d.py b/tests/e2e/iothub_e2e/aio/test_c2d.py index 980ccc583..9a38962c2 100644 --- a/tests/e2e/iothub_e2e/aio/test_c2d.py +++ b/tests/e2e/iothub_e2e/aio/test_c2d.py @@ -10,7 +10,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio # TODO: add tests for various application properties # TODO: is there a way to call send_c2d so it arrives as an object rather than a JSON string? @@ -20,8 +19,9 @@ class TestReceiveC2d(object): @pytest.mark.it("Can receive C2D") @pytest.mark.quicktest_suite - async def test_receive_c2d(self, client, service_helper, event_loop, leak_tracker): + async def test_receive_c2d(self, client, service_helper, leak_tracker): leak_tracker.set_initial_object_list() + event_loop = asyncio.get_running_loop() message = json.dumps(get_random_dict()) diff --git a/tests/e2e/iothub_e2e/aio/test_connect_disconnect.py b/tests/e2e/iothub_e2e/aio/test_connect_disconnect.py index 89d3d267b..e99d317a9 100644 --- a/tests/e2e/iothub_e2e/aio/test_connect_disconnect.py +++ b/tests/e2e/iothub_e2e/aio/test_connect_disconnect.py @@ -9,8 +9,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio - @pytest.mark.describe("Client object") class TestConnectDisconnect(object): @@ -44,7 +42,7 @@ async def test_connect_disconnect(self, brand_new_client, leak_tracker): # see "This assert fails because of initial and secondary disconnects" below @pytest.mark.skip(reason="two stage disconnect causes assertion in test code") async def test_connect_in_the_middle_of_disconnect( - self, brand_new_client, event_loop, service_helper, random_message, leak_tracker + self, brand_new_client, service_helper, random_message, leak_tracker ): """ Explanation: People will call `connect` inside `on_connection_state_change` handlers. @@ -54,6 +52,7 @@ async def test_connect_in_the_middle_of_disconnect( assert client leak_tracker.set_initial_object_list() + event_loop = asyncio.get_running_loop() reconnected_event = asyncio.Event() @@ -112,7 +111,6 @@ async def handle_on_connection_state_change(): async def test_disconnect_in_the_middle_of_connect( self, brand_new_client, - event_loop, service_helper, random_message, first_connect, @@ -128,6 +126,7 @@ async def test_disconnect_in_the_middle_of_connect( disconnect_on_next_connect_event = False leak_tracker.set_initial_object_list() + event_loop = asyncio.get_running_loop() disconnected_event = asyncio.Event() diff --git a/tests/e2e/iothub_e2e/aio/test_connect_disconnect_stress.py b/tests/e2e/iothub_e2e/aio/test_connect_disconnect_stress.py index af12a5252..ccd756e4d 100644 --- a/tests/e2e/iothub_e2e/aio/test_connect_disconnect_stress.py +++ b/tests/e2e/iothub_e2e/aio/test_connect_disconnect_stress.py @@ -10,8 +10,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio - @pytest.mark.stress @pytest.mark.describe("Client object connect/disconnect stress") diff --git a/tests/e2e/iothub_e2e/aio/test_infrastructure.py b/tests/e2e/iothub_e2e/aio/test_infrastructure.py index 5ace0aebf..1587d5c71 100644 --- a/tests/e2e/iothub_e2e/aio/test_infrastructure.py +++ b/tests/e2e/iothub_e2e/aio/test_infrastructure.py @@ -4,8 +4,6 @@ import pytest import uuid -pytestmark = pytest.mark.asyncio - @pytest.mark.describe("ServiceHelper object") class TestServiceHelper(object): diff --git a/tests/e2e/iothub_e2e/aio/test_methods.py b/tests/e2e/iothub_e2e/aio/test_methods.py index 50e7f487c..4ca196dab 100644 --- a/tests/e2e/iothub_e2e/aio/test_methods.py +++ b/tests/e2e/iothub_e2e/aio/test_methods.py @@ -11,8 +11,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio - @pytest.fixture def method_name(): diff --git a/tests/e2e/iothub_e2e/aio/test_sas_renewal.py b/tests/e2e/iothub_e2e/aio/test_sas_renewal.py index e7942b150..47621fe3b 100644 --- a/tests/e2e/iothub_e2e/aio/test_sas_renewal.py +++ b/tests/e2e/iothub_e2e/aio/test_sas_renewal.py @@ -11,8 +11,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio - @pytest.mark.skipif( test_config.config.auth not in test_config.AUTH_WITH_RENEWING_TOKEN, @@ -24,10 +22,9 @@ class TestSasRenewal(object): @pytest.mark.it("Renews and reconnects before expiry") @pytest.mark.parametrize(*parametrize.connection_retry_disabled_and_enabled) @pytest.mark.parametrize(*parametrize.auto_connect_disabled_and_enabled) - async def test_sas_renews( - self, client, event_loop, service_helper, random_message, leak_tracker - ): + async def test_sas_renews(self, client, service_helper, random_message, leak_tracker): leak_tracker.set_initial_object_list() + event_loop = asyncio.get_running_loop() connected_event = asyncio.Event() disconnected_event = asyncio.Event() diff --git a/tests/e2e/iothub_e2e/aio/test_send_message.py b/tests/e2e/iothub_e2e/aio/test_send_message.py index 40bb2daa2..bc609e9c9 100644 --- a/tests/e2e/iothub_e2e/aio/test_send_message.py +++ b/tests/e2e/iothub_e2e/aio/test_send_message.py @@ -11,8 +11,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio - @pytest.mark.describe("Client send_message method") class TestSendMessage(object): diff --git a/tests/e2e/iothub_e2e/aio/test_send_message_stress.py b/tests/e2e/iothub_e2e/aio/test_send_message_stress.py index 993ffd5d3..b8e58d4f3 100644 --- a/tests/e2e/iothub_e2e/aio/test_send_message_stress.py +++ b/tests/e2e/iothub_e2e/aio/test_send_message_stress.py @@ -15,7 +15,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio # Settings that apply to all tests in this module TELEMETRY_PAYLOAD_SIZE = 16 * 1024 diff --git a/tests/e2e/iothub_e2e/aio/test_twin.py b/tests/e2e/iothub_e2e/aio/test_twin.py index 3fd7fb357..a530246c8 100644 --- a/tests/e2e/iothub_e2e/aio/test_twin.py +++ b/tests/e2e/iothub_e2e/aio/test_twin.py @@ -11,8 +11,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio - # TODO: tests with drop_incoming and reject_incoming @@ -192,10 +190,9 @@ async def test_updates_reported_if_reject_before_sending( class TestDesiredProperties(object): @pytest.mark.it("Receives a patch for a simple desired property") @pytest.mark.quicktest_suite - async def test_receives_simple_desired_patch( - self, client, event_loop, service_helper, leak_tracker - ): + async def test_receives_simple_desired_patch(self, client, service_helper, leak_tracker): leak_tracker.set_initial_object_list() + event_loop = asyncio.get_running_loop() received_patch = None received = asyncio.Event() diff --git a/tests/e2e/iothub_e2e/aio/test_twin_stress.py b/tests/e2e/iothub_e2e/aio/test_twin_stress.py index 314f2a810..dc57798ec 100644 --- a/tests/e2e/iothub_e2e/aio/test_twin_stress.py +++ b/tests/e2e/iothub_e2e/aio/test_twin_stress.py @@ -12,8 +12,6 @@ logger = logging.getLogger(__name__) logger.setLevel(level=logging.INFO) -pytestmark = pytest.mark.asyncio - @pytest.fixture def toxic(): @@ -159,13 +157,14 @@ async def test_stress_parallel_reported_property_updates( ) @pytest.mark.it("Can receive continuous desired property updates that were sent one-at-a-time") async def test_stress_serial_desired_property_updates( - self, client, service_helper, toxic, iteration_count, event_loop, leak_tracker + self, client, service_helper, toxic, iteration_count, leak_tracker ): """ Update desired properties, one at a time, and verify that the desired property arrives at the client before the next update. """ leak_tracker.set_initial_object_list() + event_loop = asyncio.get_running_loop() patches = asyncio.Queue() @@ -202,13 +201,14 @@ async def handle_on_patch_received(patch): "Can receive continuous desired property updates that may have been sent in parallel" ) async def test_stress_parallel_desired_property_updates( - self, client, service_helper, toxic, iteration_count, batch_size, event_loop, leak_tracker + self, client, service_helper, toxic, iteration_count, batch_size, leak_tracker ): """ Update desired properties in batches. Each batch updates `batch_size` properties, with each property being updated in it's own `PATCH`. """ leak_tracker.set_initial_object_list() + event_loop = asyncio.get_running_loop() patches = asyncio.Queue() diff --git a/tests/e2e/iothub_e2e/pytest.ini b/tests/e2e/iothub_e2e/pytest.ini index 0094ca0ed..f38019b98 100644 --- a/tests/e2e/iothub_e2e/pytest.ini +++ b/tests/e2e/iothub_e2e/pytest.ini @@ -5,7 +5,6 @@ junit_logging=all junit_family=xunit2 junit_log_passing_tests=True asyncio_mode=auto -# --force-testdox to always use testdox format, even when redirecting to file addopts= --testdox --force-testdox diff --git a/tests/e2e/provisioning_e2e/pytest.ini b/tests/e2e/provisioning_e2e/pytest.ini index 2f799d7d7..2243b227d 100644 --- a/tests/e2e/provisioning_e2e/pytest.ini +++ b/tests/e2e/provisioning_e2e/pytest.ini @@ -1,2 +1,7 @@ [pytest] -addopts = --timeout 30 \ No newline at end of file +timeout=30 +testdox_format=plaintext +asyncio_mode=auto +addopts= + --testdox + --force-testdox diff --git a/tests/e2e/provisioning_e2e/tests/test_async_certificate_enrollments.py b/tests/e2e/provisioning_e2e/tests/test_async_certificate_enrollments.py index 1b8ea4ab3..0219af14e 100644 --- a/tests/e2e/provisioning_e2e/tests/test_async_certificate_enrollments.py +++ b/tests/e2e/provisioning_e2e/tests/test_async_certificate_enrollments.py @@ -29,7 +29,6 @@ ) -pytestmark = pytest.mark.asyncio logging.basicConfig(level=logging.DEBUG) diff --git a/tests/e2e/provisioning_e2e/tests/test_async_symmetric_enrollments.py b/tests/e2e/provisioning_e2e/tests/test_async_symmetric_enrollments.py index 3657db15b..914a158d5 100644 --- a/tests/e2e/provisioning_e2e/tests/test_async_symmetric_enrollments.py +++ b/tests/e2e/provisioning_e2e/tests/test_async_symmetric_enrollments.py @@ -13,7 +13,7 @@ import os import uuid -pytestmark = pytest.mark.asyncio + logging.basicConfig(level=logging.DEBUG) diff --git a/tests/unit/common/test_async_adapter.py b/tests/unit/common/test_async_adapter.py index 800b85d42..d90a895e4 100644 --- a/tests/unit/common/test_async_adapter.py +++ b/tests/unit/common/test_async_adapter.py @@ -11,7 +11,6 @@ import azure.iot.device.common.async_adapter as async_adapter logging.basicConfig(level=logging.DEBUG) -pytestmark = pytest.mark.asyncio @pytest.fixture diff --git a/tests/unit/iothub/aio/test_async_clients.py b/tests/unit/iothub/aio/test_async_clients.py index b22d1774e..3796f494a 100644 --- a/tests/unit/iothub/aio/test_async_clients.py +++ b/tests/unit/iothub/aio/test_async_clients.py @@ -44,7 +44,6 @@ SharedIoTHubModuleClientCreateFromEdgeEnvironmentWithDebugEnvTests, ) -pytestmark = pytest.mark.asyncio logging.basicConfig(level=logging.DEBUG) diff --git a/tests/unit/iothub/aio/test_async_handler_manager.py b/tests/unit/iothub/aio/test_async_handler_manager.py index b3dc5c056..781b902ab 100644 --- a/tests/unit/iothub/aio/test_async_handler_manager.py +++ b/tests/unit/iothub/aio/test_async_handler_manager.py @@ -16,7 +16,6 @@ from azure.iot.device.iothub.inbox_manager import InboxManager from azure.iot.device.iothub.aio.async_inbox import AsyncClientInbox -pytestmark = pytest.mark.asyncio logging.basicConfig(level=logging.DEBUG) # NOTE ON TEST IMPLEMENTATION: diff --git a/tests/unit/provisioning/aio/test_async_provisioning_device_client.py b/tests/unit/provisioning/aio/test_async_provisioning_device_client.py index 2b1106a52..75ebc5107 100644 --- a/tests/unit/provisioning/aio/test_async_provisioning_device_client.py +++ b/tests/unit/provisioning/aio/test_async_provisioning_device_client.py @@ -20,7 +20,6 @@ logging.basicConfig(level=logging.DEBUG) -pytestmark = pytest.mark.asyncio async def create_completed_future(result=None):