Added WINE kernel32 regression tests.
authorSteven Edwards <winehacker@gmail.com>
Sat, 14 Feb 2004 20:11:42 +0000 (20:11 +0000)
committerSteven Edwards <winehacker@gmail.com>
Sat, 14 Feb 2004 20:11:42 +0000 (20:11 +0000)
svn path=/trunk/; revision=8170

25 files changed:
reactos/apps/tests/kernel32/.cvsignore [new file with mode: 0644]
reactos/apps/tests/kernel32/Makefile [new file with mode: 0644]
reactos/apps/tests/kernel32/Makefile.in [new file with mode: 0644]
reactos/apps/tests/kernel32/alloc.c [new file with mode: 0644]
reactos/apps/tests/kernel32/atom.c [new file with mode: 0644]
reactos/apps/tests/kernel32/change.c [new file with mode: 0644]
reactos/apps/tests/kernel32/codepage.c [new file with mode: 0644]
reactos/apps/tests/kernel32/comm.c [new file with mode: 0644]
reactos/apps/tests/kernel32/console.c [new file with mode: 0644]
reactos/apps/tests/kernel32/directory.c [new file with mode: 0644]
reactos/apps/tests/kernel32/drive.c [new file with mode: 0644]
reactos/apps/tests/kernel32/environ.c [new file with mode: 0644]
reactos/apps/tests/kernel32/file.c [new file with mode: 0644]
reactos/apps/tests/kernel32/format_msg.c [new file with mode: 0644]
reactos/apps/tests/kernel32/generated.c [new file with mode: 0644]
reactos/apps/tests/kernel32/heap.c [new file with mode: 0644]
reactos/apps/tests/kernel32/locale.c [new file with mode: 0644]
reactos/apps/tests/kernel32/mailslot.c [new file with mode: 0644]
reactos/apps/tests/kernel32/path.c [new file with mode: 0644]
reactos/apps/tests/kernel32/pipe.c [new file with mode: 0644]
reactos/apps/tests/kernel32/process.c [new file with mode: 0644]
reactos/apps/tests/kernel32/profile.c [new file with mode: 0644]
reactos/apps/tests/kernel32/testlist.c [new file with mode: 0644]
reactos/apps/tests/kernel32/thread.c [new file with mode: 0644]
reactos/apps/tests/kernel32/virtual.c [new file with mode: 0644]

diff --git a/reactos/apps/tests/kernel32/.cvsignore b/reactos/apps/tests/kernel32/.cvsignore
new file mode 100644 (file)
index 0000000..d63774a
--- /dev/null
@@ -0,0 +1,6 @@
+*.o
+*.d
+*.exe
+*.coff
+*.sym
+*.map
diff --git a/reactos/apps/tests/kernel32/Makefile b/reactos/apps/tests/kernel32/Makefile
new file mode 100644 (file)
index 0000000..44f6f6e
--- /dev/null
@@ -0,0 +1,46 @@
+# $Id: Makefile,v 1.1 2004/02/14 20:11:42 sedwards Exp $
+
+PATH_TO_TOP = ../../..
+
+TARGET_NORC = yes
+
+TARGET_TYPE = program
+
+TARGET_APPTYPE = console
+
+# require os code to explicitly request A/W version of structs/functions
+TARGET_CFLAGS += -D_DISABLE_TIDENTS -D__USE_W32API
+
+TARGET_NAME = kernel32_test
+
+TARGET_SDKLIBS = kernel32.a
+
+TARGET_OBJECTS = \
+       testlist.o \
+       atom.o \
+       change.o \
+       codepage.o \
+       comm.o \
+       console.o \
+       directory.o \
+       drive.o \
+       environ.o \
+       file.o \
+       format_msg.o \
+       heap.o \
+       locale.o \
+       mailslot.o \
+       path.o \
+       pipe.o \
+       process.o \
+       profile.o \
+       thread.o \
+       virtual.o
+
+#FIXME alloc.o \
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
+
+# EOF
diff --git a/reactos/apps/tests/kernel32/Makefile.in b/reactos/apps/tests/kernel32/Makefile.in
new file mode 100644 (file)
index 0000000..7e6923e
--- /dev/null
@@ -0,0 +1,33 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+TESTDLL   = kernel32.dll
+IMPORTS   = kernel32
+
+CTESTS = \
+       alloc.c \
+       atom.c \
+       change.c \
+       codepage.c \
+       comm.c \
+       console.c \
+       directory.c \
+       drive.c \
+       environ.c \
+       file.c \
+       format_msg.c \
+       generated.c \
+       heap.c \
+       locale.c \
+       mailslot.c \
+       path.c \
+       pipe.c \
+       process.c \
+       profile.c \
+       thread.c \
+       virtual.c
+
+@MAKE_TEST_RULES@
+
+### Dependencies:
diff --git a/reactos/apps/tests/kernel32/alloc.c b/reactos/apps/tests/kernel32/alloc.c
new file mode 100644 (file)
index 0000000..ae02789
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * Unit test suite for memory allocation functions.
+ *
+ * Copyright 2002 Geoffrey Hausheer
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+
+ /* The following functions don't have tests, because either I don't know how
+   to test them, or they are WinNT only, or require multiple threads.
+   Since the last two issues shouldn't really stop the tests from being
+   written, assume for now that it is all due to the first case
+       HeapCompact
+       HeapLock
+       HeapQueryInformation
+       HeapSetInformation
+       HeapUnlock
+       HeapValidate
+       HeapWalk
+*/
+/* In addition, these features aren't being tested
+       HEAP_NO_SERIALIZE
+       HEAP_GENERATE_EXCEPTIONS
+       STATUS_ACCESS_VIOLATION (error code from HeapAlloc)
+*/
+
+static void test_Heap(void)
+{
+    SYSTEM_INFO sysInfo;
+    ULONG memchunk;
+    HANDLE heap;
+    LPVOID mem1,mem1a,mem3;
+    UCHAR *mem2,*mem2a;
+    UINT error,i;
+    DWORD dwSize;
+
+/* Retrieve the page size for this system */
+    sysInfo.dwPageSize=0;
+    GetSystemInfo(&sysInfo);
+    ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
+
+/* Create a Heap with a minimum and maximum size */
+/* Note that Windows and Wine seem to behave a bit differently with respect
+   to memory allocation.  In Windows, you can't access all the memory
+   specified in the heap (due to overhead), so choosing a reasonable maximum
+   size for the heap was done mostly by trial-and-error on Win2k.  It may need
+   more tweaking for otherWindows variants.
+*/
+    memchunk=10*sysInfo.dwPageSize;
+    heap=HeapCreate(0,2*memchunk,5*memchunk);
+
+/* Check that HeapCreate allocated the right amount of ram */
+    todo_wine {
+    /* Today HeapCreate seems to return a memory block larger than specified.
+       MSDN says the maximum heap size should be dwMaximumSize rounded up to the
+       nearest page boundary
+    */
+      mem1=HeapAlloc(heap,0,5*memchunk+1);
+      ok(mem1==NULL,"HeapCreate allocated more Ram than it should have\n");
+      if(mem1) {
+        HeapFree(heap,0,mem1);
+      }
+    }
+
+/* Check that a normal alloc works */
+    mem1=HeapAlloc(heap,0,memchunk);
+    ok(mem1!=NULL,"HeapAlloc failed\n");
+    if(mem1) {
+      ok(HeapSize(heap,0,mem1)>=memchunk, "HeapAlloc should return a big enough memory block\n");
+    }
+
+/* Check that a 'zeroing' alloc works */
+    mem2=HeapAlloc(heap,HEAP_ZERO_MEMORY,memchunk);
+    ok(mem2!=NULL,"HeapAlloc failed\n");
+    if(mem2) {
+      ok(HeapSize(heap,0,mem2)>=memchunk,"HeapAlloc should return a big enough memory block\n");
+      error=0;
+      for(i=0;i<memchunk;i++) {
+        if(mem2[i]!=0) {
+          error=1;
+        }
+      }
+      ok(!error,"HeapAlloc should have zeroed out it's allocated memory\n");
+    }
+
+/* Check that HeapAlloc returns NULL when requested way too much memory */
+    mem3=HeapAlloc(heap,0,5*memchunk);
+    ok(mem3==NULL,"HeapAlloc should return NULL\n");
+    if(mem3) {
+      ok(HeapFree(heap,0,mem3),"HeapFree didn't pass successfully\n");
+    }
+
+/* Check that HeapRealloc works */
+    mem2a=HeapReAlloc(heap,HEAP_ZERO_MEMORY,mem2,memchunk+5*sysInfo.dwPageSize);
+    ok(mem2a!=NULL,"HeapReAlloc failed\n");
+    if(mem2a) {
+      ok(HeapSize(heap,0,mem2a)>=memchunk+5*sysInfo.dwPageSize,"HeapReAlloc failed\n");
+      error=0;
+      for(i=0;i<5*sysInfo.dwPageSize;i++) {
+        if(mem2a[memchunk+i]!=0) {
+          error=1;
+        }
+      }
+      ok(!error,"HeapReAlloc should have zeroed out it's allocated memory\n");
+    }
+
+/* Check that HeapRealloc honours HEAP_REALLOC_IN_PLACE_ONLY */
+    error=0;
+    mem1a=HeapReAlloc(heap,HEAP_REALLOC_IN_PLACE_ONLY,mem1,memchunk+sysInfo.dwPageSize);
+    if(mem1a!=NULL) {
+      if(mem1a!=mem1) {
+        error=1;
+      }
+    }
+    ok(mem1a==NULL || error==0,"HeapReAlloc didn't honour HEAP_REALLOC_IN_PLACE_ONLY\n");
+
+/* Check that HeapFree works correctly */
+   if(mem1a) {
+     ok(HeapFree(heap,0,mem1a),"HeapFree failed\n");
+   } else {
+     ok(HeapFree(heap,0,mem1),"HeapFree failed\n");
+   }
+   if(mem2a) {
+     ok(HeapFree(heap,0,mem2a),"HeapFree failed\n");
+   } else {
+     ok(HeapFree(heap,0,mem2),"HeapFree failed\n");
+   }
+
+   /* 0-length buffer */
+   mem1 = HeapAlloc(heap, 0, 0);
+   ok(mem1 != NULL, "Reserved memory\n");
+
+   dwSize = HeapSize(heap, 0, mem1);
+   /* should work with 0-length buffer */
+   ok((dwSize >= 0) && (dwSize < 0xFFFFFFFF),
+      "The size of the 0-length buffer\n");
+   ok(HeapFree(heap, 0, mem1), "Freed the 0-length buffer\n");
+
+/* Check that HeapDestry works */
+   ok(HeapDestroy(heap),"HeapDestroy failed\n");
+}
+
+/* The following functions don't have tests, because either I don't know how
+   to test them, or they are WinNT only, or require multiple threads.
+   Since the last two issues shouldn't really stop the tests from being
+   written, assume for now that it is all due to the first case
+       GlobalFlags
+       GlobalMemoryStatus
+       GlobalMemoryStatusEx
+*/
+/* In addition, these features aren't being tested
+       GMEM_DISCADABLE
+       GMEM_NOCOMPACT
+*/
+static void test_Global(void)
+{
+    ULONG memchunk;
+    HGLOBAL mem1,mem2,mem2a,mem2b;
+    UCHAR *mem2ptr;
+    UINT error,i;
+    memchunk=100000;
+
+    SetLastError(NO_ERROR);
+/* Check that a normal alloc works */
+    mem1=GlobalAlloc(0,memchunk);
+    ok(mem1!=NULL,"GlobalAlloc failed\n");
+    if(mem1) {
+      ok(GlobalSize(mem1)>=memchunk, "GlobalAlloc should return a big enough memory block\n");
+    }
+
+/* Check that a 'zeroing' alloc works */
+    mem2=GlobalAlloc(GMEM_ZEROINIT,memchunk);
+    ok(mem2!=NULL,"GlobalAlloc failed: error=%ld\n",GetLastError());
+    if(mem2) {
+      ok(GlobalSize(mem2)>=memchunk,"GlobalAlloc should return a big enough memory block\n");
+      mem2ptr=GlobalLock(mem2);
+      ok(mem2ptr==mem2,"GlobalLock should have returned the same memory as was allocated\n");
+      if(mem2ptr) {
+        error=0;
+        for(i=0;i<memchunk;i++) {
+          if(mem2ptr[i]!=0) {
+            error=1;
+          }
+        }
+        ok(!error,"GlobalAlloc should have zeroed out it's allocated memory\n");
+      }
+   }
+/* Check that GlobalReAlloc works */
+/* Check that we can change GMEM_FIXED to GMEM_MOVEABLE */
+    mem2a=GlobalReAlloc(mem2,0,GMEM_MODIFY | GMEM_MOVEABLE);
+    ok(mem2a!=NULL,"GlobalReAlloc failed to convert FIXED to MOVEABLE: error=%ld\n",GetLastError());
+    if(mem2a!=NULL) {
+      mem2=mem2a;
+    }
+    mem2ptr=GlobalLock(mem2a);
+    ok(mem2ptr!=NULL && !GlobalUnlock(mem2a)&&GetLastError()==NO_ERROR,
+        "Converting from FIXED to MOVEABLE didn't REALLY work\n");
+
+/* Check that ReAllocing memory works as expected */
+    mem2a=GlobalReAlloc(mem2,2*memchunk,GMEM_MOVEABLE | GMEM_ZEROINIT);
+    ok(mem2a!=NULL,"GlobalReAlloc failed\n");
+    if(mem2a) {
+      ok(GlobalSize(mem2a)>=2*memchunk,"GlobalReAlloc failed\n");
+      mem2ptr=GlobalLock(mem2a);
+      ok(mem2ptr!=NULL,"GlobalLock Failed\n");
+      if(mem2ptr) {
+        error=0;
+        for(i=0;i<memchunk;i++) {
+          if(mem2ptr[memchunk+i]!=0) {
+            error=1;
+          }
+        }
+        ok(!error,"GlobalReAlloc should have zeroed out it's allocated memory\n");
+
+/* Check that GlobalHandle works */
+        mem2b=GlobalHandle(mem2ptr);
+        ok(mem2b==mem2a,"GlobalHandle didn't return the correct memory handle\n");
+
+/* Check that we can't discard locked memory */
+        mem2b=GlobalDiscard(mem2a);
+        ok(mem2b==NULL,"Discarded memory we shouldn't have\n");
+        ok(!GlobalUnlock(mem2a) && GetLastError()==NO_ERROR,"GlobalUnlock Failed\n");
+      }
+    }
+    if(mem1) {
+      ok(GlobalFree(mem1)==NULL,"GlobalFree failed\n");
+    }
+    if(mem2a) {
+      ok(GlobalFree(mem2a)==NULL,"GlobalFree failed\n");
+    } else {
+      ok(GlobalFree(mem2)==NULL,"GlobalFree failed\n");
+    }
+}
+
+
+/* The following functions don't have tests, because either I don't know how
+   to test them, or they are WinNT only, or require multiple threads.
+   Since the last two issues shouldn't really stop the tests from being
+   written, assume for now that it is all due to the first case
+       LocalDiscard
+       LocalFlags
+*/
+/* In addition, these features aren't being tested
+       LMEM_DISCADABLE
+       LMEM_NOCOMPACT
+*/
+static void test_Local(void)
+{
+    ULONG memchunk;
+    HLOCAL mem1,mem2,mem2a,mem2b;
+    UCHAR *mem2ptr;
+    UINT error,i;
+    memchunk=100000;
+
+/* Check that a normal alloc works */
+    mem1=LocalAlloc(0,memchunk);
+    ok(mem1!=NULL,"LocalAlloc failed: error=%ld\n",GetLastError());
+    if(mem1) {
+      ok(LocalSize(mem1)>=memchunk, "LocalAlloc should return a big enough memory block\n");
+    }
+
+/* Check that a 'zeroing' and lock alloc works */
+    mem2=LocalAlloc(LMEM_ZEROINIT|LMEM_MOVEABLE,memchunk);
+    ok(mem2!=NULL,"LocalAlloc failed: error=%ld\n",GetLastError());
+    if(mem2) {
+      ok(LocalSize(mem2)>=memchunk,"LocalAlloc should return a big enough memory block\n");
+      mem2ptr=LocalLock(mem2);
+      ok(mem2ptr!=NULL,"LocalLock: error=%ld\n",GetLastError());
+      if(mem2ptr) {
+        error=0;
+        for(i=0;i<memchunk;i++) {
+          if(mem2ptr[i]!=0) {
+            error=1;
+          }
+        }
+        ok(!error,"LocalAlloc should have zeroed out it's allocated memory\n");
+        SetLastError(0);
+        error=LocalUnlock(mem2);
+        ok(error==0 && GetLastError()==NO_ERROR,
+           "LocalUnlock Failed: rc=%d err=%ld\n",error,GetLastError());
+      }
+    }
+   mem2a=LocalFree(mem2);
+   ok(mem2a==NULL, "LocalFree failed: %p\n",mem2a);
+
+/* Reallocate mem2 as moveable memory */
+   mem2=LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,memchunk);
+   ok(mem2!=NULL, "LocalAlloc failed to create moveable memory, error=%ld\n",GetLastError());
+
+/* Check that ReAllocing memory works as expected */
+    mem2a=LocalReAlloc(mem2,2*memchunk,LMEM_MOVEABLE | LMEM_ZEROINIT);
+    ok(mem2a!=NULL,"LocalReAlloc failed, error=%ld\n",GetLastError());
+    if(mem2a) {
+      ok(LocalSize(mem2a)>=2*memchunk,"LocalReAlloc failed\n");
+      mem2ptr=LocalLock(mem2a);
+      ok(mem2ptr!=NULL,"LocalLock Failed\n");
+      if(mem2ptr) {
+        error=0;
+        for(i=0;i<memchunk;i++) {
+          if(mem2ptr[memchunk+i]!=0) {
+            error=1;
+          }
+        }
+        ok(!error,"LocalReAlloc should have zeroed out it's allocated memory\n");
+/* Check that LocalHandle works */
+        mem2b=LocalHandle(mem2ptr);
+        ok(mem2b==mem2a,"LocalHandle didn't return the correct memory handle\n");
+/* Check that we can't discard locked memory */
+        mem2b=LocalDiscard(mem2a);
+        ok(mem2b==NULL,"Discarded memory we shouldn't have\n");
+        SetLastError(NO_ERROR);
+        ok(!LocalUnlock(mem2a) && GetLastError()==NO_ERROR, "LocalUnlock Failed\n");
+      }
+    }
+    if(mem1) {
+      ok(LocalFree(mem1)==NULL,"LocalFree failed\n");
+    }
+    if(mem2a) {
+      ok(LocalFree(mem2a)==NULL,"LocalFree failed\n");
+    } else {
+      ok(LocalFree(mem2)==NULL,"LocalFree failed\n");
+    }
+}
+
+/* The Virtual* routines are not tested as thoroughly,
+   since I don't really understand how to use them correctly :)
+   The following routines are not tested at all
+      VirtualAllocEx
+      VirtualFreeEx
+      VirtualLock
+      VirtualProtect
+      VirtualProtectEx
+      VirtualQuery
+      VirtualQueryEx
+      VirtualUnlock
+    And the only features (flags) being tested are
+      MEM_COMMIT
+      MEM_RELEASE
+      PAGE_READWRITE
+    Testing the rest requires using exceptions, which I really don't
+    understand well
+*/
+static void test_Virtual(void)
+{
+    SYSTEM_INFO sysInfo;
+    ULONG memchunk;
+    UCHAR *mem1;
+    UINT error,i;
+
+/* Retrieve the page size for this system */
+    sysInfo.dwPageSize=0;
+    GetSystemInfo(&sysInfo);
+    ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
+
+/* Choose a reasonable allocation size */
+    memchunk=10*sysInfo.dwPageSize;
+
+/* Check that a normal alloc works */
+    mem1=VirtualAlloc(NULL,memchunk,MEM_COMMIT,PAGE_READWRITE);
+    ok(mem1!=NULL,"VirtualAlloc failed\n");
+    if(mem1) {
+/* check that memory is initialized to 0 */
+      error=0;
+      for(i=0;i<memchunk;i++) {
+        if(mem1[i]!=0) {
+          error=1;
+        }
+      }
+      ok(!error,"VirtualAlloc did not initialize memory to '0's\n");
+/* Check that we can read/write to memory */
+      error=0;
+      for(i=0;i<memchunk;i+=100) {
+        mem1[i]='a';
+        if(mem1[i]!='a') {
+          error=1;
+        }
+      }
+      ok(!error,"Virtual memory was not writable\n");
+    }
+    ok(VirtualFree(mem1,0,MEM_RELEASE),"VirtualFree failed\n");
+}
+START_TEST(alloc)
+{
+    test_Heap();
+    test_Global();
+    test_Local();
+    test_Virtual();
+}
diff --git a/reactos/apps/tests/kernel32/atom.c b/reactos/apps/tests/kernel32/atom.c
new file mode 100644 (file)
index 0000000..afb98f6
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Unit tests for atom functions
+ *
+ * Copyright (c) 2002 Alexandre Julliard
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
+static const WCHAR FOOBARW[] = {'F','O','O','B','A','R',0};
+static const WCHAR _foobarW[] = {'_','f','o','o','b','a','r',0};
+
+static BOOL unicode_OS;
+
+static void test_add_atom(void)
+{
+    ATOM atom, w_atom;
+    int i;
+
+    SetLastError( 0xdeadbeef );
+    atom = GlobalAddAtomA( "foobar" );
+    ok( atom >= 0xc000, "bad atom id %x\n", atom );
+    ok( GetLastError() == 0xdeadbeef, "GlobalAddAtomA set last error\n" );
+
+    /* Verify that it can be found (or not) appropriately */
+    ok( GlobalFindAtomA( "foobar" ) == atom, "could not find atom foobar\n" );
+    ok( GlobalFindAtomA( "FOOBAR" ) == atom, "could not find atom FOOBAR\n" );
+    ok( !GlobalFindAtomA( "_foobar" ), "found _foobar\n" );
+
+    /* Add the same atom, specifying string as unicode; should
+     * find the first one, not add a new one */
+    SetLastError( 0xdeadbeef );
+    w_atom = GlobalAddAtomW( foobarW );
+    if (w_atom && GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
+        unicode_OS = TRUE;
+    else
+        trace("WARNING: Unicode atom APIs are not supported on this platform\n");
+
+    if (unicode_OS)
+    {
+        ok( w_atom == atom, "Unicode atom does not match ASCII\n" );
+        ok( GetLastError() == 0xdeadbeef, "GlobalAddAtomW set last error\n" );
+    }
+
+    /* Verify that it can be found (or not) appropriately via unicode name */
+    if (unicode_OS)
+    {
+        ok( GlobalFindAtomW( foobarW ) == atom, "could not find atom foobar\n" );
+        ok( GlobalFindAtomW( FOOBARW ) == atom, "could not find atom FOOBAR\n" );
+        ok( !GlobalFindAtomW( _foobarW ), "found _foobar\n" );
+    }
+
+    /* Test integer atoms
+     * (0x0001 .. 0xbfff) should be valid;
+     * (0xc000 .. 0xffff) should be invalid */
+
+    SetLastError( 0xdeadbeef );
+    ok( GlobalAddAtomA(0) == 0 && GetLastError() == 0xdeadbeef, "succeeded to add atom 0\n" );
+    if (unicode_OS)
+    {
+        SetLastError( 0xdeadbeef );
+        ok( GlobalAddAtomW(0) == 0 && GetLastError() == 0xdeadbeef, "succeeded to add atom 0\n" );
+    }
+
+    SetLastError( 0xdeadbeef );
+    for (i = 1; i <= 0xbfff; i++)
+    {
+        SetLastError( 0xdeadbeef );
+        ok( GlobalAddAtomA((LPCSTR)i) == i && GetLastError() == 0xdeadbeef,
+            "failed to add atom %x\n", i );
+        if (unicode_OS)
+        {
+            SetLastError( 0xdeadbeef );
+            ok( GlobalAddAtomW((LPCWSTR)i) == i && GetLastError() == 0xdeadbeef,
+                "failed to add atom %x\n", i );
+        }
+    }
+
+    for (i = 0xc000; i <= 0xffff; i++)
+    {
+        ok( !GlobalAddAtomA((LPCSTR)i), "succeeded adding %x\n", i );
+        if (unicode_OS)
+            ok( !GlobalAddAtomW((LPCWSTR)i), "succeeded adding %x\n", i );
+    }
+}
+
+static void test_get_atom_name(void)
+{
+    char buf[10];
+    WCHAR bufW[10];
+    int i;
+    UINT len;
+    static const WCHAR resultW[] = {'f','o','o','b','a','r',0,'.','.','.'};
+
+    ATOM atom = GlobalAddAtomA( "foobar" );
+
+    /* Get the name of the atom we added above */
+    memset( buf, '.', sizeof(buf) );
+    len = GlobalGetAtomNameA( atom, buf, 10 );
+    ok( len == strlen("foobar"), "bad length %d\n", len );
+    ok( !memcmp( buf, "foobar\0...", 10 ), "bad buffer contents\n" );
+
+    /* Repeat, unicode-style */
+    if (unicode_OS)
+    {
+        for (i = 0; i < 10; i++) bufW[i] = '.';
+        SetLastError( 0xdeadbeef );
+        len = GlobalGetAtomNameW( atom, bufW, 10 );
+        ok( len && GetLastError() == 0xdeadbeef, "GlobalGetAtomNameW failed\n" );
+        ok( len == lstrlenW(foobarW), "bad length %d\n", len );
+        ok( !memcmp( bufW, resultW, 10*sizeof(WCHAR) ), "bad buffer contents\n" );
+    }
+
+    /* Check error code returns */
+    memset(buf, '.', 10);
+    ok( !GlobalGetAtomNameA( atom, buf,  0 ), "succeeded\n" );
+    ok( !memcmp( buf, "..........", 10 ), "should not touch buffer\n" );
+
+    if (unicode_OS)
+    {
+        static const WCHAR sampleW[10] = {'.','.','.','.','.','.','.','.','.','.'};
+
+        for (i = 0; i < 10; i++) bufW[i] = '.';
+        ok( !GlobalGetAtomNameW( atom, bufW, 0 ), "succeeded\n" );
+        ok( !memcmp( bufW, sampleW, 10 * sizeof(WCHAR) ), "should not touch buffer\n" );
+    }
+
+    /* Test integer atoms */
+    for (i = 0; i <= 0xbfff; i++)
+    {
+        memset( buf, 'a', 10 );
+        len = GlobalGetAtomNameA( (ATOM)i, buf, 10 );
+        if (i)
+        {
+            char res[20];
+            ok( (len > 1) && (len < 7), "bad length %d\n", len );
+            sprintf( res, "#%d", i );
+            memset( res + strlen(res) + 1, 'a', 10 );
+            ok( !memcmp( res, buf, 10 ), "bad buffer contents %s\n", buf );
+        }
+        else
+            ok( !len, "bad length %d\n", len );
+    }
+}
+
+static void test_error_handling(void)
+{
+    char buffer[260];
+    WCHAR bufferW[260];
+    int i;
+
+    memset( buffer, 'a', 256 );
+    buffer[256] = 0;
+    ok( !GlobalAddAtomA(buffer), "add succeeded\n" );
+    ok( !GlobalFindAtomA(buffer), "find succeeded\n" );
+
+    if (unicode_OS)
+    {
+        for (i = 0; i < 256; i++) bufferW[i] = 'b';
+        bufferW[256] = 0;
+        ok( !GlobalAddAtomW(bufferW), "add succeeded\n" );
+        ok( !GlobalFindAtomW(bufferW), "find succeeded\n" );
+    }
+}
+
+START_TEST(atom)
+{
+    test_add_atom();
+    test_get_atom_name();
+    test_error_handling();
+}
diff --git a/reactos/apps/tests/kernel32/change.c b/reactos/apps/tests/kernel32/change.c
new file mode 100644 (file)
index 0000000..88353f5
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Tests for file change notification functions
+ *
+ * Copyright (c) 2004 Hans Leidekker
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* TODO: - security attribute changes
+ *       - compound filter and multiple notifications
+ *       - subtree notifications
+ *       - non-documented flags FILE_NOTIFY_CHANGE_LAST_ACCESS and
+ *         FILE_NOTIFY_CHANGE_CREATION
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "wine/test.h"
+#include <windef.h>
+#include <winbase.h>
+
+static DWORD CALLBACK NotificationThread(LPVOID arg)
+{
+    HANDLE change = (HANDLE) arg;
+    BOOL ret = FALSE;
+    DWORD status;
+
+    status = WaitForSingleObject(change, 100);
+
+    if (status == WAIT_OBJECT_0 ) {
+        ret = FindNextChangeNotification(change);
+    }
+
+    ok(FindCloseChangeNotification(change), "FindCloseChangeNotification error: %ld\n",
+       GetLastError());
+
+    ExitThread((DWORD)ret);
+}
+
+static HANDLE StartNotificationThread(LPCSTR path, BOOL subtree, DWORD flags)
+{
+    HANDLE change, thread;
+    DWORD threadId;
+
+    change = FindFirstChangeNotificationA(path, subtree, flags);
+    ok(change != INVALID_HANDLE_VALUE, "FindFirstChangeNotification error: %ld\n", GetLastError());
+
+    thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)NotificationThread, (LPVOID)change,
+                          0, &threadId);
+    ok(thread != INVALID_HANDLE_VALUE, "CreateThread error: %ld\n", GetLastError());
+
+    return thread;
+}
+
+static DWORD FinishNotificationThread(HANDLE thread)
+{
+    DWORD status, exitcode;
+
+    status = WaitForSingleObject(thread, 5000);
+    ok(status == WAIT_OBJECT_0, "WaitForSingleObject status %ld error %ld\n", status, GetLastError());
+
+    ok(GetExitCodeThread(thread, &exitcode), "Could not retrieve thread exit code\n");
+
+    return exitcode;
+}
+
+static void test_FindFirstChangeNotification(void)
+{
+    HANDLE change, file, thread;
+    DWORD attributes, count;
+    BOOL ret;
+
+    char workdir[MAX_PATH], dirname1[MAX_PATH], dirname2[MAX_PATH];
+    char filename1[MAX_PATH], filename2[MAX_PATH];
+    static const char prefix[] = "FCN";
+    char buffer[2048];
+
+    /* pathetic checks */
+
+    change = FindFirstChangeNotificationA("not-a-file", FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+    ok(change == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND,
+       "FindFirstChangeNotification error: %ld\n", GetLastError());
+
+    if (0) /* This documents win2k behavior. It crashes on win98. */
+    { 
+        change = FindFirstChangeNotificationA(NULL, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+        ok(change == NULL && GetLastError() == ERROR_PATH_NOT_FOUND,
+        "FindFirstChangeNotification error: %ld\n", GetLastError());
+    }
+
+    ret = FindNextChangeNotification(NULL);
+    ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindNextChangeNotification error: %ld\n",
+       GetLastError());
+
+    ret = FindCloseChangeNotification(NULL);
+    ok(!ret && GetLastError() == ERROR_INVALID_HANDLE, "FindCloseChangeNotification error: %ld\n",
+       GetLastError());
+
+    ret = GetTempPathA(MAX_PATH, workdir);
+    ok(ret, "GetTempPathA error: %ld\n", GetLastError());
+
+    lstrcatA(workdir, "testFileChangeNotification");
+
+    ret = CreateDirectoryA(workdir, NULL);
+    ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
+
+    ret = GetTempFileNameA(workdir, prefix, 0, filename1);
+    ok(ret, "GetTempFileNameA error: %ld\n", GetLastError());
+
+    file = CreateFileA(filename1, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS,
+                       FILE_ATTRIBUTE_NORMAL, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+    ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
+
+    /* Try to register notification for a file. win98 and win2k behave differently here */
+    change = FindFirstChangeNotificationA(filename1, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+    ok(change == INVALID_HANDLE_VALUE && (GetLastError() == ERROR_DIRECTORY ||
+                                          GetLastError() == ERROR_FILE_NOT_FOUND),
+       "FindFirstChangeNotification error: %ld\n", GetLastError());
+
+    lstrcpyA(dirname1, filename1);
+    lstrcatA(dirname1, "dir");
+
+    ret = CreateDirectoryA(dirname1, NULL);
+    ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
+
+    /* What if we remove the directory we registered notification for? */
+    thread = StartNotificationThread(dirname1, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+    ret = RemoveDirectoryA(dirname1);
+    ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
+
+    /* win98 and win2k behave differently here */
+    ret = FinishNotificationThread(thread);
+    ok(ret || !ret, "You'll never read this\n");
+
+    /* functional checks */
+
+    /* Create a directory */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+    ret = CreateDirectoryA(dirname1, NULL);
+    ok(ret, "CreateDirectoryA error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    lstrcpyA(dirname2, dirname1);
+    lstrcatA(dirname2, "new");
+
+    /* Rename a directory */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+    ret = MoveFileA(dirname1, dirname2);
+    ok(ret, "MoveFileA error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    /* Delete a directory */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_DIR_NAME);
+    ret = RemoveDirectoryA(dirname2);
+    ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    lstrcpyA(filename2, filename1);
+    lstrcatA(filename2, "new");
+
+    /* Rename a file */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+    ret = MoveFileA(filename1, filename2);
+    ok(ret, "MoveFileA error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    /* Delete a file */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+    ret = DeleteFileA(filename2);
+    ok(ret, "DeleteFileA error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    /* Create a file */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME);
+    file = CreateFileA(filename2, GENERIC_WRITE|GENERIC_READ, 0, NULL, CREATE_ALWAYS, 
+                       FILE_ATTRIBUTE_NORMAL, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+    ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    attributes = GetFileAttributesA(filename2);
+    ok(attributes != INVALID_FILE_ATTRIBUTES, "GetFileAttributesA error: %ld\n", GetLastError());
+    attributes &= FILE_ATTRIBUTE_READONLY;
+
+    /* Change file attributes */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_ATTRIBUTES);
+    ret = SetFileAttributesA(filename2, attributes);
+    ok(ret, "SetFileAttributesA error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    /* Change last write time by writing to a file */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);
+    file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
+                       FILE_ATTRIBUTE_NORMAL, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+    ret = WriteFile(file, buffer, sizeof(buffer), &count, NULL);
+    ok(ret && count == sizeof(buffer), "WriteFile error: %ld\n", GetLastError());
+    ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    /* Change file size by truncating a file */
+    thread = StartNotificationThread(workdir, FALSE, FILE_NOTIFY_CHANGE_SIZE);
+    file = CreateFileA(filename2, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
+                       FILE_ATTRIBUTE_NORMAL, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileA error: %ld\n", GetLastError());
+    ret = WriteFile(file, buffer, sizeof(buffer) / 2, &count, NULL);
+    ok(ret && count == sizeof(buffer) / 2, "WriteFileA error: %ld\n", GetLastError());
+    ok(CloseHandle(file), "CloseHandle error: %ld\n", GetLastError());
+    ok(FinishNotificationThread(thread), "Missed notification\n");
+
+    /* clean up */
+    
+    ret = DeleteFileA(filename2);
+    ok(ret, "DeleteFileA error: %ld\n", GetLastError());
+
+    ret = RemoveDirectoryA(workdir);
+    ok(ret, "RemoveDirectoryA error: %ld\n", GetLastError());
+}
+
+START_TEST(change)
+{
+    test_FindFirstChangeNotification();
+}
diff --git a/reactos/apps/tests/kernel32/codepage.c b/reactos/apps/tests/kernel32/codepage.c
new file mode 100644 (file)
index 0000000..038b428
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Unit tests for code page to/from unicode translations
+ *
+ * Copyright (c) 2002 Dmitry Timoshkov
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+
+/* lstrcmpW is not supported on Win9x! */
+static int mylstrcmpW(const WCHAR* str1, const WCHAR* str2)
+{
+    while (*str1 && *str1==*str2) {
+        str1++;
+        str2++;
+    }
+    return *str1-*str2;
+}
+
+static void test_negative_source_length(void)
+{
+    int len;
+    char buf[10];
+    WCHAR bufW[10];
+    static const WCHAR foobarW[] = {'f','o','o','b','a','r',0};
+
+    /* Test, whether any negative source length works as strlen() + 1 */
+    SetLastError( 0xdeadbeef );
+    memset(buf,'x',sizeof(buf));
+    len = WideCharToMultiByte(CP_ACP, 0, foobarW, -2002, buf, 10, NULL, NULL);
+    ok(len == 7 && !lstrcmpA(buf, "foobar") && GetLastError() == 0xdeadbeef,
+       "WideCharToMultiByte(-2002): len=%d error=%ld\n",len,GetLastError());
+
+    SetLastError( 0xdeadbeef );
+    memset(bufW,'x',sizeof(bufW));
+    len = MultiByteToWideChar(CP_ACP, 0, "foobar", -2002, bufW, 10);
+    ok(len == 7 && !mylstrcmpW(bufW, foobarW) && GetLastError() == 0xdeadbeef,
+       "MultiByteToWideChar(-2002): len=%d error=%ld\n",len,GetLastError());
+}
+
+START_TEST(codepage)
+{
+    test_negative_source_length();
+}
diff --git a/reactos/apps/tests/kernel32/comm.c b/reactos/apps/tests/kernel32/comm.c
new file mode 100644 (file)
index 0000000..166bfb3
--- /dev/null
@@ -0,0 +1,640 @@
+/* Unit test suite for comm functions
+ *
+ * Copyright 2003 Kevin Groeneveld
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+
+#include "wine/test.h"
+#include "winbase.h"
+#include "winnls.h"
+
+typedef struct
+{
+       char string[100];
+       BOOL result;
+       BOOL old_style;
+       DCB dcb1, dcb2;
+       COMMTIMEOUTS timeouts1, timeouts2;
+} TEST;
+
+static TEST test[] =
+{
+       {
+               "baud=9600 parity=e data=5 stop=1 xon=on odsr=off octs=off dtr=on rts=on idsr=on",
+               TRUE, FALSE,
+               { 0x00000000, 0x00002580, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x05, 0x02, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00002580, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x05, 0x02, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "baud=0 parity=M data=6 stop=1.5 xon=off odsr=on octs=ON dtr=off rts=off idsr=OFF",
+               TRUE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x06, 0x03, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00000000, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x06, 0x03, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "BAUD=4000000000 parity=n data=7 stop=2 to=off",
+               TRUE, FALSE,
+               { 0x00000000, 0xee6b2800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x00, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xee6b2800, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x00, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }
+       },
+       {
+               "Baud=115200 Parity=O Data=8 To=On",
+               TRUE, FALSE,
+               { 0x00000000, 0x0001c200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x01, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x0001c200, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x01, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 }
+       },
+       {
+               "PaRiTy=s           Data=7          DTR=on",
+               TRUE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x04, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x04, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "data=4",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "data=9",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "parity=no",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "stop=0",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "stop=1.501",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "stop=3",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "to=foobar",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               " baud=9600",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "baud= 9600",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "baud=9600,data=8",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "11,n,8,1",
+               TRUE, TRUE,
+               { 0x00000000, 0x0000006e, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x0000006e, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "30 ,E, 5,1.5",
+               TRUE, TRUE,
+               { 0x00000000, 0x0000012c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x05, 0x02, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x0000012c, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x05, 0x02, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "60, m, 6, 2 ",
+               TRUE, TRUE,
+               { 0x00000000, 0x00000258, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x06, 0x03, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00000258, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x06, 0x03, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "12 , o , 7 , 1",
+               TRUE, TRUE,
+               { 0x00000000, 0x000004b0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x01, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x000004b0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x01, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "24,s,8,1.5",
+               TRUE, TRUE,
+               { 0x00000000, 0x00000960, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x04, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00000960, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x04, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "48,n,8,1,p",
+               TRUE, TRUE,
+               { 0x00000000, 0x000012c0, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x000012c0, 1, 1, 1, 1, 2, 1, 1, 0, 0, 1, 1, 2, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,N,8,1 , x ",
+               TRUE, TRUE,
+               { 0x00000000, 0x00002580, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00002580, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "19, e, 7, 1, x",
+               TRUE, TRUE,
+               { 0x00000000, 0x00004b00, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x02, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00004b00, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x02, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "0,M,7,1,P",
+               TRUE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 1, 1, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x03, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00000000, 1, 1, 1, 1, 2, 1, 1, 0, 0, 1, 1, 2, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x03, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "4000000000,O,7,1.5,X",
+               TRUE, TRUE,
+               { 0x00000000, 0xee6b2800, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x01, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xee6b2800, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x01, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,N,8,1 to=on",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,NO,8,1",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,N,4,1",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,N,9,1",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,N,8,0",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,N,8,3",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "96,N,8,1,K",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COM0:baud=115200",
+               FALSE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COMx:baud=38400 data=8",
+               TRUE, FALSE,
+               { 0x00000000, 0x00009600, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00009600, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0xff, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COMx  :to=on stop=1.5",
+               TRUE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x01, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0x01, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000EA60 }
+       },
+       {
+               "COMx:               baud=12345     data=7",
+               TRUE, FALSE,
+               { 0x00000000, 0x00003039, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00003039, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0xff, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COMx : xon=on odsr=off",
+               TRUE, FALSE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 0, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COM0:9600,N,8,1",
+               FALSE, TRUE,
+               { 0x00000000, 0x00000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x00, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0xffffffff, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0xff, 0xff, 0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COMx:9600,N,8,1",
+               TRUE, TRUE,
+               { 0x00000000, 0x00002580, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x08, 0x00, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00002580, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x08, 0x00, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COMx:  11,E,7,2",
+               TRUE, TRUE,
+               { 0x00000000, 0x0000006e, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x07, 0x02, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x0000006e, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x07, 0x02, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COMx  :19,M,5,1",
+               TRUE, TRUE,
+               { 0x00000000, 0x00004b00, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x05, 0x03, 0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x00004b00, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x05, 0x03, 0x00, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+       {
+               "COMx  :    57600,S,6,2,x",
+               TRUE, TRUE,
+               { 0x00000000, 0x0000e100, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0x00000, 0x0000, 0x0000, 0x0000, 0x06, 0x04, 0x02, (char)0x00, (char)0x00, (char)0x00, (char)0x00, (char)0x00, 0x0000 },
+               { 0xffffffff, 0x0000e100, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x1ffff, 0xffff, 0xffff, 0xffff, 0x06, 0x04, 0x02, (char)0xff, (char)0xff, (char)0xff, (char)0xff, (char)0xff, 0xffff },
+               { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+               { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff }
+       },
+};
+
+#define TEST_COUNT (sizeof(test) / sizeof(TEST))
+
+/* This function can be useful if you are modifiying the test cases and want to
+   output the contents of a DCB structure. */
+/*static print_dcb(DCB *pdcb)
+{
+       printf("0x%08x, 0x%08x, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, 0x%05x, 0x%04x, 0x%04x, 0x%04x, 0x%02x, 0x%02x, 0x%02x, (char)0x%02x, (char)0x%02x, (char)0x%02x, (char)0x%02x, (char)0x%02x, 0x%04x\n",
+               pdcb->DCBlength,
+               pdcb->BaudRate,
+               pdcb->fBinary,
+               pdcb->fParity,
+               pdcb->fOutxCtsFlow,
+               pdcb->fOutxDsrFlow,
+               pdcb->fDtrControl,
+               pdcb->fDsrSensitivity,
+               pdcb->fTXContinueOnXoff,
+               pdcb->fOutX,
+               pdcb->fInX,
+               pdcb->fErrorChar,
+               pdcb->fNull,
+               pdcb->fRtsControl,
+               pdcb->fAbortOnError,
+               pdcb->fDummy2,
+               pdcb->wReserved,
+               pdcb->XonLim,
+               pdcb->XoffLim,
+               pdcb->ByteSize,
+               pdcb->Parity,
+               pdcb->StopBits,
+               pdcb->XonChar & 0xff,
+               pdcb->XoffChar & 0xff,
+               pdcb->ErrorChar & 0xff,
+               pdcb->EofChar & 0xff,
+               pdcb->EvtChar & 0xff,
+               pdcb->wReserved1 & 0xffff );
+} */
+
+static void check_result(char *function, TEST *ptest, int initial_value, BOOL result)
+{
+       DWORD LastError = GetLastError();
+       DWORD CorrectError = (ptest->result ? 0xdeadbeef : ERROR_INVALID_PARAMETER);
+
+       ok(LastError == CorrectError, "%s(\"%s\"), 0x%02x: GetLastError() returned 0x%08lx, should be 0x%08lx\n", function, ptest->string, initial_value, LastError, CorrectError);
+       ok(result == ptest->result, "%s(\"%s\"), 0x%02x: return value should be %s\n", function, ptest->string, initial_value, ptest->result ? "TRUE" : "FALSE");
+}
+
+#define check_dcb_member(a,b) ok(pdcb1->a == pdcb2->a, "%s(\"%s\"), 0x%02x: "#a" is "b", should be "b"\n", function, ptest->string, initial_value, pdcb1->a, pdcb2->a)
+#define check_dcb_member2(a,c,b) if(pdcb2->a == c) { check_dcb_member(a,b); } else { ok(pdcb1->a == pdcb2->a || pdcb1->a == c, "%s(\"%s\"), 0x%02x: "#a" is "b", should be "b" or "b"\n", function, ptest->string, initial_value, pdcb1->a, pdcb2->a, c); }
+
+static void check_dcb(char *function, TEST *ptest, int initial_value, DCB *pdcb1, DCB *pdcb2)
+{
+       /* DCBlength is a special case since Win 9x sets it but NT does not.
+          We will accept either as correct. */
+       check_dcb_member2(DCBlength, (DWORD)sizeof(DCB), "%lu");
+
+       /* For old style control strings Win 9x does not set the next five members, NT does. */
+       if(ptest->old_style && ptest->result)
+       {
+               check_dcb_member2(fOutxCtsFlow, ((unsigned int)initial_value & 1), "%u");
+               check_dcb_member2(fDtrControl, ((unsigned int)initial_value & 3), "%u");
+               check_dcb_member2(fOutX, ((unsigned int)initial_value & 1), "%u");
+               check_dcb_member2(fInX, ((unsigned)initial_value & 1), "%u");
+               check_dcb_member2(fRtsControl, ((unsigned)initial_value & 3), "%u");
+       }
+       else
+       {
+               check_dcb_member(fOutxCtsFlow, "%u");
+               check_dcb_member(fDtrControl, "%u");
+               check_dcb_member(fOutX, "%u");
+               check_dcb_member(fInX, "%u");
+               check_dcb_member(fRtsControl, "%u");
+       }
+
+       if(ptest->result)
+       {
+               /* For the idsr=xxx parameter, NT sets fDsrSensitivity, 9x sets
+                  fOutxDsrFlow. */
+               if(!ptest->old_style)
+               {
+                       check_dcb_member2(fOutxDsrFlow, pdcb2->fDsrSensitivity, "%u");
+                       check_dcb_member2(fDsrSensitivity, pdcb2->fOutxDsrFlow, "%u");
+               }
+               else
+               {
+                       /* For old style control strings Win 9x does not set the
+                          fOutxDsrFlow member, NT does. */
+                       check_dcb_member2(fOutxDsrFlow, ((unsigned int)initial_value & 1), "%u");
+                       check_dcb_member(fDsrSensitivity, "%u");
+               }
+       }
+       else
+       {
+               check_dcb_member(fOutxDsrFlow, "%u");
+               check_dcb_member(fDsrSensitivity, "%u");
+       }
+
+       /* Check the result of the DCB members. */
+       check_dcb_member(BaudRate, "%lu");
+       check_dcb_member(fBinary, "%u");
+       check_dcb_member(fParity, "%u");
+       check_dcb_member(fTXContinueOnXoff, "%u");
+       check_dcb_member(fErrorChar, "%u");
+       check_dcb_member(fNull, "%u");
+       check_dcb_member(fAbortOnError, "%u");
+       check_dcb_member(fDummy2, "%u");
+       check_dcb_member(wReserved, "%u");
+       check_dcb_member(XonLim, "%u");
+       check_dcb_member(XoffLim, "%u");
+       check_dcb_member(ByteSize, "%u");
+       check_dcb_member(Parity, "%u");
+       check_dcb_member(StopBits, "%u");
+       check_dcb_member(XonChar, "%d");
+       check_dcb_member(XoffChar, "%d");
+       check_dcb_member(ErrorChar, "%d");
+       check_dcb_member(EofChar, "%d");
+       check_dcb_member(EvtChar, "%d");
+       check_dcb_member(wReserved1, "%u");
+}
+
+#define check_timeouts_member(a) ok(ptimeouts1->a == ptimeouts2->a, "%s(\"%s\"), 0x%02x: "#a" is %lu, should be %lu\n", function, ptest->string, initial_value, ptimeouts1->a, ptimeouts2->a);
+
+static void check_timeouts(char *function, TEST *ptest, int initial_value, COMMTIMEOUTS *ptimeouts1, COMMTIMEOUTS *ptimeouts2)
+{
+       check_timeouts_member(ReadIntervalTimeout);
+       check_timeouts_member(ReadTotalTimeoutMultiplier);
+       check_timeouts_member(ReadTotalTimeoutConstant);
+       check_timeouts_member(WriteTotalTimeoutMultiplier);
+       check_timeouts_member(WriteTotalTimeoutConstant);
+}
+
+static void test_BuildCommDCBA(TEST *ptest, int initial_value, DCB *pexpected_dcb)
+{
+       BOOL result;
+       DCB dcb;
+
+       /* set initial conditions */
+       memset(&dcb, initial_value, sizeof(DCB));
+       SetLastError(0xdeadbeef);
+
+       result = BuildCommDCBA(ptest->string, &dcb);
+
+       /* check results */
+       check_result("BuildCommDCBA", ptest, initial_value, result);
+       check_dcb("BuildCommDCBA", ptest, initial_value, &dcb, pexpected_dcb);
+}
+
+static void test_BuildCommDCBAndTimeoutsA(TEST *ptest, int initial_value, DCB *pexpected_dcb, COMMTIMEOUTS *pexpected_timeouts)
+{
+       BOOL result;
+       DCB dcb;
+       COMMTIMEOUTS timeouts;
+
+       /* set initial conditions */
+       memset(&dcb, initial_value, sizeof(DCB));
+       memset(&timeouts, initial_value, sizeof(COMMTIMEOUTS));
+       SetLastError(0xdeadbeef);
+
+       result = BuildCommDCBAndTimeoutsA(ptest->string, &dcb, &timeouts);
+
+       /* check results */
+       check_result("BuildCommDCBAndTimeoutsA", ptest, initial_value, result);
+       check_dcb("BuildCommDCBAndTimeoutsA", ptest, initial_value, &dcb, pexpected_dcb);
+       check_timeouts("BuildCommDCBAndTimeoutsA", ptest, initial_value, &timeouts, pexpected_timeouts);
+}
+
+static void test_BuildCommDCBW(TEST *ptest, int initial_value, DCB *pexpected_dcb)
+{
+       BOOL result;
+       DCB dcb;
+       WCHAR wide_string[sizeof(ptest->string)];
+
+       MultiByteToWideChar(CP_ACP, 0, ptest->string, -1, wide_string, sizeof(wide_string) / sizeof(WCHAR));
+
+       /* set initial conditions */
+       memset(&dcb, initial_value, sizeof(DCB));
+       SetLastError(0xdeadbeef);
+
+       result = BuildCommDCBW(wide_string, &dcb);
+
+       if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+               return;
+
+       /* check results */
+       check_result("BuildCommDCBW", ptest, initial_value, result);
+       check_dcb("BuildCommDCBW", ptest, initial_value, &dcb, pexpected_dcb);
+}
+
+static void test_BuildCommDCBAndTimeoutsW(TEST *ptest, int initial_value, DCB *pexpected_dcb, COMMTIMEOUTS *pexpected_timeouts)
+{
+       BOOL result;
+       DCB dcb;
+       COMMTIMEOUTS timeouts;
+       WCHAR wide_string[sizeof(ptest->string)];
+
+       MultiByteToWideChar(CP_ACP, 0, ptest->string, -1, wide_string, sizeof(wide_string) / sizeof(WCHAR));
+
+       /* set initial conditions */
+       memset(&dcb, initial_value, sizeof(DCB));
+       memset(&timeouts, initial_value, sizeof(COMMTIMEOUTS));
+       SetLastError(0xdeadbeef);
+
+       result = BuildCommDCBAndTimeoutsW(wide_string, &dcb, &timeouts);
+
+       if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+               return;
+
+       /* check results */
+       check_result("BuildCommDCBAndTimeoutsA", ptest, initial_value, result);
+       check_dcb("BuildCommDCBAndTimeoutsA", ptest, initial_value, &dcb, pexpected_dcb);
+       check_timeouts("BuildCommDCBAndTimeoutsA", ptest, initial_value, &timeouts, pexpected_timeouts);
+}
+
+static void test_BuildCommDCB(void)
+{
+       char port_name[] = "COMx";
+       char port = 0;
+       int i;
+       char *ptr;
+
+       /* Some of these tests require a valid COM port.  This loop will try to find
+          a valid port. */
+       for(port_name[3] = '1'; port_name[3] <= '9'; port_name[3]++)
+       {
+               COMMCONFIG commconfig;
+               DWORD size = sizeof(COMMCONFIG);
+               
+               if(GetDefaultCommConfig(port_name, &commconfig, &size))
+               {
+                       port = port_name[3];
+                       break;
+               }
+       }
+
+       if(!port)
+               trace("Could not find a valid COM port.  Some tests will be skipped.\n");
+
+       for(i = 0; i < TEST_COUNT; i++)
+       {
+               /* Check if this test case needs a valid COM port. */
+               ptr = strstr(test[i].string, "COMx");
+               
+               /* If required, substitute valid port number into device control string. */
+               if(ptr)
+               {
+                       if(port)
+                               ptr[3] = port;
+                       else
+                               continue;
+               }
+
+               test_BuildCommDCBA(&test[i], 0x00, &test[i].dcb1);
+               test_BuildCommDCBA(&test[i], 0xff, &test[i].dcb2);
+               test_BuildCommDCBAndTimeoutsA(&test[i], 0x00, &test[i].dcb1, &test[i].timeouts1);
+               test_BuildCommDCBAndTimeoutsA(&test[i], 0xff, &test[i].dcb2, &test[i].timeouts2);
+
+               test_BuildCommDCBW(&test[i], 0x00, &test[i].dcb1);
+               test_BuildCommDCBW(&test[i], 0xff, &test[i].dcb2);
+               test_BuildCommDCBAndTimeoutsW(&test[i], 0x00, &test[i].dcb1, &test[i].timeouts1);
+               test_BuildCommDCBAndTimeoutsW(&test[i], 0xff, &test[i].dcb2, &test[i].timeouts2);
+       }
+}
+
+START_TEST(comm)
+{
+       test_BuildCommDCB();
+}
diff --git a/reactos/apps/tests/kernel32/console.c b/reactos/apps/tests/kernel32/console.c
new file mode 100644 (file)
index 0000000..48ab3c1
--- /dev/null
@@ -0,0 +1,584 @@
+/*
+ * Unit tests for console API
+ *
+ * Copyright (c) 2003 Eric Pouech
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "wine/test.h"
+#include <windows.h>
+#include <stdio.h>
+
+/* DEFAULT_ATTRIB is used for all initial filling of the console.
+ * all modifications are made with TEST_ATTRIB so that we could check
+ * what has to be modified or not
+ */
+#define TEST_ATTRIB    (BACKGROUND_BLUE | FOREGROUND_GREEN)
+#define DEFAULT_ATTRIB (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED)
+/* when filling the screen with non-blank chars, this macro defines
+ * what character should be at position 'c'
+ */
+#define CONTENT(c)    ('A' + (((c).Y * 17 + (c).X) % 23))
+
+#define        okCURSOR(hCon, c) do { \
+  CONSOLE_SCREEN_BUFFER_INFO __sbi; \
+  BOOL expect = GetConsoleScreenBufferInfo((hCon), &__sbi) && \
+                __sbi.dwCursorPosition.X == (c).X && __sbi.dwCursorPosition.Y == (c).Y; \
+  ok(expect, "Expected cursor at (%d,%d), got (%d,%d)\n", \
+     (c).X, (c).Y, __sbi.dwCursorPosition.X, __sbi.dwCursorPosition.Y); \
+} while (0)
+
+#define okCHAR(hCon, c, ch, attr) do { \
+  char __ch; WORD __attr; DWORD __len; BOOL expect; \
+  expect = ReadConsoleOutputCharacter((hCon), &__ch, 1, (c), &__len) == 1 && __len == 1 && __ch == (ch); \
+  ok(expect, "At (%d,%d): expecting char '%c'/%02x got '%c'/%02x\n", (c).X, (c).Y, (ch), (ch), __ch, __ch); \
+  expect = ReadConsoleOutputAttribute((hCon), &__attr, 1, (c), &__len) == 1 && __len == 1 && __attr == (attr); \
+  ok(expect, "At (%d,%d): expecting attr %04x got %04x\n", (c).X, (c).Y, (attr), __attr); \
+} while (0)
+
+/* FIXME: this could be optimized on a speed point of view */
+static void resetContent(HANDLE hCon, COORD sbSize, BOOL content)
+{
+    COORD       c;
+    WORD        attr = DEFAULT_ATTRIB;
+    char        ch;
+    DWORD       len;
+
+    for (c.X = 0; c.X < sbSize.X; c.X++)
+    {
+        for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+        {
+            ch = (content) ? CONTENT(c) : ' ';
+            WriteConsoleOutputAttribute(hCon, &attr, 1, c, &len);
+            WriteConsoleOutputCharacterA(hCon, &ch, 1, c, &len);
+        }
+    }
+}
+
+static void testCursor(HANDLE hCon, COORD sbSize)
+{
+    COORD              c;
+
+    c.X = c.Y = 0;
+    ok(SetConsoleCursorPosition(0, c) == 0, "No handle\n");
+    ok(GetLastError() == ERROR_INVALID_HANDLE, "GetLastError: expecting %u got %lu\n",
+       ERROR_INVALID_HANDLE, GetLastError());
+
+    c.X = c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
+    okCURSOR(hCon, c);
+
+    c.X = sbSize.X - 1;
+    c.Y = sbSize.Y - 1;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in lower-right\n");
+    okCURSOR(hCon, c);
+
+    c.X = sbSize.X;
+    c.Y = sbSize.Y - 1;
+    ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
+       ERROR_INVALID_PARAMETER, GetLastError());
+
+    c.X = sbSize.X - 1;
+    c.Y = sbSize.Y;
+    ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
+       ERROR_INVALID_PARAMETER, GetLastError());
+
+    c.X = -1;
+    c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
+       ERROR_INVALID_PARAMETER, GetLastError());
+
+    c.X = 0;
+    c.Y = -1;
+    ok(SetConsoleCursorPosition(hCon, c) == 0, "Cursor is outside\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER, "GetLastError: expecting %u got %lu\n",
+       ERROR_INVALID_PARAMETER, GetLastError());
+}
+
+static void testWriteSimple(HANDLE hCon, COORD sbSize)
+{
+    COORD              c;
+    DWORD              len;
+    const char*                mytest = "abcdefg";
+    const int  mylen = strlen(mytest);
+
+    /* single line write */
+    c.X = c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (c.X = 0; c.X < mylen; c.X++)
+    {
+        okCHAR(hCon, c, mytest[c.X], TEST_ATTRIB);
+    }
+
+    okCURSOR(hCon, c);
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+}
+
+static void testWriteNotWrappedNotProcessed(HANDLE hCon, COORD sbSize)
+{
+    COORD              c;
+    DWORD              len, mode;
+    const char*                mytest = "abcd\nf\tg";
+    const int  mylen = strlen(mytest);
+    int                        p;
+
+    ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode & ~(ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT)),
+       "clearing wrap at EOL & processed output\n");
+
+    /* write line, wrapping disabled, buffer exceeds sb width */
+    c.X = sbSize.X - 3; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (p = mylen - 3; p < mylen; p++)
+    {
+        c.X = sbSize.X - 3 + p % 3;
+        okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+    }
+
+    c.X = 0; c.Y = 1;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+    p = sbSize.X - 3 + mylen % 3;
+    c.X = p; c.Y = 0;
+    okCURSOR(hCon, c);
+
+    /* write line, wrapping disabled, strings end on end of line */
+    c.X = sbSize.X - mylen; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (p = 0; p < mylen; p++)
+    {
+        c.X = sbSize.X - mylen + p;
+        okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+    }
+
+    c.X = 0; c.Y = 1;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+    p = sbSize.X - mylen;
+    c.X = p; c.Y = 0;
+    okCURSOR(hCon, c);
+}
+
+static void testWriteNotWrappedProcessed(HANDLE hCon, COORD sbSize)
+{
+    COORD              c;
+    DWORD              len, mode;
+    const char*                mytest = "abcd\nf\tg";
+    const int  mylen = strlen(mytest);
+    const int  mylen2 = strchr(mytest, '\n') - mytest;
+    int                        p;
+
+    ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, (mode | ENABLE_PROCESSED_OUTPUT) & ~ENABLE_WRAP_AT_EOL_OUTPUT),
+       "clearing wrap at EOL & setting processed output\n");
+
+    /* write line, wrapping disabled, buffer exceeds sb width */
+    c.X = sbSize.X - 5; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-5\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (c.X = sbSize.X - 5; c.X < sbSize.X - 1; c.X++)
+    {
+        okCHAR(hCon, c, mytest[c.X - sbSize.X + 5], TEST_ATTRIB);
+    }
+    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++)
+        okCHAR(hCon, c, ' ', TEST_ATTRIB);
+    okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+    c.X++;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+    okCURSOR(hCon, c);
+
+    /* write line, wrapping disabled, strings end on end of line */
+    c.X = sbSize.X - 4; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (c.X = sbSize.X - 4; c.X < sbSize.X; c.X++)
+    {
+        okCHAR(hCon, c, mytest[c.X - sbSize.X + 4], TEST_ATTRIB);
+    }
+    c.X = 0; c.Y++;
+    okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
+    for (c.X = 1; c.X < 8; c.X++)
+        okCHAR(hCon, c, ' ', TEST_ATTRIB);
+    okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+    c.X++;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+    okCURSOR(hCon, c);
+
+    /* write line, wrapping disabled, strings end after end of line */
+    c.X = sbSize.X - 3; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-4\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (p = mylen2 - 3; p < mylen2; p++)
+    {
+        c.X = sbSize.X - 3 + p % 3;
+        okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+    }
+    c.X = 0; c.Y = 1;
+    okCHAR(hCon, c, mytest[5], TEST_ATTRIB);
+    for (c.X = 1; c.X < 8; c.X++)
+        okCHAR(hCon, c, ' ', TEST_ATTRIB);
+    okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+    c.X++;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+    okCURSOR(hCon, c);
+}
+
+static void testWriteWrappedNotProcessed(HANDLE hCon, COORD sbSize)
+{
+    COORD              c;
+    DWORD              len, mode;
+    const char*                mytest = "abcd\nf\tg";
+    const int  mylen = strlen(mytest);
+    int                        p;
+
+    ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon,(mode | ENABLE_WRAP_AT_EOL_OUTPUT) & ~(ENABLE_PROCESSED_OUTPUT)),
+       "setting wrap at EOL & clearing processed output\n");
+
+    /* write line, wrapping enabled, buffer doesn't exceed sb width */
+    c.X = sbSize.X - 9; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (p = 0; p < mylen; p++)
+    {
+        c.X = sbSize.X - 9 + p;
+        okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+    }
+    c.X = sbSize.X - 9 + mylen;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+    c.X = 0; c.Y = 1;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+    /* write line, wrapping enabled, buffer does exceed sb width */
+    c.X = sbSize.X - 3; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    c.Y = 0;
+    for (p = 0; p < 3; p++)
+    {
+        c.X = sbSize.X - 3 + p;
+        okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+    }
+
+    c.Y = 1;
+    for (p = 0; p < mylen - 3; p++)
+    {
+        c.X = p;
+        okCHAR(hCon, c, mytest[p + 3], TEST_ATTRIB);
+    }
+    c.X = mylen - 3;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+
+    okCURSOR(hCon, c);
+}
+
+static void testWriteWrappedProcessed(HANDLE hCon, COORD sbSize)
+{
+    COORD              c;
+    DWORD              len, mode;
+    const char*                mytest = "abcd\nf\tg";
+    const int  mylen = strlen(mytest);
+    int                        p;
+
+    ok(GetConsoleMode(hCon, &mode) && SetConsoleMode(hCon, mode | (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)),
+       "setting wrap at EOL & processed output\n");
+
+    /* write line, wrapping enabled, buffer doesn't exceed sb width */
+    c.X = sbSize.X - 9; c.Y = 0;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-9\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    for (p = 0; p < 4; p++)
+    {
+        c.X = sbSize.X - 9 + p;
+        okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+    }
+    c.X = sbSize.X - 9 + p;
+    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++)
+        okCHAR(hCon, c, ' ', TEST_ATTRIB);
+    okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+    c.X++;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+    okCURSOR(hCon, c);
+
+    /* write line, wrapping enabled, buffer does exceed sb width */
+    c.X = sbSize.X - 3; c.Y = 2;
+    ok(SetConsoleCursorPosition(hCon, c) != 0, "Cursor in upper-left-3\n");
+
+    ok(WriteConsole(hCon, mytest, mylen, &len, NULL) != 0 && len == mylen, "WriteConsole\n");
+    for (p = 0; p < 3; p++)
+    {
+        c.X = sbSize.X - 3 + p;
+        okCHAR(hCon, c, mytest[p], TEST_ATTRIB);
+    }
+    c.X = 0; c.Y++;
+    okCHAR(hCon, c, mytest[3], TEST_ATTRIB);
+    c.X++;
+    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++)
+        okCHAR(hCon, c, ' ', TEST_ATTRIB);
+    okCHAR(hCon, c, mytest[7], TEST_ATTRIB);
+    c.X++;
+    okCHAR(hCon, c, ' ', DEFAULT_ATTRIB);
+    okCURSOR(hCon, c);
+}
+
+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);
+    resetContent(hCon, sbSize, FALSE);
+    testWriteNotWrappedNotProcessed(hCon, sbSize);
+    resetContent(hCon, sbSize, FALSE);
+    testWriteNotWrappedProcessed(hCon, sbSize);
+    resetContent(hCon, sbSize, FALSE);
+    testWriteWrappedNotProcessed(hCon, sbSize);
+    resetContent(hCon, sbSize, FALSE);
+    testWriteWrappedProcessed(hCon, sbSize);
+}
+
+#if 0
+static void testScroll(HANDLE hCon, COORD sbSize)
+{
+    SMALL_RECT  scroll, clip;
+    COORD       dst, c, tc;
+    CHAR_INFO   ci;
+
+#define W 11
+#define H 7
+
+    /* no clipping, src & dst rect don't overlap */
+    resetContent(hCon, sbSize, TRUE);
+
+#define IN_SRECT(r,c) ((r).Left <= (c).X && (c).X <= (r).Right && (r).Top <= (c).Y && (c).Y <= (r).Bottom)
+#define IN_SRECT2(r,d,c) ((d).X <= (c).X && (c).X <= (d).X + (r).Right - (r).Left && (d).Y <= (c).Y && (c).Y <= (d).Y + (r).Bottom - (r).Top)
+
+    scroll.Left = 0;
+    scroll.Right = W - 1;
+    scroll.Top = 0;
+    scroll.Bottom = H - 1;
+    dst.X = W + 3;
+    dst.Y = H + 3;
+    ci.Char.UnicodeChar = '#';
+    ci.Attributes = TEST_ATTRIB;
+
+    clip.Left = 0;
+    clip.Right = sbSize.X - 1;
+    clip.Top = 0;
+    clip.Bottom = sbSize.Y - 1;
+
+    ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
+
+    for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+    {
+        for (c.X = 0; c.X < sbSize.X; c.X++)
+        {
+            if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
+            {
+                tc.X = c.X - dst.X;
+                tc.Y = c.Y - dst.Y;
+                okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+            }
+            else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
+                okCHAR(hCon, c, '#', TEST_ATTRIB);
+            else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+        }
+    }
+
+    /* no clipping, src & dst rect do overlap */
+    resetContent(hCon, sbSize, TRUE);
+
+    scroll.Left = 0;
+    scroll.Right = W - 1;
+    scroll.Top = 0;
+    scroll.Bottom = H - 1;
+    dst.X = W /2;
+    dst.Y = H / 2;
+    ci.Char.UnicodeChar = '#';
+    ci.Attributes = TEST_ATTRIB;
+
+    clip.Left = 0;
+    clip.Right = sbSize.X - 1;
+    clip.Top = 0;
+    clip.Bottom = sbSize.Y - 1;
+
+    ok(ScrollConsoleScreenBuffer(hCon, &scroll, NULL, dst, &ci), "Scrolling SB\n");
+
+    for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+    {
+        for (c.X = 0; c.X < sbSize.X; c.X++)
+        {
+            if (dst.X <= c.X && c.X < dst.X + W && dst.Y <= c.Y && c.Y < dst.Y + H)
+            {
+                tc.X = c.X - dst.X;
+                tc.Y = c.Y - dst.Y;
+                okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+            }
+            else if (c.X < W && c.Y < H) okCHAR(hCon, c, '#', TEST_ATTRIB);
+            else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+        }
+    }
+
+    /* clipping, src & dst rect don't overlap */
+    resetContent(hCon, sbSize, TRUE);
+
+    scroll.Left = 0;
+    scroll.Right = W - 1;
+    scroll.Top = 0;
+    scroll.Bottom = H - 1;
+    dst.X = W + 3;
+    dst.Y = H + 3;
+    ci.Char.UnicodeChar = '#';
+    ci.Attributes = TEST_ATTRIB;
+
+    clip.Left = W / 2;
+    clip.Right = min(W + W / 2, sbSize.X - 1);
+    clip.Top = H / 2;
+    clip.Bottom = min(H + H / 2, sbSize.Y - 1);
+
+    ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
+
+    for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+    {
+        for (c.X = 0; c.X < sbSize.X; c.X++)
+        {
+            if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
+            {
+                tc.X = c.X - dst.X;
+                tc.Y = c.Y - dst.Y;
+                okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+            }
+            else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
+                okCHAR(hCon, c, '#', TEST_ATTRIB);
+            else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+        }
+    }
+
+    /* clipping, src & dst rect do overlap */
+    resetContent(hCon, sbSize, TRUE);
+
+    scroll.Left = 0;
+    scroll.Right = W - 1;
+    scroll.Top = 0;
+    scroll.Bottom = H - 1;
+    dst.X = W / 2 - 3;
+    dst.Y = H / 2 - 3;
+    ci.Char.UnicodeChar = '#';
+    ci.Attributes = TEST_ATTRIB;
+
+    clip.Left = W / 2;
+    clip.Right = min(W + W / 2, sbSize.X - 1);
+    clip.Top = H / 2;
+    clip.Bottom = min(H + H / 2, sbSize.Y - 1);
+
+    ok(ScrollConsoleScreenBuffer(hCon, &scroll, &clip, dst, &ci), "Scrolling SB\n");
+
+    for (c.Y = 0; c.Y < sbSize.Y; c.Y++)
+    {
+        for (c.X = 0; c.X < sbSize.X; c.X++)
+        {
+            if (IN_SRECT2(scroll, dst, c) && IN_SRECT(clip, c))
+            {
+                tc.X = c.X - dst.X;
+                tc.Y = c.Y - dst.Y;
+                okCHAR(hCon, c, CONTENT(tc), DEFAULT_ATTRIB);
+            }
+            else if (IN_SRECT(scroll, c) && IN_SRECT(clip, c))
+                okCHAR(hCon, c, '#', TEST_ATTRIB);
+            else okCHAR(hCon, c, CONTENT(c), DEFAULT_ATTRIB);
+        }
+    }
+}
+#endif
+
+START_TEST(console)
+{
+    HANDLE hConIn, hConOut;
+    BOOL ret;
+    CONSOLE_SCREEN_BUFFER_INFO sbi;
+
+    /* be sure we have a clean console (and that's our own)
+     * FIXME: this will make the test fail (currently) if we don't run
+     * under X11
+     * Another solution would be to rerun the test under wineconsole with
+     * the curses backend
+     */
+
+    hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+
+    /* first, we need to be sure we're attached to a console */
+    if (hConIn == INVALID_HANDLE_VALUE || hConOut == INVALID_HANDLE_VALUE)
+    {
+        /* we're not attached to a console, let's do it */
+        AllocConsole();
+        hConIn = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+        hConOut = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    }
+    /* now verify everything's ok */
+    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");
+    if (!ret) return;
+
+    /* Non interactive tests */
+    testCursor(hConOut, sbi.dwSize);
+    /* will test wrapped (on/off) & processed (on/off) strings output */
+    testWrite(hConOut, sbi.dwSize);
+    /* will test line scrolling at the bottom of the screen */
+    /* testBottomScroll(); */
+    /* will test all the scrolling operations */
+    /* this one is disabled for now, Wine's result are way too bad */
+    /* testScroll(hCon, sbi.dwSize); */
+    /* will test sb creation / modification... */
+    /* testScreenBuffer() */
+
+    /* still to be done: events generation, access rights & access on objects */
+}
diff --git a/reactos/apps/tests/kernel32/directory.c b/reactos/apps/tests/kernel32/directory.c
new file mode 100644 (file)
index 0000000..a5b81eb
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Unit test suite for directory functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+/* If you change something in these tests, please do the same
+ * for GetSystemDirectory tests.
+ */
+static void test_GetWindowsDirectoryA(void)
+{
+    UINT len, len_with_null;
+    char buf[MAX_PATH];
+
+    len_with_null = GetWindowsDirectoryA(NULL, 0);
+    ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+    lstrcpyA(buf, "foo");
+    len_with_null = GetWindowsDirectoryA(buf, 1);
+    ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+
+    lstrcpyA(buf, "foo");
+    len = GetWindowsDirectoryA(buf, len_with_null - 1);
+    ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+    ok(len == len_with_null, "GetWindowsDirectoryW returned %d, expected %d\n",
+       len, len_with_null);
+
+    lstrcpyA(buf, "foo");
+    len = GetWindowsDirectoryA(buf, len_with_null);
+    ok(lstrcmpA(buf, "foo") != 0, "should touch the buffer\n");
+    ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+    ok(len == len_with_null-1, "GetWindowsDirectoryA returned %d, expected %d\n",
+       len, len_with_null-1);
+}
+
+static void test_GetWindowsDirectoryW(void)
+{
+    UINT len, len_with_null;
+    WCHAR buf[MAX_PATH];
+    static const WCHAR fooW[] = {'f','o','o',0};
+
+    len_with_null = GetWindowsDirectoryW(NULL, 0);
+    if (len_with_null==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+    lstrcpyW(buf, fooW);
+    len = GetWindowsDirectoryW(buf, 1);
+    ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+    ok(len == len_with_null, "GetWindowsDirectoryW returned %d, expected %d\n",
+       len, len_with_null);
+
+    lstrcpyW(buf, fooW);
+    len = GetWindowsDirectoryW(buf, len_with_null - 1);
+    ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+    ok(len == len_with_null, "GetWindowsDirectoryW returned %d, expected %d\n",
+       len, len_with_null);
+
+    lstrcpyW(buf, fooW);
+    len = GetWindowsDirectoryW(buf, len_with_null);
+    ok(lstrcmpW(buf, fooW) != 0, "should touch the buffer\n");
+    ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+    ok(len == len_with_null-1, "GetWindowsDirectoryW returned %d, expected %d\n",
+       len, len_with_null-1);
+}
+
+
+/* If you change something in these tests, please do the same
+ * for GetWindowsDirectory tests.
+ */
+static void test_GetSystemDirectoryA(void)
+{
+    UINT len, len_with_null;
+    char buf[MAX_PATH];
+
+    len_with_null = GetSystemDirectoryA(NULL, 0);
+    ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+    lstrcpyA(buf, "foo");
+    len = GetSystemDirectoryA(buf, 1);
+    ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+    ok(len == len_with_null, "GetSystemDirectoryA returned %d, expected %d\n",
+       len, len_with_null);
+
+    lstrcpyA(buf, "foo");
+    len = GetSystemDirectoryA(buf, len_with_null - 1);
+    ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+    ok(len == len_with_null, "GetSystemDirectoryA returned %d, expected %d\n",
+       len, len_with_null);
+
+    lstrcpyA(buf, "foo");
+    len = GetSystemDirectoryA(buf, len_with_null);
+    ok(lstrcmpA(buf, "foo") != 0, "should touch the buffer\n");
+    ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+    ok(len == len_with_null-1, "GetSystemDirectoryW returned %d, expected %d\n",
+       len, len_with_null-1);
+}
+
+static void test_GetSystemDirectoryW(void)
+{
+    UINT len, len_with_null;
+    WCHAR buf[MAX_PATH];
+    static const WCHAR fooW[] = {'f','o','o',0};
+
+    len_with_null = GetSystemDirectoryW(NULL, 0);
+    if (len_with_null==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(len_with_null <= MAX_PATH, "should fit into MAX_PATH\n");
+
+    lstrcpyW(buf, fooW);
+    len = GetSystemDirectoryW(buf, 1);
+    ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+    ok(len == len_with_null, "GetSystemDirectoryW returned %d, expected %d\n",
+       len, len_with_null);
+
+    lstrcpyW(buf, fooW);
+    len = GetSystemDirectoryW(buf, len_with_null - 1);
+    ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+    ok(len == len_with_null, "GetSystemDirectoryW returned %d, expected %d\n",
+       len, len_with_null);
+
+    lstrcpyW(buf, fooW);
+    len = GetSystemDirectoryW(buf, len_with_null);
+    ok(lstrcmpW(buf, fooW) != 0, "should touch the buffer\n");
+    ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+    ok(len == len_with_null-1, "GetSystemDirectoryW returned %d, expected %d\n",
+       len, len_with_null-1);
+}
+
+static void test_CreateDirectoryA(void)
+{
+    char tmpdir[MAX_PATH];
+    BOOL ret;
+
+    ret = CreateDirectoryA(NULL, NULL);
+    ok(ret == FALSE && (GetLastError() == ERROR_PATH_NOT_FOUND ||
+                        GetLastError() == ERROR_INVALID_PARAMETER),
+       "CreateDirectoryA(NULL,NULL): ret=%d error=%ld\n",ret,GetLastError());
+
+    ret = CreateDirectoryA("", NULL);
+    ok(ret == FALSE && (GetLastError() == ERROR_BAD_PATHNAME ||
+                        GetLastError() == ERROR_PATH_NOT_FOUND),
+       "CreateDirectoryA(\"\",NULL): ret=%d error=%ld\n",ret,GetLastError());
+
+    ret = GetSystemDirectoryA(tmpdir, MAX_PATH);
+    ok(ret < MAX_PATH, "System directory should fit into MAX_PATH\n");
+
+    ret = SetCurrentDirectoryA(tmpdir);
+    ok(ret == TRUE, "could not chdir to the System directory\n");
+
+    ret = CreateDirectoryA(".", NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+    ret = CreateDirectoryA("..", NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+    GetTempPathA(MAX_PATH, tmpdir);
+    tmpdir[3] = 0; /* truncate the path */
+    ret = CreateDirectoryA(tmpdir, NULL);
+    ok(ret == FALSE && (GetLastError() == ERROR_ALREADY_EXISTS ||
+                        GetLastError() == ERROR_ACCESS_DENIED),
+       "CreateDirectoryA(drive_root): ret=%d error=%ld\n",ret,GetLastError());
+
+    GetTempPathA(MAX_PATH, tmpdir);
+    lstrcatA(tmpdir, "Please Remove Me");
+    ret = CreateDirectoryA(tmpdir, NULL);
+    ok(ret == TRUE, "CreateDirectoryA should always succeed\n");
+
+    ret = CreateDirectoryA(tmpdir, NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+    ret = RemoveDirectoryA(tmpdir);
+    ok(ret == TRUE, "RemoveDirectoryA should always succeed\n");
+
+    todo_wine {
+      lstrcatA(tmpdir, "?");
+      ret = CreateDirectoryA(tmpdir, NULL);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "CreateDirectoryA with ? wildcard name should fail, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+      ret = RemoveDirectoryA(tmpdir);
+
+      tmpdir[lstrlenA(tmpdir) - 1] = '*';
+      ret = CreateDirectoryA(tmpdir, NULL);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "CreateDirectoryA with * wildcard name should fail, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+      ret = RemoveDirectoryA(tmpdir);
+    }
+}
+
+static void test_CreateDirectoryW(void)
+{
+    WCHAR tmpdir[MAX_PATH];
+    BOOL ret;
+    static const WCHAR empty_strW[] = { 0 };
+    static const WCHAR tmp_dir_name[] = {'P','l','e','a','s','e',' ','R','e','m','o','v','e',' ','M','e',0};
+    static const WCHAR dotW[] = {'.',0};
+    static const WCHAR dotdotW[] = {'.','.',0};
+    static const WCHAR questionW[] = {'?',0};
+
+    ret = CreateDirectoryW(NULL, NULL);
+    if (!ret && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND, "should not create NULL path\n");
+
+    ret = CreateDirectoryW(empty_strW, NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_PATH_NOT_FOUND, "should not create empty path\n");
+
+    ret = GetSystemDirectoryW(tmpdir, MAX_PATH);
+    ok(ret < MAX_PATH, "System directory should fit into MAX_PATH\n");
+
+    ret = SetCurrentDirectoryW(tmpdir);
+    ok(ret == TRUE, "could not chdir to the System directory\n");
+
+    ret = CreateDirectoryW(dotW, NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+    ret = CreateDirectoryW(dotdotW, NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+    GetTempPathW(MAX_PATH, tmpdir);
+    tmpdir[3] = 0; /* truncate the path */
+    ret = CreateDirectoryW(tmpdir, NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_ACCESS_DENIED, "should deny access to the drive root\n");
+
+    GetTempPathW(MAX_PATH, tmpdir);
+    lstrcatW(tmpdir, tmp_dir_name);
+    ret = CreateDirectoryW(tmpdir, NULL);
+    ok(ret == TRUE, "CreateDirectoryW should always succeed\n");
+
+    ret = CreateDirectoryW(tmpdir, NULL);
+    ok(ret == FALSE && GetLastError() == ERROR_ALREADY_EXISTS, "should not create existing path\n");
+
+    ret = RemoveDirectoryW(tmpdir);
+    ok(ret == TRUE, "RemoveDirectoryW should always succeed\n");
+
+    todo_wine {
+      lstrcatW(tmpdir, questionW);
+      ret = CreateDirectoryW(tmpdir, NULL);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "CreateDirectoryW with ? wildcard name should fail with error 183, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+      ret = RemoveDirectoryW(tmpdir);
+
+      tmpdir[lstrlenW(tmpdir) - 1] = '*';
+      ret = CreateDirectoryW(tmpdir, NULL);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "CreateDirectoryW with * wildcard name should fail with error 183, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+      ret = RemoveDirectoryW(tmpdir);
+    }
+}
+
+static void test_RemoveDirectoryA(void)
+{
+    char tmpdir[MAX_PATH];
+    BOOL ret;
+
+    GetTempPathA(MAX_PATH, tmpdir);
+    lstrcatA(tmpdir, "Please Remove Me");
+    ret = CreateDirectoryA(tmpdir, NULL);
+    ok(ret == TRUE, "CreateDirectoryA should always succeed\n");
+
+    ret = RemoveDirectoryA(tmpdir);
+    ok(ret == TRUE, "RemoveDirectoryA should always succeed\n");
+
+    todo_wine {
+      lstrcatA(tmpdir, "?");
+      ret = RemoveDirectoryA(tmpdir);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "RemoveDirectoryA with ? wildcard name should fail with error 183, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+
+      tmpdir[lstrlenA(tmpdir) - 1] = '*';
+      ret = RemoveDirectoryA(tmpdir);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "RemoveDirectoryA with * wildcard name should fail with error 183, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+    }
+}
+
+static void test_RemoveDirectoryW(void)
+{
+    WCHAR tmpdir[MAX_PATH];
+    BOOL ret;
+    static const WCHAR tmp_dir_name[] = {'P','l','e','a','s','e',' ','R','e','m','o','v','e',' ','M','e',0};
+    static const WCHAR questionW[] = {'?',0};
+
+    GetTempPathW(MAX_PATH, tmpdir);
+    lstrcatW(tmpdir, tmp_dir_name);
+    ret = CreateDirectoryW(tmpdir, NULL);
+    if (!ret && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+      return;
+
+    ok(ret == TRUE, "CreateDirectoryW should always succeed\n");
+
+    ret = RemoveDirectoryW(tmpdir);
+    ok(ret == TRUE, "RemoveDirectoryW should always succeed\n");
+
+    todo_wine {
+      lstrcatW(tmpdir, questionW);
+      ret = RemoveDirectoryW(tmpdir);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "RemoveDirectoryW with wildcard should fail with error 183, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+
+      tmpdir[lstrlenW(tmpdir) - 1] = '*';
+      ret = RemoveDirectoryW(tmpdir);
+      ok(ret == FALSE && GetLastError() == ERROR_INVALID_NAME,
+         "RemoveDirectoryW with * wildcard name should fail with error 183, ret=%s error=%ld\n",
+         ret ? " True" : "False", GetLastError());
+    }
+
+}
+
+START_TEST(directory)
+{
+    test_GetWindowsDirectoryA();
+    test_GetWindowsDirectoryW();
+
+    test_GetSystemDirectoryA();
+    test_GetSystemDirectoryW();
+
+    test_CreateDirectoryA();
+    test_CreateDirectoryW();
+
+    test_RemoveDirectoryA();
+    test_RemoveDirectoryW();
+}
diff --git a/reactos/apps/tests/kernel32/drive.c b/reactos/apps/tests/kernel32/drive.c
new file mode 100644 (file)
index 0000000..085f317
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Unit test suite for drive functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+void test_GetDriveTypeA(void)
+{
+    char drive[] = "?:\\";
+    DWORD logical_drives;
+    UINT type;
+
+    logical_drives = GetLogicalDrives();
+    ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+    for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+    {
+        type = GetDriveTypeA(drive);
+        ok(type > 0 && type <= 6, "not a valid drive %c: type %u\n", drive[0], type);
+
+        if (!(logical_drives & 1))
+            ok(type == DRIVE_NO_ROOT_DIR,
+               "GetDriveTypeA should return DRIVE_NO_ROOT_DIR for inexistant drive %c: but not %u\n",
+               drive[0], type);
+
+        logical_drives >>= 1;
+    }
+}
+
+void test_GetDriveTypeW(void)
+{
+    WCHAR drive[] = {'?',':','\\',0};
+    DWORD logical_drives;
+    UINT type;
+
+    logical_drives = GetLogicalDrives();
+    ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+    for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+    {
+        type = GetDriveTypeW(drive);
+        if (type == DRIVE_UNKNOWN && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        {
+            /* Must be Win9x which doesn't support the Unicode functions */
+            return;
+        }
+        ok(type > 0 && type <= 6, "not a valid drive %c: type %u\n", drive[0], type);
+
+        if (!(logical_drives & 1))
+            ok(type == DRIVE_NO_ROOT_DIR,
+               "GetDriveTypeW should return DRIVE_NO_ROOT_DIR for inexistant drive %c: but not %u\n",
+               drive[0], type);
+
+        logical_drives >>= 1;
+    }
+}
+
+void test_GetDiskFreeSpaceA(void)
+{
+    BOOL ret;
+    DWORD sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters;
+    char drive[] = "?:\\";
+    DWORD logical_drives;
+
+    ret = GetDiskFreeSpaceA(NULL, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    ok(ret, "GetDiskFreeSpaceA error %ld\n", GetLastError());
+
+    ret = GetDiskFreeSpaceA("", &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    ok(!ret && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_INVALID_NAME),
+       "GetDiskFreeSpaceA(\"\"): ret=%d GetLastError=%ld\n",
+       ret, GetLastError());
+
+    ret = GetDiskFreeSpaceA("\\", &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    ok(ret, "GetDiskFreeSpaceA error %ld\n", GetLastError());
+
+    ret = GetDiskFreeSpaceA("/", &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    ok(ret, "GetDiskFreeSpaceA error %ld\n", GetLastError());
+
+    logical_drives = GetLogicalDrives();
+    ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+    for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+    {
+        /* Skip floppy drives because NT pops up a MessageBox if no
+         * floppy is present
+         */
+        if (GetDriveTypeA(drive)!=DRIVE_REMOVABLE)
+        {
+            ret = GetDiskFreeSpaceA(drive, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+            if (!(logical_drives & 1))
+                ok(!ret && (GetLastError() == ERROR_PATH_NOT_FOUND || GetLastError() == ERROR_INVALID_DRIVE),
+                   "GetDiskFreeSpaceA(%s): ret=%d GetLastError=%ld\n",
+                   drive, ret, GetLastError());
+            else
+                ok(ret ||
+                   (!ret && (GetLastError() == ERROR_NOT_READY || GetLastError() == ERROR_INVALID_DRIVE)),
+                   "GetDiskFreeSpaceA(%s): ret=%d GetLastError=%ld\n",
+                   drive, ret, GetLastError());
+        }
+        logical_drives >>= 1;
+    }
+}
+
+void test_GetDiskFreeSpaceW(void)
+{
+    BOOL ret;
+    DWORD sectors_per_cluster, bytes_per_sector, free_clusters, total_clusters;
+    WCHAR drive[] = {'?',':','\\',0};
+    DWORD logical_drives;
+    static const WCHAR empty_pathW[] = { 0 };
+    static const WCHAR root_pathW[] = { '\\', 0 };
+    static const WCHAR unix_style_root_pathW[] = { '/', 0 };
+
+    ret = GetDiskFreeSpaceW(NULL, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    if (ret == 0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        /* Must be Win9x which doesn't support the Unicode functions */
+        return;
+    }
+    ok(ret, "GetDiskFreeSpaceW error %ld\n", GetLastError());
+
+    ret = GetDiskFreeSpaceW(empty_pathW, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+       "GetDiskFreeSpaceW(\"\"): ret=%d GetLastError=%ld\n",
+       ret, GetLastError());
+
+    ret = GetDiskFreeSpaceW(root_pathW, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    ok(ret, "GetDiskFreeSpaceW(\"\") error %ld\n", GetLastError());
+
+    ret = GetDiskFreeSpaceW(unix_style_root_pathW, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+    ok(ret, "GetDiskFreeSpaceW error %ld\n", GetLastError());
+
+    logical_drives = GetLogicalDrives();
+    ok(logical_drives != 0, "GetLogicalDrives error %ld\n", GetLastError());
+
+    for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++)
+    {
+        /* Skip floppy drives because NT4 pops up a MessageBox if no floppy is present */
+        if (GetDriveTypeW(drive)!=DRIVE_REMOVABLE)
+        {
+            ret = GetDiskFreeSpaceW(drive, &sectors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters);
+            if (!(logical_drives & 1))
+                ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+                   "GetDiskFreeSpaceW(%c): ret=%d GetLastError=%ld\n",
+                   drive[0], ret, GetLastError());
+            else
+                ok(ret || GetLastError() == ERROR_NOT_READY,
+                   "GetDiskFreeSpaceW(%c): ret=%d GetLastError=%ld\n",
+                   drive[0], ret, GetLastError());
+        }
+        logical_drives >>= 1;
+    }
+}
+
+START_TEST(drive)
+{
+    test_GetDriveTypeA();
+    test_GetDriveTypeW();
+
+    test_GetDiskFreeSpaceA();
+    test_GetDiskFreeSpaceW();
+}
diff --git a/reactos/apps/tests/kernel32/environ.c b/reactos/apps/tests/kernel32/environ.c
new file mode 100644 (file)
index 0000000..ed32703
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Unit test suite for environment functions.
+ *
+ * Copyright 2002 Dmitry Timoshkov
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+static void test_GetSetEnvironmentVariableA(void)
+{
+    char buf[256];
+    BOOL ret;
+    DWORD ret_size;
+    static const char name[] = "SomeWildName";
+    static const char name_cased[] = "sOMEwILDnAME";
+    static const char value[] = "SomeWildValue";
+
+    ret = SetEnvironmentVariableA(name, value);
+    ok(ret == TRUE,
+       "unexpected error in SetEnvironmentVariableA, GetLastError=%ld\n",
+       GetLastError());
+
+    /* Try to retrieve the environment variable we just set */
+    ret_size = GetEnvironmentVariableA(name, NULL, 0);
+    ok(ret_size == strlen(value) + 1,
+       "should return length with terminating 0 ret_size=%ld\n", ret_size);
+
+    lstrcpyA(buf, "foo");
+    ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value));
+    ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+    ok(ret_size == strlen(value) + 1,
+       "should return length with terminating 0 ret_size=%ld\n", ret_size);
+
+    lstrcpyA(buf, "foo");
+    ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value) + 1);
+    ok(lstrcmpA(buf, value) == 0, "should touch the buffer\n");
+    ok(ret_size == strlen(value),
+       "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+    lstrcpyA(buf, "foo");
+    ret_size = GetEnvironmentVariableA(name_cased, buf, lstrlenA(value) + 1);
+    ok(lstrcmpA(buf, value) == 0, "should touch the buffer\n");
+    ok(ret_size == strlen(value),
+       "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+    /* Remove that environment variable */
+    ret = SetEnvironmentVariableA(name_cased, NULL);
+    ok(ret == TRUE, "should erase existing variable\n");
+
+    lstrcpyA(buf, "foo");
+    ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value) + 1);
+    ok(lstrcmpA(buf, "foo") == 0, "should not touch the buffer\n");
+    ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+
+    /* Check behavior of SetEnvironmentVariableA(name, "") */
+    ret = SetEnvironmentVariableA(name, value);
+    ok(ret == TRUE,
+       "unexpected error in SetEnvironmentVariableA, GetLastError=%ld\n",
+       GetLastError());
+
+    lstrcpyA(buf, "foo");
+    ret_size = GetEnvironmentVariableA(name_cased, buf, lstrlenA(value) + 1);
+    ok(lstrcmpA(buf, value) == 0, "should touch the buffer\n");
+    ok(ret_size == strlen(value),
+       "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+    ret = SetEnvironmentVariableA(name_cased, "");
+    ok(ret == TRUE,
+       "should not fail with empty value but GetLastError=%ld\n", GetLastError());
+
+    lstrcpyA(buf, "foo");
+    SetLastError(0);
+    ret_size = GetEnvironmentVariableA(name, buf, lstrlenA(value) + 1);
+    ok(ret_size == 0 &&
+       ((GetLastError() == 0 && lstrcmpA(buf, "") == 0) ||
+        (GetLastError() == ERROR_ENVVAR_NOT_FOUND)),
+       "%s should be set to \"\" (NT) or removed (Win9x) but ret_size=%ld GetLastError=%ld and buf=%s\n",
+       name, ret_size, GetLastError(), buf);
+
+    /* Test the limits */
+    ret_size = GetEnvironmentVariableA(NULL, NULL, 0);
+    ok(ret_size == 0 && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_ENVVAR_NOT_FOUND),
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+
+    ret_size = GetEnvironmentVariableA(NULL, buf, lstrlenA(value) + 1);
+    ok(ret_size == 0 && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_ENVVAR_NOT_FOUND),
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+
+    ret_size = GetEnvironmentVariableA("", buf, lstrlenA(value) + 1);
+    ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+}
+
+static void test_GetSetEnvironmentVariableW(void)
+{
+    WCHAR buf[256];
+    BOOL ret;
+    DWORD ret_size;
+    static const WCHAR name[] = {'S','o','m','e','W','i','l','d','N','a','m','e',0};
+    static const WCHAR value[] = {'S','o','m','e','W','i','l','d','V','a','l','u','e',0};
+    static const WCHAR name_cased[] = {'s','O','M','E','w','I','L','D','n','A','M','E',0};
+    static const WCHAR empty_strW[] = { 0 };
+    static const WCHAR fooW[] = {'f','o','o',0};
+
+    ret = SetEnvironmentVariableW(name, value);
+    if (ret == FALSE && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        /* Must be Win9x which doesn't support the Unicode functions */
+        return;
+    }
+    ok(ret == TRUE,
+       "unexpected error in SetEnvironmentVariableW, GetLastError=%ld\n",
+       GetLastError());
+
+    /* Try to retrieve the environment variable we just set */
+    ret_size = GetEnvironmentVariableW(name, NULL, 0);
+    ok(ret_size == lstrlenW(value) + 1,
+       "should return length with terminating 0 ret_size=%ld\n",
+       ret_size);
+
+    lstrcpyW(buf, fooW);
+    ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value));
+    ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+
+    ok(ret_size == lstrlenW(value) + 1,
+       "should return length with terminating 0 ret_size=%ld\n", ret_size);
+
+    lstrcpyW(buf, fooW);
+    ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+    ok(lstrcmpW(buf, value) == 0, "should touch the buffer\n");
+    ok(ret_size == lstrlenW(value),
+       "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+    lstrcpyW(buf, fooW);
+    ret_size = GetEnvironmentVariableW(name_cased, buf, lstrlenW(value) + 1);
+    ok(lstrcmpW(buf, value) == 0, "should touch the buffer\n");
+    ok(ret_size == lstrlenW(value),
+       "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+    /* Remove that environment variable */
+    ret = SetEnvironmentVariableW(name_cased, NULL);
+    ok(ret == TRUE, "should erase existing variable\n");
+
+    lstrcpyW(buf, fooW);
+    ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+    ok(lstrcmpW(buf, fooW) == 0, "should not touch the buffer\n");
+    ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+
+    /* Check behavior of SetEnvironmentVariableW(name, "") */
+    ret = SetEnvironmentVariableW(name, value);
+    ok(ret == TRUE,
+       "unexpected error in SetEnvironmentVariableW, GetLastError=%ld\n",
+       GetLastError());
+
+    lstrcpyW(buf, fooW);
+    ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+    ok(lstrcmpW(buf, value) == 0, "should touch the buffer\n");
+    ok(ret_size == lstrlenW(value),
+       "should return length without terminating 0 ret_size=%ld\n", ret_size);
+
+    ret = SetEnvironmentVariableW(name_cased, empty_strW);
+    ok(ret == TRUE, "should not fail with empty value but GetLastError=%ld\n", GetLastError());
+
+    lstrcpyW(buf, fooW);
+    ret_size = GetEnvironmentVariableW(name, buf, lstrlenW(value) + 1);
+    ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+    ok(lstrcmpW(buf, empty_strW) == 0, "should copy an empty string\n");
+
+    /* Test the limits */
+    ret_size = GetEnvironmentVariableW(NULL, NULL, 0);
+    ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+
+    ret_size = GetEnvironmentVariableW(NULL, buf, lstrlenW(value) + 1);
+    ok(ret_size == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND,
+       "should not find variable but ret_size=%ld GetLastError=%ld\n",
+       ret_size, GetLastError());
+
+    ret = SetEnvironmentVariableW(NULL, NULL);
+    ok(ret == FALSE && (GetLastError() == ERROR_INVALID_PARAMETER || GetLastError() == ERROR_ENVVAR_NOT_FOUND),
+       "should fail with NULL, NULL but ret=%d and GetLastError=%ld\n",
+       ret, GetLastError());
+}
+
+START_TEST(environ)
+{
+    test_GetSetEnvironmentVariableA();
+    test_GetSetEnvironmentVariableW();
+}
diff --git a/reactos/apps/tests/kernel32/file.c b/reactos/apps/tests/kernel32/file.c
new file mode 100644 (file)
index 0000000..1e754d6
--- /dev/null
@@ -0,0 +1,1006 @@
+/*
+ * Unit tests for file functions in Wine
+ *
+ * Copyright (c) 2002 Jakob Eriksson
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+
+LPCSTR filename = "testfile.xxx";
+LPCSTR sillytext =
+"en larvig liten text dx \033 gx hej 84 hej 4484 ! \001\033 bla bl\na.. bla bla."
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"1234 43 4kljf lf &%%%&&&&&& 34 4 34   3############# 33 3 3 3 # 3## 3"
+"sdlkfjasdlkfj a dslkj adsklf  \n  \nasdklf askldfa sdlkf \nsadklf asdklf asdf ";
+
+
+static void test__hread( void )
+{
+    HFILE filehandle;
+    char buffer[10000];
+    long bytes_read;
+    long bytes_wanted;
+    long i;
+
+    SetFileAttributesA(filename,FILE_ATTRIBUTE_NORMAL); /* be sure to remove stale files */
+    DeleteFileA( filename );
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READ );
+
+    ok( HFILE_ERROR != filehandle, "couldn't open file \"%s\" again (err=%ld)\n", filename, GetLastError(  ) );
+
+    bytes_read = _hread( filehandle, buffer, 2 * strlen( sillytext ) );
+
+    ok( lstrlenA( sillytext ) == bytes_read, "file read size error\n" );
+
+    for (bytes_wanted = 0; bytes_wanted < lstrlenA( sillytext ); bytes_wanted++)
+    {
+        ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+        ok( _hread( filehandle, buffer, bytes_wanted ) == bytes_wanted, "erratic _hread return value\n" );
+        for (i = 0; i < bytes_wanted; i++)
+        {
+            ok( buffer[i] == sillytext[i], "that's not what's written\n" );
+        }
+    }
+
+    ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+}
+
+
+static void test__hwrite( void )
+{
+    HFILE filehandle;
+    char buffer[10000];
+    long bytes_read;
+    long bytes_written;
+    long blocks;
+    long i;
+    char *contents;
+    HLOCAL memory_object;
+    char checksum[1];
+
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    ok( HFILE_ERROR != _hwrite( filehandle, "", 0 ), "_hwrite complains\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READ );
+
+    bytes_read = _hread( filehandle, buffer, 1);
+
+    ok( 0 == bytes_read, "file read size error\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READWRITE );
+
+    bytes_written = 0;
+    checksum[0] = '\0';
+    srand( (unsigned)time( NULL ) );
+    for (blocks = 0; blocks < 100; blocks++)
+    {
+        for (i = 0; i < (long)sizeof( buffer ); i++)
+        {
+            buffer[i] = rand(  );
+            checksum[0] = checksum[0] + buffer[i];
+        }
+        ok( HFILE_ERROR != _hwrite( filehandle, buffer, sizeof( buffer ) ), "_hwrite complains\n" );
+        bytes_written = bytes_written + sizeof( buffer );
+    }
+
+    ok( HFILE_ERROR != _hwrite( filehandle, checksum, 1 ), "_hwrite complains\n" );
+    bytes_written++;
+
+    ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+    memory_object = LocalAlloc( LPTR, bytes_written );
+
+    ok( 0 != memory_object, "LocalAlloc fails. (Could be out of memory.)\n" );
+
+    contents = LocalLock( memory_object );
+
+    filehandle = _lopen( filename, OF_READ );
+
+    contents = LocalLock( memory_object );
+
+    ok( NULL != contents, "LocalLock whines\n" );
+
+    ok( bytes_written == _hread( filehandle, contents, bytes_written), "read length differ from write length\n" );
+
+    checksum[0] = '\0';
+    i = 0;
+    do
+    {
+        checksum[0] = checksum[0] + contents[i];
+        i++;
+    }
+    while (i < bytes_written - 1);
+
+    ok( checksum[0] == contents[i], "stored checksum differ from computed checksum\n" );
+
+    ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+}
+
+
+static void test__lclose( void )
+{
+    HFILE filehandle;
+
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+}
+
+
+static void test__lcreat( void )
+{
+    HFILE filehandle;
+    char buffer[10000];
+    WIN32_FIND_DATAA search_results;
+    char slashname[] = "testfi/";
+    int err;
+    HANDLE find;
+
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+    ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+
+    ok( _hread( filehandle, buffer, strlen( sillytext ) ) ==  lstrlenA( sillytext ), "erratic _hread return value\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should be able to find file\n" );
+
+    ok( DeleteFileA(filename) != 0, "DeleteFile failed (%ld)\n", GetLastError());
+
+    filehandle = _lcreat( filename, 1 ); /* readonly */
+    ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%ld)\n", filename, GetLastError(  ) );
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite shouldn't be able to write never the less\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should be able to find file\n" );
+
+    ok( 0 == DeleteFileA( filename ), "shouldn't be able to delete a readonly file\n" );
+
+    ok( SetFileAttributesA(filename, FILE_ATTRIBUTE_NORMAL ) != 0, "couldn't change attributes on file\n" );
+
+    ok( DeleteFileA( filename ) != 0, "now it should be possible to delete the file!\n" );
+
+    filehandle = _lcreat( filename, 2 );
+    ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%ld)\n", filename, GetLastError(  ) );
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+    ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+
+    ok( _hread( filehandle, buffer, strlen( sillytext ) ) ==  lstrlenA( sillytext ), "erratic _hread return value\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should STILL be able to find file\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+
+    filehandle = _lcreat( filename, 4 ); /* SYSTEM file */
+    ok( HFILE_ERROR != filehandle, "couldn't create file \"%s\" (err=%ld)\n", filename, GetLastError(  ) );
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+    ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+
+    ok( _hread( filehandle, buffer, strlen( sillytext ) ) ==  lstrlenA( sillytext ), "erratic _hread return value\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( INVALID_HANDLE_VALUE != FindFirstFileA( filename, &search_results ), "should STILL be able to find file\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+
+    filehandle=_lcreat (slashname, 0); /* illegal name */
+    if (HFILE_ERROR==filehandle) {
+      err=GetLastError ();
+      ok (err==ERROR_INVALID_NAME || err==ERROR_PATH_NOT_FOUND,
+          "creating file \"%s\" failed with error %d\n", slashname, err);
+    } else { /* only NT succeeds */
+      _lclose(filehandle);
+      find=FindFirstFileA (slashname, &search_results);
+      if (INVALID_HANDLE_VALUE==find)
+        ok (0, "file \"%s\" not found\n", slashname);
+      else {
+        ok (0!=FindClose (find), "FindClose complains (%ld)\n", GetLastError ());
+        slashname[strlen(slashname)-1]=0;
+        ok (!strcmp (slashname, search_results.cFileName),
+            "found unexpected name \"%s\"\n", search_results.cFileName);
+        ok (FILE_ATTRIBUTE_ARCHIVE==search_results.dwFileAttributes,
+            "attributes of file \"%s\" are 0x%04lx\n", search_results.cFileName,
+            search_results.dwFileAttributes);
+      }
+      ok (0!=DeleteFileA (slashname), "Can't delete \"%s\" (%ld)\n", slashname,
+          GetLastError ());
+    }
+
+    filehandle=_lcreat (filename, 8); /* illegal attribute */
+    if (HFILE_ERROR==filehandle)
+      ok (0, "couldn't create volume label \"%s\"\n", filename);
+    else {
+      _lclose(filehandle);
+      find=FindFirstFileA (filename, &search_results);
+      if (INVALID_HANDLE_VALUE==find)
+        ok (0, "file \"%s\" not found\n", filename);
+      else {
+        ok (0!=FindClose (find), "FindClose complains (%ld)\n", GetLastError ());
+        ok (!strcmp (filename, search_results.cFileName),
+            "found unexpected name \"%s\"\n", search_results.cFileName);
+        ok (FILE_ATTRIBUTE_ARCHIVE==search_results.dwFileAttributes,
+            "attributes of file \"%s\" are 0x%04lx\n", search_results.cFileName,
+            search_results.dwFileAttributes);
+      }
+      ok (0!=DeleteFileA (filename), "Can't delete \"%s\" (%ld)\n", slashname,
+          GetLastError ());
+    }
+}
+
+
+static void test__llseek( void )
+{
+    INT i;
+    HFILE filehandle;
+    char buffer[1];
+    long bytes_read;
+
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    for (i = 0; i < 400; i++)
+    {
+        ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+    }
+    ok( HFILE_ERROR != _llseek( filehandle, 400 * strlen( sillytext ), FILE_CURRENT ), "should be able to seek\n" );
+    ok( HFILE_ERROR != _llseek( filehandle, 27 + 35 * strlen( sillytext ), FILE_BEGIN ), "should be able to seek\n" );
+
+    bytes_read = _hread( filehandle, buffer, 1);
+    ok( 1 == bytes_read, "file read size error\n" );
+    ok( buffer[0] == sillytext[27], "_llseek error, it got lost seeking\n" );
+    ok( HFILE_ERROR != _llseek( filehandle, -400 * strlen( sillytext ), FILE_END ), "should be able to seek\n" );
+
+    bytes_read = _hread( filehandle, buffer, 1);
+    ok( 1 == bytes_read, "file read size error\n" );
+    ok( buffer[0] == sillytext[0], "_llseek error, it got lost seeking\n" );
+    ok( HFILE_ERROR != _llseek( filehandle, 1000000, FILE_END ), "should be able to seek past file; poor, poor Windows programmers\n" );
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+}
+
+
+static void test__llopen( void )
+{
+    HFILE filehandle;
+    UINT bytes_read;
+    char buffer[10000];
+
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READ );
+    ok( HFILE_ERROR == _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite shouldn't be able to write!\n" );
+    bytes_read = _hread( filehandle, buffer, strlen( sillytext ) );
+    ok( strlen( sillytext )  == bytes_read, "file read size error\n" );
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READWRITE );
+    bytes_read = _hread( filehandle, buffer, 2 * strlen( sillytext ) );
+    ok( strlen( sillytext )  == bytes_read, "file read size error\n" );
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite should write just fine\n" );
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_WRITE );
+    ok( HFILE_ERROR == _hread( filehandle, buffer, 1 ), "you should only be able to write this file\n" );
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite should write just fine\n" );
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+    /* TODO - add tests for the SHARE modes  -  use two processes to pull this one off */
+}
+
+
+static void test__lread( void )
+{
+    HFILE filehandle;
+    char buffer[10000];
+    long bytes_read;
+    UINT bytes_wanted;
+    UINT i;
+
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    ok( HFILE_ERROR != _hwrite( filehandle, sillytext, strlen( sillytext ) ), "_hwrite complains\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READ );
+
+    ok( HFILE_ERROR != filehandle, "couldn't open file \"%s\" again (err=%ld)\n", filename, GetLastError());
+
+    bytes_read = _lread( filehandle, buffer, 2 * strlen( sillytext ) );
+
+    ok( lstrlenA( sillytext ) == bytes_read, "file read size error\n" );
+
+    for (bytes_wanted = 0; bytes_wanted < strlen( sillytext ); bytes_wanted++)
+    {
+        ok( 0 == _llseek( filehandle, 0, FILE_BEGIN ), "_llseek complains\n" );
+        ok( _lread( filehandle, buffer, bytes_wanted ) == bytes_wanted, "erratic _hread return value\n" );
+        for (i = 0; i < bytes_wanted; i++)
+        {
+            ok( buffer[i] == sillytext[i], "that's not what's written\n" );
+        }
+    }
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+}
+
+
+static void test__lwrite( void )
+{
+    HFILE filehandle;
+    char buffer[10000];
+    long bytes_read;
+    long bytes_written;
+    long blocks;
+    long i;
+    char *contents;
+    HLOCAL memory_object;
+    char checksum[1];
+
+    filehandle = _lcreat( filename, 0 );
+    if (filehandle == HFILE_ERROR)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+
+    ok( HFILE_ERROR != _lwrite( filehandle, "", 0 ), "_hwrite complains\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READ );
+
+    bytes_read = _hread( filehandle, buffer, 1);
+
+    ok( 0 == bytes_read, "file read size error\n" );
+
+    ok( HFILE_ERROR != _lclose(filehandle), "_lclose complains\n" );
+
+    filehandle = _lopen( filename, OF_READWRITE );
+
+    bytes_written = 0;
+    checksum[0] = '\0';
+    srand( (unsigned)time( NULL ) );
+    for (blocks = 0; blocks < 100; blocks++)
+    {
+        for (i = 0; i < (long)sizeof( buffer ); i++)
+        {
+            buffer[i] = rand(  );
+            checksum[0] = checksum[0] + buffer[i];
+        }
+        ok( HFILE_ERROR != _lwrite( filehandle, buffer, sizeof( buffer ) ), "_hwrite complains\n" );
+        bytes_written = bytes_written + sizeof( buffer );
+    }
+
+    ok( HFILE_ERROR != _lwrite( filehandle, checksum, 1 ), "_hwrite complains\n" );
+    bytes_written++;
+
+    ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+    memory_object = LocalAlloc( LPTR, bytes_written );
+
+    ok( 0 != memory_object, "LocalAlloc fails, could be out of memory\n" );
+
+    contents = LocalLock( memory_object );
+
+    filehandle = _lopen( filename, OF_READ );
+
+    contents = LocalLock( memory_object );
+
+    ok( NULL != contents, "LocalLock whines\n" );
+
+    ok( bytes_written == _hread( filehandle, contents, bytes_written), "read length differ from write length\n" );
+
+    checksum[0] = '\0';
+    i = 0;
+    do
+    {
+        checksum[0] += contents[i];
+        i++;
+    }
+    while (i < bytes_written - 1);
+
+    ok( checksum[0] == contents[i], "stored checksum differ from computed checksum\n" );
+
+    ok( HFILE_ERROR != _lclose( filehandle ), "_lclose complains\n" );
+
+    ok( DeleteFileA( filename ) != 0, "DeleteFile failed (%ld)\n", GetLastError(  ) );
+}
+
+static void test_CopyFileA(void)
+{
+    char temp_path[MAX_PATH];
+    char source[MAX_PATH], dest[MAX_PATH];
+    static const char prefix[] = "pfx";
+    DWORD ret;
+
+    ret = GetTempPathA(MAX_PATH, temp_path);
+    ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameA(temp_path, prefix, 0, source);
+    ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    ret = GetTempFileNameA(temp_path, prefix, 0, dest);
+    ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    ret = CopyFileA(source, dest, TRUE);
+    ok(!ret && GetLastError() == ERROR_FILE_EXISTS,
+       "CopyFileA: unexpected error %ld\n", GetLastError());
+
+    ret = CopyFileA(source, dest, FALSE);
+    ok(ret,  "CopyFileA: error %ld\n", GetLastError());
+
+    ret = DeleteFileA(source);
+    ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+    ret = DeleteFileA(dest);
+    ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+}
+
+static void test_CopyFileW(void)
+{
+    WCHAR temp_path[MAX_PATH];
+    WCHAR source[MAX_PATH], dest[MAX_PATH];
+    static const WCHAR prefix[] = {'p','f','x',0};
+    DWORD ret;
+
+    ret = GetTempPathW(MAX_PATH, temp_path);
+    if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(ret != 0, "GetTempPathW error %ld\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameW(temp_path, prefix, 0, source);
+    ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+    ret = GetTempFileNameW(temp_path, prefix, 0, dest);
+    ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+    ret = CopyFileW(source, dest, TRUE);
+    ok(!ret && GetLastError() == ERROR_FILE_EXISTS,
+       "CopyFileW: unexpected error %ld\n", GetLastError());
+
+    ret = CopyFileW(source, dest, FALSE);
+    ok(ret,  "CopyFileW: error %ld\n", GetLastError());
+
+    ret = DeleteFileW(source);
+    ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+    ret = DeleteFileW(dest);
+    ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+}
+
+static void test_CreateFileA(void)
+{
+    HANDLE hFile;
+    char temp_path[MAX_PATH];
+    char filename[MAX_PATH];
+    static const char prefix[] = "pfx";
+    DWORD ret;
+
+    ret = GetTempPathA(MAX_PATH, temp_path);
+    ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameA(temp_path, prefix, 0, filename);
+    ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    hFile = CreateFileA(filename, GENERIC_READ, 0, NULL,
+                        CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0);
+    ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_EXISTS,
+        "CREATE_NEW should fail if file exists and last error value should be ERROR_FILE_EXISTS\n");
+
+    ret = DeleteFileA(filename);
+    ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+}
+
+static void test_CreateFileW(void)
+{
+    HANDLE hFile;
+    WCHAR temp_path[MAX_PATH];
+    WCHAR filename[MAX_PATH];
+    static const WCHAR prefix[] = {'p','f','x',0};
+    DWORD ret;
+
+    ret = GetTempPathW(MAX_PATH, temp_path);
+    if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(ret != 0, "GetTempPathW error %ld\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameW(temp_path, prefix, 0, filename);
+    ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+    hFile = CreateFileW(filename, GENERIC_READ, 0, NULL,
+                        CREATE_NEW, FILE_FLAG_RANDOM_ACCESS, 0);
+    ok(hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_EXISTS,
+        "CREATE_NEW should fail if file exists and last error value should be ERROR_FILE_EXISTS\n");
+
+    ret = DeleteFileW(filename);
+    ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+}
+
+
+static void test_GetTempFileNameA() {
+    UINT result;
+    char out[MAX_PATH];
+    char *expected = "c:\\windows\\abc2.tmp";
+
+    /* this test may depend on the config file settings */
+    result = GetTempFileNameA("C:", "abc", 1, out);
+    ok( result != 0, "GetTempFileNameA: error %ld\n", GetLastError() );
+    ok( ((out[0] == 'C') && (out[1] == ':')) && (out[2] == '\\'), "GetTempFileNameA: first three characters should be C:\\, string was actually %s\n", out );
+
+    result = GetTempFileNameA("c:\\windows\\", "abc", 2, out);
+    ok( result != 0, "GetTempFileNameA: error %ld\n", GetLastError() );
+    ok( lstrcmpiA( out, expected ) == 0, "GetTempFileNameA: Unexpected output \"%s\" vs \"%s\"\n", out, expected );
+}
+
+
+static void test_DeleteFileA( void )
+{
+    BOOL ret;
+
+    ret = DeleteFileA(NULL);
+    ok(!ret && (GetLastError() == ERROR_INVALID_PARAMETER ||
+                GetLastError() == ERROR_PATH_NOT_FOUND),
+       "DeleteFileA(NULL) returned ret=%d error=%ld\n",ret,GetLastError());
+
+    ret = DeleteFileA("");
+    ok(!ret && (GetLastError() == ERROR_PATH_NOT_FOUND ||
+                GetLastError() == ERROR_BAD_PATHNAME),
+       "DeleteFileA(\"\") returned ret=%d error=%ld\n",ret,GetLastError());
+}
+
+static void test_DeleteFileW( void )
+{
+    BOOL ret;
+    WCHAR emptyW[]={'\0'};
+
+    ret = DeleteFileW(NULL);
+    if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+       "DeleteFileW(NULL) returned ret=%d error=%ld\n",ret,GetLastError());
+
+    ret = DeleteFileW(emptyW);
+    ok(!ret && GetLastError() == ERROR_PATH_NOT_FOUND,
+       "DeleteFileW(\"\") returned ret=%d error=%ld\n",ret,GetLastError());
+}
+
+#define IsDotDir(x)     ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
+
+static void test_MoveFileA(void)
+{
+    char tempdir[MAX_PATH];
+    char source[MAX_PATH], dest[MAX_PATH];
+    static const char prefix[] = "pfx";
+    DWORD ret;
+
+    ret = GetTempPathA(MAX_PATH, tempdir);
+    ok(ret != 0, "GetTempPathA error %ld\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameA(tempdir, prefix, 0, source);
+    ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    ret = GetTempFileNameA(tempdir, prefix, 0, dest);
+    ok(ret != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    ret = MoveFileA(source, dest);
+    ok(!ret && GetLastError() == ERROR_ALREADY_EXISTS,
+       "MoveFileA: unexpected error %ld\n", GetLastError());
+
+    ret = DeleteFileA(dest);
+    ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+
+    ret = MoveFileA(source, dest);
+    ok(ret, "MoveFileA: failed, error %ld\n", GetLastError());
+
+    lstrcatA(tempdir, "Remove Me");
+    ret = CreateDirectoryA(tempdir, NULL);
+    ok(ret == TRUE, "CreateDirectoryA failed\n");
+
+    lstrcpyA(source, dest);
+    lstrcpyA(dest, tempdir);
+    lstrcatA(dest, "\\wild?.*");
+    ret = MoveFileA(source, dest);
+    todo_wine {
+      ok(!ret, "MoveFileA: shouldn't move to wildcard file\n");
+      ok(GetLastError() == ERROR_INVALID_NAME,
+              "MoveFileA: with wildcards, unexpected error %ld\n", GetLastError());
+#if 0
+      if (ret || (GetLastError() != ERROR_INVALID_NAME))
+      {
+        WIN32_FIND_DATAA fd;
+        char temppath[MAX_PATH];
+        HANDLE hFind;
+
+        lstrcpyA(temppath, tempdir);
+        lstrcatA(temppath, "\\*.*");
+        hFind = FindFirstFileA(temppath, &fd);
+        if (INVALID_HANDLE_VALUE != hFind)
+        {
+          LPSTR lpName;
+          do
+          {
+            lpName = fd.cAlternateFileName;
+            if (!lpName[0])
+              lpName = fd.cFileName;
+            ok(IsDotDir(lpName), "MoveFileA: wildcards file created!\n");
+          }
+          while (FindNextFileA(hFind, &fd));
+          FindClose(hFind);
+        }
+      }
+#endif
+      ret = DeleteFileA(source);
+      ok(ret, "DeleteFileA: error %ld\n", GetLastError());
+      ret = DeleteFileA(dest);
+      ok(!ret, "DeleteFileA: error %ld\n", GetLastError());
+    }
+
+    ret = RemoveDirectoryA(tempdir);
+    ok(ret, "DeleteDirectoryA: error %ld\n", GetLastError());
+}
+
+static void test_MoveFileW(void)
+{
+    WCHAR temp_path[MAX_PATH];
+    WCHAR source[MAX_PATH], dest[MAX_PATH];
+    static const WCHAR prefix[] = {'p','f','x',0};
+    DWORD ret;
+
+    ret = GetTempPathW(MAX_PATH, temp_path);
+    if (ret==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(ret != 0, "GetTempPathW error %ld\n", GetLastError());
+    ok(ret < MAX_PATH, "temp path should fit into MAX_PATH\n");
+
+    ret = GetTempFileNameW(temp_path, prefix, 0, source);
+    ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+    ret = GetTempFileNameW(temp_path, prefix, 0, dest);
+    ok(ret != 0, "GetTempFileNameW error %ld\n", GetLastError());
+
+    ret = MoveFileW(source, dest);
+    ok(!ret && GetLastError() == ERROR_ALREADY_EXISTS,
+       "CopyFileW: unexpected error %ld\n", GetLastError());
+
+    ret = DeleteFileW(source);
+    ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+    ret = DeleteFileW(dest);
+    ok(ret, "DeleteFileW: error %ld\n", GetLastError());
+}
+
+#define PATTERN_OFFSET 0x10
+
+static void test_offset_in_overlapped_structure(void)
+{
+    HANDLE hFile;
+    OVERLAPPED ov;
+    DWORD done;
+    BOOL rc;
+    BYTE buf[256], pattern[] = "TeSt";
+    UINT i;
+    char temp_path[MAX_PATH], temp_fname[MAX_PATH];
+
+    ok(GetTempPathA(MAX_PATH, temp_path) != 0, "GetTempPathA error %ld\n", GetLastError());
+    ok(GetTempFileNameA(temp_path, "pfx", 0, temp_fname) != 0, "GetTempFileNameA error %ld\n", GetLastError());
+
+    /*** Write File *****************************************************/
+
+    hFile = CreateFileA(temp_fname, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
+    ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA error %ld\n", GetLastError());
+
+    for(i = 0; i < sizeof(buf); i++) buf[i] = i;
+    ok(WriteFile(hFile, buf, sizeof(buf), &done, NULL), "WriteFile error %ld\n", GetLastError());
+    ok(done == sizeof(buf), "expected number of bytes written %lu\n", done);
+
+    memset(&ov, 0, sizeof(ov));
+    ov.Offset = PATTERN_OFFSET;
+    ov.OffsetHigh = 0;
+    rc=WriteFile(hFile, pattern, sizeof(pattern), &done, &ov);
+    /* Win 9x does not support the overlapped I/O on files */
+    if (rc || GetLastError()!=ERROR_INVALID_PARAMETER) {
+        ok(rc, "WriteFile error %ld\n", GetLastError());
+        ok(done == sizeof(pattern), "expected number of bytes written %lu\n", done);
+        trace("Current offset = %04lx\n", SetFilePointer(hFile, 0, NULL, FILE_CURRENT));
+        ok(SetFilePointer(hFile, 0, NULL, FILE_CURRENT) == (PATTERN_OFFSET + sizeof(pattern)),
+           "expected file offset %d\n", PATTERN_OFFSET + sizeof(pattern));
+
+        ov.Offset = sizeof(buf) * 2;
+        ov.OffsetHigh = 0;
+        ok(WriteFile(hFile, pattern, sizeof(pattern), &done, &ov), "WriteFile error %ld\n", GetLastError());
+        ok(done == sizeof(pattern), "expected number of bytes written %lu\n", done);
+        /*trace("Current offset = %04lx\n", SetFilePointer(hFile, 0, NULL, FILE_CURRENT));*/
+        ok(SetFilePointer(hFile, 0, NULL, FILE_CURRENT) == (sizeof(buf) * 2 + sizeof(pattern)),
+           "expected file offset %d\n", sizeof(buf) * 2 + sizeof(pattern));
+    }
+
+    CloseHandle(hFile);
+
+    /*** Read File *****************************************************/
+
+    hFile = CreateFileA(temp_fname, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(hFile != INVALID_HANDLE_VALUE, "CreateFileA error %ld\n", GetLastError());
+
+    memset(buf, 0, sizeof(buf));
+    memset(&ov, 0, sizeof(ov));
+    ov.Offset = PATTERN_OFFSET;
+    ov.OffsetHigh = 0;
+    rc=ReadFile(hFile, buf, sizeof(pattern), &done, &ov);
+    /* Win 9x does not support the overlapped I/O on files */
+    if (rc || GetLastError()!=ERROR_INVALID_PARAMETER) {
+        ok(rc, "ReadFile error %ld\n", GetLastError());
+        ok(done == sizeof(pattern), "expected number of bytes read %lu\n", done);
+        trace("Current offset = %04lx\n", SetFilePointer(hFile, 0, NULL, FILE_CURRENT));
+        ok(SetFilePointer(hFile, 0, NULL, FILE_CURRENT) == (PATTERN_OFFSET + sizeof(pattern)),
+           "expected file offset %d\n", PATTERN_OFFSET + sizeof(pattern));
+        ok(!memcmp(buf, pattern, sizeof(pattern)), "pattern match failed\n");
+    }
+
+    CloseHandle(hFile);
+
+    ok(DeleteFileA(temp_fname), "DeleteFileA error %ld\n", GetLastError());
+}
+
+static void test_LockFile(void)
+{
+    HANDLE handle;
+    DWORD written;
+    OVERLAPPED overlapped;
+
+    handle = CreateFileA( filename, GENERIC_READ | GENERIC_WRITE,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+                          CREATE_ALWAYS, 0, 0 );
+    if (handle == INVALID_HANDLE_VALUE)
+    {
+        ok(0,"couldn't create file \"%s\" (err=%ld)\n",filename,GetLastError());
+        return;
+    }
+    ok( WriteFile( handle, sillytext, strlen(sillytext), &written, NULL ), "write failed\n" );
+
+    ok( LockFile( handle, 0, 0, 0, 0 ), "LockFile failed\n" );
+    ok( UnlockFile( handle, 0, 0, 0, 0 ), "UnlockFile failed\n" );
+    ok( !UnlockFile( handle, 0, 0, 0, 0 ), "UnlockFile succeeded\n" );
+
+    ok( LockFile( handle, 10, 0, 20, 0 ), "LockFile 10,20 failed\n" );
+    /* overlapping locks must fail */
+    ok( !LockFile( handle, 12, 0, 10, 0 ), "LockFile 12,10 succeeded\n" );
+    ok( !LockFile( handle, 5, 0, 6, 0 ), "LockFile 5,6 succeeded\n" );
+    /* non-overlapping locks must succeed */
+    ok( LockFile( handle, 5, 0, 5, 0 ), "LockFile 5,5 failed\n" );
+
+    ok( !UnlockFile( handle, 10, 0, 10, 0 ), "UnlockFile 10,10 succeeded\n" );
+    ok( UnlockFile( handle, 10, 0, 20, 0 ), "UnlockFile 10,20 failed\n" );
+    ok( !UnlockFile( handle, 10, 0, 20, 0 ), "UnlockFile 10,20 again succeeded\n" );
+    ok( UnlockFile( handle, 5, 0, 5, 0 ), "UnlockFile 5,5 failed\n" );
+
+    overlapped.Offset = 100;
+    overlapped.OffsetHigh = 0;
+    overlapped.hEvent = 0;
+    ok( LockFileEx( handle, 0, 0, 100, 0, &overlapped ), "LockFileEx 100,100 failed\n" );
+    /* overlapping shared locks are OK */
+    overlapped.Offset = 150;
+    ok( LockFileEx( handle, 0, 0, 100, 0, &overlapped ), "LockFileEx 150,100 failed\n" );
+    /* but exclusive is not */
+    ok( !LockFileEx( handle, LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, 0, 50, 0, &overlapped ),
+        "LockFileEx exclusive 150,50 succeeded\n" );
+    ok( UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 150,100 failed\n" );
+    ok( !UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 150,100 again succeeded\n" );
+    overlapped.Offset = 100;
+    ok( UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 100,100 failed\n" );
+    ok( !UnlockFileEx( handle, 0, 100, 0, &overlapped ), "UnlockFileEx 100,100 again succeeded\n" );
+
+    ok( LockFile( handle, 0, 0x10000000, 0, 0xf0000000 ), "LockFile failed\n" );
+    ok( !LockFile( handle, ~0, ~0, 1, 0 ), "LockFile ~0,1 succeeded\n" );
+    ok( !LockFile( handle, 0, 0x20000000, 20, 0 ), "LockFile 0x20000000,20 succeeded\n" );
+    ok( UnlockFile( handle, 0, 0x10000000, 0, 0xf0000000 ), "UnlockFile failed\n" );
+
+    /* wrap-around lock should not do anything */
+    /* (but still succeeds on NT4 so we don't check result) */
+    LockFile( handle, 0, 0x10000000, 0, 0xf0000001 );
+    ok( LockFile( handle, ~0, ~0, 1, 0 ), "LockFile ~0,1 failed\n" );
+    ok( UnlockFile( handle, ~0, ~0, 1, 0 ), "Unlockfile ~0,1 failed\n" );
+
+    /* zero-byte lock */
+    ok( LockFile( handle, 100, 0, 0, 0 ), "LockFile 100,0 failed\n" );
+    ok( !LockFile( handle, 98, 0, 4, 0 ), "LockFile 98,4 succeeded\n" );
+    ok( LockFile( handle, 90, 0, 10, 0 ), "LockFile 90,10 failed\n" );
+    ok( LockFile( handle, 100, 0, 10, 0 ), "LockFile 100,10 failed\n" );
+    ok( UnlockFile( handle, 90, 0, 10, 0 ), "UnlockFile 90,10 failed\n" );
+    ok( UnlockFile( handle, 100, 0, 10, 0 ), "UnlockFile 100,10 failed\n" );
+    ok( UnlockFile( handle, 100, 0, 0, 0 ), "UnlockFile 100,0 failed\n" );
+
+    CloseHandle( handle );
+    DeleteFileA( filename );
+}
+
+static void test_FindFirstFileA()
+{
+    HANDLE handle;
+    WIN32_FIND_DATAA search_results;
+    int err;
+
+    handle = FindFirstFileA("C:\\",&search_results);
+    err = GetLastError();
+    ok ( handle == INVALID_HANDLE_VALUE , "FindFirstFile on Root directory should Fail\n");
+    if (handle == INVALID_HANDLE_VALUE)
+      ok ( err == ERROR_FILE_NOT_FOUND, "Bad Error number %d\n", err);
+    handle = FindFirstFileA("C:\\*",&search_results);
+    ok ( handle != INVALID_HANDLE_VALUE, "FindFirstFile on C:\\* should succeed\n" );
+    ok ( FindClose(handle) == TRUE, "Failed to close handle\n");
+}
+
+static void test_FindNextFileA()
+{
+    HANDLE handle;
+    WIN32_FIND_DATAA search_results;
+    int err;
+
+    handle = FindFirstFileA("C:\\*",&search_results);
+    ok ( handle != INVALID_HANDLE_VALUE, "FindFirstFile on C:\\* should succeed\n" );
+    while (FindNextFile(handle, &search_results))
+    {
+        /* get to the end of the files */
+    }
+    ok ( FindClose(handle) == TRUE, "Failed to close handle\n");
+    err = GetLastError();
+    ok ( err == ERROR_NO_MORE_FILES, "GetLastError should return ERROR_NO_MORE_FILES\n");
+}
+
+static void test_MapFile()
+{
+    HANDLE handle, hmap;
+
+    /* be sure to remove stale files */
+    SetFileAttributesA(filename,FILE_ATTRIBUTE_NORMAL);
+    DeleteFile(filename);
+    handle = CreateFile( filename, GENERIC_READ|GENERIC_WRITE, 0, 0,
+                         CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+    ok( handle != INVALID_HANDLE_VALUE, "couldn't create test file\n");
+
+    hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0, 0, NULL );
+    ok( hmap == NULL, "mapped zero size file\n");
+    ok( GetLastError() == ERROR_FILE_INVALID, "not ERROR_FILE_INVALID\n");
+
+    hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0x1000, 0, NULL );
+    ok( hmap == NULL, "mapping should fail\n");
+    /* GetLastError() varies between win9x and WinNT */
+
+    hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0x1000, 0x10000, NULL );
+    ok( hmap == NULL, "mapping should fail\n");
+    /* GetLastError() varies between win9x and WinNT */
+
+    hmap = CreateFileMapping( handle, NULL, PAGE_READWRITE, 0, 0x1000, NULL );
+    ok( hmap != NULL, "mapping should succeed\n");
+
+    ok( CloseHandle( hmap ), "can't close mapping handle\n");
+    ok( CloseHandle( handle ), "can't close file handle\n");
+    ok( DeleteFileA( filename ), "DeleteFile failed after map\n" );
+}
+
+START_TEST(file)
+{
+    test__hread(  );
+    test__hwrite(  );
+    test__lclose(  );
+    test__lcreat(  );
+    test__llseek(  );
+    test__llopen(  );
+    test__lread(  );
+    test__lwrite(  );
+    test_GetTempFileNameA();
+    test_CopyFileA();
+    test_CopyFileW();
+    test_CreateFileA();
+    test_CreateFileW();
+    test_DeleteFileA();
+    test_DeleteFileW();
+    test_MoveFileA();
+    test_MoveFileW();
+    test_FindFirstFileA();
+    test_FindNextFileA();
+    test_LockFile();
+    test_offset_in_overlapped_structure();
+    test_MapFile();
+}
diff --git a/reactos/apps/tests/kernel32/format_msg.c b/reactos/apps/tests/kernel32/format_msg.c
new file mode 100644 (file)
index 0000000..d19e3f4
--- /dev/null
@@ -0,0 +1,226 @@
+/* Unit test suite for FormatMessageA
+ *
+ * Copyright 2002 Mike McCormack for CodeWeavers
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+
+/* #define ok(cond,failstr) if(!(cond)) {printf("line %d : %s\n",__LINE__,failstr);exit(1);} */
+
+DWORD doit(DWORD flags, LPCVOID src, DWORD msg_id, DWORD lang_id,
+           LPSTR out, DWORD outsize, ... )
+{
+    va_list list;
+    DWORD r;
+
+    va_start(list, outsize);
+    r = FormatMessageA(flags, src, msg_id,
+        lang_id, out, outsize, &list);
+    va_end(list);
+    return r;
+}
+
+void test_message_from_string(void)
+{
+    CHAR out[0x100] = {0};
+    DWORD r;
+    WCHAR szwTest[] = { 't','e','s','t',0};
+
+    /* the basics */
+    r = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "test", 0,
+        0, out, sizeof(out)/sizeof(CHAR),NULL);
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* using the format feature */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!s!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), "test");
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* no format */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1", 0,
+        0, out, sizeof(out)/sizeof(CHAR), "test");
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* two pieces */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1%2", 0,
+        0, out, sizeof(out)/sizeof(CHAR), "te","st");
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* three pieces */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1%3%2%1", 0,
+        0, out, sizeof(out)/sizeof(CHAR), "t","s","e");
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* s doesn't seem to work in format strings */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%!s!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), "test");
+    ok(!strcmp("!s!", out),"failed out=[%s]\n",out);
+    ok(r==3,"failed: r=%ld\n",r);
+
+    /* S is unicode */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!S!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), szwTest);
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* as characters */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!c!%2!c!%3!c!%1!c!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 't','e','s');
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* some numbers */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!d!%2!d!%3!d!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 1,2,3);
+    ok(!strcmp("123", out),"failed out=[%s]\n",out);
+    ok(r==3,"failed: r=%ld\n",r);
+
+    /* a single digit with some spacing */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4d!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 1);
+    ok(!strcmp("   1", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* a single digit, left justified */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!-4d!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 1);
+    ok(!strcmp("1   ", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* two digit decimal number */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4d!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 11);
+    ok(!strcmp("  11", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* a hex number */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4x!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 11);
+    ok(!strcmp("   b", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* a hex number, upper case */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4X!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 11);
+    ok(!strcmp("   B", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* a hex number, upper case, left justified */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!-4X!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 11);
+    ok(!strcmp("B   ", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* a long hex number, upper case */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "%1!4X!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 0x1ab);
+    ok(!strcmp(" 1AB", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* two percent... */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, " %%%% ", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp(" %% ", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* periods are special cases */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, " %.%. %1!d!", 0,
+        0, out, sizeof(out)/sizeof(CHAR), 0x1ab);
+    ok(!strcmp(" .. 427", out),"failed out=[%s]\n",out);
+    ok(r==7,"failed: r=%ld\n",r);
+
+    /* %0 ends the line */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "test%0test", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("test", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* %! prints an exclaimation */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "yah%!%0   ", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("yah!", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* %space */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "% %   ", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("    ", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "hi\n", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("hi\r\n", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* carriage return line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "hi\r\n", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("hi\r\n", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* carriage return line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "\r", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("\r\n", out),"failed out=[%s]\n",out);
+    ok(r==2,"failed: r=%ld\n",r);
+
+    /* carriage return line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING, "\r\r\n", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("\r\n\r\n", out),"failed out=[%s]\n",out);
+    ok(r==4,"failed: r=%ld\n",r);
+
+    /* change of pace... test the low byte of dwflags */
+    /* line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\n", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("hi ", out) || !strcmp("hi\r\n", out),"failed out=[%s]\n",out);
+    ok(r==3 || r==4,"failed: r=%ld\n",r);
+
+    /* carriage return line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "hi\r\n", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("hi ", out),"failed out=[%s]\n",out);
+    ok(r==3,"failed: r=%ld\n",r);
+
+    /* carriage return line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "\r", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp(" ", out),"failed out=[%s]\n",out);
+    ok(r==1,"failed: r=%ld\n",r);
+
+    /* carriage return line feed */
+    r = doit(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_MAX_WIDTH_MASK, "\r\r\n", 0,
+        0, out, sizeof(out)/sizeof(CHAR));
+    ok(!strcmp("  ", out),"failed out=[%s]\n",out);
+    ok(r==2,"failed: r=%ld\n",r);
+}
+
+START_TEST(format_msg)
+{
+    test_message_from_string();
+}
diff --git a/reactos/apps/tests/kernel32/generated.c b/reactos/apps/tests/kernel32/generated.c
new file mode 100644 (file)
index 0000000..886ff5f
--- /dev/null
@@ -0,0 +1,1015 @@
+/* File generated automatically from tools/winapi/test.dat; do not edit! */
+/* This file can be copied, modified and distributed without restriction. */
+
+/*
+ * Unit tests for data structure packing
+ */
+
+#define WINVER 0x0501
+#define _WIN32_IE 0x0501
+#define _WIN32_WINNT 0x0501
+
+#define WINE_NOWINSOCK
+
+#include "windows.h"
+
+#include "wine/test.h"
+
+/***********************************************************************
+ * Compability macros
+ */
+
+#define DWORD_PTR UINT_PTR
+#define LONG_PTR INT_PTR
+#define ULONG_PTR UINT_PTR
+
+/***********************************************************************
+ * Windows API extension
+ */
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define FIELD_ALIGNMENT(type, field) __alignof(((type*)0)->field)
+#elif defined(__GNUC__)
+# define FIELD_ALIGNMENT(type, field) __alignof__(((type*)0)->field)
+#else
+/* FIXME: Not sure if is possible to do without compiler extension */
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300) && defined(__cplusplus)
+# define _TYPE_ALIGNMENT(type) __alignof(type)
+#elif defined(__GNUC__)
+# define _TYPE_ALIGNMENT(type) __alignof__(type)
+#else
+/*
+ * FIXME: Not sure if is possible to do without compiler extension
+ *        (if type is not just a name that is, if so the normal)
+ *         TYPE_ALIGNMENT can be used)
+ */
+#endif
+
+#if !defined(TYPE_ALIGNMENT) && defined(_TYPE_ALIGNMENT)
+# define TYPE_ALIGNMENT _TYPE_ALIGNMENT
+#endif
+
+/***********************************************************************
+ * Test helper macros
+ */
+
+#ifdef FIELD_ALIGNMENT
+# define TEST_FIELD_ALIGNMENT(type, field, align) \
+   ok(FIELD_ALIGNMENT(type, field) == align, \
+       "FIELD_ALIGNMENT(" #type ", " #field ") == %d (expected " #align ")\n", \
+           FIELD_ALIGNMENT(type, field))
+#else
+# define TEST_FIELD_ALIGNMENT(type, field, align) do { } while (0)
+#endif
+
+#define TEST_FIELD_OFFSET(type, field, offset) \
+    ok(FIELD_OFFSET(type, field) == offset, \
+        "FIELD_OFFSET(" #type ", " #field ") == %ld (expected " #offset ")\n", \
+             FIELD_OFFSET(type, field))
+
+#ifdef _TYPE_ALIGNMENT
+#define TEST__TYPE_ALIGNMENT(type, align) \
+    ok(_TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", _TYPE_ALIGNMENT(type))
+#else
+# define TEST__TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#ifdef TYPE_ALIGNMENT
+#define TEST_TYPE_ALIGNMENT(type, align) \
+    ok(TYPE_ALIGNMENT(type) == align, "TYPE_ALIGNMENT(" #type ") == %d (expected " #align ")\n", TYPE_ALIGNMENT(type))
+#else
+# define TEST_TYPE_ALIGNMENT(type, align) do { } while (0)
+#endif
+
+#define TEST_TYPE_SIZE(type, size) \
+    ok(sizeof(type) == size, "sizeof(" #type ") == %d (expected " #size ")\n", sizeof(type))
+
+/***********************************************************************
+ * Test macros
+ */
+
+#define TEST_FIELD(type, field_type, field_name, field_offset, field_size, field_align) \
+  TEST_TYPE_SIZE(field_type, field_size); \
+  TEST_FIELD_ALIGNMENT(type, field_name, field_align); \
+  TEST_FIELD_OFFSET(type, field_name, field_offset); \
+
+#define TEST_TYPE(type, size, align) \
+  TEST_TYPE_ALIGNMENT(type, align); \
+  TEST_TYPE_SIZE(type, size)
+
+#define TEST_TYPE_POINTER(type, size, align) \
+    TEST__TYPE_ALIGNMENT(*(type)0, align); \
+    TEST_TYPE_SIZE(*(type)0, size)
+
+#define TEST_TYPE_SIGNED(type) \
+    ok((type) -1 < 0, "(" #type ") -1 < 0\n");
+
+#define TEST_TYPE_UNSIGNED(type) \
+     ok((type) -1 > 0, "(" #type ") -1 > 0\n");
+
+static void test_pack_BY_HANDLE_FILE_INFORMATION(void)
+{
+    /* BY_HANDLE_FILE_INFORMATION (pack 4) */
+    TEST_TYPE(BY_HANDLE_FILE_INFORMATION, 52, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, DWORD, dwFileAttributes, 0, 4, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, FILETIME, ftCreationTime, 4, 8, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, FILETIME, ftLastAccessTime, 12, 8, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, FILETIME, ftLastWriteTime, 20, 8, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, DWORD, dwVolumeSerialNumber, 28, 4, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, DWORD, nFileSizeHigh, 32, 4, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, DWORD, nFileSizeLow, 36, 4, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, DWORD, nNumberOfLinks, 40, 4, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, DWORD, nFileIndexHigh, 44, 4, 4);
+    TEST_FIELD(BY_HANDLE_FILE_INFORMATION, DWORD, nFileIndexLow, 48, 4, 4);
+}
+
+static void test_pack_COMMCONFIG(void)
+{
+    /* COMMCONFIG (pack 4) */
+    TEST_FIELD(COMMCONFIG, DWORD, dwSize, 0, 4, 4);
+    TEST_FIELD(COMMCONFIG, WORD, wVersion, 4, 2, 2);
+    TEST_FIELD(COMMCONFIG, WORD, wReserved, 6, 2, 2);
+}
+
+static void test_pack_COMMPROP(void)
+{
+    /* COMMPROP (pack 4) */
+    TEST_TYPE(COMMPROP, 64, 4);
+    TEST_FIELD(COMMPROP, WORD, wPacketLength, 0, 2, 2);
+    TEST_FIELD(COMMPROP, WORD, wPacketVersion, 2, 2, 2);
+    TEST_FIELD(COMMPROP, DWORD, dwServiceMask, 4, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwReserved1, 8, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwMaxTxQueue, 12, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwMaxRxQueue, 16, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwMaxBaud, 20, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwProvSubType, 24, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwProvCapabilities, 28, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwSettableParams, 32, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwSettableBaud, 36, 4, 4);
+    TEST_FIELD(COMMPROP, WORD, wSettableData, 40, 2, 2);
+    TEST_FIELD(COMMPROP, WORD, wSettableStopParity, 42, 2, 2);
+    TEST_FIELD(COMMPROP, DWORD, dwCurrentTxQueue, 44, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwCurrentRxQueue, 48, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwProvSpec1, 52, 4, 4);
+    TEST_FIELD(COMMPROP, DWORD, dwProvSpec2, 56, 4, 4);
+    TEST_FIELD(COMMPROP, WCHAR[1], wcProvChar, 60, 2, 2);
+}
+
+static void test_pack_COMMTIMEOUTS(void)
+{
+    /* COMMTIMEOUTS (pack 4) */
+    TEST_TYPE(COMMTIMEOUTS, 20, 4);
+    TEST_FIELD(COMMTIMEOUTS, DWORD, ReadIntervalTimeout, 0, 4, 4);
+    TEST_FIELD(COMMTIMEOUTS, DWORD, ReadTotalTimeoutMultiplier, 4, 4, 4);
+    TEST_FIELD(COMMTIMEOUTS, DWORD, ReadTotalTimeoutConstant, 8, 4, 4);
+    TEST_FIELD(COMMTIMEOUTS, DWORD, WriteTotalTimeoutMultiplier, 12, 4, 4);
+    TEST_FIELD(COMMTIMEOUTS, DWORD, WriteTotalTimeoutConstant, 16, 4, 4);
+}
+
+static void test_pack_COMSTAT(void)
+{
+    /* COMSTAT (pack 4) */
+    TEST_TYPE(COMSTAT, 12, 4);
+    TEST_FIELD(COMSTAT, DWORD, cbInQue, 4, 4, 4);
+    TEST_FIELD(COMSTAT, DWORD, cbOutQue, 8, 4, 4);
+}
+
+static void test_pack_CREATE_PROCESS_DEBUG_INFO(void)
+{
+    /* CREATE_PROCESS_DEBUG_INFO (pack 4) */
+    TEST_TYPE(CREATE_PROCESS_DEBUG_INFO, 40, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, HANDLE, hFile, 0, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, HANDLE, hProcess, 4, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, HANDLE, hThread, 8, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, LPVOID, lpBaseOfImage, 12, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, DWORD, dwDebugInfoFileOffset, 16, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, DWORD, nDebugInfoSize, 20, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, LPVOID, lpThreadLocalBase, 24, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, LPTHREAD_START_ROUTINE, lpStartAddress, 28, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, LPVOID, lpImageName, 32, 4, 4);
+    TEST_FIELD(CREATE_PROCESS_DEBUG_INFO, WORD, fUnicode, 36, 2, 2);
+}
+
+static void test_pack_CREATE_THREAD_DEBUG_INFO(void)
+{
+    /* CREATE_THREAD_DEBUG_INFO (pack 4) */
+    TEST_TYPE(CREATE_THREAD_DEBUG_INFO, 12, 4);
+    TEST_FIELD(CREATE_THREAD_DEBUG_INFO, HANDLE, hThread, 0, 4, 4);
+    TEST_FIELD(CREATE_THREAD_DEBUG_INFO, LPVOID, lpThreadLocalBase, 4, 4, 4);
+    TEST_FIELD(CREATE_THREAD_DEBUG_INFO, LPTHREAD_START_ROUTINE, lpStartAddress, 8, 4, 4);
+}
+
+static void test_pack_CRITICAL_SECTION(void)
+{
+    /* CRITICAL_SECTION */
+    TEST_TYPE(CRITICAL_SECTION, 24, 4);
+}
+
+static void test_pack_CRITICAL_SECTION_DEBUG(void)
+{
+    /* CRITICAL_SECTION_DEBUG */
+    TEST_TYPE(CRITICAL_SECTION_DEBUG, 32, 4);
+}
+
+static void test_pack_DCB(void)
+{
+    /* DCB (pack 4) */
+    TEST_FIELD(DCB, DWORD, DCBlength, 0, 4, 4);
+    TEST_FIELD(DCB, DWORD, BaudRate, 4, 4, 4);
+}
+
+static void test_pack_DEBUG_EVENT(void)
+{
+    /* DEBUG_EVENT (pack 4) */
+    TEST_FIELD(DEBUG_EVENT, DWORD, dwDebugEventCode, 0, 4, 4);
+    TEST_FIELD(DEBUG_EVENT, DWORD, dwProcessId, 4, 4, 4);
+    TEST_FIELD(DEBUG_EVENT, DWORD, dwThreadId, 8, 4, 4);
+}
+
+static void test_pack_EXCEPTION_DEBUG_INFO(void)
+{
+    /* EXCEPTION_DEBUG_INFO (pack 4) */
+    TEST_TYPE(EXCEPTION_DEBUG_INFO, 84, 4);
+    TEST_FIELD(EXCEPTION_DEBUG_INFO, EXCEPTION_RECORD, ExceptionRecord, 0, 80, 4);
+    TEST_FIELD(EXCEPTION_DEBUG_INFO, DWORD, dwFirstChance, 80, 4, 4);
+}
+
+static void test_pack_EXIT_PROCESS_DEBUG_INFO(void)
+{
+    /* EXIT_PROCESS_DEBUG_INFO (pack 4) */
+    TEST_TYPE(EXIT_PROCESS_DEBUG_INFO, 4, 4);
+    TEST_FIELD(EXIT_PROCESS_DEBUG_INFO, DWORD, dwExitCode, 0, 4, 4);
+}
+
+static void test_pack_EXIT_THREAD_DEBUG_INFO(void)
+{
+    /* EXIT_THREAD_DEBUG_INFO (pack 4) */
+    TEST_TYPE(EXIT_THREAD_DEBUG_INFO, 4, 4);
+    TEST_FIELD(EXIT_THREAD_DEBUG_INFO, DWORD, dwExitCode, 0, 4, 4);
+}
+
+static void test_pack_HW_PROFILE_INFOA(void)
+{
+    /* HW_PROFILE_INFOA (pack 4) */
+    TEST_TYPE(HW_PROFILE_INFOA, 124, 4);
+    TEST_FIELD(HW_PROFILE_INFOA, DWORD, dwDockInfo, 0, 4, 4);
+    TEST_FIELD(HW_PROFILE_INFOA, CHAR[HW_PROFILE_GUIDLEN], szHwProfileGuid, 4, 39, 1);
+    TEST_FIELD(HW_PROFILE_INFOA, CHAR[MAX_PROFILE_LEN], szHwProfileName, 43, 80, 1);
+}
+
+static void test_pack_HW_PROFILE_INFOW(void)
+{
+    /* HW_PROFILE_INFOW (pack 4) */
+    TEST_TYPE(HW_PROFILE_INFOW, 244, 4);
+    TEST_FIELD(HW_PROFILE_INFOW, DWORD, dwDockInfo, 0, 4, 4);
+    TEST_FIELD(HW_PROFILE_INFOW, WCHAR[HW_PROFILE_GUIDLEN], szHwProfileGuid, 4, 78, 2);
+    TEST_FIELD(HW_PROFILE_INFOW, WCHAR[MAX_PROFILE_LEN], szHwProfileName, 82, 160, 2);
+}
+
+static void test_pack_LDT_ENTRY(void)
+{
+    /* LDT_ENTRY (pack 4) */
+    TEST_FIELD(LDT_ENTRY, WORD, LimitLow, 0, 2, 2);
+    TEST_FIELD(LDT_ENTRY, WORD, BaseLow, 2, 2, 2);
+}
+
+static void test_pack_LOAD_DLL_DEBUG_INFO(void)
+{
+    /* LOAD_DLL_DEBUG_INFO (pack 4) */
+    TEST_TYPE(LOAD_DLL_DEBUG_INFO, 24, 4);
+    TEST_FIELD(LOAD_DLL_DEBUG_INFO, HANDLE, hFile, 0, 4, 4);
+    TEST_FIELD(LOAD_DLL_DEBUG_INFO, LPVOID, lpBaseOfDll, 4, 4, 4);
+    TEST_FIELD(LOAD_DLL_DEBUG_INFO, DWORD, dwDebugInfoFileOffset, 8, 4, 4);
+    TEST_FIELD(LOAD_DLL_DEBUG_INFO, DWORD, nDebugInfoSize, 12, 4, 4);
+    TEST_FIELD(LOAD_DLL_DEBUG_INFO, LPVOID, lpImageName, 16, 4, 4);
+    TEST_FIELD(LOAD_DLL_DEBUG_INFO, WORD, fUnicode, 20, 2, 2);
+}
+
+static void test_pack_LPBY_HANDLE_FILE_INFORMATION(void)
+{
+    /* LPBY_HANDLE_FILE_INFORMATION */
+    TEST_TYPE(LPBY_HANDLE_FILE_INFORMATION, 4, 4);
+    TEST_TYPE_POINTER(LPBY_HANDLE_FILE_INFORMATION, 52, 4);
+}
+
+static void test_pack_LPCOMMCONFIG(void)
+{
+    /* LPCOMMCONFIG */
+    TEST_TYPE(LPCOMMCONFIG, 4, 4);
+}
+
+static void test_pack_LPCOMMPROP(void)
+{
+    /* LPCOMMPROP */
+    TEST_TYPE(LPCOMMPROP, 4, 4);
+    TEST_TYPE_POINTER(LPCOMMPROP, 64, 4);
+}
+
+static void test_pack_LPCOMMTIMEOUTS(void)
+{
+    /* LPCOMMTIMEOUTS */
+    TEST_TYPE(LPCOMMTIMEOUTS, 4, 4);
+    TEST_TYPE_POINTER(LPCOMMTIMEOUTS, 20, 4);
+}
+
+static void test_pack_LPCOMSTAT(void)
+{
+    /* LPCOMSTAT */
+    TEST_TYPE(LPCOMSTAT, 4, 4);
+    TEST_TYPE_POINTER(LPCOMSTAT, 12, 4);
+}
+
+static void test_pack_LPCONTEXT(void)
+{
+    /* LPCONTEXT */
+    TEST_TYPE(LPCONTEXT, 4, 4);
+}
+
+static void test_pack_LPCRITICAL_SECTION(void)
+{
+    /* LPCRITICAL_SECTION */
+    TEST_TYPE(LPCRITICAL_SECTION, 4, 4);
+}
+
+static void test_pack_LPCRITICAL_SECTION_DEBUG(void)
+{
+    /* LPCRITICAL_SECTION_DEBUG */
+    TEST_TYPE(LPCRITICAL_SECTION_DEBUG, 4, 4);
+}
+
+static void test_pack_LPDCB(void)
+{
+    /* LPDCB */
+    TEST_TYPE(LPDCB, 4, 4);
+}
+
+static void test_pack_LPDEBUG_EVENT(void)
+{
+    /* LPDEBUG_EVENT */
+    TEST_TYPE(LPDEBUG_EVENT, 4, 4);
+}
+
+static void test_pack_LPEXCEPTION_POINTERS(void)
+{
+    /* LPEXCEPTION_POINTERS */
+    TEST_TYPE(LPEXCEPTION_POINTERS, 4, 4);
+}
+
+static void test_pack_LPEXCEPTION_RECORD(void)
+{
+    /* LPEXCEPTION_RECORD */
+    TEST_TYPE(LPEXCEPTION_RECORD, 4, 4);
+}
+
+static void test_pack_LPFIBER_START_ROUTINE(void)
+{
+    /* LPFIBER_START_ROUTINE */
+    TEST_TYPE(LPFIBER_START_ROUTINE, 4, 4);
+}
+
+static void test_pack_LPHW_PROFILE_INFOA(void)
+{
+    /* LPHW_PROFILE_INFOA */
+    TEST_TYPE(LPHW_PROFILE_INFOA, 4, 4);
+    TEST_TYPE_POINTER(LPHW_PROFILE_INFOA, 124, 4);
+}
+
+static void test_pack_LPHW_PROFILE_INFOW(void)
+{
+    /* LPHW_PROFILE_INFOW */
+    TEST_TYPE(LPHW_PROFILE_INFOW, 4, 4);
+    TEST_TYPE_POINTER(LPHW_PROFILE_INFOW, 244, 4);
+}
+
+static void test_pack_LPLDT_ENTRY(void)
+{
+    /* LPLDT_ENTRY */
+    TEST_TYPE(LPLDT_ENTRY, 4, 4);
+}
+
+static void test_pack_LPMEMORYSTATUS(void)
+{
+    /* LPMEMORYSTATUS */
+    TEST_TYPE(LPMEMORYSTATUS, 4, 4);
+    TEST_TYPE_POINTER(LPMEMORYSTATUS, 32, 4);
+}
+
+static void test_pack_LPOFSTRUCT(void)
+{
+    /* LPOFSTRUCT */
+    TEST_TYPE(LPOFSTRUCT, 4, 4);
+    TEST_TYPE_POINTER(LPOFSTRUCT, 136, 2);
+}
+
+static void test_pack_LPOSVERSIONINFOA(void)
+{
+    /* LPOSVERSIONINFOA */
+    TEST_TYPE(LPOSVERSIONINFOA, 4, 4);
+    TEST_TYPE_POINTER(LPOSVERSIONINFOA, 148, 4);
+}
+
+static void test_pack_LPOSVERSIONINFOEXA(void)
+{
+    /* LPOSVERSIONINFOEXA */
+    TEST_TYPE(LPOSVERSIONINFOEXA, 4, 4);
+}
+
+static void test_pack_LPOSVERSIONINFOEXW(void)
+{
+    /* LPOSVERSIONINFOEXW */
+    TEST_TYPE(LPOSVERSIONINFOEXW, 4, 4);
+}
+
+static void test_pack_LPOSVERSIONINFOW(void)
+{
+    /* LPOSVERSIONINFOW */
+    TEST_TYPE(LPOSVERSIONINFOW, 4, 4);
+    TEST_TYPE_POINTER(LPOSVERSIONINFOW, 276, 4);
+}
+
+static void test_pack_LPOVERLAPPED(void)
+{
+    /* LPOVERLAPPED */
+    TEST_TYPE(LPOVERLAPPED, 4, 4);
+    TEST_TYPE_POINTER(LPOVERLAPPED, 20, 4);
+}
+
+static void test_pack_LPOVERLAPPED_COMPLETION_ROUTINE(void)
+{
+    /* LPOVERLAPPED_COMPLETION_ROUTINE */
+    TEST_TYPE(LPOVERLAPPED_COMPLETION_ROUTINE, 4, 4);
+}
+
+static void test_pack_LPPROCESS_HEAP_ENTRY(void)
+{
+    /* LPPROCESS_HEAP_ENTRY */
+    TEST_TYPE(LPPROCESS_HEAP_ENTRY, 4, 4);
+}
+
+static void test_pack_LPPROCESS_INFORMATION(void)
+{
+    /* LPPROCESS_INFORMATION */
+    TEST_TYPE(LPPROCESS_INFORMATION, 4, 4);
+    TEST_TYPE_POINTER(LPPROCESS_INFORMATION, 16, 4);
+}
+
+static void test_pack_LPPROGRESS_ROUTINE(void)
+{
+    /* LPPROGRESS_ROUTINE */
+    TEST_TYPE(LPPROGRESS_ROUTINE, 4, 4);
+}
+
+static void test_pack_LPSECURITY_ATTRIBUTES(void)
+{
+    /* LPSECURITY_ATTRIBUTES */
+    TEST_TYPE(LPSECURITY_ATTRIBUTES, 4, 4);
+    TEST_TYPE_POINTER(LPSECURITY_ATTRIBUTES, 12, 4);
+}
+
+static void test_pack_LPSTARTUPINFOA(void)
+{
+    /* LPSTARTUPINFOA */
+    TEST_TYPE(LPSTARTUPINFOA, 4, 4);
+}
+
+static void test_pack_LPSTARTUPINFOW(void)
+{
+    /* LPSTARTUPINFOW */
+    TEST_TYPE(LPSTARTUPINFOW, 4, 4);
+    TEST_TYPE_POINTER(LPSTARTUPINFOW, 68, 4);
+}
+
+static void test_pack_LPSYSTEMTIME(void)
+{
+    /* LPSYSTEMTIME */
+    TEST_TYPE(LPSYSTEMTIME, 4, 4);
+    TEST_TYPE_POINTER(LPSYSTEMTIME, 16, 2);
+}
+
+static void test_pack_LPSYSTEM_INFO(void)
+{
+    /* LPSYSTEM_INFO */
+    TEST_TYPE(LPSYSTEM_INFO, 4, 4);
+}
+
+static void test_pack_LPSYSTEM_POWER_STATUS(void)
+{
+    /* LPSYSTEM_POWER_STATUS */
+    TEST_TYPE(LPSYSTEM_POWER_STATUS, 4, 4);
+    TEST_TYPE_POINTER(LPSYSTEM_POWER_STATUS, 12, 4);
+}
+
+static void test_pack_LPTHREAD_START_ROUTINE(void)
+{
+    /* LPTHREAD_START_ROUTINE */
+    TEST_TYPE(LPTHREAD_START_ROUTINE, 4, 4);
+}
+
+static void test_pack_LPTIME_ZONE_INFORMATION(void)
+{
+    /* LPTIME_ZONE_INFORMATION */
+    TEST_TYPE(LPTIME_ZONE_INFORMATION, 4, 4);
+    TEST_TYPE_POINTER(LPTIME_ZONE_INFORMATION, 172, 4);
+}
+
+static void test_pack_LPWIN32_FILE_ATTRIBUTE_DATA(void)
+{
+    /* LPWIN32_FILE_ATTRIBUTE_DATA */
+    TEST_TYPE(LPWIN32_FILE_ATTRIBUTE_DATA, 4, 4);
+    TEST_TYPE_POINTER(LPWIN32_FILE_ATTRIBUTE_DATA, 36, 4);
+}
+
+static void test_pack_LPWIN32_FIND_DATAA(void)
+{
+    /* LPWIN32_FIND_DATAA */
+    TEST_TYPE(LPWIN32_FIND_DATAA, 4, 4);
+    TEST_TYPE_POINTER(LPWIN32_FIND_DATAA, 320, 4);
+}
+
+static void test_pack_LPWIN32_FIND_DATAW(void)
+{
+    /* LPWIN32_FIND_DATAW */
+    TEST_TYPE(LPWIN32_FIND_DATAW, 4, 4);
+    TEST_TYPE_POINTER(LPWIN32_FIND_DATAW, 592, 4);
+}
+
+static void test_pack_LPWIN32_STREAM_ID(void)
+{
+    /* LPWIN32_STREAM_ID */
+    TEST_TYPE(LPWIN32_STREAM_ID, 4, 4);
+    TEST_TYPE_POINTER(LPWIN32_STREAM_ID, 24, 4);
+}
+
+static void test_pack_MEMORYSTATUS(void)
+{
+    /* MEMORYSTATUS (pack 4) */
+    TEST_TYPE(MEMORYSTATUS, 32, 4);
+    TEST_FIELD(MEMORYSTATUS, DWORD, dwLength, 0, 4, 4);
+    TEST_FIELD(MEMORYSTATUS, DWORD, dwMemoryLoad, 4, 4, 4);
+    TEST_FIELD(MEMORYSTATUS, SIZE_T, dwTotalPhys, 8, 4, 4);
+    TEST_FIELD(MEMORYSTATUS, SIZE_T, dwAvailPhys, 12, 4, 4);
+    TEST_FIELD(MEMORYSTATUS, SIZE_T, dwTotalPageFile, 16, 4, 4);
+    TEST_FIELD(MEMORYSTATUS, SIZE_T, dwAvailPageFile, 20, 4, 4);
+    TEST_FIELD(MEMORYSTATUS, SIZE_T, dwTotalVirtual, 24, 4, 4);
+    TEST_FIELD(MEMORYSTATUS, SIZE_T, dwAvailVirtual, 28, 4, 4);
+}
+
+static void test_pack_OFSTRUCT(void)
+{
+    /* OFSTRUCT (pack 4) */
+    TEST_TYPE(OFSTRUCT, 136, 2);
+    TEST_FIELD(OFSTRUCT, BYTE, cBytes, 0, 1, 1);
+    TEST_FIELD(OFSTRUCT, BYTE, fFixedDisk, 1, 1, 1);
+    TEST_FIELD(OFSTRUCT, WORD, nErrCode, 2, 2, 2);
+    TEST_FIELD(OFSTRUCT, WORD, Reserved1, 4, 2, 2);
+    TEST_FIELD(OFSTRUCT, WORD, Reserved2, 6, 2, 2);
+    TEST_FIELD(OFSTRUCT, BYTE[OFS_MAXPATHNAME], szPathName, 8, 128, 1);
+}
+
+static void test_pack_OSVERSIONINFOA(void)
+{
+    /* OSVERSIONINFOA (pack 4) */
+    TEST_TYPE(OSVERSIONINFOA, 148, 4);
+    TEST_FIELD(OSVERSIONINFOA, DWORD, dwOSVersionInfoSize, 0, 4, 4);
+    TEST_FIELD(OSVERSIONINFOA, DWORD, dwMajorVersion, 4, 4, 4);
+    TEST_FIELD(OSVERSIONINFOA, DWORD, dwMinorVersion, 8, 4, 4);
+    TEST_FIELD(OSVERSIONINFOA, DWORD, dwBuildNumber, 12, 4, 4);
+    TEST_FIELD(OSVERSIONINFOA, DWORD, dwPlatformId, 16, 4, 4);
+    TEST_FIELD(OSVERSIONINFOA, CHAR[128], szCSDVersion, 20, 128, 1);
+}
+
+static void test_pack_OSVERSIONINFOEXA(void)
+{
+    /* OSVERSIONINFOEXA (pack 4) */
+    TEST_FIELD(OSVERSIONINFOEXA, DWORD, dwOSVersionInfoSize, 0, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXA, DWORD, dwMajorVersion, 4, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXA, DWORD, dwMinorVersion, 8, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXA, DWORD, dwBuildNumber, 12, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXA, DWORD, dwPlatformId, 16, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXA, CHAR[128], szCSDVersion, 20, 128, 1);
+    TEST_FIELD(OSVERSIONINFOEXA, WORD, wServicePackMajor, 148, 2, 2);
+    TEST_FIELD(OSVERSIONINFOEXA, WORD, wServicePackMinor, 150, 2, 2);
+}
+
+static void test_pack_OSVERSIONINFOEXW(void)
+{
+    /* OSVERSIONINFOEXW (pack 4) */
+    TEST_FIELD(OSVERSIONINFOEXW, DWORD, dwOSVersionInfoSize, 0, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXW, DWORD, dwMajorVersion, 4, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXW, DWORD, dwMinorVersion, 8, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXW, DWORD, dwBuildNumber, 12, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXW, DWORD, dwPlatformId, 16, 4, 4);
+    TEST_FIELD(OSVERSIONINFOEXW, WCHAR[128], szCSDVersion, 20, 256, 2);
+    TEST_FIELD(OSVERSIONINFOEXW, WORD, wServicePackMajor, 276, 2, 2);
+    TEST_FIELD(OSVERSIONINFOEXW, WORD, wServicePackMinor, 278, 2, 2);
+}
+
+static void test_pack_OSVERSIONINFOW(void)
+{
+    /* OSVERSIONINFOW (pack 4) */
+    TEST_TYPE(OSVERSIONINFOW, 276, 4);
+    TEST_FIELD(OSVERSIONINFOW, DWORD, dwOSVersionInfoSize, 0, 4, 4);
+    TEST_FIELD(OSVERSIONINFOW, DWORD, dwMajorVersion, 4, 4, 4);
+    TEST_FIELD(OSVERSIONINFOW, DWORD, dwMinorVersion, 8, 4, 4);
+    TEST_FIELD(OSVERSIONINFOW, DWORD, dwBuildNumber, 12, 4, 4);
+    TEST_FIELD(OSVERSIONINFOW, DWORD, dwPlatformId, 16, 4, 4);
+    TEST_FIELD(OSVERSIONINFOW, WCHAR[128], szCSDVersion, 20, 256, 2);
+}
+
+static void test_pack_OUTPUT_DEBUG_STRING_INFO(void)
+{
+    /* OUTPUT_DEBUG_STRING_INFO (pack 4) */
+}
+
+static void test_pack_OVERLAPPED(void)
+{
+    /* OVERLAPPED (pack 4) */
+    TEST_TYPE(OVERLAPPED, 20, 4);
+    TEST_FIELD(OVERLAPPED, DWORD, Internal, 0, 4, 4);
+    TEST_FIELD(OVERLAPPED, DWORD, InternalHigh, 4, 4, 4);
+    TEST_FIELD(OVERLAPPED, DWORD, Offset, 8, 4, 4);
+    TEST_FIELD(OVERLAPPED, DWORD, OffsetHigh, 12, 4, 4);
+    TEST_FIELD(OVERLAPPED, HANDLE, hEvent, 16, 4, 4);
+}
+
+static void test_pack_PAPCFUNC(void)
+{
+    /* PAPCFUNC */
+    TEST_TYPE(PAPCFUNC, 4, 4);
+}
+
+static void test_pack_PBY_HANDLE_FILE_INFORMATION(void)
+{
+    /* PBY_HANDLE_FILE_INFORMATION */
+    TEST_TYPE(PBY_HANDLE_FILE_INFORMATION, 4, 4);
+    TEST_TYPE_POINTER(PBY_HANDLE_FILE_INFORMATION, 52, 4);
+}
+
+static void test_pack_PCRITICAL_SECTION(void)
+{
+    /* PCRITICAL_SECTION */
+    TEST_TYPE(PCRITICAL_SECTION, 4, 4);
+}
+
+static void test_pack_PCRITICAL_SECTION_DEBUG(void)
+{
+    /* PCRITICAL_SECTION_DEBUG */
+    TEST_TYPE(PCRITICAL_SECTION_DEBUG, 4, 4);
+}
+
+static void test_pack_PFIBER_START_ROUTINE(void)
+{
+    /* PFIBER_START_ROUTINE */
+    TEST_TYPE(PFIBER_START_ROUTINE, 4, 4);
+}
+
+static void test_pack_POFSTRUCT(void)
+{
+    /* POFSTRUCT */
+    TEST_TYPE(POFSTRUCT, 4, 4);
+    TEST_TYPE_POINTER(POFSTRUCT, 136, 2);
+}
+
+static void test_pack_POSVERSIONINFOA(void)
+{
+    /* POSVERSIONINFOA */
+    TEST_TYPE(POSVERSIONINFOA, 4, 4);
+    TEST_TYPE_POINTER(POSVERSIONINFOA, 148, 4);
+}
+
+static void test_pack_POSVERSIONINFOEXA(void)
+{
+    /* POSVERSIONINFOEXA */
+    TEST_TYPE(POSVERSIONINFOEXA, 4, 4);
+}
+
+static void test_pack_POSVERSIONINFOEXW(void)
+{
+    /* POSVERSIONINFOEXW */
+    TEST_TYPE(POSVERSIONINFOEXW, 4, 4);
+}
+
+static void test_pack_POSVERSIONINFOW(void)
+{
+    /* POSVERSIONINFOW */
+    TEST_TYPE(POSVERSIONINFOW, 4, 4);
+    TEST_TYPE_POINTER(POSVERSIONINFOW, 276, 4);
+}
+
+static void test_pack_PPROCESS_HEAP_ENTRY(void)
+{
+    /* PPROCESS_HEAP_ENTRY */
+    TEST_TYPE(PPROCESS_HEAP_ENTRY, 4, 4);
+}
+
+static void test_pack_PPROCESS_INFORMATION(void)
+{
+    /* PPROCESS_INFORMATION */
+    TEST_TYPE(PPROCESS_INFORMATION, 4, 4);
+    TEST_TYPE_POINTER(PPROCESS_INFORMATION, 16, 4);
+}
+
+static void test_pack_PROCESS_HEAP_ENTRY(void)
+{
+    /* PROCESS_HEAP_ENTRY (pack 4) */
+    TEST_FIELD(PROCESS_HEAP_ENTRY, LPVOID, lpData, 0, 4, 4);
+    TEST_FIELD(PROCESS_HEAP_ENTRY, DWORD, cbData, 4, 4, 4);
+    TEST_FIELD(PROCESS_HEAP_ENTRY, BYTE, cbOverhead, 8, 1, 1);
+    TEST_FIELD(PROCESS_HEAP_ENTRY, BYTE, iRegionIndex, 9, 1, 1);
+    TEST_FIELD(PROCESS_HEAP_ENTRY, WORD, wFlags, 10, 2, 2);
+}
+
+static void test_pack_PROCESS_INFORMATION(void)
+{
+    /* PROCESS_INFORMATION (pack 4) */
+    TEST_TYPE(PROCESS_INFORMATION, 16, 4);
+    TEST_FIELD(PROCESS_INFORMATION, HANDLE, hProcess, 0, 4, 4);
+    TEST_FIELD(PROCESS_INFORMATION, HANDLE, hThread, 4, 4, 4);
+    TEST_FIELD(PROCESS_INFORMATION, DWORD, dwProcessId, 8, 4, 4);
+    TEST_FIELD(PROCESS_INFORMATION, DWORD, dwThreadId, 12, 4, 4);
+}
+
+static void test_pack_PSECURITY_ATTRIBUTES(void)
+{
+    /* PSECURITY_ATTRIBUTES */
+    TEST_TYPE(PSECURITY_ATTRIBUTES, 4, 4);
+    TEST_TYPE_POINTER(PSECURITY_ATTRIBUTES, 12, 4);
+}
+
+static void test_pack_PSYSTEMTIME(void)
+{
+    /* PSYSTEMTIME */
+    TEST_TYPE(PSYSTEMTIME, 4, 4);
+    TEST_TYPE_POINTER(PSYSTEMTIME, 16, 2);
+}
+
+static void test_pack_PTIMERAPCROUTINE(void)
+{
+    /* PTIMERAPCROUTINE */
+    TEST_TYPE(PTIMERAPCROUTINE, 4, 4);
+}
+
+static void test_pack_PTIME_ZONE_INFORMATION(void)
+{
+    /* PTIME_ZONE_INFORMATION */
+    TEST_TYPE(PTIME_ZONE_INFORMATION, 4, 4);
+    TEST_TYPE_POINTER(PTIME_ZONE_INFORMATION, 172, 4);
+}
+
+static void test_pack_PWIN32_FIND_DATAA(void)
+{
+    /* PWIN32_FIND_DATAA */
+    TEST_TYPE(PWIN32_FIND_DATAA, 4, 4);
+    TEST_TYPE_POINTER(PWIN32_FIND_DATAA, 320, 4);
+}
+
+static void test_pack_PWIN32_FIND_DATAW(void)
+{
+    /* PWIN32_FIND_DATAW */
+    TEST_TYPE(PWIN32_FIND_DATAW, 4, 4);
+    TEST_TYPE_POINTER(PWIN32_FIND_DATAW, 592, 4);
+}
+
+static void test_pack_RIP_INFO(void)
+{
+    /* RIP_INFO (pack 4) */
+    TEST_TYPE(RIP_INFO, 8, 4);
+    TEST_FIELD(RIP_INFO, DWORD, dwError, 0, 4, 4);
+    TEST_FIELD(RIP_INFO, DWORD, dwType, 4, 4, 4);
+}
+
+static void test_pack_SECURITY_ATTRIBUTES(void)
+{
+    /* SECURITY_ATTRIBUTES (pack 4) */
+    TEST_TYPE(SECURITY_ATTRIBUTES, 12, 4);
+    TEST_FIELD(SECURITY_ATTRIBUTES, DWORD, nLength, 0, 4, 4);
+    TEST_FIELD(SECURITY_ATTRIBUTES, LPVOID, lpSecurityDescriptor, 4, 4, 4);
+    TEST_FIELD(SECURITY_ATTRIBUTES, BOOL, bInheritHandle, 8, 4, 4);
+}
+
+static void test_pack_STARTUPINFOA(void)
+{
+    /* STARTUPINFOA (pack 4) */
+    TEST_FIELD(STARTUPINFOA, DWORD, cb, 0, 4, 4);
+}
+
+static void test_pack_STARTUPINFOW(void)
+{
+    /* STARTUPINFOW (pack 4) */
+    TEST_TYPE(STARTUPINFOW, 68, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, cb, 0, 4, 4);
+    TEST_FIELD(STARTUPINFOW, LPWSTR, lpReserved, 4, 4, 4);
+    TEST_FIELD(STARTUPINFOW, LPWSTR, lpDesktop, 8, 4, 4);
+    TEST_FIELD(STARTUPINFOW, LPWSTR, lpTitle, 12, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwX, 16, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwY, 20, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwXSize, 24, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwYSize, 28, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwXCountChars, 32, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwYCountChars, 36, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwFillAttribute, 40, 4, 4);
+    TEST_FIELD(STARTUPINFOW, DWORD, dwFlags, 44, 4, 4);
+    TEST_FIELD(STARTUPINFOW, WORD, wShowWindow, 48, 2, 2);
+    TEST_FIELD(STARTUPINFOW, WORD, cbReserved2, 50, 2, 2);
+    TEST_FIELD(STARTUPINFOW, BYTE *, lpReserved2, 52, 4, 4);
+    TEST_FIELD(STARTUPINFOW, HANDLE, hStdInput, 56, 4, 4);
+    TEST_FIELD(STARTUPINFOW, HANDLE, hStdOutput, 60, 4, 4);
+    TEST_FIELD(STARTUPINFOW, HANDLE, hStdError, 64, 4, 4);
+}
+
+static void test_pack_SYSTEMTIME(void)
+{
+    /* SYSTEMTIME (pack 4) */
+    TEST_TYPE(SYSTEMTIME, 16, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wYear, 0, 2, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wMonth, 2, 2, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wDayOfWeek, 4, 2, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wDay, 6, 2, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wHour, 8, 2, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wMinute, 10, 2, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wSecond, 12, 2, 2);
+    TEST_FIELD(SYSTEMTIME, WORD, wMilliseconds, 14, 2, 2);
+}
+
+static void test_pack_SYSTEM_POWER_STATUS(void)
+{
+    /* SYSTEM_POWER_STATUS (pack 4) */
+    TEST_TYPE(SYSTEM_POWER_STATUS, 12, 4);
+    TEST_FIELD(SYSTEM_POWER_STATUS, BYTE, ACLineStatus, 0, 1, 1);
+    TEST_FIELD(SYSTEM_POWER_STATUS, BYTE, BatteryFlag, 1, 1, 1);
+    TEST_FIELD(SYSTEM_POWER_STATUS, BYTE, BatteryLifePercent, 2, 1, 1);
+    TEST_FIELD(SYSTEM_POWER_STATUS, BYTE, Reserved1, 3, 1, 1);
+    TEST_FIELD(SYSTEM_POWER_STATUS, DWORD, BatteryLifeTime, 4, 4, 4);
+    TEST_FIELD(SYSTEM_POWER_STATUS, DWORD, BatteryFullLifeTime, 8, 4, 4);
+}
+
+static void test_pack_TIME_ZONE_INFORMATION(void)
+{
+    /* TIME_ZONE_INFORMATION (pack 4) */
+    TEST_TYPE(TIME_ZONE_INFORMATION, 172, 4);
+    TEST_FIELD(TIME_ZONE_INFORMATION, LONG, Bias, 0, 4, 4);
+    TEST_FIELD(TIME_ZONE_INFORMATION, WCHAR[32], StandardName, 4, 64, 2);
+    TEST_FIELD(TIME_ZONE_INFORMATION, SYSTEMTIME, StandardDate, 68, 16, 2);
+    TEST_FIELD(TIME_ZONE_INFORMATION, LONG, StandardBias, 84, 4, 4);
+    TEST_FIELD(TIME_ZONE_INFORMATION, WCHAR[32], DaylightName, 88, 64, 2);
+    TEST_FIELD(TIME_ZONE_INFORMATION, SYSTEMTIME, DaylightDate, 152, 16, 2);
+    TEST_FIELD(TIME_ZONE_INFORMATION, LONG, DaylightBias, 168, 4, 4);
+}
+
+static void test_pack_UNLOAD_DLL_DEBUG_INFO(void)
+{
+    /* UNLOAD_DLL_DEBUG_INFO (pack 4) */
+    TEST_TYPE(UNLOAD_DLL_DEBUG_INFO, 4, 4);
+    TEST_FIELD(UNLOAD_DLL_DEBUG_INFO, LPVOID, lpBaseOfDll, 0, 4, 4);
+}
+
+static void test_pack_WIN32_FILE_ATTRIBUTE_DATA(void)
+{
+    /* WIN32_FILE_ATTRIBUTE_DATA (pack 4) */
+    TEST_TYPE(WIN32_FILE_ATTRIBUTE_DATA, 36, 4);
+    TEST_FIELD(WIN32_FILE_ATTRIBUTE_DATA, DWORD, dwFileAttributes, 0, 4, 4);
+    TEST_FIELD(WIN32_FILE_ATTRIBUTE_DATA, FILETIME, ftCreationTime, 4, 8, 4);
+    TEST_FIELD(WIN32_FILE_ATTRIBUTE_DATA, FILETIME, ftLastAccessTime, 12, 8, 4);
+    TEST_FIELD(WIN32_FILE_ATTRIBUTE_DATA, FILETIME, ftLastWriteTime, 20, 8, 4);
+    TEST_FIELD(WIN32_FILE_ATTRIBUTE_DATA, DWORD, nFileSizeHigh, 28, 4, 4);
+    TEST_FIELD(WIN32_FILE_ATTRIBUTE_DATA, DWORD, nFileSizeLow, 32, 4, 4);
+}
+
+static void test_pack_WIN32_FIND_DATAA(void)
+{
+    /* WIN32_FIND_DATAA (pack 4) */
+    TEST_TYPE(WIN32_FIND_DATAA, 320, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, DWORD, dwFileAttributes, 0, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, FILETIME, ftCreationTime, 4, 8, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, FILETIME, ftLastAccessTime, 12, 8, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, FILETIME, ftLastWriteTime, 20, 8, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, DWORD, nFileSizeHigh, 28, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, DWORD, nFileSizeLow, 32, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, DWORD, dwReserved0, 36, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, DWORD, dwReserved1, 40, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAA, CHAR[260], cFileName, 44, 260, 1);
+    TEST_FIELD(WIN32_FIND_DATAA, CHAR[14], cAlternateFileName, 304, 14, 1);
+}
+
+static void test_pack_WIN32_FIND_DATAW(void)
+{
+    /* WIN32_FIND_DATAW (pack 4) */
+    TEST_TYPE(WIN32_FIND_DATAW, 592, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, DWORD, dwFileAttributes, 0, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, FILETIME, ftCreationTime, 4, 8, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, FILETIME, ftLastAccessTime, 12, 8, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, FILETIME, ftLastWriteTime, 20, 8, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, DWORD, nFileSizeHigh, 28, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, DWORD, nFileSizeLow, 32, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, DWORD, dwReserved0, 36, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, DWORD, dwReserved1, 40, 4, 4);
+    TEST_FIELD(WIN32_FIND_DATAW, WCHAR[260], cFileName, 44, 520, 2);
+    TEST_FIELD(WIN32_FIND_DATAW, WCHAR[14], cAlternateFileName, 564, 28, 2);
+}
+
+static void test_pack(void)
+{
+    test_pack_BY_HANDLE_FILE_INFORMATION();
+    test_pack_COMMCONFIG();
+    test_pack_COMMPROP();
+    test_pack_COMMTIMEOUTS();
+    test_pack_COMSTAT();
+    test_pack_CREATE_PROCESS_DEBUG_INFO();
+    test_pack_CREATE_THREAD_DEBUG_INFO();
+    test_pack_CRITICAL_SECTION();
+    test_pack_CRITICAL_SECTION_DEBUG();
+    test_pack_DCB();
+    test_pack_DEBUG_EVENT();
+    test_pack_EXCEPTION_DEBUG_INFO();
+    test_pack_EXIT_PROCESS_DEBUG_INFO();
+    test_pack_EXIT_THREAD_DEBUG_INFO();
+    test_pack_HW_PROFILE_INFOA();
+    test_pack_HW_PROFILE_INFOW();
+    test_pack_LDT_ENTRY();
+    test_pack_LOAD_DLL_DEBUG_INFO();
+    test_pack_LPBY_HANDLE_FILE_INFORMATION();
+    test_pack_LPCOMMCONFIG();
+    test_pack_LPCOMMPROP();
+    test_pack_LPCOMMTIMEOUTS();
+    test_pack_LPCOMSTAT();
+    test_pack_LPCONTEXT();
+    test_pack_LPCRITICAL_SECTION();
+    test_pack_LPCRITICAL_SECTION_DEBUG();
+    test_pack_LPDCB();
+    test_pack_LPDEBUG_EVENT();
+    test_pack_LPEXCEPTION_POINTERS();
+    test_pack_LPEXCEPTION_RECORD();
+    test_pack_LPFIBER_START_ROUTINE();
+    test_pack_LPHW_PROFILE_INFOA();
+    test_pack_LPHW_PROFILE_INFOW();
+    test_pack_LPLDT_ENTRY();
+    test_pack_LPMEMORYSTATUS();
+    test_pack_LPOFSTRUCT();
+    test_pack_LPOSVERSIONINFOA();
+    test_pack_LPOSVERSIONINFOEXA();
+    test_pack_LPOSVERSIONINFOEXW();
+    test_pack_LPOSVERSIONINFOW();
+    test_pack_LPOVERLAPPED();
+    test_pack_LPOVERLAPPED_COMPLETION_ROUTINE();
+    test_pack_LPPROCESS_HEAP_ENTRY();
+    test_pack_LPPROCESS_INFORMATION();
+    test_pack_LPPROGRESS_ROUTINE();
+    test_pack_LPSECURITY_ATTRIBUTES();
+    test_pack_LPSTARTUPINFOA();
+    test_pack_LPSTARTUPINFOW();
+    test_pack_LPSYSTEMTIME();
+    test_pack_LPSYSTEM_INFO();
+    test_pack_LPSYSTEM_POWER_STATUS();
+    test_pack_LPTHREAD_START_ROUTINE();
+    test_pack_LPTIME_ZONE_INFORMATION();
+    test_pack_LPWIN32_FILE_ATTRIBUTE_DATA();
+    test_pack_LPWIN32_FIND_DATAA();
+    test_pack_LPWIN32_FIND_DATAW();
+    test_pack_LPWIN32_STREAM_ID();
+    test_pack_MEMORYSTATUS();
+    test_pack_OFSTRUCT();
+    test_pack_OSVERSIONINFOA();
+    test_pack_OSVERSIONINFOEXA();
+    test_pack_OSVERSIONINFOEXW();
+    test_pack_OSVERSIONINFOW();
+    test_pack_OUTPUT_DEBUG_STRING_INFO();
+    test_pack_OVERLAPPED();
+    test_pack_PAPCFUNC();
+    test_pack_PBY_HANDLE_FILE_INFORMATION();
+    test_pack_PCRITICAL_SECTION();
+    test_pack_PCRITICAL_SECTION_DEBUG();
+    test_pack_PFIBER_START_ROUTINE();
+    test_pack_POFSTRUCT();
+    test_pack_POSVERSIONINFOA();
+    test_pack_POSVERSIONINFOEXA();
+    test_pack_POSVERSIONINFOEXW();
+    test_pack_POSVERSIONINFOW();
+    test_pack_PPROCESS_HEAP_ENTRY();
+    test_pack_PPROCESS_INFORMATION();
+    test_pack_PROCESS_HEAP_ENTRY();
+    test_pack_PROCESS_INFORMATION();
+    test_pack_PSECURITY_ATTRIBUTES();
+    test_pack_PSYSTEMTIME();
+    test_pack_PTIMERAPCROUTINE();
+    test_pack_PTIME_ZONE_INFORMATION();
+    test_pack_PWIN32_FIND_DATAA();
+    test_pack_PWIN32_FIND_DATAW();
+    test_pack_RIP_INFO();
+    test_pack_SECURITY_ATTRIBUTES();
+    test_pack_STARTUPINFOA();
+    test_pack_STARTUPINFOW();
+    test_pack_SYSTEMTIME();
+    test_pack_SYSTEM_POWER_STATUS();
+    test_pack_TIME_ZONE_INFORMATION();
+    test_pack_UNLOAD_DLL_DEBUG_INFO();
+    test_pack_WIN32_FILE_ATTRIBUTE_DATA();
+    test_pack_WIN32_FIND_DATAA();
+    test_pack_WIN32_FIND_DATAW();
+}
+
+START_TEST(generated)
+{
+    test_pack();
+}
diff --git a/reactos/apps/tests/kernel32/heap.c b/reactos/apps/tests/kernel32/heap.c
new file mode 100644 (file)
index 0000000..358b00d
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Unit test suite for heap functions
+ *
+ * Copyright 2003 Dimitrie O. Paun
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/test.h"
+
+START_TEST(heap)
+{
+    void *mem;
+    HGLOBAL gbl;
+    SIZE_T size;
+
+    /* Heap*() functions */
+    mem = HeapAlloc(GetProcessHeap(), 0, 0);
+    ok(mem != NULL, "memory not allocated for size 0\n");
+
+    mem = HeapReAlloc(GetProcessHeap(), 0, NULL, 10);
+    ok(mem == NULL, "memory allocated by HeapReAlloc\n");
+
+    /* Global*() functions */
+    gbl = GlobalAlloc(GMEM_MOVEABLE, 0);
+    ok(gbl != NULL, "global memory not allocated for size 0\n");
+
+    gbl = GlobalReAlloc(gbl, 10, GMEM_MOVEABLE);
+    ok(gbl != NULL, "Can't realloc global memory\n");
+    size = GlobalSize(gbl);
+    ok(size >= 10 && size <= 16, "Memory not resized to size 10, instead size=%ld\n", size);
+
+    todo_wine
+    { 
+        gbl = GlobalReAlloc(gbl, 0, GMEM_MOVEABLE);
+        ok(gbl != NULL, "GlobalReAlloc should not fail on size 0\n");
+    }
+
+    size = GlobalSize(gbl);
+    ok(size == 0, "Memory not resized to size 0, instead size=%ld\n", size);
+    ok(GlobalFree(gbl) == NULL, "Memory not freed\n");
+    size = GlobalSize(gbl);
+    ok(size == 0, "Memory should have been freed, size=%ld\n", size);
+
+    gbl = GlobalReAlloc(0, 10, GMEM_MOVEABLE);
+    ok(gbl == NULL, "global realloc allocated memory\n");
+
+    /* Local*() functions */
+    gbl = LocalAlloc(GMEM_MOVEABLE, 0);
+    ok(gbl != NULL, "local memory not allocated for size 0\n");
+
+    gbl = LocalReAlloc(gbl, 10, GMEM_MOVEABLE);
+    ok(gbl != NULL, "Can't realloc local memory\n");
+    size = LocalSize(gbl);
+    ok(size >= 10 && size <= 16, "Memory not resized to size 10, instead size=%ld\n", size);
+
+    todo_wine
+    {
+        gbl = LocalReAlloc(gbl, 0, GMEM_MOVEABLE);
+        ok(gbl != NULL, "LocalReAlloc should not fail on size 0\n");
+    }
+
+    size = LocalSize(gbl);
+    ok(size == 0, "Memory not resized to size 0, instead size=%ld\n", size);
+    ok(LocalFree(gbl) == NULL, "Memory not freed\n");
+    size = LocalSize(gbl);
+    ok(size == 0, "Memory should have been freed, size=%ld\n", size);
+
+    gbl = LocalReAlloc(0, 10, GMEM_MOVEABLE);
+    ok(gbl == NULL, "local realloc allocated memory\n");
+
+}
diff --git a/reactos/apps/tests/kernel32/locale.c b/reactos/apps/tests/kernel32/locale.c
new file mode 100644 (file)
index 0000000..0c97b6c
--- /dev/null
@@ -0,0 +1,2043 @@
+/*
+ * Unit tests for locale functions
+ *
+ * Copyright 2002 YASAR Mehmet
+ * Copyright 2003 Dmitry Timoshkov
+ * Copyright 2003 Jon Griffiths
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * NOTES
+ *  We must pass LOCALE_NOUSEROVERRIDE (NUO) to formatting functions so that
+ *  even when the user has overridden their default i8n settings (e.g. in
+ *  the control panel i8n page), we will still get the expected results.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winnls.h"
+
+#ifndef DATE_YEARMONTH
+#define DATE_YEARMONTH 8
+#endif
+#ifndef MAP_EXPAND_LIGATURES
+#define MAP_EXPAND_LIGATURES   0x2000
+#endif
+
+static inline unsigned int strlenW( const WCHAR *str )
+{
+    const WCHAR *s = str;
+    while (*s) s++;
+    return s - str;
+}
+
+static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n )
+{
+    if (n <= 0) return 0;
+    while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; }
+    return *str1 - *str2;
+}
+
+static inline WCHAR *strchrW( const WCHAR *str, WCHAR ch )
+{
+    for ( ; *str; str++) if (*str == ch) return (WCHAR *)str;
+    return NULL;
+}
+
+inline static int isdigitW( WCHAR wc )
+{
+    WORD type;
+    GetStringTypeW( CT_CTYPE1, &wc, 1, &type );
+    return type & C1_DIGIT;
+}
+
+/* Some functions are only in later versions of kernel32.dll */
+static HMODULE hKernel32;
+
+typedef BOOL (WINAPI *EnumSystemLanguageGroupsAFn)(LANGUAGEGROUP_ENUMPROC,
+                                                   DWORD, LONG_PTR);
+static EnumSystemLanguageGroupsAFn pEnumSystemLanguageGroupsA;
+typedef BOOL (WINAPI *EnumLanguageGroupLocalesAFn)(LANGGROUPLOCALE_ENUMPROC,
+                                                   LGRPID, DWORD, LONG_PTR);
+static EnumLanguageGroupLocalesAFn pEnumLanguageGroupLocalesA;
+
+typedef INT (WINAPI *FoldStringAFn)(DWORD, LPCSTR, INT, LPSTR, INT);
+static FoldStringAFn pFoldStringA;
+typedef INT (WINAPI *FoldStringWFn)(DWORD, LPCWSTR, INT, LPWSTR, INT);
+static FoldStringWFn pFoldStringW;
+
+typedef BOOL (WINAPI *IsValidLanguageGroupFn)(LGRPID, DWORD);
+static IsValidLanguageGroupFn pIsValidLanguageGroup;
+
+static void InitFunctionPointers(void)
+{
+  hKernel32 = GetModuleHandleA("kernel32");
+
+  if (hKernel32)
+  {
+    pEnumSystemLanguageGroupsA = (void*)GetProcAddress(hKernel32, "EnumSystemLanguageGroupsA");
+    pEnumLanguageGroupLocalesA = (void*)GetProcAddress(hKernel32, "EnumLanguageGroupLocalesA");
+    pFoldStringA = (void*)GetProcAddress(hKernel32, "FoldStringA");
+    pFoldStringW = (void*)GetProcAddress(hKernel32, "FoldStringW");
+    pIsValidLanguageGroup = (void*)GetProcAddress(hKernel32, "IsValidLanguageGroup");
+  }
+}
+
+#define eq(received, expected, label, type) \
+        ok((received) == (expected), "%s: got " type " instead of " type "\n", \
+           (label), (received), (expected))
+
+#define BUFFER_SIZE    128
+char GlobalBuffer[BUFFER_SIZE]; /* Buffer used by callback function */
+#define COUNTOF(x) (sizeof(x)/sizeof(x)[0])
+
+#define EXPECT_LEN(len) ok(ret == (len), "Expected Len %d, got %d\n", (len), ret)
+#define EXPECT_INVALID  ok(GetLastError() == ERROR_INVALID_PARAMETER, \
+ "Expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError())
+#define EXPECT_BUFFER  ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, \
+ "Expected ERROR_INSUFFICIENT_BUFFER, got %ld\n", GetLastError())
+#define EXPECT_FLAGS  ok(GetLastError() == ERROR_INVALID_FLAGS, \
+ "Expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError())
+#define EXPECT_INVALIDFLAGS ok(GetLastError() == ERROR_INVALID_FLAGS || \
+  GetLastError() == ERROR_INVALID_PARAMETER, \
+ "Expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError())
+#define EXPECT_VALID    ok(GetLastError() == 0, \
+ "Expected GetLastError() == 0, got %ld\n", GetLastError())
+
+#define STRINGSA(x,y) strcpy(input, x); strcpy(Expected, y); SetLastError(0); buffer[0] = '\0'
+#define EXPECT_LENA EXPECT_LEN((int)strlen(Expected)+1)
+#define EXPECT_EQA ok(strncmp(buffer, Expected, strlen(Expected)) == 0, \
+  "Expected '%s', got '%s'\n", Expected, buffer)
+
+#define STRINGSW(x,y) MultiByteToWideChar(CP_ACP,0,x,-1,input,COUNTOF(input)); \
+   MultiByteToWideChar(CP_ACP,0,y,-1,Expected,COUNTOF(Expected)); \
+   SetLastError(0); buffer[0] = '\0'
+#define EXPECT_LENW EXPECT_LEN((int)strlenW(Expected)+1)
+#define EXPECT_EQW  ok(strncmpW(buffer, Expected, strlenW(Expected)) == 0, "Bad conversion\n")
+
+#define NUO LOCALE_NOUSEROVERRIDE
+
+static void test_GetLocaleInfoA()
+{
+  int ret;
+  LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+  char buffer[BUFFER_SIZE];
+
+  ok(lcid == 0x409, "wrong LCID calculated - %ld\n", lcid);
+
+  /* HTMLKit and "Font xplorer lite" expect GetLocaleInfoA to
+   * partially fill the buffer even if it is too short. See bug 637.
+   */
+  SetLastError(0); memset(buffer, 0, COUNTOF(buffer));
+  ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 0);
+  ok(ret == 7 && !buffer[0], "Expected len=7, got %d\n", ret);
+
+  SetLastError(0); memset(buffer, 0, COUNTOF(buffer));
+  ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 3);
+  EXPECT_BUFFER; EXPECT_LEN(0);
+  ok(!strcmp(buffer, "Mon"), "Expected 'Mon', got '%s'\n", buffer);
+
+  SetLastError(0); memset(buffer, 0, COUNTOF(buffer));
+  ret = GetLocaleInfoA(lcid, NUO|LOCALE_SDAYNAME1, buffer, 10);
+  EXPECT_VALID; EXPECT_LEN(7);
+  ok(!strcmp(buffer, "Monday"), "Expected 'Monday', got '%s'\n", buffer);
+}
+
+static void test_GetTimeFormatA()
+{
+  int ret;
+  SYSTEMTIME  curtime;
+  LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+  char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
+
+  memset(&curtime, 2, sizeof(SYSTEMTIME));
+  STRINGSA("tt HH':'mm'@'ss", ""); /* Invalid time */
+  ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  curtime.wHour = 8;
+  curtime.wMinute = 56;
+  curtime.wSecond = 13;
+  curtime.wMilliseconds = 22;
+  STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Valid time */
+  ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("tt HH':'mm'@'ss", "A"); /* Insufficent buffer */
+  ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, 2);
+  EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("tt HH':'mm'@'ss", "AM 08:56@13"); /* Calculate length only */
+  ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, NULL, 0);
+  EXPECT_VALID; EXPECT_LENA;
+
+  STRINGSA("", "8 AM"); /* TIME_NOMINUTESORSECONDS, default format */
+  ret = GetTimeFormatA(lcid, NUO|TIME_NOMINUTESORSECONDS, &curtime, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("m1s2m3s4", ""); /* TIME_NOMINUTESORSECONDS/complex format */
+  ret = GetTimeFormatA(lcid, TIME_NOMINUTESORSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("", "8:56 AM"); /* TIME_NOSECONDS/Default format */
+  ret = GetTimeFormatA(lcid, NUO|TIME_NOSECONDS, &curtime, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("h:m:s tt", "8:56 AM"); /* TIME_NOSECONDS */
+  strcpy(Expected, "8:56 AM");
+  ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("h.@:m.@:s.@:tt", "8.@:56AM"); /* Multiple delimiters */
+  ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("s1s2s3", ""); /* Duplicate tokens */
+  ret = GetTimeFormatA(lcid, TIME_NOSECONDS, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("t/tt", "A/AM"); /* AM time marker */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  curtime.wHour = 13;
+  STRINGSA("t/tt", "P/PM"); /* PM time marker */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("h1t2tt3m", "156"); /* TIME_NOTIMEMARKER: removes text around time marker token */
+  ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("h:m:s tt", "13:56:13 PM"); /* TIME_FORCE24HOURFORMAT */
+  ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("h:m:s", "13:56:13"); /* TIME_FORCE24HOURFORMAT doesn't add time marker */
+  ret = GetTimeFormatA(lcid, TIME_FORCE24HOURFORMAT, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  curtime.wHour = 14; /* change this to 14 or 2pm */
+  curtime.wMinute = 5;
+  curtime.wSecond = 3;
+  STRINGSA("h hh H HH m mm s ss t tt", "2 02 14 14 5 05 3 03 P PM"); /* 24 hrs, leading 0 */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  curtime.wHour = 0;
+  STRINGSA("h/H/hh/HH", "12/0/12/00"); /* "hh" and "HH" */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("h:m:s tt", "12:5:3 AM"); /* non-zero flags should fail with format, doesn't */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  /* try to convert formatting strings with more than two letters
+   * "h:hh:hhh:H:HH:HHH:m:mm:mmm:M:MM:MMM:s:ss:sss:S:SS:SSS"
+   * NOTE: We expect any letter for which there is an upper case value
+   *       we should see a replacement.  For letters that DO NOT have
+   *       upper case values we should see NO REPLACEMENT.
+   */
+  curtime.wHour = 8;
+  curtime.wMinute = 56;
+  curtime.wSecond = 13;
+  curtime.wMilliseconds = 22;
+  STRINGSA("h:hh:hhh H:HH:HHH m:mm:mmm M:MM:MMM s:ss:sss S:SS:SSS",
+           "8:08:08 8:08:08 56:56:56 M:MM:MMM 13:13:13 S:SS:SSS");
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("h", "text"); /* Don't write to buffer if len is 0 */
+  strcpy(buffer, "text");
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, 0);
+  EXPECT_VALID; EXPECT_LEN(2); EXPECT_EQA;
+
+  STRINGSA("h 'h' H 'H' HH 'HH' m 'm' s 's' t 't' tt 'tt'",
+           "8 h 8 H 08 HH 56 m 13 s A t AM tt"); /* "'" preserves tokens */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("'''", "'"); /* invalid quoted string */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  /* test that msdn suggested single quotation usage works as expected */
+  STRINGSA("''''", "'"); /* single quote mark */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("''HHHHHH", "08"); /* Normal use */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  /* and test for normal use of the single quotation mark */
+  STRINGSA("'''HHHHHH'", "'HHHHHH"); /* Normal use */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("'''HHHHHH", "'HHHHHH"); /* Odd use */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("'123'tt", ""); /* TIME_NOTIMEMARKER drops literals too */
+  ret = GetTimeFormatA(lcid, TIME_NOTIMEMARKER, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  curtime.wHour = 25;
+  STRINGSA("'123'tt", ""); /* Invalid time */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  curtime.wHour = 12;
+  curtime.wMonth = 60; /* Invalid */
+  STRINGSA("h:m:s", "12:56:13"); /* Invalid date */
+  ret = GetTimeFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+}
+
+static void test_GetDateFormatA()
+{
+  int ret;
+  SYSTEMTIME  curtime;
+  LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+  char buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
+
+  memset(&curtime, 2, sizeof(SYSTEMTIME)); /* Invalid time */
+  STRINGSA("ddd',' MMM dd yy","");
+  ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  curtime.wYear = 2002;
+  curtime.wMonth = 5;
+  curtime.wDay = 4;
+  curtime.wDayOfWeek = 3;
+  STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Simple case */
+  ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("ddd',' MMM dd yy","Sat, May 04 02"); /* Format containing "'" */
+  ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  curtime.wHour = 36; /* Invalid */
+  STRINGSA("ddd',' MMM dd ''''yy","Sat, May 04 '02"); /* Invalid time */
+  ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("ddd',' MMM dd ''''yy",""); /* Get size only */
+  ret = GetDateFormatA(lcid, 0, &curtime, input, NULL, 0);
+  EXPECT_VALID; EXPECT_LEN(16); EXPECT_EQA;
+
+  STRINGSA("ddd',' MMM dd ''''yy",""); /* Buffer too small */
+  ret = GetDateFormatA(lcid, 0, &curtime, input, buffer, 2);
+  EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("ddd',' MMM dd ''''yy","5/4/2002"); /* Default to DATE_SHORTDATE */
+  ret = GetDateFormat(lcid, NUO, &curtime, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("ddd',' MMM dd ''''yy", "Saturday, May 04, 2002"); /* DATE_LONGDATE */
+  ret = GetDateFormat(lcid, NUO|DATE_LONGDATE, &curtime, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  /* test for expected DATE_YEARMONTH behavior with null format */
+  /* NT4 returns ERROR_INVALID_FLAGS for DATE_YEARMONTH */
+  STRINGSA("ddd',' MMM dd ''''yy", ""); /* DATE_YEARMONTH */
+  ret = GetDateFormat(lcid, NUO|DATE_YEARMONTH, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_FLAGS; EXPECT_LEN(0); EXPECT_EQA;
+
+  /* Test that using invalid DATE_* flags results in the correct error */
+  /* and return values */
+  STRINGSA("m/d/y", ""); /* Invalid flags */
+  ret = GetDateFormat(lcid, DATE_YEARMONTH|DATE_SHORTDATE|DATE_LONGDATE,
+                      &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_FLAGS; EXPECT_LEN(0); EXPECT_EQA;
+}
+
+static void test_GetDateFormatW()
+{
+  int ret;
+  SYSTEMTIME  curtime;
+  WCHAR buffer[BUFFER_SIZE], input[BUFFER_SIZE], Expected[BUFFER_SIZE];
+  LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+  STRINGSW("",""); /* If flags is not zero then format must be NULL */
+  ret = GetDateFormatW(LOCALE_SYSTEM_DEFAULT, DATE_LONGDATE, NULL,
+                       input, buffer, COUNTOF(buffer));
+  if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+      return;
+  EXPECT_FLAGS; EXPECT_LEN(0); EXPECT_EQW;
+
+  STRINGSW("",""); /* NULL buffer, len > 0 */
+  ret = GetDateFormatW (lcid, 0, NULL, input, NULL, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQW;
+
+  STRINGSW("",""); /* NULL buffer, len == 0 */
+  ret = GetDateFormatW (lcid, 0, NULL, input, NULL, 0);
+  EXPECT_VALID; EXPECT_LENW; EXPECT_EQW;
+
+  curtime.wYear = 2002;
+  curtime.wMonth = 10;
+  curtime.wDay = 23;
+  curtime.wDayOfWeek = 45612; /* Should be 3 - Wednesday */
+  curtime.wHour = 65432; /* Invalid */
+  curtime.wMinute = 34512; /* Invalid */
+  curtime.wSecond = 65535; /* Invalid */
+  curtime.wMilliseconds = 12345;
+  STRINGSW("dddd d MMMM yyyy","Wednesday 23 October 2002"); /* Incorrect DOW and time */
+  ret = GetDateFormatW (lcid, 0, &curtime, input, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENW; EXPECT_EQW;
+}
+
+
+#define CY_POS_LEFT  0
+#define CY_POS_RIGHT 1
+#define CY_POS_LEFT_SPACE  2
+#define CY_POS_RIGHT_SPACE 3
+
+static void test_GetCurrencyFormatA()
+{
+  static char szDot[] = { '.', '\0' };
+  static char szComma[] = { ',', '\0' };
+  static char szDollar[] = { '$', '\0' };
+  int ret;
+  LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+  char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
+  CURRENCYFMTA format;
+
+  memset(&format, 0, sizeof(format));
+
+  STRINGSA("23",""); /* NULL output, length > 0 --> Error */
+  ret = GetCurrencyFormatA(lcid, 0, input, NULL, NULL, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("23,53",""); /* Invalid character --> Error */
+  ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("--",""); /* Double '-' --> Error */
+  ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("0-",""); /* Trailing '-' --> Error */
+  ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("0..",""); /* Double '.' --> Error */
+  ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA(" 0.1",""); /* Leading space --> Error */
+  ret = GetCurrencyFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("1234","$"); /* Length too small --> Write up to length chars */
+  ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, 2);
+  EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("2353",""); /* Format and flags given --> Error */
+  ret = GetCurrencyFormatA(lcid, NUO, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_INVALIDFLAGS; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("2353",""); /* Invalid format --> Error */
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("2353","$2,353.00"); /* Valid number */
+  ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("-2353","($2,353.00)"); /* Valid negative number */
+  ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("2353.1","$2,353.10"); /* Valid real number */
+  ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("2353.111","$2,353.11"); /* Too many DP --> Truncated */
+  ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("2353.119","$2,353.12");  /* Too many DP --> Rounded */
+  ret = GetCurrencyFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NumDigits = 0; /* No decimal separator */
+  format.LeadingZero = 0;
+  format.Grouping = 0;  /* No grouping char */
+  format.NegativeOrder = 0;
+  format.PositiveOrder = CY_POS_LEFT;
+  format.lpDecimalSep = szDot;
+  format.lpThousandSep = szComma;
+  format.lpCurrencySymbol = szDollar;
+
+  STRINGSA("2353","$2353"); /* No decimal or grouping chars expected */
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
+  STRINGSA("2353","$2353.0");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.Grouping = 2; /* Group by 100's */
+  STRINGSA("2353","$23,53.0");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.LeadingZero = 1; /* Always provide leading zero */
+  STRINGSA(".5","$0.5");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.PositiveOrder = CY_POS_RIGHT;
+  STRINGSA("1","1.0$");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.PositiveOrder = CY_POS_LEFT_SPACE;
+  STRINGSA("1","$ 1.0");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.PositiveOrder = CY_POS_RIGHT_SPACE;
+  STRINGSA("1","1.0 $");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 0;
+  STRINGSA("-1","($1.0)");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 1;
+  STRINGSA("-1","-$1.0");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 2;
+  STRINGSA("-1","$-1.0");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 3;
+  STRINGSA("-1","$1.0-");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 4;
+  STRINGSA("-1","(1.0$)");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 5;
+  STRINGSA("-1","-1.0$");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 6;
+  STRINGSA("-1","1.0-$");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 7;
+  STRINGSA("-1","1.0$-");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 8;
+  STRINGSA("-1","-1.0 $");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 9;
+  STRINGSA("-1","-$ 1.0");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 10;
+  STRINGSA("-1","1.0 $-");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 11;
+  STRINGSA("-1","$ 1.0-");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 12;
+  STRINGSA("-1","$ -1.0");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 13;
+  STRINGSA("-1","1.0- $");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 14;
+  STRINGSA("-1","($ 1.0)");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = 15;
+  STRINGSA("-1","(1.0 $)");
+  ret = GetCurrencyFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+}
+
+#define NEG_PARENS      0 /* "(1.1)" */
+#define NEG_LEFT        1 /* "-1.1"  */
+#define NEG_LEFT_SPACE  2 /* "- 1.1" */
+#define NEG_RIGHT       3 /* "1.1-"  */
+#define NEG_RIGHT_SPACE 4 /* "1.1 -" */
+
+static void test_GetNumberFormatA()
+{
+  static char szDot[] = { '.', '\0' };
+  static char szComma[] = { ',', '\0' };
+  int ret;
+  LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+  char buffer[BUFFER_SIZE], Expected[BUFFER_SIZE], input[BUFFER_SIZE];
+  NUMBERFMTA format;
+
+  memset(&format, 0, sizeof(format));
+
+  STRINGSA("23",""); /* NULL output, length > 0 --> Error */
+  ret = GetNumberFormatA(lcid, 0, input, NULL, NULL, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("23,53",""); /* Invalid character --> Error */
+  ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("--",""); /* Double '-' --> Error */
+  ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("0-",""); /* Trailing '-' --> Error */
+  ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("0..",""); /* Double '.' --> Error */
+  ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA(" 0.1",""); /* Leading space --> Error */
+  ret = GetNumberFormatA(lcid, 0, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("1234","1"); /* Length too small --> Write up to length chars */
+  ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, 2);
+  EXPECT_BUFFER; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("2353",""); /* Format and flags given --> Error */
+  ret = GetNumberFormatA(lcid, NUO, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_INVALIDFLAGS; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("2353",""); /* Invalid format --> Error */
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_INVALID; EXPECT_LEN(0); EXPECT_EQA;
+
+  STRINGSA("2353","2,353.00"); /* Valid number */
+  ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("-2353","-2,353.00"); /* Valid negative number */
+  ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("-353","-353.00"); /* test for off by one error in grouping */
+  ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("2353.1","2,353.10"); /* Valid real number */
+  ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("2353.111","2,353.11"); /* Too many DP --> Truncated */
+  ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  STRINGSA("2353.119","2,353.12");  /* Too many DP --> Rounded */
+  ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NumDigits = 0; /* No decimal separator */
+  format.LeadingZero = 0;
+  format.Grouping = 0;  /* No grouping char */
+  format.NegativeOrder = 0;
+  format.lpDecimalSep = szDot;
+  format.lpThousandSep = szComma;
+
+  STRINGSA("2353","2353"); /* No decimal or grouping chars expected */
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NumDigits = 1; /* 1 DP --> Expect decimal separator */
+  STRINGSA("2353","2353.0");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.Grouping = 2; /* Group by 100's */
+  STRINGSA("2353","23,53.0");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.LeadingZero = 1; /* Always provide leading zero */
+  STRINGSA(".5","0.5");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = NEG_PARENS;
+  STRINGSA("-1","(1.0)");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = NEG_LEFT;
+  STRINGSA("-1","-1.0");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = NEG_LEFT_SPACE;
+  STRINGSA("-1","- 1.0");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = NEG_RIGHT;
+  STRINGSA("-1","1.0-");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  format.NegativeOrder = NEG_RIGHT_SPACE;
+  STRINGSA("-1","1.0 -");
+  ret = GetNumberFormatA(lcid, 0, input, &format, buffer, COUNTOF(buffer));
+  EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+
+  lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
+
+  if (IsValidLocale(lcid, 0))
+  {
+    STRINGSA("-12345","-12 345,00"); /* Try French formatting */
+    Expected[3] = 160; /* Non breaking space */
+    ret = GetNumberFormatA(lcid, NUO, input, NULL, buffer, COUNTOF(buffer));
+    EXPECT_VALID; EXPECT_LENA; EXPECT_EQA;
+  }
+}
+
+
+/* Callback function used by TestEnumTimeFormats */
+static BOOL CALLBACK EnumTimeFormatsProc(char * lpTimeFormatString)
+{
+  trace("%s\n", lpTimeFormatString);
+  strcpy(GlobalBuffer, lpTimeFormatString);
+#if 0
+  return TRUE;
+#endif
+  return FALSE;
+}
+
+void test_EnumTimeFormats()
+{
+  int ret;
+  LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+  GlobalBuffer[0] = '\0';
+  ret = EnumTimeFormatsA(EnumTimeFormatsProc, lcid, 0);
+  ok (ret == 1 && !strcmp(GlobalBuffer,"h:mm:ss tt"), "Expected %d '%s'\n", ret, GlobalBuffer);
+}
+
+static void test_CompareStringA()
+{
+  int ret;
+  LCID lcid = MAKELCID(MAKELANGID(LANG_FRENCH, SUBLANG_DEFAULT), SORT_DEFAULT);
+
+  ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "Salute", -1);
+  ok (ret== 1, "(Salut/Salute) Expected 1, got %d\n", ret);
+
+  ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "SaLuT", -1);
+  ok (ret== 2, "(Salut/SaLuT) Expected 2, got %d\n", ret);
+
+  ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", -1, "hola", -1);
+  ok (ret== 3, "(Salut/hola) Expected 3, got %d\n", ret);
+
+  ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
+  ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+
+  lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
+
+  ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", -1);
+  ok (ret== 1, "(haha/hoho) Expected 1, got %d\n", ret);
+
+  ret = CompareStringA(lcid, NORM_IGNORECASE, "haha", -1, "hoho", 0);
+  ok (ret== 3, "(haha/hoho) Expected 3, got %d\n", ret);
+
+    ret = CompareStringA(lcid, NORM_IGNORECASE, "Salut", 5, "saLuT", -1);
+    ok (ret == 2, "(Salut/saLuT) Expected 2, got %d\n", ret);
+
+    SetLastError(0xdeadbeef);
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0x10, "NULL", -1, "NULL", -1);
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+        "unexpected error code %ld\n", GetLastError());
+    ok(!ret, "CompareStringA must fail with invalid flag\n");
+
+    ret = lstrcmpA("", "");
+    ok (!ret, "lstrcmpA(\"\", \"\") should return 0, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"EndDialog",-1,"_Property",-1);
+    ok( ret == 3, "EndDialog vs _Property ... expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"osp_vba.sreg0070",-1,"_IEWWBrowserComp",-1);
+    ok( ret == 3, "osp_vba.sreg0070 vs _IEWWBrowserComp ... expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"r",-1,"\\",-1); 
+    ok( ret == 3, "r vs \\ ... expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT,0,"osp_vba.sreg0031", -1, "OriginalDatabase", -1 );
+    ok( ret == 3, "osp_vba.sreg0031 vs OriginalDatabase ... expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aaa", -1 );
+    ok( ret == 3, "AAA vs aaa expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "aab", -1 );
+    ok( ret == 1, "AAA vs aab expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "AAA", -1, "Aab", -1 );
+    ok( ret == 1, "AAA vs Aab expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "Aab", -1 );
+    ok( ret == 1, ".AAA vs Aab expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, ".AAA", -1, "A.ab", -1 );
+    ok( ret == 1, ".AAA vs A.ab expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "AB", -1 );
+    ok( ret == 1, "aa vs AB expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aa", -1, "Aab", -1 );
+    ok( ret == 1, "aa vs Aab expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "aB", -1, "Aab", -1 );
+    ok( ret == 3, "aB vs Aab expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "Ba", -1, "bab", -1 );
+    ok( ret == 1, "Ba vs bab expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "{100}{83}{71}{71}{71}", -1, "Global_DataAccess_JRO", -1 );
+    ok( ret == 1, "{100}{83}{71}{71}{71} vs Global_DataAccess_JRO expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "a", -1, "{", -1 );
+    ok( ret == 3, "a vs { expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "A", -1, "{", -1 );
+    ok( ret == 3, "A vs { expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.5", 0, "4.0", -1 );
+    ok(ret == 1, "3.5/0 vs 4.0/-1 expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.5", -1, "4.0", -1 );
+    ok(ret == 1, "3.5 vs 4.0 expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "3.520.4403.2", -1, "4.0.2927.10", -1 );
+    ok(ret == 1, "3.520.4403.2 vs 4.0.2927.10 expected 1, got %d\n", ret);
+
+   /* hyphen and apostrophe are treated differently depending on
+    * whether SORT_STRINGSORT specified or not
+    */
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-o", -1, "/m", -1 );
+    ok(ret == 3, "-o vs /m expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "-o", -1 );
+    ok(ret == 1, "/m vs -o expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-o", -1, "/m", -1 );
+    ok(ret == 1, "-o vs /m expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "-o", -1 );
+    ok(ret == 3, "/m vs -o expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "/m", -1 );
+    ok(ret == 3, "'o vs /m expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "'o", -1 );
+    ok(ret == 1, "/m vs 'o expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "/m", -1 );
+    ok(ret == 1, "'o vs /m expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "'o", -1 );
+    ok(ret == 3, "/m vs 'o expected 3, got %d\n", ret);
+
+#if 0 /* this requires collation table patch to make it MS compatible */
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'o", -1, "-o", -1 );
+    ok(ret == 1, "'o vs -o expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'o", -1, "-o", -1 );
+    ok(ret == 1, "'o vs -o expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "'", -1, "-", -1 );
+    ok(ret == 1, "' vs - expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "'", -1, "-", -1 );
+    ok(ret == 1, "' vs - expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "/m", -1 );
+    ok(ret == 3, "`o vs /m expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "/m", -1, "`o", -1 );
+    ok(ret == 1, "/m vs `o expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "/m", -1 );
+    ok(ret == 3, "`o vs /m expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "/m", -1, "`o", -1 );
+    ok(ret == 1, "/m vs `o expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "`o", -1, "-m", -1 );
+    ok(ret == 1, "`o vs -m expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, 0, "-m", -1, "`o", -1 );
+    ok(ret == 3, "-m vs `o expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "`o", -1, "-m", -1 );
+    ok(ret == 3, "`o vs -m expected 3, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_SYSTEM_DEFAULT, SORT_STRINGSORT, "-m", -1, "`o", -1 );
+    ok(ret == 1, "-m vs `o expected 1, got %d\n", ret);
+#endif
+
+    ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ", 9);
+    ok(ret == 2, "aLuZkUtZ vs aLuZkUtZ\\0 expected 2, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 7, "aLuZkUtZ\0A", 10);
+    ok(ret == 1, "aLuZkUtZ vs aLuZkUtZ\\0A expected 1, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLuZkUtZ", 8, "aLuZkUtZ\0A", 10);
+    ok(ret == 2, "aLuZkUtZ vs aLuZkUtZ\\0A expected 2, got %d\n", ret);
+
+    ret = CompareStringA(LOCALE_USER_DEFAULT, 0, "aLu\0ZkUtZ", 8, "aLu\0ZkUtZ\0A", 10);
+    ok(ret == 2, "aLu\\0ZkUtZ vs aLu\\0ZkUtZ\\0A expected 2, got %d\n", ret);
+}
+
+void test_LCMapStringA(void)
+{
+    int ret, ret2;
+    char buf[256], buf2[256];
+    static const char upper_case[] = "\tJUST! A, TEST; STRING 1/*+-.\r\n";
+    static const char lower_case[] = "\tjust! a, test; string 1/*+-.\r\n";
+    static const char symbols_stripped[] = "justateststring1";
+
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
+
+                       upper_case, -1, buf, sizeof(buf));
+    ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
+    ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
+
+    /* test LCMAP_LOWERCASE */
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(ret == lstrlenA(upper_case) + 1,
+       "ret %d, error %ld, expected value %d\n",
+       ret, GetLastError(), lstrlenA(upper_case) + 1);
+    ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+
+    /* test LCMAP_UPPERCASE */
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+                       lower_case, -1, buf, sizeof(buf));
+    ok(ret == lstrlenA(lower_case) + 1,
+       "ret %d, error %ld, expected value %d\n",
+       ret, GetLastError(), lstrlenA(lower_case) + 1);
+    ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
+
+    /* test buffer overflow */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+                       lower_case, -1, buf, 4);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
+
+    /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
+    lstrcpyA(buf, lower_case);
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+                       buf, -1, buf, sizeof(buf));
+    if (!ret) /* Win9x */
+        trace("Ignoring LCMapStringA(LCMAP_UPPERCASE, buf, buf) error on Win9x\n");
+    else
+    {
+        ok(ret == lstrlenA(lower_case) + 1,
+           "ret %d, error %ld, expected value %d\n",
+           ret, GetLastError(), lstrlenA(lower_case) + 1);
+        ok(!lstrcmpA(buf, upper_case), "LCMapStringA should return %s, but not %s\n", upper_case, buf);
+    }
+    lstrcpyA(buf, upper_case);
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+                       buf, -1, buf, sizeof(buf));
+    if (!ret) /* Win9x */
+        trace("Ignoring LCMapStringA(LCMAP_LOWERCASE, buf, buf) error on Win9x\n");
+    else
+    {
+        ok(ret == lstrlenA(upper_case) + 1,
+           "ret %d, error %ld, expected value %d\n",
+           ret, GetLastError(), lstrlenA(lower_case) + 1);
+        ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+    }
+
+    /* otherwise src == dst should fail */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
+                       buf, 10, buf, sizeof(buf));
+    ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
+       GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
+       "unexpected error code %ld\n", GetLastError());
+    ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
+
+    /* test whether '\0' is always appended */
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringA must succeed\n");
+    ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       upper_case, lstrlenA(upper_case), buf2, sizeof(buf2));
+    ok(ret, "LCMapStringA must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+    /* test LCMAP_SORTKEY | NORM_IGNORECASE */
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringA must succeed\n");
+    ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       lower_case, -1, buf2, sizeof(buf2));
+    ok(ret2, "LCMapStringA must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+    /* test LCMAP_SORTKEY | NORM_IGNORENONSPACE */
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORENONSPACE,
+                       lower_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringA must succeed\n");
+    ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       lower_case, -1, buf2, sizeof(buf2));
+    ok(ret2, "LCMapStringA must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+    /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
+                       lower_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringA must succeed\n");
+    ret2 = LCMapStringA(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       symbols_stripped, -1, buf2, sizeof(buf2));
+    ok(ret2, "LCMapStringA must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(buf, buf2), "sort keys must be equal\n");
+
+    /* test NORM_IGNORENONSPACE */
+    lstrcpyA(buf, "foo");
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
+                       lower_case, -1, buf, sizeof(buf));
+    ok(ret == lstrlenA(lower_case) + 1, "LCMapStringA should return %d, ret = %d\n",
+       lstrlenA(lower_case) + 1, ret);
+    ok(!lstrcmpA(buf, lower_case), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+
+    /* test NORM_IGNORESYMBOLS */
+    lstrcpyA(buf, "foo");
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
+                       lower_case, -1, buf, sizeof(buf));
+    ok(ret == lstrlenA(symbols_stripped) + 1, "LCMapStringA should return %d, ret = %d\n",
+       lstrlenA(symbols_stripped) + 1, ret);
+    ok(!lstrcmpA(buf, symbols_stripped), "LCMapStringA should return %s, but not %s\n", lower_case, buf);
+
+    /* test srclen = 0 */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringA(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
+    ok(!ret, "LCMapStringA should fail with srclen = 0\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "unexpected error code %ld\n", GetLastError());
+}
+
+void test_LCMapStringW(void)
+{
+    int ret, ret2;
+    WCHAR buf[256], buf2[256];
+    char *p_buf = (char *)buf, *p_buf2 = (char *)buf2;
+    static const WCHAR upper_case[] = {'\t','J','U','S','T','!',' ','A',',',' ','T','E','S','T',';',' ','S','T','R','I','N','G',' ','1','/','*','+','-','.','\r','\n',0};
+    static const WCHAR lower_case[] = {'\t','j','u','s','t','!',' ','a',',',' ','t','e','s','t',';',' ','s','t','r','i','n','g',' ','1','/','*','+','-','.','\r','\n',0};
+    static const WCHAR symbols_stripped[] = {'j','u','s','t','a','t','e','s','t','s','t','r','i','n','g','1',0};
+    static const WCHAR fooW[] = {'f','o','o',0};
+
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | LCMAP_UPPERCASE,
+                       upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+    {
+        trace("Skipping LCMapStringW tests on Win9x\n");
+        return;
+    }
+    ok(!ret, "LCMAP_LOWERCASE and LCMAP_UPPERCASE are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HIRAGANA | LCMAP_KATAKANA,
+                       upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(!ret, "LCMAP_HIRAGANA and LCMAP_KATAKANA are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_HALFWIDTH | LCMAP_FULLWIDTH,
+                       upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(!ret, "LCMAP_HALFWIDTH | LCMAP_FULLWIDTH are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_TRADITIONAL_CHINESE | LCMAP_SIMPLIFIED_CHINESE,
+                       upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(!ret, "LCMAP_TRADITIONAL_CHINESE and LCMAP_SIMPLIFIED_CHINESE are mutually exclusive\n");
+    ok(GetLastError() == ERROR_INVALID_FLAGS,
+       "unexpected error code %ld\n", GetLastError());
+
+    /* SORT_STRINGSORT must be used exclusively with LCMAP_SORTKEY */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE | SORT_STRINGSORT,
+                       upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(GetLastError() == ERROR_INVALID_FLAGS, "expected ERROR_INVALID_FLAGS, got %ld\n", GetLastError());
+    ok(!ret, "SORT_STRINGSORT without LCMAP_SORTKEY must fail\n");
+
+    /* test LCMAP_LOWERCASE */
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+                       upper_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(ret == lstrlenW(upper_case) + 1,
+       "ret %d, error %ld, expected value %d\n",
+       ret, GetLastError(), lstrlenW(upper_case) + 1);
+    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+    /* test LCMAP_UPPERCASE */
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+                       lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(ret == lstrlenW(lower_case) + 1,
+       "ret %d, error %ld, expected value %d\n",
+       ret, GetLastError(), lstrlenW(lower_case) + 1);
+    ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+
+    /* test buffer overflow */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+                       lower_case, -1, buf, 4);
+    ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER,
+       "should return 0 and ERROR_INSUFFICIENT_BUFFER, got %d\n", ret);
+
+    /* LCMAP_UPPERCASE or LCMAP_LOWERCASE should accept src == dst */
+    lstrcpyW(buf, lower_case);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_UPPERCASE,
+                       buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(ret == lstrlenW(lower_case) + 1,
+       "ret %d, error %ld, expected value %d\n",
+       ret, GetLastError(), lstrlenW(lower_case) + 1);
+    ok(!lstrcmpW(buf, upper_case), "string compare mismatch\n");
+
+    lstrcpyW(buf, upper_case);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE,
+                       buf, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(ret == lstrlenW(upper_case) + 1,
+       "ret %d, error %ld, expected value %d\n",
+       ret, GetLastError(), lstrlenW(lower_case) + 1);
+    ok(!lstrcmpW(buf, lower_case), "string compare mismatch\n");
+
+    /* otherwise src == dst should fail */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | LCMAP_UPPERCASE,
+                       buf, 10, buf, sizeof(buf));
+    ok(GetLastError() == ERROR_INVALID_FLAGS /* NT */ ||
+       GetLastError() == ERROR_INVALID_PARAMETER /* Win9x */,
+       "unexpected error code %ld\n", GetLastError());
+    ok(!ret, "src == dst without LCMAP_UPPERCASE or LCMAP_LOWERCASE must fail\n");
+
+    /* test whether '\0' is always appended */
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringW must succeed\n");
+    ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       upper_case, lstrlenW(upper_case), buf2, sizeof(buf2));
+    ok(ret, "LCMapStringW must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+    /* test LCMAP_SORTKEY | NORM_IGNORECASE */
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORECASE,
+                       upper_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringW must succeed\n");
+    ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       lower_case, -1, buf2, sizeof(buf2));
+    ok(ret2, "LCMapStringW must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+    /* test LCMAP_SORTKEY | NORM_IGNORENONSPACE */
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORENONSPACE,
+                       lower_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringW must succeed\n");
+    ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       lower_case, -1, buf2, sizeof(buf2));
+    ok(ret2, "LCMapStringW must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+    /* test LCMAP_SORTKEY | NORM_IGNORESYMBOLS */
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY | NORM_IGNORESYMBOLS,
+                       lower_case, -1, buf, sizeof(buf));
+    ok(ret, "LCMapStringW must succeed\n");
+    ret2 = LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_SORTKEY,
+                       symbols_stripped, -1, buf2, sizeof(buf2));
+    ok(ret2, "LCMapStringW must succeed\n");
+    ok(ret == ret2, "lengths of sort keys must be equal\n");
+    ok(!lstrcmpA(p_buf, p_buf2), "sort keys must be equal\n");
+
+    /* test NORM_IGNORENONSPACE */
+    lstrcpyW(buf, fooW);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORENONSPACE,
+                       lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(ret == lstrlenW(lower_case) + 1, "LCMapStringW should return %d, ret = %d\n",
+       lstrlenW(lower_case) + 1, ret);
+    ok(!lstrcmpW(buf, lower_case), "string comparison mismatch\n");
+
+    /* test NORM_IGNORESYMBOLS */
+    lstrcpyW(buf, fooW);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, NORM_IGNORESYMBOLS,
+                       lower_case, -1, buf, sizeof(buf)/sizeof(WCHAR));
+    ok(ret == lstrlenW(symbols_stripped) + 1, "LCMapStringW should return %d, ret = %d\n",
+       lstrlenW(symbols_stripped) + 1, ret);
+    ok(!lstrcmpW(buf, symbols_stripped), "string comparison mismatch\n");
+
+    /* test srclen = 0 */
+    SetLastError(0xdeadbeef);
+    ret = LCMapStringW(LOCALE_USER_DEFAULT, 0, upper_case, 0, buf, sizeof(buf));
+    ok(!ret, "LCMapStringW should fail with srclen = 0\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+       "unexpected error code %ld\n", GetLastError());
+}
+
+#if 0 /* this requires collation table patch to make it MS compatible */
+const char *strings_sorted[] =
+{
+"'",
+"-",
+"!",
+"\"",
+".",
+":",
+"\\",
+"_",
+"`",
+"{",
+"}",
+"+",
+"0",
+"1",
+"2",
+"3",
+"4",
+"5",
+"6",
+"7",
+"8",
+"9",
+"a",
+"A",
+"b",
+"B",
+"c",
+"C"
+};
+
+const char *strings[] =
+{
+"C",
+"\"",
+"9",
+"'",
+"}",
+"-",
+"7",
+"+",
+"`",
+"1",
+"a",
+"5",
+"\\",
+"8",
+"B",
+"3",
+"_",
+"6",
+"{",
+"2",
+"c",
+"4",
+"!",
+"0",
+"A",
+":",
+"b",
+"."
+};
+
+static int compare_string1(const void *e1, const void *e2)
+{
+    const char *s1 = *(const char **)e1;
+    const char *s2 = *(const char **)e2;
+
+    return lstrcmpA(s1, s2);
+}
+
+static int compare_string2(const void *e1, const void *e2)
+{
+    const char *s1 = *(const char **)e1;
+    const char *s2 = *(const char **)e2;
+
+    return CompareStringA(0, 0, s1, -1, s2, -1) - 2;
+}
+
+static int compare_string3(const void *e1, const void *e2)
+{
+    const char *s1 = *(const char **)e1;
+    const char *s2 = *(const char **)e2;
+    char key1[256], key2[256];
+
+    LCMapStringA(0, LCMAP_SORTKEY, s1, -1, key1, sizeof(key1));
+    LCMapStringA(0, LCMAP_SORTKEY, s2, -1, key2, sizeof(key2));
+    return strcmp(key1, key2);
+}
+
+static void test_sorting(void)
+{
+    char buf[256];
+    char **str_buf = (char **)buf;
+    int i;
+
+    assert(sizeof(buf) >= sizeof(strings));
+
+    /* 1. sort using lstrcmpA */
+    memcpy(buf, strings, sizeof(strings));
+    qsort(buf, sizeof(strings)/sizeof(strings[0]), sizeof(strings[0]), compare_string1);
+    for (i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
+    {
+        ok(!strcmp(strings_sorted[i], str_buf[i]),
+           "qsort using lstrcmpA failed for element %d\n", i);
+    }
+    /* 2. sort using CompareStringA */
+    memcpy(buf, strings, sizeof(strings));
+    qsort(buf, sizeof(strings)/sizeof(strings[0]), sizeof(strings[0]), compare_string2);
+    for (i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
+    {
+        ok(!strcmp(strings_sorted[i], str_buf[i]),
+           "qsort using CompareStringA failed for element %d\n", i);
+    }
+    /* 3. sort using sort keys */
+    memcpy(buf, strings, sizeof(strings));
+    qsort(buf, sizeof(strings)/sizeof(strings[0]), sizeof(strings[0]), compare_string3);
+    for (i = 0; i < sizeof(strings)/sizeof(strings[0]); i++)
+    {
+        ok(!strcmp(strings_sorted[i], str_buf[i]),
+           "qsort using sort keys failed for element %d\n", i);
+    }
+}
+#endif
+
+static void test_FoldStringA(void)
+{
+  int ret, i;
+  char src[256], dst[256];
+  static const char digits_src[] = { 0xB9,0xB2,0xB3,'\0'  };
+  static const char digits_dst[] = { '1','2','3','\0'  };
+  static const char composite_src[] =
+  {
+    0x8a,0x8e,0x9a,0x9e,0x9f,0xc0,0xc1,0xc2,
+    0xc3,0xc4,0xc5,0xc7,0xc8,0xc9,0xca,0xcb,
+    0xcc,0xcd,0xce,0xcf,0xd1,0xd2,0xd3,0xd4,
+    0xd5,0xd6,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,
+    0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe7,0xe8,
+    0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf1,
+    0xf2,0xf3,0xf4,0xf5,0xf6,0xf8,0xf9,0xfa,
+    0xfb,0xfc,0xfd,0xff,'\0'
+  };
+  static const char composite_dst[] =
+  {
+    0x53,0x3f,0x5a,0x3f,0x73,0x3f,0x7a,0x3f,
+    0x59,0xa8,0x41,0x60,0x41,0xb4,0x41,0x5e,
+    0x41,0x7e,0x41,0xa8,0x41,0xb0,0x43,0xb8,
+    0x45,0x60,0x45,0xb4,0x45,0x5e,0x45,0xa8,
+    0x49,0x60,0x49,0xb4,0x49,0x5e,0x49,0xa8,
+    0x4e,0x7e,0x4f,0x60,0x4f,0xb4,0x4f,0x5e,
+    0x4f,0x7e,0x4f,0xa8,0x4f,0x3f,0x55,0x60,
+    0x55,0xb4,0x55,0x5e,0x55,0xa8,0x59,0xb4,
+    0x61,0x60,0x61,0xb4,0x61,0x5e,0x61,0x7e,
+    0x61,0xa8,0x61,0xb0,0x63,0xb8,0x65,0x60,
+    0x65,0xb4,0x65,0x5e,0x65,0xa8,0x69,0x60,
+    0x69,0xb4,0x69,0x5e,0x69,0xa8,0x6e,0x7e,
+    0x6f,0x60,0x6f,0xb4,0x6f,0x5e,0x6f,0x7e,
+    0x6f,0xa8,0x6f,0x3f,0x75,0x60,0x75,0xb4,
+    0x75,0x5e,0x75,0xa8,0x79,0xb4,0x79,0xa8,'\0'
+  };
+  static const char ligatures_src[] =
+  {
+    0x8C,0x9C,0xC6,0xDE,0xDF,0xE6,0xFE,'\0'
+  };
+  static const char ligatures_dst[] =
+  {
+    'O','E','o','e','A','E','T','H','s','s','a','e','t','h','\0'
+  };
+
+  if (!pFoldStringA)
+    return; /* FoldString is present in NT v3.1+, but not 95/98/Me */
+
+  /* these tests are locale specific */
+  if (GetACP() != 1252)
+  {
+      trace("Skipping FoldStringA tests for a not Latin 1 locale\n");
+      return;
+  }
+
+  /* MAP_FOLDDIGITS */
+  SetLastError(0);
+  ret = pFoldStringA(MAP_FOLDDIGITS, digits_src, -1, dst, 256);
+  if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+    return;
+  EXPECT_LEN(4); EXPECT_VALID;
+  ok(strcmp(dst, digits_dst) == 0,
+     "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", digits_dst, dst);
+  for (i = 1; i < 256; i++)
+  {
+    if (!strchr(digits_src, i))
+    {
+      src[0] = i;
+      src[1] = '\0';
+      SetLastError(0);
+      ret = pFoldStringA(MAP_FOLDDIGITS, src, -1, dst, 256);
+      EXPECT_LEN(2); EXPECT_VALID;
+      ok(dst[0] == src[0],
+         "MAP_FOLDDIGITS: Expected '%s', got '%s'\n", src, dst);
+    }
+  }
+
+  /* MAP_EXPAND_LIGATURES */
+  SetLastError(0);
+  ret = pFoldStringA(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
+  EXPECT_LEN(sizeof(ligatures_dst)); EXPECT_VALID;
+  ok(strcmp(dst, ligatures_dst) == 0,
+     "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", ligatures_dst, dst);
+  for (i = 1; i < 256; i++)
+  {
+    if (!strchr(ligatures_src, i))
+    {
+      src[0] = i;
+      src[1] = '\0';
+      SetLastError(0);
+      ret = pFoldStringA(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
+      EXPECT_LEN(2); EXPECT_VALID;
+      ok(dst[0] == src[0],
+         "MAP_EXPAND_LIGATURES: Expected '%s', got '%s'\n", src, dst);
+    }
+  }
+
+  /* MAP_COMPOSITE */
+  SetLastError(0);
+  ret = pFoldStringA(MAP_COMPOSITE, composite_src, -1, dst, 256);
+  EXPECT_VALID;
+  todo_wine
+  {
+    /* Wine gets close, but doesn't produce quite the same result as native */
+    EXPECT_LEN(121);
+    ok(strcmp(dst, composite_dst) == 0,
+       "MAP_COMPOSITE: Expected '%s', got '%s'\n", composite_dst, dst);
+  }
+
+  for (i = 1; i < 256; i++)
+  {
+    if (!strchr(composite_src, i))
+    {
+      src[0] = i;
+      src[1] = '\0';
+      SetLastError(0);
+      ret = pFoldStringA(MAP_COMPOSITE, src, -1, dst, 256);
+      EXPECT_LEN(2); EXPECT_VALID;
+      ok(dst[0] == src[0],
+         "0x%02x, 0x%02x,0x%02x,0x%02x,\n", (unsigned char)src[0],
+         (unsigned char)dst[0],(unsigned char)dst[1],(unsigned char)dst[2]);
+    }
+  }
+
+  /* MAP_FOLDCZONE */
+  for (i = 1; i < 256; i++)
+  {
+    src[0] = i;
+    src[1] = '\0';
+    SetLastError(0);
+    ret = pFoldStringA(MAP_FOLDCZONE, src, -1, dst, 256);
+    EXPECT_LEN(2); EXPECT_VALID;
+    ok(src[0] == dst[0],
+       "MAP_FOLDCZONE: Expected 0x%02x, got 0x%02x\n",
+       (unsigned char)src[0], (unsigned char)dst[0]);
+  }
+
+  /* MAP_PRECOMPOSED */
+  for (i = 1; i < 256; i++)
+  {
+    src[0] = i;
+    src[1] = '\0';
+    SetLastError(0);
+    ret = pFoldStringA(MAP_PRECOMPOSED, src, -1, dst, 256);
+    EXPECT_LEN(2); EXPECT_VALID;
+    ok(src[0] == dst[0],
+       "MAP_PRECOMPOSED: Expected 0x%02x, got 0x%02x\n",
+       (unsigned char)src[0], (unsigned char)dst[0]);
+  }
+}
+
+static void test_FoldStringW(void)
+{
+  int ret;
+  size_t i, j;
+  WCHAR src[256], dst[256], ch, prev_ch = 1;
+  static const DWORD badFlags[] =
+  {
+    0,
+    MAP_PRECOMPOSED|MAP_COMPOSITE,
+    MAP_PRECOMPOSED|MAP_EXPAND_LIGATURES,
+    MAP_COMPOSITE|MAP_EXPAND_LIGATURES
+  };
+  /* Ranges of digits 0-9 : Must be sorted! */
+  static const WCHAR digitRanges[] =
+  {
+    0x0030, /* '0'-'9' */
+    0x0660, /* Eastern Arabic */
+    0x06F0, /* Arabic - Hindu */
+    0x0966, /* Devengari */
+    0x09E6, /* Bengalii */
+    0x0A66, /* Gurmukhi */
+    0x0AE6, /* Gujarati */
+    0x0B66, /* Oriya */
+    0x0BE6, /* Tamil - No 0 */
+    0x0C66, /* Telugu */
+    0x0CE6, /* Kannada */
+    0x0D66, /* Maylayalam */
+    0x0E50, /* Thai */
+    0x0ED0, /* Laos */
+    0x2070, /* Superscript - 1, 2, 3 are out of sequence */
+    0x2080, /* Subscript */
+    0x245F, /* Circled - 0 is out of sequence */
+    0x2473, /* Bracketed */
+    0x2487, /* Full stop */
+    0x2775, /* Inverted circled - No 0 */
+    0x277F, /* Patterned circled - No 0 */
+    0x2789, /* Inverted Patterned circled - No 0 */
+    0xff10, /* Pliene chasse (?) */
+    0xffff  /* Terminator */
+  };
+  /* Digits which are represented, but out of sequence */
+  static const WCHAR outOfSequenceDigits[] =
+  {
+      0xB9,   /* Superscript 1 */
+      0xB2,   /* Superscript 2 */
+      0xB3,   /* Superscript 3 */
+      0x24EA, /* Circled 0 */
+      '\0'    /* Terminator */
+  };
+  /* Digits in digitRanges for which no representation is available */
+  static const WCHAR noDigitAvailable[] =
+  {
+      0x0BE6, /* No Tamil 0 */
+      0x2473, /* No Bracketed 0 */
+      0x2487, /* No 0 Full stop */
+      0x2775, /* No inverted circled 0 */
+      0x277F, /* No patterned circled */
+      0x2789, /* No inverted Patterned circled */
+      '\0'    /* Terminator */
+  };
+  /* Compatibility conversion results */
+  static const WCHAR compat_F900_FA2F[256+48] =
+  {
+      0x8c48, 0x66f4, 0x8eca, 0x8cc8, 0x6ed1, 0x4e32, 0x53e5, 0x9f9c,
+      0x9f9c, 0x5951, 0x91d1, 0x5587, 0x5948, 0x61f6, 0x7669, 0x7f85,
+      0x863f, 0x87ba, 0x88f8, 0x908f, 0x6a02, 0x6d1b, 0x70d9, 0x73de,
+      0x843d, 0x916a, 0x99f1, 0x4e82, 0x5375, 0x6b04, 0x721b, 0x862d,
+      0x9e1e, 0x5d50, 0x6feb, 0x85cd, 0x8964, 0x62c9, 0x81d8, 0x881f,
+      0x5eca, 0x6717, 0x6d6a, 0x72fc, 0x0000, 0x4f86, 0x51b7, 0x52de,
+      0x64c4, 0x6ad3, 0x7210, 0x76e7, 0x8001, 0x8606, 0x865c, 0x8def,
+      0x9732, 0x9b6f, 0x9dfa, 0x788c, 0x797f, 0x7da0, 0x83c9, 0x9304,
+      0x9e7f, 0x8ad6, 0x58df, 0x5f04, 0x7c60, 0x807e, 0x7262, 0x78ca,
+      0x8cc2, 0x96f7, 0x58d8, 0x5c62, 0x6a13, 0x6dda, 0x6f0f, 0x7d2f,
+      0x7e37, 0x964b, 0x52d2, 0x808b, 0x51dc, 0x51cc, 0x7a1c, 0x7dbe,
+      0x83f1, 0x9675, 0x8b80, 0x62cf, 0x6a02, 0x8afe, 0x4e39, 0x5be7,
+      0x6012, 0x7387, 0x7570, 0x5317, 0x78fb, 0x4fbf, 0x5fa9, 0x4e0d,
+      0x6ccc, 0x6578, 0x7d22, 0x53c3, 0x585e, 0x7701, 0x8449, 0x8aaa,
+      0x6bba, 0x8fb0, 0x6c88, 0x62fe, 0x82e5, 0x63a0, 0x7565, 0x4eae,
+      0x5169, 0x0000, 0x6881, 0x7ce7, 0x826f, 0x8ad2, 0x91cf, 0x52f5,
+      0x5442, 0x5973, 0x5eec, 0x65c5, 0x6ffe, 0x792a, 0x95ad, 0x9a6a,
+      0x9e97, 0x9ece, 0x529b, 0x66c6, 0x6b77, 0x8f62, 0x5e74, 0x6190,
+      0x6200, 0x649a, 0x6f23, 0x7149, 0x7489, 0x0000, 0x7df4, 0x806f,
+      0x8f26, 0x84ee, 0x9023, 0x934a, 0x5217, 0x52a3, 0x54bd, 0x70c8,
+      0x88c2, 0x8aaa, 0x5ec9, 0x5ff5, 0x637b, 0x6bae, 0x7c3e, 0x7375,
+      0x4ee4, 0x56f9, 0x5be7, 0x5dba, 0x601c, 0x73b2, 0x7469, 0x7f9a,
+      0x8046, 0x9234, 0x96f6, 0x9748, 0x9818, 0x4f8b, 0x79ae, 0x91b4,
+      0x96b8, 0x60e1, 0x4e86, 0x50da, 0x5bee, 0x5c3f, 0x6599, 0x6a02,
+      0x71ce, 0x7642, 0x84fc, 0x907c, 0x9f8d, 0x6688, 0x962e, 0x5289,
+      0x677b, 0x67f3, 0x6d41, 0x6e9c, 0x7409, 0x7559, 0x786b, 0x7d10,
+      0x985e, 0x516d, 0x622e, 0x9678, 0x502b, 0x5d19, 0x6dea, 0x8f2a,
+      0x5f8b, 0x6144, 0x6817, 0x7387, 0x9686, 0x5229, 0x540f, 0x5c65,
+      0x6613, 0x674e, 0x68a8, 0x6ce5, 0x7406, 0x75e2, 0x7f79, 0x0000,
+      0x88e1, 0x91cc, 0x96e2, 0x533f, 0x6eba, 0x541d, 0x71d0, 0x7498,
+      0x85fa, 0x0000, 0x9c57, 0x9e9f, 0x6797, 0x6dcb, 0x81e8, 0x7acb,
+      0x7b20, 0x7c92, 0x72c0, 0x7099, 0x8b58, 0x4ec0, 0x8336, 0x523a,
+      0x5207, 0x5ea6, 0x62d3, 0x7cd6, 0x5b85, 0x6d1e, 0x66b4, 0x8f3b,
+      0x884c, 0x964d, 0x898b, 0x5ed3, 0x0000, 0x0000, 0x0000, 0x0000,
+      0x585a, 0x0000, 0x6674, 0x0000, 0x0000, 0x51de, 0x8c6c, 0x76ca,
+      0x0000, 0x795e, 0x7965, 0x798f, 0x9756, 0x7cbe, 0x7fbd, 0x0000,
+      0x0000, 0x0000, 0x8af8, 0x0000, 0x0000, 0x9038, 0x90fd, 0x0000,
+      0x0000, 0x0000, 0x98ef, 0x98fc, 0x9928, 0x9db4, 0x0000, 0x0000
+  };
+  static const WCHAR compat_FE30_FEF7[200] =
+  {
+      0x2025, 0x2014, 0x2013, 0x005f, 0x005f, 0x0028, 0x0029, 0x007b,
+      0x007d, 0x3014, 0x3015, 0x3010, 0x3011, 0x300a, 0x300b, 0x3008,
+      0x3009, 0x300c, 0x300d, 0x300e, 0x300f, 0x0000, 0x0000, 0x0000,
+      0x0000, 0x203e, 0x203e, 0x203e, 0x203e, 0x005f, 0x005f, 0x005f,
+      0x002c, 0x3001, 0x002e, 0x0000, 0x003b, 0x003a, 0x003f, 0x0021,
+      0x2014, 0x0028, 0x0029, 0x007b, 0x007d, 0x3014, 0x3015, 0x0023,
+      0x0026, 0x002a, 0x002b, 0x002d, 0x003c, 0x003e, 0x003d, 0x0000,
+      0x0000, 0x0024, 0x0025, 0x0040, 0x0000, 0x0000, 0x0000, 0x0000,
+      0x064b, 0x064b, 0x064c, 0x0000, 0x064d, 0x0000, 0x064e, 0x064e,
+      0x064f, 0x064f, 0x0650, 0x0650, 0x0651, 0x0651, 0x0652, 0x0652,
+      0x0621, 0x0622, 0x0622, 0x0623, 0x0623, 0x0624, 0x0624, 0x0625,
+      0x0625, 0x0626, 0x0626, 0x0626, 0x0626, 0x0627, 0x0627, 0x0628,
+      0x0628, 0x0628, 0x0628, 0x0629, 0x0629, 0x062a, 0x062a, 0x062a,
+      0x062a, 0x062b, 0x062b, 0x062b, 0x062b, 0x062c, 0x062c, 0x062c,
+      0x062c, 0x062d, 0x062d, 0x062d, 0x062d, 0x062e, 0x062e, 0x062e,
+      0x062e, 0x062f, 0x062f, 0x0630, 0x0630, 0x0631, 0x0631, 0x0632,
+      0x0632, 0x0633, 0x0633, 0x0633, 0x0633, 0x0634, 0x0634, 0x0634,
+      0x0634, 0x0635, 0x0635, 0x0635, 0x0635, 0x0636, 0x0636, 0x0636,
+      0x0636, 0x0637, 0x0637, 0x0637, 0x0637, 0x0638, 0x0638, 0x0638,
+      0x0638, 0x0639, 0x0639, 0x0639, 0x0639, 0x063a, 0x063a, 0x063a,
+      0x063a, 0x0641, 0x0641, 0x0641, 0x0641, 0x0642, 0x0642, 0x0642,
+      0x0642, 0x0643, 0x0643, 0x0643, 0x0643, 0x0644, 0x0644, 0x0644,
+      0x0644, 0x0645, 0x0645, 0x0645, 0x0645, 0x0646, 0x0646, 0x0646,
+      0x0646, 0x0647, 0x0647, 0x0647, 0x0647, 0x0648, 0x0648, 0x0649,
+      0x0649, 0x064a, 0x064a, 0x064a, 0x064a, 0x0000, 0x0000, 0x0000
+  };
+  static const WCHAR compat_FF00_FFEF[240] =
+  {
+      0x0000, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+      0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+      0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+      0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+      0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+      0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+      0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+      0x0058, 0x0059, 0x005a, 0x005b, 0x0000, 0x005d, 0x005e, 0x005f,
+      0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+      0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+      0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+      0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,
+      0x0000, 0x3002, 0x300c, 0x300d, 0x3001, 0x30fb, 0x30f2, 0x30a1,
+      0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, 0x30e7, 0x30c3,
+      0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa, 0x30ab, 0x30ad,
+      0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7, 0x30b9, 0x30bb, 0x30bd,
+      0x30bf, 0x30c1, 0x30c4, 0x30c6, 0x30c8, 0x30ca, 0x30cb, 0x30cc,
+      0x30cd, 0x30ce, 0x30cf, 0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de,
+      0x30df, 0x30e0, 0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9,
+      0x30ea, 0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, 0x309b, 0x309c,
+      0x3164, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137,
+      0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f,
+      0x3140, 0x3141, 0x3142, 0x3143, 0x3144, 0x3145, 0x3146, 0x3147,
+      0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e, 0x0000,
+      0x0000, 0x0000, 0x314f, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154,
+      0x0000, 0x0000, 0x3155, 0x3156, 0x3157, 0x3158, 0x3159, 0x315a,
+      0x0000, 0x0000, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160,
+      0x0000, 0x0000, 0x3161, 0x3162, 0x3163, 0x0000, 0x0000, 0x0000,
+      0x00a2, 0x00a3, 0x00ac, 0x00af, 0x00a6, 0x00a5, 0x20a9, 0x0000,
+      0x2502, 0x2190, 0x2191, 0x2192, 0x2193, 0x25a0, 0x25cb, 0x0000
+  };
+  static const WCHAR ligatures_src[] =
+  {
+    0x00c6, 0x00de, 0x00df, 0x00e6, 0x00fe, 0x0132, 0x0133, 0x0152,
+    0x0153, 0x01c4, 0x01c5, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01ca,
+    0x01cb, 0x01cc, 0x01e2, 0x01e3, 0x01f1, 0x01f2, 0x01f3, 0x01fc,
+    0x01fd, 0x05f0, 0x05f1, 0x05f2, 0xfb00, 0xfb01, 0xfb02, 0xfb03,
+    0xfb04, 0xfb05, 0xfb06, '\0'
+  };
+  static const WCHAR ligatures_dst[] =
+  {
+    'A','E','T','H','s','s','a','e','t','h','I','J','i','j','O','E','o','e',
+    'D',0x017d,'D',0x017e,'d',0x017e,'L','J','L','j','l','j','N','J','N','j',
+    'n','j',0x0100,0x0112,0x0101,0x0113,'D','Z','D','z','d','z',0x00c1,0x00c9,
+    0x00e1,0x00e9,0x05d5,0x05d5,0x05d5,0x05d9,0x05d9,0x05d9,'f','f','f','i',
+    'f','l','f','f','i','f','f','l',0x017f,'t','s','t','\0'
+  };
+
+  if (!pFoldStringW)
+    return; /* FoldString is present in NT v3.1+, but not 95/98/Me */
+
+  /* Invalid flag combinations */
+  for (i = 0; i < sizeof(badFlags)/sizeof(badFlags[0]); i++)
+  {
+    src[0] = dst[0] = '\0';
+    SetLastError(0);
+    ret = pFoldStringW(badFlags[i], src, 256, dst, 256);
+    if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+      return;
+    EXPECT_LEN(0); EXPECT_FLAGS;
+  }
+
+  /* src & dst cannot be the same */
+  SetLastError(0);
+  ret = pFoldStringW(MAP_FOLDCZONE, src, -1, src, 256);
+  EXPECT_LEN(0); EXPECT_INVALID;
+
+  /* src can't be NULL */
+  SetLastError(0);
+  ret = pFoldStringW(MAP_FOLDCZONE, NULL, -1, dst, 256);
+  EXPECT_LEN(0); EXPECT_INVALID;
+
+  /* srclen can't be 0 */
+  SetLastError(0);
+  ret = pFoldStringW(MAP_FOLDCZONE, src, 0, dst, 256);
+  EXPECT_LEN(0); EXPECT_INVALID;
+
+  /* dstlen can't be < 0 */
+  SetLastError(0);
+  ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, -1);
+  EXPECT_LEN(0); EXPECT_INVALID;
+
+  /* Ret includes terminating NUL which is appended if srclen = -1 */
+  SetLastError(0);
+  src[0] = 'A';
+  src[1] = '\0';
+  dst[0] = '\0';
+  ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
+  EXPECT_LEN(2); EXPECT_VALID;
+  ok(dst[0] == 'A' && dst[1] == '\0',
+     "srclen=-1: Expected ret=2 [%d,%d], got ret=%d [%d,%d], err=%ld\n",
+     'A', '\0', ret, dst[0], dst[1], GetLastError());
+
+  /* If size is given, result is not NUL terminated */
+  SetLastError(0);
+  src[0] = 'A';
+  src[1] = 'A';
+  dst[0] = 'X';
+  dst[1] = 'X';
+  ret = pFoldStringW(MAP_FOLDCZONE, src, 1, dst, 256);
+  EXPECT_LEN(1); EXPECT_VALID;
+  ok(dst[0] == 'A' && dst[1] == 'X',
+     "srclen=1: Expected ret=1, [%d,%d], got ret=%d,[%d,%d], err=%ld\n",
+     'A','X', ret, dst[0], dst[1], GetLastError());
+
+  /* MAP_FOLDDIGITS */
+  for (j = 0; j < sizeof(digitRanges)/sizeof(digitRanges[0]); j++)
+  {
+    /* Check everything before this range */
+    for (ch = prev_ch; ch < digitRanges[j]; ch++)
+    {
+      SetLastError(0);
+      src[0] = ch;
+      src[1] = dst[0] = '\0';
+      ret = pFoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
+      EXPECT_LEN(2); EXPECT_VALID;
+
+      ok(dst[0] == ch || strchrW(outOfSequenceDigits, ch) ||
+         /* Wine (correctly) maps all Unicode 4.0+ digits */
+         isdigitW(ch) || (ch >= 0x24F5 && ch <= 0x24FD) || ch == 0x24FF,
+         "MAP_FOLDDIGITS: ch %d 0x%04x Expected unchanged got %d\n", ch, ch, dst[0]);
+    }
+
+    if (digitRanges[j] == 0xffff)
+      break; /* Finished the whole code point space */
+
+    for (ch = digitRanges[j]; ch < digitRanges[j] + 10; ch++)
+    {
+      WCHAR c;
+
+      /* Map out of sequence characters */
+      if      (ch == 0x2071) c = 0x00B9; /* Superscript 1 */
+      else if (ch == 0x2072) c = 0x00B2; /* Superscript 2 */
+      else if (ch == 0x2073) c = 0x00B3; /* Superscript 3 */
+      else if (ch == 0x245F) c = 0x24EA; /* Circled 0     */
+      else                   c = ch;
+      SetLastError(0);
+      src[0] = c;
+      src[1] = dst[0] = '\0';
+      ret = pFoldStringW(MAP_FOLDDIGITS, src, -1, dst, 256);
+      EXPECT_LEN(2); EXPECT_VALID;
+
+      ok((dst[0] == '0' + ch - digitRanges[j] && dst[1] == '\0') ||
+         strchrW(noDigitAvailable, c),
+         "MAP_FOLDDIGITS: ch %d Expected %d got %d\n",
+         ch, '0' + digitRanges[j] - ch, dst[0]);
+    }
+    prev_ch = ch;
+  }
+
+  /* MAP_FOLDCZONE */
+  for (ch = 1; ch <0xffff; ch++)
+  {
+    WCHAR expected = 0;
+
+    if (ch >= 0xF900 && ch <= 0xFA2F)
+      expected = compat_F900_FA2F[ch - 0xF900];
+    else if (ch >= 0xFE30 && ch <= 0xFEF7)
+      expected = compat_FE30_FEF7[ch - 0xFE30];
+    else if (ch >= 0xFF00 && ch <= 0xFFEF)
+      expected = compat_FF00_FFEF[ch - 0xFF00];
+
+    if (!expected)
+      expected = ch;
+
+    SetLastError(0);
+    src[0] = ch;
+    src[1] = dst[0] = '\0';
+    ret = pFoldStringW(MAP_FOLDCZONE, src, -1, dst, 256);
+    EXPECT_LEN(2); EXPECT_VALID;
+    ok(dst[0] == expected ||
+       /* Wine (correctly) uses updated mappings for some Unicode 4.0 chars */
+       (ch >= 0xFA0D && ch <= 0xFA47) ||
+       0xf92c || ch == 0xf979 || ch == 0xf995 || ch == 0xf9e7 || ch == 0xf9f1,
+       "MAP_FOLDCZONE: ch %d 0x%04x Expected 0x%04x got 0x%04x\n",
+       ch, ch, expected, dst[0]);
+  }
+
+  /* MAP_EXPAND_LIGATURES */
+  SetLastError(0);
+  ret = pFoldStringW(MAP_EXPAND_LIGATURES, ligatures_src, -1, dst, 256);
+  EXPECT_LEN(sizeof(ligatures_dst)/sizeof(ligatures_dst[0])); EXPECT_VALID;
+  ok(!memcmp(dst, ligatures_dst, sizeof(ligatures_dst)),
+     "MAP_EXPAND_LIGATURES: Expanded incorrectly\n");
+  for (i = 1; i <= 0xffff; i++)
+  {
+    if (!strchrW(ligatures_src, i))
+    {
+      src[0] = i;
+      src[1] = '\0';
+      SetLastError(0);
+      ret = pFoldStringW(MAP_EXPAND_LIGATURES, src, -1, dst, 256);
+      EXPECT_LEN(2); EXPECT_VALID;
+      ok(dst[0] == src[0],
+         "MAP_EXPAND_LIGATURES: 0x%02x : Expected 0x%02x, got 0x%02x\n",
+         i, src[0], dst[0]);
+    }
+  }
+
+  /* FIXME: MAP_PRECOMPOSED : MAP_COMPOSITE */
+}
+
+
+
+#define LCID_OK(l) \
+  ok(lcid == l, "Expected lcid = %08lx, got %08lx\n", l, lcid)
+#define MKLCID(x,y,z) MAKELCID(MAKELANGID(x, y), z)
+#define LCID_RES(src, res) lcid = ConvertDefaultLocale(src); LCID_OK(res)
+#define TEST_LCIDLANG(a,b) LCID_RES(MAKELCID(a,b), MAKELCID(a,b))
+#define TEST_LCID(a,b,c) LCID_RES(MKLCID(a,b,c), MKLCID(a,b,c))
+
+static void test_ConvertDefaultLocale(void)
+{
+  LCID lcid;
+
+  /* Doesn't change lcid, even if non default sublang/sort used */
+  TEST_LCID(LANG_ENGLISH,  SUBLANG_ENGLISH_US, SORT_DEFAULT);
+  TEST_LCID(LANG_ENGLISH,  SUBLANG_ENGLISH_UK, SORT_DEFAULT);
+  TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT,    SORT_DEFAULT);
+  TEST_LCID(LANG_JAPANESE, SUBLANG_DEFAULT,    SORT_JAPANESE_UNICODE);
+
+  /* SUBLANG_NEUTRAL -> SUBLANG_DEFAULT */
+  LCID_RES(MKLCID(LANG_ENGLISH,  SUBLANG_NEUTRAL, SORT_DEFAULT),
+           MKLCID(LANG_ENGLISH,  SUBLANG_DEFAULT, SORT_DEFAULT));
+  LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_DEFAULT),
+           MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_DEFAULT));
+  LCID_RES(MKLCID(LANG_JAPANESE, SUBLANG_NEUTRAL, SORT_JAPANESE_UNICODE),
+           MKLCID(LANG_JAPANESE, SUBLANG_DEFAULT, SORT_JAPANESE_UNICODE));
+
+  /* Invariant language is not treated specially */
+  TEST_LCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT);
+  LCID_RES(MKLCID(LANG_INVARIANT, SUBLANG_NEUTRAL, SORT_DEFAULT),
+           MKLCID(LANG_INVARIANT, SUBLANG_DEFAULT, SORT_DEFAULT));
+
+  /* User/system default languages alone are not mapped */
+  TEST_LCIDLANG(LANG_SYSTEM_DEFAULT, SORT_JAPANESE_UNICODE);
+  TEST_LCIDLANG(LANG_USER_DEFAULT,   SORT_JAPANESE_UNICODE);
+
+  /* Default lcids */
+  LCID_RES(LOCALE_SYSTEM_DEFAULT, GetSystemDefaultLCID());
+  LCID_RES(LOCALE_USER_DEFAULT,   GetUserDefaultLCID());
+  LCID_RES(LOCALE_NEUTRAL,        GetUserDefaultLCID());
+}
+
+static BOOL CALLBACK langgrp_procA(LGRPID lgrpid, LPSTR lpszNum, LPSTR lpszName,
+                                    DWORD dwFlags, LONG_PTR lParam)
+{
+  trace("%08lx, %s, %s, %08lx, %08lx\n",
+        lgrpid, lpszNum, lpszName, dwFlags, lParam);
+
+  ok(pIsValidLanguageGroup(lgrpid, dwFlags) == TRUE,
+     "Enumerated grp %ld not valid (flags %ld)\n", lgrpid, dwFlags);
+
+  /* If lParam is one, we are calling with flags defaulted from 0 */
+  ok(!lParam || (dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED),
+        "Expected dwFlags == LGRPID_INSTALLED || dwFlags == LGRPID_SUPPORTED, got %ld\n", dwFlags);
+
+  return TRUE;
+}
+
+static void test_EnumSystemLanguageGroupsA(void)
+{
+  if (!pEnumSystemLanguageGroupsA || !pIsValidLanguageGroup)
+    return;
+
+  /* No enumeration proc */
+  SetLastError(0);
+  pEnumSystemLanguageGroupsA(0, LGRPID_INSTALLED, 0);
+  EXPECT_INVALID;
+
+  /* Invalid flags */
+  SetLastError(0);
+  pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED|LGRPID_SUPPORTED, 0);
+  EXPECT_FLAGS;
+
+  /* No flags - defaults to LGRPID_INSTALLED */
+  SetLastError(0);
+  pEnumSystemLanguageGroupsA(langgrp_procA, 0, 1);
+  EXPECT_VALID;
+
+  pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_INSTALLED, 0);
+  pEnumSystemLanguageGroupsA(langgrp_procA, LGRPID_SUPPORTED, 0);
+}
+
+
+static BOOL CALLBACK lgrplocale_procA(LGRPID lgrpid, LCID lcid, LPSTR lpszNum,
+                                      LONG_PTR lParam)
+{
+  trace("%08lx, %08lx, %s, %08lx\n", lgrpid, lcid, lpszNum, lParam);
+
+  ok(pIsValidLanguageGroup(lgrpid, LGRPID_SUPPORTED) == TRUE,
+     "Enumerated grp %ld not valid\n", lgrpid);
+  ok(IsValidLocale(lcid, LCID_SUPPORTED) == TRUE,
+     "Enumerated grp locale %ld not valid\n", lcid);
+  return TRUE;
+}
+
+static void test_EnumLanguageGroupLocalesA(void)
+{
+  if (!pEnumLanguageGroupLocalesA || !pIsValidLanguageGroup)
+    return;
+
+  /* No enumeration proc */
+  SetLastError(0);
+  pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0, 0);
+  EXPECT_INVALID;
+
+  /* lgrpid too small */
+  SetLastError(0);
+  pEnumLanguageGroupLocalesA(lgrplocale_procA, 0, 0, 0);
+  EXPECT_INVALID;
+
+  /* lgrpid too big */
+  SetLastError(0);
+  pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_ARMENIAN + 1, 0, 0);
+  EXPECT_INVALID;
+
+  /* dwFlags is reserved */
+  SetLastError(0);
+  pEnumLanguageGroupLocalesA(0, LGRPID_WESTERN_EUROPE, 0x1, 0);
+  EXPECT_INVALID;
+
+  pEnumLanguageGroupLocalesA(lgrplocale_procA, LGRPID_WESTERN_EUROPE, 0, 0);
+}
+
+static void test_SetLocaleInfoA(void)
+{
+  BOOL bRet;
+  LCID lcid = GetUserDefaultLCID();
+
+  /* Null data */
+  SetLastError(0);
+  bRet = SetLocaleInfoA(lcid, LOCALE_SDATE, 0);
+  EXPECT_INVALID;
+
+  /* IDATE */
+  SetLastError(0);
+  bRet = SetLocaleInfoA(lcid, LOCALE_IDATE, (LPSTR)test_SetLocaleInfoA);
+  EXPECT_FLAGS;
+
+  /* ILDATE */
+  SetLastError(0);
+  bRet = SetLocaleInfoA(lcid, LOCALE_ILDATE, (LPSTR)test_SetLocaleInfoA);
+  EXPECT_FLAGS;
+}
+
+START_TEST(locale)
+{
+  InitFunctionPointers();
+
+#if 0
+  test_EnumTimeFormats();
+#endif
+  test_GetLocaleInfoA();
+  test_GetTimeFormatA();
+  test_GetDateFormatA();
+  test_GetDateFormatW();
+  test_GetCurrencyFormatA(); /* Also tests the W version */
+  test_GetNumberFormatA();   /* Also tests the W version */
+  test_CompareStringA();
+  test_LCMapStringA();
+  test_LCMapStringW();
+  test_FoldStringA();
+  test_FoldStringW();
+  test_ConvertDefaultLocale();
+  test_EnumSystemLanguageGroupsA();
+  test_EnumLanguageGroupLocalesA();
+  test_SetLocaleInfoA();
+#if 0 /* this requires collation table patch to make it MS compatible */
+  test_sorting();
+#endif
+}
diff --git a/reactos/apps/tests/kernel32/mailslot.c b/reactos/apps/tests/kernel32/mailslot.c
new file mode 100644 (file)
index 0000000..10b9ae5
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ *  Mailslot regression test
+ *
+ *  Copyright 2003 Mike McCormack
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <windef.h>
+#include <winbase.h>
+
+#ifndef STANDALONE
+#include "wine/test.h"
+#else
+#define START_TEST(name) main(int argc, char **argv)
+
+#define ok(cond,str) do{ if(!(cond)) printf("line %d: %s\n",__LINE__,str); }while (0)
+#define todo_wine
+
+#endif
+
+const char szmspath[] = "\\\\.\\mailslot\\wine_mailslot_test";
+
+int mailslot_test()
+{
+    HANDLE hSlot, hSlot2, hWriter, hWriter2;
+    unsigned char buffer[16];
+    DWORD count, dwMax, dwNext, dwMsgCount, dwTimeout;
+
+    /* sanity check on GetMailslotInfo */
+    dwMax = dwNext = dwMsgCount = dwTimeout = 0;
+    ok( !GetMailslotInfo( INVALID_HANDLE_VALUE, &dwMax, &dwNext,
+            &dwMsgCount, &dwTimeout ), "getmailslotinfo succeeded\n");
+
+    /* open a mailslot that doesn't exist */
+    hWriter = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+                             FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter == INVALID_HANDLE_VALUE, "non-existing mailslot\n");
+
+    /* open a mailslot without the right name */
+    hSlot = CreateMailslot( "blah", 0, 0, NULL );
+    ok( hSlot == INVALID_HANDLE_VALUE,
+            "Created mailslot with invalid name\n");
+    todo_wine
+    {
+       ok( GetLastError() == ERROR_INVALID_NAME,
+           "error should be ERROR_INVALID_NAME\n");
+    }
+
+    /* open a mailslot with a null name */
+    hSlot = CreateMailslot( NULL, 0, 0, NULL );
+    ok( hSlot == INVALID_HANDLE_VALUE,
+            "Created mailslot with invalid name\n");
+    todo_wine
+    {
+        ok( GetLastError() == ERROR_PATH_NOT_FOUND,
+            "error should be ERROR_PATH_NOT_FOUND\n");
+    }
+
+    todo_wine
+    {
+    /* valid open, but with wacky parameters ... then check them */
+    hSlot = CreateMailslot( szmspath, -1, -1, NULL );
+    ok( hSlot != INVALID_HANDLE_VALUE , "mailslot with valid name failed\n");
+    dwMax = dwNext = dwMsgCount = dwTimeout = 0;
+    ok( GetMailslotInfo( hSlot, &dwMax, &dwNext, &dwMsgCount, &dwTimeout ),
+           "getmailslotinfo failed\n");
+    ok( dwMax == -1, "dwMax incorrect\n");
+    ok( dwNext == MAILSLOT_NO_MESSAGE, "dwNext incorrect\n");
+    }
+    ok( dwMsgCount == 0, "dwMsgCount incorrect\n");
+    todo_wine
+    {
+    ok( dwTimeout == -1, "dwTimeout incorrect\n");
+    ok( GetMailslotInfo( hSlot, NULL, NULL, NULL, NULL ),
+            "getmailslotinfo failed\n");
+    ok( CloseHandle(hSlot), "failed to close mailslot\n");
+    }
+
+    todo_wine
+    {
+    /* now open it for real */
+    hSlot = CreateMailslot( szmspath, 0, 0, NULL );
+    ok( hSlot != INVALID_HANDLE_VALUE , "valid mailslot failed\n");
+    }
+
+    /* try and read/write to it */
+    count = 0;
+    memset(buffer, 0, sizeof buffer);
+    ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+            "slot read\n");
+    ok( !WriteFile( hSlot, buffer, sizeof buffer, &count, NULL),
+            "slot write\n");
+
+    /* now try and openthe client, but with the wrong sharing mode */
+    hWriter = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+                             0, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter == INVALID_HANDLE_VALUE, "bad sharing mode\n");
+    todo_wine
+    {
+    ok( GetLastError() == ERROR_SHARING_VIOLATION,
+            "error should be ERROR_SHARING_VIOLATION\n");
+
+    /* now open the client with the correct sharing mode */
+    hWriter = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+                             FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter != INVALID_HANDLE_VALUE, "existing mailslot\n");
+    }
+
+    /*
+     * opening a client should make no difference to
+     * whether we can read or write the mailslot
+     */
+    ok( !ReadFile( hSlot, buffer, sizeof buffer/2, &count, NULL),
+            "slot read\n");
+    ok( !WriteFile( hSlot, buffer, sizeof buffer/2, &count, NULL),
+            "slot write\n");
+
+    /*
+     * we can't read from this client, 
+     * but we should be able to write to it
+     */
+    ok( !ReadFile( hWriter, buffer, sizeof buffer/2, &count, NULL),
+            "can read client\n");
+    todo_wine
+    {
+    ok( WriteFile( hWriter, buffer, sizeof buffer/2, &count, NULL),
+            "can't write client\n");
+    }
+    ok( !ReadFile( hWriter, buffer, sizeof buffer/2, &count, NULL),
+            "can read client\n");
+
+    /*
+     * seeing as there's something in the slot,
+     * we should be able to read it once
+     */
+    todo_wine
+    {
+    ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+            "slot read\n");
+    ok( count == (sizeof buffer/2), "short read\n" );
+    }
+
+    /* but not again */
+    ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+            "slot read\n");
+
+    /* now try open another writer... should fail */
+    hWriter2 = CreateFile(szmspath, GENERIC_READ|GENERIC_WRITE,
+                     FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter2 == INVALID_HANDLE_VALUE, "two writers\n");
+
+    /* now try open another as a reader ... also fails */
+    hWriter2 = CreateFile(szmspath, GENERIC_READ,
+                     FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter2 == INVALID_HANDLE_VALUE, "writer + reader\n");
+
+    /* now try open another as a writer ... still fails */
+    hWriter2 = CreateFile(szmspath, GENERIC_WRITE,
+                     FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter2 == INVALID_HANDLE_VALUE, "writer\n");
+
+    /* now open another one */
+    hSlot2 = CreateMailslot( szmspath, 0, 0, NULL );
+    ok( hSlot2 == INVALID_HANDLE_VALUE , "opened two mailslots\n");
+
+    todo_wine
+    {
+    /* close the client again */
+    ok( CloseHandle( hWriter ), "closing the client\n");
+
+    /*
+     * now try reopen it with slightly different permissions ...
+     * shared writing
+     */
+    hWriter = CreateFile(szmspath, GENERIC_WRITE,
+              FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter != INVALID_HANDLE_VALUE, "sharing writer\n");
+    }
+
+    /*
+     * now try open another as a writer ...
+     * but don't share with the first ... fail
+     */
+    hWriter2 = CreateFile(szmspath, GENERIC_WRITE,
+                     FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter2 == INVALID_HANDLE_VALUE, "greedy writer succeeded\n");
+
+    todo_wine
+    {
+    /* now try open another as a writer ... and share with the first */
+    hWriter2 = CreateFile(szmspath, GENERIC_WRITE,
+              FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+    ok( hWriter2 != INVALID_HANDLE_VALUE, "2nd sharing writer\n");
+
+    /* check the mailslot info */
+    dwMax = dwNext = dwMsgCount = dwTimeout = 0;
+    ok( GetMailslotInfo( hSlot, &dwMax, &dwNext, &dwMsgCount, &dwTimeout ),
+        "getmailslotinfo failed\n");
+    ok( dwNext == MAILSLOT_NO_MESSAGE, "dwNext incorrect\n");
+    }
+    ok( dwMax == 0, "dwMax incorrect\n");
+    ok( dwMsgCount == 0, "dwMsgCount incorrect\n");
+    ok( dwTimeout == 0, "dwTimeout incorrect\n");
+
+    /* check there's still no data */
+    ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL), "slot read\n");
+
+    /* write two messages */
+    todo_wine
+    {
+    buffer[0] = 'a';
+    ok( WriteFile( hWriter, buffer, 1, &count, NULL), "1st write failed\n");
+
+    /* check the mailslot info */
+    dwNext = dwMsgCount = 0;
+    ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+        "getmailslotinfo failed\n");
+    ok( dwNext == 1, "dwNext incorrect\n");
+    ok( dwMsgCount == 1, "dwMsgCount incorrect\n");
+
+    buffer[0] = 'b';
+    buffer[1] = 'c';
+    ok( WriteFile( hWriter2, buffer, 2, &count, NULL), "2nd write failed\n");
+
+    /* check the mailslot info */
+    dwNext = dwMsgCount = 0;
+    ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+        "getmailslotinfo failed\n");
+    ok( dwNext == 1, "dwNext incorrect\n");
+    ok( dwMsgCount == 2, "dwMsgCount incorrect\n");
+
+    /* write a 3rd message with zero size */
+    ok( WriteFile( hWriter2, buffer, 0, &count, NULL), "3rd write failed\n");
+
+    /* check the mailslot info */
+    dwNext = dwMsgCount = 0;
+    ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+        "getmailslotinfo failed\n");
+    ok( dwNext == 1, "dwNext incorrect\n");
+    ok( dwMsgCount == 3, "dwMsgCount incorrect\n");
+
+    buffer[0]=buffer[1]=0;
+
+    /*
+     * then check that they come out with the correct order and size,
+     * then the slot is empty
+     */
+    ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+        "1st slot read failed\n");
+    ok( count == 1, "failed to get 1st message\n");
+    ok( buffer[0] == 'a', "1st message wrong\n");
+
+    /* check the mailslot info */
+    dwNext = dwMsgCount = 0;
+    ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+        "getmailslotinfo failed\n");
+    ok( dwNext == 2, "dwNext incorrect\n");
+    ok( dwMsgCount == 2, "dwMsgCount incorrect\n");
+
+    /* read the second message */
+    ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+        "2nd slot read failed\n");
+    ok( count == 2, "failed to get 2nd message\n");
+    ok( ( buffer[0] == 'b' ) && ( buffer[1] == 'c' ), "2nd message wrong\n");
+
+    /* check the mailslot info */
+    dwNext = dwMsgCount = 0;
+    ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+        "getmailslotinfo failed\n");
+    }
+    ok( dwNext == 0, "dwNext incorrect\n");
+    todo_wine
+    {
+    ok( dwMsgCount == 1, "dwMsgCount incorrect\n");
+
+    /* read the 3rd (zero length) message */
+    ok( ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+        "3rd slot read failed\n");
+    }
+    ok( count == 0, "failed to get 3rd message\n");
+
+    /*
+     * now there should be no more messages
+     * check the mailslot info
+     */
+    todo_wine
+    {
+    dwNext = dwMsgCount = 0;
+    ok( GetMailslotInfo( hSlot, NULL, &dwNext, &dwMsgCount, NULL ),
+        "getmailslotinfo failed\n");
+    ok( dwNext == MAILSLOT_NO_MESSAGE, "dwNext incorrect\n");
+    }
+    ok( dwMsgCount == 0, "dwMsgCount incorrect\n");
+
+    /* check that reads fail */
+    ok( !ReadFile( hSlot, buffer, sizeof buffer, &count, NULL),
+        "3rd slot read succeeded\n");
+
+    /* finally close the mailslot and its client */
+    todo_wine
+    {
+    ok( CloseHandle( hWriter2 ), "closing 2nd client\n");
+    ok( CloseHandle( hWriter ), "closing the client\n");
+    ok( CloseHandle( hSlot ), "closing the mailslot\n");
+    }
+
+    return 0;
+}
+
+START_TEST(mailslot)
+{
+    mailslot_test();
+}
diff --git a/reactos/apps/tests/kernel32/path.c b/reactos/apps/tests/kernel32/path.c
new file mode 100644 (file)
index 0000000..b5f5b53
--- /dev/null
@@ -0,0 +1,946 @@
+/*
+ * Unit test suite for Get*PathNamesA and (Get|Set)CurrentDirectoryA.
+ *
+ * Copyright 2002 Geoffrey Hausheer
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "winnls.h"
+
+#define HAS_TRAIL_SLASH_A(string) (string[lstrlenA(string)-1]=='\\')
+
+#define LONGFILE "Long File test.path"
+#define SHORTFILE "pathtest.pth"
+#define SHORTDIR "shortdir"
+#define LONGDIR "Long Directory"
+#define NONFILE_SHORT "noexist.pth"
+#define NONFILE_LONG "Non Existent File"
+#define NONDIR_SHORT "notadir"
+#define NONDIR_LONG "Non Existent Directory"
+
+#define NOT_A_VALID_DRIVE '@'
+
+/* the following characters don't work well with GetFullPathNameA
+   in Win98.  I don't know if this is a FAT thing, or if it is an OS thing
+   but I don't test these characters now.
+   NOTE: Win2k allows GetFullPathNameA to work with them though
+      |<>"
+*/
+static const CHAR funny_chars[]="!@#$%^&*()=+{}[],?'`";
+static const CHAR is_char_ok[] ="11111110111111111011";
+static const CHAR wine_todo[]  ="00000000000000000000";
+
+static DWORD (WINAPI *pGetLongPathNameA)(LPCSTR,LPSTR,DWORD);
+
+/* a structure to deal with wine todos somewhat cleanly */
+typedef struct {
+  DWORD shortlen;
+  DWORD shorterror;
+  DWORD s2llen;
+  DWORD s2lerror;
+  DWORD longlen;
+  DWORD longerror;
+} SLpassfail;
+
+/* function that tests GetFullPathNameA, GetShortPathNameA,GetLongPathNameA */
+/* NOTE: the passfail structure is used to allow cutomizeable todo checking
+         for wine.  It is not very pretty, but it sure beats duplicating this
+         function lots of times
+*/
+static void test_ValidPathA(CHAR *curdir, CHAR *subdir, CHAR *filename,
+                         CHAR *shortstr, SLpassfail *passfail, CHAR *errstr) {
+  CHAR tmpstr[MAX_PATH],
+       fullpath[MAX_PATH],      /*full path to the file (not short/long) */
+       subpath[MAX_PATH],       /*relative path to the file */
+       fullpathshort[MAX_PATH], /*absolue path to the file (short format) */
+       fullpathlong[MAX_PATH],  /*absolute path to the file (long format) */
+       curdirshort[MAX_PATH],   /*absolute path to the current dir (short) */
+       curdirlong[MAX_PATH];    /*absolute path to the current dir (long) */
+  LPSTR strptr;                 /*ptr to the filename portion of the path */
+  DWORD len;
+/* if passfail is NULL, we can perform all checks within this function,
+   otherwise, we will return the relevant data in the passfail struct, so
+   we must initialize it first
+*/
+  if(passfail!=NULL) {
+    passfail->shortlen=-1;passfail->s2llen=-1;passfail->longlen=-1;
+    passfail->shorterror=0;passfail->s2lerror=0;passfail->longerror=0;
+  }
+/* GetLongPathNameA is only supported on Win2k+ and Win98+ */
+  if(pGetLongPathNameA) {
+    ok((len=pGetLongPathNameA(curdir,curdirlong,MAX_PATH)),
+       "%s: GetLongPathNameA failed\n",errstr);
+/*GetLongPathNameA can return a trailing '\\' but shouldn't do so here */
+    ok(! HAS_TRAIL_SLASH_A(curdirlong),
+       "%s: GetLongPathNameA should not have a trailing \\\n",errstr);
+  }
+  ok((len=GetShortPathNameA(curdir,curdirshort,MAX_PATH)),
+     "%s: GetShortPathNameA failed\n",errstr);
+/*GetShortPathNameA can return a trailing '\\' but shouldn't do so here */
+  ok(! HAS_TRAIL_SLASH_A(curdirshort),
+     "%s: GetShortPathNameA should not have a trailing \\\n",errstr);
+/* build relative and absolute paths from inputs */
+  if(lstrlenA(subdir)) {
+    sprintf(subpath,"%s\\%s",subdir,filename);
+  } else {
+    lstrcpyA(subpath,filename);
+  }
+  sprintf(fullpath,"%s\\%s",curdir,subpath);
+  sprintf(fullpathshort,"%s\\%s",curdirshort,subpath);
+  sprintf(fullpathlong,"%s\\%s",curdirlong,subpath);
+/* Test GetFullPathNameA functionality */
+  len=GetFullPathNameA(subpath,MAX_PATH,tmpstr,&strptr);
+  ok(len, "GetFullPathNameA failed for: '%s'\n",subpath);
+  if(HAS_TRAIL_SLASH_A(subpath)) {
+    ok(strptr==NULL,
+       "%s: GetFullPathNameA should not return a filename ptr\n",errstr);
+    ok(lstrcmpiA(fullpath,tmpstr)==0,
+       "%s: GetFullPathNameA returned '%s' instead of '%s'\n",
+       errstr,tmpstr,fullpath);
+  } else {
+    ok(lstrcmpiA(strptr,filename)==0,
+       "%s: GetFullPathNameA returned '%s' instead of '%s'\n",
+       errstr,strptr,filename);
+    ok(lstrcmpiA(fullpath,tmpstr)==0,
+       "%s: GetFullPathNameA returned '%s' instead of '%s'\n",
+       errstr,tmpstr,fullpath);
+  }
+/* Test GetShortPathNameA functionality */
+  SetLastError(0);
+  len=GetShortPathNameA(fullpathshort,shortstr,MAX_PATH);
+  if(passfail==NULL) {
+    ok(len, "%s: GetShortPathNameA failed\n",errstr);
+  } else {
+    passfail->shortlen=len;
+    passfail->shorterror=GetLastError();
+  }
+/* Test GetLongPathNameA functionality
+   We test both conversion from GetFullPathNameA and from GetShortPathNameA
+*/
+  if(pGetLongPathNameA) {
+    if(len!=0) {
+      SetLastError(0);
+      len=pGetLongPathNameA(shortstr,tmpstr,MAX_PATH);
+      if(passfail==NULL) {
+        ok(len,
+          "%s: GetLongPathNameA failed during Short->Long conversion\n", errstr);
+        ok(lstrcmpiA(fullpathlong,tmpstr)==0,
+           "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
+           errstr,tmpstr,fullpathlong);
+      } else {
+        passfail->s2llen=len;
+        passfail->s2lerror=GetLastError();
+      }
+    }
+    SetLastError(0);
+    len=pGetLongPathNameA(fullpath,tmpstr,MAX_PATH);
+    if(passfail==NULL) {
+      ok(len, "%s: GetLongPathNameA failed\n",errstr);
+      if(HAS_TRAIL_SLASH_A(fullpath)) {
+        ok(lstrcmpiA(fullpathlong,tmpstr)==0,
+           "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
+           errstr,tmpstr,fullpathlong);
+      } else {
+        ok(lstrcmpiA(fullpathlong,tmpstr)==0,
+          "%s: GetLongPathNameA returned '%s' instead of '%s'\n",
+          errstr,tmpstr,fullpathlong);
+      }
+    } else {
+      passfail->longlen=len;
+      passfail->longerror=GetLastError();
+    }
+  }
+}
+
+/* split path into leading directory, and 8.3 filename */
+static void test_SplitShortPathA(CHAR *path,CHAR *dir,CHAR *eight,CHAR *three) {
+  int done,error;
+  int ext,fil;
+  int len,i;
+  len=lstrlenA(path);
+  ext=len; fil=len; done=0; error=0;
+/* walk backwards over path looking for '.' or '\\' separators */
+  for(i=len-1;(i>=0) && (!done);i--) {
+    if(path[i]=='.')
+      if(ext!=len) error=1; else ext=i;
+    else if(path[i]=='\\') {
+      if(i==len-1) {
+        error=1;
+      } else {
+        fil=i;
+        done=1;
+      }
+    }
+  }
+/* Check that we didn't find a trailing '\\' or multiple '.' */
+  ok(!error,"Illegal file found in 8.3 path '%s'\n",path);
+/* Separate dir, root, and extension */
+  if(ext!=len) lstrcpyA(three,path+ext+1); else lstrcpyA(three,"");
+  if(fil!=len) {
+    lstrcpynA(eight,path+fil+1,ext-fil);
+    lstrcpynA(dir,path,fil+1);
+  } else {
+    lstrcpynA(eight,path,ext+1);
+    lstrcpyA(dir,"");
+  }
+/* Validate that root and extension really are 8.3 */
+  ok(lstrlenA(eight)<=8 && lstrlenA(three)<=3,
+     "GetShortPathNAmeA did not return an 8.3 path\n");
+}
+
+/* Check that GetShortPathNameA returns a valid 8.3 path */
+static void test_LongtoShortA(CHAR *teststr,CHAR *goodstr,
+                              CHAR *ext,CHAR *errstr) {
+  CHAR dir[MAX_PATH],eight[MAX_PATH],three[MAX_PATH];
+
+  test_SplitShortPathA(teststr,dir,eight,three);
+  ok(lstrcmpiA(dir,goodstr)==0,
+     "GetShortPathNameA returned '%s' instead of '%s'\n",dir,goodstr);
+  ok(lstrcmpiA(three,ext)==0,
+     "GetShortPathNameA returned '%s' with incorrect extension\n",three);
+}
+
+/* Test that Get(Short|Long|Full)PathNameA work correctly with interesting
+   characters in the filename.
+     'valid' indicates whether this would be an allowed filename
+     'todo' indicates that wine doesn't get this right yet.
+   NOTE: We always call this routine with a non-existent filename, so
+         Get(Short|Long)PathNameA should never pass, but GetFullPathNameA
+         should.
+*/
+static void test_FunnyChars(CHAR *curdir,CHAR *filename,
+                             INT valid,INT todo,CHAR *errstr) {
+  CHAR tmpstr[MAX_PATH],tmpstr1[MAX_PATH];
+  SLpassfail passfail;
+
+  test_ValidPathA(curdir,"",filename,tmpstr,&passfail,errstr);
+  if(valid) {
+    sprintf(tmpstr1,"%s\\%s",curdir,filename);
+    if(todo) {
+      todo_wine {
+        ok((passfail.shortlen==0 &&
+            (passfail.shorterror==ERROR_FILE_NOT_FOUND || passfail.shorterror==ERROR_PATH_NOT_FOUND || !passfail.shorterror)) ||
+           (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+           "%s: GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+           errstr,passfail.shortlen,passfail.shorterror,tmpstr);
+      }
+    } else {
+      ok((passfail.shortlen==0 &&
+          (passfail.shorterror==ERROR_FILE_NOT_FOUND || passfail.shorterror==ERROR_PATH_NOT_FOUND || !passfail.shorterror)) ||
+         (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+         "%s: GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+         errstr,passfail.shortlen,passfail.shorterror,tmpstr);
+    }
+  } else {
+    if(todo) {
+      todo_wine {
+/* Win2k returns ERROR_INVALID_NAME, Win98, wine return ERROR_FILE_NOT_FOUND, NT4 doesn't set last error */
+        ok(passfail.shortlen==0 &&
+           (passfail.shorterror==ERROR_INVALID_NAME || passfail.shorterror==ERROR_FILE_NOT_FOUND || !passfail.shorterror),
+           "%s: GetShortPathA should have failed len=%ld, error=%ld\n",
+           errstr,passfail.shortlen,passfail.shorterror);
+      }
+    } else {
+      ok(passfail.shortlen==0 &&
+         (passfail.shorterror==ERROR_INVALID_NAME || passfail.shorterror==ERROR_FILE_NOT_FOUND || !passfail.shorterror),
+         "%s: GetShortPathA should have failed len=%ld, error=%ld\n",
+         errstr,passfail.shortlen,passfail.shorterror);
+    }
+  }
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    if(valid) {
+      ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+         "%s: GetLongPathA returned %ld and not %d\n",
+         errstr,passfail.longerror,ERROR_FILE_NOT_FOUND);
+    } else {
+      ok(passfail.longerror==ERROR_INVALID_NAME ||
+         passfail.longerror==ERROR_FILE_NOT_FOUND,
+         "%s: GetLongPathA returned %ld and not %d or %d'\n",
+         errstr, passfail.longerror,ERROR_INVALID_NAME,ERROR_FILE_NOT_FOUND);
+    }
+  }
+}
+
+/* Routine to test that SetCurrentDirectory behaves as expected. */
+static void test_setdir(CHAR *olddir,CHAR *newdir,
+                        CHAR *cmprstr, INT pass,CHAR *errstr)
+{
+  CHAR tmppath[MAX_PATH], *dirptr;
+  DWORD val,len,chklen;
+
+  val=SetCurrentDirectoryA(newdir);
+  len=GetCurrentDirectoryA(MAX_PATH,tmppath);
+/* if 'pass' then the SetDirectoryA was supposed to pass */
+  if(pass) {
+    dirptr=(cmprstr==NULL) ? newdir : cmprstr;
+    chklen=lstrlenA(dirptr);
+    ok(val,"%s: SetCurrentDirectoryA failed\n",errstr);
+    ok(len==chklen,
+       "%s: SetCurrentDirectory did not change the directory, though it passed\n",
+       errstr);
+    ok(lstrcmpiA(dirptr,tmppath)==0,
+       "%s: SetCurrentDirectory did not change the directory, though it passed\n",
+       errstr);
+    ok(SetCurrentDirectoryA(olddir),
+       "%s: Couldn't set directory to it's original value\n",errstr);
+  } else {
+/* else thest that it fails correctly */
+    chklen=lstrlenA(olddir);
+    ok(val==0,
+       "%s: SetCurrentDirectoryA passed when it should have failed\n",errstr);
+    ok(len==chklen,
+       "%s: SetCurrentDirectory changed the directory, though it failed\n",
+       errstr);
+    ok(lstrcmpiA(olddir,tmppath)==0,
+       "%s: SetCurrentDirectory changed the directory, though it failed\n",
+       errstr);
+  }
+}
+static void test_InitPathA(CHAR *newdir, CHAR *curDrive, CHAR *otherDrive)
+{
+  CHAR tmppath[MAX_PATH], /*path to TEMP */
+       tmpstr[MAX_PATH],
+       tmpstr1[MAX_PATH];
+  DWORD len,len1,drives;
+  INT id;
+  HANDLE hndl;
+
+  *curDrive = *otherDrive = NOT_A_VALID_DRIVE;
+
+/* Get the current drive letter */
+  if( GetCurrentDirectoryA( MAX_PATH, tmpstr))
+    *curDrive = tmpstr[0];
+  else
+    trace( "Unable to discover current drive, some tests will not be conducted.\n");
+
+/* Test GetTempPathA */
+  len=GetTempPathA(MAX_PATH,tmppath);
+  ok(len!=0 && len < MAX_PATH,"GetTempPathA failed\n");
+  ok(HAS_TRAIL_SLASH_A(tmppath),
+     "GetTempPathA returned a path that did not end in '\\'\n");
+  lstrcpyA(tmpstr,"aaaaaaaa");
+  len1=GetTempPathA(len,tmpstr);
+  ok(len1==len+1,
+     "GetTempPathA should return string length %ld instead of %ld\n",len+1,len1);
+
+/* Test GetTmpFileNameA
+   The only test we do here is whether GetTempFileNameA passes or not.
+   We do not thoroughly test this function yet (specifically, whether
+   it behaves correctly when 'unique' is non zero)
+*/
+  ok((id=GetTempFileNameA(tmppath,"path",0,newdir)),"GetTempFileNameA failed\n");
+  sprintf(tmpstr,"pat%.4x.tmp",id & 0xffff);
+  sprintf(tmpstr1,"pat%x.tmp",id & 0xffff);
+  ok(lstrcmpiA(newdir+lstrlenA(tmppath),tmpstr)==0 ||
+     lstrcmpiA(newdir+lstrlenA(tmppath),tmpstr1)==0,
+     "GetTempPath returned '%s' which doesn't match '%s' or '%s'. id=%x\n",
+     newdir,tmpstr,tmpstr1,id);
+
+/* Find first valid drive letter that is neither newdir[0] nor curDrive */
+  drives = GetLogicalDrives() & ~(1<<(newdir[0]-'A'));
+  if( *curDrive != NOT_A_VALID_DRIVE)
+    drives &= ~(1<<(*curDrive-'A'));
+  if( drives)
+    for( *otherDrive='A'; (drives & 1) == 0; drives>>=1, (*otherDrive)++);
+  else
+    trace( "Could not find alternative drive, some tests will not be conducted.\n");
+
+/* Do some CreateDirectoryA tests */
+/* It would be nice to do test the SECURITY_ATTRIBUTES, but I don't
+   really understand how they work.
+   More formal tests should be done along with CreateFile tests
+*/
+  ok(CreateDirectoryA(newdir,NULL)==0,
+     "CreateDirectoryA succeeded even though a file of the same name exists\n");
+  ok(DeleteFileA(newdir),"Couldn't delete the temporary file we just created\n");
+  ok(CreateDirectoryA(newdir,NULL),"CreateDirectoryA failed\n");
+/* Create some files to test other functions.  Note, we will test CreateFileA
+   at some later point
+*/
+  sprintf(tmpstr,"%s\\%s",newdir,SHORTDIR);
+  ok(CreateDirectoryA(tmpstr,NULL),"CreateDirectoryA failed\n");
+  sprintf(tmpstr,"%s\\%s",newdir,LONGDIR);
+  ok(CreateDirectoryA(tmpstr,NULL),"CreateDirectoryA failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,SHORTFILE);
+  hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+                   CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+  ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+  ok(CloseHandle(hndl),"CloseHandle failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,LONGFILE);
+  hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+                   CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+  ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+  ok(CloseHandle(hndl),"CloseHandle failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",newdir,LONGDIR,SHORTFILE);
+  hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+                   CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+  ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+  ok(CloseHandle(hndl),"CloseHandle failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",newdir,LONGDIR,LONGFILE);
+  hndl=CreateFileA(tmpstr,GENERIC_WRITE,0,NULL,
+                   CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
+  ok(hndl!=INVALID_HANDLE_VALUE,"CreateFileA failed\n");
+  ok(CloseHandle(hndl),"CloseHandle failed\n");
+}
+
+/* Test GetCurrentDirectory & SetCurrentDirectory */
+static void test_CurrentDirectoryA(CHAR *origdir, CHAR *newdir)
+{
+  CHAR tmpstr[MAX_PATH],tmpstr1[MAX_PATH];
+  DWORD len,len1;
+/* Save the original directory, so that we can return to it at the end
+   of the test
+*/
+  len=GetCurrentDirectoryA(MAX_PATH,origdir);
+  ok(len!=0 && len < MAX_PATH,"GetCurrentDirectoryA failed\n");
+  ok(lstrcmpiA(origdir+(len-1),"\\")!=0,
+     "GetCurrentDirectoryA should not have a trailing \\\n");
+/* Make sure that CetCurrentDirectoryA doesn't overwrite the buffer when the
+   buffer size is too small to hold the current directory
+*/
+  lstrcpyA(tmpstr,"aaaaaaa");
+  len1=GetCurrentDirectoryA(len,tmpstr);
+  ok(len1==len+1, "GetCurrentDirectoryA returned %ld instead of %ld\n",len1,len+1);
+  ok(lstrcmpiA(tmpstr,"aaaaaaa")==0,
+     "GetCurrentDirectoryA should not have modified the buffer\n");
+/* SetCurrentDirectoryA shouldn't care whether the string has a
+   trailing '\\' or not
+*/
+  sprintf(tmpstr,"%s\\",newdir);
+  test_setdir(origdir,tmpstr,newdir,1,"check 1");
+  test_setdir(origdir,newdir,NULL,1,"check 2");
+/* Set the directory to the working area.  We just tested that this works,
+   so why check it again.
+*/
+  SetCurrentDirectoryA(newdir);
+/* Check that SetCurrentDirectory fails when a non-existent dir is specified */
+  sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,NONDIR_SHORT);
+  test_setdir(newdir,tmpstr,NULL,0,"check 3");
+/* Check that SetCurrentDirectory fails for a non-existent lond directory */
+  sprintf(tmpstr,"%s\\%s\\%s",newdir,SHORTDIR,NONDIR_LONG);
+  test_setdir(newdir,tmpstr,NULL,0,"check 4");
+/* Check that SetCurrentDirectory passes with a long directory */
+  sprintf(tmpstr,"%s\\%s",newdir,LONGDIR);
+  test_setdir(newdir,tmpstr,NULL,1,"check 5");
+/* Check that SetCurrentDirectory passes with a short relative directory */
+  sprintf(tmpstr,"%s",SHORTDIR);
+  sprintf(tmpstr1,"%s\\%s",newdir,SHORTDIR);
+  test_setdir(newdir,tmpstr,tmpstr1,1,"check 6");
+/* starting with a '.' */
+  sprintf(tmpstr,".\\%s",SHORTDIR);
+  test_setdir(newdir,tmpstr,tmpstr1,1,"check 7");
+/* Check that SetCurrentDirectory passes with a short relative directory */
+  sprintf(tmpstr,"%s",LONGDIR);
+  sprintf(tmpstr1,"%s\\%s",newdir,LONGDIR);
+  test_setdir(newdir,tmpstr,tmpstr1,1,"check 8");
+/* starting with a '.' */
+  sprintf(tmpstr,".\\%s",LONGDIR);
+  test_setdir(newdir,tmpstr,tmpstr1,1,"check 9");
+}
+
+/* Cleanup the mess we made while executing these tests */
+static void test_CleanupPathA(CHAR *origdir, CHAR *curdir)
+{
+  CHAR tmpstr[MAX_PATH];
+  sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,SHORTFILE);
+  ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,LONGFILE);
+  ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",curdir,LONGDIR,SHORTFILE);
+  ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",curdir,LONGDIR,LONGFILE);
+  ok(DeleteFileA(tmpstr),"DeleteFileA failed\n");
+  sprintf(tmpstr,"%s\\%s",curdir,SHORTDIR);
+  ok(RemoveDirectoryA(tmpstr),"RemoveDirectoryA failed\n");
+  sprintf(tmpstr,"%s\\%s",curdir,LONGDIR);
+  ok(RemoveDirectoryA(tmpstr),"RemoveDirectoryA failed\n");
+  ok(SetCurrentDirectoryA(origdir),"SetCurrentDirectoryA failed\n");
+  ok(RemoveDirectoryA(curdir),"RemoveDirectoryA failed\n");
+}
+
+/* This routine will test Get(Full|Short|Long)PathNameA */
+static void test_PathNameA(CHAR *curdir, CHAR curDrive, CHAR otherDrive)
+{
+  CHAR curdir_short[MAX_PATH],
+       longdir_short[MAX_PATH];
+  CHAR tmpstr[MAX_PATH],tmpstr1[MAX_PATH],tmpstr2[MAX_PATH];
+  LPSTR strptr;                 /*ptr to the filename portion of the path */
+  DWORD len;
+  INT i;
+  CHAR dir[MAX_PATH],eight[MAX_PATH],three[MAX_PATH];
+  SLpassfail passfail;
+
+/* Get the short form of the current directory */
+  ok((len=GetShortPathNameA(curdir,curdir_short,MAX_PATH)),
+     "GetShortPathNameA failed\n");
+  ok(!HAS_TRAIL_SLASH_A(curdir_short),
+     "GetShortPathNameA should not have a trailing \\\n");
+/* Get the short form of the absolute-path to LONGDIR */
+  sprintf(tmpstr,"%s\\%s",curdir_short,LONGDIR);
+  ok((len=GetShortPathNameA(tmpstr,longdir_short,MAX_PATH)),
+     "GetShortPathNameA failed\n");
+  ok(lstrcmpiA(longdir_short+(len-1),"\\")!=0,
+     "GetShortPathNameA should not have a trailing \\\n");
+
+  if (pGetLongPathNameA) {
+    DWORD rc1,rc2;
+    sprintf(tmpstr,"%s\\%s\\%s",curdir,LONGDIR,LONGFILE);
+    rc1=(*pGetLongPathNameA)(tmpstr,NULL,0);
+    rc2=(*pGetLongPathNameA)(curdir,NULL,0);
+    ok((rc1-strlen(tmpstr))==(rc2-strlen(curdir)),
+       "GetLongPathNameA: wrong return code, %ld instead of %d\n",
+       rc1, strlen(tmpstr)+1);
+
+    sprintf(dir,"%c:",curDrive);
+    rc1=(*pGetLongPathNameA)(dir,tmpstr,sizeof(tmpstr));
+    ok(strcmp(dir,tmpstr)==0,
+       "GetLongPathNameA: returned '%s' instead of '%s' (rc=%ld)\n",
+       tmpstr,dir,rc1);
+  }
+
+/* Check the cases where both file and directory exist first */
+/* Start with a 8.3 directory, 8.3 filename */
+  test_ValidPathA(curdir,SHORTDIR,SHORTFILE,tmpstr,NULL,"test1");
+  sprintf(tmpstr1,"%s\\%s\\%s",curdir_short,SHORTDIR,SHORTFILE);
+  ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+     "GetShortPathNameA returned '%s' instead of '%s'\n",tmpstr,tmpstr1);
+/* Now try a 8.3 directory, long file name */
+  test_ValidPathA(curdir,SHORTDIR,LONGFILE,tmpstr,NULL,"test2");
+  sprintf(tmpstr1,"%s\\%s",curdir_short,SHORTDIR);
+  test_LongtoShortA(tmpstr,tmpstr1,"PAT","test2");
+/* Next is a long directory, 8.3 file */
+  test_ValidPathA(curdir,LONGDIR,SHORTFILE,tmpstr,NULL,"test3");
+  sprintf(tmpstr1,"%s\\%s",longdir_short,SHORTFILE);
+  ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+     "GetShortPathNameA returned '%s' instead of '%s'\n",tmpstr,tmpstr1);
+/*Lastly a long directory, long file */
+  test_ValidPathA(curdir,LONGDIR,LONGFILE,tmpstr,NULL,"test4");
+  test_LongtoShortA(tmpstr,longdir_short,"PAT","test4");
+
+/* Now check all of the invalid file w/ valid directory combinations */
+/* Start with a 8.3 directory, 8.3 filename */
+  test_ValidPathA(curdir,SHORTDIR,NONFILE_SHORT,tmpstr,&passfail,"test5");
+  sprintf(tmpstr1,"%s\\%s\\%s",curdir_short,SHORTDIR,NONFILE_SHORT);
+  ok((passfail.shortlen==0 &&
+      (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+       passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+     (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+     "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+     passfail.shortlen,passfail.shorterror,tmpstr);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+  }
+/* Now try a 8.3 directory, long file name */
+  test_ValidPathA(curdir,SHORTDIR,NONFILE_LONG,tmpstr,&passfail,"test6");
+  ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+  ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+     passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+     !passfail.shorterror,
+     "GetShortPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+  }
+/* Next is a long directory, 8.3 file */
+  test_ValidPathA(curdir,LONGDIR,NONFILE_SHORT,tmpstr,&passfail,"test7");
+  sprintf(tmpstr2,"%s\\%s",curdir_short,LONGDIR);
+  GetShortPathNameA(tmpstr2,tmpstr1,MAX_PATH);
+  strcat(tmpstr1,"\\" NONFILE_SHORT);
+  ok((passfail.shortlen==0 &&
+      (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+       passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+     (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+     "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+     passfail.shortlen,passfail.shorterror,tmpstr);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+      "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+  }
+/*Lastly a long directory, long file */
+  test_ValidPathA(curdir,LONGDIR,NONFILE_LONG,tmpstr,&passfail,"test8");
+  ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+  ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+     passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+     !passfail.shorterror,
+     "GetShortPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetlongPathA should have returned 'ERROR_FILE_NOT_FOUND'\n");
+  }
+/* Now try again with directories that don't exist */
+/* 8.3 directory, 8.3 filename */
+  test_ValidPathA(curdir,NONDIR_SHORT,SHORTFILE,tmpstr,&passfail,"test9");
+  sprintf(tmpstr1,"%s\\%s\\%s",curdir_short,NONDIR_SHORT,SHORTFILE);
+  ok((passfail.shortlen==0 &&
+      (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+       passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+     (passfail.shortlen==strlen(tmpstr1) && lstrcmpiA(tmpstr,tmpstr1)==0),
+     "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+     passfail.shortlen,passfail.shorterror,tmpstr);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+       passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+       passfail.longerror);
+  }
+/* Now try a 8.3 directory, long file name */
+  test_ValidPathA(curdir,NONDIR_SHORT,LONGFILE,tmpstr,&passfail,"test10");
+  ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+  ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+     passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+     !passfail.shorterror,
+     "GetShortPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+      passfail.shorterror);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+       passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+       passfail.longerror);
+  }
+/* Next is a long directory, 8.3 file */
+  test_ValidPathA(curdir,NONDIR_LONG,SHORTFILE,tmpstr,&passfail,"test11");
+  ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+  ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+     passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+     !passfail.shorterror,
+     "GetShortPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+      passfail.shorterror);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+       passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+       passfail.longerror);
+  }
+/*Lastly a long directory, long file */
+  test_ValidPathA(curdir,NONDIR_LONG,LONGFILE,tmpstr,&passfail,"test12");
+  ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+  ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+     passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+     !passfail.shorterror,
+     "GetShortPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+      passfail.shorterror);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_PATH_NOT_FOUND ||
+       passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetLongPathA returned %ld and not 'ERROR_PATH_NOT_FOUND'\n",
+       passfail.longerror);
+  }
+/* Next try directories ending with '\\' */
+/* Existing Directories */
+  sprintf(tmpstr,"%s\\",SHORTDIR);
+  test_ValidPathA(curdir,"",tmpstr,tmpstr1,NULL,"test13");
+  sprintf(tmpstr,"%s\\",LONGDIR);
+  test_ValidPathA(curdir,"",tmpstr,tmpstr1,NULL,"test14");
+/* Non-existent directories */
+  sprintf(tmpstr,"%s\\",NONDIR_SHORT);
+  test_ValidPathA(curdir,"",tmpstr,tmpstr1,&passfail,"test15");
+  sprintf(tmpstr2,"%s\\%s",curdir,tmpstr);
+  ok((passfail.shortlen==0 &&
+      (passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+       passfail.shorterror==ERROR_FILE_NOT_FOUND)) ||
+     (passfail.shortlen==strlen(tmpstr2) && lstrcmpiA(tmpstr1,tmpstr2)==0),
+     "GetShortPathNameA error: len=%ld error=%ld tmpstr=[%s]\n",
+     passfail.shortlen,passfail.shorterror,tmpstr);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetLongPathA returned %ld and not 'ERROR_FILE_NOT_FOUND'\n",
+       passfail.longerror);
+  }
+  sprintf(tmpstr,"%s\\",NONDIR_LONG);
+  test_ValidPathA(curdir,"",tmpstr,tmpstr1,&passfail,"test16");
+  ok(passfail.shortlen==0,"GetShortPathNameA passed when it shouldn't have\n");
+  ok(passfail.shorterror==ERROR_PATH_NOT_FOUND ||
+     passfail.shorterror==ERROR_FILE_NOT_FOUND ||
+     !passfail.shorterror,
+     "GetShortPathA returned %ld and not 'ERROR_FILE_NOT_FOUND'\n",
+      passfail.shorterror);
+  if(pGetLongPathNameA) {
+    ok(passfail.longlen==0,"GetLongPathNameA passed when it shouldn't have\n");
+    ok(passfail.longerror==ERROR_FILE_NOT_FOUND,
+       "GetLongPathA returned %ld and not 'ERROR_FILE_NOT_FOUND'\n",
+       passfail.longerror);
+  }
+/* Test GetFullPathNameA with drive letters */
+  if( curDrive != NOT_A_VALID_DRIVE) {
+    sprintf(tmpstr,"%c:",curdir[0]);
+    ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr2,&strptr),
+       "GetFullPathNameA(%c:) failed\n", curdir[0]);
+    GetCurrentDirectoryA(MAX_PATH,tmpstr);
+    sprintf(tmpstr1,"%s\\",tmpstr);
+    ok(lstrcmpiA(tmpstr,tmpstr2)==0 || lstrcmpiA(tmpstr1,tmpstr2)==0,
+       "GetFullPathNameA(%c:) returned '%s' instead of '%s' or '%s'\n",
+       curdir[0],tmpstr2,tmpstr,tmpstr1);
+
+    sprintf(tmpstr,"%c:\\%s\\%s",curDrive,SHORTDIR,SHORTFILE);
+    ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+    ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+       "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+    ok(lstrcmpiA(SHORTFILE,strptr)==0,
+       "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+  }
+/* Without a leading slash, insert the current directory if on the current drive */
+  sprintf(tmpstr,"%c:%s\\%s",curdir[0],SHORTDIR,SHORTFILE);
+  ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,SHORTFILE);
+  ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+      "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+  ok(lstrcmpiA(SHORTFILE,strptr)==0,
+      "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+/* Otherwise insert the missing leading slash */
+  if( otherDrive != NOT_A_VALID_DRIVE) {
+    sprintf(tmpstr,"%c:%s\\%s",otherDrive,SHORTDIR,SHORTFILE);
+    ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed for %s\n", tmpstr);
+    sprintf(tmpstr,"%c:\\%s\\%s",otherDrive,SHORTDIR,SHORTFILE);
+    ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+       "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+    ok(lstrcmpiA(SHORTFILE,strptr)==0,
+       "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+  }
+/* Xilinx tools like to mix Unix and DOS formats, which Windows handles fine.
+   So test for them. */
+  if( curDrive != NOT_A_VALID_DRIVE) {
+    sprintf(tmpstr,"%c:/%s\\%s",curDrive,SHORTDIR,SHORTFILE);
+    ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+    sprintf(tmpstr,"%c:\\%s\\%s",curDrive,SHORTDIR,SHORTFILE);
+    ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+       "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+    ok(lstrcmpiA(SHORTFILE,strptr)==0,
+       "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+  }
+/**/
+  sprintf(tmpstr,"%c:%s/%s",curdir[0],SHORTDIR,SHORTFILE);
+  ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+  sprintf(tmpstr,"%s\\%s\\%s",curdir,SHORTDIR,SHORTFILE);
+  ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+      "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+  ok(lstrcmpiA(SHORTFILE,strptr)==0,
+      "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+/* Windows will insert a drive letter in front of an absolute UNIX path, but
+    Wine probably shouldn't. */
+  sprintf(tmpstr,"/%s/%s",SHORTDIR,SHORTFILE);
+  ok(GetFullPathNameA(tmpstr,MAX_PATH,tmpstr1,&strptr),"GetFullPathNameA failed\n");
+  todo_wine {
+    if( curDrive != NOT_A_VALID_DRIVE) {
+      sprintf(tmpstr,"C:\\%s\\%s",SHORTDIR,SHORTFILE);
+      ok(lstrcmpiA(tmpstr,tmpstr1)==0,
+         "GetFullPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+    }
+  }
+/* This passes in Wine because it still contains the pointer from the previous test */
+  ok(lstrcmpiA(SHORTFILE,strptr)==0,
+      "GetFullPathNameA returned part '%s' instead of '%s'\n",strptr,SHORTFILE);
+
+/* Now try some relative paths */
+  ok(GetShortPathNameA(LONGDIR,tmpstr,MAX_PATH),"GetShortPathNameA failed\n");
+  test_SplitShortPathA(tmpstr,dir,eight,three);
+  if(pGetLongPathNameA) {
+    ok(pGetLongPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetLongPathNameA failed\n");
+    ok(lstrcmpiA(tmpstr1,LONGDIR)==0,
+       "GetLongPathNameA returned '%s' instead of '%s'\n",tmpstr1,LONGDIR);
+  }
+  sprintf(tmpstr,".\\%s",LONGDIR);
+  ok(GetShortPathNameA(tmpstr,tmpstr1,MAX_PATH),"GetShortPathNameA failed\n");
+  test_SplitShortPathA(tmpstr1,dir,eight,three);
+  ok(lstrcmpiA(dir,".")==0 || dir[0]=='\0',
+     "GetShortPathNameA did not keep relative directory [%s]\n",tmpstr1);
+  if(pGetLongPathNameA) {
+    ok(pGetLongPathNameA(tmpstr1,tmpstr1,MAX_PATH),"GetLongPathNameA failed %s\n",
+       tmpstr);
+    ok(lstrcmpiA(tmpstr1,tmpstr)==0,
+       "GetLongPathNameA returned '%s' instead of '%s'\n",tmpstr1,tmpstr);
+  }
+/* Check out Get*PathNameA on some funny characters */
+  for(i=0;i<lstrlenA(funny_chars);i++) {
+    INT valid,todo;
+    valid=(is_char_ok[i]=='0') ? 0 : 1;
+    todo=(wine_todo[i]=='0') ? 0 : 1;
+    sprintf(tmpstr1,"check%d-1",i);
+    sprintf(tmpstr,"file%c000.ext",funny_chars[i]);
+    test_FunnyChars(curdir,tmpstr,valid,todo,tmpstr1);
+    sprintf(tmpstr1,"check%d-2",i);
+    sprintf(tmpstr,"file000.e%ct",funny_chars[i]);
+    test_FunnyChars(curdir,tmpstr,valid,todo,tmpstr1);
+    sprintf(tmpstr1,"check%d-3",i);
+    sprintf(tmpstr,"%cfile000.ext",funny_chars[i]);
+    test_FunnyChars(curdir,tmpstr,valid,todo,tmpstr1);
+    sprintf(tmpstr1,"check%d-4",i);
+    sprintf(tmpstr,"file000%c.ext",funny_chars[i]);
+    test_FunnyChars(curdir,tmpstr,valid,todo,tmpstr1);
+    sprintf(tmpstr1,"check%d-5",i);
+    sprintf(tmpstr,"Long %c File",funny_chars[i]);
+    test_FunnyChars(curdir,tmpstr,valid,0,tmpstr1);
+    sprintf(tmpstr1,"check%d-6",i);
+    sprintf(tmpstr,"%c Long File",funny_chars[i]);
+    test_FunnyChars(curdir,tmpstr,valid,0,tmpstr1);
+    sprintf(tmpstr1,"check%d-7",i);
+    sprintf(tmpstr,"Long File %c",funny_chars[i]);
+    test_FunnyChars(curdir,tmpstr,valid,0,tmpstr1);
+  }
+}
+
+static void test_GetTempPathA(char* tmp_dir)
+{
+    DWORD len, len_with_null;
+    char buf[MAX_PATH];
+
+    len_with_null = strlen(tmp_dir) + 1;
+
+    lstrcpyA(buf, "foo");
+    len = GetTempPathA(MAX_PATH, buf);
+    ok(len <= MAX_PATH, "should fit into MAX_PATH\n");
+    ok(lstrcmpiA(buf, tmp_dir) == 0, "expected [%s], got [%s]\n",tmp_dir,buf);
+    ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+
+    /* Some versions of Windows touch the buffer, some don't so we don't
+     * test that. Also, NT sometimes exagerates the required buffer size
+     * so we cannot test for an exact match. Finally, the
+     * 'len_with_null - 1' case is so buggy on Windows it's not testable.
+     * For instance in some cases Win98 returns len_with_null - 1 instead
+     * of len_with_null.
+     */
+    len = GetTempPathA(1, buf);
+    ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+    len = GetTempPathA(0, NULL);
+    ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+    /* The call above gave us the buffer size that Windows thinks is needed
+     * so the next call should work
+     */
+    lstrcpyA(buf, "foo");
+    len = GetTempPathA(len, buf);
+    ok(lstrcmpiA(buf, tmp_dir) == 0, "expected [%s], got [%s]\n",tmp_dir,buf);
+    ok(len == strlen(buf), "returned length should be equal to the length of string\n");
+}
+
+static void test_GetTempPathW(char* tmp_dir)
+{
+    DWORD len, len_with_null;
+    WCHAR buf[MAX_PATH];
+    WCHAR tmp_dirW[MAX_PATH];
+    static const WCHAR fooW[] = {'f','o','o',0};
+
+    MultiByteToWideChar(CP_ACP,0,tmp_dir,-1,tmp_dirW,sizeof(tmp_dirW)/sizeof(*tmp_dirW));
+    len_with_null = lstrlenW(tmp_dirW) + 1;
+
+    /* This one is different from ANSI version: ANSI version doesn't
+     * touch the buffer, unicode version usually truncates the buffer
+     * to zero size. NT still exagerates the required buffer size
+     * sometimes so we cannot test for an exact match. Finally, the
+     * 'len_with_null - 1' case is so buggy on Windows it's not testable.
+     * For instance on NT4 it will sometimes return a path without the
+     * trailing '\\' and sometimes return an error.
+     */
+
+    lstrcpyW(buf, fooW);
+    len = GetTempPathW(MAX_PATH, buf);
+    if (len==0 && GetLastError()==ERROR_CALL_NOT_IMPLEMENTED)
+        return;
+    ok(lstrcmpiW(buf, tmp_dirW) == 0, "GetTempPathW returned an incorrect temporary path\n");
+    ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+
+    lstrcpyW(buf, fooW);
+    len = GetTempPathW(1, buf);
+    ok(buf[0] == 0, "unicode version should truncate the buffer to zero size\n");
+    ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+    len = GetTempPathW(0, NULL);
+    ok(len >= len_with_null, "Expected >= %lu, got %lu\n", len_with_null, len);
+
+    lstrcpyW(buf, fooW);
+    len = GetTempPathW(len, buf);
+    ok(lstrcmpiW(buf, tmp_dirW) == 0, "GetTempPathW returned an incorrect temporary path\n");
+    ok(len == lstrlenW(buf), "returned length should be equal to the length of string\n");
+}
+
+static void test_GetTempPath(void)
+{
+    char save_TMP[MAX_PATH];
+    char windir[MAX_PATH];
+    char buf[MAX_PATH];
+
+    GetEnvironmentVariableA("TMP", save_TMP, sizeof(save_TMP));
+
+    /* test default configuration */
+    trace("TMP=%s\n", save_TMP);
+    strcpy(buf,save_TMP);
+    if (buf[strlen(buf)-1]!='\\')
+        strcat(buf,"\\");
+    test_GetTempPathA(buf);
+    test_GetTempPathW(buf);
+
+    /* TMP=C:\WINDOWS */
+    GetWindowsDirectoryA(windir, sizeof(windir));
+    SetEnvironmentVariableA("TMP", windir);
+    GetEnvironmentVariableA("TMP", buf, sizeof(buf));
+    trace("TMP=%s\n", buf);
+    strcat(windir,"\\");
+    test_GetTempPathA(windir);
+    test_GetTempPathW(windir);
+
+    /* TMP=C:\ */
+    GetWindowsDirectoryA(windir, sizeof(windir));
+    windir[3] = 0;
+    SetEnvironmentVariableA("TMP", windir);
+    GetEnvironmentVariableA("TMP", buf, sizeof(buf));
+    trace("TMP=%s\n", buf);
+    test_GetTempPathA(windir);
+    test_GetTempPathW(windir);
+
+    /* TMP=C: i.e. use current working directory of the specified drive */
+    GetWindowsDirectoryA(windir, sizeof(windir));
+    SetCurrentDirectoryA(windir);
+    windir[2] = 0;
+    SetEnvironmentVariableA("TMP", windir);
+    GetEnvironmentVariableA("TMP", buf, sizeof(buf));
+    trace("TMP=%s\n", buf);
+    GetWindowsDirectoryA(windir, sizeof(windir));
+    strcat(windir,"\\");
+    test_GetTempPathA(windir);
+    test_GetTempPathW(windir);
+
+    SetEnvironmentVariableA("TMP", save_TMP);
+}
+
+START_TEST(path)
+{
+    CHAR origdir[MAX_PATH],curdir[MAX_PATH], curDrive, otherDrive;
+    pGetLongPathNameA = (void*)GetProcAddress( GetModuleHandleA("kernel32.dll"),
+                                               "GetLongPathNameA" );
+    test_InitPathA(curdir, &curDrive, &otherDrive);
+    test_CurrentDirectoryA(origdir,curdir);
+    test_PathNameA(curdir, curDrive, otherDrive);
+    test_CleanupPathA(origdir,curdir);
+    test_GetTempPath();
+}
diff --git a/reactos/apps/tests/kernel32/pipe.c b/reactos/apps/tests/kernel32/pipe.c
new file mode 100644 (file)
index 0000000..b86b985
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * Unit tests for named pipe functions in Wine
+ *
+ * Copyright (c) 2002 Dan Kegel
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winsock.h>
+
+#ifndef STANDALONE
+#include "wine/test.h"
+#else
+#include <assert.h>
+#define START_TEST(name) main(int argc, char **argv)
+#define ok(condition, msg) \
+       do { \
+               if(!(condition)) \
+               { \
+                       fprintf(stderr,"failed at %d\n",__LINE__); \
+                       exit(0); \
+               } \
+       } while(0)
+
+#define todo_wine
+#endif
+
+#include <wtypes.h>
+#include <winerror.h>
+
+#define PIPENAME "\\\\.\\PiPe\\tests_" __FILE__
+
+void test_CreateNamedPipe(void)
+{
+    HANDLE hnp;
+    HANDLE hFile;
+    const char obuf[] = "Bit Bucket";
+    char ibuf[32];
+    DWORD written;
+    DWORD readden;
+
+    trace("test_CreateNamedPipe starting\n");
+    /* Bad parameter checks */
+    hnp = CreateNamedPipe("not a named pipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+
+    if (hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) {
+        /* Is this the right way to notify user of skipped tests? */
+        ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
+            "CreateNamedPipe not supported on this platform, skipping tests.\n");
+        return;
+    }
+    ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_NAME,
+        "CreateNamedPipe should fail if name doesn't start with \\\\.\\pipe\n");
+
+    hnp = CreateNamedPipe(NULL,
+        PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        1, 1024, 1024, NMPWAIT_USE_DEFAULT_WAIT, NULL);
+    ok(hnp == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PATH_NOT_FOUND,
+        "CreateNamedPipe should fail if name is NULL\n");
+
+    hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(hFile == INVALID_HANDLE_VALUE
+        && GetLastError() == ERROR_FILE_NOT_FOUND,
+        "connecting to nonexistent named pipe should fail with ERROR_FILE_NOT_FOUND\n");
+
+    /* Functional checks */
+
+    hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed\n");
+
+    /* don't try to do i/o if one side couldn't be opened, as it hangs */
+    if (hFile != INVALID_HANDLE_VALUE) {
+        HANDLE hFile2;
+
+        /* Make sure we can read and write a few bytes in both directions */
+        memset(ibuf, 0, sizeof(ibuf));
+        ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL), "WriteFile\n");
+        ok(written == sizeof(obuf), "write file len\n");
+        ok(ReadFile(hFile, ibuf, sizeof(obuf), &readden, NULL), "ReadFile\n");
+        ok(readden == sizeof(obuf), "read file len\n");
+        ok(memcmp(obuf, ibuf, written) == 0, "content check\n");
+
+        memset(ibuf, 0, sizeof(ibuf));
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile\n");
+        ok(written == sizeof(obuf), "write file len\n");
+        ok(ReadFile(hnp, ibuf, sizeof(obuf), &readden, NULL), "ReadFile\n");
+        ok(readden == sizeof(obuf), "read file len\n");
+        ok(memcmp(obuf, ibuf, written) == 0, "content check\n");
+
+        /* Picky conformance tests */
+
+        /* Verify that you can't connect to pipe again
+         * until server calls DisconnectNamedPipe+ConnectNamedPipe
+         * or creates a new pipe
+         * case 1: other client not yet closed
+         */
+        hFile2 = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+        ok(hFile2 == INVALID_HANDLE_VALUE,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail\n");
+        ok(GetLastError() == ERROR_PIPE_BUSY,
+            "connecting to named pipe before other client closes should fail with ERROR_PIPE_BUSY\n");
+
+        ok(CloseHandle(hFile), "CloseHandle\n");
+
+        /* case 2: other client already closed */
+        hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+        ok(hFile == INVALID_HANDLE_VALUE,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail\n");
+        ok(GetLastError() == ERROR_PIPE_BUSY,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail with ERROR_PIPE_BUSY\n");
+
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+
+        /* case 3: server has called DisconnectNamedPipe but not ConnectNamed Pipe */
+        hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+        ok(hFile == INVALID_HANDLE_VALUE,
+            "connecting to named pipe after other client closes but before DisconnectNamedPipe should fail\n");
+        ok(GetLastError() == ERROR_PIPE_BUSY,
+            "connecting to named pipe after other client closes but before ConnectNamedPipe should fail with ERROR_PIPE_BUSY\n");
+
+        /* to be complete, we'd call ConnectNamedPipe here and loop,
+         * but by default that's blocking, so we'd either have
+         * to turn on the uncommon nonblocking mode, or
+         * use another thread.
+         */
+    }
+
+    ok(CloseHandle(hnp), "CloseHandle\n");
+
+    trace("test_CreateNamedPipe returning\n");
+}
+
+void test_CreateNamedPipe_instances_must_match(void)
+{
+    HANDLE hnp, hnp2;
+
+    /* Check no mismatch */
+    hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    hnp2 = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp2 != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    ok(CloseHandle(hnp), "CloseHandle\n");
+    ok(CloseHandle(hnp2), "CloseHandle\n");
+
+    /* Check nMaxInstances */
+    hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    hnp2 = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp2 == INVALID_HANDLE_VALUE
+        && GetLastError() == ERROR_PIPE_BUSY, "nMaxInstances not obeyed\n");
+
+    ok(CloseHandle(hnp), "CloseHandle\n");
+
+    /* Check PIPE_ACCESS_* */
+    hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    hnp2 = CreateNamedPipe(PIPENAME, PIPE_ACCESS_INBOUND, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp2 == INVALID_HANDLE_VALUE
+        && GetLastError() == ERROR_ACCESS_DENIED, "PIPE_ACCESS_* mismatch allowed\n");
+
+    ok(CloseHandle(hnp), "CloseHandle\n");
+
+    /* etc, etc */
+}
+
+/** implementation of alarm() */
+static DWORD CALLBACK alarmThreadMain(LPVOID arg)
+{
+    DWORD timeout = (DWORD) arg;
+    trace("alarmThreadMain\n");
+    Sleep(timeout);
+    ok(FALSE, "alarm\n");
+    ExitProcess(1);
+    return 1;
+}
+
+HANDLE hnp = INVALID_HANDLE_VALUE;
+
+/** Trivial byte echo server - disconnects after each session */
+static DWORD CALLBACK serverThreadMain1(LPVOID arg)
+{
+    int i;
+
+    trace("serverThreadMain1 start\n");
+    /* Set up a simple echo server */
+    hnp = CreateNamedPipe(PIPENAME "serverThreadMain1", PIPE_ACCESS_DUPLEX,
+        PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+    for (i = 0; ; i++) {
+        char buf[512];
+        DWORD written;
+        DWORD readden;
+        DWORD success;
+
+        /* Wait for client to connect */
+        trace("Server calling ConnectNamedPipe...\n");
+        ok(ConnectNamedPipe(hnp, NULL)
+            || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe\n");
+        trace("ConnectNamedPipe returned.\n");
+
+        /* Echo bytes once */
+        memset(buf, 0, sizeof(buf));
+
+        trace("Server reading...\n");
+        success = ReadFile(hnp, buf, sizeof(buf), &readden, NULL);
+        trace("Server done reading.\n");
+        ok(success, "ReadFile\n");
+        ok(readden, "short read\n");
+
+        trace("Server writing...\n");
+        ok(WriteFile(hnp, buf, readden, &written, NULL), "WriteFile\n");
+        trace("Server done writing.\n");
+        ok(written == readden, "write file len\n");
+
+        /* finish this connection, wait for next one */
+        ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
+        trace("Server done flushing.\n");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+        trace("Server done disconnecting.\n");
+    }
+}
+
+/** Trivial byte echo server - closes after each connection */
+static DWORD CALLBACK serverThreadMain2(LPVOID arg)
+{
+    int i;
+    HANDLE hnpNext = 0;
+
+    trace("serverThreadMain2\n");
+    /* Set up a simple echo server */
+    hnp = CreateNamedPipe(PIPENAME "serverThreadMain2", PIPE_ACCESS_DUPLEX,
+        PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 2,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    for (i = 0; ; i++) {
+        char buf[512];
+        DWORD written;
+        DWORD readden;
+        DWORD success;
+
+        /* Wait for client to connect */
+        trace("Server calling ConnectNamedPipe...\n");
+        ok(ConnectNamedPipe(hnp, NULL)
+            || GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe\n");
+        trace("ConnectNamedPipe returned.\n");
+
+        /* Echo bytes once */
+        memset(buf, 0, sizeof(buf));
+
+        trace("Server reading...\n");
+        success = ReadFile(hnp, buf, sizeof(buf), &readden, NULL);
+        trace("Server done reading.\n");
+        ok(success, "ReadFile\n");
+
+        trace("Server writing...\n");
+        ok(WriteFile(hnp, buf, readden, &written, NULL), "WriteFile\n");
+        trace("Server done writing.\n");
+        ok(written == readden, "write file len\n");
+
+        /* finish this connection, wait for next one */
+        ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+
+        /* Set up next echo server */
+        hnpNext =
+            CreateNamedPipe(PIPENAME "serverThreadMain2", PIPE_ACCESS_DUPLEX,
+            PIPE_TYPE_BYTE | PIPE_WAIT,
+            /* nMaxInstances */ 2,
+            /* nOutBufSize */ 1024,
+            /* nInBufSize */ 1024,
+            /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+            /* lpSecurityAttrib */ NULL);
+
+        ok(hnpNext != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+        ok(CloseHandle(hnp), "CloseHandle\n");
+        hnp = hnpNext;
+    }
+}
+
+/** Trivial byte echo server - uses overlapped named pipe calls */
+static DWORD CALLBACK serverThreadMain3(LPVOID arg)
+{
+    int i;
+    HANDLE hEvent;
+
+    trace("serverThreadMain3\n");
+    /* Set up a simple echo server */
+    hnp = CreateNamedPipe(PIPENAME "serverThreadMain3", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+        PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    hEvent = CreateEvent(NULL,  /* security attribute */
+        TRUE,                   /* manual reset event */
+        FALSE,                  /* initial state */
+        NULL);                  /* name */
+    ok(hEvent != NULL, "CreateEvent\n");
+
+    for (i = 0; ; i++) {
+        char buf[512];
+        DWORD written;
+        DWORD readden;
+        DWORD dummy;
+        DWORD success;
+        OVERLAPPED oOverlap;
+        int letWFSOEwait = (i & 2);
+        int letGORwait = (i & 1);
+       DWORD err;
+
+        memset(&oOverlap, 0, sizeof(oOverlap));
+        oOverlap.hEvent = hEvent;
+
+        /* Wait for client to connect */
+        trace("Server calling overlapped ConnectNamedPipe...\n");
+        success = ConnectNamedPipe(hnp, &oOverlap);
+        err = GetLastError();
+        ok(success || err == ERROR_IO_PENDING
+            || err == ERROR_PIPE_CONNECTED, "overlapped ConnectNamedPipe\n");
+        trace("overlapped ConnectNamedPipe returned.\n");
+        if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+            ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait ConnectNamedPipe\n");
+        success = GetOverlappedResult(hnp, &oOverlap, &dummy, letGORwait);
+       if (!letGORwait && !letWFSOEwait && !success) {
+           ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult\n");
+           success = GetOverlappedResult(hnp, &oOverlap, &dummy, TRUE);
+       }
+       ok(success, "GetOverlappedResult ConnectNamedPipe\n");
+        trace("overlapped ConnectNamedPipe operation complete.\n");
+
+        /* Echo bytes once */
+        memset(buf, 0, sizeof(buf));
+
+        trace("Server reading...\n");
+        success = ReadFile(hnp, buf, sizeof(buf), NULL, &oOverlap);
+        trace("Server ReadFile returned...\n");
+        err = GetLastError();
+        ok(success || err == ERROR_IO_PENDING, "overlapped ReadFile\n");
+        trace("overlapped ReadFile returned.\n");
+        if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+            ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait ReadFile\n");
+        success = GetOverlappedResult(hnp, &oOverlap, &readden, letGORwait);
+       if (!letGORwait && !letWFSOEwait && !success) {
+           ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult\n");
+           success = GetOverlappedResult(hnp, &oOverlap, &readden, TRUE);
+       }
+        trace("Server done reading.\n");
+        ok(success, "overlapped ReadFile\n");
+
+        trace("Server writing...\n");
+        success = WriteFile(hnp, buf, readden, NULL, &oOverlap);
+        trace("Server WriteFile returned...\n");
+        err = GetLastError();
+        ok(success || err == ERROR_IO_PENDING, "overlapped WriteFile\n");
+        trace("overlapped WriteFile returned.\n");
+        if (!success && (err == ERROR_IO_PENDING) && letWFSOEwait)
+            ok(WaitForSingleObjectEx(hEvent, INFINITE, TRUE) == 0, "wait WriteFile\n");
+        success = GetOverlappedResult(hnp, &oOverlap, &written, letGORwait);
+       if (!letGORwait && !letWFSOEwait && !success) {
+           ok(GetLastError() == ERROR_IO_INCOMPLETE, "GetOverlappedResult\n");
+           success = GetOverlappedResult(hnp, &oOverlap, &written, TRUE);
+       }
+        trace("Server done writing.\n");
+        ok(success, "overlapped WriteFile\n");
+        ok(written == readden, "write file len\n");
+
+        /* finish this connection, wait for next one */
+        ok(FlushFileBuffers(hnp), "FlushFileBuffers\n");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe\n");
+    }
+}
+
+static void exercizeServer(const char *pipename, HANDLE serverThread)
+{
+    int i;
+
+    trace("exercizeServer starting\n");
+    for (i = 0; i < 8; i++) {
+        HANDLE hFile=INVALID_HANDLE_VALUE;
+        const char obuf[] = "Bit Bucket";
+        char ibuf[32];
+        DWORD written;
+        DWORD readden;
+        int loop;
+
+        for (loop = 0; loop < 3; loop++) {
+           DWORD err;
+            trace("Client connecting...\n");
+            /* Connect to the server */
+            hFile = CreateFileA(pipename, GENERIC_READ | GENERIC_WRITE, 0,
+                NULL, OPEN_EXISTING, 0, 0);
+            if (hFile != INVALID_HANDLE_VALUE)
+                break;
+           err = GetLastError();
+           if (loop == 0)
+               ok(err == ERROR_PIPE_BUSY || err == ERROR_FILE_NOT_FOUND, "connecting to pipe\n");
+           else
+               ok(err == ERROR_PIPE_BUSY, "connecting to pipe\n");
+            trace("connect failed, retrying\n");
+            Sleep(200);
+        }
+        ok(hFile != INVALID_HANDLE_VALUE, "client opening named pipe\n");
+
+        /* Make sure it can echo */
+        memset(ibuf, 0, sizeof(ibuf));
+        trace("Client writing...\n");
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile to client end of pipe\n");
+        ok(written == sizeof(obuf), "write file len\n");
+        trace("Client reading...\n");
+        ok(ReadFile(hFile, ibuf, sizeof(obuf), &readden, NULL), "ReadFile from client end of pipe\n");
+        ok(readden == sizeof(obuf), "read file len\n");
+        ok(memcmp(obuf, ibuf, written) == 0, "content check\n");
+
+        trace("Client closing...\n");
+        ok(CloseHandle(hFile), "CloseHandle\n");
+    }
+
+    ok(TerminateThread(serverThread, 0), "TerminateThread\n");
+    CloseHandle(hnp);
+    trace("exercizeServer returning\n");
+}
+
+void test_NamedPipe_2(void)
+{
+    HANDLE serverThread;
+    DWORD serverThreadId;
+    HANDLE alarmThread;
+    DWORD alarmThreadId;
+
+    trace("test_NamedPipe_2 starting\n");
+    /* Set up a ten second timeout */
+    alarmThread = CreateThread(NULL, 0, alarmThreadMain, (void *) 10000, 0, &alarmThreadId);
+
+    /* The servers we're about to exercize do try to clean up carefully,
+     * but to reduce the change of a test failure due to a pipe handle
+     * leak in the test code, we'll use a different pipe name for each server.
+     */
+
+    /* Try server #1 */
+    serverThread = CreateThread(NULL, 0, serverThreadMain1, 0, 0, &serverThreadId);
+    ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread\n");
+    exercizeServer(PIPENAME "serverThreadMain1", serverThread);
+
+    /* Try server #2 */
+    serverThread = CreateThread(NULL, 0, serverThreadMain2, 0, 0, &serverThreadId);
+    ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread\n");
+    exercizeServer(PIPENAME "serverThreadMain2", serverThread);
+
+    if( 0 ) /* overlapped pipe server doesn't work yet - it randomly fails */
+    {
+    /* Try server #3 */
+    serverThread = CreateThread(NULL, 0, serverThreadMain3, 0, 0, &serverThreadId);
+    ok(serverThread != INVALID_HANDLE_VALUE, "CreateThread\n");
+    exercizeServer(PIPENAME "serverThreadMain3", serverThread);
+    }
+
+    ok(TerminateThread(alarmThread, 0), "TerminateThread\n");
+    trace("test_NamedPipe_2 returning\n");
+}
+
+void test_DisconnectNamedPipe(void)
+{
+    HANDLE hnp;
+    HANDLE hFile;
+    const char obuf[] = "Bit Bucket";
+    char ibuf[32];
+    DWORD written;
+    DWORD readden;
+
+    hnp = CreateNamedPipe(PIPENAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT,
+        /* nMaxInstances */ 1,
+        /* nOutBufSize */ 1024,
+        /* nInBufSize */ 1024,
+        /* nDefaultWait */ NMPWAIT_USE_DEFAULT_WAIT,
+        /* lpSecurityAttrib */ NULL);
+    ok(hnp != INVALID_HANDLE_VALUE, "CreateNamedPipe failed\n");
+
+    ok(WriteFile(hnp, obuf, sizeof(obuf), &written, NULL) == 0
+        && GetLastError() == ERROR_PIPE_LISTENING, "WriteFile to not-yet-connected pipe\n");
+    ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL) == 0
+        && GetLastError() == ERROR_PIPE_LISTENING, "ReadFile from not-yet-connected pipe\n");
+
+    hFile = CreateFileA(PIPENAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed\n");
+
+    /* don't try to do i/o if one side couldn't be opened, as it hangs */
+    if (hFile != INVALID_HANDLE_VALUE) {
+
+        /* see what happens if server calls DisconnectNamedPipe
+         * when there are bytes in the pipe
+         */
+
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL), "WriteFile\n");
+        ok(written == sizeof(obuf), "write file len\n");
+        ok(DisconnectNamedPipe(hnp), "DisconnectNamedPipe while messages waiting\n");
+        ok(WriteFile(hFile, obuf, sizeof(obuf), &written, NULL) == 0
+            && GetLastError() == ERROR_PIPE_NOT_CONNECTED, "WriteFile to disconnected pipe\n");
+        ok(ReadFile(hnp, ibuf, sizeof(ibuf), &readden, NULL) == 0
+            && GetLastError() == ERROR_PIPE_NOT_CONNECTED,
+            "ReadFile from disconnected pipe with bytes waiting\n");
+        ok(CloseHandle(hFile), "CloseHandle\n");
+    }
+
+    ok(CloseHandle(hnp), "CloseHandle\n");
+
+}
+
+START_TEST(pipe)
+{
+    trace("test 1 of 4:\n");
+    test_DisconnectNamedPipe();
+    trace("test 2 of 4:\n");
+    test_CreateNamedPipe_instances_must_match();
+    trace("test 3 of 4:\n");
+    test_NamedPipe_2();
+    trace("test 4 of 4:\n");
+    test_CreateNamedPipe();
+    trace("all tests done\n");
+}
diff --git a/reactos/apps/tests/kernel32/process.c b/reactos/apps/tests/kernel32/process.c
new file mode 100644 (file)
index 0000000..0d745c8
--- /dev/null
@@ -0,0 +1,1202 @@
+/*
+ * Unit test suite for CreateProcess function.
+ *
+ * Copyright 2002 Eric Pouech
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "wincon.h"
+#include "winnls.h"
+
+static char     base[MAX_PATH];
+static char     selfname[MAX_PATH];
+static char     resfile[MAX_PATH];
+
+static int      myARGC;
+static char**   myARGV;
+
+/* As some environment variables get very long on Unix, we only test for
+ * the first 127 bytes.
+ * Note that increasing this value past 256 may exceed the buffer size
+ * limitations of the *Profile functions (at least on Wine).
+ */
+#define MAX_LISTED_ENV_VAR      128
+
+/* ---------------- portable memory allocation thingie */
+
+static char     memory[1024*32];
+static char*    memory_index = memory;
+
+static char*    grab_memory(size_t len)
+{
+    char*       ret = memory_index;
+    /* align on dword */
+    len = (len + 3) & ~3;
+    memory_index += len;
+    assert(memory_index <= memory + sizeof(memory));
+    return ret;
+}
+
+static void     release_memory(void)
+{
+    memory_index = memory;
+}
+
+/* ---------------- simplistic tool to encode/decode strings (to hide \ " ' and such) */
+
+static const char* encodeA(const char* str)
+{
+    char*       ptr;
+    size_t      len,i;
+
+    if (!str) return "";
+    len = strlen(str) + 1;
+    ptr = grab_memory(len * 2 + 1);
+    for (i = 0; i < len; i++)
+        sprintf(&ptr[i * 2], "%02x", (unsigned char)str[i]);
+    ptr[2 * len] = '\0';
+    return ptr;
+}
+
+static const char* encodeW(const WCHAR* str)
+{
+    char*       ptr;
+    size_t      len,i;
+
+    if (!str) return "";
+    len = lstrlenW(str) + 1;
+    ptr = grab_memory(len * 4 + 1);
+    assert(ptr);
+    for (i = 0; i < len; i++)
+        sprintf(&ptr[i * 4], "%04x", (unsigned int)(unsigned short)str[i]);
+    ptr[4 * len] = '\0';
+    return ptr;
+}
+
+static unsigned decode_char(char c)
+{
+    if (c >= '0' && c <= '9') return c - '0';
+    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+    assert(c >= 'A' && c <= 'F');
+    return c - 'A' + 10;
+}
+
+static char*    decodeA(const char* str)
+{
+    char*       ptr;
+    size_t      len,i;
+
+    len = strlen(str) / 2;
+    if (!len--) return NULL;
+    ptr = grab_memory(len + 1);
+    for (i = 0; i < len; i++)
+        ptr[i] = (decode_char(str[2 * i]) << 4) | decode_char(str[2 * i + 1]);
+    ptr[len] = '\0';
+    return ptr;
+}
+
+#if 0
+/* This will be needed to decode Unicode strings saved by the child process
+ * when we test Unicode functions.
+ */
+static WCHAR*   decodeW(const char* str)
+{
+    size_t      len;
+    WCHAR*      ptr;
+    int         i;
+
+    len = strlen(str) / 4;
+    if (!len--) return NULL;
+    ptr = (WCHAR*)grab_memory(len * 2 + 1);
+    for (i = 0; i < len; i++)
+        ptr[i] = (decode_char(str[4 * i]) << 12) |
+            (decode_char(str[4 * i + 1]) << 8) |
+            (decode_char(str[4 * i + 2]) << 4) |
+            (decode_char(str[4 * i + 3]) << 0);
+    ptr[len] = '\0';
+    return ptr;
+}
+#endif
+
+/******************************************************************
+ *             init
+ *
+ * generates basic information like:
+ *      base:           absolute path to curr dir
+ *      selfname:       the way to reinvoke ourselves
+ */
+static int     init(void)
+{
+    myARGC = winetest_get_mainargs( &myARGV );
+    if (!GetCurrentDirectoryA(sizeof(base), base)) return 0;
+    strcpy(selfname, myARGV[0]);
+    return 1;
+}
+
+/******************************************************************
+ *             get_file_name
+ *
+ * generates an absolute file_name for temporary file
+ *
+ */
+static void     get_file_name(char* buf)
+{
+    char        path[MAX_PATH];
+
+    buf[0] = '\0';
+    GetTempPathA(sizeof(path), path);
+    GetTempFileNameA(path, "wt", 0, buf);
+}
+
+/******************************************************************
+ *             static void     childPrintf
+ *
+ */
+static void     childPrintf(HANDLE h, const char* fmt, ...)
+{
+    va_list     valist;
+    char        buffer[1024+4*MAX_LISTED_ENV_VAR];
+    DWORD       w;
+
+    va_start(valist, fmt);
+    vsprintf(buffer, fmt, valist);
+    va_end(valist);
+    WriteFile(h, buffer, strlen(buffer), &w, NULL);
+}
+
+
+/******************************************************************
+ *             doChild
+ *
+ * output most of the information in the child process
+ */
+static void     doChild(const char* file, const char* option)
+{
+    STARTUPINFOA        siA;
+    STARTUPINFOW        siW;
+    int                 i;
+    char*               ptrA;
+    WCHAR*              ptrW;
+    char                bufA[MAX_PATH];
+    WCHAR               bufW[MAX_PATH];
+    HANDLE              hFile = CreateFileA(file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+
+    if (hFile == INVALID_HANDLE_VALUE) return;
+
+    /* output of startup info (Ansi) */
+    GetStartupInfoA(&siA);
+    childPrintf(hFile,
+                "[StartupInfoA]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
+                "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
+                "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
+                "dwFlags=%lu\nwShowWindow=%u\n"
+                "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
+                siA.cb, encodeA(siA.lpDesktop), encodeA(siA.lpTitle),
+                siA.dwX, siA.dwY, siA.dwXSize, siA.dwYSize,
+                siA.dwXCountChars, siA.dwYCountChars, siA.dwFillAttribute,
+                siA.dwFlags, siA.wShowWindow,
+                (DWORD)siA.hStdInput, (DWORD)siA.hStdOutput, (DWORD)siA.hStdError);
+
+    /* since GetStartupInfoW is only implemented in win2k,
+     * zero out before calling so we can notice the difference
+     */
+    memset(&siW, 0, sizeof(siW));
+    GetStartupInfoW(&siW);
+    childPrintf(hFile,
+                "[StartupInfoW]\ncb=%08ld\nlpDesktop=%s\nlpTitle=%s\n"
+                "dwX=%lu\ndwY=%lu\ndwXSize=%lu\ndwYSize=%lu\n"
+                "dwXCountChars=%lu\ndwYCountChars=%lu\ndwFillAttribute=%lu\n"
+                "dwFlags=%lu\nwShowWindow=%u\n"
+                "hStdInput=%lu\nhStdOutput=%lu\nhStdError=%lu\n\n",
+                siW.cb, encodeW(siW.lpDesktop), encodeW(siW.lpTitle),
+                siW.dwX, siW.dwY, siW.dwXSize, siW.dwYSize,
+                siW.dwXCountChars, siW.dwYCountChars, siW.dwFillAttribute,
+                siW.dwFlags, siW.wShowWindow,
+                (DWORD)siW.hStdInput, (DWORD)siW.hStdOutput, (DWORD)siW.hStdError);
+
+    /* Arguments */
+    childPrintf(hFile, "[Arguments]\nargcA=%d\n", myARGC);
+    for (i = 0; i < myARGC; i++)
+    {
+        childPrintf(hFile, "argvA%d=%s\n", i, encodeA(myARGV[i]));
+    }
+    childPrintf(hFile, "CommandLineA=%s\n", encodeA(GetCommandLineA()));
+
+#if 0
+    int                 argcW;
+    WCHAR**             argvW;
+
+    /* this is part of shell32... and should be tested there */
+    argvW = CommandLineToArgvW(GetCommandLineW(), &argcW);
+    for (i = 0; i < argcW; i++)
+    {
+        childPrintf(hFile, "argvW%d=%s\n", i, encodeW(argvW[i]));
+    }
+#endif
+    childPrintf(hFile, "CommandLineW=%s\n\n", encodeW(GetCommandLineW()));
+
+    /* output of environment (Ansi) */
+    ptrA = GetEnvironmentStringsA();
+    if (ptrA)
+    {
+        char    env_var[MAX_LISTED_ENV_VAR];
+
+        childPrintf(hFile, "[EnvironmentA]\n");
+        i = 0;
+        while (*ptrA)
+        {
+            strncpy(env_var, ptrA, MAX_LISTED_ENV_VAR - 1);
+            env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
+            childPrintf(hFile, "env%d=%s\n", i, encodeA(env_var));
+            i++;
+            ptrA += strlen(ptrA) + 1;
+        }
+        childPrintf(hFile, "len=%d\n\n", i);
+    }
+
+    /* output of environment (Unicode) */
+    ptrW = GetEnvironmentStringsW();
+    if (ptrW)
+    {
+        WCHAR   env_var[MAX_LISTED_ENV_VAR];
+
+        childPrintf(hFile, "[EnvironmentW]\n");
+        i = 0;
+        while (*ptrW)
+        {
+            lstrcpynW(env_var, ptrW, MAX_LISTED_ENV_VAR - 1);
+            env_var[MAX_LISTED_ENV_VAR - 1] = '\0';
+            childPrintf(hFile, "env%d=%s\n", i, encodeW(env_var));
+            i++;
+            ptrW += lstrlenW(ptrW) + 1;
+        }
+        childPrintf(hFile, "len=%d\n\n", i);
+    }
+
+    childPrintf(hFile, "[Misc]\n");
+    if (GetCurrentDirectoryA(sizeof(bufA), bufA))
+        childPrintf(hFile, "CurrDirA=%s\n", encodeA(bufA));
+    if (GetCurrentDirectoryW(sizeof(bufW) / sizeof(bufW[0]), bufW))
+        childPrintf(hFile, "CurrDirW=%s\n", encodeW(bufW));
+    childPrintf(hFile, "\n");
+
+    if (option && strcmp(option, "console") == 0)
+    {
+        CONSOLE_SCREEN_BUFFER_INFO     sbi;
+        HANDLE hConIn  = GetStdHandle(STD_INPUT_HANDLE);
+        HANDLE hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
+        DWORD modeIn, modeOut;
+
+        childPrintf(hFile, "[Console]\n");
+        if (GetConsoleScreenBufferInfo(hConOut, &sbi))
+        {
+            childPrintf(hFile, "SizeX=%d\nSizeY=%d\nCursorX=%d\nCursorY=%d\nAttributes=%d\n",
+                        sbi.dwSize.X, sbi.dwSize.Y, sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y, sbi.wAttributes);
+            childPrintf(hFile, "winLeft=%d\nwinTop=%d\nwinRight=%d\nwinBottom=%d\n",
+                        sbi.srWindow.Left, sbi.srWindow.Top, sbi.srWindow.Right, sbi.srWindow.Bottom);
+            childPrintf(hFile, "maxWinWidth=%d\nmaxWinHeight=%d\n",
+                        sbi.dwMaximumWindowSize.X, sbi.dwMaximumWindowSize.Y);
+        }
+        childPrintf(hFile, "InputCP=%d\nOutputCP=%d\n",
+                    GetConsoleCP(), GetConsoleOutputCP());
+        if (GetConsoleMode(hConIn, &modeIn))
+            childPrintf(hFile, "InputMode=%ld\n", modeIn);
+        if (GetConsoleMode(hConOut, &modeOut))
+            childPrintf(hFile, "OutputMode=%ld\n", modeOut);
+
+        /* now that we have written all relevant information, let's change it */
+        ok(SetConsoleCP(1252), "Setting CP\n");
+        ok(SetConsoleOutputCP(1252), "Setting SB CP\n");
+        ok(SetConsoleMode(hConIn, modeIn ^ 1), "Setting mode (%ld)\n", GetLastError());
+        ok(SetConsoleMode(hConOut, modeOut ^ 1), "Setting mode (%ld)\n", GetLastError());
+        sbi.dwCursorPosition.X ^= 1;
+        sbi.dwCursorPosition.Y ^= 1;
+        ok(SetConsoleCursorPosition(hConOut, sbi.dwCursorPosition), "Setting cursor position (%ld)\n", GetLastError());
+    }
+    if (option && strcmp(option, "stdhandle") == 0)
+    {
+        HANDLE hStdIn  = GetStdHandle(STD_INPUT_HANDLE);
+        HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
+
+        if (hStdIn != INVALID_HANDLE_VALUE || hStdOut != INVALID_HANDLE_VALUE)
+        {
+            char buf[1024];
+            DWORD r, w;
+
+            ok(ReadFile(hStdIn, buf, sizeof(buf), &r, NULL) && r > 0, "Reading message from input pipe\n");
+            childPrintf(hFile, "[StdHandle]\nmsg=%s\n\n", encodeA(buf));
+            ok(WriteFile(hStdOut, buf, r, &w, NULL) && w == r, "Writing message to output pipe\n");
+        }
+    }
+
+    if (option && strcmp(option, "exit_code") == 0)
+    {
+        childPrintf(hFile, "[ExitCode]\nvalue=%d\n\n", 123);
+        CloseHandle(hFile);
+        ExitProcess(123);
+    }
+
+    CloseHandle(hFile);
+}
+
+static char* getChildString(const char* sect, const char* key)
+{
+    char        buf[1024+4*MAX_LISTED_ENV_VAR];
+    char*       ret;
+
+    GetPrivateProfileStringA(sect, key, "-", buf, sizeof(buf), resfile);
+    if (buf[0] == '\0' || (buf[0] == '-' && buf[1] == '\0')) return NULL;
+    assert(!(strlen(buf) & 1));
+    ret = decodeA(buf);
+    return ret;
+}
+
+/* FIXME: this may be moved to the wtmain.c file, because it may be needed by
+ * others... (windows uses stricmp while Un*x uses strcasecmp...)
+ */
+static int wtstrcasecmp(const char* p1, const char* p2)
+{
+    char c1, c2;
+
+    c1 = c2 = '@';
+    while (c1 == c2 && c1)
+    {
+        c1 = *p1++; c2 = *p2++;
+        if (c1 != c2)
+        {
+            c1 = toupper(c1); c2 = toupper(c2);
+        }
+    }
+    return c1 - c2;
+}
+
+static int strCmp(const char* s1, const char* s2, BOOL sensitive)
+{
+    if (!s1 && !s2) return 0;
+    if (!s2) return -1;
+    if (!s1) return 1;
+    return (sensitive) ? strcmp(s1, s2) : wtstrcasecmp(s1, s2);
+}
+
+#define okChildString(sect, key, expect) \
+    do { \
+        char* result = getChildString((sect), (key)); \
+        ok(strCmp(result, expect, 1) == 0, "%s:%s expected '%s', got '%s'\n", (sect), (key), (expect)?(expect):"(null)", result); \
+    } while (0)
+
+#define okChildIString(sect, key, expect) \
+    do { \
+        char* result = getChildString(sect, key); \
+        ok(strCmp(result, expect, 0) == 0, "%s:%s expected '%s', got '%s'\n", sect, key, expect, result); \
+    } while (0)
+
+/* using !expect insures that the test will fail if the sect/key isn't present
+ * in result file
+ */
+#define okChildInt(sect, key, expect) \
+    do { \
+        UINT result = GetPrivateProfileIntA((sect), (key), !(expect), resfile); \
+        ok(result == expect, "%s:%s expected %d, but got %d\n", (sect), (key), (int)(expect), result); \
+   } while (0)
+
+static void test_Startup(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup,si;
+
+    /* let's start simplistic */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    GetStartupInfoA(&si);
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", si.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    /* not so simplistic now */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+    startup.lpTitle = "I'm the title string";
+    startup.lpDesktop = "I'm the desktop string";
+    startup.dwXCountChars = 0x12121212;
+    startup.dwYCountChars = 0x23232323;
+    startup.dwX = 0x34343434;
+    startup.dwY = 0x45454545;
+    startup.dwXSize = 0x56565656;
+    startup.dwYSize = 0x67676767;
+    startup.dwFillAttribute = 0xA55A;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    /* not so simplistic now */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+    startup.lpTitle = "I'm the title string";
+    startup.lpDesktop = NULL;
+    startup.dwXCountChars = 0x12121212;
+    startup.dwYCountChars = 0x23232323;
+    startup.dwX = 0x34343434;
+    startup.dwY = 0x45454545;
+    startup.dwXSize = 0x56565656;
+    startup.dwYSize = 0x67676767;
+    startup.dwFillAttribute = 0xA55A;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", si.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    /* not so simplistic now */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+    startup.lpTitle = "I'm the title string";
+    startup.lpDesktop = "";
+    startup.dwXCountChars = 0x12121212;
+    startup.dwYCountChars = 0x23232323;
+    startup.dwX = 0x34343434;
+    startup.dwY = 0x45454545;
+    startup.dwXSize = 0x56565656;
+    startup.dwYSize = 0x67676767;
+    startup.dwFillAttribute = 0xA55A;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    /* not so simplistic now */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+    startup.lpTitle = NULL;
+    startup.lpDesktop = "I'm the desktop string";
+    startup.dwXCountChars = 0x12121212;
+    startup.dwYCountChars = 0x23232323;
+    startup.dwX = 0x34343434;
+    startup.dwY = 0x45454545;
+    startup.dwXSize = 0x56565656;
+    startup.dwYSize = 0x67676767;
+    startup.dwFillAttribute = 0xA55A;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", si.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    /* not so simplistic now */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+    startup.lpTitle = "";
+    startup.lpDesktop = "I'm the desktop string";
+    startup.dwXCountChars = 0x12121212;
+    startup.dwYCountChars = 0x23232323;
+    startup.dwX = 0x34343434;
+    startup.dwY = 0x45454545;
+    startup.dwXSize = 0x56565656;
+    startup.dwYSize = 0x67676767;
+    startup.dwFillAttribute = 0xA55A;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    /* not so simplistic now */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+    startup.lpTitle = "";
+    startup.lpDesktop = "";
+    startup.dwXCountChars = 0x12121212;
+    startup.dwYCountChars = 0x23232323;
+    startup.dwX = 0x34343434;
+    startup.dwY = 0x45454545;
+    startup.dwXSize = 0x56565656;
+    startup.dwYSize = 0x67676767;
+    startup.dwFillAttribute = 0xA55A;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    todo_wine okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    todo_wine okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    /* TODO: test for A/W and W/A and W/W */
+}
+
+static void test_CommandLine(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup;
+
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    /* the basics */
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s \"C:\\Program Files\\my nice app.exe\"", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("Arguments", "argcA", 4);
+    okChildString("Arguments", "argvA3", "C:\\Program Files\\my nice app.exe");
+    okChildString("Arguments", "argvA4", NULL);
+    okChildString("Arguments", "CommandLineA", buffer);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    /* from Frangois */
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s \"a\\\"b\\\\\" c\\\" d", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("Arguments", "argcA", 6);
+    okChildString("Arguments", "argvA3", "a\"b\\");
+    okChildString("Arguments", "argvA4", "c\"");
+    okChildString("Arguments", "argvA5", "d");
+    okChildString("Arguments", "argvA6", NULL);
+    okChildString("Arguments", "CommandLineA", buffer);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+}
+
+static void test_Directory(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup;
+    char windir[MAX_PATH];
+
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    /* the basics */
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    GetWindowsDirectoryA( windir, sizeof(windir) );
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, windir, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildIString("Misc", "CurrDirA", windir);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+}
+
+static BOOL is_str_env_drive_dir(const char* str)
+{
+    return str[0] == '=' && str[1] >= 'A' && str[1] <= 'Z' && str[2] == ':' &&
+        str[3] == '=' && str[4] == str[1];
+}
+
+/* compared expected child's environment (in gesA) from actual
+ * environment our child got
+ */
+static void cmpEnvironment(const char* gesA)
+{
+    int                 i, clen;
+    const char*         ptrA;
+    char*               res;
+    char                key[32];
+    BOOL                found;
+
+    clen = GetPrivateProfileIntA("EnvironmentA", "len", 0, resfile);
+    
+    /* now look each parent env in child */
+    if ((ptrA = gesA) != NULL)
+    {
+        while (*ptrA)
+        {
+            for (i = 0; i < clen; i++)
+            {
+                sprintf(key, "env%d", i);
+                res = getChildString("EnvironmentA", key);
+                if (strncmp(ptrA, res, MAX_LISTED_ENV_VAR - 1) == 0)
+                    break;
+            }
+            found = i < clen;
+            ok(found, "Parent-env string %s isn't in child process\n", ptrA);
+            
+            ptrA += strlen(ptrA) + 1;
+            release_memory();
+        }
+    }
+    /* and each child env in parent */
+    for (i = 0; i < clen; i++)
+    {
+        sprintf(key, "env%d", i);
+        res = getChildString("EnvironmentA", key);
+        if ((ptrA = gesA) != NULL)
+        {
+            while (*ptrA)
+            {
+                if (strncmp(res, ptrA, MAX_LISTED_ENV_VAR - 1) == 0)
+                    break;
+                ptrA += strlen(ptrA) + 1;
+            }
+            if (!*ptrA) ptrA = NULL;
+        }
+
+        if (!is_str_env_drive_dir(res))
+        {
+            found = ptrA != NULL;
+            ok(found, "Child-env string %s isn't in parent process\n", res);
+        }
+        /* else => should also test we get the right per drive default directory here... */
+    }
+}
+
+static void test_Environment(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup;
+    char                child_env[4096];
+    char*               ptr;
+    char*               env;
+
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    /* the basics */
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, NULL, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+    
+    cmpEnvironment(GetEnvironmentStringsA());
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    /* the basics */
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ptr = child_env;
+    sprintf(ptr, "=%c:=%s", 'C', "C:\\FOO\\BAR");
+    ptr += strlen(ptr) + 1;
+    strcpy(ptr, "PATH=C:\\WINDOWS;C:\\WINDOWS\\SYSTEM;C:\\MY\\OWN\\DIR");
+    ptr += strlen(ptr) + 1;
+    strcpy(ptr, "FOO=BAR");
+    ptr += strlen(ptr) + 1;
+    strcpy(ptr, "BAR=FOOBAR");
+    ptr += strlen(ptr) + 1;
+    /* copy all existing variables except:
+     * - WINELOADER
+     * - PATH (already set above)
+     * - the directory definitions (=[A-Z]:=)
+     */
+    for (env = GetEnvironmentStringsA(); *env; env += strlen(env) + 1)
+    {
+        if (strncmp(env, "PATH=", 5) != 0 &&
+            strncmp(env, "WINELOADER=", 11) != 0 &&
+            !is_str_env_drive_dir(env))
+        {
+            strcpy(ptr, env);
+            ptr += strlen(ptr) + 1;
+        }
+    }
+    *ptr = '\0';
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0L, child_env, NULL, &startup, &info), "CreateProcess\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+    
+    cmpEnvironment(child_env);
+
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+}
+
+static  void    test_SuspendFlag(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup;
+    DWORD               exit_status;
+
+    /* let's start simplistic */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+    ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
+    Sleep(8000);
+    ok(GetExitCodeThread(info.hThread, &exit_status) && exit_status == STILL_ACTIVE, "thread still running\n");
+    ok(ResumeThread(info.hThread) == 1, "Resuming thread\n");
+
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+}
+
+static  void    test_DebuggingFlag(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup;
+    DEBUG_EVENT         de;
+    unsigned            dbg = 0;
+
+    /* let's start simplistic */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+    /* get all startup events up to the entry point break exception */
+    do 
+    {
+        ok(WaitForDebugEvent(&de, INFINITE), "reading debug event\n");
+        ContinueDebugEvent(de.dwProcessId, de.dwThreadId, DBG_CONTINUE);
+        if (de.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) dbg++;
+    } while (de.dwDebugEventCode != EXIT_PROCESS_DEBUG_EVENT);
+
+    ok(dbg, "I have seen a debug event\n");
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+}
+
+static void test_Console(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup;
+    SECURITY_ATTRIBUTES sa;
+    CONSOLE_SCREEN_BUFFER_INFO sbi, sbiC;
+    DWORD               modeIn, modeOut, modeInC, modeOutC;
+    DWORD               cpIn, cpOut, cpInC, cpOutC;
+    DWORD               w;
+    HANDLE              hChildIn, hChildInInh, hChildOut, hChildOutInh, hParentIn, hParentOut;
+    const char*         msg = "This is a std-handle inheritance test.";
+    unsigned            msg_len;
+
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    sa.nLength = sizeof(sa);
+    sa.lpSecurityDescriptor = NULL;
+    sa.bInheritHandle = TRUE;
+
+    startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+    startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+
+    /* first, we need to be sure we're attached to a console */
+    if (startup.hStdInput == INVALID_HANDLE_VALUE || startup.hStdOutput == INVALID_HANDLE_VALUE)
+    {
+        /* we're not attached to a console, let's do it */
+        AllocConsole();
+        startup.hStdInput = CreateFileA("CONIN$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+        startup.hStdOutput = CreateFileA("CONOUT$", GENERIC_READ|GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
+    }
+    /* now verify everything's ok */
+    ok(startup.hStdInput != INVALID_HANDLE_VALUE, "Opening ConIn\n");
+    ok(startup.hStdOutput != INVALID_HANDLE_VALUE, "Opening ConOut\n");
+    startup.hStdError = startup.hStdOutput;
+
+    ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbi), "Getting sb info\n");
+    ok(GetConsoleMode(startup.hStdInput, &modeIn) && 
+       GetConsoleMode(startup.hStdOutput, &modeOut), "Getting console modes\n");
+    cpIn = GetConsoleCP();
+    cpOut = GetConsoleOutputCP();
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s console", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    /* now get the modification the child has made, and resets parents expected values */
+    ok(GetConsoleScreenBufferInfo(startup.hStdOutput, &sbiC), "Getting sb info\n");
+    ok(GetConsoleMode(startup.hStdInput, &modeInC) && 
+       GetConsoleMode(startup.hStdOutput, &modeOutC), "Getting console modes\n");
+
+    SetConsoleMode(startup.hStdInput, modeIn);
+    SetConsoleMode(startup.hStdOutput, modeOut);
+
+    cpInC = GetConsoleCP();
+    cpOutC = GetConsoleOutputCP();
+    SetConsoleCP(cpIn);
+    SetConsoleOutputCP(cpOut);
+
+    okChildInt("StartupInfoA", "cb", startup.cb);
+    okChildString("StartupInfoA", "lpDesktop", startup.lpDesktop);
+    okChildString("StartupInfoA", "lpTitle", startup.lpTitle);
+    okChildInt("StartupInfoA", "dwX", startup.dwX);
+    okChildInt("StartupInfoA", "dwY", startup.dwY);
+    okChildInt("StartupInfoA", "dwXSize", startup.dwXSize);
+    okChildInt("StartupInfoA", "dwYSize", startup.dwYSize);
+    okChildInt("StartupInfoA", "dwXCountChars", startup.dwXCountChars);
+    okChildInt("StartupInfoA", "dwYCountChars", startup.dwYCountChars);
+    okChildInt("StartupInfoA", "dwFillAttribute", startup.dwFillAttribute);
+    okChildInt("StartupInfoA", "dwFlags", startup.dwFlags);
+    okChildInt("StartupInfoA", "wShowWindow", startup.wShowWindow);
+
+    /* check child correctly inherited the console */
+    okChildInt("StartupInfoA", "hStdInput", (DWORD)startup.hStdInput);
+    okChildInt("StartupInfoA", "hStdOutput", (DWORD)startup.hStdOutput);
+    okChildInt("StartupInfoA", "hStdError", (DWORD)startup.hStdError);
+    okChildInt("Console", "SizeX", sbi.dwSize.X);
+    okChildInt("Console", "SizeY", sbi.dwSize.Y);
+    okChildInt("Console", "CursorX", sbi.dwCursorPosition.X);
+    okChildInt("Console", "CursorY", sbi.dwCursorPosition.Y);
+    okChildInt("Console", "Attributes", sbi.wAttributes);
+    okChildInt("Console", "winLeft", sbi.srWindow.Left);
+    okChildInt("Console", "winTop", sbi.srWindow.Top);
+    okChildInt("Console", "winRight", sbi.srWindow.Right);
+    okChildInt("Console", "winBottom", sbi.srWindow.Bottom);
+    okChildInt("Console", "maxWinWidth", sbi.dwMaximumWindowSize.X);
+    okChildInt("Console", "maxWinHeight", sbi.dwMaximumWindowSize.Y);
+    okChildInt("Console", "InputCP", cpIn);
+    okChildInt("Console", "OutputCP", cpOut);
+    okChildInt("Console", "InputMode", modeIn);
+    okChildInt("Console", "OutputMode", modeOut);
+
+    todo_wine ok(cpInC == 1252, "Wrong console CP (expected 1252 got %ld/%ld)\n", cpInC, cpIn);
+    todo_wine ok(cpOutC == 1252, "Wrong console-SB CP (expected 1252 got %ld/%ld)\n", cpOutC, cpOut);
+    ok(modeInC == (modeIn ^ 1), "Wrong console mode\n");
+    ok(modeOutC == (modeOut ^ 1), "Wrong console-SB mode\n");
+    ok(sbiC.dwCursorPosition.X == (sbi.dwCursorPosition.X ^ 1), "Wrong cursor position\n");
+    ok(sbiC.dwCursorPosition.Y == (sbi.dwCursorPosition.Y ^ 1), "Wrong cursor position\n");
+
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+
+    ok(CreatePipe(&hParentIn, &hChildOut, NULL, 0), "Creating parent-input pipe\n");
+    ok(DuplicateHandle(GetCurrentProcess(), hChildOut, GetCurrentProcess(), 
+                       &hChildOutInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
+       "Duplicating as inheritable child-output pipe\n");
+    CloseHandle(hChildOut);
+    ok(CreatePipe(&hChildIn, &hParentOut, NULL, 0), "Creating parent-output pipe\n");
+    ok(DuplicateHandle(GetCurrentProcess(), hChildIn, GetCurrentProcess(), 
+                       &hChildInInh, 0, TRUE, DUPLICATE_SAME_ACCESS),
+       "Duplicating as inheritable child-input pipe\n");
+    CloseHandle(hChildIn); 
+    
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
+    startup.wShowWindow = SW_SHOWNORMAL;
+    startup.hStdInput = hChildInInh;
+    startup.hStdOutput = hChildOutInh;
+    startup.hStdError = hChildOutInh;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s stdhandle", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &startup, &info), "CreateProcess\n");
+    ok(CloseHandle(hChildInInh), "Closing handle\n");
+    ok(CloseHandle(hChildOutInh), "Closing handle\n");
+
+    msg_len = strlen(msg) + 1;
+    ok(WriteFile(hParentOut, msg, msg_len, &w, NULL), "Writing to child\n");
+    ok(w == msg_len, "Should have written %u bytes, actually wrote %lu\n", msg_len, w);
+    memset(buffer, 0, sizeof(buffer));
+    ok(ReadFile(hParentIn, buffer, sizeof(buffer), &w, NULL), "Reading from child\n");
+    ok(strcmp(buffer, msg) == 0, "Should have received '%s'\n", msg);
+
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    okChildString("StdHandle", "msg", msg);
+
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+}
+
+static  void    test_ExitCode(void)
+{
+    char                buffer[MAX_PATH];
+    PROCESS_INFORMATION        info;
+    STARTUPINFOA       startup;
+    DWORD               code;
+
+    /* let's start simplistic */
+    memset(&startup, 0, sizeof(startup));
+    startup.cb = sizeof(startup);
+    startup.dwFlags = STARTF_USESHOWWINDOW;
+    startup.wShowWindow = SW_SHOWNORMAL;
+
+    get_file_name(resfile);
+    sprintf(buffer, "%s tests/process.c %s exit_code", selfname, resfile);
+    ok(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), "CreateProcess\n");
+
+    /* wait for child to terminate */
+    ok(WaitForSingleObject(info.hProcess, 30000) == WAIT_OBJECT_0, "Child process termination\n");
+    /* child process has changed result file, so let profile functions know about it */
+    WritePrivateProfileStringA(NULL, NULL, NULL, resfile);
+
+    ok(GetExitCodeProcess(info.hProcess, &code), "Getting exit code\n");
+    okChildInt("ExitCode", "value", code);
+
+    release_memory();
+    assert(DeleteFileA(resfile) != 0);
+}
+
+START_TEST(process)
+{
+    int b = init();
+    ok(b, "Basic init of CreateProcess test\n");
+    if (!b) return;
+
+    if (myARGC >= 3)
+    {
+        doChild(myARGV[2], (myARGC == 3) ? NULL : myARGV[3]);
+        return;
+    }
+    test_Startup();
+    test_CommandLine();
+    test_Directory();
+    test_Environment();
+    test_SuspendFlag();
+    test_DebuggingFlag();
+    test_Console();
+    test_ExitCode();
+    /* things that can be tested:
+     *  lookup:         check the way program to be executed is searched
+     *  handles:        check the handle inheritance stuff (+sec options)
+     *  console:        check if console creation parameters work
+     */
+}
diff --git a/reactos/apps/tests/kernel32/profile.c b/reactos/apps/tests/kernel32/profile.c
new file mode 100644 (file)
index 0000000..3fff5a4
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Unit tests for profile functions
+ *
+ * Copyright (c) 2003 Stefan Leichter
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include "windef.h"
+#include "winbase.h"
+#include "windows.h"
+
+#define KEY      "ProfileInt"
+#define SECTION  "Test"
+#define TESTFILE ".\\testwine.ini"
+
+struct _profileInt { 
+    LPCSTR section;
+    LPCSTR key;
+    LPCSTR value;
+    LPCSTR iniFile;
+    INT defaultVal;
+    UINT result;
+};
+
+static void test_profile_int(void)
+{
+    struct _profileInt profileInt[]={
+         { NULL,    NULL, NULL,          NULL,     70, 0          }, /*  0 */
+         { NULL,    NULL, NULL,          TESTFILE, -1, 4294967295U},
+         { NULL,    NULL, NULL,          TESTFILE,  1, 1          },
+         { SECTION, NULL, NULL,          TESTFILE, -1, 4294967295U},
+         { SECTION, NULL, NULL,          TESTFILE,  1, 1          },
+         { NULL,    KEY,  NULL,          TESTFILE, -1, 4294967295U}, /*  5 */
+         { NULL,    KEY,  NULL,          TESTFILE,  1, 1          },
+         { SECTION, KEY,  NULL,          TESTFILE, -1, 4294967295U},
+         { SECTION, KEY,  NULL,          TESTFILE,  1, 1          },
+         { SECTION, KEY,  "-1",          TESTFILE, -1, 4294967295U},
+         { SECTION, KEY,  "-1",          TESTFILE,  1, 4294967295U}, /* 10 */
+         { SECTION, KEY,  "1",           TESTFILE, -1, 1          },
+         { SECTION, KEY,  "1",           TESTFILE,  1, 1          },
+         { SECTION, KEY,  "+1",          TESTFILE, -1, 1          },
+         { SECTION, KEY,  "+1",          TESTFILE,  1, 1          },
+         { SECTION, KEY,  "4294967296",  TESTFILE, -1, 0          }, /* 15 */
+         { SECTION, KEY,  "4294967296",  TESTFILE,  1, 0          },
+         { SECTION, KEY,  "4294967297",  TESTFILE, -1, 1          },
+         { SECTION, KEY,  "4294967297",  TESTFILE,  1, 1          },
+         { SECTION, KEY,  "-4294967297", TESTFILE, -1, 4294967295U},
+         { SECTION, KEY,  "-4294967297", TESTFILE,  1, 4294967295U}, /* 20 */
+         { SECTION, KEY,  "42A94967297", TESTFILE, -1, 42         },
+         { SECTION, KEY,  "42A94967297", TESTFILE,  1, 42         },
+         { SECTION, KEY,  "B4294967297", TESTFILE, -1, 0          },
+         { SECTION, KEY,  "B4294967297", TESTFILE,  1, 0          },
+    };
+    int i, num_test = (sizeof(profileInt)/sizeof(struct _profileInt));
+    UINT res;
+
+    DeleteFileA( TESTFILE);
+
+    for (i=0; i < num_test; i++) {
+        if (profileInt[i].value)
+            WritePrivateProfileStringA(SECTION, KEY, profileInt[i].value, 
+                                      profileInt[i].iniFile);
+
+       res = GetPrivateProfileIntA(profileInt[i].section, profileInt[i].key, 
+                 profileInt[i].defaultVal, profileInt[i].iniFile); 
+       ok(res == profileInt[i].result, "test<%02d>: ret<%010u> exp<%010u>\n",
+                                       i, res, profileInt[i].result);
+    }
+
+    DeleteFileA( TESTFILE);
+}
+
+START_TEST(profile)
+{
+    test_profile_int();
+}
diff --git a/reactos/apps/tests/kernel32/testlist.c b/reactos/apps/tests/kernel32/testlist.c
new file mode 100644 (file)
index 0000000..38ab393
--- /dev/null
@@ -0,0 +1,66 @@
+/* Automatically generated file; DO NOT EDIT!! */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "windef.h"
+#include "winbase.h"
+
+#if 0
+extern void func_alloc(void);
+#endif
+extern void func_atom(void);
+extern void func_change(void);
+extern void func_codepage(void);
+extern void func_comm(void);
+extern void func_console(void);
+extern void func_directory(void);
+extern void func_drive(void);
+extern void func_environ(void);
+extern void func_file(void);
+extern void func_format_msg(void);
+extern void func_heap(void);
+extern void func_locale(void);
+extern void func_mailslot(void);
+extern void func_path(void);
+extern void func_pipe(void);
+extern void func_process(void);
+extern void func_profile(void);
+extern void func_thread(void);
+extern void func_virtual(void);
+
+struct test
+{
+    const char *name;
+    void (*func)(void);
+};
+
+static const struct test winetest_testlist[] =
+{
+#if 0
+    { "alloc", func_alloc },
+#endif
+    { "atom", func_atom },
+    { "change", func_change },
+    { "codepage", func_codepage },
+    { "comm", func_comm },
+    { "console", func_console },
+    { "directory", func_directory },
+    { "drive", func_drive },
+    { "environ", func_environ },
+    { "file", func_file },
+    { "format_msg", func_format_msg },
+    { "heap", func_heap },
+    { "locale", func_locale },
+    { "mailslot", func_mailslot },
+    { "path", func_path },
+    { "pipe", func_pipe },
+    { "process", func_process },
+    { "profile", func_profile },
+    { "thread", func_thread },
+    { "virtual", func_virtual },
+    { 0, 0 }
+};
+
+#define WINETEST_WANT_MAIN
+#include "wine/test.h"
diff --git a/reactos/apps/tests/kernel32/thread.c b/reactos/apps/tests/kernel32/thread.c
new file mode 100644 (file)
index 0000000..5fae968
--- /dev/null
@@ -0,0 +1,572 @@
+/*
+ * Unit test suite for directory functions.
+ *
+ * Copyright 2002 Geoffrey Hausheer
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Define _WIN32_WINNT to get SetThreadIdealProcessor on Windows */
+#define _WIN32_WINNT 0x0500
+
+#include <stdarg.h>
+
+#include "wine/test.h"
+#include <windef.h>
+#include <winbase.h>
+#include <winnt.h>
+#include <winerror.h>
+
+/* Specify the number of simultaneous threads to test */
+#define NUM_THREADS 4
+/* Specify whether to test the extended priorities for Win2k/XP */
+#define USE_EXTENDED_PRIORITIES 0
+/* Specify whether to test the stack allocation in CreateThread */
+#define CHECK_STACK 0
+
+/* Set CHECK_STACK to 1 if you want to try to test the stack-limit from
+   CreateThread.  So far I have been unable to make this work, and
+   I am in doubt as to how portable it is.  Also, according to MSDN,
+   you shouldn't mix C-run-time-libraries (i.e. alloca) with CreateThread.
+   Anyhow, the check is currently commented out
+*/
+#if CHECK_STACK
+  #ifdef __try
+    #define __TRY __try
+    #define __EXCEPT __except
+    #define __ENDTRY
+  #else
+    #include "wine/exception.h"
+  #endif
+#endif
+
+typedef BOOL (WINAPI *GetThreadPriorityBoost_t)(HANDLE,PBOOL);
+static GetThreadPriorityBoost_t pGetThreadPriorityBoost=NULL;
+
+typedef HANDLE (WINAPI *OpenThread_t)(DWORD,BOOL,DWORD);
+static OpenThread_t pOpenThread=NULL;
+
+typedef DWORD (WINAPI *SetThreadIdealProcessor_t)(HANDLE,DWORD);
+static SetThreadIdealProcessor_t pSetThreadIdealProcessor=NULL;
+
+typedef BOOL (WINAPI *SetThreadPriorityBoost_t)(HANDLE,BOOL);
+static SetThreadPriorityBoost_t pSetThreadPriorityBoost=NULL;
+
+/* Functions not tested yet:
+  AttachThreadInput
+  CreateRemoteThread
+  SetThreadContext
+  SwitchToThread
+
+In addition there are no checks that the inheritance works properly in
+CreateThread
+*/
+
+DWORD tlsIndex;
+
+typedef struct {
+  int threadnum;
+  HANDLE *event;
+  DWORD *threadmem;
+} t1Struct;
+
+/* Basic test that simulatneous threads can access shared memory,
+   that the thread local storage routines work correctly, and that
+   threads actually run concurrently
+*/
+VOID WINAPI threadFunc1(t1Struct *tstruct)
+{
+   int i;
+/* write our thread # into shared memory */
+   tstruct->threadmem[tstruct->threadnum]=GetCurrentThreadId();
+   ok(TlsSetValue(tlsIndex,(LPVOID)(tstruct->threadnum+1))!=0,
+      "TlsSetValue failed\n");
+/* The threads synchronize before terminating.  This is done by
+   Signaling an event, and waiting for all events to occur
+*/
+   SetEvent(tstruct->event[tstruct->threadnum]);
+   WaitForMultipleObjects(NUM_THREADS,tstruct->event,TRUE,INFINITE);
+/* Double check that all threads really did run by validating that
+   they have all written to the shared memory. There should be no race
+   here, since all threads were synchronized after the write.*/
+   for(i=0;i<NUM_THREADS;i++) {
+     while(tstruct->threadmem[i]==0) ;
+   }
+/* Check that noone cahnged our tls memory */
+   ok((int)TlsGetValue(tlsIndex)-1==tstruct->threadnum,
+      "TlsGetValue failed\n");
+   ExitThread(NUM_THREADS+tstruct->threadnum);
+}
+
+VOID WINAPI threadFunc2()
+{
+   ExitThread(99);
+}
+
+VOID WINAPI threadFunc3()
+{
+   HANDLE thread;
+   thread=GetCurrentThread();
+   SuspendThread(thread);
+   ExitThread(99);
+}
+
+VOID WINAPI threadFunc4(HANDLE event)
+{
+   if(event != NULL) {
+     SetEvent(event);
+   }
+   Sleep(99000);
+   ExitThread(0);
+}
+
+#if CHECK_STACK
+VOID WINAPI threadFunc5(DWORD *exitCode)
+{
+  SYSTEM_INFO sysInfo;
+  sysInfo.dwPageSize=0;
+  GetSystemInfo(&sysInfo);
+  *exitCode=0;
+   __TRY
+   {
+     alloca(2*sysInfo.dwPageSize);
+   }
+    __EXCEPT(1) {
+     *exitCode=1;
+   }
+   __ENDTRY
+   ExitThread(0);
+}
+#endif
+
+/* Check basic funcationality of CreateThread and Tls* functions */
+VOID test_CreateThread_basic()
+{
+   HANDLE thread[NUM_THREADS],event[NUM_THREADS];
+   DWORD threadid[NUM_THREADS],curthreadId;
+   DWORD threadmem[NUM_THREADS];
+   DWORD exitCode;
+   t1Struct tstruct[NUM_THREADS];
+   int error;
+   DWORD i,j;
+/* Retrieve current Thread ID for later comparisons */
+  curthreadId=GetCurrentThreadId();
+/* Allocate some local storage */
+  ok((tlsIndex=TlsAlloc())!=TLS_OUT_OF_INDEXES,"TlsAlloc failed\n");
+/* Create events for thread synchronization */
+  for(i=0;i<NUM_THREADS;i++) {
+    threadmem[i]=0;
+/* Note that it doesn't matter what type of event we chose here.  This
+   test isn't trying to thoroughly test events
+*/
+    event[i]=CreateEventA(NULL,TRUE,FALSE,NULL);
+    tstruct[i].threadnum=i;
+    tstruct[i].threadmem=threadmem;
+    tstruct[i].event=event;
+  }
+
+/* Test that passing arguments to threads works okay */
+  for(i=0;i<NUM_THREADS;i++) {
+    thread[i] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc1,
+                             &tstruct[i],0,&threadid[i]);
+    ok(thread[i]!=NULL,"Create Thread failed\n");
+  }
+/* Test that the threads actually complete */
+  for(i=0;i<NUM_THREADS;i++) {
+    error=WaitForSingleObject(thread[i],5000);
+    ok(error==WAIT_OBJECT_0, "Thread did not complete within timelimit\n");
+    if(error!=WAIT_OBJECT_0) {
+      TerminateThread(thread[i],i+NUM_THREADS);
+    }
+    ok(GetExitCodeThread(thread[i],&exitCode),"Could not retrieve ext code\n");
+    ok(exitCode==i+NUM_THREADS,"Thread returned an incorrect exit code\n");
+  }
+/* Test that each thread executed in its parent's address space
+   (it was able to change threadmem and pass that change back to its parent)
+   and that each thread id was independant).  Note that we prove that the
+   threads actually execute concurrently by having them block on each other
+   in threadFunc1
+*/
+  for(i=0;i<NUM_THREADS;i++) {
+    error=0;
+    for(j=i+1;j<NUM_THREADS;j++) {
+      if (threadmem[i]==threadmem[j]) {
+        error=1;
+      }
+    }
+    ok(!error && threadmem[i]==threadid[i] && threadmem[i]!=curthreadId,
+         "Thread did not execute successfully\n");
+    ok(CloseHandle(thread[i])!=0,"CloseHandle failed\n");
+  }
+  ok(TlsFree(tlsIndex)!=0,"TlsFree failed\n");
+}
+
+/* Check that using the CREATE_SUSPENDED flag works */
+VOID test_CreateThread_suspended()
+{
+  HANDLE thread;
+  DWORD threadId;
+  int error;
+
+  thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc2,NULL,
+                        CREATE_SUSPENDED,&threadId);
+  ok(thread!=NULL,"Create Thread failed\n");
+/* Check that the thread is suspended */
+  ok(SuspendThread(thread)==1,"Thread did not start suspended\n");
+  ok(ResumeThread(thread)==2,"Resume thread returned an invalid value\n");
+/* Check that resume thread didn't actually start the thread.  I can't think
+   of a better way of checking this than just waiting.  I am not sure if this
+   will work on slow computers.
+*/
+  ok(WaitForSingleObject(thread,1000)==WAIT_TIMEOUT,
+     "ResumeThread should not have actually started the thread\n");
+/* Now actually resume the thread and make sure that it actually completes*/
+  ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
+  ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
+     "Thread did not resume\n");
+  if(error!=WAIT_OBJECT_0) {
+    TerminateThread(thread,1);
+  }
+  ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
+}
+
+/* Check that SuspendThread and ResumeThread work */
+VOID test_SuspendThread()
+{
+  HANDLE thread,access_thread;
+  DWORD threadId,exitCode;
+  int i,error;
+
+  thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc3,NULL,
+                        0,&threadId);
+  ok(thread!=NULL,"Create Thread failed\n");
+/* Check that the thread is suspended */
+/* Note that this is a polling method, and there is a race between
+   SuspendThread being called (in the child, and the loop below timing out,
+   so the test could fail on a heavily loaded or slow computer.
+*/
+  error=0;
+  for(i=0;error==0 && i<100;i++) {
+    error=SuspendThread(thread);
+    ResumeThread(thread);
+    if(error==0) {
+      Sleep(50);
+      i++;
+    }
+  }
+  ok(error==1,"SuspendThread did not work\n");
+/* check that access restrictions are obeyed */
+  if (pOpenThread) {
+    access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_SUSPEND_RESUME),
+                           0,threadId);
+    ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
+    if (access_thread!=NULL) {
+      ok(SuspendThread(access_thread)==-1,
+         "SuspendThread did not obey access restrictions\n");
+      ok(ResumeThread(access_thread)==-1,
+         "ResumeThread did not obey access restrictions\n");
+      ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
+    }
+  }
+/* Double check that the thread really is suspended */
+  ok((error=GetExitCodeThread(thread,&exitCode))!=0 && exitCode==STILL_ACTIVE,
+     "Thread did not really suspend\n");
+/* Resume the thread, and make sure it actually completes */
+  ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
+  ok((error=WaitForSingleObject(thread,1000))==WAIT_OBJECT_0,
+     "Thread did not resume\n");
+  if(error!=WAIT_OBJECT_0) {
+    TerminateThread(thread,1);
+  }
+  /* Trying to suspend a terminated thread should fail */
+  error=SuspendThread(thread);
+  ok(error==0xffffffff, "wrong return code: %d\n", error);
+  ok(GetLastError()==ERROR_ACCESS_DENIED || GetLastError()==ERROR_NO_MORE_ITEMS, "unexpected error code: %ld\n", GetLastError());
+
+  ok(CloseHandle(thread)!=0,"CloseHandle Failed\n");
+}
+
+/* Check that TerminateThread works properly
+*/
+VOID test_TerminateThread()
+{
+  HANDLE thread,access_thread,event;
+  DWORD threadId,exitCode;
+  int i,error;
+  i=0; error=0;
+  event=CreateEventA(NULL,TRUE,FALSE,NULL);
+  thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc4,
+                        (LPVOID)event, 0,&threadId);
+  ok(thread!=NULL,"Create Thread failed\n");
+/* Terminate thread has a race condition in Wine.  If the thread is terminated
+   before it starts, it leaves a process behind.  Therefore, we wait for the
+   thread to signal that it has started.  There is no easy way to force the
+   race to occur, so we don't try to find it.
+*/
+  ok(WaitForSingleObject(event,5000)==WAIT_OBJECT_0,
+     "TerminateThread didn't work\n");
+/* check that access restrictions are obeyed */
+  if (pOpenThread) {
+    access_thread=pOpenThread(THREAD_ALL_ACCESS & (~THREAD_TERMINATE),
+                             0,threadId);
+    ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
+    if (access_thread!=NULL) {
+      ok(TerminateThread(access_thread,99)==0,
+         "TerminateThread did not obey access restrictions\n");
+      ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
+    }
+  }
+/* terminate a job and make sure it terminates */
+  ok(TerminateThread(thread,99)!=0,"TerminateThread failed\n");
+  ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+     "TerminateThread didn't work\n");
+  ok(GetExitCodeThread(thread,&exitCode)!=STILL_ACTIVE,
+     "TerminateThread should not leave the thread 'STILL_ACTIVE'\n");
+  ok(exitCode==99, "TerminateThread returned invalid exit code\n");
+  ok(CloseHandle(thread)!=0,"Error Closing thread handle\n");
+}
+
+/* Check if CreateThread obeys the specified stack size.  This code does
+   not work properly, and is currently disabled
+*/
+VOID test_CreateThread_stack()
+{
+#if CHECK_STACK
+/* The only way I know of to test the stack size is to use alloca
+   and __try/__except.  However, this is probably not portable,
+   and I couldn't get it to work under Wine anyhow.  However, here
+   is the code which should allow for testing that CreateThread
+   respects the stack-size limit
+*/
+     HANDLE thread;
+     DWORD threadId,exitCode;
+
+     SYSTEM_INFO sysInfo;
+     sysInfo.dwPageSize=0;
+     GetSystemInfo(&sysInfo);
+     ok(sysInfo.dwPageSize>0,"GetSystemInfo should return a valid page size\n");
+     thread = CreateThread(NULL,sysInfo.dwPageSize,
+                           (LPTHREAD_START_ROUTINE)threadFunc5,&exitCode,
+                           0,&threadId);
+     ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+        "TerminateThread didn't work\n");
+     ok(exitCode==1,"CreateThread did not obey stack-size-limit\n");
+     ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
+#endif
+}
+
+/* Check whether setting/retrieving thread priorities works */
+VOID test_thread_priority()
+{
+   HANDLE curthread,access_thread;
+   DWORD curthreadId,exitCode;
+   int min_priority=-2,max_priority=2;
+   BOOL disabled;
+   int i;
+
+   curthread=GetCurrentThread();
+   curthreadId=GetCurrentThreadId();
+/* Check thread priority */
+/* NOTE: on Win2k/XP priority can be from -7 to 6.  All other platforms it
+         is -2 to 2.  However, even on a real Win2k system, using thread
+         priorities beyond the -2 to 2 range does not work.  If you want to try
+         anyway, enable USE_EXTENDED_PRIORITIES
+*/
+   ok(GetThreadPriority(curthread)==THREAD_PRIORITY_NORMAL,
+      "GetThreadPriority Failed\n");
+
+   if (pOpenThread) {
+/* check that access control is obeyed */
+     access_thread=pOpenThread(THREAD_ALL_ACCESS &
+                       (~THREAD_QUERY_INFORMATION) & (~THREAD_SET_INFORMATION),
+                       0,curthreadId);
+     ok(access_thread!=NULL,"OpenThread returned an invalid handle\n");
+     if (access_thread!=NULL) {
+       ok(SetThreadPriority(access_thread,1)==0,
+          "SetThreadPriority did not obey access restrictions\n");
+       ok(GetThreadPriority(access_thread)==THREAD_PRIORITY_ERROR_RETURN,
+          "GetThreadPriority did not obey access restrictions\n");
+       if (pSetThreadPriorityBoost)
+         ok(pSetThreadPriorityBoost(access_thread,1)==0,
+            "SetThreadPriorityBoost did not obey access restrictions\n");
+       if (pGetThreadPriorityBoost)
+         ok(pGetThreadPriorityBoost(access_thread,&disabled)==0,
+            "GetThreadPriorityBoost did not obey access restrictions\n");
+       ok(GetExitCodeThread(access_thread,&exitCode)==0,
+          "GetExitCodeThread did not obey access restrictions\n");
+       ok(CloseHandle(access_thread),"Error Closing thread handle\n");
+     }
+#if USE_EXTENDED_PRIORITIES
+     min_priority=-7; max_priority=6;
+#endif
+   }
+   for(i=min_priority;i<=max_priority;i++) {
+     ok(SetThreadPriority(curthread,i)!=0,
+        "SetThreadPriority Failed for priority: %d\n",i);
+     ok(GetThreadPriority(curthread)==i,
+        "GetThreadPriority Failed for priority: %d\n",i);
+   }
+   ok(SetThreadPriority(curthread,THREAD_PRIORITY_TIME_CRITICAL)!=0,
+      "SetThreadPriority Failed\n");
+   ok(GetThreadPriority(curthread)==THREAD_PRIORITY_TIME_CRITICAL,
+      "GetThreadPriority Failed\n");
+   ok(SetThreadPriority(curthread,THREAD_PRIORITY_IDLE)!=0,
+       "SetThreadPriority Failed\n");
+   ok(GetThreadPriority(curthread)==THREAD_PRIORITY_IDLE,
+       "GetThreadPriority Failed\n");
+   ok(SetThreadPriority(curthread,0)!=0,"SetThreadPriority Failed\n");
+
+/* Check thread priority boost */
+   if (pGetThreadPriorityBoost && pSetThreadPriorityBoost) {
+     BOOL rc;
+     todo_wine {
+         SetLastError(0);
+         rc=pGetThreadPriorityBoost(curthread,&disabled);
+         if (rc!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+             ok(rc!=0,"error=%ld\n",GetLastError());
+
+             ok(pSetThreadPriorityBoost(curthread,1)!=0,
+                "error=%ld\n",GetLastError());
+             rc=pGetThreadPriorityBoost(curthread,&disabled);
+             ok(rc!=0 && disabled==1,
+                "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
+
+             ok(pSetThreadPriorityBoost(curthread,0)!=0,
+                "error=%ld\n",GetLastError());
+             rc=pGetThreadPriorityBoost(curthread,&disabled);
+             ok(rc!=0 && disabled==0,
+                "rc=%d error=%ld disabled=%d\n",rc,GetLastError(),disabled);
+         }
+     }
+   }
+}
+
+/* check the GetThreadTimes function */
+VOID test_GetThreadTimes()
+{
+     HANDLE thread,access_thread=NULL;
+     FILETIME creationTime,exitTime,kernelTime,userTime;
+     DWORD threadId;
+     int error;
+
+     thread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)threadFunc2,NULL,
+                           CREATE_SUSPENDED,&threadId);
+
+     ok(thread!=NULL,"Create Thread failed\n");
+/* check that access control is obeyed */
+     if (pOpenThread) {
+       access_thread=pOpenThread(THREAD_ALL_ACCESS &
+                                   (~THREAD_QUERY_INFORMATION), 0,threadId);
+       ok(access_thread!=NULL,
+          "OpenThread returned an invalid handle\n");
+     }
+     ok(ResumeThread(thread)==1,"Resume thread returned an invalid value\n");
+     ok(WaitForSingleObject(thread,5000)==WAIT_OBJECT_0,
+        "ResumeThread didn't work\n");
+     if(access_thread!=NULL) {
+       error=GetThreadTimes(access_thread,&creationTime,&exitTime,
+                            &kernelTime,&userTime);
+       ok(error==0, "GetThreadTimes did not obey access restrictions\n");
+       ok(CloseHandle(access_thread)!=0,"CloseHandle Failed\n");
+     }
+     creationTime.dwLowDateTime=99; creationTime.dwHighDateTime=99;
+     exitTime.dwLowDateTime=99;     exitTime.dwHighDateTime=99;
+     kernelTime.dwLowDateTime=99;   kernelTime.dwHighDateTime=99;
+     userTime.dwLowDateTime=99;     userTime.dwHighDateTime=99;
+/* GetThreadTimes should set all of the parameters passed to it */
+     error=GetThreadTimes(thread,&creationTime,&exitTime,
+                          &kernelTime,&userTime);
+     if (error!=0 || GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+       ok(error!=0,"GetThreadTimes failed\n");
+       ok(creationTime.dwLowDateTime!=99 || creationTime.dwHighDateTime!=99,
+          "creationTime was invalid\n");
+       ok(exitTime.dwLowDateTime!=99 || exitTime.dwHighDateTime!=99,
+          "exitTime was invalid\n");
+       ok(kernelTime.dwLowDateTime!=99 || kernelTime.dwHighDateTime!=99,
+          "kernelTimewas invalid\n");
+       ok(userTime.dwLowDateTime!=99 || userTime.dwHighDateTime!=99,
+          "userTime was invalid\n");
+       ok(CloseHandle(thread)!=0,"CloseHandle failed\n");
+     }
+}
+
+/* Check the processor affinity functions */
+/* NOTE: These functions should also be checked that they obey access control
+*/
+VOID test_thread_processor()
+{
+   HANDLE curthread,curproc;
+   DWORD processMask,systemMask;
+   SYSTEM_INFO sysInfo;
+   int error=0;
+
+   sysInfo.dwNumberOfProcessors=0;
+   GetSystemInfo(&sysInfo);
+   ok(sysInfo.dwNumberOfProcessors>0,
+      "GetSystemInfo failed to return a valid # of processors\n");
+/* Use the current Thread/process for all tests */
+   curthread=GetCurrentThread();
+   ok(curthread!=NULL,"GetCurrentThread failed\n");
+   curproc=GetCurrentProcess();
+   ok(curproc!=NULL,"GetCurrentProcess failed\n");
+/* Check the Affinity Mask functions */
+   ok(GetProcessAffinityMask(curproc,&processMask,&systemMask)!=0,
+      "GetProcessAffinityMask failed\n");
+   ok(SetThreadAffinityMask(curthread,processMask)==1,
+      "SetThreadAffinityMask failed\n");
+   ok(SetThreadAffinityMask(curthread,processMask+1)==0,
+      "SetThreadAffinityMask passed for an illegal processor\n");
+/* NOTE: This only works on WinNT/2000/XP) */
+   if (pSetThreadIdealProcessor) {
+     todo_wine {
+       SetLastError(0);
+       error=pSetThreadIdealProcessor(curthread,0);
+       if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+         ok(error!=-1, "SetThreadIdealProcessor failed\n");
+       }
+     }
+     if (GetLastError()!=ERROR_CALL_NOT_IMPLEMENTED) {
+       error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS+1);
+       ok(error==-1,
+          "SetThreadIdealProcessor succeeded with an illegal processor #\n");
+       todo_wine {
+         error=pSetThreadIdealProcessor(curthread,MAXIMUM_PROCESSORS);
+         ok(error==0, "SetThreadIdealProcessor returned an incorrect value\n");
+       }
+     }
+   }
+}
+
+START_TEST(thread)
+{
+   HINSTANCE lib;
+/* Neither Cygwin nor mingW export OpenThread, so do a dynamic check
+   so that the compile passes
+*/
+   lib=LoadLibraryA("kernel32");
+   ok(lib!=NULL,"Couldn't load kernel32.dll\n");
+   pGetThreadPriorityBoost=(GetThreadPriorityBoost_t)GetProcAddress(lib,"GetThreadPriorityBoost");
+   pOpenThread=(OpenThread_t)GetProcAddress(lib,"OpenThread");
+   pSetThreadIdealProcessor=(SetThreadIdealProcessor_t)GetProcAddress(lib,"SetThreadIdealProcessor");
+   pSetThreadPriorityBoost=(SetThreadPriorityBoost_t)GetProcAddress(lib,"SetThreadPriorityBoost");
+   test_CreateThread_basic();
+   test_CreateThread_suspended();
+   test_SuspendThread();
+   test_TerminateThread();
+   test_CreateThread_stack();
+   test_thread_priority();
+   test_GetThreadTimes();
+   test_thread_processor();
+}
diff --git a/reactos/apps/tests/kernel32/virtual.c b/reactos/apps/tests/kernel32/virtual.c
new file mode 100644 (file)
index 0000000..b4b4068
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Unit test suite for Virtual* family of APIs.
+ *
+ * Copyright 2004 Dmitry Timoshkov
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/test.h"
+
+static void test_VirtualAlloc(void)
+{
+    void *addr1, *addr2;
+    DWORD old_prot;
+    MEMORY_BASIC_INFORMATION info;
+
+    SetLastError(0xdeadbeef);
+    addr1 = VirtualAlloc(0, 0, MEM_RESERVE, PAGE_NOACCESS);
+    ok(addr1 == NULL, "VirtualAlloc should fail on zero-sized allocation\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER /* NT */ ||
+       GetLastError() == ERROR_NOT_ENOUGH_MEMORY, /* Win9x */
+        "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+
+    addr1 = VirtualAlloc(0, 0xFFFC, MEM_RESERVE, PAGE_NOACCESS);
+    ok(addr1 != NULL, "VirtualAlloc failed\n");
+
+    /* test a not committed memory */
+    ok(VirtualQuery(addr1, &info, sizeof(info)) == sizeof(info),
+        "VirtualQuery failed\n");
+    ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
+    ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
+    ok(info.AllocationProtect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n", info.AllocationProtect);
+    ok(info.RegionSize == 0x10000, "%lx != 0x10000\n", info.RegionSize);
+    ok(info.State == MEM_RESERVE, "%lx != MEM_RESERVE\n", info.State);
+    /* NT reports Protect == 0 for a not committed memory block */
+    ok(info.Protect == 0 /* NT */ ||
+       info.Protect == PAGE_NOACCESS, /* Win9x */
+        "%lx != PAGE_NOACCESS\n", info.Protect);
+    ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+
+    SetLastError(0xdeadbeef);
+    ok(!VirtualProtect(addr1, 0xFFFC, PAGE_READONLY, &old_prot),
+       "VirtualProtect should fail on a not committed memory\n");
+    ok(GetLastError() == ERROR_INVALID_ADDRESS /* NT */ ||
+       GetLastError() == ERROR_INVALID_PARAMETER, /* Win9x */
+        "got %ld, expected ERROR_INVALID_ADDRESS\n", GetLastError());
+
+    addr2 = VirtualAlloc(addr1, 0x1000, MEM_COMMIT, PAGE_NOACCESS);
+    ok(addr1 == addr2, "VirtualAlloc failed\n");
+
+    /* test a committed memory */
+    ok(VirtualQuery(addr1, &info, sizeof(info)) == sizeof(info),
+        "VirtualQuery failed\n");
+    ok(info.BaseAddress == addr1, "%p != %p\n", info.BaseAddress, addr1);
+    ok(info.AllocationBase == addr1, "%p != %p\n", info.AllocationBase, addr1);
+    ok(info.AllocationProtect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n", info.AllocationProtect);
+    ok(info.RegionSize == 0x1000, "%lx != 0x1000\n", info.RegionSize);
+    ok(info.State == MEM_COMMIT, "%lx != MEM_COMMIT\n", info.State);
+    /* this time NT reports PAGE_NOACCESS as well */
+    ok(info.Protect == PAGE_NOACCESS, "%lx != PAGE_NOACCESS\n", info.Protect);
+    ok(info.Type == MEM_PRIVATE, "%lx != MEM_PRIVATE\n", info.Type);
+
+    /* this should fail, since not the whole range is committed yet */
+    SetLastError(0xdeadbeef);
+    ok(!VirtualProtect(addr1, 0xFFFC, PAGE_READONLY, &old_prot),
+        "VirtualProtect should fail on a not committed memory\n");
+    ok(GetLastError() == ERROR_INVALID_ADDRESS /* NT */ ||
+       GetLastError() == ERROR_INVALID_PARAMETER, /* Win9x */
+        "got %ld, expected ERROR_INVALID_ADDRESS\n", GetLastError());
+
+    ok(VirtualProtect(addr1, 0x1000, PAGE_READONLY, &old_prot), "VirtualProtect failed\n");
+    ok(old_prot == PAGE_NOACCESS,
+        "wrong old protection: got %04lx instead of PAGE_NOACCESS\n", old_prot);
+
+    ok(VirtualProtect(addr1, 0x1000, PAGE_READWRITE, &old_prot), "VirtualProtect failed\n");
+    ok(old_prot == PAGE_READONLY,
+        "wrong old protection: got %04lx instead of PAGE_READONLY\n", old_prot);
+
+    ok(!VirtualFree(addr1, 0x10000, 0), "VirtualFree should fail with type 0\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+        "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+
+    ok(VirtualFree(addr1, 0x10000, MEM_DECOMMIT), "VirtualFree failed\n");
+
+    /* if the type is MEM_RELEASE, size must be 0 */
+    ok(!VirtualFree(addr1, 1, MEM_RELEASE), "VirtualFree should fail\n");
+    ok(GetLastError() == ERROR_INVALID_PARAMETER,
+        "got %ld, expected ERROR_INVALID_PARAMETER\n", GetLastError());
+
+    ok(VirtualFree(addr1, 0, MEM_RELEASE), "VirtualFree failed\n");
+}
+
+START_TEST(virtual)
+{
+    test_VirtualAlloc();
+}