diff --git a/dlls/oleaut32/tests/vartest.c b/dlls/oleaut32/tests/vartest.c index 56322badb81..5b1941e77ac 100644 --- a/dlls/oleaut32/tests/vartest.c +++ b/dlls/oleaut32/tests/vartest.c @@ -2,6 +2,7 @@ * VARIANT test program * * Copyright 1998 Jean-Claude Cote + * Copyright 2006 Google (Benjamin Arai) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -5093,6 +5094,323 @@ static void test_VarAdd(void) SysFreeString(rbstr); } +static void test_VarCat(void) +{ + LCID lcid; + VARIANT left, right, result, expected; + static const WCHAR sz12[] = {'1','2','\0'}; + static const WCHAR sz34[] = {'3','4','\0'}; + static const WCHAR sz1234[] = {'1','2','3','4','\0'}; + static const WCHAR date_sz12[] = {'9','/','3','0','/','1','9','8','0','1','2','\0'}; + static const WCHAR sz12_date[] = {'1','2','9','/','3','0','/','1','9','8','0','\0'}; + static const WCHAR sz_empty[] = {'\0'}; + static const WCHAR sz12_true[] = {'1','2','T','r','u','e','\0'}; + static const WCHAR sz12_false[] = {'1','2','F','a','l','s','e','\0'}; + TCHAR orig_date_format[128]; + VARTYPE leftvt, rightvt, resultvt; + HRESULT hres; + + /* Set date format for testing */ + lcid = LOCALE_USER_DEFAULT; + GetLocaleInfo(lcid,LOCALE_SSHORTDATE,orig_date_format,128); + SetLocaleInfo(lcid,LOCALE_SSHORTDATE,"M/d/yyyy"); + + VariantInit(&left); + VariantInit(&right); + VariantInit(&result); + VariantInit(&expected); + + /* Check expected types for all combinations */ + for (leftvt = 0; leftvt <= VT_BSTR_BLOB; leftvt++) + { + + SKIPTESTS(leftvt); + + for (rightvt = 0; rightvt <= VT_BSTR_BLOB; rightvt++) + { + BOOL bFail = FALSE; + SKIPTESTS(rightvt); + + if (leftvt == VT_DISPATCH || rightvt == VT_DISPATCH || + leftvt == VT_UNKNOWN || rightvt == VT_UNKNOWN || + leftvt == VT_RECORD || rightvt == VT_RECORD || + leftvt == VT_CLSID || rightvt == VT_CLSID || + leftvt == VT_BSTR_BLOB || rightvt == VT_BSTR_BLOB + ) + continue; + + if (leftvt == VT_ERROR || rightvt == VT_ERROR) + resultvt = VT_EMPTY; + else if (leftvt == VT_NULL && rightvt == VT_NULL) + resultvt = VT_NULL; + else if ((leftvt == VT_I2 || leftvt == VT_I4 || + leftvt == VT_R4 || leftvt == VT_R8 || + leftvt == VT_CY || leftvt == VT_BOOL || + leftvt == VT_BSTR || leftvt == VT_I1 || + leftvt == VT_UI1 || leftvt == VT_UI2 || + leftvt == VT_UI4 || leftvt == VT_I8 || + leftvt == VT_UI8 || leftvt == VT_INT || + leftvt == VT_UINT || leftvt == VT_EMPTY || + leftvt == VT_NULL || leftvt == VT_DECIMAL || + leftvt == VT_DATE) + && + (rightvt == VT_I2 || rightvt == VT_I4 || + rightvt == VT_R4 || rightvt == VT_R8 || + rightvt == VT_CY || rightvt == VT_BOOL || + rightvt == VT_BSTR || rightvt == VT_I1 || + rightvt == VT_UI1 || rightvt == VT_UI2 || + rightvt == VT_UI4 || rightvt == VT_I8 || + rightvt == VT_UI8 || rightvt == VT_INT || + rightvt == VT_UINT || rightvt == VT_EMPTY || + rightvt == VT_NULL || rightvt == VT_DECIMAL || + rightvt == VT_DATE)) + { + resultvt = VT_BSTR; + } + else + resultvt = VT_EMPTY; + + if (leftvt == VT_ERROR || rightvt == VT_ERROR) + { + bFail = TRUE; + } + + V_VT(&left) = leftvt; + V_VT(&right) = rightvt; + + if (leftvt == VT_BSTR) + V_BSTR(&left) = SysAllocString(sz_empty); + if (rightvt == VT_BSTR) + V_BSTR(&right) = SysAllocString(sz_empty); + if (leftvt == VT_DATE) + V_DATE(&left) = 0.0; + if (rightvt == VT_DATE) + V_DATE(&right) = 0.0; + if (leftvt == VT_DECIMAL) + VarDecFromR8(0.0, &V_DECIMAL(&left)); + if (rightvt == VT_DECIMAL) + VarDecFromR8(0.0, &V_DECIMAL(&right)); + + hres = VarCat(&left, &right, &result); + if (bFail) { + /* Determine the error code for the vt combination */ + HRESULT expected_error_num = S_OK; + if (rightvt == VT_ERROR && leftvt <= VT_VOID) + expected_error_num = DISP_E_TYPEMISMATCH; + else if (leftvt == VT_ERROR && (rightvt == VT_DATE || + rightvt == VT_ERROR || rightvt == VT_DECIMAL)) + expected_error_num = DISP_E_TYPEMISMATCH; + else if (rightvt == VT_DATE || rightvt == VT_ERROR || + rightvt == VT_DECIMAL) + expected_error_num = DISP_E_BADVARTYPE; + else if (leftvt == VT_ERROR || rightvt == VT_ERROR) + expected_error_num = DISP_E_TYPEMISMATCH; + + ok(hres == DISP_E_BADVARTYPE || hres == E_INVALIDARG || + hres == DISP_E_TYPEMISMATCH, + "VarCat: %d, %d: Expected failure 0x%lX, got 0x%lX\n", + leftvt, rightvt, expected_error_num, hres); + } + else + { + ok(V_VT(&result) == resultvt, + "VarCat: %d, %d: expected vt %d, got vt %d\n", + leftvt, rightvt, resultvt, V_VT(&result)); + if (hres != S_OK) + { + HRESULT expected_error_num; + if (leftvt == VT_VARIANT && rightvt == VT_ERROR) + expected_error_num = DISP_E_BADVARTYPE; + else if (leftvt == VT_VARIANT) + expected_error_num = DISP_E_TYPEMISMATCH; + else if (rightvt == VT_VARIANT && (leftvt == VT_EMPTY || + leftvt == VT_NULL || leftvt == VT_I2 || + leftvt == VT_I4 || leftvt == VT_R4 || + leftvt == VT_R8 || leftvt == VT_CY || + leftvt == VT_DATE || leftvt == VT_BSTR || + leftvt == VT_BOOL || leftvt == VT_DECIMAL || + leftvt == VT_I1 || leftvt == VT_UI1 || + leftvt == VT_UI2 || leftvt == VT_UI4 || + leftvt == VT_I8 || leftvt == VT_UI8 || + leftvt == VT_INT || leftvt == VT_UINT + )) + expected_error_num = DISP_E_TYPEMISMATCH; + else + expected_error_num = DISP_E_BADVARTYPE; + + ok(hres == expected_error_num, + "VarCat: %d, %d: Expected failure 0x%lX, got 0x%lX\n", + leftvt, rightvt, expected_error_num, hres); + } + } + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + } + } + + /* Runnning single comparison tests to compare outputs */ + + /* Test concat strings */ + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_BSTR; + V_VT(&expected) = VT_BSTR; + V_BSTR(&left) = SysAllocString(sz12); + V_BSTR(&right) = SysAllocString(sz34); + V_BSTR(&expected) = SysAllocString(sz1234); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_BSTR concat with VT_BSTR failed to return correct result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + /* Test if expression is VT_ERROR */ + V_VT(&left) = VT_ERROR; + V_VT(&right) = VT_BSTR; + VarCat(&left,&right,&result); + ok(V_VT(&result) == VT_EMPTY, + "VarCat: VT_ERROR concat with VT_BSTR should have returned VT_EMPTY\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_ERROR; + VarCat(&left,&right,&result); + ok(V_VT(&result) == VT_EMPTY, + "VarCat: VT_BSTR concat with VT_ERROR should have returned VT_EMPTY\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + /* Test combining boolean with number */ + V_VT(&left) = VT_INT; + V_VT(&right) = VT_BOOL; + V_VT(&expected) = VT_BSTR; + V_INT(&left) = 12; + V_BOOL(&right) = TRUE; + V_BSTR(&expected) = SysAllocString(sz12_true); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_INT concat with VT_BOOL (TRUE) returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + V_VT(&left) = VT_INT; + V_VT(&right) = VT_BOOL; + V_VT(&expected) = VT_BSTR; + V_INT(&left) = 12; + V_BOOL(&right) = FALSE; + V_BSTR(&expected) = SysAllocString(sz12_false); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_INT concat with VT_BOOL (FALSE) returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + /* Test when both expressions are numeric */ + V_VT(&left) = VT_INT; + V_VT(&right) = VT_INT; + V_VT(&expected) = VT_BSTR; + V_INT(&left) = 12; + V_INT(&right) = 34; + V_BSTR(&expected) = SysAllocString(sz1234); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: NUMBER concat with NUMBER returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + /* Test if one expression is numeric and the other is a string */ + V_VT(&left) = VT_INT; + V_VT(&right) = VT_BSTR; + V_INT(&left) = 12; + V_BSTR(&right) = SysAllocString(sz34); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: NUMBER concat with VT_BSTR, inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_INT; + V_BSTR(&left) = SysAllocString(sz12); + V_INT(&right) = 34; + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_BSTR concat with NUMBER, inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + + /* Test concat dates with strings */ + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_DATE; + V_VT(&expected) = VT_BSTR; + V_BSTR(&left) = SysAllocString(sz12); + V_DATE(&right) = 29494.0; + V_BSTR(&expected)= SysAllocString(sz12_date); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_BSTR concat with VT_DATE returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + V_VT(&left) = VT_DATE; + V_VT(&right) = VT_BSTR; + V_VT(&expected) = VT_BSTR; + V_DATE(&left) = 29494.0; + V_BSTR(&right) = SysAllocString(sz12); + V_BSTR(&expected)= SysAllocString(date_sz12); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, + "VarCat: VT_DATE concat with VT_BSTR returned inncorrect result\n"); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); + + /* Test of both expressions are empty */ + V_VT(&left) = VT_BSTR; + V_VT(&right) = VT_BSTR; + V_VT(&expected) = VT_BSTR; + V_BSTR(&left) = SysAllocString(sz_empty); + V_BSTR(&right) = SysAllocString(sz_empty); + V_BSTR(&expected)= SysAllocString(sz_empty); + VarCat(&left,&right,&result); + ok(VarCmp(&result,&left,lcid,0) == VARCMP_EQ, + "VarCat: EMPTY concat with EMPTY did not return empty VT_BSTR\n"); + + /* Restore original date format settings */ + SetLocaleInfo(lcid,LOCALE_SSHORTDATE,orig_date_format); + + VariantClear(&left); + VariantClear(&right); + VariantClear(&result); + VariantClear(&expected); +} + static HRESULT (WINAPI *pVarCmp)(LPVARIANT,LPVARIANT,LCID,ULONG); /* ERROR from wingdi.h is interfering here */ @@ -5387,5 +5705,6 @@ START_TEST(vartest) test_VarEqv(); test_VarMul(); test_VarAdd(); + test_VarCat(); test_VarCmp(); } diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index dbaec34d9ba..3080e5374d3 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -4,6 +4,7 @@ * Copyright 1998 Jean-Claude Cote * Copyright 2003 Jon Griffiths * Copyright 2005 Daniel Remenak + * Copyright 2006 Google (Benjamin Arai) * * The alorithm for conversion from Julian days to day/month/year is based on * that devised by Henry Fliegel, as implemented in PostgreSQL, which is @@ -2443,53 +2444,142 @@ VarNumFromParseNum_DecOverflow: */ HRESULT WINAPI VarCat(LPVARIANT left, LPVARIANT right, LPVARIANT out) { + VARTYPE leftvt,rightvt; + HRESULT hres; + static const WCHAR str_true[] = {'T','r','u','e','\0'}; + static const WCHAR str_false[] = {'F','a','l','s','e','\0'}; + leftvt = V_VT(left); + rightvt = V_VT(right); + TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), out); - /* Should we VariantClear out? */ - /* Can we handle array, vector, by ref etc. */ - if ((V_VT(left)&VT_TYPEMASK) == VT_NULL && - (V_VT(right)&VT_TYPEMASK) == VT_NULL) + /* Null and Null simply return Null */ + if (leftvt == VT_NULL && rightvt == VT_NULL) { V_VT(out) = VT_NULL; return S_OK; } - if (V_VT(left) == VT_BSTR && V_VT(right) == VT_BSTR) + /* VT_ERROR with any other value should return VT_NULL */ + else if (V_VT(left) == VT_ERROR || V_VT(right) == VT_ERROR) { - V_VT(out) = VT_BSTR; - VarBstrCat (V_BSTR(left), V_BSTR(right), &V_BSTR(out)); - return S_OK; + V_VT(out) = VT_EMPTY; + return DISP_E_BADVARTYPE; } - if (V_VT(left) == VT_BSTR) { - VARIANT bstrvar; - HRESULT hres; + /* Concat all type that match conformance test */ + if ((leftvt == VT_I2 || leftvt == VT_I4 || + leftvt == VT_R4 || leftvt == VT_R8 || + leftvt == VT_CY || leftvt == VT_BOOL || + leftvt == VT_BSTR || leftvt == VT_I1 || + leftvt == VT_UI1 || leftvt == VT_UI2 || + leftvt == VT_UI4 || leftvt == VT_I8 || + leftvt == VT_UI8 || leftvt == VT_INT || + leftvt == VT_UINT || leftvt == VT_EMPTY || + leftvt == VT_NULL || leftvt == VT_DATE || + leftvt == VT_DECIMAL) + && + (rightvt == VT_I2 || rightvt == VT_I4 || + rightvt == VT_R4 || rightvt == VT_R8 || + rightvt == VT_CY || rightvt == VT_BOOL || + rightvt == VT_BSTR || rightvt == VT_I1 || + rightvt == VT_UI1 || rightvt == VT_UI2 || + rightvt == VT_UI4 || rightvt == VT_I8 || + rightvt == VT_UI8 || rightvt == VT_INT || + rightvt == VT_UINT || rightvt == VT_EMPTY || + rightvt == VT_NULL || rightvt == VT_DATE || + rightvt == VT_DECIMAL)) + { + VARIANT bstrvar_left, bstrvar_right; V_VT(out) = VT_BSTR; - VariantInit(&bstrvar); - hres = VariantChangeTypeEx(&bstrvar,right,0,0,VT_BSTR); - if (hres) { - FIXME("Failed to convert right side from vt %d to VT_BSTR?\n",V_VT(right)); - return hres; - } - VarBstrCat (V_BSTR(left), V_BSTR(&bstrvar), &V_BSTR(out)); - return S_OK; - } - if (V_VT(right) == VT_BSTR) { - VARIANT bstrvar; - HRESULT hres; - V_VT(out) = VT_BSTR; - VariantInit(&bstrvar); - hres = VariantChangeTypeEx(&bstrvar,left,0,0,VT_BSTR); - if (hres) { - FIXME("Failed to convert right side from vt %d to VT_BSTR?\n",V_VT(right)); - return hres; + /* Convert left side variant to string */ + if (leftvt != VT_BSTR) + { + VariantInit(&bstrvar_left); + if (leftvt == VT_BOOL) + { + /* Bools are handled as True/False strings instead of 0/-1 as in MSDN */ + V_VT(&bstrvar_left) = VT_BSTR; + if (V_BOOL(left) == TRUE) + V_BSTR(&bstrvar_left) = SysAllocString(str_true); + else + V_BSTR(&bstrvar_left) = SysAllocString(str_false); + } + else + { + hres = VariantChangeTypeEx(&bstrvar_left,left,0,0,VT_BSTR); + if (hres != S_OK) { + VariantClear(&bstrvar_left); + VariantClear(&bstrvar_right); + if (leftvt == VT_NULL && (rightvt == VT_EMPTY || + rightvt == VT_NULL || rightvt == VT_I2 || + rightvt == VT_I4 || rightvt == VT_R4 || + rightvt == VT_R8 || rightvt == VT_CY || + rightvt == VT_DATE || rightvt == VT_BSTR || + rightvt == VT_BOOL || rightvt == VT_DECIMAL || + rightvt == VT_I1 || rightvt == VT_UI1 || + rightvt == VT_UI2 || rightvt == VT_UI4 || + rightvt == VT_I8 || rightvt == VT_UI8 || + rightvt == VT_INT || rightvt == VT_UINT)) + return DISP_E_BADVARTYPE; + return hres; + } + } } - VarBstrCat (V_BSTR(&bstrvar), V_BSTR(right), &V_BSTR(out)); + + /* convert right side variant to string */ + if (rightvt != VT_BSTR) + { + VariantInit(&bstrvar_right); + if (rightvt == VT_BOOL) + { + /* Bools are handled as True/False strings instead of 0/-1 as in MSDN */ + V_VT(&bstrvar_right) = VT_BSTR; + if (V_BOOL(right) == TRUE) + V_BSTR(&bstrvar_right) = SysAllocString(str_true); + else + V_BSTR(&bstrvar_right) = SysAllocString(str_false); + } + else + { + hres = VariantChangeTypeEx(&bstrvar_right,right,0,0,VT_BSTR); + if (hres != S_OK) { + VariantClear(&bstrvar_left); + VariantClear(&bstrvar_right); + if (rightvt == VT_NULL && (leftvt == VT_EMPTY || + leftvt == VT_NULL || leftvt == VT_I2 || + leftvt == VT_I4 || leftvt == VT_R4 || + leftvt == VT_R8 || leftvt == VT_CY || + leftvt == VT_DATE || leftvt == VT_BSTR || + leftvt == VT_BOOL || leftvt == VT_DECIMAL || + leftvt == VT_I1 || leftvt == VT_UI1 || + leftvt == VT_UI2 || leftvt == VT_UI4 || + leftvt == VT_I8 || leftvt == VT_UI8 || + leftvt == VT_INT || leftvt == VT_UINT)) + return DISP_E_BADVARTYPE; + return hres; + } + } + } + + /* Concat the resulting strings together */ + if (leftvt == VT_BSTR && rightvt == VT_BSTR) + VarBstrCat (V_BSTR(left), V_BSTR(right), &V_BSTR(out)); + else if (leftvt != VT_BSTR && rightvt != VT_BSTR) + VarBstrCat (V_BSTR(&bstrvar_left), V_BSTR(&bstrvar_right), &V_BSTR(out)); + else if (leftvt != VT_BSTR && rightvt == VT_BSTR) + VarBstrCat (V_BSTR(&bstrvar_left), V_BSTR(right), &V_BSTR(out)); + else if (leftvt == VT_BSTR && rightvt != VT_BSTR) + VarBstrCat (V_BSTR(left), V_BSTR(&bstrvar_right), &V_BSTR(out)); + + VariantClear(&bstrvar_left); + VariantClear(&bstrvar_right); return S_OK; } - FIXME ("types %d / %d not supported\n",V_VT(left)&VT_TYPEMASK, V_VT(right)&VT_TYPEMASK); + + V_VT(out) = VT_EMPTY; return S_OK; }