[KERNEL32]
[reactos.git] / rostests / winetests / kernel32 / console.c
index aacae9c..3d8ae93 100755 (executable)
@@ -24,6 +24,8 @@
 #include <stdio.h>
 
 static BOOL (WINAPI *pGetConsoleInputExeNameA)(DWORD, LPSTR);
+static DWORD (WINAPI *pGetConsoleProcessList)(LPDWORD, DWORD);
+static HANDLE (WINAPI *pOpenConsoleW)(LPCWSTR,DWORD,BOOL,DWORD);
 static BOOL (WINAPI *pSetConsoleInputExeNameA)(LPCSTR);
 
 /* DEFAULT_ATTRIB is used for all initial filling of the console.
@@ -63,6 +65,8 @@ static void init_function_pointers(void)
 
     hKernel32 = GetModuleHandleA("kernel32.dll");
     KERNEL32_GET_PROC(GetConsoleInputExeNameA);
+    KERNEL32_GET_PROC(GetConsoleProcessList);
+    KERNEL32_GET_PROC(OpenConsoleW);
     KERNEL32_GET_PROC(SetConsoleInputExeNameA);
 
 #undef KERNEL32_GET_PROC
@@ -130,7 +134,79 @@ static void testCursor(HANDLE hCon, COORD sbSize)
        ERROR_INVALID_PARAMETER, GetLastError());
 }
 
-static void testWriteSimple(HANDLE hCon, COORD sbSize)
+static void testCursorInfo(HANDLE hCon)
+{
+    BOOL ret;
+    CONSOLE_CURSOR_INFO info;
+
+    SetLastError(0xdeadbeef);
+    ret = GetConsoleCursorInfo(NULL, NULL);
+    ok(!ret, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError: expecting %u got %u\n",
+       ERROR_INVALID_HANDLE, GetLastError());
+
+    SetLastError(0xdeadbeef);
+    info.dwSize = -1;
+    ret = GetConsoleCursorInfo(NULL, &info);
+    ok(!ret, "Expected failure\n");
+    ok(info.dwSize == -1, "Expected no change for dwSize\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError: expecting %u got %u\n",
+       ERROR_INVALID_HANDLE, GetLastError());
+
+    /* Test the correct call first to distinguish between win9x and the rest */
+    SetLastError(0xdeadbeef);
+    ret = GetConsoleCursorInfo(hCon, &info);
+    ok(ret, "Expected success\n");
+    ok(info.dwSize == 25 ||
+       info.dwSize == 12 /* win9x */,
+       "Expected 12 or 25, got %d\n", info.dwSize);
+    ok(info.bVisible, "Expected the cursor to be visible\n");
+    ok(GetLastError() == 0xdeadbeef, "GetLastError: expecting %u got %u\n",
+       0xdeadbeef, GetLastError());
+
+    /* Don't test NULL CONSOLE_CURSOR_INFO, it crashes on win9x and win7 */
+}
+
+static void testEmptyWrite(HANDLE hCon)
+{
+    COORD              c;
+    DWORD              len;
+    const char*        mytest = "";
+
+    c.X = c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
+
+    len = -1;
+    ok(WriteConsole(hCon, NULL, 0, &len, NULL) != 0 && len == 0, "WriteConsole\n");
+    okCURSOR(hCon, c);
+
+    /* Passing a NULL lpBuffer with sufficiently large non-zero length succeeds
+     * on native Windows and result in memory-like contents being written to
+     * the console. Calling WriteConsoleW like this will crash on Wine. */
+    if (0)
+    {
+        len = -1;
+        ok(!WriteConsole(hCon, NULL, 16, &len, NULL) && len == -1, "WriteConsole\n");
+        okCURSOR(hCon, c);
+
+        /* Cursor advances for this call. */
+        len = -1;
+        ok(WriteConsole(hCon, NULL, 128, &len, NULL) != 0 && len == 128, "WriteConsole\n");
+    }
+
+    len = -1;
+    ok(WriteConsole(hCon, mytest, 0, &len, NULL) != 0 && len == 0, "WriteConsole\n");
+    okCURSOR(hCon, c);
+
+    /* WriteConsole does not halt on a null terminator and is happy to write
+     * memory contents beyond the actual size of the buffer. */
+    len = -1;
+    ok(WriteConsole(hCon, mytest, 16, &len, NULL) != 0 && len == 16, "WriteConsole\n");
+    c.X += 16;
+    okCURSOR(hCon, c);
+}
+
+static void testWriteSimple(HANDLE hCon)
 {
     COORD              c;
     DWORD              len;
@@ -198,6 +274,7 @@ static void testWriteNotWrappedProcessed(HANDLE hCon, COORD sbSize)
     const int  mylen = strlen(mytest);
     const int  mylen2 = strchr(mytest, '\n') - mytest;
     int                        p;
+    WORD                attr;
 
     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, (mode | ENABLE_PROCESSED_OUTPUT) & ~ENABLE_WRAP_AT_EOL_OUTPUT),
        "clearing wrap at EOL & setting processed output\n");
@@ -212,6 +289,15 @@ static void testWriteNotWrappedProcessed(HANDLE hCon, COORD sbSize)
     {
         okCHAR(hCon, c, mytest[c.X - sbSize.X + 5], TEST_ATTRIB);
     }
+
+    ReadConsoleOutputAttribute(hCon, &attr, 1, c, &len);
+    /* Win9x and WinMe change the attribs for '\n' up to 'f' */
+    if (attr == TEST_ATTRIB)
+    {
+        win_skip("Win9x/WinMe don't respect ~ENABLE_WRAP_AT_EOL_OUTPUT\n");
+        return;
+    }
+
     okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
 
     c.X = 0; c.Y++;
@@ -309,6 +395,7 @@ static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
     const char*                mytest = "abcd\nf\tg";
     const int  mylen = strlen(mytest);
     int                        p;
+    WORD                attr;
 
     ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode | (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)),
        "setting wrap at EOL & processed output\n");
@@ -324,7 +411,11 @@ static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
         okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
     }
     c.X = sbSize.X - 9 + p;
-    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+    ReadConsoleOutputAttribute(hCon, &attr, 1, c, &len);
+    if (attr == TEST_ATTRIB)
+        win_skip("Win9x/WinMe changes attribs for '\\n' up to 'f'\n");
+    else
+        okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
     c.X = 0; c.Y++;
     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
     for (c.X = 1; c.X < 8; c.X++)
@@ -347,7 +438,11 @@ static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
     c.X = 0; c.Y++;
     okCHAR(hCon, c, mytest[3], TEST_ATTRIB);
     c.X++;
-    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+    ReadConsoleOutputAttribute(hCon, &attr, 1, c, &len);
+    if (attr == TEST_ATTRIB)
+        win_skip("Win9x/WinMe changes attribs for '\\n' up to 'f'\n");
+    else
+        okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
 
     c.X = 0; c.Y++;
     okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
@@ -364,7 +459,9 @@ static void testWrite(HANDLE hCon, COORD sbSize)
     /* FIXME: should in fact insure that the sb is at least 10 character wide */
     ok(SetConsoleTextAttribute(hCon, TEST_ATTRIB), "Setting default text color\n");
     resetContent(hCon, sbSize, FALSE);
-    testWriteSimple(hCon, sbSize);
+    testEmptyWrite(hCon);
+    resetContent(hCon, sbSize, FALSE);
+    testWriteSimple(hCon);
     resetContent(hCon, sbSize, FALSE);
     testWriteNotWrappedNotProcessed(hCon, sbSize);
     resetContent(hCon, sbSize, FALSE);
@@ -622,7 +719,7 @@ static void testScreenBuffer(HANDLE hConOut)
     ret = SetConsoleOutputCP(866);
     if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
     {
-        skip("SetConsoleOutputCP is not implemented\n");
+        win_skip("SetConsoleOutputCP is not implemented\n");
         return;
     }
     ok(ret, "Cannot set output codepage to 866\n");
@@ -833,6 +930,159 @@ static void test_GetSetConsoleInputExeName(void)
     ok(!lstrcmpA(buffer, input_exe), "got %s expected %s\n", buffer, input_exe);
 }
 
+static void test_GetConsoleProcessList(void)
+{
+    DWORD ret, *list = NULL;
+
+    if (!pGetConsoleProcessList)
+    {
+        win_skip("GetConsoleProcessList is not available\n");
+        return;
+    }
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleProcessList(NULL, 0);
+    ok(ret == 0, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n",
+       GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleProcessList(NULL, 1);
+    ok(ret == 0, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n",
+       GetLastError());
+
+    /* We should only have 1 process but only for these specific unit tests as
+     * we created our own console. An AttachConsole(ATTACH_PARENT_PROCESS) would
+     * give us two processes for example.
+     */
+    list = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD));
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleProcessList(list, 0);
+    ok(ret == 0, "Expected failure\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "Expected ERROR_INVALID_PARAMETER, got %d\n",
+       GetLastError());
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleProcessList(list, 1);
+    todo_wine
+    ok(ret == 1, "Expected 1, got %d\n", ret);
+
+    HeapFree(GetProcessHeap(), 0, list);
+
+    list = HeapAlloc(GetProcessHeap(), 0, ret * sizeof(DWORD));
+
+    SetLastError(0xdeadbeef);
+    ret = pGetConsoleProcessList(list, ret);
+    todo_wine
+    ok(ret == 1, "Expected 1, got %d\n", ret);
+
+    if (ret == 1)
+    {
+        DWORD pid = GetCurrentProcessId();
+        ok(list[0] == pid, "Expected %d, got %d\n", pid, list[0]);
+    }
+
+    HeapFree(GetProcessHeap(), 0, list);
+}
+
+static void test_OpenConsoleW(void)
+{
+    static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
+    static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
+    static const WCHAR emptyW[] = {0};
+    static const WCHAR invalidW[] = {'I','N','V','A','L','I','D',0};
+
+    static const struct
+    {
+        LPCWSTR name;
+        DWORD access;
+        BOOL inherit;
+        DWORD creation;
+        DWORD gle;
+    } invalid_table[] = {
+        {NULL,     0,                            FALSE,      0,                 ERROR_INVALID_PARAMETER},
+        {NULL,     0xdeadbeef,                   0xdeadbeef, 0xdeadbeef,        ERROR_INVALID_PARAMETER},
+        {NULL,     0,                            FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {NULL,     GENERIC_READ | GENERIC_WRITE, FALSE,      0,                 ERROR_INVALID_PARAMETER},
+        {NULL,     GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {NULL,     GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_EXISTING,     ERROR_INVALID_PARAMETER},
+        {emptyW,   0,                            FALSE,      0,                 ERROR_INVALID_PARAMETER},
+        {emptyW,   0xdeadbeef,                   0xdeadbeef, 0xdeadbeef,        ERROR_INVALID_PARAMETER},
+        {emptyW,   0,                            FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {emptyW,   GENERIC_READ | GENERIC_WRITE, FALSE,      0,                 ERROR_INVALID_PARAMETER},
+        {emptyW,   GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {emptyW,   GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_EXISTING,     ERROR_INVALID_PARAMETER},
+        {invalidW, 0,                            FALSE,      0,                 ERROR_INVALID_PARAMETER},
+        {invalidW, 0xdeadbeef,                   0xdeadbeef, 0xdeadbeef,        ERROR_INVALID_PARAMETER},
+        {invalidW, 0,                            FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {invalidW, GENERIC_READ | GENERIC_WRITE, FALSE,      0,                 ERROR_INVALID_PARAMETER},
+        {invalidW, GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {invalidW, GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_EXISTING,     ERROR_INVALID_PARAMETER},
+        {coninW,   0,                            FALSE,      0,                 ERROR_SHARING_VIOLATION},
+        {coninW,   0xdeadbeef,                   0xdeadbeef, 0xdeadbeef,        ERROR_INVALID_PARAMETER},
+        {coninW,   0,                            FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {coninW,   GENERIC_READ | GENERIC_WRITE, FALSE,      0,                 ERROR_SHARING_VIOLATION},
+        {coninW,   GENERIC_READ | GENERIC_WRITE, FALSE,      CREATE_NEW,        ERROR_SHARING_VIOLATION},
+        {coninW,   GENERIC_READ | GENERIC_WRITE, FALSE,      CREATE_ALWAYS,     ERROR_SHARING_VIOLATION},
+        {coninW,   GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {coninW,   GENERIC_READ | GENERIC_WRITE, FALSE,      TRUNCATE_EXISTING, ERROR_INVALID_PARAMETER},
+        {conoutW,  0,                            FALSE,      0,                 ERROR_SHARING_VIOLATION},
+        {conoutW,  0xdeadbeef,                   0xdeadbeef, 0xdeadbeef,        ERROR_INVALID_PARAMETER},
+        {conoutW,  0,                            FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {conoutW,  GENERIC_READ | GENERIC_WRITE, FALSE,      0,                 ERROR_SHARING_VIOLATION},
+        {conoutW,  GENERIC_READ | GENERIC_WRITE, FALSE,      CREATE_NEW,        ERROR_SHARING_VIOLATION},
+        {conoutW,  GENERIC_READ | GENERIC_WRITE, FALSE,      CREATE_ALWAYS,     ERROR_SHARING_VIOLATION},
+        {conoutW,  GENERIC_READ | GENERIC_WRITE, FALSE,      OPEN_ALWAYS,       ERROR_INVALID_PARAMETER},
+        {conoutW,  GENERIC_READ | GENERIC_WRITE, FALSE,      TRUNCATE_EXISTING, ERROR_INVALID_PARAMETER},
+    };
+
+    int index;
+    HANDLE ret;
+
+    if (!pOpenConsoleW)
+    {
+        win_skip("OpenConsoleW is not available\n");
+        return;
+    }
+
+    for (index = 0; index < sizeof(invalid_table)/sizeof(invalid_table[0]); index++)
+    {
+        SetLastError(0xdeadbeef);
+        ret = pOpenConsoleW(invalid_table[index].name, invalid_table[index].access,
+                            invalid_table[index].inherit, invalid_table[index].creation);
+        ok(ret == INVALID_HANDLE_VALUE,
+           "Expected OpenConsoleW to return INVALID_HANDLE_VALUE for index %d, got %p\n",
+           index, ret);
+        ok(GetLastError() == invalid_table[index].gle,
+           "Expected GetLastError() to return %u for index %d, got %u\n",
+           invalid_table[index].gle, index, GetLastError());
+    }
+
+    /* OpenConsoleW should not touch the last error on success. */
+    SetLastError(0xdeadbeef);
+    ret = pOpenConsoleW(coninW, GENERIC_READ | GENERIC_WRITE, FALSE, OPEN_EXISTING);
+    ok(ret != INVALID_HANDLE_VALUE,
+       "Expected OpenConsoleW to return a valid handle\n");
+    ok(GetLastError() == 0xdeadbeef,
+       "Expected the last error to be untouched, got %u\n", GetLastError());
+    if (ret != INVALID_HANDLE_VALUE)
+        CloseHandle(ret);
+
+    SetLastError(0xdeadbeef);
+    ret = pOpenConsoleW(conoutW, GENERIC_READ | GENERIC_WRITE, FALSE, OPEN_EXISTING);
+    ok(ret != INVALID_HANDLE_VALUE,
+       "Expected OpenConsoleW to return a valid handle\n");
+    ok(GetLastError() == 0xdeadbeef,
+       "Expected the last error to be untouched, got %u\n", GetLastError());
+    if (ret != INVALID_HANDLE_VALUE)
+        CloseHandle(ret);
+}
+
 START_TEST(console)
 {
     HANDLE hConIn, hConOut;
@@ -858,11 +1108,14 @@ START_TEST(console)
     ok(hConIn != INVALID_HANDLE_VALUE, "Opening ConIn\n");
     ok(hConOut != INVALID_HANDLE_VALUE, "Opening ConOut\n");
 
-    ok(ret = GetConsoleScreenBufferInfo(hConOut, &sbi), "Getting sb info\n");
+    ret = GetConsoleScreenBufferInfo(hConOut, &sbi);
+    ok(ret, "Getting sb info\n");
     if (!ret) return;
 
     /* Non interactive tests */
     testCursor(hConOut, sbi.dwSize);
+    /* test parameters (FIXME: test functionality) */
+    testCursorInfo(hConOut);
     /* will test wrapped (on/off) & processed (on/off) strings output */
     testWrite(hConOut, sbi.dwSize);
     /* will test line scrolling at the bottom of the screen */
@@ -875,10 +1128,10 @@ START_TEST(console)
     /* still to be done: access rights & access on objects */
 
     if (!pGetConsoleInputExeNameA || !pSetConsoleInputExeNameA)
-    {
-        skip("GetConsoleInputExeNameA and/or SetConsoleInputExeNameA is not available\n");
-        return;
-    }
+        win_skip("GetConsoleInputExeNameA and/or SetConsoleInputExeNameA is not available\n");
     else
         test_GetSetConsoleInputExeName();
+
+    test_GetConsoleProcessList();
+    test_OpenConsoleW();
 }