test the windows specific win32reg module only win32reg functions not hit here flushkey loadkey and savekey do this first so test will be skipped if module doesn t exist now import everything tuple of major minor some tests should only run on 64bit architectures where wow64 will be starting with windows 7 and windows server 2008 r2 wow64 no longer uses registry reflection and formerly reflected keys are shared instead windows 7 and windows server 2008 r2 are version 6 1 due to this some tests are only valid up until 6 1 use a perprocess key to prevent concurrent test runs buildbot from stomping on each other on os es that support reflection we should test with a reflected key two and three kanjis meaning japan and japanese make sure that the test key is absent when the test starts subkey does not exist no more subkeys set the default value for this key create a subkey give the subkey some named values check we wrote as many items as we thought close this key this way but before we do copy the key as an integer this allows us to test that the key really gets closed and close that key that way check we can get default value for this key read the subkeys check i can enumerate over the values check i can directly access each item enumerate our main key it is not necessary to delete the values before deleting the key although subkeys must not exist we delete them manually just to prove we can shouldn t be able to delete it twice opening should now fail substitute the regular createkey and openkey calls with their extended counterparts note deletekeyex is not used here because it is platform dependent use the regular deletekey to clean up deletekeyex takes named args and is tested separately perform minimal connectregistry test which just invokes it ensure that the handle is closed if an exception occurs issue2810 a race condition in 2 6 and 3 1 may cause enumvalue or queryvalue to raise windowserror more data is available issue2810 in 2 6 and 3 1 when the key name was exactly 256 characters enumkey raised windowserror more data is available issue2810 when the value is dynamically generated these raise windowserror more data is available in 2 6 and 3 1 reflection requires xp x64vista at a minimum xp doesn t have this stuff or deletekeyex so make sure their use raises notimplementederror test for issue 14420 accept proper ranges for setvalueex py2reg which gets called by setvalueex was using pylongaslong thus raising overflowerror the implementation now uses pylongasunsignedlong to match dword s size test for issue 43984 check 1 was not set by setvalueex py2reg which gets called by setvalueex wasn t checking return value by pylongasunsignedlong thus setting 1 as value in the registry the implementation now checks pylongasunsignedlong return value to assure the value set was not 1 test for issue 16759 return unsigned int from queryvalueex reg2py which gets called by queryvalueex was returning a value generated by pylongfromlong the implementation now uses pylongfromunsignedlong to match dword s size test for issue 21151 segfault when none is passed to setvalueex test for issue 25778 regsz should not contain null characters clean up and also exercise the named arguments test that we can call the query enable and disable functions on a key which isn t on the reflection list with no consequences hklmsoftware is redirected but not reflected in all oses test that we can create open and delete keys in the 32bit area because we are doing this in a key which gets reflected test the differences of 32 and 64bit keys before and after the reflection occurs ie when the created key is closed the key should now be available in the 32bit area write a value to what currently is only in the 32bit area the key is not reflected until createdkey is closed the 64bit version of the key should not be available yet now explicitly open the 64bit version of the key make sure the original value we set is there set a new value which will get reflected to 32bit reflection uses a lastwriter wins policy so the value we set on the 64bit key should be the same on 32bit make use of a key which gets redirected and reflected queryreflectionkey returns whether or not the key is disabled hkcusoftwareclasses is reflected by default the key is now closed and would normally be reflected to the 64bit area but let s make sure that didn t happen make sure the 32bit key is actually there test the windows specific win32reg module only win32reg functions not hit here flushkey loadkey and savekey do this first so test will be skipped if module doesn t exist now import everything tuple of major minor some tests should only run on 64 bit architectures where wow64 will be starting with windows 7 and windows server 2008 r2 wow64 no longer uses registry reflection and formerly reflected keys are shared instead windows 7 and windows server 2008 r2 are version 6 1 due to this some tests are only valid up until 6 1 use a per process key to prevent concurrent test runs buildbot from stomping on each other on os es that support reflection we should test with a reflected key two and three kanjis meaning japan and japanese make sure that the test key is absent when the test starts subkey does not exist no more subkeys set the default value for this key create a sub key give the sub key some named values check we wrote as many items as we thought close this key this way but before we do copy the key as an integer this allows us to test that the key really gets closed and close that key that way check we can get default value for this key read the sub keys check i can enumerate over the values check i can directly access each item enumerate our main key it is not necessary to delete the values before deleting the key although subkeys must not exist we delete them manually just to prove we can shouldn t be able to delete it twice opening should now fail use this error name this time substitute the regular createkey and openkey calls with their extended counterparts note deletekeyex is not used here because it is platform dependent use the regular deletekey to clean up deletekeyex takes named args and is tested separately perform minimal connectregistry test which just invokes it ensure that the handle is closed if an exception occurs issue2810 a race condition in 2 6 and 3 1 may cause enumvalue or queryvalue to raise windowserror more data is available issue2810 in 2 6 and 3 1 when the key name was exactly 256 characters enumkey raised windowserror more data is available issue2810 when the value is dynamically generated these raise windowserror more data is available in 2 6 and 3 1 reflection requires xp x64 vista at a minimum xp doesn t have this stuff or deletekeyex so make sure their use raises notimplementederror test for issue 14420 accept proper ranges for setvalueex py2reg which gets called by setvalueex was using pylong_aslong thus raising overflowerror the implementation now uses pylong_asunsignedlong to match dword s size test for issue 43984 check 1 was not set by setvalueex py2reg which gets called by setvalueex wasn t checking return value by pylong_asunsignedlong thus setting 1 as value in the registry the implementation now checks pylong_asunsignedlong return value to assure the value set was not 1 test for issue 16759 return unsigned int from queryvalueex reg2py which gets called by queryvalueex was returning a value generated by pylong_fromlong the implementation now uses pylong_fromunsignedlong to match dword s size test for issue 21151 segfault when none is passed to setvalueex test for issue 25778 reg_sz should not contain null characters clean up and also exercise the named arguments test that we can call the query enable and disable functions on a key which isn t on the reflection list with no consequences hklm software is redirected but not reflected in all oses test that we can create open and delete keys in the 32 bit area because we are doing this in a key which gets reflected test the differences of 32 and 64 bit keys before and after the reflection occurs ie when the created key is closed the key should now be available in the 32 bit area write a value to what currently is only in the 32 bit area the key is not reflected until created_key is closed the 64 bit version of the key should not be available yet now explicitly open the 64 bit version of the key make sure the original value we set is there set a new value which will get reflected to 32 bit reflection uses a last writer wins policy so the value we set on the 64 bit key should be the same on 32 bit make use of a key which gets redirected and reflected queryreflectionkey returns whether or not the key is disabled hkcu software classes is reflected by default the key is now closed and would normally be reflected to the 64 bit area but let s make sure that didn t happen make sure the 32 bit key is actually there
import gc
import os, sys, errno
import threading
import unittest
from platform import machine, win32_edition
from test.support import cpython_only, import_helper


import_helper.import_module('winreg', required_on=['win'])

from winreg import *

try:
    REMOTE_NAME = sys.argv[sys.argv.index("--remote")+1]
except (IndexError, ValueError):
    REMOTE_NAME = None


WIN_VER = sys.getwindowsversion()[:2]

WIN64_MACHINE = True if machine() == "AMD64" else False





HAS_REFLECTION = True if WIN_VER < (6, 1) else False



test_key_base = "Python Test Key [%d] - Delete Me" % (os.getpid(),)
test_key_name = "SOFTWARE\\" + test_key_base

test_reflect_key_name = "SOFTWARE\\Classes\\" + test_key_base

test_data = [
    ("Int Value",     45,                                      REG_DWORD),
    ("Qword Value",   0x1122334455667788,                      REG_QWORD),
    ("String Val",    "A string value",                        REG_SZ),
    ("StringExpand",  "The path is %path%",                    REG_EXPAND_SZ),
    ("Multi-string",  ["Lots", "of", "string", "values"],      REG_MULTI_SZ),
    ("Multi-nul",     ["", "", "", ""],                        REG_MULTI_SZ),
    ("Raw Data",      b"binary\x00data",                       REG_BINARY),
    ("Big String",    "x"*(2**14-1),                           REG_SZ),
    ("Big Binary",    b"x"*(2**14),                            REG_BINARY),
    
    ("Japanese 日本", "日本語", REG_SZ),
]


@cpython_only
class HeapTypeTests(unittest.TestCase):
    def test_have_gc(self):
        self.assertTrue(gc.is_tracked(HKEYType))

    def test_immutable(self):
        with self.assertRaisesRegex(TypeError, "immutable"):
            HKEYType.foo = "bar"


class BaseWinregTests(unittest.TestCase):

    def setUp(self):
        
        
        self.delete_tree(HKEY_CURRENT_USER, test_key_name)

    def delete_tree(self, root, subkey):
        try:
            hkey = OpenKey(root, subkey, 0, KEY_ALL_ACCESS)
        except OSError:
            
            return
        while True:
            try:
                subsubkey = EnumKey(hkey, 0)
            except OSError:
                
                break
            self.delete_tree(hkey, subsubkey)
        CloseKey(hkey)
        DeleteKey(root, subkey)

    def _write_test_data(self, root_key, subkeystr="sub_key",
                         CreateKey=CreateKey):
        
        SetValue(root_key, test_key_name, REG_SZ, "Default value")
        key = CreateKey(root_key, test_key_name)
        self.assertTrue(key.handle != 0)
        
        sub_key = CreateKey(key, subkeystr)
        

        for value_name, value_data, value_type in test_data:
            SetValueEx(sub_key, value_name, 0, value_type, value_data)

        
        nkeys, nvalues, since_mod = QueryInfoKey(key)
        self.assertEqual(nkeys, 1, "Not the correct number of sub keys")
        self.assertEqual(nvalues, 1, "Not the correct number of values")
        nkeys, nvalues, since_mod = QueryInfoKey(sub_key)
        self.assertEqual(nkeys, 0, "Not the correct number of sub keys")
        self.assertEqual(nvalues, len(test_data),
                         "Not the correct number of values")
        
        
        
        int_sub_key = int(sub_key)
        CloseKey(sub_key)
        try:
            QueryInfoKey(int_sub_key)
            self.fail("It appears the CloseKey() function does "
                      "not close the actual key!")
        except OSError:
            pass
        
        int_key = int(key)
        key.Close()
        try:
            QueryInfoKey(int_key)
            self.fail("It appears the key.Close() function "
                      "does not close the actual key!")
        except OSError:
            pass
    def _read_test_data(self, root_key, subkeystr="sub_key", OpenKey=OpenKey):
        
        val = QueryValue(root_key, test_key_name)
        self.assertEqual(val, "Default value",
                         "Registry didn't give back the correct value")

        key = OpenKey(root_key, test_key_name)
        
        with OpenKey(key, subkeystr) as sub_key:
            
            index = 0
            while 1:
                try:
                    data = EnumValue(sub_key, index)
                except OSError:
                    break
                self.assertEqual(data in test_data, True,
                                 "Didn't read back the correct test data")
                index = index + 1
            self.assertEqual(index, len(test_data),
                             "Didn't read the correct number of items")
            
            for value_name, value_data, value_type in test_data:
                read_val, read_typ = QueryValueEx(sub_key, value_name)
                self.assertEqual(read_val, value_data,
                                 "Could not directly read the value")
                self.assertEqual(read_typ, value_type,
                                 "Could not directly read the value")
        sub_key.Close()
        
        read_val = EnumKey(key, 0)
        self.assertEqual(read_val, subkeystr, "Read subkey value wrong")
        try:
            EnumKey(key, 1)
            self.fail("Was able to get a second key when I only have one!")
        except OSError:
            pass

        key.Close()

    def _delete_test_data(self, root_key, subkeystr="sub_key"):
        key = OpenKey(root_key, test_key_name, 0, KEY_ALL_ACCESS)
        sub_key = OpenKey(key, subkeystr, 0, KEY_ALL_ACCESS)
        
        
        
        for value_name, value_data, value_type in test_data:
            DeleteValue(sub_key, value_name)

        nkeys, nvalues, since_mod = QueryInfoKey(sub_key)
        self.assertEqual(nkeys, 0, "subkey not empty before delete")
        self.assertEqual(nvalues, 0, "subkey not empty before delete")
        sub_key.Close()
        DeleteKey(key, subkeystr)

        try:
            
            DeleteKey(key, subkeystr)
            self.fail("Deleting the key twice succeeded")
        except OSError:
            pass
        key.Close()
        DeleteKey(root_key, test_key_name)
        
        try:
            key = OpenKey(root_key, test_key_name)
            self.fail("Could open the non-existent key")
        except OSError: 
            pass

    def _test_all(self, root_key, subkeystr="sub_key"):
        self._write_test_data(root_key, subkeystr)
        self._read_test_data(root_key, subkeystr)
        self._delete_test_data(root_key, subkeystr)

    def _test_named_args(self, key, sub_key):
        with CreateKeyEx(key=key, sub_key=sub_key, reserved=0,
                         access=KEY_ALL_ACCESS) as ckey:
            self.assertTrue(ckey.handle != 0)

        with OpenKeyEx(key=key, sub_key=sub_key, reserved=0,
                       access=KEY_ALL_ACCESS) as okey:
            self.assertTrue(okey.handle != 0)


class LocalWinregTests(BaseWinregTests):

    def test_registry_works(self):
        self._test_all(HKEY_CURRENT_USER)
        self._test_all(HKEY_CURRENT_USER, "日本-subkey")

    def test_registry_works_extended_functions(self):
        
        
        
        cke = lambda key, sub_key: CreateKeyEx(key, sub_key, 0, KEY_ALL_ACCESS)
        self._write_test_data(HKEY_CURRENT_USER, CreateKey=cke)

        oke = lambda key, sub_key: OpenKeyEx(key, sub_key, 0, KEY_READ)
        self._read_test_data(HKEY_CURRENT_USER, OpenKey=oke)

        self._delete_test_data(HKEY_CURRENT_USER)

    def test_named_arguments(self):
        self._test_named_args(HKEY_CURRENT_USER, test_key_name)
        
        
        DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_connect_registry_to_local_machine_works(self):
        
        h = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
        self.assertNotEqual(h.handle, 0)
        h.Close()
        self.assertEqual(h.handle, 0)

    def test_nonexistent_remote_registry(self):
        connect = lambda: ConnectRegistry("abcdefghijkl", HKEY_CURRENT_USER)
        self.assertRaises(OSError, connect)

    def testExpandEnvironmentStrings(self):
        r = ExpandEnvironmentStrings("%windir%\\test")
        self.assertEqual(type(r), str)
        self.assertEqual(r, os.environ["windir"] + "\\test")

    def test_context_manager(self):
        
        try:
            with ConnectRegistry(None, HKEY_LOCAL_MACHINE) as h:
                self.assertNotEqual(h.handle, 0)
                raise OSError
        except OSError:
            self.assertEqual(h.handle, 0)

    def test_changing_value(self):
        
        
        
        done = False

        class VeryActiveThread(threading.Thread):
            def run(self):
                with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
                    use_short = True
                    long_string = 'x'*2000
                    while not done:
                        s = 'x' if use_short else long_string
                        use_short = not use_short
                        SetValue(key, 'changing_value', REG_SZ, s)

        thread = VeryActiveThread()
        thread.start()
        try:
            with CreateKey(HKEY_CURRENT_USER,
                           test_key_name+'\\changing_value') as key:
                for _ in range(1000):
                    num_subkeys, num_values, t = QueryInfoKey(key)
                    for i in range(num_values):
                        name = EnumValue(key, i)
                        QueryValue(key, name[0])
        finally:
            done = True
            thread.join()
            DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value')
            DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_long_key(self):
        
        
        
        name = 'x'*256
        try:
            with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
                SetValue(key, name, REG_SZ, 'x')
                num_subkeys, num_values, t = QueryInfoKey(key)
                EnumKey(key, 0)
        finally:
            DeleteKey(HKEY_CURRENT_USER, '\\'.join((test_key_name, name)))
            DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_dynamic_key(self):
        
        
        try:
            EnumValue(HKEY_PERFORMANCE_DATA, 0)
        except OSError as e:
            if e.errno in (errno.EPERM, errno.EACCES):
                self.skipTest("access denied to registry key "
                              "(are you running in a non-interactive session?)")
            raise
        QueryValueEx(HKEY_PERFORMANCE_DATA, "")

    
    
    @unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP")
    def test_reflection_unsupported(self):
        try:
            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
                self.assertNotEqual(ck.handle, 0)

            key = OpenKey(HKEY_CURRENT_USER, test_key_name)
            self.assertNotEqual(key.handle, 0)

            with self.assertRaises(NotImplementedError):
                DisableReflectionKey(key)
            with self.assertRaises(NotImplementedError):
                EnableReflectionKey(key)
            with self.assertRaises(NotImplementedError):
                QueryReflectionKey(key)
            with self.assertRaises(NotImplementedError):
                DeleteKeyEx(HKEY_CURRENT_USER, test_key_name)
        finally:
            DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_setvalueex_value_range(self):
        
        
        
        
        try:
            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
                self.assertNotEqual(ck.handle, 0)
                SetValueEx(ck, "test_name", None, REG_DWORD, 0x80000000)
        finally:
            DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_setvalueex_negative_one_check(self):
        
        
        
        
        
        try:
            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
                with self.assertRaises(OverflowError):
                    SetValueEx(ck, "test_name_dword", None, REG_DWORD, -1)
                    SetValueEx(ck, "test_name_qword", None, REG_QWORD, -1)
                self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_dword")
                self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_qword")

        finally:
            DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_queryvalueex_return_value(self):
        
        
        
        
        try:
            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
                self.assertNotEqual(ck.handle, 0)
                test_val = 0x80000000
                SetValueEx(ck, "test_name", None, REG_DWORD, test_val)
                ret_val, ret_type = QueryValueEx(ck, "test_name")
                self.assertEqual(ret_type, REG_DWORD)
                self.assertEqual(ret_val, test_val)
        finally:
            DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_setvalueex_crash_with_none_arg(self):
        
        try:
            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
                self.assertNotEqual(ck.handle, 0)
                test_val = None
                SetValueEx(ck, "test_name", 0, REG_BINARY, test_val)
                ret_val, ret_type = QueryValueEx(ck, "test_name")
                self.assertEqual(ret_type, REG_BINARY)
                self.assertEqual(ret_val, test_val)
        finally:
            DeleteKey(HKEY_CURRENT_USER, test_key_name)

    def test_read_string_containing_null(self):
        
        try:
            with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
                self.assertNotEqual(ck.handle, 0)
                test_val = "A string\x00 with a null"
                SetValueEx(ck, "test_name", 0, REG_SZ, test_val)
                ret_val, ret_type = QueryValueEx(ck, "test_name")
                self.assertEqual(ret_type, REG_SZ)
                self.assertEqual(ret_val, "A string")
        finally:
            DeleteKey(HKEY_CURRENT_USER, test_key_name)


@unittest.skipUnless(REMOTE_NAME, "Skipping remote registry tests")
class RemoteWinregTests(BaseWinregTests):

    def test_remote_registry_works(self):
        remote_key = ConnectRegistry(REMOTE_NAME, HKEY_CURRENT_USER)
        self._test_all(remote_key)


@unittest.skipUnless(WIN64_MACHINE, "x64 specific registry tests")
class Win64WinregTests(BaseWinregTests):

    def test_named_arguments(self):
        self._test_named_args(HKEY_CURRENT_USER, test_key_name)
        
        DeleteKeyEx(key=HKEY_CURRENT_USER, sub_key=test_key_name,
                    access=KEY_ALL_ACCESS, reserved=0)

    @unittest.skipIf(win32_edition() in ('WindowsCoreHeadless', 'IoTEdgeOS'), "APIs not available on WindowsCoreHeadless")
    def test_reflection_functions(self):
        
        
        with OpenKey(HKEY_LOCAL_MACHINE, "Software") as key:
            
            self.assertTrue(QueryReflectionKey(key))
            self.assertIsNone(EnableReflectionKey(key))
            self.assertIsNone(DisableReflectionKey(key))
            self.assertTrue(QueryReflectionKey(key))

    @unittest.skipUnless(HAS_REFLECTION, "OS doesn't support reflection")
    def test_reflection(self):
        
        
        
        
        try:
            with CreateKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0,
                             KEY_ALL_ACCESS | KEY_WOW64_32KEY) as created_key:
                self.assertNotEqual(created_key.handle, 0)

                
                with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0,
                             KEY_ALL_ACCESS | KEY_WOW64_32KEY) as key:
                    self.assertNotEqual(key.handle, 0)

                
                SetValueEx(created_key, "", 0, REG_SZ, "32KEY")

                
                
                open_fail = lambda: OpenKey(HKEY_CURRENT_USER,
                                            test_reflect_key_name, 0,
                                            KEY_READ | KEY_WOW64_64KEY)
                self.assertRaises(OSError, open_fail)

            
            with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0,
                         KEY_ALL_ACCESS | KEY_WOW64_64KEY) as key:
                self.assertNotEqual(key.handle, 0)
                
                self.assertEqual("32KEY", QueryValue(key, ""))
                
                SetValueEx(key, "", 0, REG_SZ, "64KEY")

            
            
            with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0,
                         KEY_READ | KEY_WOW64_32KEY) as key:
                self.assertEqual("64KEY", QueryValue(key, ""))
        finally:
            DeleteKeyEx(HKEY_CURRENT_USER, test_reflect_key_name,
                        KEY_WOW64_32KEY, 0)

    @unittest.skipUnless(HAS_REFLECTION, "OS doesn't support reflection")
    def test_disable_reflection(self):
        
        try:
            with CreateKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0,
                             KEY_ALL_ACCESS | KEY_WOW64_32KEY) as created_key:
                
                disabled = QueryReflectionKey(created_key)
                self.assertEqual(type(disabled), bool)
                
                self.assertFalse(disabled)

                DisableReflectionKey(created_key)
                self.assertTrue(QueryReflectionKey(created_key))

            
            
            open_fail = lambda: OpenKeyEx(HKEY_CURRENT_USER,
                                          test_reflect_key_name, 0,
                                          KEY_READ | KEY_WOW64_64KEY)
            self.assertRaises(OSError, open_fail)

            
            with OpenKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0,
                           KEY_READ | KEY_WOW64_32KEY) as key:
                self.assertNotEqual(key.handle, 0)
        finally:
            DeleteKeyEx(HKEY_CURRENT_USER, test_reflect_key_name,
                        KEY_WOW64_32KEY, 0)

    def test_exception_numbers(self):
        with self.assertRaises(FileNotFoundError) as ctx:
            QueryValue(HKEY_CLASSES_ROOT, 'some_value_that_does_not_exist')


if __name__ == "__main__":
    if not REMOTE_NAME:
        print("Remote registry calls can be tested using",
              "'test_winreg.py --remote \\\\machine_name'")
    unittest.main()