|  | #!/usr/bin/perl | 
|  | # Copyright 1996-1998 Marcus Meissner | 
|  | # IPC remove code Copyright 1995 Michael Veksler | 
|  | # | 
|  | # 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(<IPCS>) { | 
|  | 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 = <PSAUX>; # fmtline | 
|  | @psformat = split(/\s\s*/,$xline); | 
|  |  | 
|  | psline: while (<PSAUX>) { | 
|  | 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(<IPCS>) { | 
|  | 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=<FIND>) { | 
|  | 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); |