Skip to content

Commit d5598a4

Browse files
committed
Reimplement the tests similarly to libpcap. [skip appveyor]
Import TESTlib.pm, TESTst.pm and TESTmt.pm from libpcap. In TESTrun use strict and warnings, also address all Perl issues that manifest because of that, remove old code that the imported files make redundant and clean the rest up. Ibid., remove the core dump file before every test and apply the "only this one test" mode even if the test does not come from the TESTLIST file; focus on tcpdump specifics and have the imported files handle all test/result logistics. The latter among other things places all temporary test files in a proper temporary directory rather than the source tree, which in turn eliminates the tests/NEW and tests/DIFF temporary directories, which also includes *.out.raw.stderr files; the .passed and .failed files have been gone since commit b82970c in 2020. This way, the tests/.gitignore file no longer has a purpose, so remove it and the associated exemption for TEST_DIST in Makefile. Remove failure-outputs.txt from the top-level .gitignore as well. Prune "make distclean" as well. Merge tests/*.tests into TESTrun: these are a part of the source tree rather than volatile external data, so instead of implementing the required run-time logistics just place the Perl data structures in the only Perl script that uses them. Convert "config_set" and "config_unset" using "skip" and equivalent Perl expressions. Since these changes rewrite most of TESTrun, reindent and reformat it to match the code style of the new files.
1 parent d8ea59b commit d5598a4

14 files changed

+1080
-903
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
/tcpdump
2323
/tcpdump.1
2424
/tcpdump-*.tar.gz
25-
failure-outputs.txt
2625
/autom4te.cache/
2726
*.VC.db
2827
*.VC.opendb

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ DayOfTheWeek, Month DD, YYYY / The Tcpdump Group
8080
tests: On HP-UX use "diff -c" by default.
8181
autogen.sh: Allow to configure Autoconf warnings.
8282
autogen.sh: Delete all trailing blank lines at end of configure.
83+
Reimplement the tests similarly to libpcap.
8384
Documentation:
8485
man: Clarify the "any" pseudo-interface further.
8586

Makefile.in

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ EXTRA_DIST = \
373373
stime.awk \
374374
tcpdump.1.in
375375

376-
TEST_DIST= `git -C "$$DIR" ls-files tests | grep -v 'tests/\..*'`
376+
TEST_DIST= `git -C "$$DIR" ls-files tests`
377377

378378
RELEASE_FILES = $(CSRC) $(HDR) $(LIBNETDISSECT_SRC) $(EXTRA_DIST) $(TEST_DIST)
379379

@@ -425,9 +425,8 @@ clean:
425425
distclean: clean
426426
rm -f Makefile config.cache config.log config.status \
427427
config.h os-proto.h stamp-h stamp-h.in $(PROG).1 \
428-
libnetdissect.a tests/.failed tests/.passed \
429-
tests/failure-outputs.txt
430-
rm -rf autom4te.cache tests/DIFF tests/NEW
428+
libnetdissect.a
429+
rm -rf autom4te.cache
431430

432431
check: tcpdump
433432
$(srcdir)/tests/TESTrun

tests/.gitignore

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/TESTlib.pm

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
require 5.8.4; # Solaris 10
2+
use strict;
3+
use warnings FATAL => qw(uninitialized);
4+
use Config;
5+
use File::Temp qw(tempdir);
6+
7+
# TESTrun helper functions (common to all projects).
8+
9+
# TESTst.pm or TESTmt.pm
10+
use subs qw(
11+
get_next_result
12+
my_tmp_id
13+
start_tests
14+
);
15+
16+
# The characters are inspired by PHPUnit format, but are not exactly the same.
17+
use constant {
18+
CHAR_SKIPPED => 'S',
19+
CHAR_PASSED => '.',
20+
CHAR_FAILED => 'F',
21+
CHAR_TIMED_OUT => 'T',
22+
};
23+
24+
my $results_to_print;
25+
my $results_printed;
26+
my $max_result_digits;
27+
my $max_results_per_line;
28+
my $flush_after_newline;
29+
my $tmpdir;
30+
my %config;
31+
32+
sub init_tmpdir {
33+
my $prefix = shift;
34+
# No File::Temp->newdir() in Perl 5.8.4.
35+
$tmpdir = tempdir (
36+
"${prefix}_XXXXXXXX",
37+
TMPDIR => 1,
38+
CLEANUP => 1
39+
);
40+
}
41+
42+
sub mytmpfile {
43+
return sprintf '%s/%s-%s', $tmpdir, my_tmp_id, shift;
44+
}
45+
46+
sub get_njobs {
47+
my $njobs;
48+
if (! defined $ENV{TESTRUN_JOBS}) {
49+
$njobs = 1;
50+
} elsif ($ENV{TESTRUN_JOBS} =~ /^\d+\z/) {
51+
$njobs = int ($ENV{TESTRUN_JOBS});
52+
} else {
53+
$njobs = 0;
54+
}
55+
die "ERROR: '$ENV{TESTRUN_JOBS}' is not a valid value for TESTRUN_JOBS" if ! $njobs;
56+
return $njobs;
57+
}
58+
59+
sub get_diff_flags {
60+
return defined $ENV{DIFF_FLAGS} ? $ENV{DIFF_FLAGS} :
61+
$^O eq 'hpux' ? '-c' :
62+
'-u';
63+
}
64+
65+
# Parse config.h into a hash for later use.
66+
sub read_config_h {
67+
my $config_h = shift;
68+
%config = {};
69+
my $re_define_uint = qr/^#define ([0-9_A-Z]+) ([0-9]+)$/;
70+
my $re_define_str = qr/^#define ([0-9_A-Z]+) "(.+)"$/;
71+
open (my $fh, '<', $config_h) || die "failed opening '$config_h'";
72+
while (<$fh>) {
73+
$config{$1} = $2 if /$re_define_uint/o || /$re_define_str/o;
74+
}
75+
close ($fh) || die "failed closing '$config_h'";
76+
}
77+
78+
# This is a simpler version of the PHP function.
79+
sub file_put_contents {
80+
my ($filename, $contents) = @_;
81+
open (my $fh, '>', $filename) || die "failed opening '$filename'";
82+
print $fh $contents;
83+
close ($fh) || die "failed closing '$filename'";
84+
}
85+
86+
# Idem.
87+
sub file_get_contents {
88+
my $filename = shift;
89+
open (my $fh, '<', $filename) || die "failed opening '$filename'";
90+
my $ret = '';
91+
$ret .= $_ while (<$fh>);
92+
close ($fh) || die "failed closing '$filename'";
93+
return $ret;
94+
}
95+
96+
sub string_in_file {
97+
my ($string, $filename) = @_;
98+
my $ret = 0;
99+
open (my $fh, '<', $filename) || die "failed opening '$filename'";
100+
while (<$fh>) {
101+
if (-1 != index $_, $string) {
102+
$ret = 1;
103+
last;
104+
}
105+
}
106+
close ($fh) || die "failed closing '$filename'";
107+
return $ret;
108+
}
109+
110+
sub skip_os {
111+
my $name = shift;
112+
return $^O eq $name ? "is $name" : '';
113+
}
114+
115+
sub skip_os_not {
116+
my $name = shift;
117+
return $^O ne $name ? "is not $name" : '';
118+
}
119+
120+
sub skip_config_def1 {
121+
my $symbol = shift;
122+
return (defined $config{$symbol} && $config{$symbol} eq '1') ?
123+
"$symbol==1" : '';
124+
}
125+
126+
sub skip_config_undef {
127+
my $symbol = shift;
128+
return (! defined $config{$symbol} || $config{$symbol} ne '1') ?
129+
"${symbol}!=1" : '';
130+
}
131+
132+
sub skip_config_have_decl {
133+
my ($name, $value) = @_;
134+
$name = 'HAVE_DECL_' . $name;
135+
# "Unlike the other ‘AC_CHECK_*S’ macros, when a symbol is not declared,
136+
# HAVE_DECL_symbol is defined to ‘0’ instead of leaving HAVE_DECL_symbol
137+
# undeclared." -- GNU Autoconf manual.
138+
#
139+
# (This requires the CMake leg to do the same for the same symbol.)
140+
die "no $name in config.h" unless defined $config{$name};
141+
return int ($config{$name}) == $value ? "$name==$value" : '';
142+
}
143+
144+
sub result_skipped {
145+
return {
146+
char => CHAR_SKIPPED,
147+
skip => shift
148+
};
149+
}
150+
151+
sub result_passed {
152+
return {char => CHAR_PASSED};
153+
}
154+
155+
sub result_failed {
156+
return {
157+
char => CHAR_FAILED,
158+
failure => {
159+
reason => shift,
160+
details => shift
161+
}
162+
};
163+
}
164+
165+
sub result_timed_out {
166+
return {
167+
char => CHAR_TIMED_OUT,
168+
failure => {reason => shift}
169+
};
170+
}
171+
172+
sub run_skip_test {
173+
my $test = shift;
174+
return result_skipped $test->{skip};
175+
}
176+
177+
# <------------------------- $maxcols -------------------------->
178+
# ............................................ 0000 / 0000 (000%)
179+
# $max_result_digits >----< >----<
180+
# <--------- $max_results_per_line ---------->
181+
sub init_results_processing {
182+
my $maxcols = 80;
183+
$results_to_print = shift;
184+
if ($Config{useithreads}) {
185+
# When using threads, STDOUT becomes line-buffered on TTYs, which is
186+
# not good for interactive progress monitoring.
187+
STDOUT->autoflush (1) if -t STDOUT;
188+
$flush_after_newline = ! -t STDOUT;
189+
}
190+
$results_printed = 0;
191+
$max_result_digits = 1 + int (log ($results_to_print) / log (10));
192+
$max_results_per_line = $maxcols - 11 - 2 * $max_result_digits;
193+
}
194+
195+
# Produce a results map in PHPUnit output format.
196+
sub print_result_char {
197+
print shift;
198+
if (++$results_printed > $results_to_print) {
199+
die "Internal error: unexpected results after 100%!";
200+
}
201+
my $results_dangling = $results_printed % $max_results_per_line;
202+
if ($results_dangling) {
203+
return if $results_printed < $results_to_print;
204+
# Complete the dangling line to keep the progress column aligned.
205+
print ' ' for (1 .. $max_results_per_line - $results_dangling);
206+
}
207+
printf " %*u / %*u (%3u%%)\n",
208+
$max_result_digits,
209+
$results_printed,
210+
$max_result_digits,
211+
$results_to_print,
212+
100 * $results_printed / $results_to_print;
213+
# When using threads, STDOUT becomes block-buffered on pipes, which is
214+
# not good for CI progress monitoring.
215+
STDOUT->flush if $flush_after_newline;
216+
}
217+
218+
sub print_result {
219+
printf " %-40s: %s\n", @_;
220+
}
221+
222+
sub test_and_report {
223+
my @tests = @_;
224+
start_tests (@tests);
225+
init_results_processing scalar @tests;
226+
my $ret = 0;
227+
# key: test label, value: reason for skipping
228+
my %skipped;
229+
# key: test label, value: hash of
230+
# * reason (mandatory, string)
231+
# * details (optional, [multi-line] string)
232+
my %failed;
233+
my $passedcount = 0;
234+
235+
# Ordering of the results is the same as ordering of the tests. Print the
236+
# results map immediately and buffer any skipped/failed test details for the
237+
# post-map diagnostics.
238+
while (defined (my $result = get_next_result)) {
239+
print_result_char ($result->{char});
240+
if (defined $result->{skip}) {
241+
$skipped{$result->{label}} = $result->{skip};
242+
} elsif (defined $result->{failure}) {
243+
$failed{$result->{label}} = $result->{failure};
244+
} else {
245+
$passedcount++;
246+
}
247+
}
248+
249+
print "\n";
250+
if (%skipped) {
251+
print "Skipped tests:\n";
252+
print_result $_, $skipped{$_} foreach (sort keys %skipped);
253+
print "\n";
254+
}
255+
if (%failed) {
256+
$ret = 1;
257+
print "Failed tests:\n";
258+
foreach (sort keys %failed) {
259+
print_result $_, $failed{$_}{reason};
260+
print $failed{$_}{details} if defined $failed{$_}{details};
261+
}
262+
print "\n";
263+
}
264+
265+
# scalar (%hash) returns incorrect value on Perl 5.8.4.
266+
my $skippedcount = scalar keys %skipped;
267+
my $failedcount = scalar keys %failed;
268+
print "------------------------------------------------\n";
269+
printf "%4u tests skipped\n", $skippedcount;
270+
printf "%4u tests failed\n", $failedcount;
271+
printf "%4u tests passed\n", $passedcount;
272+
273+
if ($skippedcount + $failedcount + $passedcount != $results_to_print) {
274+
printf STDERR "Internal error: statistics bug (%u + %u + %u != %u)\n",
275+
$skippedcount,
276+
$failedcount,
277+
$passedcount,
278+
$results_to_print;
279+
$ret = 2;
280+
}
281+
return $ret;
282+
}
283+
284+
1;

tests/TESTmt.pm

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
require 5.10.1; # Debian 6
2+
use strict;
3+
use warnings FATAL => qw(uninitialized);
4+
use threads;
5+
use Thread::Queue;
6+
# TESTlib.pm
7+
use subs qw(get_njobs);
8+
9+
# TESTrun helper functions (multithreaded implementation).
10+
11+
my $njobs;
12+
my $tmpid;
13+
my @tests;
14+
my @result_queues;
15+
my @tester_threads;
16+
my $next_to_dequeue;
17+
18+
sub my_tmp_id {
19+
return $tmpid;
20+
}
21+
22+
# Iterate over the list of tests, pick tests that belong to the current job,
23+
# run one test at a time and send the result to the job's results queue.
24+
sub tester_thread_func {
25+
my $jobid = shift;
26+
$tmpid = sprintf 'job%03u', $jobid;
27+
for (my $i = $jobid; $i < scalar @tests; $i += $njobs) {
28+
my $test = $tests[$i];
29+
my $result = $test->{func} ($test);
30+
$result->{label} = $test->{label};
31+
$result_queues[$jobid]->enqueue ($result);
32+
}
33+
# Instead of detaching let the receiver join, this works around File::Temp
34+
# not cleaning up.
35+
# No Thread::Queue->end() in Perl 5.10.1, so use an undef to mark the end.
36+
$result_queues[$jobid]->enqueue (undef);
37+
}
38+
39+
sub start_tests {
40+
$njobs = get_njobs;
41+
print "INFO: This Perl supports threads, using $njobs tester thread(s).\n";
42+
@tests = @_;
43+
for (0 .. $njobs - 1) {
44+
$result_queues[$_] = Thread::Queue->new;
45+
$tester_threads[$_] = threads->create (\&tester_thread_func, $_);
46+
}
47+
$next_to_dequeue = 0;
48+
}
49+
50+
# Here ordering of the results is the same as ordering of the tests because
51+
# this function starts at job 0 and continues round-robin, which reverses the
52+
# interleaving done in the thread function above; also because every attempt
53+
# to dequeue blocks until it returns exactly one result.
54+
sub get_next_result {
55+
for (0 .. $njobs - 1) {
56+
my $jobid = $next_to_dequeue;
57+
$next_to_dequeue = ($next_to_dequeue + 1) % $njobs;
58+
# Skip queues that have already ended.
59+
next unless defined $result_queues[$jobid];
60+
my $result = $result_queues[$jobid]->dequeue;
61+
# A test result?
62+
return $result if defined $result;
63+
# No, an end-of-queue marker.
64+
$result_queues[$jobid] = undef;
65+
$tester_threads[$jobid]->join;
66+
}
67+
# No results after one complete round, therefore done.
68+
return undef;
69+
}
70+
71+
1;

0 commit comments

Comments
 (0)