# Copyright 2010 United States Government as represented by the # Administrator of the National Aeronautics and Space Administration. # All Rights Reserved. # # 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. """Tests for the testing base code.""" import os.path import tempfile import uuid import mock from oslo_log import log as logging import oslo_messaging as messaging import nova.conf from nova import exception from nova import rpc from nova import test from nova.tests import fixtures LOG = logging.getLogger(__name__) CONF = nova.conf.CONF class IsolationTestCase(test.TestCase): """Ensure that things are cleaned up after failed tests. These tests don't really do much here, but if isolation fails a bunch of other tests should fail. """ def test_service_isolation(self): self.useFixture(fixtures.ServiceFixture('compute')) def test_rpc_consumer_isolation(self): class NeverCalled(object): def __getattribute__(self, name): if name == 'target' or name == 'oslo_rpc_server_ping': # oslo.messaging 5.31.0 explicitly looks for 'target' # on the endpoint and checks it's type, so we can't avoid # it here, just ignore it if that's the case. return assert False, "I should never get called. name: %s" % name server = rpc.get_server(messaging.Target(topic='compute', server=CONF.host), endpoints=[NeverCalled()]) server.start() class JsonTestCase(test.NoDBTestCase): def test_compare_dict_string(self): expected = { "employees": [ {"firstName": "Anna", "lastName": "Smith"}, {"firstName": "John", "lastName": "Doe"}, {"firstName": "Peter", "lastName": "Jones"} ], "locations": set(['Boston', 'Mumbai', 'Beijing', 'Perth']) } actual = """{ "employees": [ { "lastName": "Doe", "firstName": "John" }, { "lastName": "Smith", "firstName": "Anna" }, { "lastName": "Jones", "firstName": "Peter" } ], "locations": [ "Perth", "Boston", "Mumbai", "Beijing" ] }""" self.assertJsonEqual(expected, actual) def test_fail_on_list_length(self): expected = { 'top': { 'l1': { 'l2': ['a', 'b', 'c'] } } } actual = { 'top': { 'l1': { 'l2': ['c', 'a', 'b', 'd'] } } } try: self.assertJsonEqual(expected, actual) except Exception as e: # error reported is going to be a cryptic length failure # on the level2 structure. self.assertEqual( ("3 != 4: path: root.top.l1.l2. Different list items\n" "expected=['a', 'b', 'c']\n" "observed=['a', 'b', 'c', 'd']\n" "difference=['d']"), e.difference) self.assertIn( "actual:\n{'top': {'l1': {'l2': ['c', 'a', 'b', 'd']}}}", str(e)) self.assertIn( "expected:\n{'top': {'l1': {'l2': ['a', 'b', 'c']}}}", str(e)) else: self.fail("This should have raised a mismatch exception") def test_fail_on_dict_length(self): expected = { 'top': { 'l1': { 'l2': {'a': 1, 'b': 2, 'c': 3} } } } actual = { 'top': { 'l1': { 'l2': {'a': 1, 'b': 2} } } } try: self.assertJsonEqual(expected, actual) except Exception as e: self.assertEqual( ("3 != 2: path: root.top.l1.l2. Different dict key sets\n" "expected=['a', 'b', 'c']\n" "observed=['a', 'b']\n" "difference=['c']"), e.difference) else: self.fail("This should have raised a mismatch exception") def test_fail_on_dict_keys(self): expected = { 'top': { 'l1': { 'l2': {'a': 1, 'b': 2, 'c': 3} } } } actual = { 'top': { 'l1': { 'l2': {'a': 1, 'b': 2, 'd': 3} } } } try: self.assertJsonEqual(expected, actual) except Exception as e: self.assertIn( "path: root.top.l1.l2. Dict keys are not equal", e.difference) else: self.fail("This should have raised a mismatch exception") def test_fail_on_list_value(self): expected = { 'top': { 'l1': { 'l2': ['a', 'b', 'c'] } } } actual = { 'top': { 'l1': { 'l2': ['c', 'a', 'd'] } } } try: self.assertJsonEqual(expected, actual) except Exception as e: self.assertEqual( "'b' != 'c': path: root.top.l1.l2[1]", e.difference) self.assertIn( "actual:\n{'top': {'l1': {'l2': ['c', 'a', 'd']}}}", str(e)) self.assertIn( "expected:\n{'top': {'l1': {'l2': ['a', 'b', 'c']}}}", str(e)) else: self.fail("This should have raised a mismatch exception") def test_fail_on_dict_value(self): expected = { 'top': { 'l1': { 'l2': {'a': 1, 'b': 2, 'c': 3} } } } actual = { 'top': { 'l1': { 'l2': {'a': 1, 'b': 2, 'c': 4} } } } try: self.assertJsonEqual(expected, actual, 'test message') except Exception as e: self.assertEqual( "3 != 4: path: root.top.l1.l2.c", e.difference) self.assertIn("actual:\n{'top': {'l1': {'l2': {", str(e)) self.assertIn("expected:\n{'top': {'l1': {'l2': {", str(e)) self.assertIn("message: test message\n", str(e)) else: self.fail("This should have raised a mismatch exception") def test_compare_scalars(self): with self.assertRaisesRegex(AssertionError, 'True != False'): self.assertJsonEqual(True, False) class BadLogTestCase(test.NoDBTestCase): """Make sure a mis-formatted debug log will get caught.""" def test_bad_debug_log(self): self.assertRaises(KeyError, LOG.debug, "this is a misformated %(log)s", {'nothing': 'nothing'}) class MatchTypeTestCase(test.NoDBTestCase): def test_match_type_simple(self): matcher = test.MatchType(dict) self.assertEqual(matcher, {}) self.assertEqual(matcher, {"hello": "world"}) self.assertEqual(matcher, {"hello": ["world"]}) self.assertNotEqual(matcher, []) self.assertNotEqual(matcher, [{"hello": "world"}]) self.assertNotEqual(matcher, 123) self.assertNotEqual(matcher, "foo") def test_match_type_object(self): class Hello(object): pass class World(object): pass matcher = test.MatchType(Hello) self.assertEqual(matcher, Hello()) self.assertNotEqual(matcher, World()) self.assertNotEqual(matcher, 123) self.assertNotEqual(matcher, "foo") class ContainKeyValueTestCase(test.NoDBTestCase): def test_contain_key_value_normal(self): matcher = test.ContainKeyValue('foo', 'bar') self.assertEqual(matcher, {123: 'nova', 'foo': 'bar'}) self.assertNotEqual(matcher, {'foo': 123}) self.assertNotEqual(matcher, {}) def test_contain_key_value_exception(self): matcher = test.ContainKeyValue('foo', 'bar') # Raise TypeError self.assertNotEqual(matcher, 123) self.assertNotEqual(matcher, 'foo') # Raise KeyError self.assertNotEqual(matcher, {1: 2, '3': 4, 5: '6'}) self.assertNotEqual(matcher, {'bar': 'foo'}) class NovaExceptionReraiseFormatErrorTestCase(test.NoDBTestCase): """Test that format errors are reraised in tests.""" def test_format_error_in_nova_exception(self): class FakeImageException(exception.NovaException): msg_fmt = 'Image %(image_id)s has wrong type %(type)s.' # wrong kwarg ex = self.assertRaises(KeyError, FakeImageException, bogus='wrongkwarg') self.assertIn('image_id', str(ex)) # no kwarg ex = self.assertRaises(KeyError, FakeImageException) self.assertIn('image_id', str(ex)) # not enough kwargs ex = self.assertRaises(KeyError, FakeImageException, image_id='image') self.assertIn('type', str(ex)) class PatchExistsTestCase(test.NoDBTestCase): def test_with_patch_exists_true(self): """Test that "with patch_exists" can fake the existence of a file without changing other file existence checks, and that calls can be asserted on the mocked method. """ self.assertFalse(os.path.exists('fake_file')) with self.patch_exists('fake_file', True) as mock_exists: self.assertTrue(os.path.exists('fake_file')) self.assertTrue(os.path.exists(__file__)) self.assertFalse(os.path.exists('non-existent/file')) self.assertIn(mock.call('fake_file'), mock_exists.mock_calls) def test_with_patch_exists_false(self): """Test that "with patch_exists" can fake the non-existence of a file without changing other file existence checks, and that calls can be asserted on the mocked method. """ self.assertTrue(os.path.exists(__file__)) with self.patch_exists(__file__, False) as mock_exists: self.assertFalse(os.path.exists(__file__)) self.assertTrue(os.path.exists(os.path.dirname(__file__))) self.assertFalse(os.path.exists('non-existent/file')) self.assertIn(mock.call(__file__), mock_exists.mock_calls) @test.patch_exists('fake_file', True) def test_patch_exists_decorator_true(self): """Test that @patch_exists can fake the existence of a file without changing other file existence checks. """ self.assertTrue(os.path.exists('fake_file')) self.assertTrue(os.path.exists(__file__)) self.assertFalse(os.path.exists('non-existent/file')) @test.patch_exists(__file__, False) def test_patch_exists_decorator_false(self): """Test that @patch_exists can fake the non-existence of a file without changing other file existence checks. """ self.assertFalse(os.path.exists(__file__)) self.assertTrue(os.path.exists(os.path.dirname(__file__))) self.assertFalse(os.path.exists('non-existent/file')) class PatchOpenTestCase(test.NoDBTestCase): fake_contents = "These file contents don't really exist" def _test_patched_open(self): """Test that a selectively patched open can fake the contents of a file while still allowing normal, real file operations. """ self.assertFalse(os.path.exists('fake_file')) with open('fake_file') as f: self.assertEqual(self.fake_contents, f.read()) # Test we can still open and read this file from within the # same context. NOTE: We have to make sure we open the .py # file not the corresponding .pyc file. with open(__file__.rstrip('c')) as f: this_file_contents = f.read() self.assertIn("class %s(" % self.__class__.__name__, this_file_contents) self.assertNotIn("magic concatenated" "string", this_file_contents) # Test we can still create, write to, and then read from a # temporary file, from within the same context. tmp = tempfile.NamedTemporaryFile() tmp_contents = str(uuid.uuid1()) with open(tmp.name, 'w') as f: f.write(tmp_contents) with open(tmp.name) as f: self.assertEqual(tmp_contents, f.read()) return tmp.name def test_with_patch_open(self): """Test that "with patch_open" can fake the contents of a file without changing other file operations, and that calls can be asserted on the mocked method. """ with self.patch_open('fake_file', self.fake_contents) as mock_open: tmp_name = self._test_patched_open() # Test we can make assertions about how the mock_open was called. self.assertIn(mock.call('fake_file'), mock_open.mock_calls) # The mock_open should get bypassed for non-patched path values: self.assertNotIn(mock.call(__file__), mock_open.mock_calls) self.assertNotIn(mock.call(tmp_name), mock_open.mock_calls) @test.patch_open('fake_file', fake_contents) def test_patch_open_decorator(self): """Test that @patch_open can fake the contents of a file without changing other file operations. """ self._test_patched_open()