diff --git a/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py b/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py index 8c60a6c09a..cb2be90339 100644 --- a/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py +++ b/contrib/rackspace/rackspace/tests/test_cloud_loadbalancer.py @@ -17,6 +17,7 @@ import json import uuid import mock +import mox import six from heat.common import exception @@ -1229,11 +1230,11 @@ class LoadBalancerTest(common.HeatTestCase): template['Resources'][lb_name]['Properties']['metadata'] = { 'a': 1, 'b': 2} expected_body = copy.deepcopy(self.expected_body) - expected_body['metadata'] = [{'key': 'a', 'value': 1}, - {'key': 'b', 'value': 2}] - rsrc, fake_loadbalancer = self._mock_loadbalancer(template, - self.lb_name, - expected_body) + expected_body['metadata'] = mox.SameElementsAs( + [{'key': 'a', 'value': 1}, + {'key': 'b', 'value': 2}]) + rsrc, fake_loadbalancer = self._mock_loadbalancer( + template, self.lb_name, expected_body) self.m.ReplayAll() scheduler.TaskRunner(rsrc.create)() diff --git a/heat/tests/api/aws/test_api_ec2token.py b/heat/tests/api/aws/test_api_ec2token.py index ffcda3d3df..dae129b53d 100644 --- a/heat/tests/api/aws/test_api_ec2token.py +++ b/heat/tests/api/aws/test_api_ec2token.py @@ -24,12 +24,13 @@ from heat.api.aws import ec2token from heat.api.aws import exception from heat.common import wsgi from heat.tests import common +from heat.tests import utils class Ec2TokenTest(common.HeatTestCase): - ''' + """ Tests the Ec2Token middleware - ''' + """ def setUp(self): super(Ec2TokenTest, self).setUp() @@ -253,8 +254,9 @@ class Ec2TokenTest(common.HeatTestCase): "path": "/v1", "body_hash": body_hash}}) req_headers = {'Content-Type': 'application/json'} - requests.post(req_url, data=req_creds, verify=verify, cert=cert, - headers=req_headers).AndReturn(DummyHTTPResponse()) + requests.post( + req_url, data=utils.JsonEquals(req_creds), verify=verify, + cert=cert, headers=req_headers).AndReturn(DummyHTTPResponse()) def test_call_ok(self): dummy_conf = {'auth_uri': 'http://123:5000/v2.0'} diff --git a/heat/tests/api/cfn/test_api_cfn_v1.py b/heat/tests/api/cfn/test_api_cfn_v1.py index 17f9256cb1..ac7c7afc7b 100644 --- a/heat/tests/api/cfn/test_api_cfn_v1.py +++ b/heat/tests/api/cfn/test_api_cfn_v1.py @@ -33,10 +33,10 @@ policy_path = os.path.dirname(os.path.realpath(__file__)) + "/../../policy/" class CfnStackControllerTest(common.HeatTestCase): - ''' + """ Tests the API class which acts as the WSGI controller, the endpoint processing API requests after they are routed - ''' + """ def setUp(self): super(CfnStackControllerTest, self).setUp() @@ -333,7 +333,8 @@ class CfnStackControllerTest(common.HeatTestCase): 'DisableRollback': 'true', 'LastUpdatedTime': u'2012-07-09T09:13:11Z'}]}}} - self.assertEqual(expected, response) + self.assertEqual(utils.recursive_sort(expected), + utils.recursive_sort(response)) def test_describe_arn(self): # Format a dummy GET request to pass into the WSGI handler @@ -417,7 +418,8 @@ class CfnStackControllerTest(common.HeatTestCase): 'DisableRollback': 'true', 'LastUpdatedTime': u'2012-07-09T09:13:11Z'}]}}} - self.assertEqual(expected, response) + self.assertEqual(utils.recursive_sort(expected), + utils.recursive_sort(response)) def test_describe_arn_invalidtenant(self): # Format a dummy GET request to pass into the WSGI handler @@ -481,7 +483,7 @@ class CfnStackControllerTest(common.HeatTestCase): self.assertIsInstance(result, exception.HeatInvalidParameterValueError) def test_get_template_int_body(self): - '''Test the internal _get_template function.''' + """Test the internal _get_template function.""" params = {'TemplateBody': "abcdef"} dummy_req = self._dummy_GET_request(params) result = self.controller._get_template(dummy_req) diff --git a/heat/tests/api/cloudwatch/test_api_cloudwatch.py b/heat/tests/api/cloudwatch/test_api_cloudwatch.py index 8cc85dab71..2e17dd0a36 100644 --- a/heat/tests/api/cloudwatch/test_api_cloudwatch.py +++ b/heat/tests/api/cloudwatch/test_api_cloudwatch.py @@ -73,11 +73,13 @@ class WatchControllerTest(common.HeatTestCase): dims = [{'StackId': u'21617058-781e-4262-97ab-5f9df371ee52', 'Foo': 'bar'}] - self.assertEqual([{'Name': 'StackId', - 'Value': u'21617058-781e-4262-97ab-5f9df371ee52'}, - {'Name': 'Foo', 'Value': 'bar'}], - self.controller._reformat_dimensions(dims) - ) + self.assertEqual( + utils.recursive_sort( + [{'Name': 'StackId', + 'Value': u'21617058-781e-4262-97ab-5f9df371ee52'}, + {'Name': 'Foo', + 'Value': 'bar'}]), + utils.recursive_sort(self.controller._reformat_dimensions(dims))) def test_enforce_default(self): self.m.ReplayAll() @@ -301,7 +303,9 @@ class WatchControllerTest(common.HeatTestCase): 'MetricName': u'ServiceFailure3'}]}}} # First pass no query paramters filtering, should get all three - self.assertEqual(expected, self.controller.list_metrics(dummy_req)) + self.assertEqual( + utils.recursive_sort(expected), + utils.recursive_sort(self.controller.list_metrics(dummy_req))) def test_list_metrics_filter_name(self): @@ -358,7 +362,9 @@ class WatchControllerTest(common.HeatTestCase): 'Value': 1}], 'MetricName': u'ServiceFailure'}]}}} # First pass no query paramters filtering, should get all three - self.assertEqual(expected, self.controller.list_metrics(dummy_req)) + self.assertEqual( + utils.recursive_sort(expected), + utils.recursive_sort(self.controller.list_metrics(dummy_req))) def test_list_metrics_filter_namespace(self): @@ -426,7 +432,9 @@ class WatchControllerTest(common.HeatTestCase): {'Name': u'Value', 'Value': 1}], 'MetricName': u'ServiceFailure2'}]}}} - self.assertEqual(expected, self.controller.list_metrics(dummy_req)) + self.assertEqual( + utils.recursive_sort(expected), + utils.recursive_sort(self.controller.list_metrics(dummy_req))) def test_put_metric_alarm(self): # Not yet implemented, should raise HeatAPINotImplementedError diff --git a/heat/tests/api/openstack_v1/test_views_common.py b/heat/tests/api/openstack_v1/test_views_common.py index 981dcc542f..c9fbd377de 100644 --- a/heat/tests/api/openstack_v1/test_views_common.py +++ b/heat/tests/api/openstack_v1/test_views_common.py @@ -38,10 +38,12 @@ class TestViewsCommon(common.HeatTestCase): self.setUpGetCollectionLinks() links = views_common.get_collection_links(self.request, self.items) - expected = 'http://example.com/fake/path?marker=id2&limit=2' + expected_params = {'marker': ['id2'], 'limit': ['2']} next_link = filter(lambda link: link['rel'] == 'next', links).pop() self.assertEqual('next', next_link['rel']) - self.assertEqual(expected, next_link['href']) + url_path, url_params = next_link['href'].split('?', 1) + self.assertEqual(url_path, self.request.path_url) + self.assertEqual(expected_params, urlparse.parse_qs(url_params)) def test_get_collection_links_doesnt_create_next_if_no_limit(self): self.setUpGetCollectionLinks() @@ -62,9 +64,12 @@ class TestViewsCommon(common.HeatTestCase): self.request.params = {'limit': '2', 'marker': 'some_marker'} links = views_common.get_collection_links(self.request, self.items) - expected = 'http://example.com/fake/path?marker=id2&limit=2' + expected_params = {'marker': ['id2'], 'limit': ['2']} next_link = filter(lambda link: link['rel'] == 'next', links).pop() - self.assertEqual(expected, next_link['href']) + self.assertEqual('next', next_link['rel']) + url_path, url_params = next_link['href'].split('?', 1) + self.assertEqual(url_path, self.request.path_url) + self.assertEqual(expected_params, urlparse.parse_qs(url_params)) def test_get_collection_links_does_not_overwrite_other_params(self): self.setUpGetCollectionLinks() diff --git a/heat/tests/aws/test_loadbalancer.py b/heat/tests/aws/test_loadbalancer.py index 29447c0082..3bd951497d 100644 --- a/heat/tests/aws/test_loadbalancer.py +++ b/heat/tests/aws/test_loadbalancer.py @@ -88,7 +88,8 @@ class LoadBalancerTest(common.HeatTestCase): lb_defn = s.t.resource_definitions(s)[resource_name] rsrc = lb.LoadBalancer(resource_name, lb_defn, s) - nova.NovaClientPlugin._create = mock.Mock(return_value=self.fc) + self.patchobject(nova.NovaClientPlugin, '_create', + return_value=self.fc) initial_md = {'AWS::CloudFormation::Init': {'config': diff --git a/heat/tests/ceilometer/test_ceilometer_alarm.py b/heat/tests/ceilometer/test_ceilometer_alarm.py index 2bb8946a2a..55661e3474 100644 --- a/heat/tests/ceilometer/test_ceilometer_alarm.py +++ b/heat/tests/ceilometer/test_ceilometer_alarm.py @@ -198,7 +198,7 @@ class CeilometerAlarmTest(common.HeatTestCase): if 'matching_metadata' in al: del al['matching_metadata'] if query: - rule['query'] = query + rule['query'] = mox.SameElementsAs(query) al['threshold_rule'] = rule al['type'] = 'threshold' self.m.StubOutWithMock(self.fa.alarms, 'create') @@ -396,8 +396,9 @@ class CeilometerAlarmTest(common.HeatTestCase): self.m.VerifyAll() def test_mem_alarm_high_not_correct_string_parameters(self): - snippet = template_format.parse(not_string_alarm_template) + orig_snippet = template_format.parse(not_string_alarm_template) for p in ('period', 'evaluation_periods'): + snippet = copy.deepcopy(orig_snippet) snippet['Resources']['MEMAlarmHigh']['Properties'][p] = '60a' stack = utils.parse_stack(snippet) @@ -411,8 +412,9 @@ class CeilometerAlarmTest(common.HeatTestCase): "Value '60a' is not an integer" % p, six.text_type(error)) def test_mem_alarm_high_not_integer_parameters(self): - snippet = template_format.parse(not_string_alarm_template) + orig_snippet = template_format.parse(not_string_alarm_template) for p in ('period', 'evaluation_periods'): + snippet = copy.deepcopy(orig_snippet) snippet['Resources']['MEMAlarmHigh']['Properties'][p] = [60] stack = utils.parse_stack(snippet) diff --git a/heat/tests/clients/test_nova_client.py b/heat/tests/clients/test_nova_client.py index 260dfd3b20..24ef89258d 100644 --- a/heat/tests/clients/test_nova_client.py +++ b/heat/tests/clients/test_nova_client.py @@ -374,7 +374,12 @@ class NovaClientPluginMetadataTests(NovaClientPluginTestCase): self.assertEqual(expected, self.nova_plugin.meta_serialize(original)) def test_serialize_dict(self): - original = {'test_key': {'a': 'b', 'c': 'd'}} + original = collections.OrderedDict([ + ('test_key', collections.OrderedDict([ + ('a', 'b'), + ('c', 'd'), + ])) + ]) expected = {'test_key': '{"a": "b", "c": "d"}'} actual = self.nova_plugin.meta_serialize(original) self.assertEqual(json.loads(expected['test_key']), diff --git a/heat/tests/engine/test_software_config.py b/heat/tests/engine/test_software_config.py index 807f317665..0a681fe6cd 100644 --- a/heat/tests/engine/test_software_config.py +++ b/heat/tests/engine/test_software_config.py @@ -85,6 +85,11 @@ class SoftwareConfigServiceTest(common.HeatTestCase): return self.engine.create_software_config( self.ctx, group, name, config, inputs, outputs, options) + def assert_status_reason(self, expected, actual): + expected_dict = dict((i.split(' : ') for i in expected.split(', '))) + actual_dict = dict((i.split(' : ') for i in actual.split(', '))) + self.assertEqual(expected_dict, actual_dict) + def test_list_software_configs(self): config = self._create_software_config() config_id = config['id'] @@ -362,7 +367,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): sd = software_deployment_object.SoftwareDeployment.get_by_id( self.ctx, deployment_id) self.assertEqual('FAILED', sd.status) - self.assertEqual( + self.assert_status_reason( ('deploy_status_code : Deployment exited with non-zero ' 'status code: -1'), sd.status_reason) @@ -394,7 +399,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase): sd = software_deployment_object.SoftwareDeployment.get_by_id( self.ctx, deployment_id) self.assertEqual('FAILED', sd.status) - self.assertEqual( + self.assert_status_reason( ('foo : bar, deploy_status_code : Deployment exited with ' 'non-zero status code: -1'), sd.status_reason) diff --git a/heat/tests/generic_resource.py b/heat/tests/generic_resource.py index 254b809044..6303d67edf 100644 --- a/heat/tests/generic_resource.py +++ b/heat/tests/generic_resource.py @@ -11,6 +11,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections from oslo_log import log as logging import six @@ -31,8 +32,9 @@ class GenericResource(resource.Resource): Dummy resource for use in tests ''' properties_schema = {} - attributes_schema = {'foo': attributes.Schema('A generic attribute'), - 'Foo': attributes.Schema('Another generic attribute')} + attributes_schema = collections.OrderedDict([ + ('foo', attributes.Schema('A generic attribute')), + ('Foo', attributes.Schema('Another generic attribute'))]) @classmethod def is_service_available(cls, context): diff --git a/heat/tests/openstack/test_waitcondition.py b/heat/tests/openstack/test_waitcondition.py index 8fe152543f..5897f14296 100644 --- a/heat/tests/openstack/test_waitcondition.py +++ b/heat/tests/openstack/test_waitcondition.py @@ -214,7 +214,8 @@ class HeatWaitConditionTest(common.HeatTestCase): 'status': 'SUCCESS', 'id': '456'} ret = handle.handle_signal(details=test_metadata) wc_att = rsrc.FnGetAtt('data') - self.assertEqual(u'{"123": "foo", "456": "dog"}', wc_att) + self.assertEqual(json.loads(u'{"123": "foo", "456": "dog"}'), + json.loads(wc_att)) self.assertEqual('status:SUCCESS reason:cat', ret) self.m.VerifyAll() diff --git a/heat/tests/test_common_serializers.py b/heat/tests/test_common_serializers.py index eadbb7e253..5bbe623bd4 100644 --- a/heat/tests/test_common_serializers.py +++ b/heat/tests/test_common_serializers.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import datetime import webob @@ -37,13 +38,21 @@ class JSONResponseSerializerTest(common.HeatTestCase): self.assertEqual(expected, actual) def test_to_json_with_more_deep_format(self): - fixture = {"is_public": True, "name": [{"name1": "test"}]} + fixture = collections.OrderedDict([ + ('is_public', True), + ('name', [collections.OrderedDict([ + ('name1', 'test'), + ])]) + ]) expected = '{"is_public": true, "name": [{"name1": "test"}]}' actual = serializers.JSONResponseSerializer().to_json(fixture) self.assertEqual(expected, actual) def test_to_json_with_objects(self): - fixture = {"is_public": True, "value": complex(1, 2)} + fixture = collections.OrderedDict([ + ('is_public', True), + ('value', complex(1, 2)), + ]) expected = '{"is_public": true, "value": "(1+2j)"}' actual = serializers.JSONResponseSerializer().to_json(fixture) self.assertEqual(expected, actual) @@ -83,8 +92,14 @@ class XMLResponseSerializerTest(common.HeatTestCase): def test_to_xml_with_more_deep_format(self): # Note we expect tree traversal from one root key, which is compatible # with the AWS format responses we need to serialize - fixture = {"aresponse": - {"is_public": True, "name": [{"name1": "test"}]}} + fixture = collections.OrderedDict([ + ('aresponse', collections.OrderedDict([ + ('is_public', True), + ('name', [collections.OrderedDict([ + ('name1', 'test'), + ])]) + ])) + ]) expected = ('True' 'test' '') @@ -94,10 +109,13 @@ class XMLResponseSerializerTest(common.HeatTestCase): def test_to_xml_with_json_only_keys(self): # Certain keys are excluded from serialization because CFN # format demands a json blob in the XML body - fixture = {"aresponse": - {"is_public": True, - "TemplateBody": {"name1": "test"}, - "Metadata": {"name2": "test2"}}} + fixture = collections.OrderedDict([ + ('aresponse', collections.OrderedDict([ + ('is_public', True), + ('TemplateBody', {"name1": "test"}), + ('Metadata', {"name2": "test2"}), + ])) + ]) expected = ('True' '{"name1": "test"}' '{"name2": "test2"}') diff --git a/heat/tests/test_metadata_refresh.py b/heat/tests/test_metadata_refresh.py index 1e2dfcc272..ce3d5d1fc1 100644 --- a/heat/tests/test_metadata_refresh.py +++ b/heat/tests/test_metadata_refresh.py @@ -13,6 +13,7 @@ import mock import mox +from oslo_serialization import jsonutils from heat.common import identifier from heat.common import template_format @@ -286,12 +287,13 @@ class WaitCondMetadataUpdateTest(common.HeatTestCase): update_metadata('456', 'blarg', 'wibble') - self.assertEqual('{"123": "foo", "456": "blarg"}', - watch.FnGetAtt('Data')) + self.assertEqual({'123': 'foo', '456': 'blarg'}, + jsonutils.loads(watch.FnGetAtt('Data'))) self.assertEqual('{"123": "foo"}', inst.metadata_get()['test']) - self.assertEqual('{"123": "foo", "456": "blarg"}', - inst.metadata_get(refresh=True)['test']) + self.assertEqual( + {'123': 'foo', '456': 'blarg'}, + jsonutils.loads(inst.metadata_get(refresh=True)['test'])) self.m.VerifyAll() diff --git a/heat/tests/test_parameters.py b/heat/tests/test_parameters.py index 07b0904131..125496f288 100644 --- a/heat/tests/test_parameters.py +++ b/heat/tests/test_parameters.py @@ -75,7 +75,10 @@ class ParameterTestCommon(common.HeatTestCase): def test_param_to_str(self): p = new_parameter('p', {'Type': self.p_type}, self.value) - self.assertEqual(self.expected, str(p)) + if self.p_type == 'Json': + self.assertEqual(json.loads(self.expected), json.loads(str(p))) + else: + self.assertEqual(self.expected, str(p)) def test_default_no_override(self): p = new_parameter('defaulted', {'Type': self.p_type, @@ -137,7 +140,10 @@ class ParameterTestCommon(common.HeatTestCase): 'NoEcho': 'false'}, self.value) self.assertFalse(p.hidden()) - self.assertEqual(self.expected, str(p)) + if self.p_type == 'Json': + self.assertEqual(json.loads(self.expected), json.loads(str(p))) + else: + self.assertEqual(self.expected, str(p)) def test_default_empty(self): p = new_parameter('defaulted', {'Type': self.p_type, diff --git a/heat/tests/test_provider_template.py b/heat/tests/test_provider_template.py index eddb67519d..d6f1017eab 100644 --- a/heat/tests/test_provider_template.py +++ b/heat/tests/test_provider_template.py @@ -11,6 +11,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import json import os import uuid @@ -140,8 +141,13 @@ class ProviderTemplateTest(common.HeatTestCase): prop_vals = { "Foo": "Bar", "AList": ["one", "two", "three"], - "MemList": [{"key": "name", "value": "three"}, - {"key": "name", "value": "four"}], + "MemList": [collections.OrderedDict([ + ('key', 'name'), + ('value', 'three'), + ]), collections.OrderedDict([ + ('key', 'name'), + ('value', 'four'), + ])], "ListEmpty": [], "ANum": 5, "AMap": map_prop_val, @@ -165,7 +171,8 @@ class ProviderTemplateTest(common.HeatTestCase): '.member.0.value=three,' '.member.1.key=name,' '.member.1.value=four') - self.assertEqual(mem_exp, converted_params.get("MemList")) + self.assertEqual(sorted(mem_exp.split(',')), + sorted(converted_params.get("MemList").split(','))) # verify Number conversion self.assertEqual(5, converted_params.get("ANum")) # verify Map conversion diff --git a/heat/tests/test_signal.py b/heat/tests/test_signal.py index 4082899cd8..52f374ecdd 100644 --- a/heat/tests/test_signal.py +++ b/heat/tests/test_signal.py @@ -17,6 +17,7 @@ import uuid from keystoneclient import exceptions as kc_exceptions import mox import six +from six.moves.urllib import parse as urlparse from heat.common import exception from heat.common import template_format @@ -176,19 +177,25 @@ class SignalTest(common.HeatTestCase): rsrc.created_time = created_time self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) - expected_url = "".join([ + # url parameters come in unexpected order, so the conversion has to be + # done for comparison + expected_url_path = "".join([ 'http://server.test:8000/v1/signal/', 'arn%3Aopenstack%3Aheat%3A%3Atest_tenant%3Astacks%2F', 'test_stack%2FSTACKABCD1234%2Fresources%2F', - 'signal_handler?', - 'Timestamp=2012-11-29T13%3A49%3A37Z&', - 'SignatureMethod=HmacSHA256&', - 'AWSAccessKeyId=4567&', - 'SignatureVersion=2&', - 'Signature=', - 'VW4NyvRO4WhQdsQ4rxl5JMUr0AlefHN6OLsRz9oZyls%3D']) + 'signal_handler']) + expected_url_params = { + 'Timestamp': ['2012-11-29T13:49:37Z'], + 'SignatureMethod': ['HmacSHA256'], + 'AWSAccessKeyId': ['4567'], + 'SignatureVersion': ['2'], + 'Signature': ['VW4NyvRO4WhQdsQ4rxl5JMUr0AlefHN6OLsRz9oZyls=']} - self.assertEqual(expected_url, rsrc.FnGetAtt('AlarmUrl')) + url = rsrc.FnGetAtt('AlarmUrl') + url_path, url_params = url.split('?', 1) + url_params = urlparse.parse_qs(url_params) + self.assertEqual(expected_url_path, url_path) + self.assertEqual(expected_url_params, url_params) self.m.VerifyAll() def test_FnGetAtt_Alarm_Url_is_cached(self): diff --git a/heat/tests/test_stack_resource.py b/heat/tests/test_stack_resource.py index c04d021445..3a1becaca6 100644 --- a/heat/tests/test_stack_resource.py +++ b/heat/tests/test_stack_resource.py @@ -17,6 +17,7 @@ import uuid import mock from oslo_config import cfg from oslo_messaging import exceptions as msg_exceptions +from oslo_serialization import jsonutils import six import testtools @@ -156,11 +157,24 @@ class StackResourceBaseTest(common.HeatTestCase): class StackResourceTest(StackResourceBaseTest): + def setUp(self): super(StackResourceTest, self).setUp() self.templ = template_format.parse(param_template) self.simple_template = template_format.parse(simple_template) + # to get same json string from a dict for comparison, + # make sort_keys True + orig_dumps = jsonutils.dumps + + def sorted_dumps(*args, **kwargs): + kwargs.setdefault('sort_keys', True) + return orig_dumps(*args, **kwargs) + patched_dumps = mock.patch( + 'oslo_serialization.jsonutils.dumps', sorted_dumps) + patched_dumps.start() + self.addCleanup(lambda: patched_dumps.stop()) + def test_child_template_defaults_to_not_implemented(self): self.assertRaises(NotImplementedError, self.parent_resource.child_template) @@ -188,8 +202,8 @@ class StackResourceTest(StackResourceBaseTest): sig1, sig2 = self.parent_resource.implementation_signature() self.assertEqual('7b0eaabb5b82b9e90804d42e0bb739035588cb797' '82427770646686ca2235028', sig1) - self.assertEqual('5a58b34cc3dd7f4e11fa35b63daad7b6b3aaa1744' - '19eb1c42b75d102bdda5fc9', sig2) + self.assertEqual('8fa647d036b8f36909386e1e1004539dfae7a8e88' + 'c24aac0d85399e881421301', sig2) self.parent_stack.t.files["foo"] = "bar" sig1a, sig2a = self.parent_resource.implementation_signature() self.assertEqual(sig1, sig1a) diff --git a/heat/tests/utils.py b/heat/tests/utils.py index 182daaf2b9..3126aa11cf 100644 --- a/heat/tests/utils.py +++ b/heat/tests/utils.py @@ -15,8 +15,10 @@ import random import string import uuid +import mox from oslo_config import cfg from oslo_db import options +from oslo_serialization import jsonutils import sqlalchemy from heat.common import context @@ -143,3 +145,32 @@ class PhysName(object): def __repr__(self): return self._physname + + +def recursive_sort(obj): + """Recursively sort list in iterables for comparison.""" + if isinstance(obj, dict): + for v in obj.values(): + recursive_sort(v) + elif isinstance(obj, list): + obj.sort() + for i in obj: + recursive_sort(i) + return obj + + +class JsonEquals(mox.Comparator): + """Comparison class used to check if two json strings equal. + + If a dict is dumped to json, the order is undecided, so load the string + back to an object for comparison + """ + + def __init__(self, other_json): + self.other_json = other_json + + def equals(self, rhs): + return jsonutils.loads(self.other_json) == jsonutils.loads(rhs) + + def __repr__(self): + return "" % self.other_json diff --git a/tox.ini b/tox.ini index 2d1c07ea12..0eef4fd407 100644 --- a/tox.ini +++ b/tox.ini @@ -4,10 +4,7 @@ minversion = 1.6 skipsdist = True [testenv] -# Note the hash seed is set to 0 until heat can be tested with a -# random hash seed successfully. setenv = VIRTUAL_ENV={envdir} - PYTHONHASHSEED=0 usedevelop = True install_command = pip install {opts} {packages} deps = -r{toxinidir}/requirements.txt