commit d6f4c73ceb4640332c79561b4e8e7c23619aa669
Author: Francois Gouget <fgouget@free.fr>
Date:   Mon Sep 30 19:26:50 2013 +0200

    kernel32/tests: Serial port trigger-level test patch.
    
    This is just an exploration patch and is not meant to be committed.
    See the following for reference:
    http://www.tldp.org/HOWTO/Serial-HOWTO-18.html
    http://www.winehq.org/pipermail/wine-devel/2013-September/101160.html
    http://www.winehq.org/pipermail/wine-devel/2013-September/101490.html

diff --git a/dlls/kernel32/tests/comm.c b/dlls/kernel32/tests/comm.c
index 7ec5fc5..60c8f0a 100644
--- a/dlls/kernel32/tests/comm.c
+++ b/dlls/kernel32/tests/comm.c
@@ -706,8 +706,6 @@ static HANDLE test_OpenComm(BOOL doOverlap)
     {
 	if (hcom == INVALID_HANDLE_VALUE)
 	    trace("Could not find a valid COM port.\n");
-	else
-	    trace("Found Com port %s. Connected devices may disturb results\n", port_name);
 	/*shown = TRUE; */
     }
     if (hcom != INVALID_HANDLE_VALUE)
@@ -989,6 +987,120 @@ todo_wine
     }
 }
 
+
+static void test_triggerlevel(void)
+{
+    HANDLE hcom;
+    DCB dcb;
+    COMMTIMEOUTS timeouts;
+    char tbuf[65536];
+    DWORD before, after, bytes, timediff, evtmask, i;
+    BOOL res;
+    DWORD baud = SLOWBAUD;
+    OVERLAPPED ovl_write, ovl_wait;
+
+    for (i = 1; i < sizeof(tbuf); i+= (i < 50 ? 1 : i == 50 ? 950 : i))
+    {
+        hcom = test_OpenComm(TRUE);
+        if (hcom == INVALID_HANDLE_VALUE) return;
+
+        /* set a low baud rate to have ample time */
+        res = GetCommState(hcom, &dcb);
+        ok(res, "GetCommState error %d\n", GetLastError());
+        dcb.BaudRate = baud;
+        dcb.ByteSize = 8;
+        dcb.Parity = NOPARITY;
+        dcb.fRtsControl=RTS_CONTROL_ENABLE;
+        dcb.fDtrControl=DTR_CONTROL_ENABLE;
+        dcb.StopBits = ONESTOPBIT;
+        res = SetCommState(hcom, &dcb);
+        ok(res, "SetCommState error %d\n", GetLastError());
+        ZeroMemory(&dcb, sizeof(dcb));
+        res = GetCommState(hcom, &dcb);
+        ok(dcb.BaudRate == 150, "BaudRate=%d\n", dcb.BaudRate);
+        ok(dcb.ByteSize == 8, "ByteSize=%d\n", dcb.ByteSize);
+        ok(dcb.Parity == NOPARITY, "Parity=%d\n", dcb.Parity);
+        ok(dcb.StopBits == ONESTOPBIT, "StopBits=%d\n", dcb.StopBits);
+
+        ZeroMemory( &timeouts, sizeof(timeouts));
+        timeouts.ReadTotalTimeoutConstant = TIMEOUT;
+        res = SetCommTimeouts(hcom, &timeouts);
+        ok(res,"SetCommTimeouts error %d\n", GetLastError());
+
+        res = SetupComm(hcom, 1024, 1024);
+        ok(res, "SetUpComm error %d\n", GetLastError());
+
+        /* calling SetCommMask after WriteFile leads to WaitCommEvent failures
+         * due to timeout (no events) under testbot VMs and VirtualBox
+         */
+        res = SetCommMask(hcom, EV_TXEMPTY);
+        ok(res, "SetCommMask error %d\n", GetLastError());
+
+        S(U(ovl_write)).Offset = 0;
+        S(U(ovl_write)).OffsetHigh = 0;
+        ovl_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+        before = GetTickCount();
+        SetLastError(0xdeadbeef);
+        res = WriteFile(hcom, tbuf, i, &bytes, &ovl_write);
+        after = GetTickCount();
+        todo_wine
+            ok(!res && GetLastError() == ERROR_IO_PENDING, "WriteFile returned %d, error %d\n", res, GetLastError());
+        todo_wine
+            ok(!bytes, "expected 0, got %u\n", bytes);
+        ok(after - before < 30, "WriteFile took %d ms to write %d Bytes at %d Baud\n",
+           after - before, bytes, baud);
+        /* don't wait for WriteFile completion */
+
+        S(U(ovl_wait)).Offset = 0;
+        S(U(ovl_wait)).OffsetHigh = 0;
+        ovl_wait.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+        evtmask = 0;
+        before = GetTickCount();
+        SetLastError(0xdeadbeef);
+        res = WaitCommEvent(hcom, &evtmask, &ovl_wait);
+        ok(!res && GetLastError() == ERROR_IO_PENDING, "WaitCommEvent error %d\n", GetLastError());
+
+        res = WaitForSingleObject(ovl_wait.hEvent, 4000);
+        ok(res == WAIT_OBJECT_0, "WaitCommEvent failed with a timeout\n");
+        if (res == WAIT_OBJECT_0)
+        {
+            res = GetOverlappedResult(hcom, &ovl_wait, &bytes, FALSE);
+            ok(res, "GetOverlappedResult reported error %d\n", GetLastError());
+            ok(bytes == sizeof(evtmask), "expected %u, written %u\n", (UINT)sizeof(evtmask), bytes);
+            res = TRUE;
+        }
+        else
+        {
+            /* unblock pending wait */
+            trace("recovering after WAIT_TIMEOUT...\n");
+            res = SetCommMask(hcom, EV_TXEMPTY);
+            ok(res, "SetCommMask error %d\n", GetLastError());
+
+            res = WaitForSingleObject(ovl_wait.hEvent, 4000);
+            ok(res == WAIT_OBJECT_0, "WaitCommEvent failed with a timeout\n");
+
+            res = FALSE;
+            Sleep(3000);
+        }
+        after = GetTickCount();
+        ok(res, "WaitCommEvent error %d\n", GetLastError());
+        ok(evtmask & EV_TXEMPTY, "WaitCommEvent: expected EV_TXEMPTY, got %#x\n", evtmask);
+        CloseHandle(ovl_wait.hEvent);
+
+        timediff = after - before;
+        trace("%d bytes allegedly sent in %d ms, %.0fbps\n", i, timediff, ((double)i) * 10.0 * 1000.0 / (timediff ? timediff : 1));
+
+        res = WaitForSingleObject(ovl_write.hEvent, 0);
+        ok(res == WAIT_OBJECT_0, "WriteFile failed with a timeout\n");
+        res = GetOverlappedResult(hcom, &ovl_write, &bytes, FALSE);
+        ok(res, "GetOverlappedResult reported error %d\n", GetLastError());
+        ok(bytes == i, "expected %u, written %u\n", (UINT)i, bytes);
+        CloseHandle(ovl_write.hEvent);
+        CloseHandle(hcom);
+        Sleep(1000);
+    }
+}
+
 /* A new open handle should not return error or have bytes in the Queues */
 static void test_ClearCommError(void)
 {
@@ -2225,6 +2337,7 @@ START_TEST(comm)
     test_WaitBreak();
     test_stdio();
     test_read_write();
+    test_triggerlevel();
 
     if (!winetest_interactive)
     {
