Back to index

python3.2  3.2.2
test_winreg.py
Go to the documentation of this file.
00001 # Test the windows specific win32reg module.
00002 # Only win32reg functions not hit here: FlushKey, LoadKey and SaveKey
00003 
00004 import os, sys
00005 import unittest
00006 from test import support
00007 threading = support.import_module("threading")
00008 from platform import machine
00009 
00010 # Do this first so test will be skipped if module doesn't exist
00011 support.import_module('winreg')
00012 # Now import everything
00013 from winreg import *
00014 
00015 try:
00016     REMOTE_NAME = sys.argv[sys.argv.index("--remote")+1]
00017 except (IndexError, ValueError):
00018     REMOTE_NAME = None
00019 
00020 # tuple of (major, minor)
00021 WIN_VER = sys.getwindowsversion()[:2]
00022 # Some tests should only run on 64-bit architectures where WOW64 will be.
00023 WIN64_MACHINE = True if machine() == "AMD64" else False
00024 
00025 # Starting with Windows 7 and Windows Server 2008 R2, WOW64 no longer uses
00026 # registry reflection and formerly reflected keys are shared instead.
00027 # Windows 7 and Windows Server 2008 R2 are version 6.1. Due to this, some
00028 # tests are only valid up until 6.1
00029 HAS_REFLECTION = True if WIN_VER < (6, 1) else False
00030 
00031 test_key_name = "SOFTWARE\\Python Registry Test Key - Delete Me"
00032 # On OS'es that support reflection we should test with a reflected key
00033 test_reflect_key_name = "SOFTWARE\\Classes\\Python Test Key - Delete Me"
00034 
00035 test_data = [
00036     ("Int Value",     45,                                      REG_DWORD),
00037     ("String Val",    "A string value",                        REG_SZ),
00038     ("StringExpand",  "The path is %path%",                    REG_EXPAND_SZ),
00039     ("Multi-string",  ["Lots", "of", "string", "values"],      REG_MULTI_SZ),
00040     ("Raw Data",      b"binary\x00data",                       REG_BINARY),
00041     ("Big String",    "x"*(2**14-1),                           REG_SZ),
00042     ("Big Binary",    b"x"*(2**14),                            REG_BINARY),
00043     # Two and three kanjis, meaning: "Japan" and "Japanese")
00044     ("Japanese 日本", "日本語", REG_SZ),
00045 ]
00046 
00047 class BaseWinregTests(unittest.TestCase):
00048 
00049     def setUp(self):
00050         # Make sure that the test key is absent when the test
00051         # starts.
00052         self.delete_tree(HKEY_CURRENT_USER, test_key_name)
00053 
00054     def delete_tree(self, root, subkey):
00055         try:
00056             hkey = OpenKey(root, subkey, KEY_ALL_ACCESS)
00057         except WindowsError:
00058             # subkey does not exist
00059             return
00060         while True:
00061             try:
00062                 subsubkey = EnumKey(hkey, 0)
00063             except WindowsError:
00064                 # no more subkeys
00065                 break
00066             self.delete_tree(hkey, subsubkey)
00067         CloseKey(hkey)
00068         DeleteKey(root, subkey)
00069 
00070     def _write_test_data(self, root_key, subkeystr="sub_key",
00071                          CreateKey=CreateKey):
00072         # Set the default value for this key.
00073         SetValue(root_key, test_key_name, REG_SZ, "Default value")
00074         key = CreateKey(root_key, test_key_name)
00075         self.assertTrue(key.handle != 0)
00076         # Create a sub-key
00077         sub_key = CreateKey(key, subkeystr)
00078         # Give the sub-key some named values
00079 
00080         for value_name, value_data, value_type in test_data:
00081             SetValueEx(sub_key, value_name, 0, value_type, value_data)
00082 
00083         # Check we wrote as many items as we thought.
00084         nkeys, nvalues, since_mod = QueryInfoKey(key)
00085         self.assertEqual(nkeys, 1, "Not the correct number of sub keys")
00086         self.assertEqual(nvalues, 1, "Not the correct number of values")
00087         nkeys, nvalues, since_mod = QueryInfoKey(sub_key)
00088         self.assertEqual(nkeys, 0, "Not the correct number of sub keys")
00089         self.assertEqual(nvalues, len(test_data),
00090                          "Not the correct number of values")
00091         # Close this key this way...
00092         # (but before we do, copy the key as an integer - this allows
00093         # us to test that the key really gets closed).
00094         int_sub_key = int(sub_key)
00095         CloseKey(sub_key)
00096         try:
00097             QueryInfoKey(int_sub_key)
00098             self.fail("It appears the CloseKey() function does "
00099                       "not close the actual key!")
00100         except EnvironmentError:
00101             pass
00102         # ... and close that key that way :-)
00103         int_key = int(key)
00104         key.Close()
00105         try:
00106             QueryInfoKey(int_key)
00107             self.fail("It appears the key.Close() function "
00108                       "does not close the actual key!")
00109         except EnvironmentError:
00110             pass
00111 
00112     def _read_test_data(self, root_key, subkeystr="sub_key", OpenKey=OpenKey):
00113         # Check we can get default value for this key.
00114         val = QueryValue(root_key, test_key_name)
00115         self.assertEqual(val, "Default value",
00116                          "Registry didn't give back the correct value")
00117 
00118         key = OpenKey(root_key, test_key_name)
00119         # Read the sub-keys
00120         with OpenKey(key, subkeystr) as sub_key:
00121             # Check I can enumerate over the values.
00122             index = 0
00123             while 1:
00124                 try:
00125                     data = EnumValue(sub_key, index)
00126                 except EnvironmentError:
00127                     break
00128                 self.assertEqual(data in test_data, True,
00129                                  "Didn't read back the correct test data")
00130                 index = index + 1
00131             self.assertEqual(index, len(test_data),
00132                              "Didn't read the correct number of items")
00133             # Check I can directly access each item
00134             for value_name, value_data, value_type in test_data:
00135                 read_val, read_typ = QueryValueEx(sub_key, value_name)
00136                 self.assertEqual(read_val, value_data,
00137                                  "Could not directly read the value")
00138                 self.assertEqual(read_typ, value_type,
00139                                  "Could not directly read the value")
00140         sub_key.Close()
00141         # Enumerate our main key.
00142         read_val = EnumKey(key, 0)
00143         self.assertEqual(read_val, subkeystr, "Read subkey value wrong")
00144         try:
00145             EnumKey(key, 1)
00146             self.fail("Was able to get a second key when I only have one!")
00147         except EnvironmentError:
00148             pass
00149 
00150         key.Close()
00151 
00152     def _delete_test_data(self, root_key, subkeystr="sub_key"):
00153         key = OpenKey(root_key, test_key_name, 0, KEY_ALL_ACCESS)
00154         sub_key = OpenKey(key, subkeystr, 0, KEY_ALL_ACCESS)
00155         # It is not necessary to delete the values before deleting
00156         # the key (although subkeys must not exist).  We delete them
00157         # manually just to prove we can :-)
00158         for value_name, value_data, value_type in test_data:
00159             DeleteValue(sub_key, value_name)
00160 
00161         nkeys, nvalues, since_mod = QueryInfoKey(sub_key)
00162         self.assertEqual(nkeys, 0, "subkey not empty before delete")
00163         self.assertEqual(nvalues, 0, "subkey not empty before delete")
00164         sub_key.Close()
00165         DeleteKey(key, subkeystr)
00166 
00167         try:
00168             # Shouldnt be able to delete it twice!
00169             DeleteKey(key, subkeystr)
00170             self.fail("Deleting the key twice succeeded")
00171         except EnvironmentError:
00172             pass
00173         key.Close()
00174         DeleteKey(root_key, test_key_name)
00175         # Opening should now fail!
00176         try:
00177             key = OpenKey(root_key, test_key_name)
00178             self.fail("Could open the non-existent key")
00179         except WindowsError: # Use this error name this time
00180             pass
00181 
00182     def _test_all(self, root_key, subkeystr="sub_key"):
00183         self._write_test_data(root_key, subkeystr)
00184         self._read_test_data(root_key, subkeystr)
00185         self._delete_test_data(root_key, subkeystr)
00186 
00187     def _test_named_args(self, key, sub_key):
00188         with CreateKeyEx(key=key, sub_key=sub_key, reserved=0,
00189                          access=KEY_ALL_ACCESS) as ckey:
00190             self.assertTrue(ckey.handle != 0)
00191 
00192         with OpenKeyEx(key=key, sub_key=sub_key, reserved=0,
00193                        access=KEY_ALL_ACCESS) as okey:
00194             self.assertTrue(okey.handle != 0)
00195 
00196 
00197 class LocalWinregTests(BaseWinregTests):
00198 
00199     def test_registry_works(self):
00200         self._test_all(HKEY_CURRENT_USER)
00201         self._test_all(HKEY_CURRENT_USER, "日本-subkey")
00202 
00203     def test_registry_works_extended_functions(self):
00204         # Substitute the regular CreateKey and OpenKey calls with their
00205         # extended counterparts.
00206         # Note: DeleteKeyEx is not used here because it is platform dependent
00207         cke = lambda key, sub_key: CreateKeyEx(key, sub_key, 0, KEY_ALL_ACCESS)
00208         self._write_test_data(HKEY_CURRENT_USER, CreateKey=cke)
00209 
00210         oke = lambda key, sub_key: OpenKeyEx(key, sub_key, 0, KEY_READ)
00211         self._read_test_data(HKEY_CURRENT_USER, OpenKey=oke)
00212 
00213         self._delete_test_data(HKEY_CURRENT_USER)
00214 
00215     def test_named_arguments(self):
00216         self._test_named_args(HKEY_CURRENT_USER, test_key_name)
00217         # Use the regular DeleteKey to clean up
00218         # DeleteKeyEx takes named args and is tested separately
00219         DeleteKey(HKEY_CURRENT_USER, test_key_name)
00220 
00221     def test_connect_registry_to_local_machine_works(self):
00222         # perform minimal ConnectRegistry test which just invokes it
00223         h = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
00224         self.assertNotEqual(h.handle, 0)
00225         h.Close()
00226         self.assertEqual(h.handle, 0)
00227 
00228     def test_inexistant_remote_registry(self):
00229         connect = lambda: ConnectRegistry("abcdefghijkl", HKEY_CURRENT_USER)
00230         self.assertRaises(WindowsError, connect)
00231 
00232     def testExpandEnvironmentStrings(self):
00233         r = ExpandEnvironmentStrings("%windir%\\test")
00234         self.assertEqual(type(r), str)
00235         self.assertEqual(r, os.environ["windir"] + "\\test")
00236 
00237     def test_context_manager(self):
00238         # ensure that the handle is closed if an exception occurs
00239         try:
00240             with ConnectRegistry(None, HKEY_LOCAL_MACHINE) as h:
00241                 self.assertNotEqual(h.handle, 0)
00242                 raise WindowsError
00243         except WindowsError:
00244             self.assertEqual(h.handle, 0)
00245 
00246     def test_changing_value(self):
00247         # Issue2810: A race condition in 2.6 and 3.1 may cause
00248         # EnumValue or QueryValue to throw "WindowsError: More data is
00249         # available"
00250         done = False
00251 
00252         class VeryActiveThread(threading.Thread):
00253             def run(self):
00254                 with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
00255                     use_short = True
00256                     long_string = 'x'*2000
00257                     while not done:
00258                         s = 'x' if use_short else long_string
00259                         use_short = not use_short
00260                         SetValue(key, 'changing_value', REG_SZ, s)
00261 
00262         thread = VeryActiveThread()
00263         thread.start()
00264         try:
00265             with CreateKey(HKEY_CURRENT_USER,
00266                            test_key_name+'\\changing_value') as key:
00267                 for _ in range(1000):
00268                     num_subkeys, num_values, t = QueryInfoKey(key)
00269                     for i in range(num_values):
00270                         name = EnumValue(key, i)
00271                         QueryValue(key, name[0])
00272         finally:
00273             done = True
00274             thread.join()
00275             DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value')
00276             DeleteKey(HKEY_CURRENT_USER, test_key_name)
00277 
00278     def test_long_key(self):
00279         # Issue2810, in 2.6 and 3.1 when the key name was exactly 256
00280         # characters, EnumKey threw "WindowsError: More data is
00281         # available"
00282         name = 'x'*256
00283         try:
00284             with CreateKey(HKEY_CURRENT_USER, test_key_name) as key:
00285                 SetValue(key, name, REG_SZ, 'x')
00286                 num_subkeys, num_values, t = QueryInfoKey(key)
00287                 EnumKey(key, 0)
00288         finally:
00289             DeleteKey(HKEY_CURRENT_USER, '\\'.join((test_key_name, name)))
00290             DeleteKey(HKEY_CURRENT_USER, test_key_name)
00291 
00292     def test_dynamic_key(self):
00293         # Issue2810, when the value is dynamically generated, these
00294         # throw "WindowsError: More data is available" in 2.6 and 3.1
00295         EnumValue(HKEY_PERFORMANCE_DATA, 0)
00296         QueryValueEx(HKEY_PERFORMANCE_DATA, "")
00297 
00298     # Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff
00299     # or DeleteKeyEx so make sure their use raises NotImplementedError
00300     @unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP")
00301     def test_reflection_unsupported(self):
00302         try:
00303             with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck:
00304                 self.assertNotEqual(ck.handle, 0)
00305 
00306             key = OpenKey(HKEY_CURRENT_USER, test_key_name)
00307             self.assertNotEqual(key.handle, 0)
00308 
00309             with self.assertRaises(NotImplementedError):
00310                 DisableReflectionKey(key)
00311             with self.assertRaises(NotImplementedError):
00312                 EnableReflectionKey(key)
00313             with self.assertRaises(NotImplementedError):
00314                 QueryReflectionKey(key)
00315             with self.assertRaises(NotImplementedError):
00316                 DeleteKeyEx(HKEY_CURRENT_USER, test_key_name)
00317         finally:
00318             DeleteKey(HKEY_CURRENT_USER, test_key_name)
00319 
00320 
00321 @unittest.skipUnless(REMOTE_NAME, "Skipping remote registry tests")
00322 class RemoteWinregTests(BaseWinregTests):
00323 
00324     def test_remote_registry_works(self):
00325         remote_key = ConnectRegistry(REMOTE_NAME, HKEY_CURRENT_USER)
00326         self._test_all(remote_key)
00327 
00328 
00329 @unittest.skipUnless(WIN64_MACHINE, "x64 specific registry tests")
00330 class Win64WinregTests(BaseWinregTests):
00331 
00332     def test_named_arguments(self):
00333         self._test_named_args(HKEY_CURRENT_USER, test_key_name)
00334         # Clean up and also exercise the named arguments
00335         DeleteKeyEx(key=HKEY_CURRENT_USER, sub_key=test_key_name,
00336                     access=KEY_ALL_ACCESS, reserved=0)
00337 
00338     def test_reflection_functions(self):
00339         # Test that we can call the query, enable, and disable functions
00340         # on a key which isn't on the reflection list with no consequences.
00341         with OpenKey(HKEY_LOCAL_MACHINE, "Software") as key:
00342             # HKLM\Software is redirected but not reflected in all OSes
00343             self.assertTrue(QueryReflectionKey(key))
00344             self.assertIsNone(EnableReflectionKey(key))
00345             self.assertIsNone(DisableReflectionKey(key))
00346             self.assertTrue(QueryReflectionKey(key))
00347 
00348     @unittest.skipUnless(HAS_REFLECTION, "OS doesn't support reflection")
00349     def test_reflection(self):
00350         # Test that we can create, open, and delete keys in the 32-bit
00351         # area. Because we are doing this in a key which gets reflected,
00352         # test the differences of 32 and 64-bit keys before and after the
00353         # reflection occurs (ie. when the created key is closed).
00354         try:
00355             with CreateKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0,
00356                              KEY_ALL_ACCESS | KEY_WOW64_32KEY) as created_key:
00357                 self.assertNotEqual(created_key.handle, 0)
00358 
00359                 # The key should now be available in the 32-bit area
00360                 with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0,
00361                              KEY_ALL_ACCESS | KEY_WOW64_32KEY) as key:
00362                     self.assertNotEqual(key.handle, 0)
00363 
00364                 # Write a value to what currently is only in the 32-bit area
00365                 SetValueEx(created_key, "", 0, REG_SZ, "32KEY")
00366 
00367                 # The key is not reflected until created_key is closed.
00368                 # The 64-bit version of the key should not be available yet.
00369                 open_fail = lambda: OpenKey(HKEY_CURRENT_USER,
00370                                             test_reflect_key_name, 0,
00371                                             KEY_READ | KEY_WOW64_64KEY)
00372                 self.assertRaises(WindowsError, open_fail)
00373 
00374             # Now explicitly open the 64-bit version of the key
00375             with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0,
00376                          KEY_ALL_ACCESS | KEY_WOW64_64KEY) as key:
00377                 self.assertNotEqual(key.handle, 0)
00378                 # Make sure the original value we set is there
00379                 self.assertEqual("32KEY", QueryValue(key, ""))
00380                 # Set a new value, which will get reflected to 32-bit
00381                 SetValueEx(key, "", 0, REG_SZ, "64KEY")
00382 
00383             # Reflection uses a "last-writer wins policy, so the value we set
00384             # on the 64-bit key should be the same on 32-bit
00385             with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0,
00386                          KEY_READ | KEY_WOW64_32KEY) as key:
00387                 self.assertEqual("64KEY", QueryValue(key, ""))
00388         finally:
00389             DeleteKeyEx(HKEY_CURRENT_USER, test_reflect_key_name,
00390                         KEY_WOW64_32KEY, 0)
00391 
00392     @unittest.skipUnless(HAS_REFLECTION, "OS doesn't support reflection")
00393     def test_disable_reflection(self):
00394         # Make use of a key which gets redirected and reflected
00395         try:
00396             with CreateKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0,
00397                              KEY_ALL_ACCESS | KEY_WOW64_32KEY) as created_key:
00398                 # QueryReflectionKey returns whether or not the key is disabled
00399                 disabled = QueryReflectionKey(created_key)
00400                 self.assertEqual(type(disabled), bool)
00401                 # HKCU\Software\Classes is reflected by default
00402                 self.assertFalse(disabled)
00403 
00404                 DisableReflectionKey(created_key)
00405                 self.assertTrue(QueryReflectionKey(created_key))
00406 
00407             # The key is now closed and would normally be reflected to the
00408             # 64-bit area, but let's make sure that didn't happen.
00409             open_fail = lambda: OpenKeyEx(HKEY_CURRENT_USER,
00410                                           test_reflect_key_name, 0,
00411                                           KEY_READ | KEY_WOW64_64KEY)
00412             self.assertRaises(WindowsError, open_fail)
00413 
00414             # Make sure the 32-bit key is actually there
00415             with OpenKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0,
00416                            KEY_READ | KEY_WOW64_32KEY) as key:
00417                 self.assertNotEqual(key.handle, 0)
00418         finally:
00419             DeleteKeyEx(HKEY_CURRENT_USER, test_reflect_key_name,
00420                         KEY_WOW64_32KEY, 0)
00421 
00422 
00423 def test_main():
00424     support.run_unittest(LocalWinregTests, RemoteWinregTests,
00425                          Win64WinregTests)
00426 
00427 if __name__ == "__main__":
00428     if not REMOTE_NAME:
00429         print("Remote registry calls can be tested using",
00430               "'test_winreg.py --remote \\\\machine_name'")
00431     test_main()