From: Francis De Brabandere <francisdb@gmail.com> Tests for native key-matching semantics not yet implemented in Wine: Boolean and Currency keys, value-based numeric comparison at double precision across types, and Empty matching the zero/default value of any numeric type or the empty string. --- dlls/scrrun/tests/dictionary.c | 152 +++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/dlls/scrrun/tests/dictionary.c b/dlls/scrrun/tests/dictionary.c index e1cb33c45a7..b12606ec8a4 100644 --- a/dlls/scrrun/tests/dictionary.c +++ b/dlls/scrrun/tests/dictionary.c @@ -744,6 +744,40 @@ if (0) { /* crashes on native */ ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); ok(V_I4(&hash) == expected, "got hash %#lx, expected %#lx\n", V_I4(&hash), expected); + /* Boolean keys hash by their numeric value. */ + V_VT(&key) = VT_BOOL; + V_BOOL(&key) = VARIANT_FALSE; + V_I4(&hash) = 5678; + hr = IDictionary_get_HashVal(dict, &key, &hash); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); + todo_wine ok(V_I4(&hash) == get_num_hash(0.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); + + V_VT(&key) = VT_BOOL; + V_BOOL(&key) = VARIANT_TRUE; + V_I4(&hash) = 5678; + hr = IDictionary_get_HashVal(dict, &key, &hash); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); + todo_wine ok(V_I4(&hash) == get_num_hash((FLOAT)VARIANT_TRUE), "Unexpected hash %#lx.\n", V_I4(&hash)); + + /* Currency keys hash by their numeric value, matching equal numbers. */ + V_VT(&key) = VT_CY; + V_CY(&key).int64 = 0; + V_I4(&hash) = 5678; + hr = IDictionary_get_HashVal(dict, &key, &hash); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); + todo_wine ok(V_I4(&hash) == get_num_hash(0.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); + + V_VT(&key) = VT_CY; + V_CY(&key).int64 = 5 * 10000; + V_I4(&hash) = 5678; + hr = IDictionary_get_HashVal(dict, &key, &hash); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&hash) == VT_I4, "got %d\n", V_VT(&hash)); + todo_wine ok(V_I4(&hash) == get_num_hash(5.0f), "Unexpected hash %#lx.\n", V_I4(&hash)); + V_VT(&key) = VT_EMPTY; V_I4(&key) = 1234; V_I4(&hash) = 5678; @@ -1050,6 +1084,123 @@ static void test_Add(void) IDictionary_Release(dict); } +/* Add two keys to a fresh dictionary; returns TRUE if the second key is + * treated as a duplicate of the first. */ +static BOOL keys_match(VARIANT *key1, VARIANT *key2) +{ + IDictionary *dict; + VARIANT item; + HRESULT hr; + + if (FAILED(CoCreateInstance(&CLSID_Dictionary, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, + &IID_IDictionary, (void**)&dict))) + return FALSE; + + V_VT(&item) = VT_I2; + V_I2(&item) = 1; + if (FAILED(IDictionary_Add(dict, key1, &item))) + { + IDictionary_Release(dict); + return FALSE; + } + + hr = IDictionary_Add(dict, key2, &item); + IDictionary_Release(dict); + return hr == CTL_E_KEY_ALREADY_EXISTS; +} + +static void test_key_matching(void) +{ + static const VARTYPE zero_keys[] = { + VT_I2, VT_I4, VT_UI1, VT_R4, VT_R8, VT_DATE, VT_BOOL, VT_CY, + }; + VARIANT a, b; + unsigned i; + + /* Numeric keys compare by value across types (Byte/Int/Long/Single/ + * Double/Date/Currency/Boolean), independent of the source type. */ + V_VT(&a) = VT_I2; + V_I2(&a) = 5; + V_VT(&b) = VT_R8; + V_R8(&b) = 5.0; + ok(keys_match(&a, &b), "I2 5 should match R8 5.0\n"); + + V_VT(&b) = VT_CY; + V_CY(&b).int64 = 5 * 10000; + todo_wine ok(keys_match(&a, &b), "I2 5 should match CY 5\n"); + + V_VT(&b) = VT_DATE; + V_DATE(&b) = 5.0; + ok(keys_match(&a, &b), "I2 5 should match DATE 5\n"); + + V_VT(&a) = VT_BOOL; + V_BOOL(&a) = VARIANT_TRUE; + V_VT(&b) = VT_I2; + V_I2(&b) = -1; + todo_wine ok(keys_match(&a, &b), "True should match I2 -1\n"); + + /* Comparison uses double precision: values that differ in R8 but collapse + * to the same R4 are still distinct keys. */ + V_VT(&a) = VT_R8; + V_R8(&a) = 16777216.0; + V_VT(&b) = VT_R8; + V_R8(&b) = 16777217.0; + todo_wine ok(!keys_match(&a, &b), "16777216.0 and 16777217.0 should be distinct keys\n"); + + V_R8(&a) = 5.0; + V_R8(&b) = 5.0000001; + todo_wine ok(!keys_match(&a, &b), "5.0 and 5.0000001 should be distinct keys\n"); + + /* Empty matches the zero/default value of any numeric type, and False. */ + for (i = 0; i < ARRAY_SIZE(zero_keys); i++) + { + V_VT(&a) = VT_EMPTY; + V_VT(&b) = zero_keys[i]; + if (zero_keys[i] == VT_CY) + V_CY(&b).int64 = 0; + else if (zero_keys[i] == VT_R8 || zero_keys[i] == VT_DATE) + V_R8(&b) = 0.0; + else if (zero_keys[i] == VT_R4) + V_R4(&b) = 0.0f; + else + V_I4(&b) = 0; + todo_wine ok(keys_match(&a, &b), "Empty should match zero key vt %d\n", zero_keys[i]); + } + + /* Empty matches the empty string, but not a non-empty one. */ + V_VT(&a) = VT_EMPTY; + V_VT(&b) = VT_BSTR; + V_BSTR(&b) = SysAllocString(L""); + todo_wine ok(keys_match(&a, &b), "Empty should match empty string\n"); + VariantClear(&b); + + V_VT(&b) = VT_BSTR; + V_BSTR(&b) = SysAllocString(L"0"); + ok(!keys_match(&a, &b), "Empty should not match \"0\"\n"); + VariantClear(&b); + + /* Empty does not match a non-zero number or Null. */ + V_VT(&b) = VT_I2; + V_I2(&b) = 5; + ok(!keys_match(&a, &b), "Empty should not match I2 5\n"); + + V_VT(&b) = VT_NULL; + ok(!keys_match(&a, &b), "Empty should not match Null\n"); + + /* A numeric zero does not match the empty string (only Empty bridges). */ + V_VT(&a) = VT_I2; + V_I2(&a) = 0; + V_VT(&b) = VT_BSTR; + V_BSTR(&b) = SysAllocString(L""); + ok(!keys_match(&a, &b), "I2 0 should not match empty string\n"); + VariantClear(&b); + + /* Null matches only Null. */ + V_VT(&a) = VT_NULL; + V_VT(&b) = VT_NULL; + ok(keys_match(&a, &b), "Null should match Null\n"); +} + static void test_IEnumVARIANT(void) { IUnknown *enum1, *enum2; @@ -1230,6 +1381,7 @@ START_TEST(dictionary) test_Remove(); test_Item(); test_Add(); + test_key_matching(); test_IEnumVARIANT(); test_putref_Item(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10960