# -*- coding: utf-8 -*-

from __future__ import absolute_import, print_function, unicode_literals

import salt.utils.stringutils
import salt.utils.win_reg as win_reg
from salt.exceptions import CommandExecutionError
from salt.ext import six
from tests.support.helpers import destructiveTest, random_string
from tests.support.mock import MagicMock, patch
from tests.support.unit import TestCase, skipIf

try:
    import win32api

    HAS_WIN32 = True
except ImportError:
    HAS_WIN32 = False

UNICODE_KEY = "Unicode Key \N{TRADE MARK SIGN}"
UNICODE_VALUE = (
    "Unicode Value " "\N{COPYRIGHT SIGN},\N{TRADE MARK SIGN},\N{REGISTERED SIGN}"
)
FAKE_KEY = "SOFTWARE\\{0}".format(random_string("SaltTesting-", lowercase=False))


@skipIf(not HAS_WIN32, "Tests require win32 libraries")
class WinFunctionsTestCase(TestCase):
    """
    Test cases for salt.utils.win_reg
    """

    def test_broadcast_change_success(self):
        """
        Tests the broadcast_change function
        """
        with patch("win32gui.SendMessageTimeout", return_value=("", 0)):
            self.assertTrue(win_reg.broadcast_change())

    def test_broadcast_change_fail(self):
        """
        Tests the broadcast_change function failure
        """
        with patch("win32gui.SendMessageTimeout", return_value=("", 1)):
            self.assertFalse(win_reg.broadcast_change())

    def test_key_exists_existing(self):
        """
        Tests the key_exists function using a well known registry key
        """
        self.assertTrue(win_reg.key_exists(hive="HKLM", key="SOFTWARE\\Microsoft"))

    def test_key_exists_non_existing(self):
        """
        Tests the key_exists function using a non existing registry key
        """
        self.assertFalse(win_reg.key_exists(hive="HKLM", key=FAKE_KEY))

    def test_key_exists_invalid_hive(self):
        """
        Tests the key_exists function using an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.key_exists,
            hive="BADHIVE",
            key="SOFTWARE\\Microsoft",
        )

    def test_key_exists_unknown_key_error(self):
        """
        Tests the key_exists function with an unknown key error
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertRaises(
                win32api.error,
                win_reg.key_exists,
                hive="HKLM",
                key="SOFTWARE\\Microsoft",
            )

    def test_value_exists_existing(self):
        """
        Tests the value_exists function using a well known registry key
        """
        self.assertTrue(
            win_reg.value_exists(
                hive="HKLM",
                key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                vname="CommonFilesDir",
            )
        )

    def test_value_exists_non_existing(self):
        """
        Tests the value_exists function using a non existing registry key
        """
        self.assertFalse(
            win_reg.value_exists(
                hive="HKLM",
                key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                vname="NonExistingValueName",
            )
        )

    def test_value_exists_invalid_hive(self):
        """
        Tests the value_exists function using an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.value_exists,
            hive="BADHIVE",
            key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
            vname="CommonFilesDir",
        )

    def test_value_exists_key_not_exist(self):
        """
        Tests the value_exists function when the key does not exist
        """
        mock_error = MagicMock(
            side_effect=win32api.error(2, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertFalse(
                win_reg.value_exists(
                    hive="HKLM",
                    key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                    vname="CommonFilesDir",
                )
            )

    def test_value_exists_unknown_key_error(self):
        """
        Tests the value_exists function with an unknown error when opening the
        key
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertRaises(
                win32api.error,
                win_reg.value_exists,
                hive="HKLM",
                key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                vname="CommonFilesDir",
            )

    def test_value_exists_empty_default_value(self):
        """
        Tests the value_exists function when querying the default value
        """
        mock_error = MagicMock(
            side_effect=win32api.error(2, "RegQueryValueEx", "Empty Value")
        )
        with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error):
            self.assertTrue(
                win_reg.value_exists(
                    hive="HKLM",
                    key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                    vname=None,
                )
            )

    def test_value_exists_no_vname(self):
        """
        Tests the value_exists function when the vname does not exist
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegQueryValueEx", "Empty Value")
        )
        with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error):
            self.assertFalse(
                win_reg.value_exists(
                    hive="HKLM",
                    key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                    vname="NonExistingValuePair",
                )
            )

    def test_list_keys_existing(self):
        """
        Test the list_keys function using a well known registry key
        """
        self.assertIn("Microsoft", win_reg.list_keys(hive="HKLM", key="SOFTWARE"))

    def test_list_keys_non_existing(self):
        """
        Test the list_keys function using a non existing registry key
        """
        expected = (False, "Cannot find key: HKLM\\{0}".format(FAKE_KEY))
        self.assertEqual(win_reg.list_keys(hive="HKLM", key=FAKE_KEY), expected)

    def test_list_keys_invalid_hive(self):
        """
        Test the list_keys function when passing an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.list_keys,
            hive="BADHIVE",
            key="SOFTWARE\\Microsoft",
        )

    def test_list_keys_unknown_key_error(self):
        """
        Tests the list_keys function with an unknown key error
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertRaises(
                win32api.error,
                win_reg.list_keys,
                hive="HKLM",
                key="SOFTWARE\\Microsoft",
            )

    def test_list_values_existing(self):
        """
        Test the list_values function using a well known registry key
        """
        values = win_reg.list_values(
            hive="HKLM", key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion"
        )
        keys = []
        for value in values:
            keys.append(value["vname"])
        self.assertIn("ProgramFilesDir", keys)

    def test_list_values_non_existing(self):
        """
        Test the list_values function using a non existing registry key
        """
        expected = (False, "Cannot find key: HKLM\\{0}".format(FAKE_KEY))
        self.assertEqual(win_reg.list_values(hive="HKLM", key=FAKE_KEY), expected)

    def test_list_values_invalid_hive(self):
        """
        Test the list_values function when passing an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.list_values,
            hive="BADHIVE",
            key="SOFTWARE\\Microsoft",
        )

    def test_list_values_unknown_key_error(self):
        """
        Tests the list_values function with an unknown key error
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertRaises(
                win32api.error,
                win_reg.list_values,
                hive="HKLM",
                key="SOFTWARE\\Microsoft",
            )

    def test_read_value_existing(self):
        """
        Test the read_value function using a well known registry value
        """
        ret = win_reg.read_value(
            hive="HKLM",
            key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
            vname="ProgramFilesPath",
        )
        self.assertEqual(ret["vdata"], "%ProgramFiles%")

    def test_read_value_default(self):
        """
        Test the read_value function reading the default value using a well
        known registry key
        """
        ret = win_reg.read_value(
            hive="HKLM", key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion"
        )
        self.assertEqual(ret["vdata"], "(value not set)")

    def test_read_value_non_existing(self):
        """
        Test the read_value function using a non existing value pair
        """
        expected = {
            "comment": "Cannot find fake_name in HKLM\\SOFTWARE\\Microsoft\\"
            "Windows\\CurrentVersion",
            "vdata": None,
            "vname": "fake_name",
            "success": False,
            "hive": "HKLM",
            "key": "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
        }
        self.assertDictEqual(
            win_reg.read_value(
                hive="HKLM",
                key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                vname="fake_name",
            ),
            expected,
        )

    def test_read_value_non_existing_key(self):
        """
        Test the read_value function using a non existing registry key
        """
        expected = {
            "comment": "Cannot find key: HKLM\\{0}".format(FAKE_KEY),
            "vdata": None,
            "vname": "fake_name",
            "success": False,
            "hive": "HKLM",
            "key": FAKE_KEY,
        }
        self.assertDictEqual(
            win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_name"), expected
        )

    def test_read_value_invalid_hive(self):
        """
        Test the read_value function when passing an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.read_value,
            hive="BADHIVE",
            key="SOFTWARE\\Microsoft",
            vname="ProgramFilesPath",
        )

    def test_read_value_unknown_key_error(self):
        """
        Tests the read_value function with an unknown key error
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertRaises(
                win32api.error,
                win_reg.read_value,
                hive="HKLM",
                key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                vname="ProgramFilesPath",
            )

    def test_read_value_unknown_value_error(self):
        """
        Tests the read_value function with an unknown value error
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegQueryValueEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegQueryValueEx", mock_error):
            self.assertRaises(
                win32api.error,
                win_reg.read_value,
                hive="HKLM",
                key="SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
                vname="ProgramFilesPath",
            )

    @destructiveTest
    def test_read_value_multi_sz_empty_list(self):
        """
        An empty REG_MULTI_SZ value should return an empty list, not None
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM",
                    key=FAKE_KEY,
                    vname="empty_list",
                    vdata=[],
                    vtype="REG_MULTI_SZ",
                )
            )
            expected = {
                "hive": "HKLM",
                "key": FAKE_KEY,
                "success": True,
                "vdata": [],
                "vname": "empty_list",
                "vtype": "REG_MULTI_SZ",
            }
            self.assertEqual(
                win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="empty_list",),
                expected,
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_set_value(self):
        """
        Test the set_value function
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data"
                )
            )
            expected = {
                "hive": "HKLM",
                "key": FAKE_KEY,
                "success": True,
                "vdata": "fake_data",
                "vname": "fake_name",
                "vtype": "REG_SZ",
            }
            self.assertEqual(
                win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_name"),
                expected,
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_set_value_default(self):
        """
        Test the set_value function on the default value
        """
        try:
            self.assertTrue(
                win_reg.set_value(hive="HKLM", key=FAKE_KEY, vdata="fake_default_data")
            )
            expected = {
                "hive": "HKLM",
                "key": FAKE_KEY,
                "success": True,
                "vdata": "fake_default_data",
                "vname": "(Default)",
                "vtype": "REG_SZ",
            }
            self.assertEqual(win_reg.read_value(hive="HKLM", key=FAKE_KEY), expected)
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_set_value_unicode_key(self):
        """
        Test the set_value function on a unicode key
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM",
                    key="\\".join([FAKE_KEY, UNICODE_KEY]),
                    vname="fake_name",
                    vdata="fake_value",
                )
            )
            expected = {
                "hive": "HKLM",
                "key": "\\".join([FAKE_KEY, UNICODE_KEY]),
                "success": True,
                "vdata": "fake_value",
                "vname": "fake_name",
                "vtype": "REG_SZ",
            }
            self.assertEqual(
                win_reg.read_value(
                    hive="HKLM",
                    key="\\".join([FAKE_KEY, UNICODE_KEY]),
                    vname="fake_name",
                ),
                expected,
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_set_value_unicode_value(self):
        """
        Test the set_value function on a unicode value
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_unicode", vdata=UNICODE_VALUE
                )
            )
            expected = {
                "hive": "HKLM",
                "key": FAKE_KEY,
                "success": True,
                "vdata": UNICODE_VALUE,
                "vname": "fake_unicode",
                "vtype": "REG_SZ",
            }
            self.assertEqual(
                win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="fake_unicode"),
                expected,
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_set_value_reg_dword(self):
        """
        Test the set_value function on a REG_DWORD value
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM",
                    key=FAKE_KEY,
                    vname="dword_value",
                    vdata=123,
                    vtype="REG_DWORD",
                )
            )
            expected = {
                "hive": "HKLM",
                "key": FAKE_KEY,
                "success": True,
                "vdata": 123,
                "vname": "dword_value",
                "vtype": "REG_DWORD",
            }
            self.assertEqual(
                win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="dword_value"),
                expected,
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_set_value_reg_qword(self):
        """
        Test the set_value function on a REG_QWORD value
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM",
                    key=FAKE_KEY,
                    vname="qword_value",
                    vdata=123,
                    vtype="REG_QWORD",
                )
            )
            expected = {
                "hive": "HKLM",
                "key": FAKE_KEY,
                "success": True,
                "vdata": 123,
                "vname": "qword_value",
                "vtype": "REG_QWORD",
            }
            self.assertEqual(
                win_reg.read_value(hive="HKLM", key=FAKE_KEY, vname="qword_value"),
                expected,
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    def test_set_value_invalid_hive(self):
        """
        Test the set_value function when passing an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.set_value,
            hive="BADHIVE",
            key=FAKE_KEY,
            vname="fake_name",
            vdata="fake_data",
        )

    def test_set_value_open_create_failure(self):
        """
        Test the set_value function when there is a problem opening/creating
        the key
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegCreateKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegCreateKeyEx", mock_error):
            self.assertFalse(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data"
                )
            )

    def test_set_value_type_error(self):
        """
        Test the set_value function when the wrong type of data is passed
        """
        mock_error = MagicMock(side_effect=TypeError("Mocked TypeError"))
        with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error):
            self.assertFalse(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data"
                )
            )

    def test_set_value_system_error(self):
        """
        Test the set_value function when a SystemError occurs while setting the
        value
        """
        mock_error = MagicMock(side_effect=SystemError("Mocked SystemError"))
        with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error):
            self.assertFalse(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data"
                )
            )

    def test_set_value_value_error(self):
        """
        Test the set_value function when a ValueError occurs while setting the
        value
        """
        mock_error = MagicMock(side_effect=ValueError("Mocked ValueError"))
        with patch("salt.utils.win_reg.win32api.RegSetValueEx", mock_error):
            self.assertFalse(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data"
                )
            )

    def test_cast_vdata_reg_binary(self):
        """
        Test the cast_vdata function with REG_BINARY
        Should always return binary data
        """
        vdata = salt.utils.stringutils.to_bytes("test data")
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_BINARY")
        self.assertTrue(isinstance(result, six.binary_type))

        vdata = salt.utils.stringutils.to_str("test data")
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_BINARY")
        self.assertTrue(isinstance(result, six.binary_type))

        vdata = salt.utils.stringutils.to_unicode("test data")
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_BINARY")
        self.assertTrue(isinstance(result, six.binary_type))

    def test_cast_vdata_reg_dword(self):
        """
        Test the cast_vdata function with REG_DWORD
        Should always return integer
        """
        vdata = 1
        expected = 1
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_DWORD")
        self.assertEqual(result, expected)

        vdata = "1"
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_DWORD")
        self.assertEqual(result, expected)

        vdata = "0000001"
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_DWORD")
        self.assertEqual(result, expected)

    def test_cast_vdata_reg_expand_sz(self):
        """
        Test the cast_vdata function with REG_EXPAND_SZ
        Should always return unicode
        """
        vdata = salt.utils.stringutils.to_str("test data")
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_EXPAND_SZ")
        self.assertTrue(isinstance(result, six.text_type))

        vdata = salt.utils.stringutils.to_bytes("test data")
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_EXPAND_SZ")
        self.assertTrue(isinstance(result, six.text_type))

    def test_cast_vdata_reg_multi_sz(self):
        """
        Test the cast_vdata function with REG_MULTI_SZ
        Should always return a list of unicode strings
        """
        vdata = [
            salt.utils.stringutils.to_str("test string"),
            salt.utils.stringutils.to_bytes("test bytes"),
        ]
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_MULTI_SZ")
        self.assertTrue(isinstance(result, list))
        for item in result:
            self.assertTrue(isinstance(item, six.text_type))

    def test_cast_vdata_reg_qword(self):
        """
        Test the cast_vdata function with REG_QWORD
        Should always return a long integer
        `int` is `long` is default on Py3
        """
        vdata = 1
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_QWORD")
        if six.PY2:
            # pylint: disable=incompatible-py3-code,undefined-variable
            self.assertTrue(isinstance(result, long))
            # pylint: enable=incompatible-py3-code,undefined-variable
        else:
            self.assertTrue(isinstance(result, int))

        vdata = "1"
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_QWORD")
        if six.PY2:
            # pylint: disable=incompatible-py3-code,undefined-variable
            self.assertTrue(isinstance(result, long))
            # pylint: enable=incompatible-py3-code,undefined-variable
        else:
            self.assertTrue(isinstance(result, int))

    def test_cast_vdata_reg_sz(self):
        """
        Test the cast_vdata function with REG_SZ
        Should always return unicode
        """
        vdata = salt.utils.stringutils.to_str("test data")
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_SZ")
        self.assertTrue(isinstance(result, six.text_type))

        vdata = salt.utils.stringutils.to_bytes("test data")
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_SZ")
        self.assertTrue(isinstance(result, six.text_type))

        vdata = None
        result = win_reg.cast_vdata(vdata=vdata, vtype="REG_SZ")
        self.assertTrue(isinstance(result, six.text_type))
        self.assertEqual(result, "")

    @destructiveTest
    def test_delete_value(self):
        """
        Test the delete_value function
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_data"
                )
            )
            self.assertTrue(
                win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_name")
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    def test_delete_value_non_existing(self):
        """
        Test the delete_value function on non existing value
        """
        mock_error = MagicMock(
            side_effect=win32api.error(2, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertIsNone(
                win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_name")
            )

    def test_delete_value_invalid_hive(self):
        """
        Test the delete_value function when passing an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.delete_value,
            hive="BADHIVE",
            key=FAKE_KEY,
            vname="fake_name",
        )

    def test_delete_value_unknown_error(self):
        """
        Test the delete_value function when there is a problem opening the key
        """
        mock_error = MagicMock(
            side_effect=win32api.error(123, "RegOpenKeyEx", "Unknown error")
        )
        with patch("salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error):
            self.assertRaises(
                win32api.error,
                win_reg.delete_value,
                hive="HKLM",
                key=FAKE_KEY,
                vname="fake_name",
            )

    @destructiveTest
    def test_delete_value_unicode(self):
        """
        Test the delete_value function on a unicode value
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_unicode", vdata=UNICODE_VALUE
                )
            )
            self.assertTrue(
                win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname="fake_unicode")
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_delete_value_unicode_vname(self):
        """
        Test the delete_value function on a unicode vname
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname=UNICODE_KEY, vdata="junk data"
                )
            )
            self.assertTrue(
                win_reg.delete_value(hive="HKLM", key=FAKE_KEY, vname=UNICODE_KEY)
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_delete_value_unicode_key(self):
        """
        Test the delete_value function on a unicode key
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM",
                    key="\\".join([FAKE_KEY, UNICODE_KEY]),
                    vname="fake_name",
                    vdata="junk data",
                )
            )
            self.assertTrue(
                win_reg.delete_value(
                    hive="HKLM",
                    key="\\".join([FAKE_KEY, UNICODE_KEY]),
                    vname="fake_name",
                )
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    def test_delete_key_recursive_invalid_hive(self):
        """
        Test the delete_key_recursive function when passing an invalid hive
        """
        self.assertRaises(
            CommandExecutionError,
            win_reg.delete_key_recursive,
            hive="BADHIVE",
            key=FAKE_KEY,
        )

    def test_delete_key_recursive_key_not_found(self):
        """
        Test the delete_key_recursive function when the passed key to delete is
        not found.
        """
        self.assertFalse(win_reg.key_exists(hive="HKLM", key=FAKE_KEY))
        self.assertFalse(win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY))

    def test_delete_key_recursive_too_close(self):
        """
        Test the delete_key_recursive function when the passed key to delete is
        too close to root, such as
        """
        mock_true = MagicMock(return_value=True)
        with patch("salt.utils.win_reg.key_exists", mock_true):
            self.assertFalse(win_reg.delete_key_recursive(hive="HKLM", key="FAKE_KEY"))

    @destructiveTest
    def test_delete_key_recursive(self):
        """
        Test the delete_key_recursive function
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value"
                )
            )
            expected = {"Deleted": ["\\".join(["HKLM", FAKE_KEY])], "Failed": []}
            self.assertDictEqual(
                win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_delete_key_recursive_failed_to_open_key(self):
        """
        Test the delete_key_recursive function on failure to open the key
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value"
                )
            )
            expected = {
                "Deleted": [],
                "Failed": ["\\".join(["HKLM", FAKE_KEY]) + " Failed to connect to key"],
            }
            mock_true = MagicMock(return_value=True)
            mock_error = MagicMock(
                side_effect=[
                    1,
                    win32api.error(3, "RegOpenKeyEx", "Failed to connect to key"),
                ]
            )
            with patch("salt.utils.win_reg.key_exists", mock_true), patch(
                "salt.utils.win_reg.win32api.RegOpenKeyEx", mock_error
            ):
                self.assertDictEqual(
                    win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected
                )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_delete_key_recursive_failed_to_delete(self):
        """
        Test the delete_key_recursive function on failure to delete a key
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM", key=FAKE_KEY, vname="fake_name", vdata="fake_value"
                )
            )
            expected = {
                "Deleted": [],
                "Failed": ["\\".join(["HKLM", FAKE_KEY]) + " Unknown error"],
            }
            # pylint: disable=undefined-variable
            mock_error = MagicMock(side_effect=WindowsError("Unknown error"))
            # pylint: enable=undefined-variable
            with patch("salt.utils.win_reg.win32api.RegDeleteKey", mock_error):
                self.assertDictEqual(
                    win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY), expected
                )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    @destructiveTest
    def test_delete_key_recursive_unicode(self):
        """
        Test the delete_key_recursive function on value within a unicode key
        """
        try:
            self.assertTrue(
                win_reg.set_value(
                    hive="HKLM",
                    key="\\".join([FAKE_KEY, UNICODE_KEY]),
                    vname="fake_name",
                    vdata="fake_value",
                )
            )
            expected = {
                "Deleted": ["\\".join(["HKLM", FAKE_KEY, UNICODE_KEY])],
                "Failed": [],
            }
            self.assertDictEqual(
                win_reg.delete_key_recursive(
                    hive="HKLM", key="\\".join([FAKE_KEY, UNICODE_KEY])
                ),
                expected,
            )
        finally:
            win_reg.delete_key_recursive(hive="HKLM", key=FAKE_KEY)

    def test__to_unicode_int(self):
        """
        Test the ``_to_unicode`` function when it receives an integer value.
        Should return a unicode value, which is unicode in PY2 and str in PY3.
        """
        if six.PY3:
            self.assertTrue(isinstance(win_reg._to_unicode(1), str))
        else:
            # fmt: off
            self.assertTrue(
                isinstance(
                    win_reg._to_unicode(1),
                    unicode,  # pylint: disable=incompatible-py3-code,undefined-variable
                )
            )
            # fmt: on
