diff --git a/dlls/msi/tests/automation.c b/dlls/msi/tests/automation.c index 86afcf92440..69cb9c64137 100644 --- a/dlls/msi/tests/automation.c +++ b/dlls/msi/tests/automation.c @@ -37,8 +37,9 @@ static const WCHAR szMSITEST[] = { 'M','S','I','T','E','S','T',0 }; static const WCHAR szProductCode[] = { '{','F','1','C','3','A','F','5','0','-','8','B','5','6','-','4','A','6','9','-','A','0','0','C','-','0','0','7','7','3','F','E','4','2','F','3','0','}',0 }; static const WCHAR szUpgradeCode[] = { '{','C','E','0','6','7','E','8','D','-','2','E','1','A','-','4','3','6','7','-','B','7','3','4','-','4','E','B','2','B','D','A','D','6','5','6','5','}',0 }; static const WCHAR szProductInfoException[] = { 'P','r','o','d','u','c','t','I','n','f','o',',','P','r','o','d','u','c','t',',','A','t','t','r','i','b','u','t','e',0 }; -CHAR CURR_DIR[MAX_PATH]; -EXCEPINFO excepinfo; +static FILETIME systemtime; +static CHAR CURR_DIR[MAX_PATH]; +static EXCEPINFO excepinfo; /* * OLE automation data @@ -187,6 +188,30 @@ static const msi_table tables[] = ADD_TABLE(service_control) }; +typedef struct _msi_summary_info +{ + UINT property; + UINT datatype; + INT iValue; + FILETIME *pftValue; + const CHAR *szValue; +} msi_summary_info; + +#define ADD_INFO_I2(property, iValue) {property, VT_I2, iValue, NULL, NULL} +#define ADD_INFO_I4(property, iValue) {property, VT_I4, iValue, NULL, NULL} +#define ADD_INFO_LPSTR(property, szValue) {property, VT_LPSTR, 0, NULL, szValue} +#define ADD_INFO_FILETIME(property, pftValue) {property, VT_FILETIME, 0, pftValue, NULL} + +static const msi_summary_info summary_info[] = +{ + ADD_INFO_LPSTR(PID_TEMPLATE, ";1033"), + ADD_INFO_LPSTR(PID_REVNUMBER, "{004757CA-5092-49c2-AD20-28E1CE0DF5F2}"), + ADD_INFO_I4(PID_PAGECOUNT, 100), + ADD_INFO_I4(PID_WORDCOUNT, 0), + ADD_INFO_FILETIME(PID_CREATE_DTM, &systemtime), + ADD_INFO_FILETIME(PID_LASTPRINTED, &systemtime) +}; + /* * Database Helpers */ @@ -202,26 +227,24 @@ static void write_file(const CHAR *filename, const char *data, int data_size) CloseHandle(hf); } -static void write_msi_summary_info(MSIHANDLE db) +static void write_msi_summary_info(MSIHANDLE db, const msi_summary_info *info, int num_info) { MSIHANDLE summary; UINT r; + int j; - r = MsiGetSummaryInformationA(db, NULL, 4, &summary); + r = MsiGetSummaryInformationA(db, NULL, num_info, &summary); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - r = MsiSummaryInfoSetPropertyA(summary, PID_TEMPLATE, VT_LPSTR, 0, NULL, ";1033"); - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + /* import summary information into the stream */ + for (j = 0; j < num_info; j++) + { + const msi_summary_info *entry = &info[j]; - r = MsiSummaryInfoSetPropertyA(summary, PID_REVNUMBER, VT_LPSTR, 0, NULL, - "{004757CA-5092-49c2-AD20-28E1CE0DF5F2}"); - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - - r = MsiSummaryInfoSetPropertyA(summary, PID_PAGECOUNT, VT_I4, 100, NULL, NULL); - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); - - r = MsiSummaryInfoSetPropertyA(summary, PID_WORDCOUNT, VT_I4, 0, NULL, NULL); - ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + r = MsiSummaryInfoSetPropertyA(summary, entry->property, entry->datatype, + entry->iValue, entry->pftValue, entry->szValue); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); + } /* write the summary changes back to the stream */ r = MsiSummaryInfoPersist(summary); @@ -230,7 +253,8 @@ static void write_msi_summary_info(MSIHANDLE db) MsiCloseHandle(summary); } -static void create_database(const CHAR *name, const msi_table *tables, int num_tables) +static void create_database(const CHAR *name, const msi_table *tables, int num_tables, + const msi_summary_info *info, int num_info) { MSIHANDLE db; UINT r; @@ -252,7 +276,7 @@ static void create_database(const CHAR *name, const msi_table *tables, int num_t DeleteFileA(table->filename); } - write_msi_summary_info(db); + write_msi_summary_info(db, info, num_info); r = MsiDatabaseCommit(db); ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %u\n", r); @@ -367,7 +391,7 @@ static void check_service_is_installed(void) * Automation helpers and tests */ -/* ok-like statement which takes two unicode strings as arguments */ +/* ok-like statement which takes two unicode strings or one unicode and one ANSI string as arguments */ static CHAR string1[MAX_PATH], string2[MAX_PATH]; #define ok_w2(format, szString1, szString2) \ @@ -379,6 +403,18 @@ static CHAR string1[MAX_PATH], string2[MAX_PATH]; ok(0, format, string1, string2); \ } +#define ok_aw(format, aString, wString) \ +\ + WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \ + if (lstrcmpA(string1, aString) != 0) \ + ok(0, format, string1, aString); \ + +#define ok_awplus(format, extra, aString, wString) \ +\ + WideCharToMultiByte(CP_ACP, 0, wString, -1, string1, MAX_PATH, NULL, NULL); \ + if (lstrcmpA(string1, aString) != 0) \ + ok(0, format, extra, string1, aString); \ + /* exception checker */ static WCHAR szSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0}; @@ -584,6 +620,8 @@ static void test_dispatch(void) } /* invocation helper function */ +static int _invoke_todo_vtResult = 0; + static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, VARTYPE vtResult) { OLECHAR *name = NULL; @@ -609,7 +647,10 @@ static HRESULT invoke(IDispatch *pDispatch, LPCSTR szName, WORD wFlags, DISPPARA if (hr == S_OK) { - ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult); + if (_invoke_todo_vtResult) todo_wine + ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult); + else + ok(V_VT(pVarResult) == vtResult, "Variant result type is %d, expected %d\n", V_VT(pVarResult), vtResult); if (vtResult != VT_EMPTY) { hr = VariantChangeTypeEx(pVarResult, pVarResult, LOCALE_NEUTRAL, 0, vtResult); @@ -1032,6 +1073,22 @@ static HRESULT Database_OpenView(IDispatch *pDatabase, LPCWSTR szSql, IDispatch return hr; } +static HRESULT Database_SummaryInformation(IDispatch *pDatabase, int iUpdateCount, IDispatch **pSummaryInfo) +{ + VARIANT varresult; + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + HRESULT hr; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = iUpdateCount; + + hr = invoke(pDatabase, "SummaryInformation", DISPATCH_PROPERTYGET, &dispparams, &varresult, VT_DISPATCH); + *pSummaryInfo = V_DISPATCH(&varresult); + return hr; +} + static HRESULT View_Execute(IDispatch *pView, IDispatch *pRecord) { VARIANT varresult; @@ -1193,8 +1250,100 @@ static HRESULT StringList_Count(IDispatch *pStringList, int *pCount) return hr; } +static HRESULT SummaryInfo_PropertyGet(IDispatch *pSummaryInfo, int pid, VARIANT *pVarResult, VARTYPE vtExpect) +{ + VARIANTARG vararg[1]; + DISPPARAMS dispparams = {vararg, NULL, sizeof(vararg)/sizeof(VARIANTARG), 0}; + + VariantInit(&vararg[0]); + V_VT(&vararg[0]) = VT_I4; + V_I4(&vararg[0]) = pid; + return invoke(pSummaryInfo, "Property", DISPATCH_PROPERTYGET, &dispparams, pVarResult, vtExpect); +} + /* Test the various objects */ +static void test_SummaryInfo(IDispatch *pSummaryInfo, const msi_summary_info *info, int num_info) +{ + static const WCHAR szPropertyException[] = { 'P','r','o','p','e','r','t','y',',','P','i','d',0 }; + VARIANT varresult; + HRESULT hr; + int j; + + /* SummaryInfo::Property, get for properties we have set */ + for (j = 0; j < num_info; j++) + { + const msi_summary_info *entry = &info[j]; + + int vt = entry->datatype; + if (vt == VT_LPSTR) vt = VT_BSTR; + else if (vt == VT_FILETIME) vt = VT_DATE; + + hr = SummaryInfo_PropertyGet(pSummaryInfo, entry->property, &varresult, vt); + ok(hr == S_OK, "SummaryInfo_Property (pid %d) failed, hresult 0x%08x\n", entry->property, hr); + if (V_VT(&varresult) != vt) + skip("Skipping property tests due to type mismatch\n"); + else if (vt == VT_I2) + ok(V_I2(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I2 result expected to be %d, but was %d\n", + entry->property, entry->iValue, V_I2(&varresult)); + else if (vt == VT_I4) + ok(V_I4(&varresult) == entry->iValue, "SummaryInfo_Property (pid %d) I4 result expected to be %d, but was %d\n", + entry->property, entry->iValue, V_I4(&varresult)); + else if (vt == VT_DATE) todo_wine + { + SYSTEMTIME st; + FILETIME ft; + DATE d; + + FileTimeToLocalFileTime(entry->pftValue, &ft); + FileTimeToSystemTime(&ft, &st); + SystemTimeToVariantTime(&st, &d); + ok(d == V_DATE(&varresult), "SummaryInfo_Property (pid %d) DATE result expected to be %lf, but was %lf\n", entry->property, d, V_DATE(&varresult)); + } + else if (vt == VT_BSTR) + { + ok_awplus("SummaryInfo_Property (pid %d) BSTR result expected to be %s, but was %s\n", entry->property, entry->szValue, V_BSTR(&varresult)); + } + else + skip("SummaryInfo_Property (pid %d) unhandled result type %d\n", entry->property, vt); + } + + /* SummaryInfo::Property, get; invalid arguments */ + + /* Invalid pids */ + hr = SummaryInfo_PropertyGet(pSummaryInfo, -1, &varresult, VT_EMPTY); + todo_wine ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + ok_exception(hr, szPropertyException); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, 1000, &varresult, VT_EMPTY); + todo_wine ok(hr == DISP_E_EXCEPTION, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + ok_exception(hr, szPropertyException); + + /* Unsupported pids */ + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_DICTIONARY, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_THUMBNAIL, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + /* Pids we have not set, one for each type */ + _invoke_todo_vtResult = 1; + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CODEPAGE, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_TITLE, &varresult, VT_EMPTY); + todo_wine ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_EDITTIME, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + hr = SummaryInfo_PropertyGet(pSummaryInfo, PID_CHARCOUNT, &varresult, VT_EMPTY); + ok(hr == S_OK, "SummaryInfo_PropertyGet failed, hresult 0x%08x\n", hr); + + _invoke_todo_vtResult = 0; +} + static void test_Database(IDispatch *pDatabase) { static WCHAR szSql[] = { 'S','E','L','E','C','T',' ','`','F','e','a','t','u','r','e','`',' ','F','R','O','M',' ','`','F','e','a','t','u','r','e','`',' ','W','H','E','R','E',' ','`','F','e','a','t','u','r','e','_','P','a','r','e','n','t','`','=','\'','O','n','e','\'',0 }; @@ -1202,7 +1351,7 @@ static void test_Database(IDispatch *pDatabase) static WCHAR szTwo[] = { 'T','w','o',0 }; static WCHAR szStringDataField[] = { 'S','t','r','i','n','g','D','a','t','a',',','F','i','e','l','d',0 }; static WCHAR szModifyModeRecord[] = { 'M','o','d','i','f','y',',','M','o','d','e',',','R','e','c','o','r','d',0 }; - IDispatch *pView = NULL; + IDispatch *pView = NULL, *pSummaryInfo = NULL; HRESULT hr; hr = Database_OpenView(pDatabase, szSql, &pView); @@ -1298,6 +1447,16 @@ static void test_Database(IDispatch *pDatabase) IDispatch_Release(pView); } + + /* Database::SummaryInformation */ + hr = Database_SummaryInformation(pDatabase, 0, &pSummaryInfo); + ok(hr == S_OK, "Database_SummaryInformation failed, hresult 0x%08x\n", hr); + ok(pSummaryInfo != NULL, "Database_SummaryInformation should not have returned NULL record\n"); + if (pSummaryInfo) + { + test_SummaryInfo(pSummaryInfo, summary_info, sizeof(summary_info)/sizeof(msi_summary_info)); + IDispatch_Release(pSummaryInfo); + } } static void test_Session(IDispatch *pSession) @@ -2032,7 +2191,8 @@ static void test_Installer(void) } /* Prepare package */ - create_database(msifile, tables, sizeof(tables) / sizeof(msi_table)); + create_database(msifile, tables, sizeof(tables) / sizeof(msi_table), + summary_info, sizeof(summary_info) / sizeof(msi_summary_info)); len = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, CURR_DIR, -1, szPath, MAX_PATH); ok(len, "MultiByteToWideChar returned error %d\n", GetLastError()); @@ -2107,6 +2267,8 @@ START_TEST(automation) CLSID clsid; IUnknown *pUnk; + GetSystemTimeAsFileTime(&systemtime); + GetCurrentDirectoryA(MAX_PATH, prev_path); GetTempPath(MAX_PATH, temp_path); SetCurrentDirectoryA(temp_path);