diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:48:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:48:59 +0000 |
commit | d835b2cae8abc71958b69362162e6a70c3d7ef63 (patch) | |
tree | 81052e3d2ce3e1bcda085f73d925e9d6257dec15 /test/unittests/test_lock.py | |
parent | Initial commit. (diff) | |
download | crmsh-d835b2cae8abc71958b69362162e6a70c3d7ef63.tar.xz crmsh-d835b2cae8abc71958b69362162e6a70c3d7ef63.zip |
Adding upstream version 4.6.0.upstream/4.6.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/unittests/test_lock.py')
-rw-r--r-- | test/unittests/test_lock.py | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/test/unittests/test_lock.py b/test/unittests/test_lock.py new file mode 100644 index 0000000..a8dc982 --- /dev/null +++ b/test/unittests/test_lock.py @@ -0,0 +1,271 @@ +""" +Unitary tests for crmsh/lock.py + +:author: xinliang +:organization: SUSE Linux GmbH +:contact: XLiang@suse.de + +:since: 2020-12-18 +""" + +# pylint:disable=C0103,C0111,W0212,W0611 + +import unittest + +try: + from unittest import mock +except ImportError: + import mock + +from crmsh import lock, config + + +class TestLock(unittest.TestCase): + """ + Unitary tests for crmsh.lock.Lock + """ + + @classmethod + def setUpClass(cls): + """ + Global setUp. + """ + + def setUp(self): + """ + Test setUp. + """ + self.local_inst = lock.Lock() + + def tearDown(self): + """ + Test tearDown. + """ + + @classmethod + def tearDownClass(cls): + """ + Global tearDown. + """ + + @mock.patch('crmsh.sh.ShellUtils.get_stdout_stderr') + def test_run(self, mock_run): + mock_run.return_value = (0, "output data", None) + rc, out, err = self.local_inst._run("test_cmd") + mock_run.assert_called_once_with("test_cmd") + + @mock.patch('crmsh.lock.Lock._run') + def test_create_lock_dir_false(self, mock_run): + mock_run.return_value = (1, None, None) + rc = self.local_inst._create_lock_dir() + self.assertEqual(rc, False) + mock_run.assert_called_once_with("mkdir {}".format(lock.Lock.LOCK_DIR_DEFAULT)) + + @mock.patch('crmsh.lock.Lock._run') + def test_create_lock_dir(self, mock_run): + mock_run.return_value = (0, None, None) + rc = self.local_inst._create_lock_dir() + self.assertEqual(rc, True) + mock_run.assert_called_once_with("mkdir {}".format(lock.Lock.LOCK_DIR_DEFAULT)) + + @mock.patch('crmsh.lock.Lock._create_lock_dir') + def test_lock_or_fail(self, mock_create): + mock_create.return_value = False + with self.assertRaises(lock.ClaimLockError) as err: + self.local_inst._lock_or_fail() + self.assertEqual("Failed to claim lock (the lock directory exists at {})".format(lock.Lock.LOCK_DIR_DEFAULT), str(err.exception)) + mock_create.assert_called_once_with() + + @mock.patch('crmsh.lock.Lock._run') + def test_unlock(self, mock_run): + self.local_inst.lock_owner = True + self.local_inst._unlock() + mock_run.assert_called_once_with("rm -rf {}".format(lock.Lock.LOCK_DIR_DEFAULT)) + + @mock.patch('crmsh.lock.Lock._unlock') + @mock.patch('crmsh.lock.Lock._lock_or_fail') + def test_lock_exception(self, mock_lock, mock_unlock): + mock_lock.side_effect = lock.ClaimLockError + + with self.assertRaises(lock.ClaimLockError): + with self.local_inst.lock(): + pass + + mock_lock.assert_called_once_with() + mock_unlock.assert_called_once_with() + + @mock.patch('crmsh.lock.Lock._unlock') + @mock.patch('crmsh.lock.Lock._lock_or_fail') + def test_lock(self, mock_lock, mock_unlock): + with self.local_inst.lock(): + pass + mock_lock.assert_called_once_with() + mock_unlock.assert_called_once_with() + + +class TestRemoteLock(unittest.TestCase): + """ + Unitary tests for crmsh.lock.RemoteLock + """ + + @classmethod + def setUpClass(cls): + """ + Global setUp. + """ + + def setUp(self): + """ + Test setUp. + """ + self.lock_inst = lock.RemoteLock("node1") + self.lock_inst_no_wait = lock.RemoteLock("node1", wait=False) + + def tearDown(self): + """ + Test tearDown. + """ + + @classmethod + def tearDownClass(cls): + """ + Global tearDown. + """ + + @mock.patch('crmsh.sh.ClusterShell.get_rc_stdout_stderr_without_input') + def test_run_ssh_error(self, mock_run): + mock_run.return_value = (255, None, "ssh error") + with self.assertRaises(lock.SSHError) as err: + self.lock_inst._run("cmd") + self.assertEqual("ssh error", str(err.exception)) + mock_run.assert_called_once_with("node1", "cmd") + + @mock.patch('crmsh.sh.ClusterShell.get_rc_stdout_stderr_without_input') + def test_run(self, mock_run): + mock_run.return_value = (0, None, None) + res = self.lock_inst._run("cmd") + self.assertEqual(res, mock_run.return_value) + mock_run.assert_called_once_with("node1", "cmd") + + def test_lock_timeout_error_format(self): + config.core.lock_timeout = "pwd" + with self.assertRaises(ValueError) as err: + self.lock_inst.lock_timeout + self.assertEqual("Invalid format of core.lock_timeout(should be a number)", str(err.exception)) + + def test_lock_timeout_min_error(self): + config.core.lock_timeout = "12" + with self.assertRaises(ValueError) as err: + self.lock_inst.lock_timeout + self.assertEqual("Minimum value of core.lock_timeout should be 120", str(err.exception)) + + def test_lock_timeout(self): + config.core.lock_timeout = "130" + self.assertEqual(self.lock_inst.lock_timeout, 130) + + @mock.patch('crmsh.lock.RemoteLock._run') + def test_get_online_nodelist_error(self, mock_run): + mock_run.return_value = (1, None, "error data") + with self.assertRaises(ValueError) as err: + self.lock_inst._get_online_nodelist() + self.assertEqual("error data", str(err.exception)) + mock_run.assert_called_once_with("crm_node -l") + + @mock.patch('crmsh.lock.RemoteLock._run') + def test_get_online_nodelist(self, mock_run): + output = """ + 1084783297 15sp2-1 member + 1084783193 15sp2-2 lost + 1084783331 15sp2-3 member + """ + mock_run.return_value = (0, output, None) + res = self.lock_inst._get_online_nodelist() + self.assertEqual(res, ["15sp2-1", "15sp2-3"]) + mock_run.assert_called_once_with("crm_node -l") + + @mock.patch('crmsh.lock.Lock._create_lock_dir') + @mock.patch('crmsh.lock.RemoteLock.lock_timeout', new_callable=mock.PropertyMock) + @mock.patch('time.time') + def test_lock_or_wait_break(self, mock_time, mock_time_out, mock_create): + mock_time.return_value = 10000 + mock_time_out.return_value = 120 + mock_create.return_value = True + + self.lock_inst._lock_or_wait() + + mock_time.assert_called_once_with() + mock_time_out.assert_called_once_with() + + @mock.patch('time.sleep') + @mock.patch('logging.Logger.warning') + @mock.patch('crmsh.lock.RemoteLock._get_online_nodelist') + @mock.patch('crmsh.lock.Lock._create_lock_dir') + @mock.patch('crmsh.lock.RemoteLock.lock_timeout', new_callable=mock.PropertyMock) + @mock.patch('time.time') + def test_lock_or_wait_timed_out(self, mock_time, mock_time_out, mock_create, + mock_get_nodelist, mock_warn, mock_sleep): + mock_time.side_effect = [10000, 10121] + mock_time_out.return_value = 120 + mock_create.return_value = False + mock_get_nodelist.return_value = ["node2"] + + with self.assertRaises(lock.ClaimLockError) as err: + self.lock_inst._lock_or_wait() + self.assertEqual("Timed out after 120 seconds. Cannot continue since the lock directory exists at the node (node1:{})".format(lock.Lock.LOCK_DIR_DEFAULT), str(err.exception)) + + mock_time.assert_has_calls([ mock.call(), mock.call()]) + mock_time_out.assert_has_calls([mock.call(), mock.call(), mock.call()]) + mock_create.assert_called_once_with() + mock_get_nodelist.assert_called_once_with() + mock_warn.assert_called_once_with('Might have unfinished process on other nodes, wait %ss...', 120) + mock_sleep.assert_called_once_with(10) + + @mock.patch('time.sleep') + @mock.patch('logging.Logger.warning') + @mock.patch('crmsh.lock.RemoteLock._get_online_nodelist') + @mock.patch('crmsh.lock.Lock._create_lock_dir') + @mock.patch('crmsh.lock.RemoteLock.lock_timeout', new_callable=mock.PropertyMock) + @mock.patch('time.time') + def test_lock_or_wait_again(self, mock_time, mock_time_out, mock_create, + mock_get_nodelist, mock_warn, mock_sleep): + mock_time.side_effect = [10000, 10010, 10020] + mock_time_out.side_effect = [120, 120, 120] + mock_create.side_effect = [False, False, True] + mock_get_nodelist.side_effect = [["node1"], ["node1", "node2"]] + + self.lock_inst._lock_or_wait() + + mock_time.assert_has_calls([mock.call(), mock.call(), mock.call()]) + mock_time_out.assert_has_calls([mock.call(), mock.call(), mock.call()]) + mock_create.assert_has_calls([mock.call(), mock.call(), mock.call()]) + mock_get_nodelist.assert_has_calls([mock.call(), mock.call()]) + mock_warn.assert_called_once_with('Might have unfinished process on other nodes, wait %ss...', 120) + mock_sleep.assert_has_calls([mock.call(10), mock.call(10)]) + + @mock.patch('crmsh.lock.Lock._unlock') + @mock.patch('crmsh.lock.RemoteLock._lock_or_wait') + def test_lock_exception(self, mock_lock, mock_unlock): + mock_lock.side_effect = lock.ClaimLockError + + with self.assertRaises(lock.ClaimLockError): + with self.lock_inst.lock(): + pass + + mock_lock.assert_called_once_with() + mock_unlock.assert_called_once_with() + + @mock.patch('crmsh.lock.Lock._unlock') + @mock.patch('crmsh.lock.RemoteLock._lock_or_wait') + def test_lock(self, mock_lock, mock_unlock): + with self.lock_inst.lock(): + pass + mock_lock.assert_called_once_with() + mock_unlock.assert_called_once_with() + + @mock.patch('crmsh.lock.Lock._unlock') + @mock.patch('crmsh.lock.RemoteLock._lock_or_fail') + def test_lock_no_wait(self, mock_lock, mock_unlock): + with self.lock_inst_no_wait.lock(): + pass + mock_lock.assert_called_once_with() + mock_unlock.assert_called_once_with() |