Until recently, os-win did not have any functional tests. As we're now starting to add such tests, we need to clearly separate unit tests. Note that most OpenStack projects have already done this separation. This change moves the unit tests to a separate folder. Change-Id: I1649fae7095f9b4024e526358ac70a58189365bf
270 lines
9.9 KiB
Python
270 lines
9.9 KiB
Python
# Copyright 2016 Cloudbase Solutions Srl
|
|
# 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.
|
|
|
|
import ctypes
|
|
|
|
import ddt
|
|
import mock
|
|
|
|
from os_win import constants
|
|
from os_win import exceptions
|
|
from os_win.tests.unit import test_base
|
|
from os_win.utils.compute import _clusapi_utils
|
|
|
|
|
|
@ddt.ddt
|
|
class ClusApiUtilsTestCase(test_base.OsWinBaseTestCase):
|
|
def setUp(self):
|
|
super(ClusApiUtilsTestCase, self).setUp()
|
|
|
|
self._clusapi = mock.patch.object(
|
|
_clusapi_utils, 'clusapi', create=True).start()
|
|
|
|
self._clusapi_utils = _clusapi_utils.ClusApiUtils()
|
|
|
|
self._run_patcher = mock.patch.object(self._clusapi_utils,
|
|
'_run_and_check_output')
|
|
self._mock_run = self._run_patcher.start()
|
|
|
|
def _mock_ctypes(self):
|
|
self._ctypes = mock.Mock()
|
|
# This is used in order to easily make assertions on the variables
|
|
# passed by reference.
|
|
self._ctypes.byref = lambda x: (x, "byref")
|
|
self._ctypes.c_wchar_p = lambda x: (x, 'c_wchar_p')
|
|
self._ctypes.sizeof = lambda x: (x, 'sizeof')
|
|
|
|
mock.patch.object(_clusapi_utils, 'ctypes', self._ctypes).start()
|
|
|
|
def test_run_and_check_output(self):
|
|
self._clusapi_utils._win32utils = mock.Mock()
|
|
self._clusapi_utils._run_and_check_output = (
|
|
self._run_patcher.temp_original)
|
|
|
|
mock_win32utils_run_and_check_output = (
|
|
self._clusapi_utils._win32utils.run_and_check_output)
|
|
|
|
ret_val = self._clusapi_utils._run_and_check_output(
|
|
mock.sentinel.func,
|
|
mock.sentinel.arg,
|
|
fake_kwarg=mock.sentinel.kwarg)
|
|
|
|
mock_win32utils_run_and_check_output.assert_called_once_with(
|
|
mock.sentinel.func,
|
|
mock.sentinel.arg,
|
|
fake_kwarg=mock.sentinel.kwarg,
|
|
failure_exc=exceptions.ClusterWin32Exception)
|
|
self.assertEqual(mock_win32utils_run_and_check_output.return_value,
|
|
ret_val)
|
|
|
|
def test_dword_align(self):
|
|
self.assertEqual(8, self._clusapi_utils._dword_align(5))
|
|
self.assertEqual(4, self._clusapi_utils._dword_align(4))
|
|
|
|
def test_get_clusprop_value_struct(self):
|
|
val_type = ctypes.c_ubyte * 3
|
|
expected_padding_sz = 1
|
|
|
|
clusprop_val_struct = self._clusapi_utils._get_clusprop_value_struct(
|
|
val_type)
|
|
|
|
expected_fields = [('syntax', _clusapi_utils.DWORD),
|
|
('length', _clusapi_utils.DWORD),
|
|
('value', val_type),
|
|
('_padding', ctypes.c_ubyte * expected_padding_sz)]
|
|
self.assertEqual(expected_fields, clusprop_val_struct._fields_)
|
|
|
|
def test_get_property_list_entry(self):
|
|
fake_prop_name = 'fake prop name'
|
|
fake_prop_syntax = 1
|
|
fake_prop_val = (ctypes.c_wchar * 10)()
|
|
fake_prop_val.value = 'fake prop'
|
|
|
|
entry = self._clusapi_utils.get_property_list_entry(
|
|
name=fake_prop_name,
|
|
syntax=fake_prop_syntax,
|
|
value=fake_prop_val)
|
|
|
|
self.assertEqual(_clusapi_utils.CLUSPROP_SYNTAX_NAME,
|
|
entry.name.syntax)
|
|
self.assertEqual(fake_prop_name,
|
|
entry.name.value)
|
|
self.assertEqual(
|
|
ctypes.sizeof(ctypes.c_wchar) * (len(fake_prop_name) + 1),
|
|
entry.name.length)
|
|
|
|
self.assertEqual(fake_prop_syntax,
|
|
entry.value.syntax)
|
|
self.assertEqual(bytearray(fake_prop_val),
|
|
bytearray(entry.value.value))
|
|
self.assertEqual(
|
|
ctypes.sizeof(fake_prop_val),
|
|
entry.value.length)
|
|
|
|
self.assertEqual(_clusapi_utils.CLUSPROP_SYNTAX_ENDMARK,
|
|
entry._endmark)
|
|
|
|
def test_get_property_list(self):
|
|
entry_0 = self._clusapi_utils.get_property_list_entry(
|
|
name='fake prop name',
|
|
syntax=1,
|
|
value=ctypes.c_uint(2))
|
|
entry_1 = self._clusapi_utils.get_property_list_entry(
|
|
name='fake prop name',
|
|
syntax=2,
|
|
value=ctypes.c_ubyte(5))
|
|
|
|
prop_list = self._clusapi_utils.get_property_list(
|
|
[entry_0, entry_1])
|
|
|
|
self.assertEqual(2, prop_list.count)
|
|
self.assertEqual(bytearray(entry_0) + bytearray(entry_1),
|
|
prop_list.entries_buff)
|
|
|
|
@ddt.data('fake cluster name', None)
|
|
def test_open_cluster(self, cluster_name):
|
|
self._mock_ctypes()
|
|
|
|
handle = self._clusapi_utils.open_cluster(cluster_name)
|
|
|
|
expected_handle_arg = (
|
|
self._ctypes.c_wchar_p(cluster_name)
|
|
if cluster_name else None)
|
|
self._mock_run.assert_called_once_with(
|
|
self._clusapi.OpenCluster,
|
|
expected_handle_arg,
|
|
**self._clusapi_utils._open_handle_check_flags)
|
|
|
|
self.assertEqual(self._mock_run.return_value, handle)
|
|
|
|
def test_open_cluster_group(self):
|
|
self._mock_ctypes()
|
|
|
|
handle = self._clusapi_utils.open_cluster_group(
|
|
mock.sentinel.cluster_handle,
|
|
mock.sentinel.group_name)
|
|
|
|
self._mock_run.assert_called_once_with(
|
|
self._clusapi.OpenClusterGroup,
|
|
mock.sentinel.cluster_handle,
|
|
self._ctypes.c_wchar_p(mock.sentinel.group_name),
|
|
**self._clusapi_utils._open_handle_check_flags)
|
|
|
|
self.assertEqual(self._mock_run.return_value, handle)
|
|
|
|
def test_open_cluster_node(self):
|
|
self._mock_ctypes()
|
|
|
|
handle = self._clusapi_utils.open_cluster_node(
|
|
mock.sentinel.cluster_handle,
|
|
mock.sentinel.node_name)
|
|
|
|
self._mock_run.assert_called_once_with(
|
|
self._clusapi.OpenClusterNode,
|
|
mock.sentinel.cluster_handle,
|
|
self._ctypes.c_wchar_p(mock.sentinel.node_name),
|
|
**self._clusapi_utils._open_handle_check_flags)
|
|
|
|
self.assertEqual(self._mock_run.return_value, handle)
|
|
|
|
def test_close_cluster(self):
|
|
self._clusapi_utils.close_cluster(mock.sentinel.handle)
|
|
self._clusapi.CloseCluster.assert_called_once_with(
|
|
mock.sentinel.handle)
|
|
|
|
def test_close_cluster_group(self):
|
|
self._clusapi_utils.close_cluster_group(mock.sentinel.handle)
|
|
self._clusapi.CloseClusterGroup.assert_called_once_with(
|
|
mock.sentinel.handle)
|
|
|
|
def test_close_cluster_node(self):
|
|
self._clusapi_utils.close_cluster_node(mock.sentinel.handle)
|
|
self._clusapi.CloseClusterNode.assert_called_once_with(
|
|
mock.sentinel.handle)
|
|
|
|
def test_cancel_cluster_group_operation(self):
|
|
self._clusapi_utils.cancel_cluster_group_operation(
|
|
mock.sentinel.group_handle)
|
|
|
|
self._mock_run.assert_called_once_with(
|
|
self._clusapi.CancelClusterGroupOperation,
|
|
mock.sentinel.group_handle,
|
|
0,
|
|
ignored_error_codes=[_clusapi_utils.ERROR_IO_PENDING])
|
|
|
|
@ddt.data(mock.sentinel.prop_list, None)
|
|
def test_move_cluster_group(self, prop_list):
|
|
self._mock_ctypes()
|
|
|
|
expected_prop_list_arg = (
|
|
self._ctypes.byref(prop_list) if prop_list else None)
|
|
expected_prop_list_sz = (
|
|
self._ctypes.sizeof(prop_list) if prop_list else 0)
|
|
|
|
self._clusapi_utils.move_cluster_group(
|
|
mock.sentinel.group_handle,
|
|
mock.sentinel.dest_node_handle,
|
|
mock.sentinel.move_flags,
|
|
prop_list)
|
|
|
|
self._mock_run.assert_called_once_with(
|
|
self._clusapi.MoveClusterGroupEx,
|
|
mock.sentinel.group_handle,
|
|
mock.sentinel.dest_node_handle,
|
|
mock.sentinel.move_flags,
|
|
expected_prop_list_arg,
|
|
expected_prop_list_sz,
|
|
ignored_error_codes=[_clusapi_utils.ERROR_IO_PENDING])
|
|
|
|
def test_get_cluster_group_state(self):
|
|
owner_node = 'fake owner node'
|
|
|
|
def fake_get_state(inst,
|
|
group_handle, node_name_buff, node_name_len,
|
|
error_ret_vals, error_on_nonzero_ret_val,
|
|
ret_val_is_err_code):
|
|
self.assertEqual(mock.sentinel.group_handle, group_handle)
|
|
|
|
# Those arguments would not normally get to the ClusApi
|
|
# function, instead being used by the helper invoking
|
|
# it and catching errors. For convenience, we validate
|
|
# those arguments at this point.
|
|
self.assertEqual([constants.CLUSTER_GROUP_STATE_UNKNOWN],
|
|
error_ret_vals)
|
|
self.assertFalse(error_on_nonzero_ret_val)
|
|
self.assertFalse(ret_val_is_err_code)
|
|
|
|
node_name_len_arg = ctypes.cast(
|
|
node_name_len,
|
|
ctypes.POINTER(_clusapi_utils.DWORD)).contents
|
|
self.assertEqual(self._clusapi_utils._MAX_NODE_NAME,
|
|
node_name_len_arg.value)
|
|
|
|
node_name_arg = ctypes.cast(
|
|
node_name_buff,
|
|
ctypes.POINTER(
|
|
ctypes.c_wchar *
|
|
self._clusapi_utils._MAX_NODE_NAME)).contents
|
|
node_name_arg.value = owner_node
|
|
return mock.sentinel.group_state
|
|
|
|
self._mock_run.side_effect = fake_get_state
|
|
|
|
state_info = self._clusapi_utils.get_cluster_group_state(
|
|
mock.sentinel.group_handle)
|
|
expected_state_info = dict(state=mock.sentinel.group_state,
|
|
owner_node=owner_node)
|
|
self.assertDictEqual(expected_state_info, state_info)
|