Rewrite the test suite for better compat and stability
authorKirill Isakov <is-kir@ya.ru>
Mon, 19 Jul 2021 08:32:13 +0000 (14:32 +0600)
committerKirill Isakov <is-kir@ya.ru>
Mon, 19 Jul 2021 08:32:13 +0000 (14:32 +0600)
Keeps all of the previous checks, but uses tinc scripts
instead of sleep(1) delays.

Improves and/or adds compatibility with:
  - Windows (Msys2);
  - FreeBSD;
  - NetBSD;
  - OpenBSD.

17 files changed:
configure.ac
test/Makefile.am
test/algorithms.test
test/basic.test
test/commandline.test
test/executables.test
test/import-export.test
test/invite-join.test
test/invite-offline.test
test/invite-tinc-up.test
test/legacy-protocol.test
test/ns-ping.test
test/scripts.test
test/security.test
test/sptps-basic.test
test/testlib.sh.in
test/variables.test

index 6d850ed..f278e3c 100644 (file)
@@ -131,6 +131,7 @@ AM_CONDITIONAL(UML, test "$uml" = true)
 AM_CONDITIONAL(VDE, test "$vde" = true)
 AM_CONDITIONAL(TUNEMU, test "$tunemu" = true)
 AM_CONDITIONAL(WITH_SYSTEMD, test "$systemd" = true)
+AM_CONDITIONAL(WITH_LEGACY_PROTOCOL, test "x$enable_legacy_protocol" != "xno")
 
 AC_CACHE_SAVE
 
index dae30dc..8c8d08f 100644 (file)
@@ -1,17 +1,25 @@
 TESTS = \
        basic.test \
-       commandline.test \
        executables.test \
+       commandline.test \
        import-export.test \
        invite-join.test \
        invite-offline.test \
        invite-tinc-up.test \
-       legacy-protocol.test \
-       ns-ping.test \
+       variables.test \
        scripts.test \
        security.test \
-       sptps-basic.test \
-       variables.test
+       sptps-basic.test
+
+if WITH_LEGACY_PROTOCOL
+TESTS += \
+        legacy-protocol.test \
+        algorithms.test
+endif
+
+if LINUX
+TESTS += ns-ping.test
+endif
 
 dist_check_SCRIPTS = $(TESTS)
 
index c506a5d..04d9131 100755 (executable)
@@ -1,46 +1,56 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize two nodes
+echo [STEP] Initialize two nodes
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
-set Port 32755
+set Port 30070
 set Address localhost
 set ExperimentalProtocol no
 EOF
 
-$tinc $c2 <<EOF
+tinc bar <<EOF
 init bar
 set DeviceType dummy
 set Port 0
 set ExperimentalProtocol no
 EOF
 
-# Exchange configuration
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
 
-$tinc $c1 export | $tinc $c2 exchange | $tinc $c1 import
-$tinc $c2 add ConnectTo foo
-$tinc $c1 start $r1
+echo [STEP] Exchange configuration
 
-# Test various ciphers and digests
+tinc foo export | tinc bar exchange | tinc foo import
+tinc bar add ConnectTo foo
+start_tinc foo
 
-for digest in none md5 sha1 sha256 sha512; do
-       for cipher in none bf-cbc aes-128-cbc aes-256-cbc camellia-128-cbc camellia-256-cbc; do
-               echo Testing $cipher $digest
-               $tinc $c2 <<EOF
+echo [STEP] Test various ciphers and digests
+
+# The full suite results in a large test matrix and it takes a lot of time to run.
+# The list was reduced to the strongest available algorithms (and "none").
+digests="none sha256 sha512"
+ciphers="none aes-256-cbc camellia-256-cbc"
+
+for digest in $digests; do
+  for cipher in $ciphers; do
+    echo "Testing $cipher $digest"
+
+    tinc bar <<EOF
 set Digest $digest
 set Cipher $cipher
 EOF
 
-               $tinc $c2 start $r2
-               sleep 2;
-               $tinc $c1 info bar
-               $tinc $c1 info bar | grep -q 'directly with UDP'
-               $tinc $c2 stop
-       done
-done
+    start_tinc bar
+    wait_script foo hosts/bar-up
+    wait_script bar hosts/foo-up
+
+    tinc foo info bar | grep reachable
 
-$tinc $c1 stop
+    tinc bar stop
+  done
+done
index c377202..d4d2cc6 100755 (executable)
@@ -1,20 +1,26 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize and test one node
+echo [STEP] Initialize and test one node
 
-$tinc $c1 init foo
-$tinc $c1 set DeviceType dummy
-$tinc $c1 set Port 0
+tinc foo <<EOF
+init foo
+set DeviceType dummy
+set Port 0
+EOF
 
-# Test running in the foreground
+echo [STEP] Test running in the foreground
 
-(sleep 1; $tinc $c1 stop) &
-$tinc $c1 start $r1 -D
+create_script foo tinc-up '
+  tinc foo stop &
+'
+start_tinc foo -D
 
-# Test running tinc in the background
+echo [STEP] Test running tinc in the background
 
-$tinc $c1 start $r1
-sleep 1
-$tinc $c1 stop
+create_script foo tinc-up '
+  tinc foo stop &
+'
+start_tinc foo
index f785b22..5d6e6a6 100755 (executable)
@@ -1,54 +1,49 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize one node
+echo [STEP] Initialize one node
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
 set Port 0
 EOF
 
-cat >$d1/tinc-up <<EOF
-#!/bin/sh
-read pid rest <$d1/pid
-(sleep 0.1; kill \$pid) &
-EOF
-
-cat >$d1/tinc-up.cmd <<EOF
-start /min ../$tinc $c1 stop
-EOF
+create_script foo tinc-up '
+  tinc foo stop &
+'
 
-# Test tincd command line options that should work
+echo [STEP] Test tincd command line options that should work
 
-$tincd $c1 $r1 -D
-$tincd $c1 $r1 --no-detach
-$tincd $c1 $r1 -D -d
-$tincd $c1 $r1 -D -d2
-$tincd $c1 $r1 -D -d 2
-$tincd $c1 $r1 -D -n foo
-$tincd $c1 $r1 -D -nfoo
-$tincd $c1 $r1 -D --net=foo
-$tincd $c1 $r1 -D --net foo
+tincd foo -D
+tincd foo --no-detach
+tincd foo -D -d
+tincd foo -D -d2
+tincd foo -D -d 2
+tincd foo -D -n foo
+tincd foo -D -nfoo
+tincd foo -D --net=foo
+tincd foo -D --net foo
 
-# Test tincd command line options that should not work
+echo [STEP] Test tincd command line options that should not work
 
-$tincd $c1 $r1 foo && exit 1 || true
-$tincd $c1 $r1 --pidfile && exit 1 || true
-$tincd $c1 $r1 --foo && exit 1 || true
+must_fail tincd foo foo
+must_fail tincd foo --pidfile
+must_fail tincd foo --foo
 
-# Test tinc command line options that should work
+echo [STEP] Test tinc command line options that should work
 
-$tinc $c1 get name
-$tinc $c1 -n foo get name
-$tinc $c1 -nfoo get name
-$tinc $c1 --net=foo get name
-$tinc $c1 --net foo get name
+tinc foo get name
+tinc foo -n foo get name
+tinc foo -nfoo get name
+tinc foo --net=foo get name
+tinc foo --net foo get name
 
-# Test tinc command line options that should not work
+echo [STEP] Test tinc command line options that should not work
 
-$tinc $c1 -n foo get somethingreallyunknown && exit 1 || true
-$tinc $c1 --net && exit 1 || true
-$tinc $c1 --net get name && exit 1 || true
-$tinc $c1 foo && exit 1 || true
+must_fail tinc foo -n foo get somethingreallyunknown
+must_fail tinc foo --net
+must_fail tinc foo --net get name
+must_fail tinc foo foo
index 0cf9723..28bff2a 100755 (executable)
@@ -1,10 +1,14 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Just test whether the executables work
-$tincd --help
-$tinc --help
-if [ -e $sptps_test ]; then
-       $sptps_test --help
+echo [STEP] Just test whether the executables work
+
+tinc foo --help
+
+tincd foo --help
+
+if [ -e "$SPTPS_TEST" ]; then
+  $SPTPS_TEST --help
 fi
index e7bca23..190d21b 100755 (executable)
@@ -1,64 +1,67 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize three nodes
+echo [STEP] Initialize three nodes
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
-set Port 32752
+set Port 30000
 set Address localhost
 EOF
 
-$tinc $c2 <<EOF
+tinc bar <<EOF
 init bar
 set DeviceType dummy
 set Port 0
 EOF
 
-$tinc $c3 <<EOF
+tinc baz <<EOF
 init baz
 set DeviceType dummy
 set Port 0
 EOF
 
-# Test import, export and exchange commands
+echo [STEP] Test import, export and exchange commands
 
-$tinc $c1 export | $tinc $c2 exchange | $tinc $c1 import
+tinc foo export | tinc bar exchange | tinc foo import
 
-# Test export-all and exchange-all
+echo [STEP] Test export-all and exchange-all
 
-$tinc $c1 export-all | $tinc $c3 exchange | $tinc $c1 import
-$tinc $c1 exchange-all </dev/null | $tinc $c2 import
+tinc foo export-all | tinc baz exchange | tinc foo import
+tinc foo exchange-all </dev/null | tinc bar import
 
-# Test equivalence of host config files
+echo [STEP] Test equivalence of host config files
 
-cmp $d1/hosts/foo $d2/hosts/foo
-cmp $d1/hosts/foo $d3/hosts/foo
-cmp $d1/hosts/bar $d2/hosts/bar
-cmp $d1/hosts/bar $d3/hosts/bar
-cmp $d1/hosts/baz $d2/hosts/baz
-cmp $d1/hosts/baz $d3/hosts/baz
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAZ/hosts/foo"
+diff -w "$DIR_FOO/hosts/bar" "$DIR_BAR/hosts/bar"
+diff -w "$DIR_FOO/hosts/bar" "$DIR_BAZ/hosts/bar"
+diff -w "$DIR_FOO/hosts/baz" "$DIR_BAR/hosts/baz"
+diff -w "$DIR_FOO/hosts/baz" "$DIR_BAZ/hosts/baz"
 
-# Check whether the nodes can connect to each other
+echo [STEP] Check whether the nodes can connect to each other
 
-$tinc $c1 start $r1
+create_script foo tinc-up '
+  tinc bar add ConnectTo foo
+  tinc baz add ConnectTo foo
+'
 
-$tinc $c2 add ConnectTo foo
-$tinc $c3 add ConnectTo foo
+create_script foo hosts/bar-up
+create_script foo hosts/baz-up
 
-sleep 1
+start_tinc foo
 
-$tinc $c2 start $r2
-$tinc $c3 start $r3
+wait_script foo tinc-up
 
-sleep 1
+start_tinc bar
+start_tinc baz
 
-test `$tinc $c1 dump reachable nodes | wc -l` = 3
-test `$tinc $c2 dump reachable nodes | wc -l` = 3
-test `$tinc $c3 dump reachable nodes | wc -l` = 3
+wait_script foo hosts/bar-up
+wait_script foo hosts/baz-up
 
-$tinc $c3 stop
-$tinc $c2 stop
-$tinc $c1 stop
+require_nodes foo 3
+require_nodes bar 3
+require_nodes baz 3
index 5167320..892767c 100755 (executable)
@@ -1,46 +1,50 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize one node
+echo [STEP] Initialize one node
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
 set Mode switch
 set Broadcast no
 set Address localhost
-set Port 32751
-start $r1
+set Port 30010
 EOF
 
-# Generate an invitation and let another node join the VPN
+start_tinc foo
 
-$tinc $c1 invite bar | $tinc $c2 join
+echo [STEP] Generate an invitation and let another node join the VPN
 
-# Test equivalence of host config files
+tinc foo invite bar | tinc bar join
 
-cmp $d1/hosts/foo $d2/hosts/foo
-test "`grep ^Ed25519PublicKey $d1/hosts/bar`" = "`grep ^Ed25519PublicKey $d2/hosts/bar`"
+echo [STEP] Test equivalence of host config files
 
-# Test Mode, Broadcast and ConnectTo statements
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
 
-test `$tinc $c2 get Mode` = switch
-test `$tinc $c2 get Broadcast` = no
-test `$tinc $c2 get ConnectTo` = foo
+echo [STEP] Test Mode, Broadcast and ConnectTo statements
 
-# Check whether the new node can join the VPN
+test "$(tinc bar get Mode)" = switch
+test "$(tinc bar get Broadcast)" = no
+test "$(tinc bar get ConnectTo)" = foo
 
-$tinc $c2 << EOF
+echo [STEP] Check whether the new node can join the VPN
+
+tinc bar <<EOF
 set DeviceType dummy
 set Port 0
-start $r2
 EOF
 
-sleep 1
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
+
+start_tinc bar
 
-test `$tinc $c1 dump reachable nodes | wc -l` = 2
-test `$tinc $c2 dump reachable nodes | wc -l` = 2
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
 
-$tinc $c2 stop
-$tinc $c1 stop
+require_nodes foo 2
+require_nodes bar 2
index e515919..61c0f0a 100755 (executable)
@@ -1,49 +1,51 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize one node
+echo [STEP] Initialize one node
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
 set Mode switch
 set Broadcast no
 set Address localhost
-set Port 32758
+set Port 30020
 EOF
 
-# Generate an invitation offline and let another node join the VPN
+echo [STEP] Generate an invitation offline and let another node join the VPN
 
-invitation=`$tinc $c1 invite bar | tr -d '\r'`
+invitation=$(tinc foo invite bar)
 
-$tinc $c1 start $r1
+start_tinc foo
+tinc bar join "$invitation"
 
-$tinc $c2 join $invitation
+echo [STEP] Test equivalence of host config files
 
-# Test equivalence of host config files
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
 
-cmp $d1/hosts/foo $d2/hosts/foo
-test "`grep ^Ed25519PublicKey $d1/hosts/bar`" = "`grep ^Ed25519PublicKey $d2/hosts/bar`"
+echo [STEP] Test Mode, Broadcast and ConnectTo statements
 
-# Test Mode, Broadcast and ConnectTo statements
+test "$(tinc bar get Mode)" = switch
+test "$(tinc bar get Broadcast)" = no
+test "$(tinc bar get ConnectTo)" = foo
 
-test `$tinc $c2 get Mode` = switch
-test `$tinc $c2 get Broadcast` = no
-test `$tinc $c2 get ConnectTo` = foo
+echo [STEP] Check whether the new node can join the VPN
 
-# Check whether the new node can join the VPN
-
-$tinc $c2 << EOF
+tinc bar <<EOF
 set DeviceType dummy
 set Port 0
-start $r2
 EOF
 
-sleep 1
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
+
+start_tinc bar
 
-test `$tinc $c1 dump reachable nodes | wc -l` = 2
-test `$tinc $c2 dump reachable nodes | wc -l` = 2
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
 
-$tinc $c2 stop
-$tinc $c1 stop
+require_nodes foo 2
+require_nodes bar 2
index 55123b8..6cf280b 100755 (executable)
@@ -1,59 +1,53 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize one node
+echo [STEP] Initialize one node
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
 set Address localhost
-set Port 32756
-start $r1
+set Port 30030
 EOF
 
-# Generate an invitation and let another node join the VPN
+create_script foo tinc-up
+start_tinc foo
+wait_script foo tinc-up
 
-sleep 1
+echo [STEP] Generate an invitation and let another node join the VPN
 
-cat >$d1/invitation-created <<EOF
-#!/bin/sh
-echo Name = \$NODE >\$INVITATION_FILE
-echo Ifconfig = 93.184.216.34/24 >>\$INVITATION_FILE
-echo Route = 2606:2800:220:1::/64 2606:2800:220:1:248:1893:25c8:1946 >>\$INVITATION_FILE
-echo Route = 1.2.3.4 1234:: >>\$INVITATION_FILE
-$tinc $c1 export >>\$INVITATION_FILE
-EOF
-
-cat >$d1/invitation-created.cmd <<EOF
-echo Name = %NODE% >%INVITATION_FILE%
-echo Ifconfig = 93.184.216.34/24 >>%INVITATION_FILE%
-echo Route = 2606:2800:220:1::/64 2606:2800:220:1:248:1893:25c8:1946 >>%INVITATION_FILE%
-echo Route = 1.2.3.4 1234:: >>%INVITATION_FILE%
-$tinc $c1 export >>%INVITATION_FILE%
-EOF
-
-chmod u+x $d1/invitation-created
+# shellcheck disable=SC2016
+create_script foo invitation-created '
+cat >"$INVITATION_FILE" <<INVITE
+Name = $NODE
+Ifconfig = 93.184.216.34/24
+Route = 2606:2800:220:1::/64 2606:2800:220:1:248:1893:25c8:1946
+Route = 1.2.3.4 1234::
 
-$tinc $c1 invite bar | tail -1 | $tinc $c2 --batch join
+$(tinc foo export)
+INVITE
+'
 
-# Test equivalence of host config files
+tinc foo invite bar | tail -1 | tinc bar --batch join
 
-cmp $d1/hosts/foo $d2/hosts/foo
-test "`grep ^Ed25519PublicKey $d1/hosts/bar`" = "`grep ^Ed25519PublicKey $d2/hosts/bar`"
+echo [STEP] Test equivalence of host config files
 
-# Check if the tinc-up.invitation file is created and contains the right commands
+diff -w "$DIR_FOO/hosts/foo" "$DIR_BAR/hosts/foo"
+test "$(grep ^Ed25519PublicKey "$DIR_FOO/hosts/bar")" = "$(grep ^Ed25519PublicKey "$DIR_BAR/hosts/bar")"
 
-test -f $d2/tinc-up.invitation
+echo [STEP] Check if the tinc-up.invitation file is created and contains the right commands
 
-fgrep -q "93.184.216.34/24" $d2/tinc-up.invitation
-fgrep -q "2606:2800:220:1::/64" $d2/tinc-up.invitation
-fgrep -q "2606:2800:220:1:248:1893:25c8:1946" $d2/tinc-up.invitation
-fgrep -q "1234::" $d2/tinc-up.invitation && exit 1
+bar_tinc_up="$DIR_BAR/tinc-up.invitation"
+test -f "$bar_tinc_up"
 
-# Check that no tinc-up is created and that tinc-up.invitation is not executable
+grep -F -q "93.184.216.34/24" "$bar_tinc_up"
+grep -F -q "2606:2800:220:1::/64" "$bar_tinc_up"
+grep -F -q "2606:2800:220:1:248:1893:25c8:1946" "$bar_tinc_up"
+must_fail grep -F -q "1234::" "$bar_tinc_up"
 
-test -x $d2/tinc-up.invitation && exit 1
-test -f $d2/tinc-up && exit 1
+echo [STEP] Check that no tinc-up is created and that tinc-up.invitation is not executable
 
-$tinc $c1 stop
+must_fail test -x "$bar_tinc_up"
+must_fail test -f "$DIR_BAR/tinc-up"
index 5205877..3446621 100755 (executable)
@@ -1,84 +1,90 @@
 #!/bin/sh
 
-# Skip this test if the legacy protocol is disabled
-if grep -q "define DISABLE_LEGACY 1" "${0%/*}/../config.h"; then
-       exit 77
-fi
-
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize two nodes
+echo [STEP] Initialize two nodes
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
-set Port 32753
+set Port 30060
 set Address localhost
-set PingTimeout 1
+add Subnet 10.98.98.1
+set PingTimeout 2
 EOF
 
-$tinc $c2 <<EOF
+tinc bar <<EOF
 init bar
 set DeviceType dummy
 set Port 0
-set PingTimeout 1
-set MaxTimeout 1
+add Subnet 10.98.98.2
+set PingTimeout 2
+set MaxTimeout 2
 EOF
 
-# Exchange host config files
+echo [STEP] Exchange host config files
+
+tinc foo export | tinc bar exchange | tinc foo import
+tinc bar add ConnectTo foo
 
-$tinc $c1 export | $tinc $c2 exchange | $tinc $c1 import
-$tinc $c2 add ConnectTo foo
+echo [STEP] Foo 1.1, bar 1.0
 
-# Foo 1.1, bar 1.0
+tinc bar set ExperimentalProtocol no
+tinc foo del bar.Ed25519PublicKey
+tinc bar del foo.Ed25519PublicKey
 
-$tinc $c2 set ExperimentalProtocol no
-$tinc $c1 del bar.Ed25519PublicKey
-$tinc $c2 del foo.Ed25519PublicKey
+create_script foo hosts/bar-up
+create_script bar hosts/foo-up
 
-$tinc $c1 start $r1
-$tinc $c2 start $r2
+start_tinc foo
+start_tinc bar
 
-sleep 1
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
 
-test `$tinc $c1 dump reachable nodes | wc -l` = 2
-test `$tinc $c2 dump reachable nodes | wc -l` = 2
+require_nodes foo 2
+require_nodes bar 2
 
-$tinc $c2 stop
-$tinc $c1 stop
+tinc bar stop
+tinc foo stop
 
-test -z "`$tinc $c1 get bar.Ed25519PublicKey`"
-test -z "`$tinc $c2 get foo.Ed25519PublicKey`"
+test -z "$(tinc foo get bar.Ed25519PublicKey)"
+test -z "$(tinc bar get foo.Ed25519PublicKey)"
 
-# Foo 1.1, bar upgrades to 1.1
+echo [STEP] Foo 1.1, bar upgrades to 1.1
 
-$tinc $c2 del ExperimentalProtocol
+tinc bar del ExperimentalProtocol
 
-$tinc $c1 start $r1
-$tinc $c2 start $r2
+start_tinc foo
+start_tinc bar
 
-sleep 5
+wait_script foo hosts/bar-up
+wait_script bar hosts/foo-up
 
-test `$tinc $c1 dump reachable nodes | wc -l` = 2
-test `$tinc $c2 dump reachable nodes | wc -l` = 2
+require_nodes foo 2
+require_nodes bar 2
 
-$tinc $c2 stop
-$tinc $c1 stop
+tinc bar stop
+tinc foo stop
 
-test -n "`$tinc $c1 get bar.Ed25519PublicKey`"
-test -n "`$tinc $c2 get foo.Ed25519PublicKey`"
+test -n "$(tinc foo get bar.Ed25519PublicKey)"
+test -n "$(tinc bar get foo.Ed25519PublicKey)"
 
-# Bar downgrades, must no longer be allowed to connect
+echo [STEP] Bar downgrades, must no longer be allowed to connect
 
-$tinc $c2 set ExperimentalProtocol no
+tinc bar set ExperimentalProtocol no
 
-$tinc $c1 start $r1
-$tinc $c2 start $r2
+create_script foo subnet-up
+start_tinc foo
+wait_script foo subnet-up
 
-sleep 5
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
 
-test `$tinc $c1 dump reachable nodes | wc -l` = 1
-test `$tinc $c2 dump reachable nodes | wc -l` = 1
+# There is no reliable way to wait for 'not connecting'.
+sleep 10
 
-$tinc $c2 stop
-$tinc $c1 stop
+require_nodes foo 1
+require_nodes bar 1
index 5a5415c..5931c75 100755 (executable)
@@ -1,72 +1,79 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Skip this test if we aren't root or if "ip netns" does not exist
+echo "[STEP] Skip this test if we aren't root or if 'ip netns' does not exist"
 
-test "`id -u`" = "0" || exit 77
-ip netns list || exit 77
+test "$(id -u)" = "0" || exit $EXIT_SKIP_TEST
+test -e /dev/net/tun || exit $EXIT_SKIP_TEST
+ip netns list || exit $EXIT_SKIP_TEST
 
-# Initialize two nodes
+ip_foo=192.168.1.1
+ip_bar=192.168.1.2
+mask=24
 
-$tinc $c1 <<EOF
+echo [STEP] Initialize two nodes
+
+tinc foo <<EOF
 init foo
-set Subnet 192.168.1.1
+set Subnet $ip_foo
 set Interface ping.test1
-set Port 32577
+set Port 30090
 set Address localhost
 set AutoConnect no
 EOF
 
-cat >$d1/tinc-up <<EOF
-#!/bin/sh
-ip netns add ping.test1
-ip link set dev \$INTERFACE netns ping.test1
-ip netns exec ping.test1 ip addr add 192.168.1.1/24 dev \$INTERFACE
-ip netns exec ping.test1 ip link set \$INTERFACE up
-EOF
+# shellcheck disable=SC2016
+create_script foo tinc-up "
+  ip netns add ping.test1
+  ip link set dev \$INTERFACE netns ping.test1
+  ip netns exec ping.test1 ip addr add $ip_foo/$mask dev \$INTERFACE
+  ip netns exec ping.test1 ip link set \$INTERFACE up
+"
 
-$tinc $c2 <<EOF
+tinc bar <<EOF
 init bar
-set Subnet 192.168.1.2
+set Subnet $ip_bar
 set Interface ping.test2
-set Port 32574
+set Port 30091
 set AutoConnect no
 EOF
 
-cat >$d2/tinc-up <<EOF
-#!/bin/sh
-ip netns add ping.test2
-ip link set dev \$INTERFACE netns ping.test2
-ip netns exec ping.test2 ip addr add 192.168.1.2/24 dev \$INTERFACE
-ip netns exec ping.test2 ip link set \$INTERFACE up
-EOF
+# shellcheck disable=SC2016
+create_script bar tinc-up "
+  ip netns add ping.test2
+  ip link set dev \$INTERFACE netns ping.test2
+  ip netns exec ping.test2 ip addr add $ip_bar/$mask dev \$INTERFACE
+  ip netns exec ping.test2 ip link set \$INTERFACE up
+"
+
+echo [STEP] Exchange configuration files
 
-# Exchange configuration files
+tinc foo export | tinc bar exchange | tinc foo import
 
-$tinc $c1 export | $tinc $c2 exchange | $tinc $c1 import
+echo [STEP] Start tinc
 
-# Start tinc
+start_tinc foo
+start_tinc bar
 
-$tinc $c1 start $r1
-$tinc $c2 start $r2
+wait_script foo tinc-up
+wait_script bar tinc-up
 
-sleep 1
+echo [STEP] The nodes should not be able to ping each other if there is no connection
 
-# The nodes should not be able to ping each other if there is no connection
+must_fail ip netns exec ping.test1 ping -W1 -c3 $ip_bar
 
-ip netns exec ping.test1 ping -W1 -c3 192.168.1.2 && exit 1
+echo [STEP] After connecting they should be
 
-# After connecting they should be
+create_script bar hosts/foo-up
 
-$tinc $c2 add ConnectTo foo
-sleep 1
+tinc bar add ConnectTo foo
+wait_script bar hosts/foo-up
 
-ip netns exec ping.test1 ping -W1 -c3 192.168.1.2
+ip netns exec ping.test1 ping -W1 -c3 $ip_bar
 
-# Clean up
+echo [STEP] Clean up
 
-$tinc $c2 stop
-$tinc $c1 stop
 ip netns del ping.test2
 ip netns del ping.test1
index 7889e1d..a367827 100755 (executable)
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-echo Initializing node...
+echo [STEP] Initializing server node
 
-# Initialize server node
+port_foo=30040
+port_bar=30041
 
-$tinc $c1 <<EOF
+tinc foo <<EOF
 init foo
 set DeviceType dummy
-set Port 32759
+set Port $port_foo
 set Address 127.0.0.1
 add Subnet 10.0.0.1
 add Subnet fec0::/64
 EOF
 
-# Set up scripts
-
-echo Setting up scripts...
-
-OUT=$d1/scripts.out
-rm -f $OUT 
-
-for script in tinc-up tinc-down host-up host-down subnet-up subnet-down hosts/foo-up hosts/foo-down hosts/bar-up hosts/bar-down invitation-created invitation-accepted; do
-cat >$d1/$script << EOF
-#!/bin/sh
-echo $script \$NETNAME,\$NAME,\$DEVICE,\$IFACE,\$NODE,\$REMOTEADDRESS,\$REMOTEPORT,\$SUBNET,\$WEIGHT,\$INVITATION_FILE,\$INVITATION_URL,\$DEBUG >>$OUT
+echo [STEP] Setting up scripts
+
+OUT=$DIR_FOO/scripts.out
+rm -f "$OUT"
+
+for script in \
+  tinc-up tinc-down \
+  host-up host-down \
+  subnet-up subnet-down \
+  hosts/foo-up hosts/foo-down \
+  hosts/bar-up hosts/bar-down \
+  invitation-created invitation-accepted; do
+
+  commands=$(
+    cat <<EOF
+      if is_windows && [ -n "\$INVITATION_FILE" ]; then
+        INVITATION_FILE=\$(cygpath --unix -- "\$INVITATION_FILE")
+      fi
+      echo >>'$OUT' "$script" "$TINC_SCRIPT_VARS"
 EOF
-chmod u+x $d1/$script
+  )
 
-cat >$d1/$script.cmd << EOF
-echo $script %NETNAME%,%NAME%,%DEVICE%,%IFACE%,%NODE%,%REMOTEADDRESS%,%REMOTEPORT%,%SUBNET%,%WEIGHT%,%INVITATION_FILE%,%INVITATION_URL%,%DEBUG% >>$OUT
-EOF
+  create_script foo "$script" "$commands"
 done
 
-# Start server node
-
-echo Starting server node...
-
-$tinc $c1 -n netname start $r1
+echo [STEP] Starting server node
 
-echo foo-started >>$OUT
+start_tinc foo -n netname
+wait_script foo subnet-up 2
+echo foo-started >>"$OUT"
 
-# Invite client node
+echo [STEP] Inviting client node
 
-echo Inviting client node...
+url=$(tinc foo -n netname2 invite bar)
+file=$(basename "$(find "$DIR_FOO/invitations" -type f ! -name ed25519_key.priv)")
 
-url=`$tinc $c1 -n netname2 invite bar | tr -d '\r'`
-file=`cd $d1/invitations; ls | grep -v ed25519_key.priv`
-echo bar-invited >>$OUT
+if is_windows; then
+  file=$(cygpath --unix -- "$file")
+fi
 
-echo Joining client node...
+wait_script foo invitation-created
+echo bar-invited >>"$OUT"
 
-$tinc $c2 -n netname3 join $url
-echo bar-joined >>$OUT
+echo [STEP] Joining client node
 
-# Start and stop client node
+tinc bar -n netname3 join "$url"
+wait_script foo invitation-accepted
+echo bar-joined >>"$OUT"
 
-echo Starting client node...
+echo [STEP] Starting client node
 
-$tinc $c2 << EOF
+tinc bar <<EOF
 set DeviceType dummy
-set Port 32760
+set Port $port_bar
 add Subnet 10.0.0.2
 add Subnet fec0::/64#5
-start $r2
 EOF
 
-sleep 1
+start_tinc bar
+wait_script foo subnet-up 2
+echo bar-started-1 >>"$OUT"
 
-echo bar-started >>$OUT
+tinc foo debug 4
+tinc bar stop
+wait_script foo subnet-down 2
+echo bar-stopped >>"$OUT"
 
-$tinc $c1 debug 4
-$tinc $c2 stop
+tinc foo debug 5
+start_tinc bar
+wait_script foo subnet-up 2
+echo bar-started-2 >>"$OUT"
 
-sleep 1
+echo [STEP] Stop server node
 
-echo bar-stopped >>$OUT
+tinc foo stop
+tinc bar stop
+wait_script foo tinc-down
 
-$tinc $c1 debug 5
-$tinc $c2 start $r2
+echo [STEP] Check if the script output is what is expected
 
-sleep 1
-
-echo bar-started >>$OUT
-
-# Stop server node
-
-$tinc $c1 stop
-sleep 1
-$tinc $c2 stop
-
-# Check if the script output is what is expected
-
-cat >$OUT.expected << EOF
+cat >"$OUT.expected" <<EOF
 tinc-up netname,foo,dummy,,,,,,,,,5
 subnet-up netname,foo,dummy,,foo,,,10.0.0.1,,,,5
 subnet-up netname,foo,dummy,,foo,,,fec0::/64,,,,5
 foo-started
-invitation-created netname2,foo,,,bar,,,,,$d1/invitations/$file,$url,
+invitation-created netname2,foo,,,bar,,,,,$DIR_FOO/invitations/$file,$url,
 bar-invited
 invitation-accepted netname,foo,dummy,,bar,127.0.0.1,,,,,,5
 bar-joined
-host-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5
-hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,5
-bar-started
-host-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,4
-hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,4
-subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,4
-subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,4
+host-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
+bar-started-1
+host-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,4
+hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,4
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,4
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,4
 bar-stopped
-host-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5
-hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,5
-subnet-up netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,5
-bar-started
-host-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5
-hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,32760,,,,,5
-subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,10.0.0.2,,,,5
-subnet-down netname,foo,dummy,,bar,127.0.0.1,32760,fec0::/64,5,,,5
+host-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+hosts/bar-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
+subnet-up netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
+bar-started-2
+host-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+hosts/bar-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,,,,,5
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,10.0.0.2,,,,5
+subnet-down netname,foo,dummy,,bar,127.0.0.1,$port_bar,fec0::/64,5,,,5
 subnet-down netname,foo,dummy,,foo,,,10.0.0.1,,,,5
 subnet-down netname,foo,dummy,,foo,,,fec0::/64,,,,5
 tinc-down netname,foo,dummy,,,,,,,,,5
 EOF
 
-tr -d '\r' <$OUT >$OUT.actual
-
-cmp $OUT.actual $OUT.expected
+diff -w "$OUT" "$OUT.expected"
index 6c15bda..91a5da5 100755 (executable)
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Skip this test if tools are missing
+echo [STEP] Skip this test if tools are missing
 
-which nc >/dev/null || exit 77
+which nc >/dev/null || exit $EXIT_SKIP_TEST
+which timeout >/dev/null || exit $EXIT_SKIP_TEST
 
-if [ "$(uname)" = "Darwin" ]; then
-    alias timeout=gtimeout
-fi
+foo_port=30050
+bar_port=30051
 
-which timeout >/dev/null || exit 77
+# usage: splice protocol_version
+splice() {
+  ./splice foo localhost $foo_port bar localhost $bar_port "$1" &
+  sleep 10
+}
 
-# Initialize two nodes
+# usage: send_with_timeout "data to send" "data expected to receive"
+send_with_timeout() {
+  data=$1
+  expected=$3
 
-$tinc $c1 <<EOF
+  result=$(
+    (
+      sleep 6
+      printf "%s\n" "$data"
+    ) | timeout 10 nc localhost $foo_port
+  ) && exit 1
+
+  test $? = $EXIT_TIMEOUT
+
+  if [ -z "$expected" ]; then
+    test -z "$result"
+  else
+    echo "$result" | grep -q "^$expected"
+  fi
+}
+
+echo [STEP] Initialize two nodes
+
+tinc foo <<EOF
 init foo
 set DeviceType dummy
-set Port 32754
+set Port $foo_port
 set Address localhost
-set PingTimeout 1
+set PingTimeout 3
 set AutoConnect no
+set Subnet 10.96.96.1
 EOF
 
-$tinc $c2 <<EOF
+tinc bar <<EOF
 init bar
 set DeviceType dummy
-set Port 32755
-set PingTimeout 1
-set MaxTimeout 1
+set Port $bar_port
+set PingTimeout 3
+set MaxTimeout 3
 set ExperimentalProtocol no
 set AutoConnect no
+set Subnet 10.96.96.2
 EOF
 
-# Exchange host config files
+echo [STEP] Exchange host config files
 
-$tinc $c1 export | $tinc $c2 exchange | $tinc $c1 import
+tinc foo export | tinc bar exchange | tinc foo import
 
-$tinc $c1 start $r1
-$tinc $c2 start $r2
+create_script foo subnet-up
+start_tinc foo
+wait_script foo subnet-up
 
-# No ID sent by responding node if we don't send an ID first, before the timeout
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
 
-result=`(sleep 2; echo "0 bar 17.7") | timeout 3 nc localhost 32754` && exit 1
-test $? = 124
-test -z "$result"
+echo "[STEP] No ID sent by responding node if we don't send an ID first, before the timeout"
+send_with_timeout "0 bar 17.7" ""
 
-# ID sent if initiator sends first, but still tarpitted
+echo [STEP] ID sent if initiator sends first, but still tarpitted
+send_with_timeout "0 bar 17.7" "0 foo 17.7"
 
-result=`echo "0 bar 17.7" | timeout 3 nc localhost 32754` && exit 1
-test $? = 124
-test "`echo "$result" | head -c 10`" = "0 foo 17.7"
+echo [STEP] No invalid IDs allowed
+send_with_timeout "0 foo 17.7" ""
+send_with_timeout "0 baz 17.7" ""
 
-# No invalid IDs allowed
+echo [STEP] No NULL METAKEYs allowed
+data="0 foo 17.0\n1 0 672 0 0 834188619F4D943FD0F4B1336F428BD4AC06171FEABA66BD2356BC9593F0ECD643F0E4B748C670D7750DFDE75DC9F1D8F65AB1026F5ED2A176466FBA4167CC567A2085ABD070C1545B180BDA86020E275EA9335F509C57786F4ED2378EFFF331869B856DDE1C05C461E4EECAF0E2FB97AF77B7BC2AD1B34C12992E45F5D1254BBF0C3FB224ABB3E8859594A83B6CA393ED81ECAC9221CE6BC71A727BCAD87DD80FC0834B87BADB5CB8FD3F08BEF90115A8DF1923D7CD9529729F27E1B8ABD83C4CF8818AE10257162E0057A658E265610B71F9BA4B365A20C70578FAC65B51B91100392171BA12A440A5E93C4AA62E0C9B6FC9B68F953514AAA7831B4B2C31C4\n"
+send_with_timeout "$data" "" # Not even the ID should be sent when the first packet contains illegal data
 
-result=`echo "0 foo 17.7" | timeout 1 nc localhost 32754` && exit 1
-test $? = 124
-test -z "$result"
+echo [STEP] No splicing allowed
 
-result=`echo "0 baz 17.7" | timeout 1 nc localhost 32754` && exit 1
-test $? = 124
-test -z "$result"
+tinc bar stop
+tinc bar del ExperimentalProtocol
 
-# No NULL METAKEYs allowed
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
 
-result=`printf "0 foo 17.0\n1 0 672 0 0 834188619F4D943FD0F4B1336F428BD4AC06171FEABA66BD2356BC9593F0ECD643F0E4B748C670D7750DFDE75DC9F1D8F65AB1026F5ED2A176466FBA4167CC567A2085ABD070C1545B180BDA86020E275EA9335F509C57786F4ED2378EFFF331869B856DDE1C05C461E4EECAF0E2FB97AF77B7BC2AD1B34C12992E45F5D1254BBF0C3FB224ABB3E8859594A83B6CA393ED81ECAC9221CE6BC71A727BCAD87DD80FC0834B87BADB5CB8FD3F08BEF90115A8DF1923D7CD9529729F27E1B8ABD83C4CF8818AE10257162E0057A658E265610B71F9BA4B365A20C70578FAC65B51B91100392171BA12A440A5E93C4AA62E0C9B6FC9B68F953514AAA7831B4B2C31C4\n" | timeout 3 nc localhost 32755` && exit 1
-test $? = 124
-test -z "$result" # Not even the ID should be sent when the first packet contains illegal data
+splice 17.7
+pid=$!
 
-# No splicing allowed
+require_nodes foo 1
+require_nodes bar 1
 
-$tinc $c2 stop
-$tinc $c2 del ExperimentalProtocol
-$tinc $c2 start $r2
+kill $pid
 
-./splice foo localhost 32754 bar localhost 32755 17.7 &
-sleep 3
-test `$tinc $c1 dump reachable nodes | wc -l` = 1
-test `$tinc $c2 dump reachable nodes | wc -l` = 1
-kill $!
+tinc bar stop
+tinc foo stop
 
-$tinc $c2 stop
-$tinc $c1 stop
+echo [STEP] Test splicing again with legacy protocol
 
-# Test splicing again with legacy protocol
+tinc foo set ExperimentalProtocol no
+tinc bar set ExperimentalProtocol no
 
-$tinc $c1 set ExperimentalProtocol no
-$tinc $c2 set ExperimentalProtocol no
+create_script foo subnet-up
+start_tinc foo
+wait_script foo subnet-up
 
-$tinc $c1 start $r1
-$tinc $c2 start $r2
+create_script bar subnet-up
+start_tinc bar
+wait_script bar subnet-up
 
-./splice foo localhost 32754 bar localhost 32755 17.0 &
-sleep 3
-test `$tinc $c1 dump reachable nodes | wc -l` = 1
-test `$tinc $c2 dump reachable nodes | wc -l` = 1
-kill $!
+splice 17.0
+pid=$!
 
-# Clean up
+require_nodes foo 1
+require_nodes bar 1
 
-$tinc $c2 stop
-$tinc $c1 stop
+kill $pid
index b6d081f..34bef1a 100755 (executable)
@@ -1,32 +1,45 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Skip this test if we did not compile sptps_test
+echo [STEP] Skip this test if we did not compile sptps_test
 
-test -e $sptps_test -a -e $sptps_keypair || exit 77
+test -e "$SPTPS_TEST" -a -e "$SPTPS_KEYPAIR" || exit $EXIT_SKIP_TEST
 
-# Generate keys
+port=30080
 
-mkdir -p $d1
+server_priv="$DIR_FOO/server.priv"
+client_priv="$DIR_FOO/client.priv"
+server_pub="$DIR_FOO/server.pub"
+client_pub="$DIR_FOO/client.pub"
 
-$sptps_keypair $d1/server.priv $d1/server.pub
-$sptps_keypair $d1/client.priv $d1/client.pub
+echo [STEP] Generate keys
 
-# Test transfer of a simple file.
+mkdir -p "$DIR_FOO"
+$SPTPS_KEYPAIR "$server_priv" "$server_pub"
+$SPTPS_KEYPAIR "$client_priv" "$client_pub"
 
-(sleep 1; $sptps_test -4 -q $d1/client.priv $d1/server.pub localhost 32750 <Makefile) &
-$sptps_test -4 $d1/server.priv $d1/client.pub 32750 >$d1/out1
-cmp $d1/out1 Makefile
+echo [STEP] Test transfer of a simple file
 
-$sptps_test -4 -q $d1/server.priv $d1/client.pub 32750 <Makefile &
-sleep 1
-$sptps_test -4 $d1/client.priv $d1/server.pub localhost 32750 > $d1/out2
-cmp $d1/out2 Makefile
+reference=sptps-basic.test
 
-# Datagram mode
+(
+  sleep 3
+  $SPTPS_TEST -4 -q "$client_priv" "$server_pub" localhost $port <"$reference"
+) &
 
-$sptps_test -4 -dq $d1/server.priv $d1/client.pub 32750 <Makefile &
-sleep 1
-sleep 1 | $sptps_test -4 -dq $d1/client.priv $d1/server.pub localhost 32750 >$d1/out3
-cmp $d1/out3 Makefile
+$SPTPS_TEST -4 "$server_priv" "$client_pub" $port >"$DIR_FOO/out1"
+diff -w "$DIR_FOO/out1" "$reference"
+
+$SPTPS_TEST -4 -q "$server_priv" "$client_pub" $port <"$reference" &
+sleep 3
+$SPTPS_TEST -4 "$client_priv" "$server_pub" localhost $port >"$DIR_FOO/out2"
+diff -w "$DIR_FOO/out2" "$reference"
+
+echo [STEP] Datagram mode
+
+$SPTPS_TEST -4 -dq "$server_priv" "$client_pub" $port <"$reference" &
+sleep 3
+sleep 3 | $SPTPS_TEST -4 -dq "$client_priv" "$server_pub" localhost $port >"$DIR_FOO/out3"
+diff -w "$DIR_FOO/out3" "$reference"
index 6a091cf..185aec0 100644 (file)
 #!/bin/sh
 
-# Paths to executables
+set -ex
+
+echo [STEP] Initialize test library
+
+# Paths to compiled executables
+
+# realpath on FreeBSD fails if the path does not exist.
+realdir() {
+  [ -e "$1" ] || mkdir -p "$1"
+  if type realpath >/dev/null; then
+    realpath "$1"
+  else
+    readlink -f "$1"
+  fi
+}
+
+tincd_path=$(realdir "../src/tincd@EXEEXT@")
+tinc_path=$(realdir "../src/tinc@EXEEXT@")
+
+SPTPS_TEST=$(realdir "../src/sptps_test@EXEEXT@")
+SPTPS_KEYPAIR=$(realdir "../src/sptps_keypair@EXEEXT@")
+
+# Exit status list
+EXIT_SKIP_TEST=77
 
-tincd=../src/tincd@EXEEXT@
-tinc=../src/tinc@EXEEXT@
-sptps_test=../src/sptps_test@EXEEXT@
-sptps_keypair=../src/sptps_keypair@EXEEXT@
+# The list of the environment variables that tinc injects into the scripts it calls.
+# shellcheck disable=SC2016
+TINC_SCRIPT_VARS='$NETNAME,$NAME,$DEVICE,$IFACE,$NODE,$REMOTEADDRESS,$REMOTEPORT,$SUBNET,$WEIGHT,$INVITATION_FILE,$INVITATION_URL,$DEBUG'
 
 # Test directories
 
-scriptname=`basename $0`
+# Reuse script name if it was passed in an env var (when imported from tinc scripts).
+if [ -z "$SCRIPTNAME" ]; then
+  SCRIPTNAME=$(basename "$0")
+fi
 
-n1=$scriptname.1
-n2=$scriptname.2
-n3=$scriptname.3
+# Network names for tincd daemons.
+net1=$SCRIPTNAME.1
+net2=$SCRIPTNAME.2
+net3=$SCRIPTNAME.3
 
-d1=$PWD/$n1
-d2=$PWD/$n2
-d3=$PWD/$n3
+# Configuration/pidfile directories for tincd daemons.
+DIR_FOO=$(realdir "$PWD/$net1")
+DIR_BAR=$(realdir "$PWD/$net2")
+DIR_BAZ=$(realdir "$PWD/$net3")
 
-# Default arguments for both tinc and tincd
+# Register helper functions
 
-c1="-n $n1 --config=$d1 --pidfile=$d1/pid"
-c2="-n $n2 --config=$d2 --pidfile=$d2/pid"
-c3="-n $n3 --config=$d3 --pidfile=$d3/pid"
+# Alias gtimeout to timeout if it exists.
+if type gtimeout >/dev/null; then
+  timeout() { gtimeout "$@"; }
+fi
 
-# Arguments when running tincd
+# Are the shell tools provided by busybox?
+is_busybox() {
+  timeout --help 2>&1 | grep -q -i busybox
+}
 
-r1="--logfile=$d1/log -d5"
-r2="--logfile=$d2/log -d5"
-r3="--logfile=$d3/log -d5"
+# busybox timeout returns 128 + signal number (which is TERM by default)
+if is_busybox; then
+  EXIT_TIMEOUT=$((128 + 15))
+else
+  EXIT_TIMEOUT=124
+fi
 
-# Check for leftover tinc daemons
+# Is this msys2?
+is_windows() {
+  test "$(uname -o)" = Msys
+}
 
-[ -f $d1/pid ] && $tinc $c1 stop
-[ -f $d2/pid ] && $tinc $c2 stop
-[ -f $d3/pid ] && $tinc $c3 stop
+# Are we running on a CI server?
+is_ci() {
+  test "$CI"
+}
 
-# Remove test directories
+# Dump error message and exit with an error.
+bail() {
+  echo >&2 "$@"
+  exit 1
+}
 
-rm -rf $d1 $d2 $d3
+# Remove carriage returns to normalize strings on Windows for easier comparisons.
+rm_cr() {
+  tr -d '\r'
+}
 
-# Exit on errors, log all commands being executed
+# Executes whatever is passed to it, checking that the resulting exit code is non-zero.
+must_fail() {
+  if "$@"; then
+    bail "expected a non-zero exit code"
+  fi
+}
 
-set -ex
+# Runs its arguments with timeout(1) or gtimeout(1) if either are installed.
+# Usage: try_limit_time 10 command --with --args
+if type timeout >/dev/null; then
+  if is_busybox; then
+    # busybox does not support --foreground
+    try_limit_time() {
+      time=$1
+      shift
+      timeout "$time" "$@"
+    }
+  else
+    # BSD and GNU timeout do not require special handling
+    try_limit_time() {
+      time=$1
+      shift
+      timeout --foreground "$time" "$@"
+    }
+  fi
+else
+  try_limit_time() {
+    echo >&2 "timeout was not found, running without time limits!"
+    shift
+    "$@"
+  }
+fi
+
+# wc -l on mac prints whitespace before the actual number.
+# This is simplest cross-platform alternative without that behavior.
+count_lines() {
+  awk 'END{ print NR }'
+}
+
+# Calls compiled tinc, passing any supplied arguments.
+# Usage: tinc { foo | bar | baz } --arg1 val1 "$args"
+tinc() {
+  peer=$1
+  shift
+
+  case "$peer" in
+  foo) try_limit_time 30 "$tinc_path" -n "$net1" --config="$DIR_FOO" --pidfile="$DIR_FOO/pid" "$@" ;;
+  bar) try_limit_time 30 "$tinc_path" -n "$net2" --config="$DIR_BAR" --pidfile="$DIR_BAR/pid" "$@" ;;
+  baz) try_limit_time 30 "$tinc_path" -n "$net3" --config="$DIR_BAZ" --pidfile="$DIR_BAZ/pid" "$@" ;;
+  *) bail "invalid command [[$peer $*]]" ;;
+  esac
+}
+
+# Calls compiled tincd, passing any supplied arguments.
+# Usage: tincd { foo | bar | baz } --arg1 val1 "$args"
+tincd() {
+  peer=$1
+  shift
+
+  case "$peer" in
+  foo) try_limit_time 30 "$tincd_path" -n "$net1" --config="$DIR_FOO" --pidfile="$DIR_FOO/pid" --logfile="$DIR_FOO/log" -d5 "$@" ;;
+  bar) try_limit_time 30 "$tincd_path" -n "$net2" --config="$DIR_BAR" --pidfile="$DIR_BAR/pid" --logfile="$DIR_BAR/log" -d5 "$@" ;;
+  baz) try_limit_time 30 "$tincd_path" -n "$net3" --config="$DIR_BAZ" --pidfile="$DIR_BAZ/pid" --logfile="$DIR_BAZ/log" -d5 "$@" ;;
+  *) bail "invalid command [[$peer $*]]" ;;
+  esac
+}
+
+# Start the specified tinc daemon.
+# usage: start_tinc { foo | bar | baz }
+start_tinc() {
+  peer=$1
+  shift
+
+  case "$peer" in
+  foo) tinc "$peer" start --logfile="$DIR_FOO/log" -d5 "$@" ;;
+  bar) tinc "$peer" start --logfile="$DIR_BAR/log" -d5 "$@" ;;
+  baz) tinc "$peer" start --logfile="$DIR_BAZ/log" -d5 "$@" ;;
+  *) bail "invalid peer $peer" ;;
+  esac
+}
+
+# Stop all tinc clients.
+stop_all_tincs() {
+  (
+    # In case these pid files are mangled.
+    set +e
+    [ -f "$DIR_FOO/pid" ] && tinc foo stop
+    [ -f "$DIR_BAR/pid" ] && tinc bar stop
+    [ -f "$DIR_BAZ/pid" ] && tinc baz stop
+    true
+  )
+}
+
+# Checks that the number of reachable nodes matches what is expected.
+# usage: require_nodes node_name expected_number
+require_nodes() {
+  echo >&2 "Check that we're able to reach tincd"
+  test "$(tinc "$1" pid | count_lines)" = 1
+
+  echo >&2 "Check the number of reachable nodes for $1 (expecting $2)"
+  actual="$(tinc "$1" dump reachable nodes | count_lines)"
+
+  if [ "$actual" != "$2" ]; then
+    echo >&2 "tinc $1: expected $2 reachable nodes, got $actual"
+    exit 1
+  fi
+}
+
+peer_directory() {
+  case "$peer" in
+  foo) echo "$DIR_FOO" ;;
+  bar) echo "$DIR_BAR" ;;
+  baz) echo "$DIR_BAZ" ;;
+  *) bail "invalid peer $peer" ;;
+  esac
+}
+
+# This is an append-only log of all scripts executed by all peers.
+script_runs_log() {
+  echo "$(peer_directory "$1")/script-runs.log"
+}
+
+# Create tincd script. If it fails, it kills the test script with SIGTERM.
+# usage: create_script { foo | bar | baz } { tinc-up | host-down | ... } 'script content'
+create_script() {
+  peer=$1
+  script=$2
+  shift 2
+
+  # This is the line that we should start from when reading the script execution log while waiting
+  # for $script from $peer. It is a poor man's hash map to avoid polluting tinc's home directory with
+  # "last seen" files. There seem to be no good solutions to this that are compatible with all shells.
+  line_var=$(next_line_var "$peer" "$script")
+
+  # We must reassign it here in case the script is recreated.
+  # shellcheck disable=SC2229
+  read -r "$line_var" <<EOF
+1
+EOF
+
+  # Full path to the script.
+  script_path=$(peer_directory "$peer")/$script
+
+  # Full path to the script execution log (one for each peer).
+  script_log=$(script_runs_log "$peer")
+  printf '' >"$script_log"
+
+  # Script output is redirected into /dev/null. Otherwise, it ends up
+  # in tinc's output and breaks things like 'tinc invite'.
+  cat >"$script_path" <<EOF
+#!/bin/sh
+(
+  cd "$PWD" || exit 1
+  SCRIPTNAME="$SCRIPTNAME" . ./testlib.sh
+  $@
+  echo "$script,\$$,$TINC_SCRIPT_VARS" >>"$script_log"
+) >/dev/null 2>&1 || kill -TERM $$
+EOF
+
+  chmod u+x "$script_path"
+
+  if is_windows; then
+    echo "@$MINGW_SHELL '$script_path'" >"$script_path.cmd"
+  fi
+}
+
+# Returns the name of the variable that contains the line number
+# we should read next when waiting on $script from $peer.
+# usage: next_line_var foo host-up
+next_line_var() {
+  peer=$1
+  script=$(echo "$2" | sed 's/[^a-zA-Z0-9]/_/g')
+  printf "%s" "next_line_${peer}_${script}"
+}
+
+# Waits for `peer`'s script `script` to finish `count` number of times.
+# usage: wait_script { foo | bar | baz } { tinc-up | host-up | ... } [count=1]
+wait_script() {
+  peer=$1
+  script=$2
+  count=$3
+
+  if [ -z "$count" ] || [ "$count" -lt 1 ]; then
+    count=1
+  fi
+
+  # Find out the location of the log and how many lines we should skip
+  # (because we've already seen them in previous invocations of wait_script
+  # for current $peer and $script).
+  line_var=$(next_line_var "$peer" "$script")
+
+  # eval is the only solution supported by POSIX shells.
+  # https://github.com/koalaman/shellcheck/wiki/SC3053
+  #   1. $line_var expands into 'next_line_foo_hosts_bar_up'
+  #   2. the name is substituted and the command becomes 'echo "$next_line_foo_hosts_bar_up"'
+  #   3. the command is evaluated and the line number is assigned to $line
+  line=$(eval "echo \"\$$line_var\"")
+
+  # This is the file that we monitor for script execution records.
+  script_log=$(script_runs_log "$peer")
+
+  # Starting from $line, read until $count matches are found.
+  # Print the number of the last matching line and exit.
+  # GNU tail 2.82 and newer terminates by itself when the pipe breaks.
+  # To support other tails we do an explicit `kill`.
+  # FIFO is useful here because otherwise it's difficult to determine
+  # which tail process should be killed. We could stick them in a process
+  # group by enabling job control, but this results in weird behavior when
+  # running tests in parallel on some interactive shells
+  # (e.g. when /bin/sh is symlinked to dash).
+  new_line=$(
+    try_limit_time 60 sh -c "
+      fifo=\$$.fifo
+      cleanup() { rm -f \$fifo; }
+      cleanup && trap cleanup EXIT
+
+      mkfifo \$$.fifo
+      tail -n '+$line' -f '$script_log' >\$fifo &
+      grep -n -m '$count' '^$script,'   <\$fifo
+      kill \$!
+    " | awk -F: 'END { print $1 }'
+  )
+
+  # Remember the next line number for future reference. We'll use it if
+  # wait_script is called again with same $peer and $script.
+  read -r "${line_var?}" <<EOF
+$((line + new_line))
+EOF
+}
+
+# Are we running tests in parallel?
+is_parallel() {
+  # Grep the make flags for any of: '-j', '-j5', '-j 42', but not 'n-j', '-junk'.
+  echo "$MAKEFLAGS" | grep -E -q '(^|[[:space:]])-j[[:digit:]]*([[:space:]]|$)'
+}
+
+# Cleanup after running each script.
+cleanup() {
+  (
+    set +ex
+
+    stop_all_tincs
+
+    # Ask nicely, then kill anything that's left.
+    if is_ci && ! is_parallel; then
+      kill_processes() {
+        signal=$1
+        shift
+        for process in "$@"; do
+          pkill -"SIG$signal" -x -u "$(id -u)" "$process"
+        done
+      }
+      echo >&2 "CI server detected, performing aggressive cleanup"
+      kill_processes TERM tinc tincd
+      kill_processes KILL tinc tincd
+    fi
+  ) || true
+}
+
+# Generate path to current shell which can be used from Windows applications.
+if is_windows; then
+  MINGW_SHELL=$(cygpath --mixed -- "$SHELL")
+fi
+
+# This was called from a tincd script. Skip executing commands with side effects.
+[ -n "$NAME" ] && return
+
+echo [STEP] Check for leftover tinc daemons and test directories
+
+# Cleanup leftovers from previous runs.
+stop_all_tincs
+
+# On Windows this can actually fail. We don't want to suppress possible failure with -f.
+if [ -d "$DIR_FOO" ]; then rm -r "$DIR_FOO"; fi
+if [ -d "$DIR_BAR" ]; then rm -r "$DIR_BAR"; fi
+if [ -d "$DIR_BAZ" ]; then rm -r "$DIR_BAZ"; fi
+
+# Register cleanup function so we don't have to call it everywhere
+# (and failed scripts do not leave stray tincd running).
+trap cleanup EXIT INT TERM
index d9494b4..d4c606f 100755 (executable)
@@ -1,86 +1,93 @@
 #!/bin/sh
 
+# shellcheck source=testlib.sh
 . "${0%/*}/testlib.sh"
 
-# Initialize one node
+echo [STEP] Initialize one node
 
-$tinc $c1 init foo
-test "`$tinc $c1 get Name | tr -d '\r'`" = "foo"
+tinc foo init foo
+test "$(tinc foo get Name)" = "foo"
 
-# Test case sensitivity
+echo [STEP] Test case sensitivity
 
-$tinc $c1 set Mode switch
-test "`$tinc $c1 get Mode | tr -d '\r'`" = "switch"
-test "`$tinc $c1 get mode | tr -d '\r'`" = "switch"
-$tinc $c1 set mode router
-test "`$tinc $c1 get Mode | tr -d '\r'`" = "router"
-test "`$tinc $c1 get mode | tr -d '\r'`" = "router"
-$tinc $c1 set Mode Switch
-test "`$tinc $c1 get Mode | tr -d '\r'`" = "Switch"
+tinc foo set Mode switch
+test "$(tinc foo get Mode)" = "switch"
+test "$(tinc foo get mode)" = "switch"
 
-# Test deletion
+tinc foo set mode router
+test "$(tinc foo get Mode)" = "router"
+test "$(tinc foo get mode)" = "router"
 
-$tinc $c1 del Mode hub && exit 1 || true
-$tinc $c1 del Mode switch
-test -z "`$tinc $c1 get Mode`"
+tinc foo set Mode Switch
+test "$(tinc foo get Mode)" = "Switch"
 
-# There can only be one Mode variable
+echo [STEP] Test deletion
 
-$tinc $c1 add Mode switch
-$tinc $c1 add Mode hub
-test "`$tinc $c1 get Mode | tr -d '\r'`" = "hub"
+must_fail tinc foo del Mode hub
+tinc foo del Mode switch
+test -z "$(tinc foo get Mode)"
 
-# Test addition/deletion of multivalued variables
+echo [STEP] There can only be one Mode variable
 
-$tinc $c1 add Subnet 1
-$tinc $c1 add Subnet 2
-$tinc $c1 add Subnet 2
-$tinc $c1 add Subnet 3
-test "`$tinc $c1 get Subnet | tr -d '\r'`" = "1
+tinc foo add Mode switch
+tinc foo add Mode hub
+test "$(tinc foo get Mode)" = "hub"
+
+echo [STEP] Test addition/deletion of multivalued variables
+
+tinc foo add Subnet 1
+tinc foo add Subnet 2
+tinc foo add Subnet 2
+tinc foo add Subnet 3
+test "$(tinc foo get Subnet | rm_cr)" = "1
 2
 3"
-$tinc $c1 del Subnet 2
-test "`$tinc $c1 get Subnet | tr -d '\r'`" = "1
+
+tinc foo del Subnet 2
+test "$(tinc foo get Subnet | rm_cr)" = "1
 3"
-$tinc $c1 del Subnet
-test -z "`$tinc $c1 get Subnet`"
 
-# We should not be able to get/set server variables using node.variable syntax
+tinc foo del Subnet
+test -z "$(tinc foo get Subnet)"
 
-test -z "`$tinc $c1 get foo.Name`"
-$tinc $c1 set foo.Name bar && exit 1 || true
+echo [STEP] We should not be able to get/set server variables using node.variable syntax
 
-# Test getting/setting host variables for other nodes
+test -z "$(tinc foo get foo.Name)"
+must_fail tinc foo set foo.Name bar
 
-touch $d1/hosts/bar
+echo [STEP] Test getting/setting host variables for other nodes
 
-$tinc $c1 add bar.PMTU 1
-$tinc $c1 add bar.PMTU 2
-test "`$tinc $c1 get bar.PMTU | tr -d '\r'`" = "2"
+touch "$DIR_FOO/hosts/bar"
 
-$tinc $c1 add bar.Subnet 1
-$tinc $c1 add bar.Subnet 2
-$tinc $c1 add bar.Subnet 2
-$tinc $c1 add bar.Subnet 3
-test "`$tinc $c1 get bar.Subnet | tr -d '\r'`" = "1
+tinc foo add bar.PMTU 1
+tinc foo add bar.PMTU 2
+test "$(tinc foo get bar.PMTU)" = "2"
+
+tinc foo add bar.Subnet 1
+tinc foo add bar.Subnet 2
+tinc foo add bar.Subnet 2
+tinc foo add bar.Subnet 3
+test "$(tinc foo get bar.Subnet | rm_cr)" = "1
 2
 3"
-$tinc $c1 del bar.Subnet 2
-test "`$tinc $c1 get bar.Subnet | tr -d '\r'`" = "1
+
+tinc foo del bar.Subnet 2
+test "$(tinc foo get bar.Subnet | rm_cr)" = "1
 3"
-$tinc $c1 del bar.Subnet
-test -z "`$tinc $c1 get bar.Subnet`"
 
-# We should not be able to get/set for nodes with invalid names
+tinc foo del bar.Subnet
+test -z "$(tinc foo get bar.Subnet)"
+
+echo [STEP] We should not be able to get/set for nodes with invalid names
 
-touch $d1/hosts/qu-ux
+touch "$DIR_FOO/hosts/qu-ux"
+must_fail tinc foo set qu-ux.Subnet 1
 
-$tinc $c1 set qu-ux.Subnet 1 && exit 1 || true
+echo [STEP] We should not be able to set obsolete variables unless forced
 
-# We should not be able to set obsolete variables unless forced
+must_fail tinc foo set PrivateKey 12345
+tinc foo --force set PrivateKey 12345
+test "$(tinc foo get PrivateKey)" = "12345"
 
-$tinc $c1 set PrivateKey 12345 && exit 1 || true
-$tinc $c1 --force set PrivateKey 12345
-test "`$tinc $c1 get PrivateKey | tr -d '\r'`" = "12345"
-$tinc $c1 del PrivateKey
-test -z "`$tinc $c1 get PrivateKey`"
+tinc foo del PrivateKey
+test -z "$(tinc foo get PrivateKey)"