]>
The Tcpdump Group git mirrors - tcpdump/blob - tests/TESTrun
4 # Were we told where to find tcpdump?
6 if (!($TCPDUMP = $ENV{TCPDUMP_BIN
})) {
8 # No. Use the appropriate path.
10 if ($^O
eq 'MSWin32') {
12 # XXX - assume, for now, a Visual Studio debug build, so that
13 # tcpdump is in the Debug subdirectory.
15 $TCPDUMP = "Debug\\tcpdump"
17 $TCPDUMP = "./tcpdump"
22 # Make true and false work as Booleans.
24 use constant
{ true
=> 1, false
=> 0 };
27 use POSIX
qw( WEXITSTATUS WIFEXITED);
28 use Cwd
qw(abs_path getcwd);
29 use File
::Path
qw(mkpath); # mkpath works with ancient perl, as well as newer perl
31 use Data
::Dumper
; # for debugging.
33 # these are created in the directory where we are run, which might be
35 my $newdir = "tests/NEW";
36 my $diffdir= "tests/DIFF";
39 my $origdir = getcwd
();
40 my $srcdir = $ENV{'srcdir'} || ".";
43 # Force UTC, so time stamps are printed in a standard time zone, and
44 # tests don't have to be run in the time zone in which the output
50 # Get the tests directory from $0.
52 my $testsdir = dirname
($0);
55 # Convert it to an absolute path, so it works even after we do a cd.
57 $testsdir = abs_path
($testsdir);
58 print "Running tests from ${testsdir}\n";
60 unshift(@INC, $testsdir);
65 my $failureoutput=$origdir . "/tests/failure-outputs.txt";
67 # truncate the output file
68 open(FAILUREOUTPUT
, ">" . $failureoutput);
77 # XXX - just do this directly in Perl?
79 if ($^O
eq 'MSWin32') {
80 my $winpath = File
::Spec
->canonpath($path);
81 system "type $winpath";
88 local($name, $input, $output, $options) = @_;
91 $outputbase = basename
($output);
95 my $rawstderrlog = "tests/NEW/${outputbase}.raw.stderr";
96 my $stderrlog = "tests/NEW/${outputbase}.stderr";
100 # we used to do this as a nice pipeline, but the problem is that $r fails to
101 # to be set properly if the tcpdump core dumps.
103 # Furthermore, on Windows, fc can't read the standard input, so we
104 # can't do it as a pipeline in any case.
105 $r = system "$TCPDUMP -# -n -r $input $options >tests/NEW/${outputbase} 2>${rawstderrlog}";
108 # Something other than "tcpdump opened the file, read it, and
109 # dissected all the packets". What happened?
111 # We write out an exit status after whatever the subprocess
112 # wrote out, so it shows up when we diff the expected output
115 open(OUTPUT
, ">>"."tests/NEW/$outputbase") || die "fail to open $outputbase\n";
117 # failed to start due to error.
119 printf OUTPUT
"FAILED TO RUN: status: %d\n", $status;
121 if ($^O
eq 'MSWin32') {
123 # On Windows, the return value of system is the lower 8
124 # bits of the exit status of the process, shifted left
127 # If the process crashed, rather than exiting, the
128 # exit status will be one of the EXCEPTION_ values
129 # listed in the documentation for the GetExceptionCode()
132 # Those are defined as STATUS_ values, which should have
133 # 0xC in the topmost 4 bits (being fatal error
134 # statuses); some of them have a value that fits in
135 # the lower 8 bits. We could, I guess, assume that
136 # any value that 1) isn't returned by tcpdump and 2)
137 # corresponds to the lower 8 bits of a STATUS_ value
138 # used as an EXCEPTION_ value indicates that tcpdump
139 # exited with that exception.
141 # However, as we're running tcpdump with system, which
142 # runs the command through cmd.exe, and as cmd.exe
143 # doesn't map the command's exit code to its own exit
144 # code in any straightforward manner, we can't get
145 # that information in any case, so there's no point
146 # in trying to interpret it in that fashion.
151 # On UN*Xes, the return status is a POSIX as filled in
152 # by wait() or waitpid().
154 # POSIX offers some calls for analyzing it, such as
155 # WIFSIGNALED() to test whether it indicates that the
156 # process was terminated by a signal, WTERMSIG() to
157 # get the signal number from it, WIFEXITED() to test
158 # whether it indicates that the process exited normally,
159 # and WEXITSTATUS() to get the exit status from it.
161 # POSIX doesn't standardize core dumps, so the POSIX
162 # calls can't test whether a core dump occurred.
163 # However, all the UN*Xes we are likely to encounter
164 # follow Research UNIX in this regard, with the exit
165 # status containing either 0 or a signal number in
166 # the lower 7 bits, with 0 meaning "exited rather
167 # than being terminated by a signal", the "core dumped"
168 # flag in the 0x80 bit, and, if the signal number is
169 # 0, the exit status in the next 8 bits up.
171 # This should be cleaned up to use the POSIX calls
172 # from the Perl library - and to define an additional
173 # WCOREDUMP() call to test the "core dumped" bit and
176 # But note also that, as we're running tcpdump with
177 # system, which runs the command through a shell, if
178 # tcpdump crashes, we'll only know that if the shell
179 # maps the signal indication and uses that as its
182 # The good news is that the Bourne shell, and compatible
183 # shells, have traditionally done that. If the process
184 # for which the shell reports the exit status terminates
185 # with a signal, it adds 128 to the signal number and
186 # returns that as its exit status. (This is why the
187 # "this is now working right" behavior described in a
188 # comment below is occurring.)
190 # As tcpdump itself never returns with an exit status
191 # >= 128, we can try checking for an exit status with
192 # the 0x80 bit set and, if we have one, get the signal
193 # number from the lower 7 bits of the exit status. We
194 # can't get the "core dumped" indication from the
195 # shell's exit status; all we can do is check whether
196 # there's a core file.
199 $coredump = $r & 127;
202 $status = WEXITSTATUS
($r);
206 if($coredump || $status) {
207 printf OUTPUT
"EXIT CODE %08x: dump:%d code: %d\n", $r, $coredump, $status;
209 printf OUTPUT
"EXIT CODE %08x\n", $r;
217 # Compare tcpdump's output with what we think it should be.
218 # If tcpdump failed to produce output, we've produced our own
219 # "output" above, with the exit status.
221 if ($^O
eq 'MSWin32') {
222 my $winoutput = File
::Spec
->canonpath($output);
223 $r = system "fc /lb1000 /t /1 $winoutput tests\\NEW\\$outputbase >tests\\DIFF\\$outputbase.diff";
226 $r = system "diff $output tests/NEW/$outputbase >tests/DIFF/$outputbase.diff";
227 $diffstat = WEXITSTATUS
($r);
231 # process the standard error file, sanitize "reading from" line,
234 open(ERRORRAW
, "<" . $rawstderrlog);
235 open(ERROROUT
, ">" . $stderrlog);
237 next if /^$/; # blank lines are boring
238 if(/^(reading from file )(.*)(,.*)$/) {
239 my $filename = basename
($2);
240 print ERROROUT
"${1}${filename}${3}\n";
249 if ( -f
"$output.stderr" ) {
251 # Compare the standard error with what we think it should be.
253 if ($^O
eq 'MSWin32') {
254 my $winoutput = File
::Spec
->canonpath($output);
255 my $canonstderrlog = File
::Spec
->canonpath($stderrlog);
256 $nr = system "fc /lb1000 /t /1 $winoutput.stderr $canonstderrlog >tests\DIFF\$outputbase.stderr.diff";
257 $errdiffstat = $nr >> 8;
259 $nr = system "diff $output.stderr $stderrlog >tests/DIFF/$outputbase.stderr.diff";
260 $errdiffstat = WEXITSTATUS
($nr);
268 if($linecount == 0 && $status == 0) {
275 #print sprintf("END: %08x\n", $r);
278 if($linecount == 0) {
279 printf " %-40s: passed\n", $name;
281 printf " %-40s: passed with error messages:\n", $name;
282 showfile
($stderrlog);
284 unlink "tests/DIFF/$outputbase.diff";
288 printf " %-40s: TEST FAILED(exit core=%d/diffstat=%d,%d/r=%d)", $name, $coredump, $diffstat, $errdiffstat, $r;
289 open FOUT
, '>>tests/failure-outputs.txt';
290 printf FOUT
"\nFailed test: $name\n\n";
292 if(-f
"tests/DIFF/$outputbase.diff") {
294 # XXX - just do this directly in Perl?
296 if ($^O
eq 'MSWin32') {
297 system "type tests\\DIFF\\$outputbase.diff >> tests\\failure-outputs.txt";
299 system "cat tests/DIFF/$outputbase.diff >> tests/failure-outputs.txt";
304 print " (failed to execute: $!)\n";
308 # this is not working right, $r == 0x8b00 when there is a core dump.
309 # clearly, we need some platform specific perl magic to take this apart, so look for "core"
311 # In particular, on Solaris 10 SPARC an alignment problem results in SIGILL,
312 # a core dump and $r set to 0x00008a00 ($? == 138 in the shell).
313 if($r & 127 || -f
"core") {
314 my $with = ($r & 128) ?
'with' : 'without';
318 printf " (terminated with signal %u, %s coredump)", ($r & 127), $with;
319 if($linecount == 0) {
322 print " with error messages:\n";
323 showfile
($stderrlog);
325 return(($r & 128) ?
10 : 20);
327 if($linecount == 0) {
330 print " with error messages:\n";
331 showfile
($stderrlog);
337 if(defined($confighhash)) {
341 $main::confighhash
= {};
343 # this could be loaded once perhaps.
344 open(CONFIG_H
, "config.h") || die "Can not open config.h: $!\n";
347 if(/^\#define (.*) 1/) {
348 #print "Setting $1\n";
349 $main::confighhash
->{$1} = 1;
353 #print Dumper($main::confighhash);
355 # also run tcpdump --fp-type to get the type of floating-point
356 # arithmetic we're doing, setting a HAVE_{fptype} key based
357 # on the value it prints
358 open(FPTYPE_PIPE
, "$TCPDUMP --fp-type |") or die("piping tcpdump --fp-type failed\n");
359 my $fptype_val = <FPTYPE_PIPE
>;
362 if($fptype_val == "9877.895") {
363 $have_fptype = "HAVE_FPTYPE1";
365 $have_fptype = "HAVE_FPTYPE2";
367 $main::confighhash
->{$have_fptype} = 1;
369 return $main::confighhash
;
373 sub runOneComplexTest
{
374 local($testconfig) = @_;
376 my $output = $testconfig->{output
};
377 my $input = $testconfig->{input
};
378 my $name = $testconfig->{name
};
379 my $options= $testconfig->{args
};
383 my $configset = $testconfig->{config_set
};
384 my $configunset = $testconfig->{config_unset
};
385 my $ch = loadconfighash
();
388 if(defined($configset)) {
389 $foundit = ($ch->{$configset} == 1);
391 if(defined($configunset)) {
392 $unfoundit=($ch->{$configunset} != 1);
396 printf " %-40s: skipped (%s not set)\n", $name, $configset;
401 printf " %-40s: skipped (%s set)\n", $name, $configunset;
406 #print Dumper($testconfig);
408 # EXPAND any occurrences of @TESTDIR@ to $testsdir
409 $options =~ s/\@TESTDIR\@/$testsdir/;
411 my $result = runtest
($name,
412 $testsdir . "/" . $input,
413 $testsdir . "/" . $output,
423 # *.tests files are PERL hash definitions. They should create an array of hashes
424 # one per test, and place it into the variable @testlist.
425 sub runComplexTests
{
426 my @files = glob( $testsdir . '/*.tests' );
427 foreach $file (@files) {
428 my @testlist = undef;
430 print "FILE: ${file}\n";
431 open(FILE
, "<".$file) || die "can not open $file: $!";
434 $definitions = <FILE
>;
437 #print "STUFF: ${definitions}\n";
439 if(defined($testlist)) {
441 #print Dumper($testlist);
442 foreach $test (@
$testlist) {
443 runOneComplexTest
($test);
446 warn "File: ${file} could not be loaded as PERL: $!";
455 open(TESTLIST
, "<" . "${testsdir}/TESTLIST") || die "no ${testsdir}/TESTFILE: $!\n";
461 ($name, $input, $output, @options) = split;
462 #print "processing ${only} vs ${name}\n";
463 next if(defined($only) && $only ne $name);
465 my $options = join(" ", @options);
466 #print "@{options} becomes ${options}\n";
468 my $hash = { name
=> $name,
473 runOneComplexTest
($hash);
477 if(scalar(@ARGV) == 0) {
481 runSimpleTests
($ARGV[0]);
484 # exit with number of failing tests.
485 print "------------------------------------------------\n";
486 printf("%4u tests failed\n",$failedcount);
487 printf("%4u tests passed\n",$passedcount);
489 showfile
(${failureoutput
});