#!/usr/bin/perl # Copyright 1996-1998 Marcus Meissner # IPC remove code Copyright 1995 Michael Veksler # # 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: # # This perl script automatically test runs ALL windows .exe and .scr binaries # it finds (and can access) on your computer. It creates a subdirectory called # runs/ and stores the output there. It also does (unique) diffs between runs. # # It only reruns the test if ChangeLog or the executeable is NEWER than the # run file. (If you want to rerun everything inbetween releases, touch # ChangeLog.) # # BEGIN OF USER CONFIGURATION # # Path to WINE executeable. If not specified, 'wine' is searched in the path. # $wine = 'wine'; # # WINE options. -managed when using a windowmanager is probably not good in # automatic testruns. # $wineoptions=''; # # Path to WINE ChangeLog. Used as timestamp for new releases... # $changelog = '/home/marcus/wine/ChangeLog'; # # How long before automatically killing all subprocesses # 30 is good for automatic testing, 300 or more for interactive testing. # $waittime = 50; # #diff command # $diff='diff -u'; # # truncate at how-much-lines # $trunclines=200; # $<||die "Running this script under UID 0 is a great security risk (and risk for existing windows installations on mounted DOS/W95 partitions). If you really want to, comment out this line.\n"; # # END OF USER CONFIGURATION # if (! -d "runs") { die "no subdirectory runs/ found in $cwd. Please create one first!\n";} # look for the exact path to wine executeable in case we need it for a # replacement changelog. if (! ($wine =~ /\//)) { # no path specified. Look it up. @paths = split(/:/,$ENV{'PATH'}); foreach $path (@paths) { if (-e "$path/$wine" && -x "$path/$wine") { $wine = "$path/$wine"; last; } } } # if we don't have a changelog use the modification date of the WINE executeable if (! -e $changelog) { $changelog = $wine; } # sanity check so we just fill runs/ with errors. (-x $wine) || die "no $wine executable found!\n"; # dito. will print usage system("$wine -h >/dev/null")||die "wine call failed:$!\n"; print "Using $wine as WINE executeable.\n"; print "Using $changelog as testrun timereference.\n"; chomp($cwd = `pwd`); # Find out all present semaphores so we don't remove them later. $IPC_RMID=0; $USER=$ENV{'USER'}; open(IPCS,"ipcs|"); while() { split; # try to find out the IPC-ID, assume it is the first number. foreach (@_) { $_ ne int($_) && next; # not a decimal number $num=$_; last; } if (/sem/i .. /^\s*$/ ) { index($_,$USER)>=0 || next; $sem_used{$num}=1; print "found $num\n"; } } close(IPCS); sub kill_subprocesses { local($killedalready,%parentof,%kids,$changed,%cmdline); # FIXME: substitute ps command that shows PID,PPID and COMMAND # On Linux' latest procps this is "ps aulc" # open(PSAUX,"ps aulc|"); # lookup all processes, remember their parents and cmdlines. %parentof=(); $xline = ; # fmtline @psformat = split(/\s\s*/,$xline); psline: while () { chop; @psline = split(/\s\s*/); $pid=0; for ($i=0;$i<=$#psformat;$i++) { if ($psformat[$i] =~ /COMMAND/) { die unless $pid; $cmdline{$pid}=$psline[$i]; break; } if ($psformat[$i] =~ /PPID/ ) { $parentof{$pid} = $psline[$i]; next; } if ($psformat[$i] =~ /PID/ ) { $pid = $psline[$i]; next; } } } close(PSAUX); # find out all kids of this perlscript %kids = (); $kids{$$} = 1; $changed = 1; while ($changed) { $changed = 0; foreach (keys %parentof) { next if ($kids{$_}); if ($kids{$parentof{$_}}) { $changed = 1; $kids{$_}=1; } } } # .. but do not consider us for killing delete $kids{$$}; # remove all processes killed in the meantime from %killedalready. foreach $pid (keys %killedalready) { delete $killedalready{$pid} if (!$kids{$pid} ); } # kill all subprocesses called 'wine'. Do not kill find, diff, sh # and friends, which are also subprocesses of us. foreach (keys %kids) { next unless ($cmdline{$_} =~ /((.|)wine|dosmod)/); # if we have already killed it using -TERM, use -KILL if ($killedalready{$_}) { kill(9,$_); # FIXME: use correct number? } else { kill(15,$_); # FIXME: use correct number? } $killedalready{$_}=1; } alarm($waittime); # wait again... }; # borrowed from tools/ipcl. See comments there. # killing wine subprocesses unluckily leaves all of their IPC stuff lying # around. We have to wipe it or we run out of it. sub cleanup_wine_ipc { open(IPCS,"ipcs|"); while() { split; # try to find out the IPC-ID, assume it is the first number. foreach (@_) { $_ ne int($_) && next; # not a decimal number $num=$_; last; } # was there before start of this script, skip it. # # FIXME: this doesn't work for programs started during the testrun. # if (/sem/i .. /^\s*$/ ) { index($_,$USER)>=0 || next; push(@sem,$num); } } foreach (@sem) { $sem_used{$_} && next; semctl($_, 0, $IPC_RMID,0); } close(IPCS); } # kill all subwineprocesses for automatic runs. sub alarmhandler { print "timer triggered.\n"; &kill_subprocesses; } $SIG{'ALRM'} = "alarmhandler"; # NOTE: following find will also cross NFS mounts, so be sure to have nothing # mounted that's not on campus or add relevant ! -fstype nfs or similar. # $startdir = '/'; $startdir = $ARGV[0] if ($ARGV[0] && (-d $ARGV[0])); open(FIND,"find $startdir -type f \\( -name \"*.EXE\" -o -name \"*.exe\" -o -name \"*.scr\" -o -name \"*.SCR\" \\) -print|"); while ($exe=) { chop($exe); # This could change during a testrun (by doing 'make' for instance) # FIXME: doesn't handle missing libwine.so during compile... (-x $wine) || die "no $wine executable found!\n"; # Skip all mssetup, acmsetup , installshield whatever exes. # they seem to work, mostly and starting them is just annoying. next if ($exe =~ /acmsetup|unwise|testexit|_msset|isun|st4u|st5u|_mstest|_isdel|ms-setup|~ms|unin/io); $runfile = $exe; $runfile =~ s/[\/ ]/_/g; $runfile =~ s/\.exe$//g; $runfile =~ s/\.scr$//ig; $flag=0; # # Check if changelog is newer, if not, continue # if ( -e "runs/${runfile}.out" && (-M $changelog > -M "runs/${runfile}.out") && (-M $exe > -M "runs/${runfile}.out") ) { #print "skipping $exe, already done.\n"; next; } # now testrun... print "$exe:\n"; $dir = $exe; $dir =~ s/^(.*)\/[^\/]*$/$1/; #cut of the basename. alarm($waittime); chdir($dir)||die "$dir:$!"; if ($exe =~ /\.scr/i) { system("echo quit|$wine $wineoptions \"$exe /s\" >$cwd/${runfile}.out 2>&1"); } else { system("echo quit|$wine $wineoptions \"$exe\" >$cwd/${runfile}.out 2>&1"); } alarm(1000);# so it doesn't trigger in the diff, kill or find. system("touch $cwd/runs/${runfile}.out"); system("$diff $cwd/runs/${runfile}.out $cwd/${runfile}.out|head -$trunclines"); system("head -$trunclines $cwd/${runfile}.out >$cwd/runs/${runfile}.out"); unlink("$cwd/${runfile}.out"); &kill_subprocesses; &cleanup_wine_ipc; chdir($cwd); } close(FIND);