Allow IDL files and RPC clients in tests

Dan Hipschman dsh at linux.ucla.edu
Wed Apr 18 23:39:02 CDT 2007


This extends the test framework to allow for tests that need IDL files and RPC
clients.  It also includes a single small sample test that I plan to extend if
this gets accepted.  With this, we can test the stub files that widl generates.
The sample test passed on XP SP1 (I couldn't test on SP2, as that machine lies
behind a very strict corporate firewall).  I also ran winetest with success.

---
 dlls/Maketest.rules.in              |   22 ++++++++----
 dlls/rpcrt4/tests/ClientMakefile.in |   15 ++++++++
 dlls/rpcrt4/tests/Makefile.in       |    5 ++-
 dlls/rpcrt4/tests/server.c          |   65 +++++++++++++++++++++++++++++++++
 dlls/rpcrt4/tests/server.idl        |   34 +++++++++++++++++
 dlls/rpcrt4/tests/server_test.c     |   42 +++++++++++++++++++++
 include/wine/test.h                 |   69 +++++++++++++++++++++++++++++++++++
 programs/winetest/main.c            |   38 ++++++++++++++++----
 tools/make_makefiles                |   16 ++++++--
 9 files changed, 287 insertions(+), 19 deletions(-)
 create mode 100644 dlls/rpcrt4/tests/ClientMakefile.in
 create mode 100644 dlls/rpcrt4/tests/server.c
 create mode 100644 dlls/rpcrt4/tests/server.idl
 create mode 100644 dlls/rpcrt4/tests/server_test.c

diff --git a/dlls/Maketest.rules.in b/dlls/Maketest.rules.in
index 73bf4d9..56b7e27 100644
--- a/dlls/Maketest.rules.in
+++ b/dlls/Maketest.rules.in
@@ -8,20 +8,25 @@
 #
 # plus all variables required by the global Make.rules.in
 #
+# Optionally, the variable CLIENT may be set to the value `client' if the
+# module to be built is a test client for an RPC server.
+#
 
 DLLFLAGS     = @DLLFLAGS@
-DEFS         = $(EXTRADEFS)
+DEFS         = $(EXTRADEFS) -DTESTBASE=\"$(DLLBASE)\"
 
-MODULE       = $(TESTDLL:%.dll=%)_test.exe
+DLLBASE      = $(TESTDLL:%.dll=%)
+MODULE       = $(DLLBASE)_test$(CLIENT).exe
 TESTRESULTS  = $(CTESTS:.c=.ok)
 TESTPROGRAM  = $(MODULE)$(DLLEXT)
 RUNTESTFLAGS = -q -P wine -M $(TESTDLL) -T $(TOPOBJDIR) -p $(TESTPROGRAM)
 
 C_SRCS       = $(CTESTS)
 ALL_LIBS     = $(IMPORTS:%=-l%) $(EXTRALIBS) $(LDFLAGS) $(LIBS)
-EXTRA_OBJS   = testlist.o
+TESTLIST     = test$(CLIENT)list
+EXTRA_OBJS   = $(TESTLIST).o
 
-CROSSTEST    = $(TESTDLL:%.dll=%)_crosstest.exe
+CROSSTEST    = $(TESTDLL:%.dll=%)_crosstest$(CLIENT).exe
 CROSSOBJS    = $(C_SRCS:.c=.cross.o) $(RC_SRCS:.rc=.res.cross.o) $(BISON_SRCS:.y=.tab.cross.o) $(LEX_SRCS:.l=.yy.cross.o) $(IDL_GEN_C_SRCS:.c=.cross.o) testlist.cross.o
 CROSSCC      = @CROSSCC@
 CROSSWINDRES = @CROSSWINDRES@
@@ -29,6 +34,7 @@ CROSSWINDRES = @CROSSWINDRES@
 @MAKE_RULES@
 
 all: $(TESTPROGRAM)
+	if test x"$(CLIENT)" = x && test -f ClientMakefile ; then $(MAKE) -f ClientMakefile ; fi
 
 # Rules for .so main module
 
@@ -42,10 +48,10 @@ $(MODULE): $(OBJS) $(RCOBJS) Makefile.in
 
 # Rules for building test list
 
-testlist.c: Makefile.in $(MAKECTESTS)
+$(TESTLIST).c: Makefile.in $(MAKECTESTS)
 	$(MAKECTESTS) -o $@ $(CTESTS)
 
-testlist.o: testlist.c $(TOPSRCDIR)/include/wine/test.h
+$(TESTLIST).o: $(TESTLIST).c $(TOPSRCDIR)/include/wine/test.h
 
 # Rules for testing
 
@@ -72,6 +78,8 @@ $(CROSSTEST): $(CROSSOBJS) Makefile.in
 
 testclean::
 	$(RM) $(TESTRESULTS)
+	if test x"$(CLIENT)" = x && test -f ClientMakefile ; then $(MAKE) -f ClientMakefile testclean ; fi
 
 clean::
-	$(RM) testlist.c $(MODULE) $(TESTRESULTS) $(CROSSTEST)
+	$(RM) $(TESTLIST).c $(MODULE) $(TESTRESULTS) $(CROSSTEST)
+	if test x"$(CLIENT)" = x && test -f ClientMakefile ; then $(MAKE) -f ClientMakefile clean ; fi
diff --git a/dlls/rpcrt4/tests/ClientMakefile.in b/dlls/rpcrt4/tests/ClientMakefile.in
new file mode 100644
index 0000000..69031e9
--- /dev/null
+++ b/dlls/rpcrt4/tests/ClientMakefile.in
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+TESTDLL   = rpcrt4.dll
+IMPORTS   = rpcrt4 kernel32
+EXTRALIBS = -luuid
+CLIENT    = client
+
+CTESTS = server_test.c
+IDL_C_SRCS = server.idl
+
+ at MAKE_TEST_RULES@
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/dlls/rpcrt4/tests/Makefile.in b/dlls/rpcrt4/tests/Makefile.in
index 036cd21..c4b5be3 100644
--- a/dlls/rpcrt4/tests/Makefile.in
+++ b/dlls/rpcrt4/tests/Makefile.in
@@ -10,7 +10,10 @@ CTESTS = \
 	cstub.c \
 	generated.c \
 	ndr_marshall.c \
-	rpc.c
+	rpc.c \
+	server.c
+
+IDL_S_SRCS = server.idl
 
 @MAKE_TEST_RULES@
 
diff --git a/dlls/rpcrt4/tests/server.c b/dlls/rpcrt4/tests/server.c
new file mode 100644
index 0000000..3970394
--- /dev/null
+++ b/dlls/rpcrt4/tests/server.c
@@ -0,0 +1,65 @@
+/*
+ * A simple RPC server.
+ *
+ * Copyright (C) Dan Hipschman 2007
+ *
+ * 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
+ */
+
+#include "wine/test.h"
+#include "server.h"
+
+int
+Test(void)
+{
+  return TESTCODE;
+}
+
+HANDLE event;
+
+void
+Stop(void)
+{
+  ok(RPC_S_OK == RpcMgmtStopServerListening(NULL),
+     "RpcMgmtStopServerListening failed\n");
+  ok(RPC_S_OK == RpcServerUnregisterIf(NULL, NULL, FALSE),
+     "RpcServerUnregisterIf failed\n");
+  ok(SetEvent(event), "SetEvent failed\n");
+}
+
+START_TEST(server)
+{
+  static unsigned char protseq[] = PROTSEQ;
+  static unsigned char endpoint[] = ENDPOINT;
+
+  ok(RPC_S_OK == RpcServerUseProtseqEp(protseq, 20, endpoint, NULL),
+     "RpcServerUseProtseqEp failed\n");
+  ok(RPC_S_OK == RpcServerRegisterIf(IServer_v0_0_s_ifspec, NULL, NULL),
+     "RpcServerRegisterIf failed\n");
+  ok(RPC_S_OK == RpcServerListen(1, 20, TRUE),
+     "RpcServerListen failed\n");
+
+  event = CreateEvent(NULL, FALSE, FALSE, NULL);
+  ok(event != NULL, "CreateEvent failed\n");
+
+  ok(winetest_run_testclient("server_test"), "client process failed\n");
+
+  ok(WAIT_OBJECT_0 == WaitForSingleObject(event, INFINITE),
+     "WaitForSingleObject failed\n");
+todo_wine {
+  ok(RPC_S_OK == RpcMgmtWaitServerListen(),
+     "RpcMgmtWaitServerListening failed\n");
+}
+}
diff --git a/dlls/rpcrt4/tests/server.idl b/dlls/rpcrt4/tests/server.idl
new file mode 100644
index 0000000..80b6add
--- /dev/null
+++ b/dlls/rpcrt4/tests/server.idl
@@ -0,0 +1,34 @@
+/*
+ * A simple interface to test the RPC server.
+ *
+ * Copyright (C) Dan Hipschman 2007
+ *
+ * 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
+ */
+
+/* So the server and client are always on the same page.  */
+cpp_quote("#define PROTSEQ \"ncacn_ip_tcp\"")
+cpp_quote("#define ENDPOINT \"4114\"")
+cpp_quote("#define TESTCODE 4114")
+
+[
+  uuid(00000000-4114-0704-1701-000000000000),
+  implicit_handle(handle_t IServer_IfHandle)
+]
+interface IServer
+{
+  int Test(void);
+  void Stop(void);
+}
diff --git a/dlls/rpcrt4/tests/server_test.c b/dlls/rpcrt4/tests/server_test.c
new file mode 100644
index 0000000..449fd1b
--- /dev/null
+++ b/dlls/rpcrt4/tests/server_test.c
@@ -0,0 +1,42 @@
+/*
+ * A simple RPC client.
+ *
+ * Copyright (C) Dan Hipschman 2007
+ *
+ * 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
+ */
+
+#include "wine/test.h"
+#include "server.h"
+
+START_TEST(server_test)
+{
+  static unsigned char protseq[] = PROTSEQ;
+  static unsigned char endpoint[] = ENDPOINT;
+  static unsigned char address[] = "127.0.0.1";
+  unsigned char *binding;
+
+  ok(RPC_S_OK == RpcStringBindingCompose(NULL, protseq, address,
+                                         endpoint, NULL, &binding),
+     "RpcStringBindingCompose failed\n");
+  ok(RPC_S_OK == RpcBindingFromStringBinding(binding, &IServer_IfHandle),
+     "RpcBindingFromStringBinding failed\n");
+
+  ok(Test() == TESTCODE, "RPC `Test' returned the wrong result\n");
+  Stop();
+
+  ok(RPC_S_OK == RpcStringFree(&binding), "RpcStringFree failed\n");
+  ok(RPC_S_OK == RpcBindingFree(&IServer_IfHandle), "RpcBindingFree failed\n");
+}
diff --git a/include/wine/test.h b/include/wine/test.h
index ea79475..9cbab0f 100644
--- a/include/wine/test.h
+++ b/include/wine/test.h
@@ -25,6 +25,10 @@
 #include <stdlib.h>
 #include <windef.h>
 #include <winbase.h>
+#ifdef STANDALONE
+#include <rpc.h>
+#include <rpcndr.h>
+#endif
 
 #ifdef __WINE_WINE_LIBRARY_H
 #error wine/library.h should not be used in Wine tests
@@ -57,6 +61,7 @@ extern void winetest_start_todo( const char* platform );
 extern int winetest_loop_todo(void);
 extern void winetest_end_todo( const char* platform );
 extern int winetest_get_mainargs( char*** pargv );
+extern int winetest_run_testclient( const char *subtest );
 
 #ifdef STANDALONE
 #define START_TEST(name) \
@@ -187,6 +192,70 @@ typedef struct
 } tls_data;
 static DWORD tls_index;
 
+
+/* Include these for tests that use widl */
+void __RPC_FAR *__RPC_USER midl_user_allocate(size_t n)
+{
+    return malloc(n);
+}
+
+void __RPC_USER midl_user_free(void __RPC_FAR *p)
+{
+    free(p);
+}
+
+
+static int file_exists(const char *name)
+{
+    HANDLE h = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ,
+                           NULL, OPEN_EXISTING, 0, 0);
+
+    if (h != INVALID_HANDLE_VALUE) {
+        CloseHandle(h);
+        return 1;
+    } else
+        return 0;
+}
+
+int winetest_run_testclient(const char *subtest)
+{
+    size_t namelen = strlen(TESTBASE) + 11; /* 11 == strlen("_testclient") */
+    char *filename = malloc(namelen + 8); /* 8 == strlen(".exe.so") + 1 */
+    char *cmdline = malloc(namelen + strlen(subtest) + 9);
+    int status = 0;
+
+    if (filename != NULL && cmdline != NULL)
+    {
+        STARTUPINFO startup_info;
+        PROCESS_INFORMATION client_info;
+        DWORD client_exit;
+
+        sprintf(filename, "%s_testclient.exe.so", TESTBASE);
+        if (!file_exists(filename))
+            filename[namelen + 4] = '\0';
+        sprintf(cmdline, "%s %s", filename, subtest);
+
+        ZeroMemory(&startup_info, sizeof startup_info);
+        startup_info.cb = sizeof startup_info;
+
+        if (CreateProcess(filename, cmdline, NULL, NULL, FALSE, 0,
+                          NULL, NULL, &startup_info, &client_info)
+            && WAIT_OBJECT_0 == WaitForSingleObject(client_info.hProcess, INFINITE)
+            && GetExitCodeProcess(client_info.hProcess, &client_exit)
+            && CloseHandle(client_info.hProcess)
+            && CloseHandle(client_info.hThread)
+            && client_exit == 0)
+        {
+            status = 1;
+        }
+    }
+
+    free(cmdline);
+    free(filename);
+
+    return status;
+}
+
 static tls_data* get_tls_data(void)
 {
     tls_data* data;
diff --git a/programs/winetest/main.c b/programs/winetest/main.c
index f743ffc..7e5c1bc 100644
--- a/programs/winetest/main.c
+++ b/programs/winetest/main.c
@@ -36,6 +36,7 @@
 #  include <unistd.h>
 #endif
 #include <windows.h>
+#include <tchar.h>
 
 #include "winetest.h"
 #include "resource.h"
@@ -248,9 +249,14 @@ extract_test (struct wine_test *test, const char *dir, LPTSTR res_name)
     CharLowerA( test->name );
     test->exename = strmake (NULL, "%s/%s", dir, test->name);
     exepos = strstr (test->name, "_test.exe");
-    if (!exepos) report (R_FATAL, "Not an .exe file: %s", test->name);
-    *exepos = 0;
-    test->name = xrealloc (test->name, exepos - test->name + 1);
+    if (!exepos) {
+        if (!strstr (test->name, "_testclient.exe"))
+            report (R_FATAL, "Not an .exe file: %s", test->name);
+    } else {
+        *exepos = 0;
+        test->name = xrealloc (test->name, exepos - test->name + 1);
+    }
+    xprintf ("Extracting %s\n", test->name);
     report (R_STEP, "Extracting: %s", test->name);
 
     if (!(fout = fopen (test->exename, "wb")) ||
@@ -424,7 +430,13 @@ static BOOL CALLBACK
 EnumTestFileProc (HMODULE hModule, LPCTSTR lpszType,
                   LPTSTR lpszName, LONG_PTR lParam)
 {
-    (*(int*)lParam)++;
+    char *name = xstrdup (lpszName);
+    CharLowerA (name);
+
+    if (strstr (name, "_test.exe"))
+        (*(int*)lParam)++;
+
+    free (name);
     return TRUE;
 }
 
@@ -433,9 +445,21 @@ extract_test_proc (HMODULE hModule, LPCTSTR lpszType,
                    LPTSTR lpszName, LONG_PTR lParam)
 {
     const char *tempdir = (const char *)lParam;
-    get_subtests( tempdir, &wine_tests[nr_of_files], lpszName );
-    nr_of_tests += wine_tests[nr_of_files].subtest_count;
-    nr_of_files++;
+    char *name = xstrdup (lpszName);
+    CharLowerA (name);
+
+    if (strstr (name, "_test.exe")) {
+        get_subtests (tempdir, &wine_tests[nr_of_files], lpszName);
+        nr_of_tests += wine_tests[nr_of_files].subtest_count;
+        nr_of_files++;
+    } else {
+      struct wine_test dummy;
+      extract_test (&dummy, tempdir, lpszName);
+      free (dummy.name);
+      free (dummy.exename);
+    }
+
+    free (name);
     return TRUE;
 }
 
diff --git a/tools/make_makefiles b/tools/make_makefiles
index 90f9220..4824162 100755
--- a/tools/make_makefiles
+++ b/tools/make_makefiles
@@ -205,11 +205,11 @@ sub parse_makefile($)
 
 if (-d ".git")
 {
-    @makefiles = map { s/\.in$//; $_; } split /\s/, `git ls-files -c Makefile.in \\*/Makefile.in`;
+    @makefiles = map { s/\.in$//; $_; } split /\s/, `git ls-files -c Makefile.in \\*/Makefile.in \\*/ClientMakefile.in`;
 }
 else
 {
-    @makefiles = map { s/^\.\/(.*)\.in/$1/; $_; } split(/\s/,`find . -name Makefile.in -print`);
+    @makefiles = map { s/^\.\/(.*)\.in/$1/; $_; } split(/\s/,`find . -name Makefile.in -o -name ClientMakefile.in -print`);
 }
 
 foreach my $file (sort values %makerules, @makefiles)
@@ -244,14 +244,16 @@ replace_in_file( "configure.ac", '^MAKE_RULES', '^AC_OUTPUT$', @lines);
 
 sub update_winetest(@)
 {
-    my (@tests, @lines);
+    my (@tests, @testclients, @lines);
 
     foreach my $file (@_)
     {
         if ($file =~ /^dlls\/(.*)\/tests\/Makefile/) { push @tests, $1; }
+        elsif ($file =~ /^dlls\/(.*)\/tests\/ClientMakefile/) { push @testclients, $1; }
     }
     push @lines, "TESTBINS =";
     push @lines, map { " \\\n\t" . $_ . "_test.exe"; } sort @tests;
+    push @lines, map { " \\\n\t" . $_ . "_testclient.exe"; } sort @testclients;
     push @lines, "\n\n";
 
     foreach my $test (sort @tests)
@@ -259,12 +261,18 @@ sub update_winetest(@)
         push @lines, "${test}_test.exe: \$(DLLDIR)/$test/tests/${test}_test.exe\$(DLLEXT)\n";
         push @lines, "\tcp \$(DLLDIR)/$test/tests/${test}_test.exe\$(DLLEXT) \$\@ && \$(STRIP) \$\@\n";
     }
+    foreach $test (sort @testclients)
+    {
+        push @lines, "${test}_testclient.exe: \$(DLLDIR)/$test/tests/${test}_testclient.exe\$(DLLEXT)\n";
+        push @lines, "\tcp \$(DLLDIR)/$test/tests/${test}_testclient.exe\$(DLLEXT) \$\@ && \$(STRIP) \$\@\n";
+    }
     push @lines, "\n# Special rules\n";
 
     replace_in_file( "programs/winetest/Makefile.in", '^TESTBINS\s*=', '^# Special rules', @lines );
 
     replace_in_file( "programs/winetest/winetest.rc", ' TESTRES ', undef,
-                     map { $_ . "_test.exe TESTRES \"" . $_ . "_test.exe\"\n"; } sort @tests );
+                     (map { $_ . "_test.exe TESTRES \"" . $_ . "_test.exe\"\n"; } sort @tests),
+                     map { $_ . "_testclient.exe TESTRES \"" . $_ . "_testclient.exe\"\n"; } sort @testclients);
 
     # return a list of test exe files for .gitignore
     return map { "programs/winetest/" . $_ . "_test.exe"; } sort @tests;



More information about the wine-patches mailing list