From 6a646d0c3fd3c1d33183bb338235aaa43f86d1f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 17:44:37 +0000 Subject: [PATCH 01/15] Bump pytest from 7.4.0 to 8.0.2 Bumps [pytest](https://github.com/pytest-dev/pytest) from 7.4.0 to 8.0.2. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/7.4.0...8.0.2) --- updated-dependencies: - dependency-name: pytest dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements-tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tests.txt b/requirements-tests.txt index 29655ab7a3..ce253cf871 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -1,7 +1,7 @@ coverage[toml]==7.2.7; python_version >= '3.8' requests>=2.31.0 requests_mock==1.11.0 -pytest==7.4.0 +pytest==8.0.2 pytest-xdist==3.3.1 pytest-timeout==2.2.0 pytest-benchmark[histogram]==4.0.0 From 20ae7315661fee522d332665a48f7868e788569c Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 15 Apr 2024 21:50:06 +0200 Subject: [PATCH 02/15] Try using loadscope pytest-xdist test distribution method to see if this helps with a weird race we started to see more often with latest version of pytest. --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 9dcc2f406d..43942453bc 100644 --- a/tox.ini +++ b/tox.ini @@ -36,9 +36,9 @@ setenv = # NOTE: By default we run tests on CI in parallel to speed up the build # To avoid per-test function process safety issues we run all tests in a single # file in the same worker process. -# for pytest-xdist, we want to distribute tests by file aka --dist loadfile +# for pytest-xdist, we want to distribute tests by file (class + method) aka --dist loadscope commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 --timeout=15 -n auto --dist loadfile --ignore libcloud/test/benchmarks/ --ignore-glob "*test_list_objects_filtering_performance*" + pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 --timeout=15 -n auto --dist loadscope --ignore libcloud/test/benchmarks/ --ignore-glob "*test_list_objects_filtering_performance*" [testenv:py3.8-dist] # Verify library installs without any dependencies when using python setup.py From 83a1114a0a204b85009113d3241a22feb80c2245 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Mon, 15 Apr 2024 21:53:48 +0200 Subject: [PATCH 03/15] Revert "Try using loadscope pytest-xdist test distribution method to see if this" This reverts commit 20ae7315661fee522d332665a48f7868e788569c. --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 43942453bc..9dcc2f406d 100644 --- a/tox.ini +++ b/tox.ini @@ -36,9 +36,9 @@ setenv = # NOTE: By default we run tests on CI in parallel to speed up the build # To avoid per-test function process safety issues we run all tests in a single # file in the same worker process. -# for pytest-xdist, we want to distribute tests by file (class + method) aka --dist loadscope +# for pytest-xdist, we want to distribute tests by file aka --dist loadfile commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 --timeout=15 -n auto --dist loadscope --ignore libcloud/test/benchmarks/ --ignore-glob "*test_list_objects_filtering_performance*" + pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 --timeout=15 -n auto --dist loadfile --ignore libcloud/test/benchmarks/ --ignore-glob "*test_list_objects_filtering_performance*" [testenv:py3.8-dist] # Verify library installs without any dependencies when using python setup.py From 60b4817a168a4da45f764b69e1e447b410fbc693 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 18:12:44 +0200 Subject: [PATCH 04/15] Upgrade pytest-xdist dependency. --- requirements-tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tests.txt b/requirements-tests.txt index ddcca27357..517a078c25 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -2,7 +2,7 @@ coverage[toml]==7.2.7; python_version >= '3.8' requests>=2.31.0 requests_mock==1.11.0 pytest==8.0.2 -pytest-xdist==3.3.1 +pytest-xdist==3.5.0 pytest-timeout==2.2.0 pytest-benchmark[histogram]==4.0.0 cryptography==42.0.5 From a52f5d9f6dca7850e74fa4bb2b56653d5f6401a2 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 18:20:09 +0200 Subject: [PATCH 05/15] Try a workaround for tests which started failing with new pytest version (>= 8.0). In theory with mocking in place, those tests should be safe to run in parallel, but because of some reason, they are prone to race condition, but only with pytest >= 8.0.0. To avoid this race condition, we now run tests which are not thread safe separately at the end in non parallel fashion. Tests which are not safe to run in parallel are marked with "serial" pytest marker. --- libcloud/test/test_init.py | 3 +++ tox.ini | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libcloud/test/test_init.py b/libcloud/test/test_init.py index 7eccd45502..07ca57ca83 100644 --- a/libcloud/test/test_init.py +++ b/libcloud/test/test_init.py @@ -20,6 +20,7 @@ from unittest import mock from unittest.mock import patch +import pytest import libcloud from libcloud import _init_once, reset_debug from libcloud.base import DriverTypeNotFoundError @@ -48,6 +49,7 @@ def setUp(self): paramiko_logger.setLevel(logging.INFO) @mock.patch.dict(os.environ, {"LIBCLOUD_DEBUG": ""}, clear=True) + @pytest.mark.serial def test_init_once_and_no_debug_mode(self): if have_paramiko: paramiko_logger = logging.getLogger("paramiko") @@ -68,6 +70,7 @@ def test_init_once_and_no_debug_mode(self): self.assertEqual(paramiko_log_level, logging.INFO) @mock.patch.dict(os.environ, {"LIBCLOUD_DEBUG": TEMP_LOGFILE_PATH}, clear=True) + @pytest.mark.serial def test_init_once_and_debug_mode(self): if have_paramiko: paramiko_logger = logging.getLogger("paramiko") diff --git a/tox.ini b/tox.ini index 9dcc2f406d..a1dc821956 100644 --- a/tox.ini +++ b/tox.ini @@ -37,8 +37,11 @@ setenv = # To avoid per-test function process safety issues we run all tests in a single # file in the same worker process. # for pytest-xdist, we want to distribute tests by file aka --dist loadfile +# Tests which are not safe to run in paralell are marked with "serial" tag +# and run separately at the end commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 --timeout=15 -n auto --dist loadfile --ignore libcloud/test/benchmarks/ --ignore-glob "*test_list_objects_filtering_performance*" + pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 --timeout=15 -n auto --dist loadfile --ignore libcloud/test/benchmarks/ --ignore-glob "*test_list_objects_filtering_performance*" -m "not serial" + pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 --timeout=15 --ignore libcloud/test/benchmarks/ --ignore-glob "*test_list_objects_filtering_performance*" -m "serial" [testenv:py3.8-dist] # Verify library installs without any dependencies when using python setup.py From 5ce6c01d18c452c34249b20482b235d58177d17e Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 18:23:24 +0200 Subject: [PATCH 06/15] Fix lint. --- libcloud/test/test_init.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libcloud/test/test_init.py b/libcloud/test/test_init.py index 07ca57ca83..d35bf9c31e 100644 --- a/libcloud/test/test_init.py +++ b/libcloud/test/test_init.py @@ -21,6 +21,7 @@ from unittest.mock import patch import pytest + import libcloud from libcloud import _init_once, reset_debug from libcloud.base import DriverTypeNotFoundError From def28c943f52b7752184944cbf09afa190692b29 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 18:26:08 +0200 Subject: [PATCH 07/15] Upgrade pytest to 8.1.1 since it includes some changes which may fix weird race condition we've been seeing. --- requirements-tests.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-tests.txt b/requirements-tests.txt index 517a078c25..0833871974 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -1,7 +1,7 @@ coverage[toml]==7.2.7; python_version >= '3.8' requests>=2.31.0 requests_mock==1.11.0 -pytest==8.0.2 +pytest==8.1.1 pytest-xdist==3.5.0 pytest-timeout==2.2.0 pytest-benchmark[histogram]==4.0.0 From 9e2a11d50766ca7d913ee6208d5aa7d61a7452a5 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 18:39:03 +0200 Subject: [PATCH 08/15] Test a change to see if it helps with cross test pollution and related CI test run failures. --- libcloud/test/common/test_digitalocean_v2.py | 17 ++++-- libcloud/test/compute/test_digitalocean_v2.py | 57 ++++++++++--------- libcloud/test/dns/test_digitalocean.py | 5 ++ 3 files changed, 48 insertions(+), 31 deletions(-) diff --git a/libcloud/test/common/test_digitalocean_v2.py b/libcloud/test/common/test_digitalocean_v2.py index e73979ae8e..90fc365f3a 100644 --- a/libcloud/test/common/test_digitalocean_v2.py +++ b/libcloud/test/common/test_digitalocean_v2.py @@ -15,22 +15,29 @@ import sys import unittest +from libcloud.http import LibcloudConnection from libcloud.test import MockHttp, LibcloudTestCase from libcloud.utils.py3 import httplib from libcloud.common.types import InvalidCredsError from libcloud.test.secrets import DIGITALOCEAN_v2_PARAMS from libcloud.test.file_fixtures import FileFixtures from libcloud.common.digitalocean import DigitalOceanBaseDriver +from libcloud.dns.drivers.digitalocean import DigitalOceanDNSDriver class DigitalOceanTests(LibcloudTestCase): def setUp(self): - DigitalOceanBaseDriver.connectionCls.conn_class = DigitalOceanMockHttp - DigitalOceanMockHttp.type = None + DigitalOceanBaseDriver.connectionCls.conn_class = DigitalOceanCommonMockHttp + DigitalOceanCommonMockHttp.type = None self.driver = DigitalOceanBaseDriver(*DIGITALOCEAN_v2_PARAMS) + def tearDown(self): + LibcloudConnection.type = None + DigitalOceanCommonMockHttp.type = None + DigitalOceanBaseDriver.connectionCls.conn_class = LibcloudConnection + def test_authentication(self): - DigitalOceanMockHttp.type = "UNAUTHORIZED" + DigitalOceanCommonMockHttp.type = "UNAUTHORIZED" self.assertRaises(InvalidCredsError, self.driver.ex_account_info) def test_ex_account_info(self): @@ -51,13 +58,13 @@ def test_ex_get_event(self): self.assertEqual(action["type"], "power_on") def test__paginated_request(self): - DigitalOceanMockHttp.type = "page_1" + DigitalOceanCommonMockHttp.type = "page_1" actions = self.driver._paginated_request("/v2/actions", "actions") self.assertEqual(actions[0]["id"], 12345671) self.assertEqual(actions[0]["status"], "completed") -class DigitalOceanMockHttp(MockHttp): +class DigitalOceanCommonMockHttp(MockHttp): fixtures = FileFixtures("common", "digitalocean") response = { diff --git a/libcloud/test/compute/test_digitalocean_v2.py b/libcloud/test/compute/test_digitalocean_v2.py index 168ada99a4..e5593284dc 100644 --- a/libcloud/test/compute/test_digitalocean_v2.py +++ b/libcloud/test/compute/test_digitalocean_v2.py @@ -16,6 +16,7 @@ import unittest from datetime import datetime +from libcloud.http import LibcloudConnection from libcloud.test import MockHttp, LibcloudTestCase from libcloud.utils.py3 import httplib, assertRaisesRegex from libcloud.common.types import InvalidCredsError @@ -35,10 +36,14 @@ # class DigitalOceanTests(unittest.TestCase, TestCaseMixin): class DigitalOcean_v2_Tests(LibcloudTestCase): def setUp(self): - DigitalOceanNodeDriver.connectionCls.conn_class = DigitalOceanMockHttp - DigitalOceanMockHttp.type = None + DigitalOceanNodeDriver.connectionCls.conn_class = DigitalOceanComputeMockHttp + DigitalOceanComputeMockHttp.type = None self.driver = DigitalOceanNodeDriver(*DIGITALOCEAN_v2_PARAMS) + def tearDown(self): + LibcloudConnection.type = None + DigitalOceanComputeMockHttp.type = None + def test_v1_Error(self): self.assertRaises( DigitalOcean_v1_Error, @@ -56,7 +61,7 @@ def test_v2_uses_v1_key(self): ) def test_authentication(self): - DigitalOceanMockHttp.type = "UNAUTHORIZED" + DigitalOceanComputeMockHttp.type = "UNAUTHORIZED" self.assertRaises(InvalidCredsError, self.driver.list_nodes) def test_list_images_success(self): @@ -128,7 +133,7 @@ def test_create_node_invalid_size(self): size = self.driver.list_sizes()[0] location = self.driver.list_locations()[0] - DigitalOceanMockHttp.type = "INVALID_IMAGE" + DigitalOceanComputeMockHttp.type = "INVALID_IMAGE" expected_msg = ( r"You specified an invalid image for Droplet creation." + r" \(code: (404|HTTPStatus.NOT_FOUND)\)" @@ -146,13 +151,13 @@ def test_create_node_invalid_size(self): def test_reboot_node_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "REBOOT" + DigitalOceanComputeMockHttp.type = "REBOOT" result = self.driver.reboot_node(node) self.assertTrue(result) def test_create_image_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "SNAPSHOT" + DigitalOceanComputeMockHttp.type = "SNAPSHOT" result = self.driver.create_image(node, "My snapshot") self.assertTrue(result) @@ -164,62 +169,62 @@ def test_get_image_success(self): def test_delete_image_success(self): image = self.driver.get_image(12345) - DigitalOceanMockHttp.type = "DESTROY" + DigitalOceanComputeMockHttp.type = "DESTROY" result = self.driver.delete_image(image) self.assertTrue(result) def test_ex_power_on_node_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "POWERON" + DigitalOceanComputeMockHttp.type = "POWERON" result = self.driver.ex_power_on_node(node) self.assertTrue(result) def test_ex_shutdown_node_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "SHUTDOWN" + DigitalOceanComputeMockHttp.type = "SHUTDOWN" result = self.driver.ex_shutdown_node(node) self.assertTrue(result) def test_ex_hard_reboot_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "POWERCYCLE" + DigitalOceanComputeMockHttp.type = "POWERCYCLE" result = self.driver.ex_hard_reboot(node) self.assertTrue(result) def test_ex_rebuild_node_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "REBUILD" + DigitalOceanComputeMockHttp.type = "REBUILD" result = self.driver.ex_rebuild_node(node) self.assertTrue(result) def test_ex_resize_node_success(self): node = self.driver.list_nodes()[0] size = self.driver.list_sizes()[0] - DigitalOceanMockHttp.type = "RESIZE" + DigitalOceanComputeMockHttp.type = "RESIZE" result = self.driver.ex_resize_node(node, size) self.assertTrue(result) def test_destroy_node_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "DESTROY" + DigitalOceanComputeMockHttp.type = "DESTROY" result = self.driver.destroy_node(node) self.assertTrue(result) def test_ex_change_kernel_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "KERNELCHANGE" + DigitalOceanComputeMockHttp.type = "KERNELCHANGE" result = self.driver.ex_change_kernel(node, 7515) self.assertTrue(result) def test_ex_enable_ipv6_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "ENABLEIPV6" + DigitalOceanComputeMockHttp.type = "ENABLEIPV6" result = self.driver.ex_enable_ipv6(node) self.assertTrue(result) def test_ex_rename_node_success(self): node = self.driver.list_nodes()[0] - DigitalOceanMockHttp.type = "RENAME" + DigitalOceanComputeMockHttp.type = "RENAME" result = self.driver.ex_rename_node(node, "fedora helios") self.assertTrue(result) @@ -231,7 +236,7 @@ def test_list_key_pairs(self): self.assertEqual(keys[0].public_key, "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAQQDGk5 example") def test_create_key_pair(self): - DigitalOceanMockHttp.type = "CREATE" + DigitalOceanComputeMockHttp.type = "CREATE" key = self.driver.create_key_pair( name="test1", public_key="ssh-rsa AAAAB3NzaC1yc2EAAAADAQsxRiUKn example" ) @@ -250,7 +255,7 @@ def test__paginated_request_single_page(self): self.assertEqual(nodes[0]["size_slug"], "s-1vcpu-1gb") def test__paginated_request_two_pages(self): - DigitalOceanMockHttp.type = "PAGE_ONE" + DigitalOceanComputeMockHttp.type = "PAGE_ONE" nodes = self.driver._paginated_request("/v2/droplets", "droplets") self.assertEqual(len(nodes), 2) @@ -264,13 +269,13 @@ def test_list_volumes(self): self.assertEqual(volume.driver, self.driver) def test_list_volumes_empty(self): - DigitalOceanMockHttp.type = "EMPTY" + DigitalOceanComputeMockHttp.type = "EMPTY" volumes = self.driver.list_volumes() self.assertEqual(len(volumes), 0) def test_create_volume(self): nyc1 = [r for r in self.driver.list_locations() if r.id == "nyc1"][0] - DigitalOceanMockHttp.type = "CREATE" + DigitalOceanComputeMockHttp.type = "CREATE" volume = self.driver.create_volume(4, "example", nyc1) self.assertEqual(volume.id, "62766883-2c28-11e6-b8e6-000f53306ae1") self.assertEqual(volume.name, "example") @@ -280,19 +285,19 @@ def test_create_volume(self): def test_attach_volume(self): node = self.driver.list_nodes()[0] volume = self.driver.list_volumes()[0] - DigitalOceanMockHttp.type = "ATTACH" + DigitalOceanComputeMockHttp.type = "ATTACH" resp = self.driver.attach_volume(node, volume) self.assertTrue(resp) def test_detach_volume(self): volume = self.driver.list_volumes()[0] - DigitalOceanMockHttp.type = "DETACH" + DigitalOceanComputeMockHttp.type = "DETACH" resp = self.driver.detach_volume(volume) self.assertTrue(resp) def test_destroy_volume(self): volume = self.driver.list_volumes()[0] - DigitalOceanMockHttp.type = "DESTROY" + DigitalOceanComputeMockHttp.type = "DESTROY" resp = self.driver.destroy_volume(volume) self.assertTrue(resp) @@ -307,7 +312,7 @@ def test_list_volume_snapshots(self): def test_create_volume_snapshot(self): volume = self.driver.list_volumes()[0] - DigitalOceanMockHttp.type = "CREATE" + DigitalOceanComputeMockHttp.type = "CREATE" snapshot = self.driver.create_volume_snapshot(volume, "test-snapshot") self.assertEqual(snapshot.id, "c0def940-9324-11e6-9a56-000f533176b1") self.assertEqual(snapshot.name, "test-snapshot") @@ -316,7 +321,7 @@ def test_create_volume_snapshot(self): def test_delete_volume_snapshot(self): volume = self.driver.list_volumes()[0] snapshot = self.driver.list_volume_snapshots(volume)[0] - DigitalOceanMockHttp.type = "DELETE" + DigitalOceanComputeMockHttp.type = "DELETE" result = self.driver.delete_volume_snapshot(snapshot) self.assertTrue(result) @@ -396,7 +401,7 @@ def test_ex_detach_floating_ip_from_node(self): self.assertTrue(ret) -class DigitalOceanMockHttp(MockHttp): +class DigitalOceanComputeMockHttp(MockHttp): fixtures = ComputeFileFixtures("digitalocean_v2") def _v2_regions(self, method, url, body, headers): diff --git a/libcloud/test/dns/test_digitalocean.py b/libcloud/test/dns/test_digitalocean.py index 41c1958749..e65435d45a 100644 --- a/libcloud/test/dns/test_digitalocean.py +++ b/libcloud/test/dns/test_digitalocean.py @@ -15,6 +15,7 @@ import sys import unittest +from libcloud.http import LibcloudConnection from libcloud.test import MockHttp, LibcloudTestCase from libcloud.dns.types import RecordType from libcloud.utils.py3 import httplib @@ -29,6 +30,10 @@ def setUp(self): DigitalOceanDNSMockHttp.type = None self.driver = DigitalOceanDNSDriver(*DIGITALOCEAN_v2_PARAMS) + def tearDown(self): + LibcloudConnection.type = None + DigitalOceanDNSMockHttp.type = None + def test_list_zones(self): zones = self.driver.list_zones() self.assertTrue(len(zones) >= 1) From 841dea766478f37c086dbc19f0a88c5107dad99b Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 18:57:24 +0200 Subject: [PATCH 09/15] Fix bug in the base MockHttp class which was there for a long time since we incorrectly used class attribute value instead of the value passed to the method. This could result in all kinds of weird edge cases and test failures in case the environment wasn't correctly reset before / after each test. --- libcloud/test/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcloud/test/__init__.py b/libcloud/test/__init__.py index 37a74b2b3b..d45c82c84d 100644 --- a/libcloud/test/__init__.py +++ b/libcloud/test/__init__.py @@ -205,7 +205,7 @@ def _get_method_name(self, type, use_param, qs, path): ) # Python 3.7 no longer quotes ~ if type: - meth_name = "{}_{}".format(meth_name, self.type) + meth_name = "{}_{}".format(meth_name, type) if use_param and use_param in qs: param = qs[use_param][0].replace(".", "_").replace("-", "_") From 7267fc4f9480c15a8286b98f8063d9093665b01f Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 19:06:09 +0200 Subject: [PATCH 10/15] Additional changes to the DigitalOcean tests to avoid cross test pollution and related failures when running tests in parallel. Also remove other changes which were added which are not actually needed. --- libcloud/test/common/test_digitalocean_v2.py | 8 ++------ libcloud/test/compute/test_digitalocean_v2.py | 4 ---- libcloud/test/dns/test_digitalocean.py | 4 ---- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/libcloud/test/common/test_digitalocean_v2.py b/libcloud/test/common/test_digitalocean_v2.py index 90fc365f3a..0f39e9ceee 100644 --- a/libcloud/test/common/test_digitalocean_v2.py +++ b/libcloud/test/common/test_digitalocean_v2.py @@ -21,21 +21,17 @@ from libcloud.common.types import InvalidCredsError from libcloud.test.secrets import DIGITALOCEAN_v2_PARAMS from libcloud.test.file_fixtures import FileFixtures -from libcloud.common.digitalocean import DigitalOceanBaseDriver +from libcloud.common.digitalocean import DigitalOceanBaseDriver, DigitalOcean_v2_BaseDriver from libcloud.dns.drivers.digitalocean import DigitalOceanDNSDriver class DigitalOceanTests(LibcloudTestCase): def setUp(self): DigitalOceanBaseDriver.connectionCls.conn_class = DigitalOceanCommonMockHttp + DigitalOcean_v2_BaseDriver.connectionCls.conn_class = DigitalOceanCommonMockHttp DigitalOceanCommonMockHttp.type = None self.driver = DigitalOceanBaseDriver(*DIGITALOCEAN_v2_PARAMS) - def tearDown(self): - LibcloudConnection.type = None - DigitalOceanCommonMockHttp.type = None - DigitalOceanBaseDriver.connectionCls.conn_class = LibcloudConnection - def test_authentication(self): DigitalOceanCommonMockHttp.type = "UNAUTHORIZED" self.assertRaises(InvalidCredsError, self.driver.ex_account_info) diff --git a/libcloud/test/compute/test_digitalocean_v2.py b/libcloud/test/compute/test_digitalocean_v2.py index e5593284dc..310f2e7788 100644 --- a/libcloud/test/compute/test_digitalocean_v2.py +++ b/libcloud/test/compute/test_digitalocean_v2.py @@ -40,10 +40,6 @@ def setUp(self): DigitalOceanComputeMockHttp.type = None self.driver = DigitalOceanNodeDriver(*DIGITALOCEAN_v2_PARAMS) - def tearDown(self): - LibcloudConnection.type = None - DigitalOceanComputeMockHttp.type = None - def test_v1_Error(self): self.assertRaises( DigitalOcean_v1_Error, diff --git a/libcloud/test/dns/test_digitalocean.py b/libcloud/test/dns/test_digitalocean.py index e65435d45a..952c060acf 100644 --- a/libcloud/test/dns/test_digitalocean.py +++ b/libcloud/test/dns/test_digitalocean.py @@ -30,10 +30,6 @@ def setUp(self): DigitalOceanDNSMockHttp.type = None self.driver = DigitalOceanDNSDriver(*DIGITALOCEAN_v2_PARAMS) - def tearDown(self): - LibcloudConnection.type = None - DigitalOceanDNSMockHttp.type = None - def test_list_zones(self): zones = self.driver.list_zones() self.assertTrue(len(zones) >= 1) From 923a3aec2504fd033e0de0a5d6487b3ad98e1baa Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 19:23:22 +0200 Subject: [PATCH 11/15] For now, don't run problematic tests with coverage tox target. --- tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index a1dc821956..abe1a57a1b 100644 --- a/tox.ini +++ b/tox.ini @@ -327,13 +327,14 @@ deps = -r{toxinidir}/integration/storage/requirements.txt commands = pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 integration/storage +# NOTE: For now, we don't run problematic serial tests with coverage target [testenv:coverage] deps = -r{toxinidir}/requirements-tests.txt setenv = CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - coverage run --source=libcloud -m pytest --benchmark-disable + coverage run --source=libcloud -m pytest --benchmark-disable -m "not serial" [testenv:coverage-ci] passenv = @@ -346,7 +347,7 @@ deps = setenv = CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - coverage run --source=libcloud -m pytest --benchmark-disable + coverage run --source=libcloud -m pytest --benchmark-disable -m "not serial" coverage xml [testenv:isort] From 6ba2819ccbadad5fe89984ea2cef8313ba432aae Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 19:25:27 +0200 Subject: [PATCH 12/15] Try fixing problematic init_once tests. --- libcloud/test/test_init.py | 3 ++- tox.ini | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libcloud/test/test_init.py b/libcloud/test/test_init.py index d35bf9c31e..0bf18ea855 100644 --- a/libcloud/test/test_init.py +++ b/libcloud/test/test_init.py @@ -44,9 +44,10 @@ def setUp(self): # Reset debug level reset_debug() - # Reset paramiko log level + # Reset paramiko log level and handlers if have_paramiko: paramiko_logger = logging.getLogger("paramiko") + paramiko_logger.handlers = [] paramiko_logger.setLevel(logging.INFO) @mock.patch.dict(os.environ, {"LIBCLOUD_DEBUG": ""}, clear=True) diff --git a/tox.ini b/tox.ini index abe1a57a1b..a1dc821956 100644 --- a/tox.ini +++ b/tox.ini @@ -327,14 +327,13 @@ deps = -r{toxinidir}/integration/storage/requirements.txt commands = pytest --color=yes -rsx -vvv --capture=tee-sys -o log_cli=True --durations=10 integration/storage -# NOTE: For now, we don't run problematic serial tests with coverage target [testenv:coverage] deps = -r{toxinidir}/requirements-tests.txt setenv = CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - coverage run --source=libcloud -m pytest --benchmark-disable -m "not serial" + coverage run --source=libcloud -m pytest --benchmark-disable [testenv:coverage-ci] passenv = @@ -347,7 +346,7 @@ deps = setenv = CRYPTOGRAPHY_ALLOW_OPENSSL_102=1 commands = cp libcloud/test/secrets.py-dist libcloud/test/secrets.py - coverage run --source=libcloud -m pytest --benchmark-disable -m "not serial" + coverage run --source=libcloud -m pytest --benchmark-disable coverage xml [testenv:isort] From f4fe2fbfd17f6039940afe08b97a9e7241ee9075 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 19:53:32 +0200 Subject: [PATCH 13/15] Also clean up / rest DigitalOcean mock http classes as part of tearDown. --- libcloud/test/common/test_digitalocean_v2.py | 5 ++++- libcloud/test/compute/test_digitalocean_v2.py | 18 ++++++++++++++++-- libcloud/test/dns/test_digitalocean.py | 7 +++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/libcloud/test/common/test_digitalocean_v2.py b/libcloud/test/common/test_digitalocean_v2.py index 0f39e9ceee..957f7a66bc 100644 --- a/libcloud/test/common/test_digitalocean_v2.py +++ b/libcloud/test/common/test_digitalocean_v2.py @@ -22,7 +22,6 @@ from libcloud.test.secrets import DIGITALOCEAN_v2_PARAMS from libcloud.test.file_fixtures import FileFixtures from libcloud.common.digitalocean import DigitalOceanBaseDriver, DigitalOcean_v2_BaseDriver -from libcloud.dns.drivers.digitalocean import DigitalOceanDNSDriver class DigitalOceanTests(LibcloudTestCase): @@ -32,6 +31,10 @@ def setUp(self): DigitalOceanCommonMockHttp.type = None self.driver = DigitalOceanBaseDriver(*DIGITALOCEAN_v2_PARAMS) + def tearDown(self): + LibcloudConnection.type = None + DigitalOceanCommonMockHttp.type = None + def test_authentication(self): DigitalOceanCommonMockHttp.type = "UNAUTHORIZED" self.assertRaises(InvalidCredsError, self.driver.ex_account_info) diff --git a/libcloud/test/compute/test_digitalocean_v2.py b/libcloud/test/compute/test_digitalocean_v2.py index 310f2e7788..7ff55ce881 100644 --- a/libcloud/test/compute/test_digitalocean_v2.py +++ b/libcloud/test/compute/test_digitalocean_v2.py @@ -24,8 +24,15 @@ from libcloud.test.secrets import DIGITALOCEAN_v1_PARAMS, DIGITALOCEAN_v2_PARAMS from libcloud.utils.iso8601 import UTC from libcloud.test.file_fixtures import ComputeFileFixtures -from libcloud.common.digitalocean import DigitalOcean_v1_Error -from libcloud.compute.drivers.digitalocean import DigitalOceanNodeDriver +from libcloud.common.digitalocean import ( + DigitalOcean_v1_Error, + DigitalOceanBaseDriver, + DigitalOcean_v2_BaseDriver, +) +from libcloud.compute.drivers.digitalocean import ( + DigitalOceanNodeDriver, + DigitalOcean_v2_NodeDriver, +) try: import simplejson as json @@ -36,10 +43,17 @@ # class DigitalOceanTests(unittest.TestCase, TestCaseMixin): class DigitalOcean_v2_Tests(LibcloudTestCase): def setUp(self): + DigitalOceanBaseDriver.connectionCls.conn_class = DigitalOceanComputeMockHttp + DigitalOcean_v2_BaseDriver.connectionCls.conn_class = DigitalOceanComputeMockHttp DigitalOceanNodeDriver.connectionCls.conn_class = DigitalOceanComputeMockHttp + DigitalOcean_v2_NodeDriver.connectionCls.conn_class = DigitalOceanComputeMockHttp DigitalOceanComputeMockHttp.type = None self.driver = DigitalOceanNodeDriver(*DIGITALOCEAN_v2_PARAMS) + def tearDown(self): + LibcloudConnection.type = None + DigitalOceanComputeMockHttp.type = None + def test_v1_Error(self): self.assertRaises( DigitalOcean_v1_Error, diff --git a/libcloud/test/dns/test_digitalocean.py b/libcloud/test/dns/test_digitalocean.py index 952c060acf..2b2118b385 100644 --- a/libcloud/test/dns/test_digitalocean.py +++ b/libcloud/test/dns/test_digitalocean.py @@ -21,15 +21,22 @@ from libcloud.utils.py3 import httplib from libcloud.test.secrets import DIGITALOCEAN_v2_PARAMS from libcloud.test.file_fixtures import DNSFileFixtures +from libcloud.common.digitalocean import DigitalOceanBaseDriver, DigitalOcean_v2_BaseDriver from libcloud.dns.drivers.digitalocean import DigitalOceanDNSDriver class DigitalOceanDNSTests(LibcloudTestCase): def setUp(self): + DigitalOceanBaseDriver.connectionCls.conn_class = DigitalOceanDNSMockHttp + DigitalOcean_v2_BaseDriver.connectionCls.conn_class = DigitalOceanDNSMockHttp DigitalOceanDNSDriver.connectionCls.conn_class = DigitalOceanDNSMockHttp DigitalOceanDNSMockHttp.type = None self.driver = DigitalOceanDNSDriver(*DIGITALOCEAN_v2_PARAMS) + def tearDown(self): + LibcloudConnection.type = None + DigitalOceanDNSMockHttp.type = None + def test_list_zones(self): zones = self.driver.list_zones() self.assertTrue(len(zones) >= 1) From e93deea66e7da5ed2751e3ce83b0a6c5d53902db Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 21:46:43 +0200 Subject: [PATCH 14/15] Add changelog entry for #1994. --- CHANGES.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 092a402303..c9e999187d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -67,6 +67,22 @@ Storage (#1996) [Salih Kerem Dokuz - @keremdokuz] +Other / Development +~~~~~-------------- + +- pytest library used for running tests and microbenchmarks has been upgraded to + v8.1. + + Changes in the pytest test discovery and collection mechanism and ordering + have uncovered some race conditions and cross test pollution which has been + addressed. + + Now all the tests are passing, but it's possible that there are still some + race conditions hiding which many only pop up in the future (since we run + tests in parallel and order in which they run is not fully deterministic). + (#1994) + [Tomaz Muraus - @Kami] + Changes in Apache Libcloud 3.8.0 -------------------------------- From 532bb183ba825b6b4574b78da2c3edcc2d94b535 Mon Sep 17 00:00:00 2001 From: Tomaz Muraus Date: Tue, 16 Apr 2024 21:48:22 +0200 Subject: [PATCH 15/15] Also run rstcheck on README.rst file. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a1dc821956..64f6bbb1da 100644 --- a/tox.ini +++ b/tox.ini @@ -142,7 +142,7 @@ commands = bash -c "./scripts/dist_wheel_install_check.sh" deps = -r{toxinidir}/requirements-docs.txt changedir = docs -commands = rstcheck --report-level warning ../CHANGES.rst +commands = rstcheck --report-level warning ../README.rst rstcheck --report-level warning ../CHANGES.rst rstcheck --report-level warning ../CONTRIBUTING.rst python ../contrib/generate_provider_feature_matrix_table.py