Running conformance tests on Windows

Francois Gouget fgouget at free.fr
Mon Sep 16 05:48:16 CDT 2002


The attached script will let you compile and run the conformance tests
on Windows using Visual C++. Here's how:

 * first save the script as program/winetest/make_win32
 * then from Wine's top directory run
   ./program/winetest/make_win32 --zip
 * this will generate Visual C++ makefiles, and then a zip file
containing all you need.
 * uncompress this zip file on a Windows machine
 * retrieve the latest MS Windows SDK. The Visual C++ 6 headers are not
sufficient to compile the conformance tests.
 * run the SDK's SetEnv.bat script
 * in the top directory type:
   nmake -f wine_tests.mak

This will recursively build and run the tests. You can also go to a test
directory and just build or run the tests in that directory:

    cd dlls\ntdll\tests
    rem Just build the tests in that directory
    nmake -f wine_tests.mak build
    rem now run them too
    nmake -f wine_tests.mak test
    rem delete the result files
    nmake -f wine_tests.mak testclean
    rem re-run the tests, equivalent to the 'test' target
    nmake -f wine_tests.mak
    rem clean everything
    nmake -f wine_tests.mak clean

The zip file will also contain a batch file that can be used to run the
tests once compiled. So what you can do is compile the tests, and then
make a new zip file that contains just that batch file and the test
executables. This will make it possible for someone with no compiler to
run the tests.

   Oh, and make-win32 --help is your friend.


Early results:

Well, no very good.
 * First compiling the tests is not a given. The requirement of using a
recent Microsoft Windows SDK is quite annoying but there appears to be
no way around that.
 * Second, I would really like to be able to compile the tests directly
from Wine's sources just by mounting them via Samba. But then I get into
big mess between Wine's headers and the Windows headers. I'll keep
poking at this.
 * Many tests don't compile. But that's ok, I have patches pending that
solve most issues so that should no be an problem for long anymore.
 * And once you get them compiled quite a few of them fail! :-(
   That's why it is ultra important to run the conformance tests on Windows.


Changelog:

 * programs/winetest/make_win32
   programs/winetest/runtest.bat

   Script to generate Visual C++ makefiles and this make it possible to
compile and run the tests on Windows.


-- 
Francois Gouget         fgouget at free.fr        http://fgouget.free.fr/
     The software said it requires Win95 or better, so I installed Linux.
-------------- next part --------------
#!/usr/bin/perl -w
use strict;

# TODO:
# * generate makefiles for Cygwin/Mingw/etc.
# * make it possible to rezip just the executables and runtests.bat
# * generate dsp/dsw files?
# * make it possible to compile the tests directly from Wine's sources


my $name0=$0;
$name0 =~ s+^.*/++;

my $basename="wine_tests";

my $topdir;
my $do_zip;
my $do_tgz;
my $do_msvc=1;
my $to_archive;


###
#
# Helper functions
#
###

sub path_to_dotdot($)
{
    my $totop=$_[0];
    if (-f $totop)
    {
        $totop=~ s%/[^/]*$%%;
    }
    else
    {
        $totop=~ s%/$%%;
    }
    if ($totop !~ /^\//)
    {
        $totop=~ s%^./%%;
        $totop=~ s%/./%%;
        $totop=~ s%[^/]+%..%g;
    }
    return $totop;
}

###
#
# Generate Visual C++ Makefiles
#
###

sub write_list($)
{
    my $list=$_[0];

    foreach my $item (@$list)
    {
        print MAKEFILE " \\\r\n\t$item";
    }
    print MAKEFILE "\r\n" if (@$list);
}

sub write_msvc_makefile($$$$$)
{
    my $filename=$_[0];
    my $module=$_[1];
    my $imports=$_[2];
    my $perl_tests=$_[3];
    my $c_tests=$_[4];

    open(MAKEFILE,">$filename") || die "Could not write to $filename\n";

    my $totop=path_to_dotdot($filename);
    $totop=~ s+/+\\+g;
    print MAKEFILE "TOTOP = $totop\r\n";
    print MAKEFILE "\r\n";
    my $msvc_module=@$module[0];
    $msvc_module=~s/\.dll$//;
    print MAKEFILE "MODULE=$msvc_module\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "IMPORTS = ";
    my @msvc_imports=();
    foreach my $dll (@$imports)
    {
        push @msvc_imports, "$dll.lib" if (defined $dll and $dll ne "ntdll");
    }
    print "import=[",join("|", at msvc_imports),"]\n";
    push @msvc_imports, "$msvc_module.lib" if ($msvc_module ne "ntdll");
    write_list(\@msvc_imports);
    print MAKEFILE "\r\n";
    # FIXME: We don't handle the Perl tests yet
    print MAKEFILE "PLTESTS = ";
    write_list($perl_tests);
    print MAKEFILE "\r\n";
    print MAKEFILE "CTESTS = ";
    write_list($c_tests);
    print MAKEFILE "\r\n";
    print MAKEFILE "CC = cl -nologo -c\r\n";
    print MAKEFILE "CFLAGS = -DWIN32 -D_X86_ -D__i386__\r\n";
    print MAKEFILE "INCLUDE=\$(TOTOP)\\include;\$(INCLUDE)\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "RUNTEST      = \$(TOTOP)\\programs\\winetest\\runtest.bat\r\n";
    print MAKEFILE "RUNTESTFLAGS = \r\n";
    print MAKEFILE "TESTRESULTS  = \$(CTESTS:.c=.msvcok)\r\n";
    print MAKEFILE "TESTPROGRAM  = \$(MODULE)_test\r\n";
    print MAKEFILE "TESTLIST     = testlist.c\r\n";
    print MAKEFILE "TESTOBJS     = \$(TESTMAIN) \$(TESTLIST:.c=.obj) \$(CTESTS:.c=.obj)\r\n";
    print MAKEFILE "TESTMAIN     = \$(TOTOP)\\programs\\winetest\\wtmain.obj\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "all: build test\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "build: \$(TESTPROGRAM).exe\r\n";
    print MAKEFILE "test: build \$(CTESTS:.c=.msvcok)\r\n";
    print MAKEFILE "clean: testclean\r\n";
    print MAKEFILE "\tdel \$(TESTPROGRAM).exe \$(CTESTS:.c=.obj) 2>nul\r\n";
    print MAKEFILE "testclean:\r\n";
    print MAKEFILE "\tdel \$(CTESTS:.c=.msvcok) 2>nul\r\n";
    print MAKEFILE "\r\n";

    print MAKEFILE "\$(CTESTS:.c=.msvcok): \$(TESTPROGRAM).exe\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE ".c.msvcok:\r\n";
    print MAKEFILE "\t\$(RUNTEST) \$(TESTPROGRAM).exe \$(<B) \$\@ \$(RUNTESTFLAGS)\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE ".c.obj:\r\n";
    print MAKEFILE "\t\$(CC) \$(CFLAGS) -Fo\$*.obj \$<\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "\$(TESTPROGRAM).exe: \$(TESTOBJS)\r\n";
    print MAKEFILE "\tlink -nologo -out:\$\@ \$(LDFLAGS) \$(TESTOBJS) \$(IMPORTS:%=%.lib)\r\n";

    close(MAKEFILE);

    # List the files that need to be archived
    print "building archive list\n";
    my $dirname=$filename;
    $dirname =~ s+/[^/]*$++;
    $to_archive->{$filename}=1;
    foreach my $file (@$perl_tests,@$c_tests)
    {
        print "   $dirname/$file\n";
        $to_archive->{"$dirname/$file"}=1;
    }
    if (@$c_tests)
    {
        $to_archive->{"$dirname/testlist.c"}=1;
    }
}

sub write_msvc_top_makefile($$)
{
    my $filename=$_[0];
    my $subdirs=$_[1];

    open(MAKEFILE,">$filename") || die "Could not write to $filename\n";

    print MAKEFILE "SUBDIRS = ";
    # FIXME: is this modifying the content of $subdirs"
    my @dirs=map { s+^./++; s+/+\\+g; "$_.dir"; } @$subdirs;
    write_list(\@dirs);
    print MAKEFILE "\r\n";
    print MAKEFILE "all: test\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "build: \$(SUBDIRS:.dir=.build)\r\n";
    print MAKEFILE "\$(SUBDIRS:.dir=.build):\r\n";
    print MAKEFILE "\tcd \$*\r\n";
    print MAKEFILE "\tnmake -nologo -f $basename.mak build\r\n";
    print MAKEFILE "\tcd \$(MAKEDIR)\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "test: \$(SUBDIRS:.dir=.test)\r\n";
    print MAKEFILE "\$(SUBDIRS:.dir=.test):\r\n";
    print MAKEFILE "\tcd \$*\r\n";
    print MAKEFILE "\tnmake -nologo -f $basename.mak test\r\n";
    print MAKEFILE "\tcd \$(MAKEDIR)\r\n";
    print MAKEFILE "\r\n";
    print MAKEFILE "clean: \$(SUBDIRS:.dir=.clean)\r\n";
    print MAKEFILE "\$(SUBDIRS:.dir=.clean):\r\n";
    print MAKEFILE "\tcd \$*\r\n";
    print MAKEFILE "\tnmake -nologo -f $basename.mak clean\r\n";
    print MAKEFILE "\tcd \$(MAKEDIR)\r\n";

    close(MAKEFILE);

    # List the files that need to be archived
    my $dirname=$filename;
    $dirname =~ s+/[^/]*$++;
    $to_archive->{$filename}=1;
    $to_archive->{"$dirname/include/wine/test.h"}=1;
    $to_archive->{"$dirname/include/winternl.h"}=1;
    $to_archive->{"$dirname/programs/winetest/wtmain.c"}=1;
    $to_archive->{"$dirname/programs/winetest/runtest.bat"}=1;
}


###
#
# Generate Mingw/Cygwin Makefiles
#
###

# To change the default location of the Cygwin / Mingw / Wine headers,
# modify the following variables.
my $cygwin_windows_includes ="/usr/include/w32api";
my $mingw_windows_includes ="/mingw/include";
my $wine_windows_includes ="./include";

sub write_cyg_makefile($$$$$)
{
    my $filename=$_[0];
    my $module=$_[1];
    my $imports=$_[2];
    my $perl_tests=$_[3];
    my $c_tests=$_[4];

    # FIXME: TODO
}


###
#
# Generate a batch file to run all the tests
# (for those who don't have a C compiler on Windows)
#
###

sub write_runtests_bat($$$$$)
{
    my $filename=$_[0];
    my $module=$_[1];
    my $imports=$_[2];
    my $perl_tests=$_[3];
    my $c_tests=$_[4];

    open(FILE,">$filename") || die "Could not write to $filename\n";

    my $totop=path_to_dotdot($filename);
    $totop=~ s+/+\\+g;
    my $module=@$module[0];
    $module=~s/\.dll$//;

    print FILE "\r\n";
    print FILE "nmake /f wine_tests.mak test\r\n";
    print FILE "goto end\r\n";
    print FILE "\@set RUNTESTFLAGS=\r\n";
    print FILE "\r\n";

    foreach my $test (@$c_tests)
    {
        $test =~ s/\.c$//;
        print FILE "call $totop\\programs\\winetest\\runtest.bat ${module}_test.exe $test $test.msvcok \%RUNTESTFLAGS\%\r\n";
        print FILE "if errorlevel 1 goto end\r\n";
    }
    print FILE "\r\n";
    print FILE ":end\r\n";

    close(FILE);

    # List the files that need to be archived
    $to_archive->{$filename}=1;
}


###
#
# Read Wine's makefiles
#
###

sub read_makefile($)
{
    my $filename=$_[0];
    my $module=[];
    my $imports=[];
    my $perl_tests=[];
    my $c_tests=[];
    my $subdirs=[];

    if (open(MAKEFILE,$filename))
    {
        my $current_list;
        my $dirname=$filename;
        if (!($dirname =~ s+/[^/]*$++))
        {
            $dirname=".";
        }

        while (<MAKEFILE>)
        {
            if (s/^\s*TESTDLL\s*=//)
            {
                $current_list=$module;
            }
            elsif (s/^\s*IMPORTS\s*=//)
            {
                $current_list=$imports;
            }
            elsif (s/^\s*(LIBPROG)?SUBDIRS\s*=\s*//)
            {
                $current_list=$subdirs;
            }
            elsif (s/^\s*BASEDIRS\s*=\s*//)
            {
                $current_list=$subdirs;
            }
            elsif (s/^\s*PLTESTS\s*=//)
            {
                $current_list=$perl_tests;
            }
            elsif (s/^\s*CTESTS\s*=\s*//)
            {
                $current_list=$c_tests;
            }
            if (defined $current_list)
            {
                my $continue=(s/\\$//?1:0);
                s/^\s*//;
                push @$current_list, split /\s+/;
                undef $current_list if (!$continue);
            }
        }
        close(MAKEFILE);
    }
    return ($module,$imports,$perl_tests,$c_tests,$subdirs);
}

sub process_makefile($$);
sub process_makefile($$)
{
    my $test_dirs=$_[0];
    my $filename=$_[1];
    my $dirname=$filename;
    $dirname =~ s+/[^/]*$++;

    #print "reading $filename (in $dirname)\n";
    my ($module,$imports,$perl_tests,$c_tests,$subdirs)=read_makefile($filename);
    if (@$module>0 and (@$perl_tests>0 or @$c_tests>0))
    {
        print "$dirname has tests\n";
        push @$test_dirs,$dirname;
        if (@$c_tests)
        {
            # create the testslist.c file
            my $totop=path_to_dotdot($dirname);
            print("$topdir/programs/winetest/make_ctests @$c_tests >$dirname/testlist.c");
            system("$topdir/programs/winetest/make_ctests @$c_tests >$dirname/testlist.c");
        }
        if ($do_msvc)
        {
            write_msvc_makefile("$dirname/$basename.mak",$module,$imports,$perl_tests,$c_tests);
            write_runtests_bat("$dirname/runtests.bat",$module,$imports,$perl_tests,$c_tests);
        }
    }
    foreach my $subdir (@$subdirs)
    {
        print "$dirname/$subdir/Makefile.in\n";
        if (-e "$dirname/$subdir/Makefile.in")
        {
            process_makefile($test_dirs,"$dirname/$subdir/Makefile.in");
        }
    }
}


###
#
# Main: Parse command-line options
#
###

my $usage;
while (@ARGV > 0)
{
    my $arg = shift @ARGV;
    if ($arg eq "--top")
    {
        if (defined $topdir)
        {
            print STDERR "$name0:error: --topdir can only be specified once\n";
            $usage = 1;
            last;
        }
        if (!@ARGV)
        {
            print STDERR "$name0:error: missing argument for --topdir\n";
            $usage = 1;
            last;
        }
        $topdir = shift @ARGV;
    }
    elsif ($arg eq "--zip")
    {
      $do_zip = 1;
    }
    elsif ($arg eq "--tgz")
    {
      $do_tgz = 1;
    }
    elsif ($arg eq "--msvc")
    {
      $do_msvc = 1;
    }
    elsif ($arg eq "--no-msvc")
    {
      undef $do_msvc;
    }
    elsif ($arg eq "-?" or $arg eq "-h" or $arg eq "--help")
    {
        $usage = 2;
    }
    else
    {
        print STDERR "$name0:error: unknown option\n";
        $usage = 1;
    }
}

# Check or detect $topdir
if (defined $topdir)
{
    if (! -f "$topdir/configure")
    {
        print STDERR "$name0:error: wrong --topdir argument, $topdir/configure does not exist\n";
        $usage = 1;
    }
}
else
{
    # try to detect it automatically
    if (-f "./configure") { $topdir = "."; }
    elsif (-f "../configure") { $topdir = ".."; }
    elsif (-f "../../configure") { $topdir = "../.."; }
    elsif (-f "../../../configure") { $topdir = "../../.."; }
    else {
        print STDERR "$name0:error: could not locate 'configure'. Use the --topdir option.\n";
        $usage = 1;
    }
}
if ($usage)
{
    print STDERR "Usage: $name0 [--help] [--topdir directory]\n";
    print STDERR "              [--zip] [--tgz] [--msvc|--no-msvc]\n";
    print STDERR "Options:\n";
    print STDERR "\n";
    print STDERR "   --topdir  specifies the Wine source directory (autodetected if not specified)\n";
    print STDERR "   --zip     zip all needed files for test (default)\n";
    print STDERR "   --tgz     archive (tar.gz) all needed files for test (default)\n";
    print STDERR "   --msvc    create makefiles for MS Visual C++ (default)\n";
    print STDERR "   --no-msvc do not create makefiles for MS Visual C++\n";
    print STDERR "   --help,-h shows this help message\n";

    exit $usage;
}


###
#
# Start generating files
#
###
my $test_dirs=[];
process_makefile($test_dirs,"$topdir/Makefile.in");
if ($do_msvc)
{
    write_msvc_top_makefile("$topdir/$basename.mak",$test_dirs);
}
if ($do_zip)
{
    system("zip","$basename.zip",keys %$to_archive);
}
if ($do_tgz)
{
    system("tar","cfz","$basename.tar.gz",keys %$to_archive);
}
-------------- next part --------------
A non-text attachment was scrubbed...
Name: runtest.bat
Type: application/x-msdos-program
Size: 82 bytes
Desc: runtest.bat
Url : http://www.winehq.org/pipermail/wine-patches/attachments/20020916/127cd3a1/runtest.bat


More information about the wine-patches mailing list