winetest: the Wine test shell

Dimitrie O. Paun dpaun at rogers.com
Wed Dec 3 01:57:01 CST 2003


Hi Alexandre,

This is not perfect (but then again, what is? :)), but I've
done the following:
  -- use CreateDirectory() instead of mkdir()
  -- rename winetests to winetest, as per your request
  -- removed the CROSS compilation support. It should work
     with a different build tree, but I haven't tested that
  -- removed striping the tests from the build. This wasn't
     right for multiple reasons:
	* it was using a hardcoded 'strip' command instead
	  of the configure one
	* it seems strange that building winetest should
	  magically affect all the tests
	* why should we strip every time
     Maybe we should replace this with an explicit
	make strip-tests
     or some such. Suggestins are welcome.

But I think it looks decent enough that it can go in, so we
can start sending small incremental patches to it.

ChangeLog
    Jakob Eriksson <jakov at vmlinux.org>
    Dimitrie O. Paun <dpaun at rogers.com>
    Ferenc Wagner <wferi at afavant.elte.hu>
    New Wine test shell utility.

Index: configure.ac
===================================================================
RCS file: /var/cvs/wine/configure.ac,v
retrieving revision 1.214
diff -u -r1.214 configure.ac
--- configure.ac	2 Dec 2003 04:11:09 -0000	1.214
+++ configure.ac	3 Dec 2003 05:34:38 -0000
@@ -1657,6 +1657,7 @@
 programs/winemenubuilder/Makefile
 programs/winemine/Makefile
 programs/winepath/Makefile
+programs/winetest/Makefile
 programs/winevdm/Makefile
 programs/winhelp/Makefile
 programs/winver/Makefile
--- /dev/null	2003-01-30 05:24:37.000000000 -0500
+++ programs/winetest/Makefile.in	2003-12-03 00:44:17.000000000 -0500
@@ -0,0 +1,26 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = winetest.exe
+APPMODE   = gui
+IMPORTS   = user32 ws2_32
+
+C_SRCS = main.c send.c util.c
+EXTRA_OBJS = winetest.o
+RC_SRCS = winetest.rc
+
+ at MAKE_PROG_RULES@
+
+# Special rules
+
+winetest.rc: maketests tests.list
+	$(SRCDIR)/maketests -r $(SRCDIR)/tests.list > $@ || ( $(RM) $@ && exit 1 )
+
+winetest.c: maketests tests.list
+	$(SRCDIR)/maketests -t $(SRCDIR)/tests.list > $@ || ( $(RM) $@ && exit 1 )
+
+clean::
+	rm -f winetest.c winetest.rc
+
+### Dependencies:
--- /dev/null	2003-01-30 05:24:37.000000000 -0500
+++ programs/winetest/maketests	2003-12-03 02:23:43.000000000 -0500
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+MODE="$1"
+
+BINDIR="../.."
+TESTS=`sed 's/#.*//' "$2"`
+
+echo "/* Automatically generated -- do not edit! */"
+case $MODE in
+    -t)
+        echo '#include "winetest.h"'
+	if [ -z "$WINE_BUILD" ]; then
+	    WINE_BUILD="`date`"
+	    echo "warning: using automatically generated BUILD tag: $WINE_BUILD" >/dev/stderr
+	fi
+        echo "const char build_tag[] = \"$WINE_BUILD\";"
+	echo "struct wine_test wine_tests[] = {"
+    ;;
+esac
+
+i=0
+for test in $TESTS; do
+    i=$(($i+1))
+    testname=`basename $test`
+
+    # first try the ELF test, then the PE one, else skip
+    filename="$BINDIR/${test}_test.exe.so"
+    if ! [ -f "$filename" ]; then
+	filename="$BINDIR/${test}_test.exe"
+	if ! [ -f "$filename" ]; then
+	    echo "warning: Can't find executable for '$test', skipping" >/dev/stderr
+	    continue
+	fi
+    fi
+
+    case $MODE in
+	-r)
+	    echo "TEST$i USERDATA \"$filename\""
+	;;
+	-t)
+	    echo "    { \"$testname\", \"TEST$i\" },"
+	;;
+    esac
+done
+
+case $MODE in
+    -t)
+	echo "    { 0 }"
+	echo "};"
+    ;;
+esac
--- /dev/null	2003-01-30 05:24:37.000000000 -0500
+++ programs/winetest/winetest.h	2003-12-03 00:50:23.000000000 -0500
@@ -0,0 +1,32 @@
+#ifndef __WINETESTS_H
+#define __WINETESTS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+struct wine_test
+{
+    const char *name;
+    const char *resource;
+    int subtest_count;
+    char **subtests;
+    int is_elf;
+    char *exename;
+};
+
+extern struct wine_test wine_tests[];
+
+extern const char build_tag[];
+
+void fatal (const char* msg);
+void warning (const char* msg);
+void *xmalloc (size_t len);
+void *xrealloc (void *op, size_t len);
+void xprintf (const char *fmt, ...);
+char *vstrmake (size_t *lenp, const char *fmt, va_list ap);
+char *strmake (size_t *lenp, const char *fmt, ...);
+
+int send_file (const char *name);
+
+#endif /* __WINETESTS_H */
--- /dev/null	2003-01-30 05:24:37.000000000 -0500
+++ programs/winetest/main.c	2003-12-03 01:00:29.000000000 -0500
@@ -0,0 +1,277 @@
+/*
+ * Wine Conformance Test EXE
+ *
+ * Copyright 2003 Jakob Eriksson   (for Solid Form Sweden AB)
+ * 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
+ *
+ * This program is dedicated to Anna Lindh,
+ * Swedish Minister of Foreign Affairs.
+ * Anna was murdered September 11, 2003.
+ *
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <wtypes.h>
+#include <errno.h>
+#ifdef HAVE_UNISTD_H
+#  include <unistd.h>
+#endif
+
+#include "winetest.h"
+
+void print_version ()
+{
+    OSVERSIONINFOEX ver;
+    BOOL ext;
+
+    ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+    if (!(ext = GetVersionEx ((OSVERSIONINFO *) &ver)))
+    {
+	ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+	if (!GetVersionEx ((OSVERSIONINFO *) &ver))
+	    fatal("Can't get OS version.");
+    }
+
+    xprintf ("    dwMajorVersion=%ld\n    dwMinorVersion=%ld\n"
+             "    dwBuildNumber=%ld\n    PlatformId=%ld\n    szCSDVersion=%s\n",
+             ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
+             ver.dwPlatformId, ver.szCSDVersion);
+
+    if (!ext) return;
+
+    xprintf ("    wServicePackMajor=%d\n    wServicePackMinor=%d\n"
+             "    wSuiteMask=%d\n    wProductType=%d\n    wReserved=%d\n",
+             ver.wServicePackMajor, ver.wServicePackMinor, ver.wSuiteMask,
+             ver.wProductType, ver.wReserved);
+}
+
+static inline int is_dot_dir(const char* x)
+{
+    return ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))));
+}
+
+void remove_dir (const char *dir)
+{
+    HANDLE  hFind;
+    WIN32_FIND_DATA wfd;
+    char path[MAX_PATH];
+    size_t dirlen = strlen (dir);
+
+    /* Make sure the directory exists before going further */
+    memcpy (path, dir, dirlen);
+    strcpy (path + dirlen++, "\\*");
+    hFind = FindFirstFile (path, &wfd);
+    if (hFind == INVALID_HANDLE_VALUE) return;
+
+    do {
+        char *lp = wfd.cFileName;
+
+        if (!lp[0]) lp = wfd.cAlternateFileName; /* ? FIXME not (!lp) ? */
+        if (is_dot_dir (lp)) continue;
+        strcpy (path + dirlen, lp);
+        if (FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
+            remove_dir(path);
+        else if (!DeleteFile (path))
+            warning (strmake (NULL, "Can't delete file %s: error %d", path, GetLastError ()));
+    } while (FindNextFile (hFind, &wfd));
+    FindClose (hFind);
+    if (!RemoveDirectory (dir))
+        warning (strmake (NULL, "Can't remove directory %s: error %d", dir, GetLastError ()));
+}
+
+void* extract_rcdata (const char *name, DWORD* size)
+{
+    HRSRC rsrc;
+    HGLOBAL hdl;
+
+    rsrc = FindResource (0, name, "USERDATA");
+    if (!rsrc) return 0;
+    *size = SizeofResource (0, rsrc);
+    if (!*size) return 0;
+    hdl = LoadResource (0, rsrc);
+    if (!hdl) return 0;
+    return LockResource (hdl);
+}
+
+void extract_test (const char *dir, struct wine_test* test)
+{
+    BYTE* code;
+    DWORD size;
+    FILE* fout;
+
+    code = extract_rcdata (test->resource, &size);
+    if (!code) fatal (strmake (NULL, "Can't get resource %s.", test->resource));
+
+    test->is_elf = (code[1] == 'E' && code[2] == 'L' && code[3] == 'F');
+    test->exename = strmake(NULL, "%s/%s_test.exe%s", dir, test->name, test->is_elf ? ".so" : "");
+
+    if (!(fout = fopen(test->exename, "wb")) ||
+        (fwrite (code, size, 1, fout) != 1) ||
+        fclose (fout)) fatal (strmake (NULL, "Failed to write file %s.", test->name));
+}
+
+int get_subtests (struct wine_test tests[])
+{
+    char *subname;
+    FILE *subfile;
+    size_t subsize, bytes_read, total;
+    char buffer[8000], *index;
+    const char header[] = "Valid test names:", seps[] = " \r\n";
+    int oldstdout;
+    const char *argv[] = {"wine", NULL, NULL};
+    struct wine_test* test;
+    int allocated, all_subtests = 0;
+
+    subname = tempnam (0, "sub");
+    if (!subname) fatal ("Can't name subtests file.");
+    oldstdout = dup (1);
+    if (-1 == oldstdout) fatal ("Can't preserve stdout.");
+    subfile = fopen (subname, "w+b");
+    if (!subfile) fatal ("Can't open subtests file.");
+    if (-1 == dup2 (fileno (subfile), 1))
+        fatal ("Can't redirect output to subtests.");
+    fclose (subfile);
+
+    for (test = tests; test->name; test++) {
+        lseek (1, 0, SEEK_SET);
+        argv[1] = test->exename;
+        if (test->is_elf)
+            spawnvp (_P_WAIT, "wine", argv);
+        else
+            spawnvp (_P_WAIT, test->exename, argv+1);
+        subsize = lseek (1, 0, SEEK_CUR);
+        if (subsize >= sizeof buffer) {
+            fprintf (stderr, "Subtests output too big: %s.\n",
+                     test->name);
+            continue;
+        }
+
+        lseek (1, 0, SEEK_SET);
+        total = 0;
+        while ((bytes_read = read (1, buffer + total, subsize - total))
+               && (signed)bytes_read != -1)
+            total += bytes_read;
+        if (bytes_read) {
+            fprintf (stderr, "Error reading %s.\n", test->name);
+            continue;
+        }
+        buffer[total] = 0;
+        index = strstr (buffer, header) + sizeof header;
+        if (!index) {
+            fprintf (stderr, "Can't parse subtests output of %s.\n",
+                     test->name);
+            continue;
+        }
+
+        allocated = 10;
+        test->subtests = xmalloc (allocated * sizeof (char*));
+        test->subtest_count = 0;
+        index = strtok (index, seps);
+        while (index) {
+            if (test->subtest_count == allocated) {
+                allocated *= 2;
+                test->subtests = xrealloc (test->subtests,
+                                           allocated * sizeof (char*));
+            }
+            test->subtests[test->subtest_count++] = strdup (index);
+            index = strtok (NULL, seps);
+        }
+        test->subtests = xrealloc (test->subtests,
+                                   test->subtest_count * sizeof (char*));
+        all_subtests += test->subtest_count;
+    }
+    close (1);
+
+    if (-1 == dup2 (oldstdout, 1)) fatal ("Can't recover old stdout.");
+    close (oldstdout);
+
+    if (remove (subname)) fatal ("Can't remove subtests file.");
+    free (subname);
+
+    return all_subtests;
+}
+
+void run_test (struct wine_test* test, const char* subtest)
+{
+    int status;
+    const char *argv[] = {"wine", test->exename, subtest, NULL};
+
+    fprintf (stderr, "Running %s:%s\n", test->name, subtest);
+    xprintf ("%s:%s start\n", test->name, subtest);
+    if (test->is_elf)
+        status = spawnvp (_P_WAIT, "wine", argv);
+    else
+        status = spawnvp (_P_WAIT, test->exename, argv+1);
+    if (status == -1)
+        xprintf ("Can't run: %d, errno=%d: %s\n", status, errno, strerror (errno));
+    xprintf ("%s:%s done (%x)\n", test->name, subtest, status);
+}
+
+int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdline, int cmdshow)
+{
+    struct wine_test* test;
+    int nr_of_tests, subtest;
+    char *tempdir, *logname;
+    FILE *logfile;
+
+    SetErrorMode (SEM_FAILCRITICALERRORS);
+
+    if (setvbuf (stdout, NULL, _IONBF, 0)) fatal ("Can't unbuffer output.");
+
+    tempdir = tempnam (0, "wct");
+    if (!tempdir) fatal ("Can't name temporary dir (check TMP).");
+    fprintf (stderr, "tempdir=%s\n", tempdir);
+    if (!CreateDirectory (tempdir, NULL)) fatal (strmake (NULL, "Could not create directory: %s", tempdir));
+
+    logname = tempnam (0, "res");
+    if (!logname) fatal ("Can't name logfile.");
+    fprintf (stderr, "logname=%s\n", logname);
+
+    logfile = fopen (logname, "ab");
+    if (!logfile) fatal ("Could not open logfile.");
+    if (-1 == dup2 (fileno (logfile), 1)) fatal ("Can't redirect stdout.");
+    fclose (logfile);
+
+    xprintf ("Tests from build %s\n", build_tag);
+    xprintf ("Operating system version:\n");
+    print_version ();
+    xprintf ("Test output:\n" );
+
+    for (test = wine_tests; test->name; test++)
+        extract_test (tempdir, test);
+
+    nr_of_tests = get_subtests (wine_tests);
+
+    for (test = wine_tests; test->name; test++)
+	for (subtest = 0; subtest < test->subtest_count; subtest++)
+	    run_test (test, test->subtests[subtest]);
+
+    close (1);
+
+    remove_dir (tempdir);
+
+    if (send_file (logname))
+        fatal ("Can't submit logfile (network of file error).");
+
+    if (remove (logname))
+        fatal (strmake (NULL, "Can't remove logfile: %d.", errno));
+
+    return 0;
+}
--- /dev/null	2003-01-30 05:24:37.000000000 -0500
+++ programs/winetest/send.c	2003-12-03 00:37:53.000000000 -0500
@@ -0,0 +1,158 @@
+#include <winsock.h>
+#include <stdio.h>
+
+#include "winetest.h"
+
+SOCKET
+open_http (const char *ipnum)
+{
+    WSADATA wsad;
+    struct sockaddr_in sa;
+    SOCKET s;
+
+    if (WSAStartup (MAKEWORD (2,2), &wsad)) return INVALID_SOCKET;
+
+    s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    if (s != INVALID_SOCKET) {
+        sa.sin_family = AF_INET;
+        sa.sin_port = htons (80);
+        sa.sin_addr.s_addr = inet_addr (ipnum);
+        if (!connect (s, (struct sockaddr*)&sa,
+                      sizeof (struct sockaddr_in)))
+            return s;
+    }
+    WSACleanup ();
+    return INVALID_SOCKET;
+}
+
+int
+close_http (SOCKET s)
+{
+    int ret;
+
+    ret = closesocket (s);
+    return (WSACleanup () || ret);
+}
+
+int
+send_buf (SOCKET s, const char *buf, size_t length)
+{
+    int sent;
+
+    while (length > 0) {
+        sent = send (s, buf, length, 0);
+        if (sent == SOCKET_ERROR) return 1;
+        buf += sent;
+        length -= sent;
+    }
+    return 0;
+}
+
+int
+send_str (SOCKET s, const char *fmt, ...)
+{
+    va_list ap;
+    char *p;
+    int ret;
+    size_t len;
+
+    va_start (ap, fmt);
+    p = vstrmake (&len, fmt, ap);
+    va_end (ap);
+    if (!p) return 1;
+    ret = send_buf (s, p, len);
+    free (p);
+    return ret;
+}
+
+int
+send_file (const char *name)
+{
+    SOCKET s;
+    FILE *f;
+    unsigned char buffer[8192];
+    size_t bytes_read, total, filesize;
+    char *str;
+    int ret;
+
+    /* RFC 2068 */
+#define SEP "-"
+    const char head[] = "POST /~wferi/cgi-bin/winetests.cgi HTTP/1.0\r\n"
+        "Host: afavant\r\n"
+        "User-Agent: Winetests Shell\r\n"
+        "Content-Type: multipart/form-data; boundary=" SEP "\r\n"
+        "Content-Length: %u\r\n\r\n";
+    const char body1[] = "--" SEP "\r\n"
+        "Content-Disposition: form-data; name=reportfile; filename=\"%s\"\r\n"
+        "Content-Type: application/octet-stream\r\n\r\n";
+    const char body2[] = "\r\n--" SEP "\r\n"
+        "Content-Dispoition: form-data; name=submit\r\n\r\n"
+        "Upload File\r\n"
+        "--" SEP "--\r\n";
+
+    s = open_http ("157.181.170.47");
+    if (s == INVALID_SOCKET) {
+        fprintf (stderr, "Can't open connection: %x.\n",
+                 WSAGetLastError ());
+        return 1;
+    }
+
+    f = fopen (name, "rb");
+    if (!f) goto abort1;
+    fseek (f, 0, SEEK_END);
+    filesize = ftell (f);
+    if (filesize > 1024*1024) goto abort2;
+    fseek (f, 0, SEEK_SET);
+
+    str = strmake (&total, body1, name);
+    ret = send_str (s, head, filesize + total + sizeof body2 - 1) ||
+        send_buf (s, str, total);
+    free (str);
+    if (ret) {
+        fprintf (stderr, "Can't send header.\n");
+        goto abort2;
+    }
+
+    while ((bytes_read = fread (buffer, 1, sizeof buffer, f)))
+        if (send_buf (s, buffer, bytes_read)) {
+            fprintf (stderr, "Can't send body.\n");
+            goto abort2;
+        }
+    fclose (f);
+
+    if (send_buf (s, body2, sizeof body2 - 1)) {
+        fprintf (stderr, "Can't send trailer.\n");
+        goto abort2;
+    }
+
+    total = 0;
+    while ((bytes_read = recv (s, buffer + total,
+                               sizeof buffer - total, 0))) {
+        if ((signed)bytes_read == SOCKET_ERROR) {
+            fprintf (stderr, "Error receiving response: %d.\n",
+                     WSAGetLastError ());
+            goto abort1;
+        }
+        total += bytes_read;
+        if (total == sizeof buffer) {
+            fprintf (stderr, "Buffer overflow.\n");
+            goto abort1;
+        }
+    }
+    if (close_http (s)) {
+        fprintf (stderr, "Error closing connection.\n");
+        return 1;
+    }
+
+    str = strmake (&bytes_read, "Received %s (%d bytes).\n",
+                   name, filesize);
+    ret = memcmp (str, buffer + total - bytes_read, bytes_read);
+    free (str);
+    return ret!=0;
+
+ abort2:
+    fclose (f);
+ abort1:
+    close_http (s);
+    return 1;
+}
--- /dev/null	2003-01-30 05:24:37.000000000 -0500
+++ programs/winetest/util.c	2003-12-03 00:50:05.000000000 -0500
@@ -0,0 +1,75 @@
+#include <windows.h>
+
+#include "winetest.h"
+
+void fatal (const char* msg)
+{
+    MessageBox (NULL, msg, "Fatal Error", MB_ICONERROR | MB_OK);
+    exit (1);
+}
+
+void warning (const char* msg)
+{
+    MessageBox (NULL, msg, "Warning", MB_ICONWARNING | MB_OK);
+}
+
+void *xmalloc (size_t len)
+{
+    void *p = malloc (len);
+
+    if (!p) fatal ("Out of memory.");
+    return p;
+}
+
+void *xrealloc (void *op, size_t len)
+{
+    void *p = realloc (op, len);
+
+    if (!p) fatal ("Out of memory.");
+    return p;
+}
+
+void xprintf (const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    if (vprintf (fmt, ap) < 0) fatal ("Can't write logs.");
+    va_end (ap);
+}
+
+char *vstrmake (size_t *lenp, const char *fmt, va_list ap)
+{
+    size_t size = 1000;
+    char *p, *q;
+    int n;
+
+    p = malloc (size);
+    if (!p) return NULL;
+    while (1) {
+        n = vsnprintf (p, size, fmt, ap);
+        if (n < 0) size *= 2;   /* Windows */
+        else if ((unsigned)n >= size) size = n+1; /* glibc */
+        else break;
+        q = realloc (p, size);
+        if (!q) {
+          free (p);
+          return NULL;
+       }
+       p = q;
+    }
+    if (lenp) *lenp = n;
+    return p;
+}
+
+char *strmake (size_t *lenp, const char *fmt, ...)
+{
+    va_list ap;
+    char *p;
+
+    va_start (ap, fmt);
+    p = vstrmake (lenp, fmt, ap);
+    if (!p) fatal ("Out of memory.");
+    va_end (ap);
+    return p;
+}
--- /dev/null	2003-01-30 05:24:37.000000000 -0500
+++ programs/winetest/tests.list	2003-11-25 13:20:42.000000000 -0500
@@ -0,0 +1,18 @@
+dlls/advapi32/tests/advapi32
+dlls/comctl32/tests/comctl32
+#dlls/dsound/tests/dsound
+dlls/gdi/tests/gdi32
+dlls/kernel/tests/kernel32
+dlls/msvcrt/tests/msvcrt
+dlls/netapi32/tests/netapi32
+dlls/ntdll/tests/ntdll
+dlls/oleaut32/tests/oleaut32
+dlls/rpcrt4/tests/rpcrt4
+dlls/shell32/tests/shell32
+dlls/shlwapi/tests/shlwapi
+dlls/urlmon/tests/urlmon
+dlls/user/tests/user32
+dlls/wininet/tests/wininet
+dlls/winmm/tests/winmm
+dlls/winsock/tests/ws2_32
+dlls/winspool/tests/winspool.drv


-- 
Dimi.




More information about the wine-patches mailing list