Skip to content

Commit c96ba4f

Browse files
committed
Add basic testing
TODO: improve and properly integrate. Implements: iputils#243 Signed-off-by: Petr Vorel <pvorel@suse.cz>
1 parent c48032f commit c96ba4f

File tree

10 files changed

+312
-21
lines changed

10 files changed

+312
-21
lines changed

build-aux/meson-build-dist-man.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/bin/sh -eu
22
# SPDX-License-Identifier: GPL-2.0-or-later
3-
# Copyright (c) Iputils Project, 2024
3+
# Copyright (c) Iputils Project, 2024-2025
44
#
55
# This script should be invoked by meson itself on 'meson dist'
66
# (invoked by tools/create-tarballs.sh).
@@ -16,7 +16,7 @@ fi
1616
cd "$MESON_DIST_ROOT"
1717
DIR=$(mktemp -d)
1818

19-
meson setup "$DIR" -DBUILD_MANS=true -DBUILD_HTML_MANS=true
19+
meson setup "$DIR" -DBUILD_MANS=true -DBUILD_HTML_MANS=true -DSKIP_TESTS=true
2020
meson compile -C "$DIR"
2121
cp -v "$DIR"/doc/* doc/
2222
rm -rf "$DIR"

test/arping/arping-01-basic.t

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/perl -w
2+
3+
use Test::Command tests => 2;
4+
use Test::More;
5+
6+
diag("Running as UID: $>");
7+
diag("PATH = $ENV{PATH}");
8+
my $arping = $ARGV[0] // 'arping';
9+
diag("passed cmd: $arping");
10+
printf("# actually used cmd: ");
11+
system("/bin/sh", "-c", "command -v $arping");
12+
13+
# -V
14+
{
15+
my $cmd = Test::Command->new(cmd => "$arping -V");
16+
$cmd->exit_is_num(0);
17+
subtest 'output' => sub {
18+
$cmd->stdout_like(qr/^arping from iputils /, 'Print version');
19+
$cmd->stdout_like(qr/libcap: (yes|no), IDN: (yes|no), NLS: (yes|no), error.h: (yes|no), getrandom\(\): (yes|no), __fpending\(\): (yes|no)$/, 'Print config');
20+
}
21+
}

test/arping/meson.build

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# SPDX-License-Identifier: GPL-2.0-or-later
2-
# Copyright (c) 2021 Petr Vorel <pvorel@suse.cz>
2+
# Copyright (c) 2021-2025 Petr Vorel <pvorel@suse.cz>
33

4-
cmd = arping
5-
cmd_name = 'arping '
6-
args = ['-V']
4+
message('Build directory is: ' + meson.current_build_dir())
75

8-
name = cmd_name + ' '.join(args)
9-
test(name, cmd, args : args)
6+
foreach t : ['arping-01-basic.t']
7+
test(t, find_program(t), args: arping)
8+
endforeach
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/perl -w
2+
3+
use Test::Command tests => 2;
4+
use Test::More;
5+
6+
diag("Running as UID: $>");
7+
diag("PATH = $ENV{PATH}");
8+
my $clockdiff = $ARGV[0] // 'clockdiff';
9+
diag("passed cmd: $clockdiff");
10+
printf("# actually used cmd: ");
11+
system("/bin/sh", "-c", "command -v $clockdiff");
12+
13+
# -V
14+
{
15+
my $cmd = Test::Command->new(cmd => "$clockdiff -V");
16+
$cmd->exit_is_num(0);
17+
subtest 'output' => sub {
18+
$cmd->stdout_like(qr/^clockdiff from iputils /, 'Print version');
19+
$cmd->stdout_like(qr/libcap: (yes|no), IDN: (yes|no), NLS: (yes|no), error.h: (yes|no), getrandom\(\): (yes|no), __fpending\(\): (yes|no)$/, 'Print config');
20+
}
21+
}

test/clockdiff/meson.build

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# SPDX-License-Identifier: GPL-2.0-or-later
2-
# Copyright (c) 2021 Petr Vorel <pvorel@suse.cz>
2+
# Copyright (c) 2021-2025 Petr Vorel <pvorel@suse.cz>
33

4-
cmd = clockdiff
5-
cmd_name = 'clockdiff '
6-
args = ['-V']
4+
message('Build directory is: ' + meson.current_build_dir())
75

8-
name = cmd_name + ' '.join(args)
9-
test(name, cmd, args : args)
6+
foreach t : ['clockdiff-01-basic.t']
7+
test(t, find_program(t), args: clockdiff)
8+
endforeach

test/ping/meson.build

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
# SPDX-License-Identifier: GPL-2.0-or-later
2-
# Copyright (c) 2021 Petr Vorel <pvorel@suse.cz>
2+
# Copyright (c) 2021-2025 Petr Vorel <pvorel@suse.cz>
33

4+
message('Build directory is: ' + meson.current_build_dir())
5+
6+
foreach t : ['ping-01-basics.t', 'ping-02-errors.t' ]
7+
test(t, find_program(t), args: ping)
8+
endforeach
9+
10+
# FIXME: convert to perl
411
# GitHub CI does not have working IPv6
512
# https://github.com/actions/virtual-environments/issues/668
613
ipv6_dst = []

test/ping/ping-01-basics.t

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/perl -w
2+
3+
use Socket qw(AF_INET AF_INET6 AF_UNSPEC SOCK_STREAM AI_CANONNAME);
4+
use Socket::GetAddrInfo qw(getaddrinfo);
5+
use Test::Command tests => 11;
6+
use Test::More;
7+
8+
diag("Running as UID: $>");
9+
diag("PATH = $ENV{PATH}");
10+
my $ping = $ARGV[0] // 'ping';
11+
diag("passed cmd: $ping");
12+
printf("# actually used cmd: ");
13+
system("/bin/sh", "-c", "command -v $ping");
14+
15+
sub get_target
16+
{
17+
my $target = shift;
18+
my $target_ai_family = shift // AF_UNSPEC;
19+
20+
my $hints = {
21+
flags => AI_CANONNAME,
22+
family => $target_ai_family,
23+
socktype => SOCK_STREAM,
24+
};
25+
26+
die "invalid family: '$target_ai_family'" unless ($target_ai_family == AF_INET || $target_ai_family == AF_INET6 || $target_ai_family == AF_UNSPEC);
27+
28+
my ($err, @res) = getaddrinfo($target, undef, $hints);
29+
die "getaddrinfo error: $err\n" if $err;
30+
31+
foreach my $ai (@res) {
32+
next if $target_ai_family != AF_UNSPEC && $target_ai_family != $ai->{family};
33+
34+
# AF_INET6 does not perform getaddrinfo()
35+
return $target if ($ai->{family} == AF_INET6);
36+
37+
# AF_INET has extra getaddrinfo() to get canonname
38+
$hints = {
39+
ai_family => AF_INET,
40+
ai_protocol => IPPROTO_UDP,
41+
ai_flags => AI_CANONNAME, # getaddrinfo_flags
42+
};
43+
my ($err, @res) = getaddrinfo($target, undef, $hints); # TODO: or $ai?
44+
die "getaddrinfo error: $err\n" if $err;
45+
46+
return defined($ai->{canonname}) ? $ai->{canonname} : $target;
47+
}
48+
}
49+
50+
# -V
51+
{
52+
my $cmd = Test::Command->new(cmd => "$ping -V");
53+
$cmd->exit_is_num(0);
54+
subtest 'output' => sub {
55+
$cmd->stdout_like(qr/^ping from iputils /, 'Print version');
56+
$cmd->stdout_like(qr/libcap: (yes|no), IDN: (yes|no), NLS: (yes|no), error.h: (yes|no), getrandom\(\): (yes|no), __fpending\(\): (yes|no)$/, 'Print config');
57+
}
58+
}
59+
60+
# 127.0.0.1
61+
{
62+
my $cmd = Test::Command->new(cmd => "$ping -c1 127.0.0.1");
63+
$cmd->exit_is_num(0);
64+
subtest 'output' => sub {
65+
$cmd->stdout_like(qr/64 bytes from 127\.0\.0\.1/, 'Ping received from 127.0.0.1');
66+
$cmd->stdout_like(qr/0% packet loss/, 'No packet loss');
67+
$cmd->stdout_like(qr/time=\d+\.\d+ ms/, 'Ping time present');
68+
$cmd->stdout_like(qr~rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$~,
69+
'RTT time present');
70+
$cmd->stdout_like(qr{^PING 127\.0\.0\.1 \(127\.0\.0\.1\) 56\(84\) bytes of data\.
71+
64 bytes from 127\.0\.0\.1: icmp_seq=1 ttl=\d+ time=\d\.\d{3} ms
72+
73+
--- 127.0.0.1 ping statistics ---
74+
1 packets transmitted, 1 received, 0% packet loss, time \d+ms
75+
rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$},
76+
'Entire ping output matched exactly');
77+
}
78+
}
79+
80+
# ::1
81+
SKIP: {
82+
if ($ENV{SKIP_IPV6}) {
83+
skip 'IPv6 tests', 2;
84+
}
85+
my $cmd = Test::Command->new(cmd => "$ping -c1 ::1");
86+
$cmd->exit_is_num(0);
87+
subtest 'output' => sub {
88+
$cmd->stdout_like(qr/64 bytes from ::1/, 'Ping received from ::1');
89+
$cmd->stdout_like(qr/0% packet loss/, 'No packet loss');
90+
$cmd->stdout_like(qr/time=\d+\.\d+ ms/, 'Ping time present');
91+
$cmd->stdout_like(qr~rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$~,
92+
'RTT time present');
93+
$cmd->stdout_like(qr{^PING ::1 \(::1\) 56 data bytes
94+
64 bytes from ::1: icmp_seq=1 ttl=\d+ time=\d\.\d{3} ms
95+
96+
--- ::1 ping statistics ---
97+
1 packets transmitted, 1 received, 0% packet loss, time \d+ms
98+
rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$},
99+
'Entire ping output matched exactly');
100+
}
101+
}
102+
103+
my $localhost = "localhost";
104+
my $localhost_target_ipv4 = get_target($localhost, AF_INET);
105+
my $localhost_target_ipv6 = get_target($localhost, AF_INET6);
106+
diag("localhost_cannon_ipv4: '$localhost_target_ipv4'");
107+
diag("localhost_cannon_ipv6: '$localhost_target_ipv6'");
108+
die "Undefined cannonical name for $localhost on IPv4" unless defined $localhost_target_ipv4;
109+
die "Undefined cannonical name for $localhost on IPv6" unless defined $localhost_target_ipv6;
110+
111+
# localhost
112+
{
113+
my $cmd = Test::Command->new(cmd => "$ping -c1 $localhost");
114+
$cmd->exit_is_num(0);
115+
}
116+
117+
# -4 localhost
118+
{
119+
my $cmd = Test::Command->new(cmd => "$ping -c1 -4 $localhost");
120+
$cmd->exit_is_num(0);
121+
subtest 'output' => sub {
122+
$cmd->stdout_like(qr/64 bytes from $localhost_target_ipv4 \(127\.0\.0\.1\)/, "Ping received from $localhost (IPv4)");
123+
$cmd->stdout_like(qr/0% packet loss/, 'No packet loss');
124+
$cmd->stdout_like(qr/time=\d+\.\d+ ms/, 'Ping time present');
125+
$cmd->stdout_like(qr~rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$~,
126+
'RTT time present');
127+
$cmd->stdout_like(qr{^PING $localhost_target_ipv4 \(127\.0\.0\.1\) 56\(84\) bytes of data\.
128+
64 bytes from $localhost_target_ipv4 \(127\.0\.0\.1\): icmp_seq=1 ttl=\d+ time=\d\.\d{3} ms
129+
130+
--- $localhost_target_ipv4 ping statistics ---
131+
1 packets transmitted, 1 received, 0% packet loss, time \d+ms
132+
rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$},
133+
'Entire ping output matched exactly');
134+
}
135+
}
136+
137+
# -6 localhost
138+
SKIP: {
139+
if ($ENV{SKIP_IPV6}) {
140+
skip 'IPv6 tests', 2;
141+
}
142+
my $cmd = Test::Command->new(cmd => "$ping -c1 -6 $localhost");
143+
$cmd->exit_is_num(0);
144+
subtest 'output' => sub {
145+
$cmd->stdout_like(qr/64 bytes from $localhost_target_ipv6 \(::1\)/, "Ping received from $localhost (IPv6)");
146+
$cmd->stdout_like(qr/0% packet loss/, 'No packet loss');
147+
$cmd->stdout_like(qr/time=\d+\.\d+ ms/, 'Ping time present');
148+
$cmd->stdout_like(qr~rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$~,
149+
'RTT time present');
150+
}
151+
}

test/ping/ping-02-errors.t

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/perl -w
2+
3+
use Test::Command tests => 6;
4+
use Test::More;
5+
6+
diag("Running as UID: $>");
7+
diag("PATH = $ENV{PATH}");
8+
my $ping = $ARGV[0] // 'ping';
9+
diag("passed cmd: $ping");
10+
printf("# actually used cmd: ");
11+
system("/bin/sh", "-c", "command -v $ping");
12+
13+
# no arg
14+
{
15+
my $cmd = Test::Command->new(cmd => "$ping", env => { LC_ALL => 'C', LANG => 'C' });
16+
$cmd->exit_is_num(2);
17+
subtest 'output' => sub {
18+
$cmd->stderr_is_eq("$ping: usage error: Destination address required\n");
19+
}
20+
}
21+
22+
# -c1 -i0.001 127.0.0.1
23+
{
24+
my $cmd = Test::Command->new(cmd => "$ping -c1 -i0.001 127.0.0.1", env => { LC_ALL => 'C', LANG => 'C' });
25+
$cmd->exit_is_num($> == 0 ? 0 : 2);
26+
subtest 'output' => sub {
27+
if ($> == 0) {
28+
$cmd->stdout_like(qr/^PING 127\.0\.0\.1.*bytes of data\.$/m, 'Ping header');
29+
$cmd->stdout_like(qr/64 bytes from 127\.0\.0\.1: icmp_seq=1 ttl=\d+ time=\d+\.\d+ ms/m, 'Ping reply line');
30+
$cmd->stdout_like(qr/1 packets transmitted, 1 received, 0% packet loss/, 'Ping success summary');
31+
$cmd->stdout_like(qr{^PING 127\.0\.0\.1 \(127\.0\.0\.1\) 56\(84\) bytes of data\.
32+
64 bytes from 127\.0\.0\.1: icmp_seq=1 ttl=\d+ time=\d\.\d{3} ms
33+
34+
--- 127.0.0.1 ping statistics ---
35+
1 packets transmitted, 1 received, 0% packet loss, time \d+ms
36+
rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$},
37+
'Entire ping output matched exactly');
38+
} else {
39+
$cmd->stderr_like(qr/cannot flood/i);
40+
$cmd->stdout_like(qr/PING 127\.0\.0\.1/);
41+
$cmd->stderr_like(qr/use -i 0\.002/);
42+
$cmd->stderr_like(qr/.*ping: cannot flood, minimal interval for user must be >= 2 ms, use -i 0\.002 \(or higher\)/);
43+
}
44+
}
45+
}
46+
47+
# -c1 -i0.001 ::1
48+
SKIP: {
49+
if ($ENV{SKIP_IPV6}) {
50+
skip 'IPv6 tests', 2;
51+
}
52+
my $cmd = Test::Command->new(cmd => "$ping -c1 -i0.001 ::1", env => { LC_ALL => 'C', LANG => 'C' });
53+
$cmd->exit_is_num($> == 0 ? 0 : 2);
54+
subtest 'output' => sub {
55+
if ($> == 0) {
56+
$cmd->stdout_like(qr/^PING ::1 \(::1\) 56 data bytes$/m, 'Ping header');
57+
$cmd->stdout_like(qr/64 bytes from ::1: icmp_seq=1 ttl=\d+ time=\d+\.\d+ ms/m, 'Ping reply line');
58+
$cmd->stdout_like(qr/1 packets transmitted, 1 received, 0% packet loss/, 'Ping success summary');
59+
$cmd->stdout_like(qr{^PING ::1 \(::1\) 56 data bytes
60+
64 bytes from ::1: icmp_seq=1 ttl=\d+ time=\d\.\d{3} ms
61+
62+
--- ::1 ping statistics ---
63+
1 packets transmitted, 1 received, 0% packet loss, time \d+ms
64+
rtt min/avg/max/mdev = \d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3}/\d+\.\d{3} ms$},
65+
'Entire ping output matched exactly');
66+
} else {
67+
$cmd->stderr_like(qr/cannot flood/i);
68+
$cmd->stdout_like(qr/PING ::1/);
69+
$cmd->stderr_like(qr/use -i 0\.002/);
70+
$cmd->stderr_like(qr/.*ping: cannot flood, minimal interval for user must be >= 2 ms, use -i 0\.002 \(or higher\)/);
71+
}
72+
}
73+
}

test/tracepath/meson.build

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# SPDX-License-Identifier: GPL-2.0-or-later
2-
# Copyright (c) 2021 Petr Vorel <pvorel@suse.cz>
2+
# Copyright (c) 2021-2025 Petr Vorel <pvorel@suse.cz>
33

4-
cmd = tracepath
5-
cmd_name = 'tracepath '
6-
args = ['-V']
4+
message('Build directory is: ' + meson.current_build_dir())
75

8-
name = cmd_name + ' '.join(args)
9-
test(name, cmd, args : args)
6+
foreach t : ['tracepath-01-basic.t']
7+
test(t, find_program(t), args: tracepath)
8+
endforeach
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/perl -w
2+
3+
use Test::Command tests => 2;
4+
use Test::More;
5+
6+
diag("Running as UID: $>");
7+
diag("PATH = $ENV{PATH}");
8+
my $tracepath = $ARGV[0] // 'tracepath';
9+
diag("passed cmd: $tracepath");
10+
printf("# actually used cmd: ");
11+
system("/bin/sh", "-c", "command -v $tracepath");
12+
13+
# -V
14+
{
15+
my $cmd = Test::Command->new(cmd => "$tracepath -V");
16+
$cmd->exit_is_num(0);
17+
subtest 'output' => sub {
18+
$cmd->stdout_like(qr/^tracepath from iputils /, 'Print version');
19+
$cmd->stdout_like(qr/libcap: (yes|no), IDN: (yes|no), NLS: (yes|no), error.h: (yes|no), getrandom\(\): (yes|no), __fpending\(\): (yes|no)$/, 'Print config');
20+
}
21+
}

0 commit comments

Comments
 (0)