1# Test the windows specific win32reg module. 2# Only win32reg functions not hit here: FlushKey, LoadKey and SaveKey 3 4import gc 5import os, sys, errno 6import threading 7import unittest 8from platform import machine, win32_edition 9from test.support import cpython_only, import_helper 10 11# Do this first so test will be skipped if module doesn't exist 12import_helper.import_module('winreg', required_on=['win']) 13# Now import everything 14from winreg import * 15 16try: 17 REMOTE_NAME = sys.argv[sys.argv.index("--remote")+1] 18except (IndexError, ValueError): 19 REMOTE_NAME = None 20 21# tuple of (major, minor) 22WIN_VER = sys.getwindowsversion()[:2] 23# Some tests should only run on 64-bit architectures where WOW64 will be. 24WIN64_MACHINE = True if machine() == "AMD64" else False 25 26# Starting with Windows 7 and Windows Server 2008 R2, WOW64 no longer uses 27# registry reflection and formerly reflected keys are shared instead. 28# Windows 7 and Windows Server 2008 R2 are version 6.1. Due to this, some 29# tests are only valid up until 6.1 30HAS_REFLECTION = True if WIN_VER < (6, 1) else False 31 32# Use a per-process key to prevent concurrent test runs (buildbot!) from 33# stomping on each other. 34test_key_base = "Python Test Key [%d] - Delete Me" % (os.getpid(),) 35test_key_name = "SOFTWARE\\" + test_key_base 36# On OS'es that support reflection we should test with a reflected key 37test_reflect_key_name = "SOFTWARE\\Classes\\" + test_key_base 38 39test_data = [ 40 ("Int Value", 45, REG_DWORD), 41 ("Qword Value", 0x1122334455667788, REG_QWORD), 42 ("String Val", "A string value", REG_SZ), 43 ("StringExpand", "The path is %path%", REG_EXPAND_SZ), 44 ("Multi-string", ["Lots", "of", "string", "values"], REG_MULTI_SZ), 45 ("Multi-nul", ["", "", "", ""], REG_MULTI_SZ), 46 ("Raw Data", b"binary\x00data", REG_BINARY), 47 ("Big String", "x"*(2**14-1), REG_SZ), 48 ("Big Binary", b"x"*(2**14), REG_BINARY), 49 # Two and three kanjis, meaning: "Japan" and "Japanese". 50 ("Japanese 日本", "日本語", REG_SZ), 51] 52 53 54@cpython_only 55class HeapTypeTests(unittest.TestCase): 56 def test_have_gc(self): 57 self.assertTrue(gc.is_tracked(HKEYType)) 58 59 def test_immutable(self): 60 with self.assertRaisesRegex(TypeError, "immutable"): 61 HKEYType.foo = "bar" 62 63 64class BaseWinregTests(unittest.TestCase): 65 66 def setUp(self): 67 # Make sure that the test key is absent when the test 68 # starts. 69 self.delete_tree(HKEY_CURRENT_USER, test_key_name) 70 71 def delete_tree(self, root, subkey): 72 try: 73 hkey = OpenKey(root, subkey, 0, KEY_ALL_ACCESS) 74 except OSError: 75 # subkey does not exist 76 return 77 while True: 78 try: 79 subsubkey = EnumKey(hkey, 0) 80 except OSError: 81 # no more subkeys 82 break 83 self.delete_tree(hkey, subsubkey) 84 CloseKey(hkey) 85 DeleteKey(root, subkey) 86 87 def _write_test_data(self, root_key, subkeystr="sub_key", 88 CreateKey=CreateKey): 89 # Set the default value for this key. 90 SetValue(root_key, test_key_name, REG_SZ, "Default value") 91 key = CreateKey(root_key, test_key_name) 92 self.assertTrue(key.handle != 0) 93 # Create a sub-key 94 sub_key = CreateKey(key, subkeystr) 95 # Give the sub-key some named values 96 97 for value_name, value_data, value_type in test_data: 98 SetValueEx(sub_key, value_name, 0, value_type, value_data) 99 100 # Check we wrote as many items as we thought. 101 nkeys, nvalues, since_mod = QueryInfoKey(key) 102 self.assertEqual(nkeys, 1, "Not the correct number of sub keys") 103 self.assertEqual(nvalues, 1, "Not the correct number of values") 104 nkeys, nvalues, since_mod = QueryInfoKey(sub_key) 105 self.assertEqual(nkeys, 0, "Not the correct number of sub keys") 106 self.assertEqual(nvalues, len(test_data), 107 "Not the correct number of values") 108 # Close this key this way... 109 # (but before we do, copy the key as an integer - this allows 110 # us to test that the key really gets closed). 111 int_sub_key = int(sub_key) 112 CloseKey(sub_key) 113 try: 114 QueryInfoKey(int_sub_key) 115 self.fail("It appears the CloseKey() function does " 116 "not close the actual key!") 117 except OSError: 118 pass 119 # ... and close that key that way :-) 120 int_key = int(key) 121 key.Close() 122 try: 123 QueryInfoKey(int_key) 124 self.fail("It appears the key.Close() function " 125 "does not close the actual key!") 126 except OSError: 127 pass 128 def _read_test_data(self, root_key, subkeystr="sub_key", OpenKey=OpenKey): 129 # Check we can get default value for this key. 130 val = QueryValue(root_key, test_key_name) 131 self.assertEqual(val, "Default value", 132 "Registry didn't give back the correct value") 133 134 key = OpenKey(root_key, test_key_name) 135 # Read the sub-keys 136 with OpenKey(key, subkeystr) as sub_key: 137 # Check I can enumerate over the values. 138 index = 0 139 while 1: 140 try: 141 data = EnumValue(sub_key, index) 142 except OSError: 143 break 144 self.assertEqual(data in test_data, True, 145 "Didn't read back the correct test data") 146 index = index + 1 147 self.assertEqual(index, len(test_data), 148 "Didn't read the correct number of items") 149 # Check I can directly access each item 150 for value_name, value_data, value_type in test_data: 151 read_val, read_typ = QueryValueEx(sub_key, value_name) 152 self.assertEqual(read_val, value_data, 153 "Could not directly read the value") 154 self.assertEqual(read_typ, value_type, 155 "Could not directly read the value") 156 sub_key.Close() 157 # Enumerate our main key. 158 read_val = EnumKey(key, 0) 159 self.assertEqual(read_val, subkeystr, "Read subkey value wrong") 160 try: 161 EnumKey(key, 1) 162 self.fail("Was able to get a second key when I only have one!") 163 except OSError: 164 pass 165 166 key.Close() 167 168 def _delete_test_data(self, root_key, subkeystr="sub_key"): 169 key = OpenKey(root_key, test_key_name, 0, KEY_ALL_ACCESS) 170 sub_key = OpenKey(key, subkeystr, 0, KEY_ALL_ACCESS) 171 # It is not necessary to delete the values before deleting 172 # the key (although subkeys must not exist). We delete them 173 # manually just to prove we can :-) 174 for value_name, value_data, value_type in test_data: 175 DeleteValue(sub_key, value_name) 176 177 nkeys, nvalues, since_mod = QueryInfoKey(sub_key) 178 self.assertEqual(nkeys, 0, "subkey not empty before delete") 179 self.assertEqual(nvalues, 0, "subkey not empty before delete") 180 sub_key.Close() 181 DeleteKey(key, subkeystr) 182 183 try: 184 # Shouldn't be able to delete it twice! 185 DeleteKey(key, subkeystr) 186 self.fail("Deleting the key twice succeeded") 187 except OSError: 188 pass 189 key.Close() 190 DeleteKey(root_key, test_key_name) 191 # Opening should now fail! 192 try: 193 key = OpenKey(root_key, test_key_name) 194 self.fail("Could open the non-existent key") 195 except OSError: # Use this error name this time 196 pass 197 198 def _test_all(self, root_key, subkeystr="sub_key"): 199 self._write_test_data(root_key, subkeystr) 200 self._read_test_data(root_key, subkeystr) 201 self._delete_test_data(root_key, subkeystr) 202 203 def _test_named_args(self, key, sub_key): 204 with CreateKeyEx(key=key, sub_key=sub_key, reserved=0, 205 access=KEY_ALL_ACCESS) as ckey: 206 self.assertTrue(ckey.handle != 0) 207 208 with OpenKeyEx(key=key, sub_key=sub_key, reserved=0, 209 access=KEY_ALL_ACCESS) as okey: 210 self.assertTrue(okey.handle != 0) 211 212 213class LocalWinregTests(BaseWinregTests): 214 215 def test_registry_works(self): 216 self._test_all(HKEY_CURRENT_USER) 217 self._test_all(HKEY_CURRENT_USER, "日本-subkey") 218 219 def test_registry_works_extended_functions(self): 220 # Substitute the regular CreateKey and OpenKey calls with their 221 # extended counterparts. 222 # Note: DeleteKeyEx is not used here because it is platform dependent 223 cke = lambda key, sub_key: CreateKeyEx(key, sub_key, 0, KEY_ALL_ACCESS) 224 self._write_test_data(HKEY_CURRENT_USER, CreateKey=cke) 225 226 oke = lambda key, sub_key: OpenKeyEx(key, sub_key, 0, KEY_READ) 227 self._read_test_data(HKEY_CURRENT_USER, OpenKey=oke) 228 229 self._delete_test_data(HKEY_CURRENT_USER) 230 231 def test_named_arguments(self): 232 self._test_named_args(HKEY_CURRENT_USER, test_key_name) 233 # Use the regular DeleteKey to clean up 234 # DeleteKeyEx takes named args and is tested separately 235 DeleteKey(HKEY_CURRENT_USER, test_key_name) 236 237 def test_connect_registry_to_local_machine_works(self): 238 # perform minimal ConnectRegistry test which just invokes it 239 h = ConnectRegistry(None, HKEY_LOCAL_MACHINE) 240 self.assertNotEqual(h.handle, 0) 241 h.Close() 242 self.assertEqual(h.handle, 0) 243 244 def test_nonexistent_remote_registry(self): 245 connect = lambda: ConnectRegistry("abcdefghijkl", HKEY_CURRENT_USER) 246 self.assertRaises(OSError, connect) 247 248 def testExpandEnvironmentStrings(self): 249 r = ExpandEnvironmentStrings("%windir%\\test") 250 self.assertEqual(type(r), str) 251 self.assertEqual(r, os.environ["windir"] + "\\test") 252 253 def test_context_manager(self): 254 # ensure that the handle is closed if an exception occurs 255 try: 256 with ConnectRegistry(None, HKEY_LOCAL_MACHINE) as h: 257 self.assertNotEqual(h.handle, 0) 258 raise OSError 259 except OSError: 260 self.assertEqual(h.handle, 0) 261 262 def test_changing_value(self): 263 # Issue2810: A race condition in 2.6 and 3.1 may cause 264 # EnumValue or QueryValue to raise "WindowsError: More data is 265 # available" 266 done = False 267 268 class VeryActiveThread(threading.Thread): 269 def run(self): 270 with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: 271 use_short = True 272 long_string = 'x'*2000 273 while not done: 274 s = 'x' if use_short else long_string 275 use_short = not use_short 276 SetValue(key, 'changing_value', REG_SZ, s) 277 278 thread = VeryActiveThread() 279 thread.start() 280 try: 281 with CreateKey(HKEY_CURRENT_USER, 282 test_key_name+'\\changing_value') as key: 283 for _ in range(1000): 284 num_subkeys, num_values, t = QueryInfoKey(key) 285 for i in range(num_values): 286 name = EnumValue(key, i) 287 QueryValue(key, name[0]) 288 finally: 289 done = True 290 thread.join() 291 DeleteKey(HKEY_CURRENT_USER, test_key_name+'\\changing_value') 292 DeleteKey(HKEY_CURRENT_USER, test_key_name) 293 294 def test_long_key(self): 295 # Issue2810, in 2.6 and 3.1 when the key name was exactly 256 296 # characters, EnumKey raised "WindowsError: More data is 297 # available" 298 name = 'x'*256 299 try: 300 with CreateKey(HKEY_CURRENT_USER, test_key_name) as key: 301 SetValue(key, name, REG_SZ, 'x') 302 num_subkeys, num_values, t = QueryInfoKey(key) 303 EnumKey(key, 0) 304 finally: 305 DeleteKey(HKEY_CURRENT_USER, '\\'.join((test_key_name, name))) 306 DeleteKey(HKEY_CURRENT_USER, test_key_name) 307 308 def test_dynamic_key(self): 309 # Issue2810, when the value is dynamically generated, these 310 # raise "WindowsError: More data is available" in 2.6 and 3.1 311 try: 312 EnumValue(HKEY_PERFORMANCE_DATA, 0) 313 except OSError as e: 314 if e.errno in (errno.EPERM, errno.EACCES): 315 self.skipTest("access denied to registry key " 316 "(are you running in a non-interactive session?)") 317 raise 318 QueryValueEx(HKEY_PERFORMANCE_DATA, "") 319 320 # Reflection requires XP x64/Vista at a minimum. XP doesn't have this stuff 321 # or DeleteKeyEx so make sure their use raises NotImplementedError 322 @unittest.skipUnless(WIN_VER < (5, 2), "Requires Windows XP") 323 def test_reflection_unsupported(self): 324 try: 325 with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: 326 self.assertNotEqual(ck.handle, 0) 327 328 key = OpenKey(HKEY_CURRENT_USER, test_key_name) 329 self.assertNotEqual(key.handle, 0) 330 331 with self.assertRaises(NotImplementedError): 332 DisableReflectionKey(key) 333 with self.assertRaises(NotImplementedError): 334 EnableReflectionKey(key) 335 with self.assertRaises(NotImplementedError): 336 QueryReflectionKey(key) 337 with self.assertRaises(NotImplementedError): 338 DeleteKeyEx(HKEY_CURRENT_USER, test_key_name) 339 finally: 340 DeleteKey(HKEY_CURRENT_USER, test_key_name) 341 342 def test_setvalueex_value_range(self): 343 # Test for Issue #14420, accept proper ranges for SetValueEx. 344 # Py2Reg, which gets called by SetValueEx, was using PyLong_AsLong, 345 # thus raising OverflowError. The implementation now uses 346 # PyLong_AsUnsignedLong to match DWORD's size. 347 try: 348 with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: 349 self.assertNotEqual(ck.handle, 0) 350 SetValueEx(ck, "test_name", None, REG_DWORD, 0x80000000) 351 finally: 352 DeleteKey(HKEY_CURRENT_USER, test_key_name) 353 354 def test_setvalueex_negative_one_check(self): 355 # Test for Issue #43984, check -1 was not set by SetValueEx. 356 # Py2Reg, which gets called by SetValueEx, wasn't checking return 357 # value by PyLong_AsUnsignedLong, thus setting -1 as value in the registry. 358 # The implementation now checks PyLong_AsUnsignedLong return value to assure 359 # the value set was not -1. 360 try: 361 with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: 362 with self.assertRaises(OverflowError): 363 SetValueEx(ck, "test_name_dword", None, REG_DWORD, -1) 364 SetValueEx(ck, "test_name_qword", None, REG_QWORD, -1) 365 self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_dword") 366 self.assertRaises(FileNotFoundError, QueryValueEx, ck, "test_name_qword") 367 368 finally: 369 DeleteKey(HKEY_CURRENT_USER, test_key_name) 370 371 def test_queryvalueex_return_value(self): 372 # Test for Issue #16759, return unsigned int from QueryValueEx. 373 # Reg2Py, which gets called by QueryValueEx, was returning a value 374 # generated by PyLong_FromLong. The implementation now uses 375 # PyLong_FromUnsignedLong to match DWORD's size. 376 try: 377 with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: 378 self.assertNotEqual(ck.handle, 0) 379 test_val = 0x80000000 380 SetValueEx(ck, "test_name", None, REG_DWORD, test_val) 381 ret_val, ret_type = QueryValueEx(ck, "test_name") 382 self.assertEqual(ret_type, REG_DWORD) 383 self.assertEqual(ret_val, test_val) 384 finally: 385 DeleteKey(HKEY_CURRENT_USER, test_key_name) 386 387 def test_setvalueex_crash_with_none_arg(self): 388 # Test for Issue #21151, segfault when None is passed to SetValueEx 389 try: 390 with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: 391 self.assertNotEqual(ck.handle, 0) 392 test_val = None 393 SetValueEx(ck, "test_name", 0, REG_BINARY, test_val) 394 ret_val, ret_type = QueryValueEx(ck, "test_name") 395 self.assertEqual(ret_type, REG_BINARY) 396 self.assertEqual(ret_val, test_val) 397 finally: 398 DeleteKey(HKEY_CURRENT_USER, test_key_name) 399 400 def test_read_string_containing_null(self): 401 # Test for issue 25778: REG_SZ should not contain null characters 402 try: 403 with CreateKey(HKEY_CURRENT_USER, test_key_name) as ck: 404 self.assertNotEqual(ck.handle, 0) 405 test_val = "A string\x00 with a null" 406 SetValueEx(ck, "test_name", 0, REG_SZ, test_val) 407 ret_val, ret_type = QueryValueEx(ck, "test_name") 408 self.assertEqual(ret_type, REG_SZ) 409 self.assertEqual(ret_val, "A string") 410 finally: 411 DeleteKey(HKEY_CURRENT_USER, test_key_name) 412 413 414@unittest.skipUnless(REMOTE_NAME, "Skipping remote registry tests") 415class RemoteWinregTests(BaseWinregTests): 416 417 def test_remote_registry_works(self): 418 remote_key = ConnectRegistry(REMOTE_NAME, HKEY_CURRENT_USER) 419 self._test_all(remote_key) 420 421 422@unittest.skipUnless(WIN64_MACHINE, "x64 specific registry tests") 423class Win64WinregTests(BaseWinregTests): 424 425 def test_named_arguments(self): 426 self._test_named_args(HKEY_CURRENT_USER, test_key_name) 427 # Clean up and also exercise the named arguments 428 DeleteKeyEx(key=HKEY_CURRENT_USER, sub_key=test_key_name, 429 access=KEY_ALL_ACCESS, reserved=0) 430 431 @unittest.skipIf(win32_edition() in ('WindowsCoreHeadless', 'IoTEdgeOS'), "APIs not available on WindowsCoreHeadless") 432 def test_reflection_functions(self): 433 # Test that we can call the query, enable, and disable functions 434 # on a key which isn't on the reflection list with no consequences. 435 with OpenKey(HKEY_LOCAL_MACHINE, "Software") as key: 436 # HKLM\Software is redirected but not reflected in all OSes 437 self.assertTrue(QueryReflectionKey(key)) 438 self.assertIsNone(EnableReflectionKey(key)) 439 self.assertIsNone(DisableReflectionKey(key)) 440 self.assertTrue(QueryReflectionKey(key)) 441 442 @unittest.skipUnless(HAS_REFLECTION, "OS doesn't support reflection") 443 def test_reflection(self): 444 # Test that we can create, open, and delete keys in the 32-bit 445 # area. Because we are doing this in a key which gets reflected, 446 # test the differences of 32 and 64-bit keys before and after the 447 # reflection occurs (ie. when the created key is closed). 448 try: 449 with CreateKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0, 450 KEY_ALL_ACCESS | KEY_WOW64_32KEY) as created_key: 451 self.assertNotEqual(created_key.handle, 0) 452 453 # The key should now be available in the 32-bit area 454 with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0, 455 KEY_ALL_ACCESS | KEY_WOW64_32KEY) as key: 456 self.assertNotEqual(key.handle, 0) 457 458 # Write a value to what currently is only in the 32-bit area 459 SetValueEx(created_key, "", 0, REG_SZ, "32KEY") 460 461 # The key is not reflected until created_key is closed. 462 # The 64-bit version of the key should not be available yet. 463 open_fail = lambda: OpenKey(HKEY_CURRENT_USER, 464 test_reflect_key_name, 0, 465 KEY_READ | KEY_WOW64_64KEY) 466 self.assertRaises(OSError, open_fail) 467 468 # Now explicitly open the 64-bit version of the key 469 with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0, 470 KEY_ALL_ACCESS | KEY_WOW64_64KEY) as key: 471 self.assertNotEqual(key.handle, 0) 472 # Make sure the original value we set is there 473 self.assertEqual("32KEY", QueryValue(key, "")) 474 # Set a new value, which will get reflected to 32-bit 475 SetValueEx(key, "", 0, REG_SZ, "64KEY") 476 477 # Reflection uses a "last-writer wins policy, so the value we set 478 # on the 64-bit key should be the same on 32-bit 479 with OpenKey(HKEY_CURRENT_USER, test_reflect_key_name, 0, 480 KEY_READ | KEY_WOW64_32KEY) as key: 481 self.assertEqual("64KEY", QueryValue(key, "")) 482 finally: 483 DeleteKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 484 KEY_WOW64_32KEY, 0) 485 486 @unittest.skipUnless(HAS_REFLECTION, "OS doesn't support reflection") 487 def test_disable_reflection(self): 488 # Make use of a key which gets redirected and reflected 489 try: 490 with CreateKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0, 491 KEY_ALL_ACCESS | KEY_WOW64_32KEY) as created_key: 492 # QueryReflectionKey returns whether or not the key is disabled 493 disabled = QueryReflectionKey(created_key) 494 self.assertEqual(type(disabled), bool) 495 # HKCU\Software\Classes is reflected by default 496 self.assertFalse(disabled) 497 498 DisableReflectionKey(created_key) 499 self.assertTrue(QueryReflectionKey(created_key)) 500 501 # The key is now closed and would normally be reflected to the 502 # 64-bit area, but let's make sure that didn't happen. 503 open_fail = lambda: OpenKeyEx(HKEY_CURRENT_USER, 504 test_reflect_key_name, 0, 505 KEY_READ | KEY_WOW64_64KEY) 506 self.assertRaises(OSError, open_fail) 507 508 # Make sure the 32-bit key is actually there 509 with OpenKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 0, 510 KEY_READ | KEY_WOW64_32KEY) as key: 511 self.assertNotEqual(key.handle, 0) 512 finally: 513 DeleteKeyEx(HKEY_CURRENT_USER, test_reflect_key_name, 514 KEY_WOW64_32KEY, 0) 515 516 def test_exception_numbers(self): 517 with self.assertRaises(FileNotFoundError) as ctx: 518 QueryValue(HKEY_CLASSES_ROOT, 'some_value_that_does_not_exist') 519 520 521if __name__ == "__main__": 522 if not REMOTE_NAME: 523 print("Remote registry calls can be tested using", 524 "'test_winreg.py --remote \\\\machine_name'") 525 unittest.main() 526