Hi Andre, According to my test plan of the registry merging project, I will add new tests for HKCR. At first I tried to add them to the old code but I find it become too long and hard-reading. I had to rewrite them to a series of strict and neat tests, classified as read/write operations, including keys & values enumerating and deleting. I used many pre-definitions which make the test look fluent, but I'm not sure if the code is acceptable. http://newtestbot.winehq.org/JobDetails.pl?Key=942
static void test_classesroot(void) { DWORD res, len, i; HKEY hkey; char name[32];
#define CREATE_KEY(key, path) \ { \ HKEY tmp; \ if ((res = RegCreateKeyExA( key, path, 0, NULL, 0, \
KEY_ENUMERATE_SUB_KEYS|KEY_SET_VALUE|KEY_QUERY_VALUE|KEY_CREATE_SUB_KEY, \ NULL, &tmp, NULL )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegCreateKeyExA %p\%s failed with %08X\n", key, path, res); \ return; \ } \ else \ { \ RegCloseKey(tmp); \ } \ } #define DELETE_KEY(key, path) \ { \ DWORD res; \ if ((res = RegDeleteKey(key, path)) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegDeleteKey %p\%s failed with %08X\n", key, path, res); \ return; \ } \ } #define SET_VALUE(key, path, name, value) \ { \ HKEY tmp; \ if ((res = RegOpenKeyExA( key, path, 0, \ KEY_SET_VALUE, &tmp )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegOpenKeyExA %p\%s failed with %08X\n", key, path, res); \ return; \ } \ else \ { \ if ((res = RegSetValueEx( tmp, name, 0, REG_SZ, \ (CONST BYTE*)value, strlen(value) )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegSetValueEx %p\%s failed with %08X\n", tmp, name, res); \ return; \ } \ RegCloseKey(tmp); \ } \ } #define CHECK_EXIST_KEY(key, path) \ ok(exist_key(key, path), "key %p\%s should exist\n", key, path); #define CHECK_NOT_EXIST_KEY(key, path) \ ok(!exist_key(key, path), "key %p\%s should exist\n", key, path); #define CHECK_VALUE(key, path, name, value) \ { \ HKEY tmp; \ if ((res = RegOpenKeyExA( key, path, 0, \ KEY_QUERY_VALUE, &tmp )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegOpenKeyExA %p\%s failed with %08X\n", key, path, res); \ return; \ } \ else \ { \ char buf[32]; \ DWORD type = REG_SZ, size = sizeof(buf); \ if ((res = RegQueryValueEx( tmp, name, 0, &type, \ (BYTE*)buf, &size )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegQueryValueEx %p\%s failed with %08X\n", tmp, name, res); \ return; \ } \ ok(!strcmp(value, buf), "%s expected '%s' but got '%s' in %p\%s\n", name, value, buf, key, path); \ RegCloseKey(tmp); \ } \ } #define CHECK_NON_VALUE(key, path, name) \ { \ HKEY tmp; \ if ((res = RegOpenKeyExA( key, path, 0, \ KEY_QUERY_VALUE, &tmp )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegOpenKeyExA %p\%s failed with %08X\n", key, path, res); \ return; \ } \ else \ { \ char buf[32]; \ DWORD type = REG_SZ, size = sizeof(buf); \ if ((res = RegQueryValueEx( tmp, name, 0, &type, \ (BYTE*)buf, &size )) == ERROR_SUCCESS) \ { \ ok(FALSE, "%s should not exist in %p\%s\n", name, key, path); \ } \ else if (res != ERROR_FILE_NOT_FOUND) \ { \ ok(FALSE, "RegQueryValueEx %p\%s failed with %08X\n", tmp, name, res); \ return; \ } \ RegCloseKey(tmp); \ } \ } #define DELETE_VALUE(key, path, name) \ { \ HKEY tmp; \ if ((res = RegOpenKeyExA( key, path, 0, \ KEY_SET_VALUE, &tmp )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegOpenKeyExA %p\%s failed with %08X\n", key, path, res); \ return; \ } \ else \ { \ if ((res = RegDeleteValueA(tmp, name)) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegDeleteValue %s in %p\%s failed with %08X\n", name, key, path, res); \ return; \ } \ RegCloseKey(tmp); \ } \ } #define CHECK_KEY_INFO(key, path, c1, c2) \ { \ HKEY tmp; \ if ((res = RegOpenKeyExA( key, path, 0, \ KEY_QUERY_VALUE, &tmp )) != ERROR_SUCCESS) \ { \ ok(FALSE, "RegOpenKeyExA %p\%s failed with %08X\n", key, path, res); \ return; \ } \ else \ { \ DWORD n1, n2; \ if (ERROR_SUCCESS != (res = RegQueryInfoKeyA( tmp, NULL, NULL, NULL, \ &n1, NULL, NULL, &n2, NULL, NULL, NULL, NULL ))) \ { \ ok(FALSE, "RegQueryInfoKeyA failed with %08X\n\n", res); \ return; \ } \ ok(n1 == c1, "subkey count expected %d but got %d in %p\%s\n", c1, n1, key, path); \ ok(n2 == c2, "value count expected %d but got %d in %p\%s\n", c2, n2, key, path); \ RegCloseKey(tmp); \ } \ }
/* test create_key priority */
cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); if ((res = RegOpenKeyExA( HKEY_CLASSES_ROOT, "WineTestCls", 0, STANDARD_RIGHTS_READ, &hkey )) != ERROR_SUCCESS) { skip("classes merging not supported\n"); cleanup_classes(); return; } /* test create_key priority */
/* create from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CLASSES_ROOT, "WineTestCls"); CHECK_EXIST_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls");
/* create from hklm then hkcr */ cleanup_classes(); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_CLASSES_ROOT, "WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls");
/* create from hkcu then hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_CLASSES_ROOT, "WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls");
/* further test based on the below */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); if ((res = RegOpenKeyExA( HKEY_CLASSES_ROOT, "WineTestCls", 0, KEY_CREATE_SUB_KEY, &hkey )) != ERROR_SUCCESS) { ok(FALSE, "RegOpenKeyExA %p\%s failed with %08X\n", hkey, "WineTestCls", res); return; } /* create subkey under the handle opened from the key that exists only in hkcu but not hklm */ CREATE_KEY(hkey, "subkey1"); RegCloseKey(hkey); /* check that subkey is created in hklm, proving that the handle from hkcr is not same as opened in hkcu, even when it does not conflict with hklm */ CHECK_EXIST_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls\subkey1"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls\subkey1");
/* test delete_key priority */
/* create from hkcu and delete from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); DELETE_KEY(HKEY_CLASSES_ROOT, "WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls");
/* create from hklm and delete from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); DELETE_KEY(HKEY_CLASSES_ROOT, "WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls");
/* create from hkcu and hklm and delete twice from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); DELETE_KEY(HKEY_CLASSES_ROOT, "WineTestCls"); CHECK_EXIST_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); /* delete again */ DELETE_KEY(HKEY_CLASSES_ROOT, "WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls");
/* test set_value priority */
/* set new value from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcr"); CHECK_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcr"); CHECK_NON_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1");
/* set value from hkcu and reset value from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); SET_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcr"); CHECK_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcr"); CHECK_NON_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1");
/* set value from hklm and reset value from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm"); SET_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcr"); CHECK_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcr"); CHECK_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm");
/* set value from hkcu and hklm and reset value from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm"); SET_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcr"); CHECK_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcr"); CHECK_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm");
/* set value from hklm in a key not conflict with hkcu and reset value from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm"); SET_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcr"); CHECK_NOT_EXIST_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CHECK_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hkcr");
/* test delete_value priority, no need to test when not conflict */
/* set value from hkcu and hklm then delete twice from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm"); DELETE_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1"); CHECK_NON_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1"); CHECK_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm"); /* delete again */ DELETE_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1"); CHECK_NON_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1");
/* test query_value priority, noneed to test when not conflict */
/* set value to hkcu and create the key in hklm then query from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CHECK_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcu");
/* set value to hklm and create the key in hkcu then query from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm"); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CHECK_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hklm");
/* set conflict value both to hkcu and hklm then query from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hklm"); CHECK_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcu");
/* set non conflict value both to hkcu and hklm then query from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val2", "hklm"); CHECK_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val1", "hkcu"); CHECK_VALUE(HKEY_CLASSES_ROOT, "WineTestCls", "val2", "hklm");
/* test query_info_key priority, no need to test when queried key does not conflict */
/* check subkey and value count for conflict subkeys from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls\subkey1"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls\subkey1"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val1", "hkcu"); CHECK_KEY_INFO(HKEY_CLASSES_ROOT, "WineTestCls", 1, 1);
/* check subkey and value count for non conflict subkeys from hkcr */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls\subkey1"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls\subkey2"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val2", "hkcu"); CHECK_KEY_INFO(HKEY_CLASSES_ROOT, "WineTestCls", 2, 2);
/* test enum_key priority, no need to test when enumerated key does not conflict */
/* create subkeys and values in hkcu and hklm */ cleanup_classes(); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls\subkey1"); CREATE_KEY(HKEY_CURRENT_USER, "Software\Classes\WineTestCls\subkey3"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val1", "hkcu"); SET_VALUE(HKEY_CURRENT_USER, "Software\Classes\WineTestCls", "val3", "hkcu"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls\subkey3"); CREATE_KEY(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls\subkey2"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val3", "hkcu"); SET_VALUE(HKEY_LOCAL_MACHINE, "Software\Classes\WineTestCls", "val2", "hkcu"); /* check the count */ CHECK_KEY_INFO(HKEY_CLASSES_ROOT, "WineTestCls", 3, 3); if ((res = RegOpenKeyExA( HKEY_CLASSES_ROOT, "WineTestCls", 0, KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &hkey )) != ERROR_SUCCESS) { ok(FALSE, "RegOpenKeyExA %p\%s failed with %08X\n", hkey, "WineTestCls", res); return; } /* check the names and order of subkeys when enumerating */ i = 0; len = sizeof(name); while (ERROR_SUCCESS == (res = RegEnumKeyExA(hkey, i, name, &len, NULL, NULL, NULL, NULL))) { ++ i; if (i > 3) { ok(FALSE, "got extra subkey name %s\n", name); } else if (strncmp(name, "subkey", 6) || name[6] != (i + '0') || name[7]) { ok(FALSE, "got unexpected subkey name or order %s at index %d\n", name, i); } len = sizeof(name); } if (res != ERROR_NO_MORE_ITEMS) { ok(FALSE, "RegEnumKeyExA failed with %08X\n", res); return; } if (i < 3) { ok(FALSE, "expected 3 subkeys but got only %d\n", i); } /* check the names and order of values when enumerating */ i = 0; len = sizeof(name); while (ERROR_SUCCESS == (res = RegEnumValueA(hkey, i, name, &len, NULL, NULL, NULL, NULL))) { ++ i; if (i > 3) { ok(FALSE, "got extra value name %s\n", name); } else if (strncmp(name, "val", 3) || name[3] != (i + '0') || name[4]) { ok(FALSE, "got unexpected value name or order %s at index %d\n", name, i); } len = sizeof(name); } if (res != ERROR_NO_MORE_ITEMS) { ok(FALSE, "RegEnumValueA failed with %08X\n", res); return; } if (i < 3) { ok(FALSE, "expected 3 value but got only %d\n", i); } RegCloseKey(hkey);
cleanup_classes(); }