From 3dfc251a2f2500f7b20b3b0c62c9d435a00b1900 Mon Sep 17 00:00:00 2001 From: Mikey Alexander Date: Mon, 26 Oct 2009 14:07:04 -0800 Subject: Adding Program Manager DDE Conformance Tests --- dlls/shell32/tests/progman_dde.c | 561 ++++++++++++++++++++++++++++++++++++++ 1 files changed, 561 insertions(+), 0 deletions(-) create mode 100644 dlls/shell32/tests/progman_dde.c diff --git a/dlls/shell32/tests/progman_dde.c b/dlls/shell32/tests/progman_dde.c new file mode 100644 index 0000000..507540e --- /dev/null +++ b/dlls/shell32/tests/progman_dde.c @@ -0,0 +1,561 @@ +/* + * Unit test of the Program Manager DDE Interfaces + * + * Copyright 2009 Mikey Alexander + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* Basic DDE Program Manager Tests + * - Covers basic CreateGroup, ShowGroup, DeleteGroup, AddItem, and DeleteItem + * functionality + * - Todo: Handle CommonGroupFlag + * Handle Difference between administrator and non-administrator calls + * as documented + * Better AddItem Tests (Lots of parameters to test) + * Tests for Invalid Characters in Names / Invalid Parameters + * + * - todo_wine current per stubbed implementation of dde.c in shell32. + * - original testing under WinXP SP2 (virtualbox) and Win98 (qemu) + * + * - october 2009 Mikey Alexander + */ + +#include +#include +#include +#include "dde.h" +#include "ddeml.h" +#include "winuser.h" +#include "shlobj.h" + +/* Timeout on DdeClientTransaction Call */ +#define MS_TIMEOUT_VAL 1000 +/* # of times to poll for window creation */ +#define PDDE_POLL_NUM 50 +/* time to sleep between polls */ +#define PDDE_POLL_TIME 200 + +#define TRUE 1 +#define FALSE 0 + +/* Call Info */ +#define DDE_TEST_MISC 0x00010000 +#define DDE_TEST_CREATEGROUP 0x00020000 +#define DDE_TEST_DELETEGROUP 0x00030000 +#define DDE_TEST_SHOWGROUP 0x00040000 +#define DDE_TEST_ADDITEM 0x00050000 +#define DDE_TEST_DELETEITEM 0x00060000 +#define DDE_TEST_COMPOUND 0x00070000 +#define DDE_TEST_CALLMASK 0x00ff0000 + +/* Type of Test (Common, Individual) */ +#define DDE_TEST_COMMON 0x01000000 +#define DDE_TEST_INDIVIDUAL 0x02000000 + +#define DDE_TEST_NUMMASK 0x0000ffff + +HDDEDATA CALLBACK DdeCallback(UINT type, + UINT format, + HCONV hConv, + HSZ hsz1, + HSZ hsz2, + HDDEDATA hDDEData, + ULONG_PTR data1, + ULONG_PTR data2) +{ + trace("Callback: type=%i, format=%i\n", type, format); + return NULL; +} + +/* Returns 0 if not Windows 9x (95, 98, ME) */ +int IsWin9x(void) +{ + return (GetVersion() & 0x80000000); +} + +char * GetStringFromTestParams(int testParams) +{ + int testNum; + static char testParamString[64]; + char *callId; + + testNum = testParams & DDE_TEST_NUMMASK; + switch (testParams & DDE_TEST_CALLMASK) + { + default: + case DDE_TEST_MISC: + callId = (char *) "MISC"; + break; + case DDE_TEST_CREATEGROUP: + callId = (char *) "C_G"; + break; + case DDE_TEST_DELETEGROUP: + callId = (char *) "D_G"; + break; + case DDE_TEST_SHOWGROUP: + callId = (char *) "S_G"; + break; + case DDE_TEST_ADDITEM: + callId = (char *) "A_I"; + break; + case DDE_TEST_DELETEITEM: + callId = (char *) "D_I"; + break; + case DDE_TEST_COMPOUND: + callId = (char *) "CPD"; + break; + } + + sprintf(testParamString, " [%s:%i]", callId, testNum); + return (char *) testParamString; +} + +/* Transfer DMLERR's into text readable strings for Error Messages */ +const char * GetStringFromError(UINT err) +{ + const char * retstr; + switch (err) + { + case DMLERR_NO_ERROR: + retstr = "DMLERR_NO_ERROR"; + break; + case DMLERR_ADVACKTIMEOUT: + retstr = "DMLERR_ADVACKTIMEOUT"; + break; + case DMLERR_BUSY: + retstr = "DMLERR_BUSY"; + break; + case DMLERR_DATAACKTIMEOUT: + retstr = "DMLERR_DATAACKTIMEOUT"; + break; + case DMLERR_DLL_NOT_INITIALIZED: + retstr = "DMLERR_DLL_NOT_INITIALIZED"; + break; + case DMLERR_DLL_USAGE: + retstr = "DMLERR_DLL_USAGE"; + break; + case DMLERR_EXECACKTIMEOUT: + retstr = "DMLERR_EXECACKTIMOUT"; + break; + case DMLERR_INVALIDPARAMETER: + retstr = "DMLERR_INVALIDPARAMETER"; + break; + case DMLERR_LOW_MEMORY: + retstr = "DMLERR_LOW_MEMORY"; + break; + case DMLERR_MEMORY_ERROR: + retstr = "DMLERR_MEMORY_ERROR"; + break; + case DMLERR_NOTPROCESSED: + retstr = "DMLERR_NOTPROCESSED"; + break; + case DMLERR_NO_CONV_ESTABLISHED: + retstr = "DMLERR_NO_CONV_ESTABLISHED"; + break; + case DMLERR_POKEACKTIMEOUT: + retstr = "DMLERR_POKEACKTIMEOUT"; + break; + case DMLERR_POSTMSG_FAILED: + retstr = "DMLERR_POSTMSG_FAILED"; + break; + case DMLERR_REENTRANCY: + retstr = "DMLERR_REENTRANCY"; + break; + case DMLERR_SERVER_DIED: + retstr = "DMLERR_SERVER_DIED"; + break; + case DMLERR_SYS_ERROR: + retstr = "DMLERR_SYS_ERROR"; + break; + case DMLERR_UNADVACKTIMEOUT: + retstr = "DMLERR_UNADVACKTIMEOUT"; + break; + case DMLERR_UNFOUND_QUEUE_ID: + retstr = "DMLERR_UNFOUND_QUEUE_ID"; + break; + default: + retstr = "Unknown DML Error"; + break; + } + + return retstr; +} + +/* Helper Function to Transfer DdeGetLastError into a String */ +const char * GetDdeLastErrorStr(DWORD instance) +{ + UINT err = DdeGetLastError(instance); + + return GetStringFromError(err); +} + +/* Execute a Dde Command and return the error & result */ +/* Note: Progman DDE always returns a pointer to 0x00000001 on a successful result */ +void DdeExecuteCommand(DWORD instance, HCONV hConv, char *strCmd, HDDEDATA *hData, UINT *err, int testParams) +{ + HDDEDATA command; + + command = DdeCreateDataHandle(instance, (LPBYTE) strCmd, strlen(strCmd)+1, 0, 0L, 0, 0); + ok (command != NULL, "DdeCreateDataHandle Error %s.%s\n", GetDdeLastErrorStr(instance), GetStringFromTestParams(testParams)); + *hData = DdeClientTransaction((void FAR *) command, + -1, + hConv, + 0L, + 0, + XTYP_EXECUTE, + MS_TIMEOUT_VAL, + NULL); + + /* hData is technically a pointer, but for Program Manager, + * it is NULL (error) or 1L (success) + * TODO: Check other versions of Windows to verify 1 is returned. + * While it is unlikely that anyone is actually testing that the result is 1 + * if all versions of windows return 1, Wine should also. + */ + if (*hData == NULL) + { + *err = DdeGetLastError(instance); + } + else + { + *err = DMLERR_NO_ERROR; + todo_wine + { + ok(*hData == (HDDEDATA) 1L, "Expected HDDEDATA Handle == 1L, actually %x.%s\n", (UINT) *hData, GetStringFromTestParams(testParams)); + } + } +} + +/* Check if Window is onscreen with the appropriate name. + * + * Windows are not created syncronously. So we do not know + * when and if the window will be created/shown on screen. + * This function implements a polling mechanism to determine + * creation. + * A more complicated method would be to use SetWindowsHookEx. + * Since polling worked fine in my testing, no reason to implement + * the other. Comments about other methods of determining when + * window creation happened were not encouraging. + */ +void CheckWindowCreated(char *winName, int closeWindow, int testParams) +{ + HWND window = NULL; + int i; + + /* Poll for Window Creation */ + for (i = 0; window == NULL && i < PDDE_POLL_NUM; i++) + { + Sleep(PDDE_POLL_TIME); + window = FindWindow(NULL, winName); + } + ok (window != NULL, "Window \"%s\" was not created.%s\n", winName, GetStringFromTestParams(testParams)); + + /* Close Window as desired. */ + if (window != NULL && closeWindow) + { + SendMessage(window, WM_SYSCOMMAND, SC_CLOSE, 0); + } +} + +/* Check for Existence (or not existence) */ +void CheckFileExistsInProgramGroups(char *fileName, int shouldExist, int isGroup, char *groupName, int testParams) +{ + char *path; + int err; + DWORD attributes; + int len; + + path = (char *) malloc(MAX_PATH); + if (path != NULL) + { + int specialFolder; + + /* Win 9x doesn't support common */ + if ((testParams & DDE_TEST_COMMON) && !IsWin9x()) + { + specialFolder = CSIDL_COMMON_PROGRAMS; + } else { + specialFolder = CSIDL_PROGRAMS; + } + err = SHGetSpecialFolderPath(NULL, path, specialFolder, FALSE); + len = strlen(path) + strlen(fileName)+1; + if (groupName != NULL) + { + len += strlen(groupName)+1; + } + ok (len <= MAX_PATH, "Path Too Long.%s\n", GetStringFromTestParams(testParams)); + if (len <= MAX_PATH) + { + if (groupName != NULL) + { + strcat(path, "\\"); + strcat(path, groupName); + } + strcat(path, "\\"); + strcat(path, fileName); + attributes = GetFileAttributes(path); + if (!shouldExist) + { + ok (attributes == INVALID_FILE_ATTRIBUTES , "File exists and shouldn't %s.%s\n", path, GetStringFromTestParams(testParams)); + } else { + if (attributes == INVALID_FILE_ATTRIBUTES) + { + ok (FALSE, "Created File %s doesn't exist.%s\n", path, GetStringFromTestParams(testParams)); + } else if (isGroup) { + ok (attributes & FILE_ATTRIBUTE_DIRECTORY, "%s is not a folder (attr=%x).%s\n", path, attributes, GetStringFromTestParams(testParams)); + } else { + ok (attributes & FILE_ATTRIBUTE_ARCHIVE, "Created File %s has wrong attributes (%x).%s\n", path, attributes, GetStringFromTestParams(testParams)); + } + } + } + free(path); + } +} + +/* Create Group Test. + * command and expected_result. + * if expected_result is DMLERR_NO_ERROR, test + * 1. group was created + * 2. window is open + */ +void CreateGroupTest(DWORD instance, HCONV hConv, char *command, UINT expected_result, char *groupName, int testParams) +{ + HDDEDATA hData; + UINT error; + + /* Execute Command & Check Result */ + DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams); + todo_wine + { + ok (expected_result == error, "CreateGroup %s: Expected Error %s, received %s.%s\n", groupName, GetStringFromError(expected_result), GetStringFromError(error), GetStringFromTestParams(testParams)); + } + + /* No Error */ + if (error == DMLERR_NO_ERROR) + { + + /* Check if Group Now Exists */ + CheckFileExistsInProgramGroups(groupName, TRUE, TRUE, NULL, testParams); + /* Check if Window is Open (polling) */ + CheckWindowCreated(groupName, TRUE, testParams); + } +} + +/* Show Group Test. + * command and expected_result. + * if expected_result is DMLERR_NO_ERROR, test + * 1. window is open + */ +void ShowGroupTest(DWORD instance, HCONV hConv, char *command, UINT expected_result, char *groupName, int closeAfterShowing, int testParams) +{ + HDDEDATA hData; + UINT error; + + DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams); +/* todo_wine... Is expected to fail, wine stubbed functions DO fail */ +/* TODO REMOVE THIS CODE!!! */ + if (expected_result == DMLERR_NOTPROCESSED) + { + ok (expected_result == error, "ShowGroup %s: Expected Error %s, received %s.%s\n", groupName, GetStringFromError(expected_result), GetStringFromError(error), GetStringFromTestParams(testParams)); + } else { + todo_wine + { + ok (expected_result == error, "ShowGroup %s: Expected Error %s, received %s.%s\n", groupName, GetStringFromError(expected_result), GetStringFromError(error), GetStringFromTestParams(testParams)); + } + } + + if (error == DMLERR_NO_ERROR) + { + /* Check if Window is Open (polling) */ + CheckWindowCreated(groupName, closeAfterShowing, testParams); + } +} + +/* Create Group Test. + * command and expected_result. + * if expected_result is DMLERR_NO_ERROR, test + * 1. group does not exist + */ +void DeleteGroupTest(DWORD instance, HCONV hConv, char *command, UINT expected_result, char *groupName, int testParams) +{ + HDDEDATA hData; + UINT error; + + DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams); + todo_wine + { + ok (expected_result == error, "DeleteGroup %s: Expected Error %s, received %s.%s\n", groupName, GetStringFromError(expected_result), GetStringFromError(error), GetStringFromTestParams(testParams)); + } + + if (error == DMLERR_NO_ERROR) + { + /* Check that Group does not exist */ + CheckFileExistsInProgramGroups(groupName, FALSE, TRUE, NULL, testParams); + } +} + +void AddItemTest(DWORD instance, HCONV hConv, char *command, UINT expected_result, char *fileName, char *groupName, int testParams) +{ + HDDEDATA hData; + UINT error; + + DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams); + todo_wine + { + ok (expected_result == error, "AddItem %s: Expected Error %s, received %s.%s\n", fileName, GetStringFromError(expected_result), GetStringFromError(error), GetStringFromTestParams(testParams)); + } + + if (error == DMLERR_NO_ERROR) + { + /* Check that File exists */ + CheckFileExistsInProgramGroups(fileName, TRUE, FALSE, groupName, testParams); + } +} + +void DeleteItemTest(DWORD instance, HCONV hConv, char *command, UINT expected_result, char *fileName, char *groupName, int testParams) +{ + HDDEDATA hData; + UINT error; + + DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams); + todo_wine + { + ok (expected_result == error, "DeleteItem %s: Expected Error %s, received %s.%s\n", fileName, GetStringFromError(expected_result), GetStringFromError(error), GetStringFromTestParams(testParams)); + } + + if (error == DMLERR_NO_ERROR) + { + /* Check that File exists */ + CheckFileExistsInProgramGroups(fileName, FALSE, FALSE, groupName, testParams); + } +} + +void CompoundCommandTest(DWORD instance, HCONV hConv, char *command, UINT expected_result, char *groupName, char *fileName1, char *fileName2, int testParams) +{ + HDDEDATA hData; + UINT error; + + DdeExecuteCommand(instance, hConv, command, &hData, &error, testParams); + todo_wine + { + ok (expected_result == error, "Compound String %s: Expected Error %s, received %s.%s\n", command, GetStringFromError(expected_result), GetStringFromError(error), GetStringFromTestParams(testParams)); + } + + if (error == DMLERR_NO_ERROR) + { + /* Check that File exists */ + CheckFileExistsInProgramGroups(groupName, TRUE, TRUE, NULL, testParams); + CheckWindowCreated(groupName, FALSE, testParams); + CheckFileExistsInProgramGroups(fileName1, TRUE, FALSE, groupName, testParams); + CheckFileExistsInProgramGroups(fileName2, TRUE, FALSE, groupName, testParams); + } +} + +/* 1st set of tests */ +int DdeTestProgman(DWORD instance, HCONV hConv) +{ + HDDEDATA hData; + UINT error; + int testnum; + + testnum = 1; + /* Invalid Command */ + DdeExecuteCommand(instance, hConv, (char *) "[InvalidCommand()]", &hData, &error, DDE_TEST_MISC|testnum++); + ok (error == DMLERR_NOTPROCESSED, "InvalidCommand(), expected error %s, received %s.\n", GetStringFromError(DMLERR_NOTPROCESSED), GetStringFromError(error)); + + /* CreateGroup Tests (including AddItem, DeleteItem) */ + CreateGroupTest(instance, hConv, (char *) "[CreateGroup(Group1)]", DMLERR_NO_ERROR, (char *) "Group1", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++); + AddItemTest(instance, hConv, (char *) "[AddItem(c:\\f1g1,f1g1Name)]", DMLERR_NO_ERROR, (char *) "f1g1Name.lnk", (char *) "Group1", DDE_TEST_COMMON|DDE_TEST_ADDITEM|testnum++); + AddItemTest(instance, hConv, (char *) "[AddItem(c:\\f2g1,f2g1Name)]", DMLERR_NO_ERROR, (char *) "f2g1Name.lnk", (char *) "Group1", DDE_TEST_COMMON|DDE_TEST_ADDITEM|testnum++); + DeleteItemTest(instance, hConv, (char *) "[DeleteItem(f2g1Name)]", DMLERR_NO_ERROR, (char *) "f2g1Name.lnk", (char *) "Group1", DDE_TEST_COMMON|DDE_TEST_DELETEITEM|testnum++); + AddItemTest(instance, hConv, (char *) "[AddItem(c:\\f3g1,f3g1Name)]", DMLERR_NO_ERROR, (char *) "f3g1Name.lnk", (char *) "Group1", DDE_TEST_COMMON|DDE_TEST_ADDITEM|testnum++); + CreateGroupTest(instance, hConv, (char *) "[CreateGroup(Group2)]", DMLERR_NO_ERROR, (char *) "Group2", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++); + /* Create Group that already exists - same instance */ + CreateGroupTest(instance, hConv, (char *) "[CreateGroup(Group1)]", DMLERR_NO_ERROR, (char *) "Group1", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++); + + /* ShowGroup Tests */ + ShowGroupTest(instance, hConv, (char *) "[ShowGroup(Group1)]", DMLERR_NOTPROCESSED, (char *) "Startup", TRUE, DDE_TEST_SHOWGROUP|testnum++); + DeleteItemTest(instance, hConv, (char *) "[DeleteItem(f3g1Name)]", DMLERR_NO_ERROR, (char *) "f3g1Name.lnk", (char *) "Group1", DDE_TEST_COMMON|DDE_TEST_DELETEITEM|testnum++); + ShowGroupTest(instance, hConv, (char *) "[ShowGroup(Startup,0)]", DMLERR_NO_ERROR, (char *) "Startup", TRUE, DDE_TEST_SHOWGROUP|testnum++); + ShowGroupTest(instance, hConv, (char *) "[ShowGroup(Group1,0)]", DMLERR_NO_ERROR, (char *) "Group1", FALSE, DDE_TEST_SHOWGROUP|testnum++); + + /* DeleteGroup Test - Note that Window is Open for this test */ + DeleteGroupTest(instance, hConv, (char *) "[DeleteGroup(Group1)]", DMLERR_NO_ERROR, (char *) "Group1", DDE_TEST_DELETEGROUP|testnum++); + + /* Compound Execute String Command */ + CompoundCommandTest(instance, hConv, (char *) "[CreateGroup(Group3)][AddItem(c:\\f1g3,f1g3Name)][AddItem(c:\\f2g3,f2g3Name)]", DMLERR_NO_ERROR, (char *) "Group3", (char *) "f1g3Name.lnk", (char *) "f2g3Name.lnk", DDE_TEST_COMMON|DDE_TEST_COMPOUND|testnum++); + DeleteGroupTest(instance, hConv, (char *) "[DeleteGroup(Group3)]", DMLERR_NO_ERROR, (char *) "Group3", DDE_TEST_DELETEGROUP|testnum++); + + /* Full Parameters of Add Item */ + /* AddItem(CmdLine[,Name[,IconPath[,IconIndex[,xPos,yPos[,DefDir[,HotKey[,fMinimize[fSeparateSpace]]]]]]]) */ + + return testnum; +} + +/* 2nd set of tests */ +void DdeTestProgman2(DWORD instance, HCONV hConv, int testnum) +{ + /* Create Group that already exists */ + CreateGroupTest(instance, hConv, (char *) "[CreateGroup(Group2)]", DMLERR_NO_ERROR, (char *) "Group2", DDE_TEST_COMMON|DDE_TEST_CREATEGROUP|testnum++); + DeleteGroupTest(instance, hConv, (char *) "[DeleteGroup(Group2)]", DMLERR_NO_ERROR, (char *) "Group2", DDE_TEST_COMMON|DDE_TEST_DELETEGROUP|testnum++); +} + +START_TEST(progman_dde) +{ + DWORD instance = 0; + UINT err; + HSZ hszProgman; + HCONV hConv; + int testnum; + + /* Initialize DDE Instance */ + err = DdeInitialize(&instance, DdeCallback, APPCMD_CLIENTONLY, 0); + ok (err == DMLERR_NO_ERROR, "DdeInitialize Error %s\n", GetStringFromError(err)); + + /* Create Connection */ + hszProgman = DdeCreateStringHandle(instance, "PROGMAN", CP_WINANSI); + ok (hszProgman != NULL, "DdeCreateStringHandle Error %s\n", GetDdeLastErrorStr(instance)); + hConv = DdeConnect(instance, hszProgman, hszProgman, NULL); + ok (hConv != NULL, "DdeConnect Error %s\n", GetDdeLastErrorStr(instance)); + ok (DdeFreeStringHandle(instance, hszProgman), "DdeFreeStringHandle failure\n"); + + /* Run Tests */ + testnum = DdeTestProgman(instance, hConv); + + /* Cleanup & Exit */ + ok (DdeDisconnect(hConv), "DdeDisonnect Error %s\n", GetDdeLastErrorStr(instance)); + ok (DdeUninitialize(instance), "DdeUninitialize failed\n"); + + /* 2nd Instance (Followup Tests) */ + /* Initialize DDE Instance */ + instance = 0; + err = DdeInitialize(&instance, DdeCallback, APPCMD_CLIENTONLY, 0); + ok (err == DMLERR_NO_ERROR, "DdeInitialize Error %s\n", GetStringFromError(err)); + + /* Create Connection */ + hszProgman = DdeCreateStringHandle(instance, "PROGMAN", CP_WINANSI); + ok (hszProgman != NULL, "DdeCreateStringHandle Error %s\n", GetDdeLastErrorStr(instance)); + hConv = DdeConnect(instance, hszProgman, hszProgman, NULL); + ok (hConv != NULL, "DdeConnect Error %s\n", GetDdeLastErrorStr(instance)); + ok (DdeFreeStringHandle(instance, hszProgman), "DdeFreeStringHandle failure\n"); + + /* Run Tests */ + DdeTestProgman2(instance, hConv, testnum); + + /* Cleanup & Exit */ + ok (DdeDisconnect(hConv), "DdeDisonnect Error %s\n", GetDdeLastErrorStr(instance)); + ok (DdeUninitialize(instance), "DdeUninitialize failed\n"); +} -- 1.6.0.4