How's this look? I've only tested it lightly, but it should
be pretty close; with this, "make test" ought to recurse
down into programs/cmd/tests and run the cmd tests, too.
Shortcomings:
- I don't think I need chomp() in tests/cmd.c anymore, that was a workaround
- I think I can turn on echo again in tests/*.cmd, that was another workaround
- I have yet to add a way to specify that a test is todo_wine
Feedback welcome. I'll try to submit something tomorrow
that takes care of the above known issues and any issues
anyone else points out.
(I'm actually kind of amazed the build machinery changes work,
I didn't really take the time to understand all of the gears and pulleys.)
From db19220c66a89018cf4497e2144b79e80428ed90 Mon Sep 17 00:00:00 2001
From: Dan Kegel <d...@pantry.(none)>
Date: Thu, 31 Dec 2009 17:09:32 -0800
Subject: [PATCH] Initial conformance test support for programs, starting with cmd.
---
.gitignore | 1 +
Makefile.in | 2 +-
configure.ac | 3 +
programs/Makefile.in | 3 +-
programs/Makeprog.rules.in | 2 +-
programs/Maketest.rules.in | 71 ++++++
programs/cmd/tests/Makefile.in | 25 ++
programs/cmd/tests/cmd.c | 441 ++++++++++++++++++++++++++++++++++
programs/cmd/tests/test_done.cmd | 51 ++++
programs/cmd/tests/test_done.out.exp | 10 +
tools/make_makefiles | 2 +
11 files changed, 608 insertions(+), 3 deletions(-)
create mode 100644 programs/Maketest.rules.in
create mode 100644 programs/cmd/tests/Makefile.in
create mode 100644 programs/cmd/tests/cmd.c
create mode 100644 programs/cmd/tests/test_done.cmd
create mode 100644 programs/cmd/tests/test_done.out.exp
diff --git a/.gitignore b/.gitignore
index 7dd1f9d..454185c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -222,6 +222,7 @@ loader/wine.de.man
loader/wine.fr.man
loader/wine.man
programs/Makeprog.rules
+programs/Maketest.rules
programs/rpcss/epm.h
programs/rpcss/epm_s.c
programs/rpcss/irot.h
diff --git a/Makefile.in b/Makefile.in
index 2d2d8b7..91a4fea 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -44,7 +44,7 @@ SUBDIRS = \
INSTALLSUBDIRS = @ALL_TOP_DIRS@
# Sub-directories to run make test into
-TESTSUBDIRS = dlls
+TESTSUBDIRS = dlls programs
all: Make.rules $(PROGRAMS)
@echo "Wine build complete."
diff --git a/configure.ac b/configure.ac
index 2a9c83a..ddf404b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2100,6 +2100,7 @@ AC_SUBST(ALL_TOP_DIRS,"")
AC_SUBST(ALL_DLL_DIRS,"")
AC_SUBST(ALL_IMPLIB_DIRS,"")
AC_SUBST(ALL_TEST_DIRS,"")
+AC_SUBST(ALL_PROGTEST_DIRS,"")
AC_SUBST(ALL_PROGRAM_DIRS,"")
AC_SUBST(ALL_PROGRAM_INSTALL_DIRS,"")
AC_SUBST(ALL_PROGRAM_BIN_INSTALL_DIRS,"")
@@ -2120,6 +2121,7 @@ WINE_CONFIG_MAKERULES([dlls/Makedll.rules],[MAKE_DLL_RULES],[Make.rules])
WINE_CONFIG_MAKERULES([dlls/Makeimplib.rules],[MAKE_IMPLIB_RULES],[Make.rules])
WINE_CONFIG_MAKERULES([dlls/Maketest.rules],[MAKE_TEST_RULES],[Make.rules])
WINE_CONFIG_MAKERULES([programs/Makeprog.rules],[MAKE_PROG_RULES],[Make.rules])
+WINE_CONFIG_MAKERULES([programs/Maketest.rules],[MAKE_PROGTEST_RULES],[Make.rules])
WINE_CONFIG_MAKEFILE([Makefile],[Make.rules])
WINE_CONFIG_MAKEFILE([dlls/Makefile],[Make.rules],[],[ALL_TOP_DIRS])
@@ -2599,6 +2601,7 @@ WINE_CONFIG_MAKEFILE([programs/Makefile],[Make.rules],[],[ALL_TOP_DIRS])
WINE_CONFIG_MAKEFILE([programs/cacls/Makefile],[programs/Makeprog.rules],[programs],[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS])
WINE_CONFIG_MAKEFILE([programs/clock/Makefile],[programs/Makeprog.rules],[programs],[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS])
WINE_CONFIG_MAKEFILE([programs/cmd/Makefile],[programs/Makeprog.rules],[programs],[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS])
+WINE_CONFIG_MAKEFILE([programs/cmd/tests/Makefile],[programs/Maketest.rules],[programs],[ALL_PROGTEST_DIRS],[enable_tests])
WINE_CONFIG_MAKEFILE([programs/cmdlgtst/Makefile],[programs/Makeprog.rules],[programs],[ALL_PROGRAM_DIRS])
WINE_CONFIG_MAKEFILE([programs/control/Makefile],[programs/Makeprog.rules],[programs],[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS])
WINE_CONFIG_MAKEFILE([programs/dxdiag/Makefile],[programs/Makeprog.rules],[programs],[ALL_PROGRAM_DIRS,ALL_PROGRAM_INSTALL_DIRS])
diff --git a/programs/Makefile.in b/programs/Makefile.in
index b339aa3..526995c 100644
--- a/programs/Makefile.in
+++ b/programs/Makefile.in
@@ -3,7 +3,8 @@ TOPOBJDIR = ..
SRCDIR = @srcdir@
VPATH = @srcdir@
-SUBDIRS = @ALL_PROGRAM_DIRS@
+PROGTESTSUBDIRS= @ALL_PROGTEST_DIRS@
+SUBDIRS = @ALL_PROGRAM_DIRS@ $(PROGTESTSUBDIRS)
INSTALLSUBDIRS = @ALL_PROGRAM_INSTALL_DIRS@
INSTALLPROGS = @ALL_PROGRAM_BIN_INSTALL_DIRS@
diff --git a/programs/Makeprog.rules.in b/programs/Makeprog.rules.in
index 77119e8..2a3f417 100644
--- a/programs/Makeprog.rules.in
+++ b/programs/Makeprog.rules.in
@@ -17,7 +17,7 @@ INSTALLDIRS = $(DESTDIR)$(bindir) $(DESTDIR)$(dlldir) $(DESTDIR)$(fakedlldir) $(
@MAKE_RULES@
-all: $(MODULE)$(DLLEXT) $(MODULE)$(FAKEEXT)
+all: $(MODULE)$(DLLEXT) $(MODULE)$(FAKEEXT) $(SUBDIRS)
$(MODULE) $(MODULE).so $(MODULE).fake: $(OBJS) Makefile.in
$(WINEGCC) $(APPMODE) $(OBJS) -o $@ $(ALL_LIBS) $(DELAYIMPORTS:%=-Wb,-d%)
diff --git a/programs/Maketest.rules.in b/programs/Maketest.rules.in
new file mode 100644
index 0000000..88b1647
--- /dev/null
+++ b/programs/Maketest.rules.in
@@ -0,0 +1,71 @@
+# Global rules for building program unit tests -*-Makefile-*-
+#
+# Each individual makefile should define the following variables:
+# TESTEXE : the exe to test
+# CTESTS : list of C test programs
+# EXTRALIBS : extra libraries to link in (optional)
+# EXTRADEFS : extra symbol definitions, like -DWINELIB (optional)
+#
+# plus all variables required by the global Make.rules.in
+#
+
+DLLFLAGS = @DLLFLAGS@
+DEFS = -DWINE_STRICT_PROTOTYPES $(EXTRADEFS)
+
+MODULE = $(TESTEXE:%.exe=%)_test.exe
+TESTRESULTS = $(CTESTS:.c=.ok)
+TESTPROGRAM = $(MODULE)$(DLLEXT)
+RUNTESTFLAGS = -q -P wine -M $(TESTEXE) -T $(TOPOBJDIR) -p $(TESTPROGRAM)
+
+C_SRCS = $(CTESTS)
+ALL_LIBS = $(IMPORTS:%=-l%) $(EXTRALIBS) $(LDFLAGS) $(LIBS)
+EXTRA_OBJS = testlist.o $(MOD_EXTRA_OBJS)
+
+CROSSTEST = $(TESTEXE:%.exe=%)_crosstest.exe
+CROSSCC = @CROSSCC@
+CROSSWINEGCC = $(TOOLSDIR)/tools/winegcc/winegcc @CROSSTARGETFLAGS@ -B$(TOOLSDIR)/tools/winebuild --sysroot=$(TOPOBJDIR)
+MAKEDEPFLAGS = -xo -xcross.o
+
+...@make_rules@
+
+all: $(TESTPROGRAM)
+
+$(MODULE) $(MODULE).so: $(OBJS) Makefile.in
+ $(WINEGCC) $(APPMODE) $(OBJS) -o $@ $(LIBPORT) $(ALL_LIBS)
+
+# Rules for building test list
+
+testlist.c: Makefile.in $(MAKECTESTS)
+ $(MAKECTESTS) -o $@ $(CTESTS)
+
+testlist.o: testlist.c $(TOPSRCDIR)/include/wine/test.h
+
+# Rules for testing
+
+check test:: $(TESTRESULTS)
+
+$(TESTRESULTS): $(MODULE)$(DLLEXT) ../$(TESTEXE).so
+
+# Rules for cross-compiling tests
+
+CROSSOBJS = $(OBJS:.o=.cross.o)
+
+.SUFFIXES: .cross.o
+
+.c.cross.o:
+ $(CROSSCC) -c $(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) -o $@ $<
+
+crosstest:: @CROSSTEST@
+
+$(CROSSTEST): $(CROSSOBJS) Makefile.in
+ $(CROSSWINEGCC) $(CROSSOBJS) -o $@ $(ALL_LIBS)
+
+$(CROSSOBJS): $(IDL_GEN_HEADERS)
+
+# Rules for cleaning
+
+testclean::
+ $(RM) $(TESTRESULTS)
+
+clean::
+ $(RM) testlist.c $(MODULE) $(TESTRESULTS) $(CROSSTEST)
diff --git a/programs/cmd/tests/Makefile.in b/programs/cmd/tests/Makefile.in
new file mode 100644
index 0000000..e62bb8a
--- /dev/null
+++ b/programs/cmd/tests/Makefile.in
@@ -0,0 +1,25 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+TESTEXE = cmd.exe
+IMPORTS = kernel32 msvcrt
+EXTRAINCL = -I$(TOPSRCDIR)/include/msvcrt
+MODCFLAGS = -fno-builtin
+
+CTESTS = \
+ cmd.c
+
+TESTRCS = tests.rc
+MOD_EXTRA_OBJS = $(TESTRCS:.rc=.res)
+
+...@make_progtest_rules@
+
+clean::
+ $(RM) $(TESTRCS)
+
+tests.rc:
+ ls test_*.cmd | sed 's/\(.*\)/\1 TESTDATA "\1"/' > tests.rc
+ ls test_*.out.exp | sed 's/\(.*\)/\1 TESTDATA "\1"/' >> tests.rc
+
+...@dependencies@ # everything below this line is overwritten by make depend
diff --git a/programs/cmd/tests/cmd.c b/programs/cmd/tests/cmd.c
new file mode 100644
index 0000000..0156018
--- /dev/null
+++ b/programs/cmd/tests/cmd.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright 2009 Dan Kegel
+ * Copyright 2009 Jacek Caban 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ *
+ * Simple conformance/regression tests for cmd
+ *
+ * Each test consists of a .cmd file (the commands to run)
+ * and a .out.exp file (stdout from cmd running the .cmd file).
+ *
+ * The files are bundled into resources in the .exe.
+ * The program unpacks them into $TEMP and cd's to $TEMP
+ * before running tests, unless WINETEST_INTERACTIVE is set.
+ *
+ * stderr is ignored at the moment (partly because it tends
+ * to be full of localized error messages that are hard to
+ * compare across versions of windows and across languages).
+ *
+ * Commands that output natural language (e.g. english)
+ * should be redirected to nul for now, so they don't
+ * fail on non-english developers' systems. We may
+ * do something nicer later.
+ *
+ * @@ is a special keyword in the .out.exp file which
+ * is expanded to the directory in which the tests are
+ * run plus a backslash before comparison.
+ *
+ * For each test file that passes on real Windows,
+ * the program saves a .out.exp.new file for use as
+ * a new reference file if desired.
+ *
+ * If WINETEST_INTERACTIVE is set, the program will read
+ * tests from the current directory instead of from its
+ * bundled resources, and will leave the logs around
+ * for you to look at.
+ *
+ * If WINETEST_DEBUG is set to 2, the program will copy
+ * the output logs verbatim to stdout.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+
+#include "wine/test.h"
+
+static char curdir[2048];
+
+/*
+ * Return TRUE if cmd.exe is available
+ */
+static int cmd_available(void)
+{
+ STARTUPINFOA si;
+ PROCESS_INFORMATION pi;
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ /* only the unicode version really needs a non-const 2nd arg? */
+ if (CreateProcessA(NULL, (char *)"cmd /c exit 0", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
+ CloseHandle(pi.hThread);
+ CloseHandle(pi.hProcess);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* copied from winetest.c */
+static int running_under_wine (void)
+{
+ HMODULE module = GetModuleHandleA("ntdll.dll");
+
+ if (!module) return 0;
+ return (GetProcAddress(module, "wine_server_call") != NULL);
+}
+
+/* Run test_name.cmd, save stdout to test_name.out, return TRUE if could create process.
+ * Throws away stderr for now, since we don't want to match Windows error messages
+ * word-for-word unless some app depends on them.
+ * Returns exit code in *pExitCode.
+ */
+static BOOL run_one_test(const char *test_name, DWORD *pExitCode)
+{
+ SECURITY_ATTRIBUTES sa;
+ STARTUPINFOA si;
+ PROCESS_INFORMATION pi;
+ HANDLE file;
+ BOOL bres = FALSE;
+ char cmdname[2048];
+ char outname[2048];
+
+ trace("run_test(%s)\n", test_name);
+ sprintf(cmdname, "%s.cmd", test_name);
+ sprintf(outname, "%s.out", test_name);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ file = CreateFileA(outname, GENERIC_WRITE, 0, &sa, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (file == INVALID_HANDLE_VALUE) {
+ skip("can't create output file %s, skipping cmd test\n", outname);
+ return FALSE;
+ }
+
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags = STARTF_USESTDHANDLES;
+ /* Should we dup these? */
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = file;
+ si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ bres = CreateProcessA(NULL, cmdname, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
+ CloseHandle(file);
+ if (!bres) {
+ skip("can't create process '%s', skipping cmd test\n", cmdname);
+ return FALSE;
+ }
+
+ WaitForSingleObject(pi.hProcess, INFINITE);
+ if (!GetExitCodeProcess(pi.hProcess, pExitCode))
+ trace("GetExitCodeProcess failed?\n");
+ else
+ trace("Exit code for %s is %d\n", cmdname, *pExitCode);
+ CloseHandle(pi.hThread);
+ CloseHandle(pi.hProcess);
+ return TRUE;
+}
+
+/* Remove trailing whitespace. Only needed until next release of Wine? */
+static void chomp(char *buf)
+{
+ char *p = buf + strlen(buf) - 1;
+ while ((p >= buf) && isspace(*p))
+ *p-- = 0;
+}
+
+/* Remove runs of three or more whitespace, leaving just two whitespaces behind.
+ * This is intended to compensate for things like the ATTRIB command,
+ * which uses more whitespace in Vista.
+ */
+static void limit_space_runs(char *buf)
+{
+ char *p = buf;
+ char *q = buf;
+ int nspaces = 0;
+ while (*p) {
+ if (isspace(*p))
+ nspaces++;
+ else
+ nspaces = 0;
+ if (nspaces < 3)
+ *q++ = *p;
+ p++;
+ }
+ *q = 0;
+}
+
+/* Rewind and read fp_in, write fname.exp.new
+ * and replace each instance of the current
+ * directory with the keyword @@ in case the
+ * user wants to use this run as the new .out.exp
+ */
+static void unexpand_curdir(FILE *fp_in, const char *fname_in)
+{
+ char fname_out[2048];
+ FILE *fp_out;
+ char buf[2048];
+ int curdirlen = strlen(curdir);
+
+ sprintf(fname_out, "%s.exp.new", fname_in);
+ trace("Test passed and this is windows, so generating new reference file %s in case you need it.\n", fname_out);
+ fp_out = fopen(fname_out, "wb");
+ if (!fp_out) {
+ trace("can't create %s\n", fname_out);
+ return;
+ }
+ rewind(fp_in);
+ while (fgets(buf, sizeof(buf), fp_in)) {
+ char *p = buf;
+ while ((p = strstr(p, curdir)) != NULL) {
+ /* implode current directory to @@ */
+ memmove(p + 2, p + curdirlen, strlen(p+curdirlen)+1);
+ memcpy(p, "@@", 2);
+ }
+ fputs(buf, fp_out);
+ }
+ fclose(fp_out);
+}
+
+/* Compare given output to reference, return 0 on identical, nonzero on different */
+/* FIXME: don't quit after first error; skip to next test in file */
+static int check_one_logfile(const char *name, const char *suffix)
+{
+ char fname_out[2048];
+ char fname_exp[2048];
+ FILE *fp_out = NULL, *fp_exp = NULL;
+ char buf_out[2048];
+ char buf_exp[2048];
+ int line;
+ int ret = 1;
+
+ sprintf(fname_out, "%s.%s", name, suffix);
+ fp_out = fopen(fname_out, "rb");
+
+ sprintf(fname_exp, "%s.%s.exp", name, suffix);
+ fp_exp = fopen(fname_exp, "rb");
+
+ if (fp_exp && ! fp_out) {
+ trace("%s exists, but %s doesn't\n", fname_exp, fname_out);
+ goto out;
+ }
+ if (!fp_exp && fp_out) {
+ trace("%s exists, but %s doesn't\n", fname_out, fname_exp);
+ goto out;
+ }
+
+ line = 0;
+ while (1) {
+ const char *pout, *pexp;
+ char *p;
+
+ line++;
+ pout = fgets(buf_out, sizeof(buf_out), fp_out);
+ pexp = fgets(buf_exp, sizeof(buf_exp), fp_exp);
+
+ if (!pout && !pexp)
+ break;
+
+ if (pexp && ! pout) {
+ trace("%s EOF before %s\n", fname_out, fname_exp);
+ goto out;
+ }
+ if (!pexp && pout) {
+ trace("%s EOF before %s\n", fname_exp, fname_out);
+ goto out;
+ }
+ p = buf_exp;
+ while ((p = strstr(p, "@@")) != NULL) {
+ /* expand @@ to current directory */
+ memmove(p + strlen(curdir), p + 2, strlen(p+2)+1);
+ memcpy(p, curdir, strlen(curdir));
+ }
+ /* trim trailing CR or LF's to work around another bug */
+ chomp(buf_exp);
+ chomp(buf_out);
+ /* compress runs of spaces to work around differences
+ * between XP and Vista in output of commands like ATTRIB
+ */
+ limit_space_runs(buf_exp);
+ limit_space_runs(buf_out);
+
+ if (strcmp(pout, pexp)) {
+ ok(FALSE, "%s and %s differ on line %d: '%s' vs '%s'\n", fname_out, fname_exp, line, pout, pexp);
+ ok(FALSE, "%s and %s differ on line %d: '%s' vs '%s'\n", fname_out, fname_exp, line, pout, pexp);
+ goto out;
+ }
+ }
+ ret= 0;
+ if (getenv("WINETEST_INTERACTIVE") != NULL && !running_under_wine()) {
+ /* As a convenience for the test developer,
+ * if the test succeeded and this is Windows,
+ * generate a .out.exp.new file with the current
+ * directory replace with the keyword @@
+ * for use as the new .out.exp reference file.
+ * (It's easy to forget to do this, so it seemed
+ * worth automating.)
+ */
+ unexpand_curdir(fp_out, fname_out);
+ }
+
+out:
+ if (fp_out)
+ fclose(fp_out);
+ if (fp_exp)
+ fclose(fp_exp);
+ return ret;
+}
+
+/* Dump a file to stdout */
+static void show_log(const char *name, const char *suffix)
+{
+ char fname_out[2048];
+ FILE *fp_out = NULL;
+ char buf_out[2048];
+
+ sprintf(fname_out, "%s.%s", name, suffix);
+ fp_out = fopen(fname_out, "rb");
+
+ if (!fp_out) {
+ trace("%s doesn't exist?\n", fname_out);
+ return;
+ }
+ while (fgets(buf_out, sizeof(buf_out), fp_out))
+ fputs(buf_out, stdout);
+}
+
+static void run_tests(BOOL verbose)
+{
+ WIN32_FIND_DATA finddata;
+ HANDLE hFind;
+
+ /* Run all test*.cmd files in current directory, compare output to test*.out.exp */
+
+ hFind = FindFirstFile("test*.cmd", &finddata);
+ if (INVALID_HANDLE_VALUE == hFind) {
+ printf("no tests?\n");
+ return;
+ }
+ do {
+ char testname[2048];
+ const char *dotcmd = strrchr(finddata.cFileName, '.');
+ int len;
+ DWORD err;
+ int pass;
+
+ if (!dotcmd) {
+ printf("bug\n");
+ continue;
+ }
+ len = dotcmd - finddata.cFileName;
+ memcpy(testname, finddata.cFileName, len);
+ testname[len] = 0;
+
+ err = 0;
+ pass = (run_one_test(testname, &err) && !check_one_logfile(testname, "out") && err == 0);
+
+ if (verbose) {
+ printf("------- Begin test %s output ---------\n", testname);
+ show_log(testname, "out");
+ printf("------- End test %s output ---------\n", testname);
+ }
+ }
+ while (FindNextFile(hFind, &finddata) != 0);
+}
+
+static BOOL CALLBACK unpack_file_proc(HMODULE hModule, LPCTSTR lpszType,
+ LPTSTR lpszName, LONG_PTR lParam)
+{
+ DWORD size = 0;
+ HGLOBAL hdl = 0;
+ HRSRC rsrc = 0;
+ LPVOID addr = 0;
+ int nwritten;
+ FILE *fp;
+
+trace("unpack_file_proc(%s)\n", lpszName);
+ if (!(rsrc = FindResourceA(NULL, lpszName, lpszType)) ||
+ !(size = SizeofResource(0, rsrc)) ||
+ !(hdl = LoadResource(0, rsrc)) ||
+ !(addr = LockResource(hdl))) {
+ trace("Can't get resource %s; rsrc %p size %u hdl %p addr %p\n",
+ lpszName, rsrc, size, hdl, addr);
+ return FALSE;
+ }
+
+ fp = fopen(lpszName, "wb");
+ if (!fp) {
+ perror(lpszName);
+ return FALSE;
+ }
+ nwritten = fwrite(addr, 1, size, fp);
+ if (fclose(fp) || size != nwritten) {
+ perror(lpszName);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Unpack all the TESTDATA resources to the given directory */
+int extract_test_data(void)
+{
+ return EnumResourceNames (NULL, "TESTDATA", unpack_file_proc, 0);
+}
+
+START_TEST(cmd)
+{
+ BOOL nounpack = FALSE;
+ BOOL verbose = FALSE;
+
+ if (!cmd_available()) {
+ skip("cmd not installed, skipping cmd tests\n");
+ return;
+ }
+
+ /* If WINETEST_INTERACTIVE is set, just run whatever tests are in current directory */
+ nounpack = (winetest_interactive != 0);
+ /* If WINETEST_DEBUG > 1, show test output */
+ verbose = (winetest_debug > 1);
+
+ if (!nounpack) {
+ char tempdir[2048];
+
+ GetTempPath(sizeof(tempdir), tempdir);
+
+ /* If not nounpack, create an empty temporary directory,
+ * cd to it, and unpack the bundled tests there.
+ * FIXME: just work in system temp directory for now.
+ */
+ SetCurrentDirectory(tempdir);
+ system("erase TEST*.* > nul 2> nul");
+
+ if (!extract_test_data()) {
+ ok(FALSE, "extraction failed\n");
+ return;
+ }
+ }
+
+ /* Run the tests. */
+ GetCurrentDirectory(sizeof(curdir), curdir);
+ if (curdir[strlen(curdir)-1] != '\\')
+ strcat(curdir, "\\");
+ trace("Running tests in directory %s\n", curdir);
+ run_tests(verbose);
+
+ if (!nounpack) {
+ /* Remove our temporary directory.
+ * FIXME: just remove our files from system temp directory for now.
+ */
+ system("erase TEST*.*");
+ }
+}
diff --git a/programs/cmd/tests/test_done.cmd b/programs/cmd/tests/test_done.cmd
new file mode 100644
index 0000000..a672d0e
--- /dev/null
+++ b/programs/cmd/tests/test_done.cmd
@@ -0,0 +1,51 @@
+...@prompt C:\fake$G
+...@echo off
+
+rem Easy cmd tests
+rem Only include tests that pass in Wine in this file
+rem Note: test scripts must start with "@prompt C:\fake$G" so
+rem it doesn't matter what directory they're run from.
+rem Unforunately we need to turn echo off for now because
+rem Wine doesn't output a blank line before each prompt like Windows does.
+
+rem Tests for echo
+echo word
+echo 'singlequotedword'
+echo "doublequotedword"
+...@echo at-echoed-word
+echo "/?"
+echo.
+echo .
+
+rem Tests for rem
+rem First, set errorlevel to 1, then make sure it got set
+rem cmd /c exit 1 doesn't seem to work on nt, so try using exit /b in a subroutine
+goto :afterseterr
+:seterr
+exit /b 1
+goto :eof
+:afterseterr
+
+call seterr
+if errorlevel 1 echo yep, errorlevel is 1
+rem Now check whether rem resets errorlevel to zero
+if errorlevel 1 echo yep, errorlevel is still 1
+
+rem Add tests for more commands here
+
+rem Add tests for fixed bugs here
+
+rem Bug 1370
+rem "start cannot be found in wcmd"
+echo exit > bug1370.bat
+start /wait bug1370.bat
+del bug1370.bat
+
+rem Bug 1564
+rem "in wcmd.exe, "if exist %directory%" always false"
+mkdir bug_1564
+if exist bug_1564 echo "yup, it's there"
+rmdir bug_1564
+
+rem echo Done
+cmd /c exit 0
diff --git a/programs/cmd/tests/test_done.out.exp b/programs/cmd/tests/test_done.out.exp
new file mode 100644
index 0000000..ba85329
--- /dev/null
+++ b/programs/cmd/tests/test_done.out.exp
@@ -0,0 +1,10 @@
+word
+'singlequotedword'
+"doublequotedword"
+at-echoed-word
+"/?"
+
+.
+yep, errorlevel is 1
+yep, errorlevel is still 1
+"yup, it's there"
diff --git a/tools/make_makefiles b/tools/make_makefiles
index 3ebe273..3458db0 100755
--- a/tools/make_makefiles
+++ b/tools/make_makefiles
@@ -29,6 +29,7 @@ my %makerules =
"MAKE_IMPLIB_RULES" => "dlls/Makeimplib.rules",
"MAKE_TEST_RULES" => "dlls/Maketest.rules",
"MAKE_PROG_RULES" => "programs/Makeprog.rules",
+ "MAKE_PROGTEST_RULES" => "programs/Maketest.rules",
);
# Programs that we want to install in the bin directory too
@@ -346,6 +347,7 @@ sub update_makefiles(@)
}
elsif ($rules eq $makerules{"MAKE_IMPLIB_RULES"}) { $args = ",[dlls],[ALL_IMPLIB_DIRS]"; }
elsif ($rules eq $makerules{"MAKE_TEST_RULES"}) { $args = ",[dlls],[ALL_TEST_DIRS],[enable_tests]"; }
+ elsif ($rules eq $makerules{"MAKE_PROGTEST_RULES"}) { $args = ",[programs],[ALL_PROGTEST_DIRS],[enable_tests]"; }
elsif ($rules eq $makerules{"MAKE_PROG_RULES"})
{
(my $name = $file) =~ s/^programs\/(.*)\/Makefile/$1/;
--
1.6.3.3