Add unit tests to increase coverage

This patch proposes adding several new unit tests in effort to increase
the total unit test coverage.

Additionally, the ./tempest/serial_tests/ directory is not included in
coverage calculation. The reason being we don't want to test tests with
unit tests, which becomes a bit of an overkill.

Finally, there is an addition of a new option for the coverage program
called --fail-under. It will result the program in failure if the
coverage is under set percentage. Reason for this implementation is to
encourage writing unit tests for new code.

Change-Id: I804116413cd7d73cd7e5ae71409a8855ef937b88
diff --git a/.coveragerc b/.coveragerc
index 449e62c..398755b 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,4 +1,4 @@
 [run]
 branch = True
 source = tempest
-omit = tempest/tests/*,tempest/scenario/test_*.py,tempest/api/*
+omit = tempest/tests/*,tempest/scenario/test_*.py,tempest/api/*,tempest/serial_tests/*
diff --git a/tempest/tests/common/test_compute.py b/tempest/tests/common/test_compute.py
index 142bb08..4d933cd 100644
--- a/tempest/tests/common/test_compute.py
+++ b/tempest/tests/common/test_compute.py
@@ -17,8 +17,9 @@
 
 from urllib import parse as urlparse
 
-
 from tempest.common import compute
+from tempest import exceptions
+from tempest.lib import exceptions as lib_exc
 from tempest.tests import base
 
 
@@ -28,6 +29,58 @@
         self.client_sock = mock.Mock()
         self.url = urlparse.urlparse("http://www.fake.com:80")
 
+    @mock.patch('tempest.common.compute.' 'config.CONF.validation')
+    def test_get_server_ip_connect_method_floating(self, mock_conf):
+        fake_server = {'id': 'fake-uuid'}
+        fake_vr = {'floating_ip': {'ip': '10.10.10.1'}}
+        mock_conf.connect_method = 'floating'
+
+        fake_server_ip = compute.get_server_ip(fake_server, fake_vr)
+        self.assertEqual(fake_server_ip, '10.10.10.1')
+
+        # assert that InvalidParam is raised when validadation
+        # resources are not set
+        self.assertRaises(lib_exc.InvalidParam,
+                          compute.get_server_ip,
+                          fake_server)
+
+    @mock.patch('tempest.common.compute.' 'config.CONF.validation')
+    def test_get_server_ip_connect_method_fixed(self, mock_conf):
+        fake_server = {'id': 'fake-uuid',
+                       'addresses': {
+                           'private': [
+                               {'addr': '192.168.0.3',
+                                'version': 4}]}}
+        mock_conf.connect_method = 'fixed'
+        mock_conf.network_for_ssh = 'private'
+        mock_conf.ip_version_for_ssh = 4
+
+        fake_server_ip = compute.get_server_ip(fake_server)
+        self.assertEqual(fake_server_ip, '192.168.0.3')
+
+        fake_server_v6 = {'id': 'fake-uuid',
+                          'addresses': {
+                              'private': [
+                                  {'addr': '2345:0425:2CA1::0567:5673:23b5',
+                                   'version': 6}]}}
+        # assert when server is unreachable
+        self.assertRaises(exceptions.ServerUnreachable,
+                          compute.get_server_ip,
+                          fake_server_v6)
+
+    @mock.patch('tempest.common.compute.' 'config.CONF.validation')
+    def test_get_server_ip_invalid_config(self, mock_conf):
+        fake_server = {'id': 'fake-uuid',
+                       'addresses': {
+                           'private': [
+                               {'addr': '192.168.0.3',
+                                'version': 4}]}}
+        mock_conf.connect_method = 'fake-method'
+        # assert when the connection method is not correctly set
+        self.assertRaises(lib_exc.InvalidConfiguration,
+                          compute.get_server_ip,
+                          fake_server)
+
     def test_rfp_frame_not_cached(self):
         # rfp negotiation frame arrived separately after upgrade
         # response, so it's not cached.
diff --git a/tempest/tests/common/test_waiters.py b/tempest/tests/common/test_waiters.py
index 93c949e..f194173 100755
--- a/tempest/tests/common/test_waiters.py
+++ b/tempest/tests/common/test_waiters.py
@@ -27,6 +27,78 @@
 import tempest.tests.utils as utils
 
 
+class TestServerWaiters(base.TestCase):
+    def setUp(self):
+        super(TestServerWaiters, self).setUp()
+        self.client = mock.MagicMock()
+        self.client.build_timeout = 1
+        self.client.build_interval = 1
+
+    def test_wait_for_server_status(self):
+        fake_server = {'id': 'fake-uuid',
+                       'status': 'ACTIVE'}
+        self.client.show_server.return_value = ({'server': fake_server})
+        start_time = int(time.time())
+        waiters.wait_for_server_status(
+            self.client, fake_server['id'], 'ACTIVE')
+        end_time = int(time.time())
+        # Ensure waiter returns before build_timeout
+        self.assertLess((end_time - start_time), 10)
+
+    def test_wait_for_server_status_build(self):
+        fake_server = {'id': 'fake-uuid',
+                       'status': 'BUILD'}
+        self.client.show_server.return_value = ({'server': fake_server})
+        start_time = int(time.time())
+        waiters.wait_for_server_status(self.client, fake_server['id'], 'BUILD')
+        end_time = int(time.time())
+        # Ensure waiter returns before build_timeout
+        self.assertLess((end_time - start_time), 10)
+
+    def test_wait_for_server_status_timeout(self):
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        fake_server = {'id': 'fake-uuid',
+                       'status': 'SAVING'}
+        self.client.show_server.return_value = ({'server': fake_server})
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_server_status,
+                          self.client, fake_server['id'], 'ACTIVE')
+
+    def test_wait_for_server_status_error_on_server_build(self):
+        fake_server = {'id': 'fake-uuid',
+                       'status': 'ERROR'}
+        self.client.show_server.return_value = ({'server': fake_server})
+        self.assertRaises(exceptions.BuildErrorException,
+                          waiters.wait_for_server_status,
+                          self.client, fake_server['id'], 'ACTIVE')
+
+    def test_wait_for_server_termination(self):
+        fake_server = {'id': 'fake-uuid',
+                       'status': 'ACTIVE'}
+        self.client.show_server.side_effect = lib_exc.NotFound
+        waiters.wait_for_server_termination(self.client, fake_server['id'])
+
+    def test_wait_for_server_termination_timeout(self):
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        fake_server = {'id': 'fake-uuid',
+                       'status': 'ACTIVE'}
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_server_termination,
+                          self.client, fake_server['id'])
+
+    def test_wait_for_server_termination_error_status(self):
+        fake_server = {'id': 'fake-uuid',
+                       'status': 'ERROR'}
+        self.client.show_server.return_value = ({'server': fake_server})
+        self.assertRaises(lib_exc.DeleteErrorException,
+                          waiters.wait_for_server_termination,
+                          self.client, fake_server['id'])
+
+
 class TestImageWaiters(base.TestCase):
     def setUp(self):
         super(TestImageWaiters, self).setUp()
@@ -145,6 +217,15 @@
                           waiters.wait_for_image_copied_to_stores,
                           self.client, 'fake_image_id')
 
+    def test_wait_for_image_copied_to_stores_status_killed(self):
+        self.client.show_image.return_value = ({
+            'status': 'killed',
+            'os_glance_importing_to_stores': None,
+            'os_glance_failed_import': 'fake_os_glance_failed_import'})
+        self.assertRaises(exceptions.ImageKilledException,
+                          waiters.wait_for_image_copied_to_stores,
+                          self.client, 'fake_image_id')
+
     def test_wait_for_image_tasks_status(self):
         self.client.show_image_tasks.return_value = ({
             'tasks': [{'status': 'success'}]})
@@ -168,6 +249,28 @@
                           waiters.wait_for_image_tasks_status,
                           self.client, 'fake_image_id', 'success')
 
+    def test_wait_for_tasks_status(self):
+        self.client.show_tasks.return_value = ({
+            'status': 'success'})
+        start_time = int(time.time())
+        waiters.wait_for_tasks_status(
+            self.client, 'fake_task_id', 'success')
+        end_time = int(time.time())
+        # Ensure waiter returns before build_timeout
+        self.assertLess((end_time - start_time), 10)
+
+    def test_wait_for_tasks_status_timeout(self):
+        time_mock = self.patch('time.time')
+        self.patch('time.time', side_effect=[0., 1.])
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        self.client.show_tasks.return_value = (
+            {'status': 'success'},
+            {'status': 'processing'})
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_tasks_status,
+                          self.client, 'fake_task_id', 'success')
+
 
 class TestInterfaceWaiters(base.TestCase):
 
@@ -366,6 +469,31 @@
                                       mock.call(mock.sentinel.volume_id),
                                       mock.call(mock.sentinel.volume_id)])
 
+    def test_wait_for_volume_retype(self):
+        fake_volume = {'volume_type': {'id': 'fake-uuid'}}
+        show_volume = mock.Mock(return_value={'volume': fake_volume})
+        client = mock.Mock(resource_type="volume",
+                           build_interval=1,
+                           build_timeout=1,
+                           show_volume=show_volume)
+        waiters.wait_for_volume_retype(
+            client, mock.sentinel.volume_id, fake_volume['volume_type'])
+
+    def test_wait_for_volume_retype_timeout(self):
+        fake_volume = {'volume_type': {'id': 'fake-uuid'}}
+        show_volume = mock.Mock(return_value={'volume': fake_volume})
+        client = mock.Mock(resource_type="volume",
+                           build_interval=1,
+                           build_timeout=1,
+                           show_volume=show_volume)
+
+        self.patch('time.time', side_effect=[0., client.build_timeout + 1.])
+        self.patch('time.sleep')
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_volume_retype,
+                          client, mock.sentinel.volume_id,
+                          'fake_volume_type')
+
     @mock.patch.object(time, 'sleep')
     def test_wait_for_volume_status_error_restoring(self, mock_sleep):
         # Tests that the wait method raises VolumeRestoreErrorException if
@@ -450,7 +578,25 @@
                                       mock.call(uuids.volume_id),
                                       mock.call(uuids.volume_id)])
 
-    def test_wait_for_volume_attachment(self):
+    def test_wait_for_volume_attachment_create_timeout(self):
+        show_volume = mock.MagicMock(return_value={
+            'volume': {'attachments': [
+                {'attachment_id': uuids.attachment_id,
+                 'server_id': uuids.server_id,
+                 'volume_id': uuids.volume_id}]}})
+        client = mock.Mock(spec=volumes_client.VolumesClient,
+                           build_interval=1,
+                           build_timeout=1,
+                           show_volume=show_volume)
+        self.patch('time.time', side_effect=[0., client.build_timeout + 1.])
+        self.patch('time.sleep')
+        # Assert that a timeout is raised if the attachment is not
+        # created within required time
+        self.assertRaises(lib_exc.TimeoutException,
+                          waiters.wait_for_volume_attachment_create,
+                          client, 'fake_volume_id', 'fake_server_id')
+
+    def test_wait_for_volume_attachment_remove(self):
         vol_detached = {'volume': {'attachments': []}}
         vol_attached = {'volume': {'attachments': [
                        {'attachment_id': uuids.attachment_id}]}}
@@ -469,7 +615,7 @@
                                       mock.call(uuids.volume_id),
                                       mock.call(uuids.volume_id)])
 
-    def test_wait_for_volume_attachment_timeout(self):
+    def test_wait_for_volume_attachment_remove_timeout(self):
         show_volume = mock.MagicMock(return_value={
             'volume': {'attachments': [
                 {'attachment_id': uuids.attachment_id}]}})
@@ -635,6 +781,67 @@
             .1
         )
 
+    def test_wait_for_caching(self):
+        mock_client = mock.Mock(
+            build_interval=1,
+            build_timeout=1
+        )
+        mock_cache_client = mock.Mock()
+        mock_cache_client.list_cache.return_value = {
+            "cached_images": [{
+                "image_id": 'fake_image_id'}]}
+        waiters.wait_for_caching(
+            mock_client, mock_cache_client, 'fake_image_id')
+
+    def test_wait_for_caching_timeout(self):
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        mock_client = mock.Mock(
+            build_interval=1,
+            build_timeout=1
+        )
+        mock_cache_client = mock.Mock()
+        mock_cache_client.list_cache.return_value = {
+            "cached_images": [{
+                "image_id": 'fake_image_id'}]}
+        # Assert that TimeoutException is raised when the image
+        # failed to cache in time
+        self.assertRaises(
+            lib_exc.TimeoutException,
+            waiters.wait_for_caching,
+            mock_client,
+            mock_cache_client,
+            'fake_image_id'
+        )
+
+    def test_wait_for_object_create(self):
+        mock_object_client = mock.Mock(
+            build_interval=1,
+            build_timeout=1
+        )
+        waiters.wait_for_object_create(
+            mock_object_client, 'fake_container', 'fake_object')
+
+    def test_wait_for_object_create_timeout(self):
+        time_mock = self.patch('time.time')
+        time_mock.side_effect = utils.generate_timeout_series(1)
+
+        mock_object_client = mock.Mock(
+            build_interval=1,
+            build_timeout=1
+        )
+        # Assert that TimeoutException is raised when the object is not
+        # created in time
+        self.assertRaises(
+            lib_exc.TimeoutException,
+            waiters.wait_for_object_create,
+            mock_object_client,
+            'fake_container',
+            'fake_object',
+            .1
+        )
+
 
 class TestPortCreationWaiter(base.TestCase):
     def test_wait_for_port_status(self):
diff --git a/tempest/tests/lib/common/test_cred_client.py b/tempest/tests/lib/common/test_cred_client.py
index 7ea660b..e44c5ed 100644
--- a/tempest/tests/lib/common/test_cred_client.py
+++ b/tempest/tests/lib/common/test_cred_client.py
@@ -38,6 +38,13 @@
         self.projects_client.create_tenant.assert_called_once_with(
             name='fake_name', description='desc')
 
+    def test_show_project(self):
+        self.projects_client.show_tenant.return_value = {
+            'tenant': 'a_tenant'
+        }
+        res = self.creds_client.show_project('fake_id')
+        self.assertEqual('a_tenant', res)
+
     def test_delete_project(self):
         self.creds_client.delete_project('fake_id')
         self.projects_client.delete_tenant.assert_called_once_with(
@@ -79,11 +86,32 @@
         self.projects_client.create_project.assert_called_once_with(
             name='fake_name', description='desc', domain_id='fake_domain_id')
 
+    def test_show_project(self):
+        self.projects_client.show_project.return_value = {
+            'project': 'a_tenant'
+        }
+        res = self.creds_client.show_project('fake_id')
+        self.assertEqual('a_tenant', res)
+
     def test_delete_project(self):
         self.creds_client.delete_project('fake_id')
         self.projects_client.delete_project.assert_called_once_with(
             'fake_id')
 
+    def test_create_domain(self):
+        self.domains_client.create_domain.return_value = {
+            'domain': 'a_tenant'
+        }
+        res = self.creds_client.create_domain('fake_name', 'desc')
+        self.assertEqual('a_tenant', res)
+        self.domains_client.create_domain.assert_called_once_with(
+            name='fake_name', description='desc')
+
+    def test_delete_domain(self):
+        self.creds_client.delete_domain('fake_id')
+        self.domains_client.delete_domain.assert_called_once_with(
+            'fake_id')
+
     def test_get_credentials(self):
         ret = self.creds_client.get_credentials(
             {'name': 'some_user', 'id': 'fake_id'},
diff --git a/tox.ini b/tox.ini
index 47ef5eb..fa32ba3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -64,7 +64,7 @@
   coverage combine
   coverage html -d cover
   coverage xml -o cover/coverage.xml
-  coverage report
+  coverage report -m --fail-under=76
 
 [testenv:debug]
 commands = oslo_debug_helper -t tempest/tests {posargs}