summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMonty Taylor <mordred@inaugust.com>2012-10-25 17:23:53 -0700
committerMonty Taylor <mordred@inaugust.com>2012-11-10 12:10:21 -0800
commit8eea01449e7b43a5729df31df9f3f3ffeb55ec78 (patch)
tree959216e5a3a10bc08382fe7dc35a59c53bd9d9ed
parent22ece8529dfb88c3129600212822a2066e773c7b (diff)
downloadnova-8eea01449e7b43a5729df31df9f3f3ffeb55ec78.tar.gz
nova-8eea01449e7b43a5729df31df9f3f3ffeb55ec78.tar.xz
nova-8eea01449e7b43a5729df31df9f3f3ffeb55ec78.zip
Remove custom test assertions.
Now that we testtools, We can use stock testtools assert methods instead of defining our own. As part of removing them, we add Matcher classes to use in the testtools.assertThat method. testtools matchers can be arbitrarily combined and chained, so give more flexibility than plain asserts Related to blueprint grizzly-testtools. Change-Id: I26d1dbac8dc3c322eb55c96c48330c0e38636107
-rw-r--r--nova/test.py83
-rw-r--r--nova/tests/api/ec2/test_cinder_cloud.py28
-rw-r--r--nova/tests/api/ec2/test_cloud.py9
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_aggregates.py4
-rw-r--r--nova/tests/api/openstack/compute/contrib/test_cloudpipe.py3
-rw-r--r--nova/tests/api/openstack/compute/test_consoles.py5
-rw-r--r--nova/tests/api/openstack/compute/test_extensions.py9
-rw-r--r--nova/tests/api/openstack/compute/test_flavors.py9
-rw-r--r--nova/tests/api/openstack/compute/test_images.py12
-rw-r--r--nova/tests/api/openstack/compute/test_limits.py5
-rw-r--r--nova/tests/api/openstack/compute/test_server_actions.py5
-rw-r--r--nova/tests/api/openstack/compute/test_servers.py46
-rw-r--r--nova/tests/api/openstack/compute/test_versions.py7
-rw-r--r--nova/tests/compute/test_compute.py8
-rw-r--r--nova/tests/image/test_glance.py11
-rw-r--r--nova/tests/matchers.py196
-rw-r--r--nova/tests/network/test_manager.py9
-rw-r--r--nova/tests/scheduler/test_host_manager.py9
-rw-r--r--nova/tests/scheduler/test_least_cost.py5
-rw-r--r--nova/tests/scheduler/test_scheduler.py7
-rw-r--r--nova/tests/test_api.py7
-rw-r--r--nova/tests/test_bdm.py3
-rw-r--r--nova/tests/test_db_api.py18
-rw-r--r--nova/tests/test_libvirt.py23
-rw-r--r--nova/tests/test_matchers.py144
-rw-r--r--nova/tests/test_xenapi.py11
26 files changed, 483 insertions, 193 deletions
diff --git a/nova/test.py b/nova/test.py
index feb489b30..9efbf40b0 100644
--- a/nova/test.py
+++ b/nova/test.py
@@ -141,86 +141,3 @@ class TestCase(testtools.TestCase):
svc.start()
self._services.append(svc)
return svc
-
- # Useful assertions
- def assertDictMatch(self, d1, d2, approx_equal=False, tolerance=0.001):
- """Assert two dicts are equivalent.
-
- This is a 'deep' match in the sense that it handles nested
- dictionaries appropriately.
-
- NOTE:
-
- If you don't care (or don't know) a given value, you can specify
- the string DONTCARE as the value. This will cause that dict-item
- to be skipped.
-
- """
- def raise_assertion(msg):
- d1str = str(d1)
- d2str = str(d2)
- base_msg = ('Dictionaries do not match. %(msg)s d1: %(d1str)s '
- 'd2: %(d2str)s' % locals())
- raise AssertionError(base_msg)
-
- d1keys = set(d1.keys())
- d2keys = set(d2.keys())
- if d1keys != d2keys:
- d1only = d1keys - d2keys
- d2only = d2keys - d1keys
- raise_assertion('Keys in d1 and not d2: %(d1only)s. '
- 'Keys in d2 and not d1: %(d2only)s' % locals())
-
- for key in d1keys:
- d1value = d1[key]
- d2value = d2[key]
- try:
- error = abs(float(d1value) - float(d2value))
- within_tolerance = error <= tolerance
- except (ValueError, TypeError):
- # If both values aren't convertible to float, just ignore
- # ValueError if arg is a str, TypeError if it's something else
- # (like None)
- within_tolerance = False
-
- if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
- self.assertDictMatch(d1value, d2value)
- elif 'DONTCARE' in (d1value, d2value):
- continue
- elif approx_equal and within_tolerance:
- continue
- elif d1value != d2value:
- raise_assertion("d1['%(key)s']=%(d1value)s != "
- "d2['%(key)s']=%(d2value)s" % locals())
-
- def assertDictListMatch(self, L1, L2, approx_equal=False, tolerance=0.001):
- """Assert a list of dicts are equivalent."""
- def raise_assertion(msg):
- L1str = str(L1)
- L2str = str(L2)
- base_msg = ('List of dictionaries do not match: %(msg)s '
- 'L1: %(L1str)s L2: %(L2str)s' % locals())
- raise AssertionError(base_msg)
-
- L1count = len(L1)
- L2count = len(L2)
- if L1count != L2count:
- raise_assertion('Length mismatch: len(L1)=%(L1count)d != '
- 'len(L2)=%(L2count)d' % locals())
-
- for d1, d2 in zip(L1, L2):
- self.assertDictMatch(d1, d2, approx_equal=approx_equal,
- tolerance=tolerance)
-
- def assertSubDictMatch(self, sub_dict, super_dict):
- """Assert a sub_dict is subset of super_dict."""
- self.assertEqual(True,
- set(sub_dict.keys()).issubset(set(super_dict.keys())))
- for k, sub_value in sub_dict.items():
- super_value = super_dict[k]
- if isinstance(sub_value, dict):
- self.assertSubDictMatch(sub_value, super_value)
- elif 'DONTCARE' in (sub_value, super_value):
- continue
- else:
- self.assertEqual(sub_value, super_value)
diff --git a/nova/tests/api/ec2/test_cinder_cloud.py b/nova/tests/api/ec2/test_cinder_cloud.py
index 47f78ab75..8167a4b76 100644
--- a/nova/tests/api/ec2/test_cinder_cloud.py
+++ b/nova/tests/api/ec2/test_cinder_cloud.py
@@ -33,6 +33,7 @@ from nova.openstack.common import rpc
from nova import test
from nova.tests import fake_network
from nova.tests.image import fake
+from nova.tests import matchers
from nova import volume
@@ -434,18 +435,18 @@ class CinderCloudTestCase(test.TestCase):
result = {}
self.cloud._format_instance_bdm(self.context, inst1['uuid'],
'/dev/sdb1', result)
- self.assertSubDictMatch(
+ self.assertThat(
{'rootDeviceType': self._expected_instance_bdm1['rootDeviceType']},
- result)
+ matchers.IsSubDictOf(result))
self._assertEqualBlockDeviceMapping(
self._expected_block_device_mapping0, result['blockDeviceMapping'])
result = {}
self.cloud._format_instance_bdm(self.context, inst2['uuid'],
'/dev/sdc1', result)
- self.assertSubDictMatch(
+ self.assertThat(
{'rootDeviceType': self._expected_instance_bdm2['rootDeviceType']},
- result)
+ matchers.IsSubDictOf(result))
self._tearDownBlockDeviceMapping(inst1, inst2, volumes)
@@ -465,7 +466,7 @@ class CinderCloudTestCase(test.TestCase):
found = False
for y in result:
if x['deviceName'] == y['deviceName']:
- self.assertSubDictMatch(x, y)
+ self.assertThat(x, matchers.IsSubDictOf(y))
found = True
break
self.assertTrue(found)
@@ -477,24 +478,19 @@ class CinderCloudTestCase(test.TestCase):
(inst1, inst2, volumes) = self._setUpBlockDeviceMapping()
result = self._assertInstance(inst1['id'])
- self.assertSubDictMatch(self._expected_instance_bdm1, result)
+ self.assertThat(
+ self._expected_instance_bdm1,
+ matchers.IsSubDictOf(result))
self._assertEqualBlockDeviceMapping(
self._expected_block_device_mapping0, result['blockDeviceMapping'])
result = self._assertInstance(inst2['id'])
- self.assertSubDictMatch(self._expected_instance_bdm2, result)
+ self.assertThat(
+ self._expected_instance_bdm2,
+ matchers.IsSubDictOf(result))
self._tearDownBlockDeviceMapping(inst1, inst2, volumes)
- def assertDictListUnorderedMatch(self, L1, L2, key):
- self.assertEqual(len(L1), len(L2))
- for d1 in L1:
- self.assertTrue(key in d1)
- for d2 in L2:
- self.assertTrue(key in d2)
- if d1[key] == d2[key]:
- self.assertDictMatch(d1, d2)
-
def _setUpImageSet(self, create_volumes_and_snapshots=False):
mappings1 = [
{'device': '/dev/sda1', 'virtual': 'root'},
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index e4c423fcc..26202009b 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -43,6 +43,7 @@ from nova.openstack.common import rpc
from nova import test
from nova.tests import fake_network
from nova.tests.image import fake
+from nova.tests import matchers
from nova import utils
from nova.virt import fake as fake_virt
from nova import volume
@@ -963,7 +964,7 @@ class CloudTestCase(test.TestCase):
for d2 in L2:
self.assertTrue(key in d2)
if d1[key] == d2[key]:
- self.assertDictMatch(d1, d2)
+ self.assertThat(d1, matchers.DictMatches(d2))
def _setUpImageSet(self, create_volumes_and_snapshots=False):
mappings1 = [
@@ -1283,17 +1284,17 @@ class CloudTestCase(test.TestCase):
'imageType': 'machine',
'description': None}
result = self.cloud._format_image(image)
- self.assertDictMatch(result, expected)
+ self.assertThat(result, matchers.DictMatches(expected))
image['properties']['image_location'] = None
expected['imageLocation'] = 'None (name)'
result = self.cloud._format_image(image)
- self.assertDictMatch(result, expected)
+ self.assertThat(result, matchers.DictMatches(expected))
image['name'] = None
image['properties']['image_location'] = 'location'
expected['imageLocation'] = 'location'
expected['name'] = 'location'
result = self.cloud._format_image(image)
- self.assertDictMatch(result, expected)
+ self.assertThat(result, matchers.DictMatches(expected))
def test_deregister_image(self):
deregister_image = self.cloud.deregister_image
diff --git a/nova/tests/api/openstack/compute/contrib/test_aggregates.py b/nova/tests/api/openstack/compute/contrib/test_aggregates.py
index 4fa68bd7d..a209fdce8 100644
--- a/nova/tests/api/openstack/compute/contrib/test_aggregates.py
+++ b/nova/tests/api/openstack/compute/contrib/test_aggregates.py
@@ -22,6 +22,7 @@ from nova import context
from nova import exception
from nova.openstack.common import log as logging
from nova import test
+from nova.tests import matchers
LOG = logging.getLogger(__name__)
AGGREGATE_LIST = [
@@ -319,7 +320,8 @@ class AggregateTestCase(test.TestCase):
def stub_update_aggregate(context, aggregate, values):
self.assertEqual(context, self.context, "context")
self.assertEqual("1", aggregate, "aggregate")
- self.assertDictMatch(body["set_metadata"]['metadata'], values)
+ self.assertThat(body["set_metadata"]['metadata'],
+ matchers.DictMatches(values))
return AGGREGATE
self.stubs.Set(self.controller.api,
"update_aggregate_metadata",
diff --git a/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py b/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py
index 97b78f81e..d3d51471b 100644
--- a/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py
+++ b/nova/tests/api/openstack/compute/contrib/test_cloudpipe.py
@@ -24,6 +24,7 @@ from nova.openstack.common import timeutils
from nova import test
from nova.tests.api.openstack import fakes
from nova.tests import fake_network
+from nova.tests import matchers
from nova import utils
@@ -108,7 +109,7 @@ class CloudpipeTest(test.TestCase):
'state': 'running',
'instance_id': 7777,
'created_at': '1981-10-20T00:00:00Z'}]}
- self.assertDictMatch(res_dict, response)
+ self.assertThat(res_dict, matchers.DictMatches(response))
def test_cloudpipe_create(self):
def launch_vpn_instance(context):
diff --git a/nova/tests/api/openstack/compute/test_consoles.py b/nova/tests/api/openstack/compute/test_consoles.py
index 6ea8149cf..ccdc8b5e0 100644
--- a/nova/tests/api/openstack/compute/test_consoles.py
+++ b/nova/tests/api/openstack/compute/test_consoles.py
@@ -30,6 +30,7 @@ from nova import flags
from nova.openstack.common import timeutils
from nova import test
from nova.tests.api.openstack import fakes
+from nova.tests import matchers
from nova import utils
@@ -167,7 +168,7 @@ class ConsolesControllerTest(test.TestCase):
req = fakes.HTTPRequest.blank(self.url + '/20')
res_dict = self.controller.show(req, self.uuid, '20')
- self.assertDictMatch(res_dict, expected)
+ self.assertThat(res_dict, matchers.DictMatches(expected))
def test_show_console_unknown_console(self):
def fake_get_console(cons_self, context, instance_id, console_id):
@@ -211,7 +212,7 @@ class ConsolesControllerTest(test.TestCase):
req = fakes.HTTPRequest.blank(self.url)
res_dict = self.controller.index(req, self.uuid)
- self.assertDictMatch(res_dict, expected)
+ self.assertThat(res_dict, matchers.DictMatches(expected))
def test_delete_console(self):
def fake_get_console(cons_self, context, instance_id, console_id):
diff --git a/nova/tests/api/openstack/compute/test_extensions.py b/nova/tests/api/openstack/compute/test_extensions.py
index ceb90d24c..c556bfb65 100644
--- a/nova/tests/api/openstack/compute/test_extensions.py
+++ b/nova/tests/api/openstack/compute/test_extensions.py
@@ -29,6 +29,7 @@ from nova import flags
from nova.openstack.common import jsonutils
from nova import test
from nova.tests.api.openstack import fakes
+from nova.tests import matchers
FLAGS = flags.FLAGS
@@ -347,7 +348,7 @@ class ResourceExtensionTest(ExtensionTestCase):
"code": 400
}
}
- self.assertDictMatch(expected, body)
+ self.assertThat(expected, matchers.DictMatches(body))
def test_non_exist_resource(self):
res_ext = base_extensions.ResourceExtension('tweedles',
@@ -365,7 +366,7 @@ class ResourceExtensionTest(ExtensionTestCase):
"code": 404
}
}
- self.assertDictMatch(expected, body)
+ self.assertThat(expected, matchers.DictMatches(body))
class InvalidExtension(object):
@@ -430,7 +431,7 @@ class ActionExtensionTest(ExtensionTestCase):
"code": 400
}
}
- self.assertDictMatch(expected, body)
+ self.assertThat(expected, matchers.DictMatches(body))
def test_non_exist_action(self):
body = dict(blah=dict(name="test"))
@@ -451,7 +452,7 @@ class ActionExtensionTest(ExtensionTestCase):
"code": 400
}
}
- self.assertDictMatch(expected, body)
+ self.assertThat(expected, matchers.DictMatches(body))
class RequestExtensionTest(ExtensionTestCase):
diff --git a/nova/tests/api/openstack/compute/test_flavors.py b/nova/tests/api/openstack/compute/test_flavors.py
index 77d40df03..4122dd4bc 100644
--- a/nova/tests/api/openstack/compute/test_flavors.py
+++ b/nova/tests/api/openstack/compute/test_flavors.py
@@ -29,6 +29,7 @@ from nova import exception
from nova import flags
from nova import test
from nova.tests.api.openstack import fakes
+from nova.tests import matchers
FLAGS = flags.FLAGS
@@ -219,7 +220,7 @@ class FlavorsTest(test.TestCase):
'rel': 'next'}
]
}
- self.assertDictMatch(flavor, expected)
+ self.assertThat(flavor, matchers.DictMatches(expected))
def test_get_flavor_detail_with_limit(self):
req = fakes.HTTPRequest.blank('/v2/fake/flavors/detail?limit=1')
@@ -251,7 +252,8 @@ class FlavorsTest(test.TestCase):
href_parts = urlparse.urlparse(response_links[0]['href'])
self.assertEqual('/v2/fake/flavors', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
- self.assertDictMatch({'limit': ['1'], 'marker': ['1']}, params)
+ self.assertThat({'limit': ['1'], 'marker': ['1']},
+ matchers.DictMatches(params))
def test_get_flavor_with_limit(self):
req = fakes.HTTPRequest.blank('/v2/fake/flavors?limit=2')
@@ -297,7 +299,8 @@ class FlavorsTest(test.TestCase):
href_parts = urlparse.urlparse(response_links[0]['href'])
self.assertEqual('/v2/fake/flavors', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
- self.assertDictMatch({'limit': ['2'], 'marker': ['2']}, params)
+ self.assertThat({'limit': ['2'], 'marker': ['2']},
+ matchers.DictMatches(params))
def test_get_flavor_list_detail(self):
req = fakes.HTTPRequest.blank('/v2/fake/flavors/detail')
diff --git a/nova/tests/api/openstack/compute/test_images.py b/nova/tests/api/openstack/compute/test_images.py
index af1dee30b..cd9aa8167 100644
--- a/nova/tests/api/openstack/compute/test_images.py
+++ b/nova/tests/api/openstack/compute/test_images.py
@@ -32,6 +32,7 @@ from nova import exception
from nova import flags
from nova import test
from nova.tests.api.openstack import fakes
+from nova.tests import matchers
from nova import utils
@@ -112,7 +113,7 @@ class ImagesControllerTest(test.TestCase):
},
}
- self.assertDictMatch(expected_image, actual_image)
+ self.assertThat(actual_image, matchers.DictMatches(expected_image))
def test_get_image_with_custom_prefix(self):
self.flags(osapi_compute_link_prefix='https://zoo.com:42',
@@ -166,7 +167,7 @@ class ImagesControllerTest(test.TestCase):
}],
},
}
- self.assertDictMatch(expected_image, actual_image)
+ self.assertThat(actual_image, matchers.DictMatches(expected_image))
def test_get_image_404(self):
fake_req = fakes.HTTPRequest.blank('/v2/fake/images/unknown')
@@ -461,7 +462,7 @@ class ImagesControllerTest(test.TestCase):
},
]
- self.assertDictListMatch(expected, response_list)
+ self.assertThat(expected, matchers.DictListMatches(response_list))
def test_get_image_details_with_limit(self):
request = fakes.HTTPRequest.blank('/v2/fake/images/detail?limit=2')
@@ -537,13 +538,14 @@ class ImagesControllerTest(test.TestCase):
}],
}]
- self.assertDictListMatch(expected, response_list)
+ self.assertThat(expected, matchers.DictListMatches(response_list))
href_parts = urlparse.urlparse(response_links[0]['href'])
self.assertEqual('/v2/fake/images', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
- self.assertDictMatch({'limit': ['2'], 'marker': ['124']}, params)
+ self.assertThat({'limit': ['2'], 'marker': ['124']},
+ matchers.DictMatches(params))
def test_image_detail_filter_with_name(self):
image_service = self.mox.CreateMockAnything()
diff --git a/nova/tests/api/openstack/compute/test_limits.py b/nova/tests/api/openstack/compute/test_limits.py
index 32f2e1294..32e7ab9e0 100644
--- a/nova/tests/api/openstack/compute/test_limits.py
+++ b/nova/tests/api/openstack/compute/test_limits.py
@@ -30,6 +30,7 @@ from nova.api.openstack import xmlutil
import nova.context
from nova.openstack.common import jsonutils
from nova import test
+from nova.tests import matchers
TEST_LIMITS = [
@@ -862,7 +863,7 @@ class LimitsViewBuilderTest(test.TestCase):
output = self.view_builder.build(self.rate_limits,
self.absolute_limits)
- self.assertDictMatch(output, expected_limits)
+ self.assertThat(output, matchers.DictMatches(expected_limits))
def test_build_limits_empty_limits(self):
expected_limits = {"limits": {"rate": [],
@@ -871,7 +872,7 @@ class LimitsViewBuilderTest(test.TestCase):
abs_limits = {}
rate_limits = []
output = self.view_builder.build(rate_limits, abs_limits)
- self.assertDictMatch(output, expected_limits)
+ self.assertThat(output, matchers.DictMatches(expected_limits))
class LimitsXMLSerializationTest(test.TestCase):
diff --git a/nova/tests/api/openstack/compute/test_server_actions.py b/nova/tests/api/openstack/compute/test_server_actions.py
index 0afa00f2b..1d1695cba 100644
--- a/nova/tests/api/openstack/compute/test_server_actions.py
+++ b/nova/tests/api/openstack/compute/test_server_actions.py
@@ -30,6 +30,7 @@ from nova.openstack.common import importutils
from nova import test
from nova.tests.api.openstack import fakes
from nova.tests.image import fake
+from nova.tests import matchers
from nova import utils
@@ -1052,7 +1053,7 @@ class TestServerActionXMLDeserializer(test.TestCase):
],
},
}
- self.assertDictMatch(request['body'], expected)
+ self.assertThat(request['body'], matchers.DictMatches(expected))
def test_rebuild_minimum(self):
serial_request = """<?xml version="1.0" encoding="UTF-8"?>
@@ -1065,7 +1066,7 @@ class TestServerActionXMLDeserializer(test.TestCase):
"imageRef": "http://localhost/images/1",
},
}
- self.assertDictMatch(request['body'], expected)
+ self.assertThat(request['body'], matchers.DictMatches(expected))
def test_rebuild_no_imageRef(self):
serial_request = """<?xml version="1.0" encoding="UTF-8"?>
diff --git a/nova/tests/api/openstack/compute/test_servers.py b/nova/tests/api/openstack/compute/test_servers.py
index afa181ee3..7aa76c848 100644
--- a/nova/tests/api/openstack/compute/test_servers.py
+++ b/nova/tests/api/openstack/compute/test_servers.py
@@ -47,6 +47,7 @@ from nova import test
from nova.tests.api.openstack import fakes
from nova.tests import fake_network
from nova.tests.image import fake
+from nova.tests import matchers
from nova import utils
@@ -315,7 +316,7 @@ class ServersControllerTest(test.TestCase):
}
}
- self.assertDictMatch(res_dict, expected_server)
+ self.assertThat(res_dict, matchers.DictMatches(expected_server))
def test_get_server_with_active_status_by_id(self):
image_bookmark = "http://localhost/fake/images/10"
@@ -381,7 +382,7 @@ class ServersControllerTest(test.TestCase):
}
}
- self.assertDictMatch(res_dict, expected_server)
+ self.assertThat(res_dict, matchers.DictMatches(expected_server))
def test_get_server_with_id_image_ref_by_id(self):
image_ref = "10"
@@ -450,7 +451,7 @@ class ServersControllerTest(test.TestCase):
}
}
- self.assertDictMatch(res_dict, expected_server)
+ self.assertThat(res_dict, matchers.DictMatches(expected_server))
def test_get_server_addresses_from_cache(self):
pub0 = ('172.19.0.1', '172.19.0.2',)
@@ -501,7 +502,7 @@ class ServersControllerTest(test.TestCase):
],
},
}
- self.assertDictMatch(res_dict, expected)
+ self.assertThat(res_dict, matchers.DictMatches(expected))
def test_get_server_addresses_nonexistent_network(self):
url = '/v2/fake/servers/%s/ips/network_0' % FAKE_UUID
@@ -596,7 +597,7 @@ class ServersControllerTest(test.TestCase):
params = urlparse.parse_qs(href_parts.query)
expected_params = {'limit': ['3'],
'marker': [fakes.get_fake_uuid(2)]}
- self.assertDictMatch(expected_params, params)
+ self.assertThat(params, matchers.DictMatches(expected_params))
def test_get_servers_with_limit_bad_value(self):
req = fakes.HTTPRequest.blank('/v2/fake/servers?limit=aaa')
@@ -618,7 +619,7 @@ class ServersControllerTest(test.TestCase):
self.assertEqual('/v2/fake/servers', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
expected = {'limit': ['3'], 'marker': [fakes.get_fake_uuid(2)]}
- self.assertDictMatch(expected, params)
+ self.assertThat(params, matchers.DictMatches(expected))
def test_get_server_details_with_limit_bad_value(self):
req = fakes.HTTPRequest.blank('/v2/fake/servers/detail?limit=aaa')
@@ -640,9 +641,9 @@ class ServersControllerTest(test.TestCase):
href_parts = urlparse.urlparse(servers_links[0]['href'])
self.assertEqual('/v2/fake/servers', href_parts.path)
params = urlparse.parse_qs(href_parts.query)
-
- self.assertDictMatch({'limit': ['3'], 'blah': ['2:t'],
- 'marker': [fakes.get_fake_uuid(2)]}, params)
+ expected = {'limit': ['3'], 'blah': ['2:t'],
+ 'marker': [fakes.get_fake_uuid(2)]}
+ self.assertThat(params, matchers.DictMatches(expected))
def test_get_servers_with_too_big_limit(self):
req = fakes.HTTPRequest.blank('/v2/fake/servers?limit=30')
@@ -3253,7 +3254,7 @@ class TestServerCreateRequestXMLDeserializer(test.TestCase):
],
},
}
- self.assertDictMatch(request['body'], expected)
+ self.assertThat(request['body'], matchers.DictMatches(expected))
def test_spec_request(self):
image_bookmark_link = ("http://servers.api.openstack.org/1234/"
@@ -3698,7 +3699,7 @@ class ServersViewBuilderTest(test.TestCase):
}
output = self.view_builder.basic(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
def test_build_server_with_project_id(self):
expected_server = {
@@ -3720,7 +3721,7 @@ class ServersViewBuilderTest(test.TestCase):
}
output = self.view_builder.basic(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
def test_build_server_detail(self):
image_bookmark = "http://localhost/fake/images/5"
@@ -3779,7 +3780,7 @@ class ServersViewBuilderTest(test.TestCase):
}
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
def test_build_server_detail_with_fault(self):
self.instance['vm_state'] = vm_states.ERROR
@@ -3853,7 +3854,7 @@ class ServersViewBuilderTest(test.TestCase):
self.request.context = context.RequestContext('fake', 'fake')
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
def test_build_server_detail_with_fault_no_details_not_admin(self):
self.instance['vm_state'] = vm_states.ERROR
@@ -3871,7 +3872,8 @@ class ServersViewBuilderTest(test.TestCase):
self.request.context = context.RequestContext('fake', 'fake')
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output['server']['fault'], expected_fault)
+ self.assertThat(output['server']['fault'],
+ matchers.DictMatches(expected_fault))
def test_build_server_detail_with_fault_admin(self):
self.instance['vm_state'] = vm_states.ERROR
@@ -3890,7 +3892,8 @@ class ServersViewBuilderTest(test.TestCase):
self.request.context = context.get_admin_context()
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output['server']['fault'], expected_fault)
+ self.assertThat(output['server']['fault'],
+ matchers.DictMatches(expected_fault))
def test_build_server_detail_with_fault_no_details_admin(self):
self.instance['vm_state'] = vm_states.ERROR
@@ -3908,7 +3911,8 @@ class ServersViewBuilderTest(test.TestCase):
self.request.context = context.get_admin_context()
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output['server']['fault'], expected_fault)
+ self.assertThat(output['server']['fault'],
+ matchers.DictMatches(expected_fault))
def test_build_server_detail_with_fault_but_active(self):
self.instance['vm_state'] = vm_states.ACTIVE
@@ -3989,7 +3993,7 @@ class ServersViewBuilderTest(test.TestCase):
}
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
def test_build_server_detail_with_accessipv4(self):
@@ -4051,7 +4055,7 @@ class ServersViewBuilderTest(test.TestCase):
}
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
def test_build_server_detail_with_accessipv6(self):
@@ -4113,7 +4117,7 @@ class ServersViewBuilderTest(test.TestCase):
}
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
def test_build_server_detail_with_metadata(self):
@@ -4177,7 +4181,7 @@ class ServersViewBuilderTest(test.TestCase):
}
output = self.view_builder.show(self.request, self.instance)
- self.assertDictMatch(output, expected_server)
+ self.assertThat(output, matchers.DictMatches(expected_server))
class ServerXMLSerializationTest(test.TestCase):
diff --git a/nova/tests/api/openstack/compute/test_versions.py b/nova/tests/api/openstack/compute/test_versions.py
index 4520faa48..d59270bd6 100644
--- a/nova/tests/api/openstack/compute/test_versions.py
+++ b/nova/tests/api/openstack/compute/test_versions.py
@@ -26,6 +26,7 @@ from nova.openstack.common import jsonutils
from nova import test
from nova.tests.api.openstack import common
from nova.tests.api.openstack import fakes
+from nova.tests import matchers
from nova import utils
@@ -346,7 +347,8 @@ class VersionsTest(test.TestCase):
},
], }
- self.assertDictMatch(expected, jsonutils.loads(res.body))
+ self.assertThat(jsonutils.loads(res.body),
+ matchers.DictMatches(expected))
def test_multi_choice_image_xml(self):
req = webob.Request.blank('/images/1')
@@ -416,7 +418,8 @@ class VersionsTest(test.TestCase):
},
], }
- self.assertDictMatch(expected, jsonutils.loads(res.body))
+ self.assertThat(jsonutils.loads(res.body),
+ matchers.DictMatches(expected))
class VersionsViewBuilderTests(test.TestCase):
diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py
index d47a861c3..adddef37d 100644
--- a/nova/tests/compute/test_compute.py
+++ b/nova/tests/compute/test_compute.py
@@ -60,6 +60,7 @@ from nova.tests.db.fakes import FakeModel
from nova.tests import fake_network
from nova.tests import fake_network_cache_model
from nova.tests.image import fake as fake_image
+from nova.tests import matchers
from nova import utils
from nova.virt import fake
from nova.volume import cinder
@@ -4509,7 +4510,7 @@ class ComputeAPITestCase(BaseTestCase):
]
bdms.sort()
expected_result.sort()
- self.assertDictListMatch(bdms, expected_result)
+ self.assertThat(bdms, matchers.DictListMatches(expected_result))
self.compute_api._update_block_device_mapping(
self.context, instance_types.get_default_instance_type(),
@@ -4545,7 +4546,7 @@ class ComputeAPITestCase(BaseTestCase):
{'no_device': True, 'device_name': '/dev/sdd4'}]
bdms.sort()
expected_result.sort()
- self.assertDictListMatch(bdms, expected_result)
+ self.assertThat(bdms, matchers.DictListMatches(expected_result))
for bdm in db.block_device_mapping_get_all_by_instance(
self.context, instance['uuid']):
@@ -5056,7 +5057,8 @@ class ComputeAPIAggrTestCase(BaseTestCase):
metadata['foo_key1'] = None
expected = self.api.update_aggregate_metadata(self.context,
aggr['id'], metadata)
- self.assertDictMatch(expected['metadata'], {'foo_key2': 'foo_value2'})
+ self.assertThat(expected['metadata'],
+ matchers.DictMatches({'foo_key2': 'foo_value2'}))
def test_delete_aggregate(self):
"""Ensure we can delete an aggregate."""
diff --git a/nova/tests/image/test_glance.py b/nova/tests/image/test_glance.py
index 13e090cef..e8baf4353 100644
--- a/nova/tests/image/test_glance.py
+++ b/nova/tests/image/test_glance.py
@@ -28,6 +28,7 @@ from nova.image import glance
from nova import test
from nova.tests.api.openstack import fakes
from nova.tests.glance import stubs as glance_stubs
+from nova.tests import matchers
class NullWriter(object):
@@ -155,10 +156,10 @@ class TestGlanceImageService(test.TestCase):
'properties': {'instance_id': '42', 'user_id': 'fake'},
'owner': None,
}
- self.assertDictMatch(image_meta, expected)
+ self.assertThat(image_meta, matchers.DictMatches(expected))
image_metas = self.service.detail(self.context)
- self.assertDictMatch(image_metas[0], expected)
+ self.assertThat(image_metas[0], matchers.DictMatches(expected))
def test_create_without_instance_id(self):
"""
@@ -188,7 +189,7 @@ class TestGlanceImageService(test.TestCase):
'owner': None,
}
actual = self.service.show(self.context, image_id)
- self.assertDictMatch(actual, expected)
+ self.assertThat(actual, matchers.DictMatches(expected))
def test_create(self):
fixture = self._make_fixture(name='test image')
@@ -259,7 +260,7 @@ class TestGlanceImageService(test.TestCase):
'owner': None,
}
- self.assertDictMatch(meta, expected)
+ self.assertThat(meta, matchers.DictMatches(expected))
i = i + 1
def test_detail_limit(self):
@@ -315,7 +316,7 @@ class TestGlanceImageService(test.TestCase):
'deleted': None,
'owner': None,
}
- self.assertDictMatch(meta, expected)
+ self.assertThat(meta, matchers.DictMatches(expected))
i = i + 1
def test_detail_invalid_marker(self):
diff --git a/nova/tests/matchers.py b/nova/tests/matchers.py
new file mode 100644
index 000000000..c3b88d2e5
--- /dev/null
+++ b/nova/tests/matchers.py
@@ -0,0 +1,196 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 United States Government as represented by the
+# Administrator of the National Aeronautics and Space Administration.
+# Copyright 2012 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+"""Matcher classes to be used inside of the testtools assertThat framework."""
+
+import pprint
+
+
+class DictKeysMismatch(object):
+ def __init__(self, d1only, d2only):
+ self.d1only = d1only
+ self.d2only = d2only
+
+ def describe(self):
+ return ('Keys in d1 and not d2: %(d1only)s.'
+ ' Keys in d2 and not d1: %(d2only)s' % self.__dict__)
+
+ def get_details(self):
+ return {}
+
+
+class DictMismatch(object):
+ def __init__(self, key, d1_value, d2_value):
+ self.key = key
+ self.d1_value = d1_value
+ self.d2_value = d2_value
+
+ def describe(self):
+ return ("Dictionaries do not match at %(key)s."
+ " d1: %(d1_value)s d2: %(d2_value)s" % self.__dict__)
+
+ def get_details(self):
+ return {}
+
+
+class DictMatches(object):
+
+ def __init__(self, d1, approx_equal=False, tolerance=0.001):
+ self.d1 = d1
+ self.approx_equal = approx_equal
+ self.tolerance = tolerance
+
+ def __str__(self):
+ return 'DictMatches(%s)' % (pprint.pformat(self.d1))
+
+ # Useful assertions
+ def match(self, d2):
+ """Assert two dicts are equivalent.
+
+ This is a 'deep' match in the sense that it handles nested
+ dictionaries appropriately.
+
+ NOTE:
+
+ If you don't care (or don't know) a given value, you can specify
+ the string DONTCARE as the value. This will cause that dict-item
+ to be skipped.
+
+ """
+
+ d1keys = set(self.d1.keys())
+ d2keys = set(d2.keys())
+ if d1keys != d2keys:
+ d1only = d1keys - d2keys
+ d2only = d2keys - d1keys
+ return DictKeysMismatch(d1only, d2only)
+
+ for key in d1keys:
+ d1value = self.d1[key]
+ d2value = d2[key]
+ try:
+ error = abs(float(d1value) - float(d2value))
+ within_tolerance = error <= self.tolerance
+ except (ValueError, TypeError):
+ # If both values aren't convertible to float, just ignore
+ # ValueError if arg is a str, TypeError if it's something else
+ # (like None)
+ within_tolerance = False
+
+ if hasattr(d1value, 'keys') and hasattr(d2value, 'keys'):
+ matcher = DictMatches(d1value)
+ did_match = matcher.match(d2value)
+ if did_match is not None:
+ return did_match
+ elif 'DONTCARE' in (d1value, d2value):
+ continue
+ elif self.approx_equal and within_tolerance:
+ continue
+ elif d1value != d2value:
+ return DictMismatch(key, d1value, d2value)
+
+
+class ListLengthMismatch(object):
+ def __init__(self, len1, len2):
+ self.len1 = len1
+ self.len2 = len2
+
+ def describe(self):
+ return ('Length mismatch: len(L1)=%(len1)d != '
+ 'len(L2)=%(len2)d' % self.__dict__)
+
+ def get_details(self):
+ return {}
+
+
+class DictListMatches(object):
+
+ def __init__(self, l1, approx_equal=False, tolerance=0.001):
+ self.l1 = l1
+ self.approx_equal = approx_equal
+ self.tolerance = tolerance
+
+ def __str__(self):
+ return 'DictListMatches(%s)' % (pprint.pformat(self.l1))
+
+ # Useful assertions
+ def match(self, l2):
+ """Assert a list of dicts are equivalent."""
+
+ l1count = len(self.l1)
+ l2count = len(l2)
+ if l1count != l2count:
+ return ListLengthMismatch(l1count, l2count)
+
+ for d1, d2 in zip(self.l1, l2):
+ matcher = DictMatches(d2,
+ approx_equal=self.approx_equal,
+ tolerance=self.tolerance)
+ did_match = matcher.match(d1)
+ if did_match:
+ return did_match
+
+
+class SubDictMismatch(object):
+ def __init__(self,
+ key=None,
+ sub_value=None,
+ super_value=None,
+ keys=False):
+ self.key = key
+ self.sub_value = sub_value
+ self.super_value = super_value
+ self.keys = keys
+
+ def describe(self):
+ if self.keys:
+ return "Keys between dictionaries did not match"
+ else:
+ return("Dictionaries do not match at %s. d1: %s d2: %s"
+ % (self.key,
+ self.super_value,
+ self.sub_value))
+
+ def get_details(self):
+ return {}
+
+
+class IsSubDictOf(object):
+
+ def __init__(self, super_dict):
+ self.super_dict = super_dict
+
+ def __str__(self):
+ return 'IsSubDictOf(%s)' % (self.super_dict)
+
+ def match(self, sub_dict):
+ """Assert a sub_dict is subset of super_dict."""
+ if not set(sub_dict.keys()).issubset(set(self.super_dict.keys())):
+ return SubDictMismatch(keys=True)
+ for k, sub_value in sub_dict.items():
+ super_value = self.super_dict[k]
+ if isinstance(sub_value, dict):
+ matcher = IsSubDictOf(super_value)
+ did_match = matcher.match(sub_value)
+ if did_match is not None:
+ return did_match
+ elif 'DONTCARE' in (sub_value, super_value):
+ continue
+ else:
+ if sub_value != super_value:
+ return SubDictMismatch(k, sub_value, super_value)
diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py
index 77fccd904..698ce8f59 100644
--- a/nova/tests/network/test_manager.py
+++ b/nova/tests/network/test_manager.py
@@ -32,6 +32,7 @@ import nova.policy
from nova import test
from nova.tests import fake_ldap
from nova.tests import fake_network
+from nova.tests import matchers
from nova import utils
@@ -168,7 +169,7 @@ class FlatNetworkTestCase(test.TestCase):
'bridge_interface': None,
'vlan': None}
- self.assertDictMatch(nw, check)
+ self.assertThat(nw, matchers.DictMatches(check))
check = {'broadcast': '192.168.%d.255' % nid,
'dhcp_server': '192.168.1.1',
@@ -184,13 +185,13 @@ class FlatNetworkTestCase(test.TestCase):
'00000000-0000-0000-0000-00000000000000%02d' % nid,
'should_create_vlan': False,
'should_create_bridge': False}
- self.assertDictMatch(info, check)
+ self.assertThat(info, matchers.DictMatches(check))
check = [{'enabled': 'DONTCARE',
'ip': '2001:db8:0:1::%x' % nid,
'netmask': 64,
'gateway': 'fe80::def'}]
- self.assertDictListMatch(info['ip6s'], check)
+ self.assertThat(info['ip6s'], matchers.DictListMatches(check))
num_fixed_ips = len(info['ips'])
check = [{'enabled': 'DONTCARE',
@@ -198,7 +199,7 @@ class FlatNetworkTestCase(test.TestCase):
'netmask': '255.255.255.0',
'gateway': '192.168.%d.1' % nid}
for ip_num in xrange(1, num_fixed_ips + 1)]
- self.assertDictListMatch(info['ips'], check)
+ self.assertThat(info['ips'], matchers.DictListMatches(check))
def test_validate_networks(self):
self.mox.StubOutWithMock(db, 'network_get')
diff --git a/nova/tests/scheduler/test_host_manager.py b/nova/tests/scheduler/test_host_manager.py
index d7d732d34..0984cbf80 100644
--- a/nova/tests/scheduler/test_host_manager.py
+++ b/nova/tests/scheduler/test_host_manager.py
@@ -24,6 +24,7 @@ from nova import exception
from nova.openstack.common import timeutils
from nova.scheduler import host_manager
from nova import test
+from nova.tests import matchers
from nova.tests.scheduler import fakes
@@ -92,7 +93,7 @@ class HostManagerTestCase(test.TestCase):
def test_update_service_capabilities(self):
service_states = self.host_manager.service_states
- self.assertDictMatch(service_states, {})
+ self.assertEqual(len(service_states.keys()), 0)
self.mox.StubOutWithMock(timeutils, 'utcnow')
timeutils.utcnow().AndReturn(31337)
timeutils.utcnow().AndReturn(31339)
@@ -116,11 +117,11 @@ class HostManagerTestCase(test.TestCase):
expected = {('host1', 'node1'): host1_compute_capabs,
('host2', 'node2'): host2_compute_capabs}
- self.assertDictMatch(service_states, expected)
+ self.assertThat(service_states, matchers.DictMatches(expected))
def test_update_service_capabilities_node_key(self):
service_states = self.host_manager.service_states
- self.assertDictMatch(service_states, {})
+ self.assertThat(service_states, matchers.DictMatches({}))
host1_cap = {'hypervisor_hostname': 'host1-hvhn'}
host2_cap = {}
@@ -135,7 +136,7 @@ class HostManagerTestCase(test.TestCase):
host2_cap['timestamp'] = 31338
expected = {('host1', 'host1-hvhn'): host1_cap,
('host2', None): host2_cap}
- self.assertDictMatch(service_states, expected)
+ self.assertThat(service_states, matchers.DictMatches(expected))
def test_get_all_host_states(self):
diff --git a/nova/tests/scheduler/test_least_cost.py b/nova/tests/scheduler/test_least_cost.py
index 3689a30bd..1d180d718 100644
--- a/nova/tests/scheduler/test_least_cost.py
+++ b/nova/tests/scheduler/test_least_cost.py
@@ -19,6 +19,7 @@ from nova import context
from nova.scheduler import host_manager
from nova.scheduler import least_cost
from nova import test
+from nova.tests import matchers
from nova.tests.scheduler import fakes
@@ -92,11 +93,11 @@ class TestWeightedHost(test.TestCase):
def test_dict_conversion_without_host_state(self):
host = least_cost.WeightedHost('someweight')
expected = {'weight': 'someweight'}
- self.assertDictMatch(host.to_dict(), expected)
+ self.assertThat(host.to_dict(), matchers.DictMatches(expected))
def test_dict_conversion_with_host_state(self):
host_state = host_manager.HostState('somehost', None)
host = least_cost.WeightedHost('someweight', host_state)
expected = {'weight': 'someweight',
'host': 'somehost'}
- self.assertDictMatch(host.to_dict(), expected)
+ self.assertThat(host.to_dict(), matchers.DictMatches(expected))
diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py
index 6a0e93b7d..4de1c074a 100644
--- a/nova/tests/scheduler/test_scheduler.py
+++ b/nova/tests/scheduler/test_scheduler.py
@@ -36,6 +36,7 @@ from nova.openstack.common import timeutils
from nova.scheduler import driver
from nova.scheduler import manager
from nova import test
+from nova.tests import matchers
from nova.tests.scheduler import fakes
from nova import utils
@@ -138,7 +139,7 @@ class SchedulerManagerTestCase(test.TestCase):
'local_gb_used': 512,
'memory_mb': 1024,
'memory_mb_used': 512}}
- self.assertDictMatch(result, expected)
+ self.assertThat(result, matchers.DictMatches(expected))
def _mox_schedule_method_helper(self, method_name):
# Make sure the method exists that we're going to test call
@@ -721,7 +722,7 @@ class SchedulerDriverModuleTestCase(test.TestCase):
result = driver.encode_instance(instance, True)
expected = {'id': instance['id'], '_is_precooked': False}
- self.assertDictMatch(result, expected)
+ self.assertThat(result, matchers.DictMatches(expected))
# Orig dict not changed
self.assertNotEqual(result, instance)
@@ -729,6 +730,6 @@ class SchedulerDriverModuleTestCase(test.TestCase):
expected = {}
expected.update(instance)
expected['_is_precooked'] = True
- self.assertDictMatch(result, expected)
+ self.assertThat(result, matchers.DictMatches(expected))
# Orig dict not changed
self.assertNotEqual(result, instance)
diff --git a/nova/tests/test_api.py b/nova/tests/test_api.py
index 6e0a97c0c..b2cd23a0c 100644
--- a/nova/tests/test_api.py
+++ b/nova/tests/test_api.py
@@ -41,6 +41,7 @@ from nova import exception
from nova import flags
from nova.openstack.common import timeutils
from nova import test
+from nova.tests import matchers
FLAGS = flags.FLAGS
@@ -163,7 +164,7 @@ class Ec2utilsTestCase(test.TestCase):
'virtual_name': 'ephemeral0'}}}
out_dict = ec2utils.dict_from_dotted_str(in_str)
- self.assertDictMatch(out_dict, expected_dict)
+ self.assertThat(out_dict, matchers.DictMatches(expected_dict))
def test_properties_root_defice_name(self):
mappings = [{"device": "/dev/sda1", "virtual": "root"}]
@@ -209,8 +210,8 @@ class Ec2utilsTestCase(test.TestCase):
'device': '/dev/sdc1'},
{'virtual': 'ephemeral1',
'device': '/dev/sdc1'}]
- self.assertDictListMatch(block_device.mappings_prepend_dev(mappings),
- expected_result)
+ self.assertThat(block_device.mappings_prepend_dev(mappings),
+ matchers.DictListMatches(expected_result))
class ApiEc2TestCase(test.TestCase):
diff --git a/nova/tests/test_bdm.py b/nova/tests/test_bdm.py
index 381ed8070..2d0349534 100644
--- a/nova/tests/test_bdm.py
+++ b/nova/tests/test_bdm.py
@@ -22,6 +22,7 @@ Tests for Block Device Mapping Code.
from nova.api.ec2 import cloud
from nova.api.ec2 import ec2utils
from nova import test
+from nova.tests import matchers
class BlockDeviceMappingEc2CloudTestCase(test.TestCase):
@@ -41,7 +42,7 @@ class BlockDeviceMappingEc2CloudTestCase(test.TestCase):
def _assertApply(self, action, bdm_list):
for bdm, expected_result in bdm_list:
- self.assertDictMatch(action(bdm), expected_result)
+ self.assertThat(action(bdm), matchers.DictMatches(expected_result))
def test_parse_block_device_mapping(self):
self.stubs.Set(ec2utils,
diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py
index fb277bcb8..62943b59f 100644
--- a/nova/tests/test_db_api.py
+++ b/nova/tests/test_db_api.py
@@ -28,6 +28,7 @@ from nova import exception
from nova import flags
from nova.openstack.common import timeutils
from nova import test
+from nova.tests import matchers
from nova import utils
@@ -619,14 +620,16 @@ class AggregateDBApiTestCase(test.TestCase):
ctxt = context.get_admin_context()
result = _create_aggregate(context=ctxt)
expected_metadata = db.aggregate_metadata_get(ctxt, result['id'])
- self.assertDictMatch(expected_metadata, _get_fake_aggr_metadata())
+ self.assertThat(expected_metadata,
+ matchers.DictMatches(_get_fake_aggr_metadata()))
def test_aggregate_create_delete_create_with_metadata(self):
"""Ensure aggregate metadata is deleted bug 1052479."""
ctxt = context.get_admin_context()
result = _create_aggregate(context=ctxt)
expected_metadata = db.aggregate_metadata_get(ctxt, result['id'])
- self.assertDictMatch(expected_metadata, _get_fake_aggr_metadata())
+ self.assertThat(expected_metadata,
+ matchers.DictMatches(_get_fake_aggr_metadata()))
db.aggregate_delete(ctxt, result['id'])
result = _create_aggregate(metadata=None)
expected_metadata = db.aggregate_metadata_get(ctxt, result['id'])
@@ -748,7 +751,8 @@ class AggregateDBApiTestCase(test.TestCase):
values['metadata'] = _get_fake_aggr_metadata()
db.aggregate_update(ctxt, 1, values)
expected = db.aggregate_metadata_get(ctxt, result.id)
- self.assertDictMatch(_get_fake_aggr_metadata(), expected)
+ self.assertThat(_get_fake_aggr_metadata(),
+ matchers.DictMatches(expected))
def test_aggregate_update_with_existing_metadata(self):
"""Ensure an aggregate can be updated with existing metadata."""
@@ -759,7 +763,7 @@ class AggregateDBApiTestCase(test.TestCase):
values['metadata']['fake_key1'] = 'foo'
db.aggregate_update(ctxt, 1, values)
expected = db.aggregate_metadata_get(ctxt, result.id)
- self.assertDictMatch(values['metadata'], expected)
+ self.assertThat(values['metadata'], matchers.DictMatches(expected))
def test_aggregate_update_raise_not_found(self):
"""Ensure AggregateNotFound is raised when updating an aggregate."""
@@ -805,7 +809,7 @@ class AggregateDBApiTestCase(test.TestCase):
metadata = _get_fake_aggr_metadata()
db.aggregate_metadata_add(ctxt, result.id, metadata)
expected = db.aggregate_metadata_get(ctxt, result.id)
- self.assertDictMatch(metadata, expected)
+ self.assertThat(metadata, matchers.DictMatches(expected))
def test_aggregate_metadata_update(self):
"""Ensure we can update metadata for the aggregate."""
@@ -818,7 +822,7 @@ class AggregateDBApiTestCase(test.TestCase):
db.aggregate_metadata_add(ctxt, result.id, new_metadata)
expected = db.aggregate_metadata_get(ctxt, result.id)
metadata[key] = 'foo'
- self.assertDictMatch(metadata, expected)
+ self.assertThat(metadata, matchers.DictMatches(expected))
def test_aggregate_metadata_delete(self):
"""Ensure we can delete metadata for the aggregate."""
@@ -829,7 +833,7 @@ class AggregateDBApiTestCase(test.TestCase):
db.aggregate_metadata_delete(ctxt, result.id, metadata.keys()[0])
expected = db.aggregate_metadata_get(ctxt, result.id)
del metadata[metadata.keys()[0]]
- self.assertDictMatch(metadata, expected)
+ self.assertThat(metadata, matchers.DictMatches(expected))
def test_aggregate_metadata_delete_raise_not_found(self):
"""Ensure AggregateMetadataNotFound is raised when deleting."""
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 6e49121f1..4c2886a49 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -45,6 +45,7 @@ from nova import test
from nova.tests import fake_libvirt_utils
from nova.tests import fake_network
import nova.tests.image.fake
+from nova.tests import matchers
from nova import utils
from nova.virt.disk import api as disk
from nova.virt import driver
@@ -630,7 +631,7 @@ class LibvirtConnTestCase(test.TestCase):
'id': 'fake'
}
result = conn.get_volume_connector(volume)
- self.assertDictMatch(expected, result)
+ self.assertThat(expected, matchers.DictMatches(result))
def test_get_guest_config(self):
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
@@ -1923,11 +1924,11 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
return_value = conn.check_can_live_migrate_destination(self.context,
instance_ref, compute_info, compute_info, True)
- self.assertDictMatch(return_value,
- {"filename": "file",
- 'disk_available_mb': 409600,
- "disk_over_commit": False,
- "block_migration": True})
+ self.assertThat({"filename": "file",
+ 'disk_available_mb': 409600,
+ "disk_over_commit": False,
+ "block_migration": True},
+ matchers.DictMatches(return_value))
def test_check_can_live_migrate_dest_all_pass_no_block_migration(self):
instance_ref = db.instance_create(self.context, self.test_instance)
@@ -1949,11 +1950,11 @@ class LibvirtConnTestCase(test.TestCase):
self.mox.ReplayAll()
return_value = conn.check_can_live_migrate_destination(self.context,
instance_ref, compute_info, compute_info, False)
- self.assertDictMatch(return_value,
- {"filename": "file",
- "block_migration": False,
- "disk_over_commit": False,
- "disk_available_mb": None})
+ self.assertThat({"filename": "file",
+ "block_migration": False,
+ "disk_over_commit": False,
+ "disk_available_mb": None},
+ matchers.DictMatches(return_value))
def test_check_can_live_migrate_dest_incompatible_cpu_raises(self):
instance_ref = db.instance_create(self.context, self.test_instance)
diff --git a/nova/tests/test_matchers.py b/nova/tests/test_matchers.py
new file mode 100644
index 000000000..b764b3d45
--- /dev/null
+++ b/nova/tests/test_matchers.py
@@ -0,0 +1,144 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 Hewlett-Packard Development Company, L.P.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import testtools
+from testtools.tests.matchers import helpers
+
+from nova.tests import matchers
+
+
+class TestDictMatches(testtools.TestCase, helpers.TestMatchersInterface):
+
+ matches_matcher = matchers.DictMatches(
+ {'foo': 'bar', 'baz': 'DONTCARE',
+ 'cat': {'tabby': True, 'fluffy': False}}
+ )
+
+ matches_matches = [
+ {'foo': 'bar', 'baz': 'noox', 'cat': {'tabby': True, 'fluffy': False}},
+ {'foo': 'bar', 'baz': 'quux', 'cat': {'tabby': True, 'fluffy': False}},
+ ]
+
+ matches_mismatches = [
+ {},
+ {'foo': 'bar', 'baz': 'qux'},
+ {'foo': 'bop', 'baz': 'qux',
+ 'cat': {'tabby': True, 'fluffy': False}},
+ {'foo': 'bar', 'baz': 'quux',
+ 'cat': {'tabby': True, 'fluffy': True}},
+ {'foo': 'bar', 'cat': {'tabby': True, 'fluffy': False}},
+ ]
+
+ str_examples = [
+ ("DictMatches({'baz': 'DONTCARE', 'cat':"
+ " {'fluffy': False, 'tabby': True}, 'foo': 'bar'})",
+ matches_matcher),
+ ]
+
+ describe_examples = [
+ ("Keys in d1 and not d2: set(['foo', 'baz', 'cat'])."
+ " Keys in d2 and not d1: set([])", {}, matches_matcher),
+ ("Dictionaries do not match at fluffy. d1: False d2: True",
+ {'foo': 'bar', 'baz': 'quux',
+ 'cat': {'tabby': True, 'fluffy': True}}, matches_matcher),
+ ("Dictionaries do not match at foo. d1: bar d2: bop",
+ {'foo': 'bop', 'baz': 'quux',
+ 'cat': {'tabby': True, 'fluffy': False}}, matches_matcher),
+ ]
+
+
+class TestDictListMatches(testtools.TestCase, helpers.TestMatchersInterface):
+
+ matches_matcher = matchers.DictListMatches(
+ [{'foo': 'bar', 'baz': 'DONTCARE',
+ 'cat': {'tabby': True, 'fluffy': False}},
+ {'dog': 'yorkie'},
+ ])
+
+ matches_matches = [
+ [{'foo': 'bar', 'baz': 'qoox',
+ 'cat': {'tabby': True, 'fluffy': False}},
+ {'dog': 'yorkie'}],
+ [{'foo': 'bar', 'baz': False,
+ 'cat': {'tabby': True, 'fluffy': False}},
+ {'dog': 'yorkie'}],
+ ]
+
+ matches_mismatches = [
+ [],
+ {},
+ [{'foo': 'bar', 'baz': 'qoox',
+ 'cat': {'tabby': True, 'fluffy': True}},
+ {'dog': 'yorkie'}],
+ [{'foo': 'bar', 'baz': False,
+ 'cat': {'tabby': True, 'fluffy': False}},
+ {'cat': 'yorkie'}],
+ [{'foo': 'bop', 'baz': False,
+ 'cat': {'tabby': True, 'fluffy': False}},
+ {'dog': 'yorkie'}],
+ ]
+
+ str_examples = [
+ ("DictListMatches([{'baz': 'DONTCARE', 'cat':"
+ " {'fluffy': False, 'tabby': True}, 'foo': 'bar'},\n"
+ " {'dog': 'yorkie'}])",
+ matches_matcher),
+ ]
+
+ describe_examples = [
+ ("Length mismatch: len(L1)=2 != len(L2)=0", {}, matches_matcher),
+ ("Dictionaries do not match at fluffy. d1: True d2: False",
+ [{'foo': 'bar', 'baz': 'qoox',
+ 'cat': {'tabby': True, 'fluffy': True}},
+ {'dog': 'yorkie'}],
+ matches_matcher),
+ ]
+
+
+class TestDictMatches(testtools.TestCase, helpers.TestMatchersInterface):
+
+ matches_matcher = matchers.IsSubDictOf(
+ {'foo': 'bar', 'baz': 'DONTCARE',
+ 'cat': {'tabby': True, 'fluffy': False}}
+ )
+
+ matches_matches = [
+ {'foo': 'bar', 'baz': 'noox', 'cat': {'tabby': True, 'fluffy': False}},
+ {'foo': 'bar', 'baz': 'quux'}
+ ]
+
+ matches_mismatches = [
+ {'foo': 'bop', 'baz': 'qux',
+ 'cat': {'tabby': True, 'fluffy': False}},
+ {'foo': 'bar', 'baz': 'quux',
+ 'cat': {'tabby': True, 'fluffy': True}},
+ {'foo': 'bar', 'cat': {'tabby': True, 'fluffy': False}, 'dog': None},
+ ]
+
+ str_examples = [
+ ("IsSubDictOf({'foo': 'bar', 'baz': 'DONTCARE',"
+ " 'cat': {'fluffy': False, 'tabby': True}})",
+ matches_matcher),
+ ]
+
+ describe_examples = [
+ ("Dictionaries do not match at fluffy. d1: False d2: True",
+ {'foo': 'bar', 'baz': 'quux',
+ 'cat': {'tabby': True, 'fluffy': True}}, matches_matcher),
+ ("Dictionaries do not match at foo. d1: bar d2: bop",
+ {'foo': 'bop', 'baz': 'quux',
+ 'cat': {'tabby': True, 'fluffy': False}}, matches_matcher),
+ ]
diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py
index a145d1675..c1c8c1cbd 100644
--- a/nova/tests/test_xenapi.py
+++ b/nova/tests/test_xenapi.py
@@ -41,6 +41,7 @@ from nova.tests.db import fakes as db_fakes
from nova.tests import fake_network
from nova.tests import fake_utils
import nova.tests.image.fake as fake_image
+from nova.tests import matchers
from nova.tests.xenapi import stubs
from nova.virt import fake
from nova.virt.xenapi import agent
@@ -359,7 +360,7 @@ class XenAPIVMTestCase(stubs.XenAPITestBase):
}
instance = self._create_instance()
expected = self.conn.get_diagnostics(instance)
- self.assertDictMatch(fake_diagnostics, expected)
+ self.assertThat(fake_diagnostics, matchers.DictMatches(expected))
def test_instance_snapshot_fails_with_no_primary_vdi(self):
def create_bad_vbd(session, vm_ref, vdi_ref, userdevice,
@@ -2090,7 +2091,8 @@ class XenAPIAggregateTestCase(stubs.XenAPITestBase):
self.conn._pool.add_to_aggregate(self.context, aggregate, "host")
result = db.aggregate_get(self.context, aggregate.id)
self.assertTrue(fake_init_pool.called)
- self.assertDictMatch(self.fake_metadata, result.metadetails)
+ self.assertThat(self.fake_metadata,
+ matchers.DictMatches(result.metadetails))
def test_join_slave(self):
"""Ensure join_slave gets called when the request gets to master."""
@@ -2168,8 +2170,9 @@ class XenAPIAggregateTestCase(stubs.XenAPITestBase):
self.conn._pool.remove_from_aggregate(self.context, aggregate, "host")
result = db.aggregate_get(self.context, aggregate.id)
self.assertTrue(fake_clear_pool.called)
- self.assertDictMatch({pool_states.POOL_FLAG: 'XenAPI',
- pool_states.KEY: pool_states.ACTIVE}, result.metadetails)
+ self.assertThat({pool_states.POOL_FLAG: 'XenAPI',
+ pool_states.KEY: pool_states.ACTIVE},
+ matchers.DictMatches(result.metadetails))
def test_remote_master_non_empty_pool(self):
"""Ensure AggregateError is raised if removing the master."""