Improve unit tests to succeed without hash tweak

PYTHONHASHSEED was set to 0 to disable hash randomization, because some
tests assertions were order sensitive. This commit is to improve the
assertions, so the PYTHONHASHSEED hack in tox.ini is not needed.

Change-Id: I4ff09d202af818d27321e8e83718e82d0c48e3d2
Closes-Bug: 1348818
This commit is contained in:
Wang Muyu 2015-08-16 11:21:24 -04:00
parent 87e625e20c
commit b070ed0286
19 changed files with 183 additions and 67 deletions

View File

@ -17,6 +17,7 @@ import json
import uuid import uuid
import mock import mock
import mox
import six import six
from heat.common import exception from heat.common import exception
@ -1229,11 +1230,11 @@ class LoadBalancerTest(common.HeatTestCase):
template['Resources'][lb_name]['Properties']['metadata'] = { template['Resources'][lb_name]['Properties']['metadata'] = {
'a': 1, 'b': 2} 'a': 1, 'b': 2}
expected_body = copy.deepcopy(self.expected_body) expected_body = copy.deepcopy(self.expected_body)
expected_body['metadata'] = [{'key': 'a', 'value': 1}, expected_body['metadata'] = mox.SameElementsAs(
{'key': 'b', 'value': 2}] [{'key': 'a', 'value': 1},
rsrc, fake_loadbalancer = self._mock_loadbalancer(template, {'key': 'b', 'value': 2}])
self.lb_name, rsrc, fake_loadbalancer = self._mock_loadbalancer(
expected_body) template, self.lb_name, expected_body)
self.m.ReplayAll() self.m.ReplayAll()
scheduler.TaskRunner(rsrc.create)() scheduler.TaskRunner(rsrc.create)()

View File

@ -24,12 +24,13 @@ from heat.api.aws import ec2token
from heat.api.aws import exception from heat.api.aws import exception
from heat.common import wsgi from heat.common import wsgi
from heat.tests import common from heat.tests import common
from heat.tests import utils
class Ec2TokenTest(common.HeatTestCase): class Ec2TokenTest(common.HeatTestCase):
''' """
Tests the Ec2Token middleware Tests the Ec2Token middleware
''' """
def setUp(self): def setUp(self):
super(Ec2TokenTest, self).setUp() super(Ec2TokenTest, self).setUp()
@ -253,8 +254,9 @@ class Ec2TokenTest(common.HeatTestCase):
"path": "/v1", "path": "/v1",
"body_hash": body_hash}}) "body_hash": body_hash}})
req_headers = {'Content-Type': 'application/json'} req_headers = {'Content-Type': 'application/json'}
requests.post(req_url, data=req_creds, verify=verify, cert=cert, requests.post(
headers=req_headers).AndReturn(DummyHTTPResponse()) req_url, data=utils.JsonEquals(req_creds), verify=verify,
cert=cert, headers=req_headers).AndReturn(DummyHTTPResponse())
def test_call_ok(self): def test_call_ok(self):
dummy_conf = {'auth_uri': 'http://123:5000/v2.0'} dummy_conf = {'auth_uri': 'http://123:5000/v2.0'}

View File

@ -33,10 +33,10 @@ policy_path = os.path.dirname(os.path.realpath(__file__)) + "/../../policy/"
class CfnStackControllerTest(common.HeatTestCase): class CfnStackControllerTest(common.HeatTestCase):
''' """
Tests the API class which acts as the WSGI controller, Tests the API class which acts as the WSGI controller,
the endpoint processing API requests after they are routed the endpoint processing API requests after they are routed
''' """
def setUp(self): def setUp(self):
super(CfnStackControllerTest, self).setUp() super(CfnStackControllerTest, self).setUp()
@ -333,7 +333,8 @@ class CfnStackControllerTest(common.HeatTestCase):
'DisableRollback': 'true', 'DisableRollback': 'true',
'LastUpdatedTime': u'2012-07-09T09:13:11Z'}]}}} '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): def test_describe_arn(self):
# Format a dummy GET request to pass into the WSGI handler # Format a dummy GET request to pass into the WSGI handler
@ -417,7 +418,8 @@ class CfnStackControllerTest(common.HeatTestCase):
'DisableRollback': 'true', 'DisableRollback': 'true',
'LastUpdatedTime': u'2012-07-09T09:13:11Z'}]}}} '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): def test_describe_arn_invalidtenant(self):
# Format a dummy GET request to pass into the WSGI handler # Format a dummy GET request to pass into the WSGI handler
@ -481,7 +483,7 @@ class CfnStackControllerTest(common.HeatTestCase):
self.assertIsInstance(result, exception.HeatInvalidParameterValueError) self.assertIsInstance(result, exception.HeatInvalidParameterValueError)
def test_get_template_int_body(self): def test_get_template_int_body(self):
'''Test the internal _get_template function.''' """Test the internal _get_template function."""
params = {'TemplateBody': "abcdef"} params = {'TemplateBody': "abcdef"}
dummy_req = self._dummy_GET_request(params) dummy_req = self._dummy_GET_request(params)
result = self.controller._get_template(dummy_req) result = self.controller._get_template(dummy_req)

View File

@ -73,11 +73,13 @@ class WatchControllerTest(common.HeatTestCase):
dims = [{'StackId': u'21617058-781e-4262-97ab-5f9df371ee52', dims = [{'StackId': u'21617058-781e-4262-97ab-5f9df371ee52',
'Foo': 'bar'}] 'Foo': 'bar'}]
self.assertEqual([{'Name': 'StackId', self.assertEqual(
utils.recursive_sort(
[{'Name': 'StackId',
'Value': u'21617058-781e-4262-97ab-5f9df371ee52'}, 'Value': u'21617058-781e-4262-97ab-5f9df371ee52'},
{'Name': 'Foo', 'Value': 'bar'}], {'Name': 'Foo',
self.controller._reformat_dimensions(dims) 'Value': 'bar'}]),
) utils.recursive_sort(self.controller._reformat_dimensions(dims)))
def test_enforce_default(self): def test_enforce_default(self):
self.m.ReplayAll() self.m.ReplayAll()
@ -301,7 +303,9 @@ class WatchControllerTest(common.HeatTestCase):
'MetricName': u'ServiceFailure3'}]}}} 'MetricName': u'ServiceFailure3'}]}}}
# First pass no query paramters filtering, should get all three # 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): def test_list_metrics_filter_name(self):
@ -358,7 +362,9 @@ class WatchControllerTest(common.HeatTestCase):
'Value': 1}], 'Value': 1}],
'MetricName': u'ServiceFailure'}]}}} 'MetricName': u'ServiceFailure'}]}}}
# First pass no query paramters filtering, should get all three # 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): def test_list_metrics_filter_namespace(self):
@ -426,7 +432,9 @@ class WatchControllerTest(common.HeatTestCase):
{'Name': u'Value', {'Name': u'Value',
'Value': 1}], 'Value': 1}],
'MetricName': u'ServiceFailure2'}]}}} '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): def test_put_metric_alarm(self):
# Not yet implemented, should raise HeatAPINotImplementedError # Not yet implemented, should raise HeatAPINotImplementedError

View File

@ -38,10 +38,12 @@ class TestViewsCommon(common.HeatTestCase):
self.setUpGetCollectionLinks() self.setUpGetCollectionLinks()
links = views_common.get_collection_links(self.request, self.items) 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() next_link = filter(lambda link: link['rel'] == 'next', links).pop()
self.assertEqual('next', next_link['rel']) 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): def test_get_collection_links_doesnt_create_next_if_no_limit(self):
self.setUpGetCollectionLinks() self.setUpGetCollectionLinks()
@ -62,9 +64,12 @@ class TestViewsCommon(common.HeatTestCase):
self.request.params = {'limit': '2', 'marker': 'some_marker'} self.request.params = {'limit': '2', 'marker': 'some_marker'}
links = views_common.get_collection_links(self.request, self.items) 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() 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): def test_get_collection_links_does_not_overwrite_other_params(self):
self.setUpGetCollectionLinks() self.setUpGetCollectionLinks()

View File

@ -88,7 +88,8 @@ class LoadBalancerTest(common.HeatTestCase):
lb_defn = s.t.resource_definitions(s)[resource_name] lb_defn = s.t.resource_definitions(s)[resource_name]
rsrc = lb.LoadBalancer(resource_name, lb_defn, s) 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': initial_md = {'AWS::CloudFormation::Init':
{'config': {'config':

View File

@ -198,7 +198,7 @@ class CeilometerAlarmTest(common.HeatTestCase):
if 'matching_metadata' in al: if 'matching_metadata' in al:
del al['matching_metadata'] del al['matching_metadata']
if query: if query:
rule['query'] = query rule['query'] = mox.SameElementsAs(query)
al['threshold_rule'] = rule al['threshold_rule'] = rule
al['type'] = 'threshold' al['type'] = 'threshold'
self.m.StubOutWithMock(self.fa.alarms, 'create') self.m.StubOutWithMock(self.fa.alarms, 'create')
@ -396,8 +396,9 @@ class CeilometerAlarmTest(common.HeatTestCase):
self.m.VerifyAll() self.m.VerifyAll()
def test_mem_alarm_high_not_correct_string_parameters(self): 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'): for p in ('period', 'evaluation_periods'):
snippet = copy.deepcopy(orig_snippet)
snippet['Resources']['MEMAlarmHigh']['Properties'][p] = '60a' snippet['Resources']['MEMAlarmHigh']['Properties'][p] = '60a'
stack = utils.parse_stack(snippet) stack = utils.parse_stack(snippet)
@ -411,8 +412,9 @@ class CeilometerAlarmTest(common.HeatTestCase):
"Value '60a' is not an integer" % p, six.text_type(error)) "Value '60a' is not an integer" % p, six.text_type(error))
def test_mem_alarm_high_not_integer_parameters(self): 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'): for p in ('period', 'evaluation_periods'):
snippet = copy.deepcopy(orig_snippet)
snippet['Resources']['MEMAlarmHigh']['Properties'][p] = [60] snippet['Resources']['MEMAlarmHigh']['Properties'][p] = [60]
stack = utils.parse_stack(snippet) stack = utils.parse_stack(snippet)

View File

@ -374,7 +374,12 @@ class NovaClientPluginMetadataTests(NovaClientPluginTestCase):
self.assertEqual(expected, self.nova_plugin.meta_serialize(original)) self.assertEqual(expected, self.nova_plugin.meta_serialize(original))
def test_serialize_dict(self): 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"}'} expected = {'test_key': '{"a": "b", "c": "d"}'}
actual = self.nova_plugin.meta_serialize(original) actual = self.nova_plugin.meta_serialize(original)
self.assertEqual(json.loads(expected['test_key']), self.assertEqual(json.loads(expected['test_key']),

View File

@ -85,6 +85,11 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
return self.engine.create_software_config( return self.engine.create_software_config(
self.ctx, group, name, config, inputs, outputs, options) 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): def test_list_software_configs(self):
config = self._create_software_config() config = self._create_software_config()
config_id = config['id'] config_id = config['id']
@ -362,7 +367,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
sd = software_deployment_object.SoftwareDeployment.get_by_id( sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id) self.ctx, deployment_id)
self.assertEqual('FAILED', sd.status) self.assertEqual('FAILED', sd.status)
self.assertEqual( self.assert_status_reason(
('deploy_status_code : Deployment exited with non-zero ' ('deploy_status_code : Deployment exited with non-zero '
'status code: -1'), 'status code: -1'),
sd.status_reason) sd.status_reason)
@ -394,7 +399,7 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
sd = software_deployment_object.SoftwareDeployment.get_by_id( sd = software_deployment_object.SoftwareDeployment.get_by_id(
self.ctx, deployment_id) self.ctx, deployment_id)
self.assertEqual('FAILED', sd.status) self.assertEqual('FAILED', sd.status)
self.assertEqual( self.assert_status_reason(
('foo : bar, deploy_status_code : Deployment exited with ' ('foo : bar, deploy_status_code : Deployment exited with '
'non-zero status code: -1'), 'non-zero status code: -1'),
sd.status_reason) sd.status_reason)

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import collections
from oslo_log import log as logging from oslo_log import log as logging
import six import six
@ -31,8 +32,9 @@ class GenericResource(resource.Resource):
Dummy resource for use in tests Dummy resource for use in tests
''' '''
properties_schema = {} properties_schema = {}
attributes_schema = {'foo': attributes.Schema('A generic attribute'), attributes_schema = collections.OrderedDict([
'Foo': attributes.Schema('Another generic attribute')} ('foo', attributes.Schema('A generic attribute')),
('Foo', attributes.Schema('Another generic attribute'))])
@classmethod @classmethod
def is_service_available(cls, context): def is_service_available(cls, context):

View File

@ -214,7 +214,8 @@ class HeatWaitConditionTest(common.HeatTestCase):
'status': 'SUCCESS', 'id': '456'} 'status': 'SUCCESS', 'id': '456'}
ret = handle.handle_signal(details=test_metadata) ret = handle.handle_signal(details=test_metadata)
wc_att = rsrc.FnGetAtt('data') 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.assertEqual('status:SUCCESS reason:cat', ret)
self.m.VerifyAll() self.m.VerifyAll()

View File

@ -14,6 +14,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import collections
import datetime import datetime
import webob import webob
@ -37,13 +38,21 @@ class JSONResponseSerializerTest(common.HeatTestCase):
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
def test_to_json_with_more_deep_format(self): 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"}]}' expected = '{"is_public": true, "name": [{"name1": "test"}]}'
actual = serializers.JSONResponseSerializer().to_json(fixture) actual = serializers.JSONResponseSerializer().to_json(fixture)
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
def test_to_json_with_objects(self): 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)"}' expected = '{"is_public": true, "value": "(1+2j)"}'
actual = serializers.JSONResponseSerializer().to_json(fixture) actual = serializers.JSONResponseSerializer().to_json(fixture)
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
@ -83,8 +92,14 @@ class XMLResponseSerializerTest(common.HeatTestCase):
def test_to_xml_with_more_deep_format(self): def test_to_xml_with_more_deep_format(self):
# Note we expect tree traversal from one root key, which is compatible # Note we expect tree traversal from one root key, which is compatible
# with the AWS format responses we need to serialize # with the AWS format responses we need to serialize
fixture = {"aresponse": fixture = collections.OrderedDict([
{"is_public": True, "name": [{"name1": "test"}]}} ('aresponse', collections.OrderedDict([
('is_public', True),
('name', [collections.OrderedDict([
('name1', 'test'),
])])
]))
])
expected = ('<aresponse><is_public>True</is_public>' expected = ('<aresponse><is_public>True</is_public>'
'<name><member><name1>test</name1></member></name>' '<name><member><name1>test</name1></member></name>'
'</aresponse>') '</aresponse>')
@ -94,10 +109,13 @@ class XMLResponseSerializerTest(common.HeatTestCase):
def test_to_xml_with_json_only_keys(self): def test_to_xml_with_json_only_keys(self):
# Certain keys are excluded from serialization because CFN # Certain keys are excluded from serialization because CFN
# format demands a json blob in the XML body # format demands a json blob in the XML body
fixture = {"aresponse": fixture = collections.OrderedDict([
{"is_public": True, ('aresponse', collections.OrderedDict([
"TemplateBody": {"name1": "test"}, ('is_public', True),
"Metadata": {"name2": "test2"}}} ('TemplateBody', {"name1": "test"}),
('Metadata', {"name2": "test2"}),
]))
])
expected = ('<aresponse><is_public>True</is_public>' expected = ('<aresponse><is_public>True</is_public>'
'<TemplateBody>{"name1": "test"}</TemplateBody>' '<TemplateBody>{"name1": "test"}</TemplateBody>'
'<Metadata>{"name2": "test2"}</Metadata></aresponse>') '<Metadata>{"name2": "test2"}</Metadata></aresponse>')

View File

@ -13,6 +13,7 @@
import mock import mock
import mox import mox
from oslo_serialization import jsonutils
from heat.common import identifier from heat.common import identifier
from heat.common import template_format from heat.common import template_format
@ -286,12 +287,13 @@ class WaitCondMetadataUpdateTest(common.HeatTestCase):
update_metadata('456', 'blarg', 'wibble') update_metadata('456', 'blarg', 'wibble')
self.assertEqual('{"123": "foo", "456": "blarg"}', self.assertEqual({'123': 'foo', '456': 'blarg'},
watch.FnGetAtt('Data')) jsonutils.loads(watch.FnGetAtt('Data')))
self.assertEqual('{"123": "foo"}', self.assertEqual('{"123": "foo"}',
inst.metadata_get()['test']) inst.metadata_get()['test'])
self.assertEqual('{"123": "foo", "456": "blarg"}', self.assertEqual(
inst.metadata_get(refresh=True)['test']) {'123': 'foo', '456': 'blarg'},
jsonutils.loads(inst.metadata_get(refresh=True)['test']))
self.m.VerifyAll() self.m.VerifyAll()

View File

@ -75,6 +75,9 @@ class ParameterTestCommon(common.HeatTestCase):
def test_param_to_str(self): def test_param_to_str(self):
p = new_parameter('p', {'Type': self.p_type}, self.value) p = new_parameter('p', {'Type': self.p_type}, self.value)
if self.p_type == 'Json':
self.assertEqual(json.loads(self.expected), json.loads(str(p)))
else:
self.assertEqual(self.expected, str(p)) self.assertEqual(self.expected, str(p))
def test_default_no_override(self): def test_default_no_override(self):
@ -137,6 +140,9 @@ class ParameterTestCommon(common.HeatTestCase):
'NoEcho': 'false'}, 'NoEcho': 'false'},
self.value) self.value)
self.assertFalse(p.hidden()) self.assertFalse(p.hidden())
if self.p_type == 'Json':
self.assertEqual(json.loads(self.expected), json.loads(str(p)))
else:
self.assertEqual(self.expected, str(p)) self.assertEqual(self.expected, str(p))
def test_default_empty(self): def test_default_empty(self):

View File

@ -11,6 +11,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import collections
import json import json
import os import os
import uuid import uuid
@ -140,8 +141,13 @@ class ProviderTemplateTest(common.HeatTestCase):
prop_vals = { prop_vals = {
"Foo": "Bar", "Foo": "Bar",
"AList": ["one", "two", "three"], "AList": ["one", "two", "three"],
"MemList": [{"key": "name", "value": "three"}, "MemList": [collections.OrderedDict([
{"key": "name", "value": "four"}], ('key', 'name'),
('value', 'three'),
]), collections.OrderedDict([
('key', 'name'),
('value', 'four'),
])],
"ListEmpty": [], "ListEmpty": [],
"ANum": 5, "ANum": 5,
"AMap": map_prop_val, "AMap": map_prop_val,
@ -165,7 +171,8 @@ class ProviderTemplateTest(common.HeatTestCase):
'.member.0.value=three,' '.member.0.value=three,'
'.member.1.key=name,' '.member.1.key=name,'
'.member.1.value=four') '.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 # verify Number conversion
self.assertEqual(5, converted_params.get("ANum")) self.assertEqual(5, converted_params.get("ANum"))
# verify Map conversion # verify Map conversion

View File

@ -17,6 +17,7 @@ import uuid
from keystoneclient import exceptions as kc_exceptions from keystoneclient import exceptions as kc_exceptions
import mox import mox
import six import six
from six.moves.urllib import parse as urlparse
from heat.common import exception from heat.common import exception
from heat.common import template_format from heat.common import template_format
@ -176,19 +177,25 @@ class SignalTest(common.HeatTestCase):
rsrc.created_time = created_time rsrc.created_time = created_time
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) 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/', 'http://server.test:8000/v1/signal/',
'arn%3Aopenstack%3Aheat%3A%3Atest_tenant%3Astacks%2F', 'arn%3Aopenstack%3Aheat%3A%3Atest_tenant%3Astacks%2F',
'test_stack%2FSTACKABCD1234%2Fresources%2F', 'test_stack%2FSTACKABCD1234%2Fresources%2F',
'signal_handler?', 'signal_handler'])
'Timestamp=2012-11-29T13%3A49%3A37Z&', expected_url_params = {
'SignatureMethod=HmacSHA256&', 'Timestamp': ['2012-11-29T13:49:37Z'],
'AWSAccessKeyId=4567&', 'SignatureMethod': ['HmacSHA256'],
'SignatureVersion=2&', 'AWSAccessKeyId': ['4567'],
'Signature=', 'SignatureVersion': ['2'],
'VW4NyvRO4WhQdsQ4rxl5JMUr0AlefHN6OLsRz9oZyls%3D']) '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() self.m.VerifyAll()
def test_FnGetAtt_Alarm_Url_is_cached(self): def test_FnGetAtt_Alarm_Url_is_cached(self):

View File

@ -17,6 +17,7 @@ import uuid
import mock import mock
from oslo_config import cfg from oslo_config import cfg
from oslo_messaging import exceptions as msg_exceptions from oslo_messaging import exceptions as msg_exceptions
from oslo_serialization import jsonutils
import six import six
import testtools import testtools
@ -156,11 +157,24 @@ class StackResourceBaseTest(common.HeatTestCase):
class StackResourceTest(StackResourceBaseTest): class StackResourceTest(StackResourceBaseTest):
def setUp(self): def setUp(self):
super(StackResourceTest, self).setUp() super(StackResourceTest, self).setUp()
self.templ = template_format.parse(param_template) self.templ = template_format.parse(param_template)
self.simple_template = template_format.parse(simple_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): def test_child_template_defaults_to_not_implemented(self):
self.assertRaises(NotImplementedError, self.assertRaises(NotImplementedError,
self.parent_resource.child_template) self.parent_resource.child_template)
@ -188,8 +202,8 @@ class StackResourceTest(StackResourceBaseTest):
sig1, sig2 = self.parent_resource.implementation_signature() sig1, sig2 = self.parent_resource.implementation_signature()
self.assertEqual('7b0eaabb5b82b9e90804d42e0bb739035588cb797' self.assertEqual('7b0eaabb5b82b9e90804d42e0bb739035588cb797'
'82427770646686ca2235028', sig1) '82427770646686ca2235028', sig1)
self.assertEqual('5a58b34cc3dd7f4e11fa35b63daad7b6b3aaa1744' self.assertEqual('8fa647d036b8f36909386e1e1004539dfae7a8e88'
'19eb1c42b75d102bdda5fc9', sig2) 'c24aac0d85399e881421301', sig2)
self.parent_stack.t.files["foo"] = "bar" self.parent_stack.t.files["foo"] = "bar"
sig1a, sig2a = self.parent_resource.implementation_signature() sig1a, sig2a = self.parent_resource.implementation_signature()
self.assertEqual(sig1, sig1a) self.assertEqual(sig1, sig1a)

View File

@ -15,8 +15,10 @@ import random
import string import string
import uuid import uuid
import mox
from oslo_config import cfg from oslo_config import cfg
from oslo_db import options from oslo_db import options
from oslo_serialization import jsonutils
import sqlalchemy import sqlalchemy
from heat.common import context from heat.common import context
@ -143,3 +145,32 @@ class PhysName(object):
def __repr__(self): def __repr__(self):
return self._physname 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 "<equals to json '%s'>" % self.other_json

View File

@ -4,10 +4,7 @@ minversion = 1.6
skipsdist = True skipsdist = True
[testenv] [testenv]
# Note the hash seed is set to 0 until heat can be tested with a
# random hash seed successfully.
setenv = VIRTUAL_ENV={envdir} setenv = VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
usedevelop = True usedevelop = True
install_command = pip install {opts} {packages} install_command = pip install {opts} {packages}
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt