aboutgitcodebugslistschat
diff options
context:
space:
mode:
-rw-r--r--packet.c54
-rw-r--r--packet.h2
-rw-r--r--tap.c4
-rw-r--r--test/.gitignore2
-rw-r--r--test/Makefile24
-rw-r--r--test/build/all61
-rwxr-xr-xtest/build/build.py109
-rw-r--r--test/build/clang_tidy17
-rw-r--r--test/build/cppcheck17
-rwxr-xr-xtest/build/static_checkers.sh26
-rw-r--r--test/lib/exeter58
-rw-r--r--test/pasta_options/log_to_file10
-rwxr-xr-xtest/run18
-rwxr-xr-xtest/smoke/smoke.sh33
14 files changed, 306 insertions, 129 deletions
diff --git a/packet.c b/packet.c
index 27693c5..890561b 100644
--- a/packet.c
+++ b/packet.c
@@ -91,14 +91,17 @@ static int packet_check_range(const struct pool *p, const char *ptr, size_t len,
return 0;
}
/**
- * pool_full() - Is a packet pool full?
+ * pool_can_fit() - Can a new packet fit in the pool?
* @p: Pointer to packet pool
+ * @data: check data can fit in the pool
*
- * Return: true if the pool is full, false if more packets can be added
+ * Return: true if @data can be added, false otherwise
*/
-bool pool_full(const struct pool *p)
+bool pool_can_fit(const struct pool *p, struct iov_tail *data)
{
- return p->count >= p->size;
+ iov_tail_prune(data);
+
+ return p->count + data->cnt + (data->cnt > 1) <= p->size;
}
/**
@@ -111,11 +114,9 @@ bool pool_full(const struct pool *p)
void packet_add_do(struct pool *p, struct iov_tail *data,
const char *func, int line)
{
- size_t idx = p->count;
- const char *start;
- size_t len;
+ size_t idx = p->count, i, offset;
- if (pool_full(p)) {
+ if (!pool_can_fit(p, data)) {
debug("add packet index %zu to pool with size %zu, %s:%i",
idx, p->size, func, line);
return;
@@ -124,18 +125,30 @@ void packet_add_do(struct pool *p, struct iov_tail *data,
if (!iov_tail_prune(data))
return;
- ASSERT(data->cnt == 1); /* we don't support iovec */
+ if (data->cnt > 1) {
+ p->pkt[idx].iov_base = NULL;
+ p->pkt[idx].iov_len = data->cnt;
+ idx++;
+ }
- len = data->iov[0].iov_len - data->off;
- start = (char *)data->iov[0].iov_base + data->off;
+ offset = data->off;
+ for (i = 0; i < data->cnt; i++) {
+ const char *start;
+ size_t len;
- if (packet_check_range(p, start, len, func, line))
- return;
+ len = data->iov[i].iov_len - offset;
+ start = (char *)data->iov[i].iov_base + offset;
+ offset = 0;
- p->pkt[idx].iov_base = (void *)start;
- p->pkt[idx].iov_len = len;
+ if (packet_check_range(p, start, len, func, line))
+ return;
- p->count++;
+ p->pkt[idx].iov_base = (void *)start;
+ p->pkt[idx].iov_len = len;
+ idx++;
+ }
+
+ p->count = idx;
}
/**
@@ -165,9 +178,14 @@ bool packet_get_do(const struct pool *p, size_t idx,
return false;
}
- data->cnt = 1;
+ if (p->pkt[idx].iov_base) {
+ data->cnt = 1;
+ data->iov = &p->pkt[idx];
+ } else {
+ data->cnt = p->pkt[idx].iov_len;
+ data->iov = &p->pkt[idx + 1];
+ }
data->off = 0;
- data->iov = &p->pkt[idx];
for (i = 0; i < data->cnt; i++) {
ASSERT_WITH_MSG(!packet_check_range(p, data->iov[i].iov_base,
diff --git a/packet.h b/packet.h
index e51cbd1..ba8d5c2 100644
--- a/packet.h
+++ b/packet.h
@@ -37,7 +37,7 @@ void packet_add_do(struct pool *p, struct iov_tail *data,
const char *func, int line);
bool packet_get_do(const struct pool *p, const size_t idx,
struct iov_tail *data, const char *func, int line);
-bool pool_full(const struct pool *p);
+bool pool_can_fit(const struct pool *p, struct iov_tail *data);
void pool_flush(struct pool *p);
#define packet_add(p, data) \
diff --git a/tap.c b/tap.c
index 5e0173b..7ba6399 100644
--- a/tap.c
+++ b/tap.c
@@ -1103,14 +1103,14 @@ void tap_add_packet(struct ctx *c, struct iov_tail *data,
switch (ntohs(eh->h_proto)) {
case ETH_P_ARP:
case ETH_P_IP:
- if (pool_full(pool_tap4)) {
+ if (!pool_can_fit(pool_tap4, data)) {
tap4_handler(c, pool_tap4, now);
pool_flush(pool_tap4);
}
packet_add(pool_tap4, data);
break;
case ETH_P_IPV6:
- if (pool_full(pool_tap6)) {
+ if (!pool_can_fit(pool_tap6, data)) {
tap6_handler(c, pool_tap6, now);
pool_flush(pool_tap6);
}
diff --git a/test/.gitignore b/test/.gitignore
index 3573444..9412f0d 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -11,3 +11,5 @@ nstool
rampstream
guest-key
guest-key.pub
+/exeter/
+*.bats
diff --git a/test/Makefile b/test/Makefile
index bf63db8..a774285 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -5,6 +5,8 @@
# Copyright Red Hat
# Author: David Gibson <david@gibson.dropbear.id.au>
+BATS = bats -j $(shell nproc)
+EXETOOL = exeter/exetool/exetool
WGET = wget -c
DEBIAN_IMGS = debian-8.11.0-openstack-amd64.qcow2 \
@@ -50,18 +52,24 @@ UBUNTU_NEW_IMGS = xenial-server-cloudimg-powerpc-disk1.img \
jammy-server-cloudimg-s390x.img
UBUNTU_IMGS = $(UBUNTU_OLD_IMGS) $(UBUNTU_NEW_IMGS)
-DOWNLOAD_ASSETS = mbuto podman \
+DOWNLOAD_ASSETS = exeter mbuto podman \
$(DEBIAN_IMGS) $(FEDORA_IMGS) $(OPENSUSE_IMGS) $(UBUNTU_IMGS)
TESTDATA_ASSETS = small.bin big.bin medium.bin \
rampstream
LOCAL_ASSETS = mbuto.img mbuto.mem.img podman/bin/podman QEMU_EFI.fd \
$(DEBIAN_IMGS:%=prepared-%) $(FEDORA_IMGS:%=prepared-%) \
$(UBUNTU_NEW_IMGS:%=prepared-%) \
- nstool guest-key guest-key.pub \
+ nstool guest-key guest-key.pub $(EXETOOL) \
$(TESTDATA_ASSETS)
ASSETS = $(DOWNLOAD_ASSETS) $(LOCAL_ASSETS)
+EXETER_PYPATH = exeter/py3
+EXETER_BATS = smoke/smoke.sh.bats \
+ build/build.py.bats build/static_checkers.sh.bats
+BATS_FILES = $(EXETER_BATS) \
+ podman/test/system/505-networking-pasta.bats
+
CFLAGS = -Wall -Werror -Wextra -pedantic -std=c99
assets: $(ASSETS)
@@ -70,6 +78,11 @@ assets: $(ASSETS)
pull-%: %
git -C $* pull
+exeter:
+ git clone https://gitlab.com/dgibson/exeter.git
+
+exeter/exetool/exetool: pull-exeter
+
mbuto:
git clone git://mbuto.sh/mbuto
@@ -115,6 +128,12 @@ medium.bin:
big.bin:
dd if=/dev/urandom bs=1M count=10 of=$@
+$(EXETER_BATS): %.bats: % $(EXETOOL)
+ PYTHONPATH=$(EXETER_PYPATH) $(EXETOOL) bats -- $< > $@
+
+bats: $(BATS_FILES) pull-podman
+ PYTHONPATH=$(EXETER_PYPATH) CONTAINERS_HELPER_BINARY_DIR=.. $(BATS) $(BATS_FILES)
+
check: assets
./run
@@ -124,6 +143,7 @@ debug: assets
clean:
rm -f perf.js *~
rm -f $(LOCAL_ASSETS)
+ rm -f $(EXETER_BATS)
rm -rf test_logs
rm -f prepared-*.qcow2 prepared-*.img
diff --git a/test/build/all b/test/build/all
deleted file mode 100644
index 1f79e0d..0000000
--- a/test/build/all
+++ /dev/null
@@ -1,61 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# PASST - Plug A Simple Socket Transport
-# for qemu/UNIX domain socket mode
-#
-# PASTA - Pack A Subtle Tap Abstraction
-# for network namespace/tap device mode
-#
-# test/build/all - Build targets, one by one, then all together, check output
-#
-# Copyright (c) 2021 Red Hat GmbH
-# Author: Stefano Brivio <sbrivio@redhat.com>
-
-htools make cc rm uname getconf mkdir cp rm man
-
-test Build passt
-host make clean
-check ! [ -e passt ]
-host CFLAGS="-Werror" make passt
-check [ -f passt ]
-
-test Build pasta
-host make clean
-check ! [ -e pasta ]
-host CFLAGS="-Werror" make pasta
-check [ -h pasta ]
-
-test Build qrap
-host make clean
-check ! [ -e qrap ]
-host CFLAGS="-Werror" make qrap
-check [ -f qrap ]
-
-test Build all
-host make clean
-check ! [ -e passt ]
-check ! [ -e pasta ]
-check ! [ -e qrap ]
-host CFLAGS="-Werror" make
-check [ -f passt ]
-check [ -h pasta ]
-check [ -f qrap ]
-
-test Install
-host mkdir __STATEDIR__/prefix
-host prefix=__STATEDIR__/prefix make install
-check [ -f __STATEDIR__/prefix/bin/passt ]
-check [ -h __STATEDIR__/prefix/bin/pasta ]
-check [ -f __STATEDIR__/prefix/bin/qrap ]
-check man -M __STATEDIR__/prefix/share/man -W passt
-check man -M __STATEDIR__/prefix/share/man -W pasta
-check man -M __STATEDIR__/prefix/share/man -W qrap
-
-test Uninstall
-host prefix=__STATEDIR__/prefix make uninstall
-check ! [ -f __STATEDIR__/prefix/bin/passt ]
-check ! [ -h __STATEDIR__/prefix/bin/pasta ]
-check ! [ -f __STATEDIR__/prefix/bin/qrap ]
-check ! man -M __STATEDIR__/prefix/share/man -W passt 2>/dev/null
-check ! man -M __STATEDIR__/prefix/share/man -W pasta 2>/dev/null
-check ! man -M __STATEDIR__/prefix/share/man -W qrap 2>/dev/null
diff --git a/test/build/build.py b/test/build/build.py
new file mode 100755
index 0000000..e49287c
--- /dev/null
+++ b/test/build/build.py
@@ -0,0 +1,109 @@
+#! /usr/bin/env python3
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/build/build.py - Test build and install targets
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+import contextlib
+import os
+from pathlib import Path
+import subprocess
+import tempfile
+from typing import Iterable, Iterator
+
+import exeter
+
+def sh(cmd):
+ """Run given command in a shell"""
+ subprocess.run(cmd, shell=True)
+
+
+@contextlib.contextmanager
+def clone_sources() -> Iterator[str]:
+ """Create a temporary copy of the passt sources.
+
+ When the context enters create a temporary directory and copy the
+ passt sources into it. Clean it up when the context exits.
+ """
+
+ os.chdir('..') # Move from test/ to repo base
+ with tempfile.TemporaryDirectory(ignore_cleanup_errors=False) as tmpdir:
+ sh(f"cp --parents -d $(git ls-files) {tmpdir}")
+ os.chdir(tmpdir)
+ yield tmpdir
+
+
+def test_make(target: str, expected_files: list[str]) -> None:
+ """Test `make {target}`
+
+ Arguments:
+ target -- make target to invoke
+ expected_files -- files make is expected to create
+
+ Verifies that
+ 1) `make target` completes successfully
+ 2) expected_files care created by `make target`
+ 3) expected_files are removed by `make clean`
+ """
+
+ ex_paths = [Path(f) for f in expected_files]
+ with clone_sources():
+ for p in ex_paths:
+ assert not p.exists(), f"{p} existed before make"
+ sh(f'make {target} CFLAGS="-Werror"')
+ for p in ex_paths:
+ assert p.exists(), f"{p} wasn't made"
+ sh('make clean')
+ for p in ex_paths:
+ assert not p.exists(), f"{p} existed after make clean"
+
+
+exeter.register('make_passt', test_make, 'passt', ['passt'])
+exeter.register('make_pasta', test_make, 'pasta', ['pasta'])
+exeter.register('make_qrap', test_make, 'qrap', ['qrap'])
+exeter.register('make_all', test_make, 'all', ['passt', 'pasta', 'qrap'])
+
+
+@exeter.test
+def test_install_uninstall() -> None:
+ """Test `make install` and `make uninstall`
+
+ Tests that `make install` installs the expected files to the
+ install prefix, and that `make uninstall` removes them again.
+ """
+
+ with clone_sources():
+ with tempfile.TemporaryDirectory(ignore_cleanup_errors=False) \
+ as prefix:
+ bindir = Path(prefix) / 'bin'
+ mandir = Path(prefix) / 'share/man'
+ progs = ['passt', 'pasta', 'qrap']
+
+ # Install
+ sh(f'make install CFLAGS="-Werror" prefix={prefix}')
+
+ for prog in progs:
+ exe = bindir / prog
+ assert exe.is_file(), f"{exe} does not exist as a regular file"
+ sh(f'man -M {mandir} -W {prog}')
+
+ # Uninstall
+ sh(f'make uninstall prefix={prefix}')
+
+ for prog in progs:
+ exe = bindir / prog
+ assert not exe.exists(), f"{exe} exists after uninstall"
+ sh(f'! man -M {mandir} -W {prog}')
+
+
+if __name__ == '__main__':
+ exeter.main()
diff --git a/test/build/clang_tidy b/test/build/clang_tidy
deleted file mode 100644
index 40573bf..0000000
--- a/test/build/clang_tidy
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# PASST - Plug A Simple Socket Transport
-# for qemu/UNIX domain socket mode
-#
-# PASTA - Pack A Subtle Tap Abstraction
-# for network namespace/tap device mode
-#
-# test/build/clang_tidy - Run source through clang-tidy(1) linter
-#
-# Copyright (c) 2021 Red Hat GmbH
-# Author: Stefano Brivio <sbrivio@redhat.com>
-
-htools clang-tidy
-
-test Run clang-tidy
-host make clang-tidy
diff --git a/test/build/cppcheck b/test/build/cppcheck
deleted file mode 100644
index 0e1dbce..0000000
--- a/test/build/cppcheck
+++ /dev/null
@@ -1,17 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-or-later
-#
-# PASST - Plug A Simple Socket Transport
-# for qemu/UNIX domain socket mode
-#
-# PASTA - Pack A Subtle Tap Abstraction
-# for network namespace/tap device mode
-#
-# test/build/cppcheck - Run source through cppcheck(1) linter
-#
-# Copyright (c) 2021 Red Hat GmbH
-# Author: Stefano Brivio <sbrivio@redhat.com>
-
-htools cppcheck
-
-test Run cppcheck
-host make cppcheck
diff --git a/test/build/static_checkers.sh b/test/build/static_checkers.sh
new file mode 100755
index 0000000..42806e7
--- /dev/null
+++ b/test/build/static_checkers.sh
@@ -0,0 +1,26 @@
+#! /bin/sh
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/build/static_checkers.sh - Run static checkers
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+. $(dirname $0)/../exeter/sh/exeter.sh
+
+exeter_register cppcheck make -C .. cppcheck
+exeter_set_description cppcheck "passt sources pass cppcheck"
+
+exeter_register clang_tidy make -C .. clang-tidy
+exeter_set_description clang_tidy "passt sources pass clang-tidy"
+
+exeter_main "$@"
+
+
diff --git a/test/lib/exeter b/test/lib/exeter
new file mode 100644
index 0000000..3b19bea
--- /dev/null
+++ b/test/lib/exeter
@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/lib/exeter - Run exeter tests within the rest of passt's tests
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+EXETOOL="$BASEPATH/exeter/exetool/exetool"
+
+# is_exeter() - Determine if a test file is an exeter program
+# $@: Command line to invoke test program
+is_exeter() {
+ $EXETOOL probe -- "$@"
+}
+
+# exeter() - Run each test in an exeter program, logging each test separately
+# $@: Command line to invoke exeter test program
+exeter() {
+ STATESETUP="${STATEBASE}/$1"
+ mkdir -p "${STATESETUP}"
+
+ context_setup_host host
+ layout_host
+
+ cd test
+
+ __ntests=$($EXETOOL list -- "$@" | wc -l)
+ if [ $? != 0 ]; then
+ info "Failed to get exeter manifest for $@"
+ pause_continue \
+ "Press any key to pause test session" \
+ "Resuming in " \
+ "Paused, press any key to continue" \
+ 5
+ return
+ fi
+
+ status_file_start "$* (exeter)" ${__ntests}
+ [ ${CI} -eq 1 ] && video_link "${1}"
+
+ for __testid in $($EXETOOL list -- "$@"); do
+ __desc="$($EXETOOL desc -- "$@" -- ${__testid})"
+ status_test_start "${__desc}"
+ context_run host "$@" "${__testid}" && status_test_ok || status_test_fail
+ done
+
+ cd ..
+
+ teardown_context_watch ${PANE_HOST} host
+}
diff --git a/test/pasta_options/log_to_file b/test/pasta_options/log_to_file
index 3ead06c..db78b04 100644
--- a/test/pasta_options/log_to_file
+++ b/test/pasta_options/log_to_file
@@ -30,19 +30,19 @@ endef
test Log creation
-set PORTS -t 10001,10002 -u 10001,10002
+set PORTS -t 10001,10002 -u 10001,10002 -T none -U none
set LOG_FILE __STATEDIR__/pasta.log
-passt ./pasta -l __LOG_FILE__ -- /bin/true
+passt ./pasta __PORTS__ -l __LOG_FILE__ -- /bin/true
check [ -s __LOG_FILE__ ]
test Log truncated on creation
-passt ./pasta -l __LOG_FILE__ -- /bin/true & wait
+passt ./pasta __PORTS__ -l __LOG_FILE__ -- /bin/true & wait
pout PID2 echo $!
check head -1 __LOG_FILE__ | grep '^pasta .* [(]__PID2__[)]$'
test Maximum log size
-passtb ./pasta --config-net -d -f -l __LOG_FILE__ --log-size $((100 * 1024)) -- sh -c 'while true; do tcp_crr --nolog -l1 -P 10001 -C 10002 -6; done'
+passtb ./pasta __PORTS__ --config-net -d -f -l __LOG_FILE__ --log-size $((100 * 1024)) -- sh -c 'while true; do tcp_crr --nolog -l1 -P 10001 -C 10002 -6; done'
sleep 1
flood_log_client
@@ -67,7 +67,7 @@ passt unshare -rUm
passt mkdir __STATEDIR__/t
passt mount -t tmpfs none __STATEDIR__/t
set LOG_FILE __STATEDIR__/t/log
-passt ./pasta --config-net -d -l __LOG_FILE__ --log-size $((100 * 1024))
+passt ./pasta __PORTS__ --config-net -d -l __LOG_FILE__ --log-size $((100 * 1024))
flood_log_server
flood_log_client
diff --git a/test/run b/test/run
index f73c311..f858e55 100755
--- a/test/run
+++ b/test/run
@@ -43,6 +43,9 @@ KERNEL=${KERNEL:-"/boot/vmlinuz-$(uname -r)"}
COMMIT="$(git log --oneline --no-decorate -1)"
+# Let exeter tests written in Python find their modules
+export PYTHONPATH=${BASEPATH}/exeter/py3
+
. lib/util
. lib/context
. lib/setup
@@ -53,6 +56,7 @@ COMMIT="$(git log --oneline --no-decorate -1)"
. lib/layout_ugly
. lib/test
. lib/video
+. lib/exeter
# cleanup() - Remove temporary files
cleanup() {
@@ -67,11 +71,9 @@ run() {
perf_init
[ ${CI} -eq 1 ] && video_start ci
- setup build
- test build/all
- test build/cppcheck
- test build/clang_tidy
- teardown build
+ exeter smoke/smoke.sh
+ exeter build/build.py
+ exeter build/static_checkers.sh
setup pasta
test pasta/ndp
@@ -223,6 +225,10 @@ run_selected() {
__setup=
for __test; do
+ if is_exeter "test/${__test}"; then
+ exeter "${__test}"
+ continue
+ fi
# HACK: the migrate tests need the setup repeated for
# each test
if [ "${__test%%/*}" != "${__setup}" -o \
@@ -234,7 +240,7 @@ run_selected() {
test "${__test}"
done
- teardown "${__setup}"
+ [ -n "${__setup}" ] && teardown "${__setup}"
log "PASS: ${STATUS_PASS}, FAIL: ${STATUS_FAIL}, SKIPPED: ${STATUS_SKIPPED}"
diff --git a/test/smoke/smoke.sh b/test/smoke/smoke.sh
new file mode 100755
index 0000000..a642fb9
--- /dev/null
+++ b/test/smoke/smoke.sh
@@ -0,0 +1,33 @@
+#! /bin/sh
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# PASST - Plug A Simple Socket Transport
+# for qemu/UNIX domain socket mode
+#
+# PASTA - Pack A Subtle Tap Abstraction
+# for network namespace/tap device mode
+#
+# test/smoke/smoke.sh - Basic smoke tests
+#
+# Copyright Red Hat
+# Author: David Gibson <david@gibson.dropbear.id.au>
+
+. $(dirname $0)/../exeter/sh/exeter.sh
+
+PASST=$(dirname $0)/../../passt
+PASTA=$(dirname $0)/../../pasta
+
+exeter_register passt_version $PASST --version
+exeter_set_description passt_version "Check passt --version works"
+
+exeter_register pasta_version $PASTA --version
+exeter_set_description pasta_version "Check pasta --version works"
+
+exeter_register passt_help $PASST --help
+exeter_set_description passt_help "Check passt --help works"
+
+exeter_register pasta_help $PASTA --help
+exeter_set_description pasta_help "Check pasta --help works"
+
+exeter_main "$@"