Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6a646d0
Bump pytest from 7.4.0 to 8.0.2
dependabot[bot] Feb 26, 2024
8192d0e
Merge branch 'trunk' into dependabot/pip/pytest-8.0.2
Kami Apr 14, 2024
dc68310
Merge branch 'trunk' into dependabot/pip/pytest-8.0.2
Kami Apr 14, 2024
5e0e675
Merge branch 'trunk' into dependabot/pip/pytest-8.0.2
Kami Apr 15, 2024
7fa07c4
Merge branch 'trunk' into dependabot/pip/pytest-8.0.2
Kami Apr 15, 2024
20ae731
Try using loadscope pytest-xdist test distribution method to see if this
Kami Apr 15, 2024
83a1114
Revert "Try using loadscope pytest-xdist test distribution method to …
Kami Apr 15, 2024
836eeee
Merge branch 'trunk' of http://gitbox.apache.org/repos/asf/libcloud i…
Kami Apr 16, 2024
60b4817
Upgrade pytest-xdist dependency.
Kami Apr 16, 2024
587bf98
Merge branch 'trunk' of http://gitbox.apache.org/repos/asf/libcloud i…
Kami Apr 16, 2024
a52f5d9
Try a workaround for tests which started failing with new pytest
Kami Apr 16, 2024
5ce6c01
Fix lint.
Kami Apr 16, 2024
def28c9
Upgrade pytest to 8.1.1 since it includes some changes which may fix
Kami Apr 16, 2024
9e2a11d
Test a change to see if it helps with cross test pollution and related
Kami Apr 16, 2024
841dea7
Fix bug in the base MockHttp class which was there for a long time since
Kami Apr 16, 2024
7267fc4
Additional changes to the DigitalOcean tests to avoid cross test
Kami Apr 16, 2024
923a3ae
For now, don't run problematic tests with coverage tox target.
Kami Apr 16, 2024
6ba2819
Try fixing problematic init_once tests.
Kami Apr 16, 2024
f4fe2fb
Also clean up / rest DigitalOcean mock http classes as part of tearDown.
Kami Apr 16, 2024
e93deea
Add changelog entry for #1994.
Kami Apr 16, 2024
532bb18
Also run rstcheck on README.rst file.
Kami Apr 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------------------------------

Expand Down
2 changes: 1 addition & 1 deletion libcloud/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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("-", "_")
Expand Down
18 changes: 12 additions & 6 deletions libcloud/test/common/test_digitalocean_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,28 @@
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.common.digitalocean import DigitalOceanBaseDriver, DigitalOcean_v2_BaseDriver


class DigitalOceanTests(LibcloudTestCase):
def setUp(self):
DigitalOceanBaseDriver.connectionCls.conn_class = DigitalOceanMockHttp
DigitalOceanMockHttp.type = None
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

def test_authentication(self):
DigitalOceanMockHttp.type = "UNAUTHORIZED"
DigitalOceanCommonMockHttp.type = "UNAUTHORIZED"
self.assertRaises(InvalidCredsError, self.driver.ex_account_info)

def test_ex_account_info(self):
Expand All @@ -51,13 +57,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 = {
Expand Down
71 changes: 43 additions & 28 deletions libcloud/test/compute/test_digitalocean_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,23 @@
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
from libcloud.compute.base import NodeImage
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
Expand All @@ -35,10 +43,17 @@
# class DigitalOceanTests(unittest.TestCase, TestCaseMixin):
class DigitalOcean_v2_Tests(LibcloudTestCase):
def setUp(self):
DigitalOceanNodeDriver.connectionCls.conn_class = DigitalOceanMockHttp
DigitalOceanMockHttp.type = None
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,
Expand All @@ -56,7 +71,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):
Expand Down Expand Up @@ -128,7 +143,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)\)"
Expand All @@ -146,13 +161,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)

Expand All @@ -164,62 +179,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)

Expand All @@ -231,7 +246,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"
)
Expand All @@ -250,7 +265,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)

Expand All @@ -264,13 +279,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")
Expand All @@ -280,19 +295,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)

Expand All @@ -307,7 +322,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")
Expand All @@ -316,7 +331,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)

Expand Down Expand Up @@ -396,7 +411,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):
Expand Down
8 changes: 8 additions & 0 deletions libcloud/test/dns/test_digitalocean.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,28 @@
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
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)
Expand Down
7 changes: 6 additions & 1 deletion libcloud/test/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
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
Expand All @@ -42,12 +44,14 @@ 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)
@pytest.mark.serial
def test_init_once_and_no_debug_mode(self):
if have_paramiko:
paramiko_logger = logging.getLogger("paramiko")
Expand All @@ -68,6 +72,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")
Expand Down
4 changes: 2 additions & 2 deletions requirements-tests.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
coverage[toml]==7.2.7; python_version >= '3.8'
requests>=2.31.0
requests_mock==1.11.0
pytest==7.4.0
pytest-xdist==3.3.1
pytest==8.1.1
pytest-xdist==3.5.0
pytest-timeout==2.2.0
pytest-benchmark[histogram]==4.0.0
cryptography==42.0.5
Expand Down
Loading