summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorLuca Muscariello <lumuscar@cisco.com>2022-03-30 22:29:28 +0200
committerMauro Sardara <msardara@cisco.com>2022-03-31 19:51:47 +0200
commitc46e5df56b67bb8ea7a068d39324c640084ead2b (patch)
treeeddeb17785938e09bc42eec98ee09b8a28846de6 /tests
parent18fa668f25d3cc5463417ce7df6637e31578e898 (diff)
feat: boostrap hicn 22.02
The current patch provides several new features, improvements, bug fixes and also complete rewrite of entire components. - lib The hicn packet parser has been improved with a new packet format fully based on UDP. The TCP header is still temporarily supported but the UDP header will replace completely the new hicn packet format. Improvements have been made to make sure every packet parsing operation is made via this library. The current new header can be used as header between the payload and the UDP header or as trailer in the UDP surplus area to be tested when UDP options will start to be used. - hicn-light The portable packet forwarder has been completely rewritten from scratch with the twofold objective to improve performance and code size but also to drop dependencies such as libparc which is now removed by the current implementation. - hicn control the control library is the agent that is used to program the packet forwarders via their binary API. This component has benefited from significant improvements in terms of interaction model which is now event driven and more robust to failures. - VPP plugin has been updated to support VPP 22.02 - transport Major improvement have been made to the RTC protocol, to the support of IO modules and to the security sub system. Signed manifests are the default data authenticity and integrity framework. Confidentiality can be enabled by sharing the encryption key to the prod/cons layer. The library has been tested with group key based applications such as broadcast/multicast and real-time on-line meetings with trusted server keys or MLS. - testing Unit testing has been introduced using GoogleTest. One third of the code base is covered by unit testing with priority on critical features. Functional testing has also been introduce using Docker, linux bridging and Robot Framework to define test with Less Code techniques to facilitate the extension of the coverage. Co-authored-by: Mauro Sardara <msardara@cisco.com> Co-authored-by: Jordan Augé <jordan.auge+fdio@cisco.com> Co-authored-by: Michele Papalini <micpapal@cisco.com> Co-authored-by: Angelo Mantellini <manangel@cisco.com> Co-authored-by: Jacques Samain <jsamain@cisco.com> Co-authored-by: Olivier Roques <oroques+fdio@cisco.com> Co-authored-by: Enrico Loparco <eloparco@cisco.com> Co-authored-by: Giulio Grassi <gigrassi@cisco.com> Change-Id: I75d0ef70f86d921e3ef503c99271216ff583c215 Signed-off-by: Luca Muscariello <muscariello@ieee.org> Signed-off-by: Mauro Sardara <msardara@cisco.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/.env13
-rw-r--r--tests/2-nodes-hicn-light.yml65
-rw-r--r--tests/2-nodes-vpp-bridge.yml107
-rw-r--r--tests/2-nodes-vpp-memif-replication.yml143
-rw-r--r--tests/2-nodes-vpp-memif.yml116
-rw-r--r--tests/2-nodes.yml43
-rw-r--r--tests/Dockerfile.ci5
-rwxr-xr-xtests/channel.sh22
-rwxr-xr-xtests/config.sh290
-rw-r--r--tests/forwarder.robot45
-rw-r--r--tests/functional-tests/2-nodes-hicn-light.robot24
-rw-r--r--tests/functional-tests/2-nodes-vpp-bridge.robot24
-rw-r--r--tests/functional-tests/2-nodes-vpp-memif-replication.robot23
-rw-r--r--tests/functional-tests/2-nodes-vpp-memif.robot23
-rw-r--r--tests/resources/libraries/robot/common.robot23
-rw-r--r--tests/resources/libraries/robot/runtest.robot92
-rw-r--r--tests/run-functional.sh40
-rw-r--r--tests/test_forwarder.sh437
18 files changed, 1535 insertions, 0 deletions
diff --git a/tests/.env b/tests/.env
new file mode 100644
index 000000000..35c861bda
--- /dev/null
+++ b/tests/.env
@@ -0,0 +1,13 @@
+# Topologies
+TOPOLOGY_2_NODES=2-nodes
+
+# Container names
+TEST_LIGHT=hicn-light
+TEST_VPP_BRIDGE=vpp-bridge
+TEST_VPP_MEMIF=vpp-memif
+TEST_VPP_MEMIF_REPLICATION=vpp-memif-replication
+
+# names
+RTC_PRODUCER=b002::1
+RAAQM_PRODUCER=b002::2
+PING_PRODUCER=b002::3
diff --git a/tests/2-nodes-hicn-light.yml b/tests/2-nodes-hicn-light.yml
new file mode 100644
index 000000000..e62a6c705
--- /dev/null
+++ b/tests/2-nodes-hicn-light.yml
@@ -0,0 +1,65 @@
+version: "3"
+services:
+ client:
+ container_name: ${TEST_LIGHT}-client
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo ip link add br0 type bridge
+ sudo ip addr add 192.168.1.1/24 dev br0
+ sudo ip link set eth0 master br0
+ sudo ip link set eth0 up
+ sudo ip link set br0 up
+ sudo ip route add 192.168.1.2 via 192.168.1.1 dev br0
+ tee -a /tmp/hicn-light.conf <<EOF
+ add listener udp local0 192.168.1.1 9199 br0
+ add connection udp conn0 192.168.1.2 9199 192.168.1.1 9199
+ add route conn0 b002::/64 1
+ EOF
+
+ rm -f /tmp/lite_client.log
+
+ sudo hicn-light-daemon \
+ --daemon \
+ --log-file /tmp/lite_client.log \
+ --config /tmp/hicn-light.conf --capacity 0
+
+ tail -f /dev/null
+
+ server:
+ container_name: ${TEST_LIGHT}-server
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo ip link add br0 type bridge
+ sudo ip addr add 192.168.1.2/24 dev br0
+ sudo ip link set eth0 master br0
+ sudo ip link set eth0 up
+ sudo ip link set br0 up
+
+ tee -a /tmp/hicn-light.conf <<EOF
+ add listener udp local0 192.168.1.2 9199 br0
+ add connection udp conn0 192.168.1.1 9199 192.168.1.2 9199
+ add route conn0 b002::/64 1
+ EOF
+
+ rm -f /tmp/lite_server.log
+
+ sudo hicn-light-daemon \
+ --daemon \
+ --log-file /tmp/lite_server.log \
+ --config /tmp/hicn-light.conf --capacity 0
+
+ sleep 4
+
+ hiperf -z hicnlightng_module -D -S -R -B 4000kbps ${RTC_PRODUCER}/128
+ hiperf -z hicnlightng_module -D -S ${RAAQM_PRODUCER}/128
+ hicn-ping-server -z hicnlightng_module -d -s 0 -n ${PING_PRODUCER}/128
+
+ tail -f /dev/null
diff --git a/tests/2-nodes-vpp-bridge.yml b/tests/2-nodes-vpp-bridge.yml
new file mode 100644
index 000000000..d9426844f
--- /dev/null
+++ b/tests/2-nodes-vpp-bridge.yml
@@ -0,0 +1,107 @@
+version: "3"
+services:
+ client:
+ container_name: ${TEST_VPP_BRIDGE}-client
+ volumes:
+ - ..:/workspace
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo ip link add br0 type bridge
+ sudo ip link set eth0 master br0
+ sudo ip link set eth0 up
+ sudo ip link set br0 up
+ sudo mkdir -p /var/log/vpp
+ sudo tee /etc/vpp/startup.conf <<EOF
+ cpu { main-core 1 }
+ plugins {
+ path /usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins
+ plugin default { disable }
+ plugin acl_plugin.so { enable }
+ plugin nat_plugin.so { enable }
+ plugin dhcp_plugin.so { enable }
+ plugin dpdk_plugin.so { enable }
+ plugin dns_plugin.so { enable }
+ plugin ping_plugin.so { enable }
+ plugin memif_plugin.so { enable }
+ plugin nsim_plugin.so { enable }
+ plugin hicn_plugin.so { enable }
+ }
+
+ unix {
+ startup-config /etc/vpp/client-up.txt
+ cli-listen /run/vpp/cli.sock
+ log /var/log/vpp/vpp.log
+ }
+ EOF
+
+ sudo tee /etc/vpp/client-up.txt <<EOF
+ create tap id 0 host-bridge br0
+ set int state tap0 up
+ set int ip addr tap0 192.168.1.1/24
+ set int ip addr tap0 2001::1/64
+ ip route add b002::1/64 via 2001::2 tap0
+ EOF
+
+ sudo vpp -c /etc/vpp/startup.conf
+ sleep 5
+ sudo vppctl hicn enable b002::1/64
+
+ tail -f /dev/null
+
+ server:
+ container_name: ${TEST_VPP_BRIDGE}-server
+ volumes:
+ - ..:/workspace
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo ip link add br0 type bridge
+ sudo ip link set eth0 master br0
+ sudo ip link set eth0 up
+ sudo ip link set br0 up
+ sudo mkdir -p /var/log/vpp
+ sudo tee /etc/vpp/startup.conf <<EOF
+ cpu { main-core 2 }
+ plugins {
+ path /usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins
+ plugin default { disable }
+ plugin acl_plugin.so { enable }
+ plugin nat_plugin.so { enable }
+ plugin dhcp_plugin.so { enable }
+ plugin dpdk_plugin.so { enable }
+ plugin dns_plugin.so { enable }
+ plugin ping_plugin.so { enable }
+ plugin memif_plugin.so { enable }
+ plugin nsim_plugin.so { enable }
+ plugin hicn_plugin.so { enable }
+ }
+ unix {
+ startup-config /etc/vpp/server-up.txt
+ cli-listen /run/vpp/cli.sock
+ log /var/log/vpp/vpp.log
+ }
+ EOF
+ sudo tee /etc/vpp/server-up.txt <<EOF
+ create tap id 0 host-bridge br0
+ set int state tap0 up
+ set int ip addr tap0 192.168.1.12/24
+ set int ip addr tap0 2001::2/64
+ EOF
+
+ sudo vpp -c /etc/vpp/startup.conf
+ sleep 5
+
+ sudo hiperf -D -S -R -B 4000kbps -z memif_module ${RTC_PRODUCER}/128
+ sleep 1
+ sudo hiperf -D -S -z memif_module ${RAAQM_PRODUCER}/128
+ sleep 1
+ sudo hicn-ping-server -d -s 0 -n ${PING_PRODUCER}/128 -z memif_module
+
+ tail -f /dev/null
diff --git a/tests/2-nodes-vpp-memif-replication.yml b/tests/2-nodes-vpp-memif-replication.yml
new file mode 100644
index 000000000..37f028dac
--- /dev/null
+++ b/tests/2-nodes-vpp-memif-replication.yml
@@ -0,0 +1,143 @@
+version: "3"
+services:
+ vpp_client_memif:
+ build:
+ context: ..
+ dockerfile: ${DOCKERFILE}
+ args:
+ BASE_IMAGE: ${BASE_IMAGE}
+ stdin_open: true
+ tty: true
+ working_dir: /workspace
+ container_name: ${TEST_VPP_MEMIF_REPLICATION}-client
+ hostname: client
+ volumes:
+ - /tmp/memif:/memif
+ - ..:/workspace
+ entrypoint: [/bin/bash, -c]
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo mkdir -p /var/log/vpp
+ sudo tee /etc/vpp/startup.conf <<EOF
+ cpu { main-core 1 }
+ buffers { buffers-per-numa 600000 }
+ plugins {
+ path /hicn-root/lib/vpp_plugins:/usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins
+ plugin default { disable }
+ plugin acl_plugin.so { enable }
+ plugin nat_plugin.so { enable }
+ plugin dhcp_plugin.so { enable }
+ plugin dpdk_plugin.so { enable }
+ plugin dns_plugin.so { enable }
+ plugin ping_plugin.so { enable }
+ plugin memif_plugin.so { enable }
+ plugin nsim_plugin.so { enable }
+ plugin hicn_plugin.so { enable }
+ }
+
+ unix {
+ startup-config /etc/vpp/client-up.txt
+ cli-listen /run/vpp/cli.sock
+ log /var/log/vpp/vpp.log
+ }
+ EOF
+
+ sudo tee /etc/vpp/client-up.txt <<EOF
+ create memif socket id 1 filename /memif/memif1.sock
+ create interface memif id 0 socket-id 1 hw-addr aa:bb:cc:dd:ee:01 master
+ set int state memif1/0 up
+ set int ip addr memif1/0 192.168.1.1/24
+ set int ip addr memif1/0 2001::1/64
+ set ip neighbor memif1/0 2001::2 aa:bb:cc:dd:ee:02 static
+ ip route add b002::1/64 via 2001::2 memif1/0
+ create interface memif id 1 socket-id 1 hw-addr aa:bb:bb:bb:ee:01 master
+ set int state memif1/1 up
+ set int ip addr memif1/1 192.168.2.1/24
+ set int ip addr memif1/1 2002::1/64
+ set ip neighbor memif1/1 2002::2 aa:bb:bb:bb:ee:02 static
+ ip route add b002::1/64 via 2002::2 memif1/1
+ EOF
+
+ sudo rm /memif/memif1.sock
+
+ sudo vpp -c /etc/vpp/startup.conf
+ sleep 5
+
+ sudo vppctl hicn enable b002::1/64
+ sudo vppctl hicn strategy set 2 prefix b002::/64
+
+ tail -f /dev/null
+ vpp_server_memif:
+ build:
+ context: ..
+ dockerfile: ${DOCKERFILE}
+ args:
+ BASE_IMAGE: ${BASE_IMAGE}
+ stdin_open: true
+ tty: true
+ working_dir: /workspace
+ container_name: ${TEST_VPP_MEMIF_REPLICATION}-server
+ hostname: server
+ volumes:
+ - /tmp/memif:/memif
+ - ..:/workspace
+ entrypoint: [/bin/bash, -c]
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo mkdir -p /var/log/vpp
+ sudo tee /etc/vpp/startup.conf <<EOF
+ cpu { main-core 2 }
+ buffers { buffers-per-numa 600000 }
+
+ plugins {
+ path /hicn-root/lib/vpp_plugins:/usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins
+ plugin default { disable }
+ plugin acl_plugin.so { enable }
+ plugin nat_plugin.so { enable }
+ plugin dhcp_plugin.so { enable }
+ plugin dpdk_plugin.so { enable }
+ plugin dns_plugin.so { enable }
+ plugin ping_plugin.so { enable }
+ plugin memif_plugin.so { enable }
+ plugin nsim_plugin.so { enable }
+ plugin hicn_plugin.so { enable }
+ }
+ unix {
+ startup-config /etc/vpp/server-up.txt
+ cli-listen /run/vpp/cli.sock
+ log /var/log/vpp/vpp.log
+ }
+ EOF
+
+ sudo tee /etc/vpp/server-up.txt <<EOF
+ create memif socket id 1 filename /memif/memif1.sock
+ create interface memif id 0 socket-id 1 hw-addr aa:bb:cc:dd:ee:02 slave
+ set int state memif1/0 up
+ set int ip addr memif1/0 192.168.1.2/24
+ set int ip addr memif1/0 2001::2/64
+ set ip neighbor memif1/0 2001::1 aa:bb:cc:dd:ee:01 static
+ create interface memif id 1 socket-id 1 hw-addr aa:bb:bb:bb:ee:02 slave
+ set int state memif1/1 up
+ set int ip addr memif1/1 192.168.2.2/24
+ set int ip addr memif1/1 2002::2/64
+ set ip neighbor memif1/1 2002::1 aa:bb:bb:bb:ee:01 static
+ EOF
+
+ sudo vpp -c /etc/vpp/startup.conf
+ sleep 10
+
+ sudo hiperf -D -S -R -B 4000kbps -z memif_module ${RTC_PRODUCER}/128
+ sleep 5
+ sudo hiperf -D -S -z memif_module ${RAAQM_PRODUCER}/128
+ sleep 5
+ sudo hicn-ping-server -d -s 0 -n ${PING_PRODUCER}/128 -z memif_module
+
+ tail -f /dev/null
diff --git a/tests/2-nodes-vpp-memif.yml b/tests/2-nodes-vpp-memif.yml
new file mode 100644
index 000000000..034437dbc
--- /dev/null
+++ b/tests/2-nodes-vpp-memif.yml
@@ -0,0 +1,116 @@
+version: "3"
+services:
+ client:
+ container_name: ${TEST_VPP_MEMIF}-client
+ networks: []
+ volumes:
+ - /tmp/memif:/memif
+ - ..:/workspace
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo mkdir -p /var/log/vpp
+ sudo tee /etc/vpp/startup.conf <<EOF
+ cpu { main-core 1 }
+ buffers { buffers-per-numa 600000 }
+ plugins {
+ path /usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins
+ plugin default { disable }
+ plugin acl_plugin.so { enable }
+ plugin nat_plugin.so { enable }
+ plugin dhcp_plugin.so { enable }
+ plugin dpdk_plugin.so { enable }
+ plugin dns_plugin.so { enable }
+ plugin ping_plugin.so { enable }
+ plugin memif_plugin.so { enable }
+ plugin nsim_plugin.so { enable }
+ plugin hicn_plugin.so { enable }
+ }
+
+ unix {
+ startup-config /etc/vpp/client-up.txt
+ cli-listen /run/vpp/cli.sock
+ log /var/log/vpp/vpp.log
+ }
+ EOF
+
+ sudo tee /etc/vpp/client-up.txt <<EOF
+ comment { Creating memif }
+ create memif socket id 1 filename /memif/memif1.sock
+ create interface memif id 0 socket-id 1 mode ip master
+ set int state memif1/0 up
+ comment { Configuring memif }
+ set int ip addr memif1/0 192.168.1.1/24
+ set int ip addr memif1/0 2001::1/64
+ comment { Add route }
+ ip route add b002::1/64 via 2001::2 memif1/0
+ EOF
+
+ sudo rm /memif/memif1.sock
+ sudo vpp -c /etc/vpp/startup.conf
+ sleep 5
+
+ sudo vppctl hicn enable b002::1/64
+
+ tail -f /dev/null
+
+ server:
+ container_name: ${TEST_VPP_MEMIF}-server
+ networks: []
+ volumes:
+ - /tmp/memif:/memif
+ - ..:/workspace
+ command:
+ - |
+ if [ -d /workspace/build-dev ]; then
+ ninja -C /workspace/build-dev install
+ fi
+
+ sudo mkdir -p /var/log/vpp
+ sudo tee /etc/vpp/startup.conf <<EOF
+ cpu { main-core 2 }
+ buffers { buffers-per-numa 600000 }
+
+ plugins {
+ path /usr/lib/x86_64-linux-gnu/vpp_plugins:/usr/lib/vpp_plugins
+ plugin default { disable }
+ plugin acl_plugin.so { enable }
+ plugin nat_plugin.so { enable }
+ plugin dhcp_plugin.so { enable }
+ plugin dpdk_plugin.so { enable }
+ plugin dns_plugin.so { enable }
+ plugin ping_plugin.so { enable }
+ plugin memif_plugin.so { enable }
+ plugin nsim_plugin.so { enable }
+ plugin hicn_plugin.so { enable }
+ }
+ unix {
+ startup-config /etc/vpp/server-up.txt
+ cli-listen /run/vpp/cli.sock
+ log /var/log/vpp/vpp.log
+ }
+ EOF
+
+ sudo tee /etc/vpp/server-up.txt <<EOF
+ comment { Creating memif }
+ create memif socket id 1 filename /memif/memif1.sock
+ create interface memif id 0 socket-id 1 mode ip slave
+ set int state memif1/0 up
+ comment { Configuring memif }
+ set int ip addr memif1/0 192.168.1.2/24
+ set int ip addr memif1/0 2001::2/64
+ EOF
+
+ sudo vpp -c /etc/vpp/startup.conf
+ sleep 10
+
+ sudo hiperf -D -S -R -B 4000kbps -z memif_module ${RTC_PRODUCER}/128
+ sleep 5
+ sudo hiperf -D -S -z memif_module ${RAAQM_PRODUCER}/128
+ sleep 5
+ sudo hicn-ping-server -d -s 0 -n ${PING_PRODUCER}/128 -z memif_module
+
+ tail -f /dev/null
diff --git a/tests/2-nodes.yml b/tests/2-nodes.yml
new file mode 100644
index 000000000..ce87e5876
--- /dev/null
+++ b/tests/2-nodes.yml
@@ -0,0 +1,43 @@
+version: "3"
+services:
+ client:
+ build:
+ context: ..
+ dockerfile: ${DOCKERFILE}
+ args:
+ BASE_IMAGE: ${BASE_IMAGE}
+ privileged: true
+ stdin_open: true
+ hostname: client
+ tty: true
+ working_dir: /workspace
+ networks:
+ - kernel
+ volumes:
+ - ..:/workspace
+ entrypoint: [/bin/bash, -x, -c]
+ command:
+ - tail -f /dev/null
+
+ server:
+ build:
+ context: ..
+ dockerfile: ${DOCKERFILE}
+ args:
+ BASE_IMAGE: ${BASE_IMAGE}
+ privileged: true
+ stdin_open: true
+ hostname: server
+ tty: true
+ working_dir: /workspace
+ networks:
+ - kernel
+ volumes:
+ - ..:/workspace
+ entrypoint: [/bin/bash, -x, -c]
+ command:
+ - tail -f /dev/null
+
+networks:
+ kernel:
+ driver: bridge
diff --git a/tests/Dockerfile.ci b/tests/Dockerfile.ci
new file mode 100644
index 000000000..29f2e9dac
--- /dev/null
+++ b/tests/Dockerfile.ci
@@ -0,0 +1,5 @@
+ARG BASE_IMAGE
+
+FROM ${BASE_IMAGE}
+
+RUN sudo sed -i 's,secure_path="\(.*\)",secure_path="/hicn-root/bin:\1",' /etc/sudoers
diff --git a/tests/channel.sh b/tests/channel.sh
new file mode 100755
index 000000000..28a187e72
--- /dev/null
+++ b/tests/channel.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+set -e
+
+DEV=$2
+RATE=$3mbit
+DELAY=$4ms
+JITTER=$5ms
+LOSS=$6
+
+if [[ $1 -eq "set" ]]; then
+ tc qdisc add dev "$DEV" root handle 1:0 htb default 1
+ tc class add dev "$DEV" parent 1:0 classid 1:1 htb rate "$RATE"
+ tc qdisc add dev "$DEV" parent 1:1 handle 2:0 netem delay "$DELAY"
+ "$JITTER" loss random "$LOSS"
+ echo "Dev: $DEV, rate: $RATE, delay: $DELAY, jitter: $JITTER, loss: $LOSS%"
+elif [[ $1 -eq "change" ]]; then
+ tc qdisc change dev "$DEV" parent 1:1 handle 2:0 netem delay "$DELAY" "$JITTER" loss random "$LOSS"
+ echo "Dev: $DEV, rate: $RATE, delay: $DELAY, jitter: $JITTER, loss: $LOSS%"
+else
+ echo "set or change"
+fi
diff --git a/tests/config.sh b/tests/config.sh
new file mode 100755
index 000000000..bcd27e0b9
--- /dev/null
+++ b/tests/config.sh
@@ -0,0 +1,290 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+SCRIPT_PATH=$(
+ cd "$(dirname "${BASH_SOURCE}")"
+ pwd -P
+)
+
+# Import test configurations and names
+source ${SCRIPT_PATH}/.env
+
+set -a
+DOCKERFILE=${DOCKERFILE:-Dockerfile.dev}
+BASE_IMAGE=${BASE_IMAGE:-hicn}
+BUILD_SOFTWARE=${BUILD_SOFTWARE:-1}
+set +a
+
+HIPERF_CMD_RTC="hiperf -n 50 -C -H -R ${RTC_PRODUCER}"
+HIPERF_CMD_MEMIF_RTC="${HIPERF_CMD_RTC} -z memif_module"
+POSTPROCESS_COMMAND_RAAQM_RTC='tail -n +3 | \
+ tr -s " " | \
+ cut -f 4 -d " " | \
+ sort -n | \
+ tail -n 40 | \
+ head -n 35 | \
+ awk "BEGIN { \
+ c=0; \
+ s=0; \
+ }{ \
+ a[n++]=\$1; \
+ s+=\$1; \
+ } END { \
+ print int(a[0]), int(a[n-1]), int(s/n) \
+ }"'
+
+HIPERF_CMD_RAAQM="hiperf -n 50 -i 200 -C -H ${RAAQM_PRODUCER}"
+HIPERF_CMD_CBR="${HIPERF_CMD_RAAQM} -W 350 -M 0"
+HIPERF_CMD_MEMIF_RAAQM="${HIPERF_CMD_RAAQM} -z memif_module"
+HIPERF_CMD_MEMIF_CBR="${HIPERF_CMD_CBR} -z memif_module"
+
+PING_CMD="hicn-ping-client -m 50 -i 200000 -n ${PING_PRODUCER}"
+PING_CMD_MEMIF="${PING_CMD} -z memif_module"
+POSTPROCESS_COMMAND_PING='grep trip | \
+ cut -f 4 -d " " | \
+ sort -n | \
+ tail -n 40 | \
+ head -n 35 | \
+ awk "BEGIN { \
+ c=0; \
+ s=0; \
+ }{ \
+ a[n++]=\$1; \
+ s+=\$1; \
+ } END { \
+ print int(a[0]), int(a[n-1]), int(s/n) \
+ }"'
+
+declare -a topologies
+declare -a configurations
+
+# Fill topology array using the same values in the .env file
+for topology in $(compgen -A variable | grep TOPOLOGY_); do
+ topologies+=("${!topology}")
+done
+
+# Fill configurations array using the same values in the .env file
+for test in $(compgen -A variable | grep TEST_); do
+ configurations+=("${!test}")
+done
+
+declare -A tests=(
+ ["hicn-light-rtc"]="${HIPERF_CMD_RTC} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-bridge-rtc"]="${HIPERF_CMD_MEMIF_RTC} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-memif-rtc"]="${HIPERF_CMD_MEMIF_RTC} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-memif-replication-rtc"]="${HIPERF_CMD_MEMIF_RTC} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+
+ ["hicn-light-requin"]="${HIPERF_CMD_RAAQM} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-bridge-requin"]="${HIPERF_CMD_MEMIF_RAAQM} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-memif-requin"]="${HIPERF_CMD_MEMIF_RAAQM} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-memif-replication-requin"]="${HIPERF_CMD_MEMIF_RAAQM} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+
+ ["hicn-light-cbr"]="${HIPERF_CMD_CBR} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-bridge-cbr"]="${HIPERF_CMD_MEMIF_CBR} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-memif-cbr"]="${HIPERF_CMD_MEMIF_CBR} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+ ["vpp-memif-replication-cbr"]="${HIPERF_CMD_MEMIF_CBR} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_RAAQM_RTC}"
+
+ ["hicn-light-latency"]="${PING_CMD} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_PING}"
+ ["vpp-bridge-latency"]="${PING_CMD_MEMIF} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_PING}"
+ ["vpp-memif-latency"]="${PING_CMD_MEMIF} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_PING}"
+ ["vpp-memif-replication-latency"]="${PING_CMD_MEMIF} | tee >(>&2 cat) | ${POSTPROCESS_COMMAND_PING}"
+)
+
+declare -A link_model=(
+ ["500-0-0-0"]="500 0 0 0"
+ ["500-1-0-0"]="500 1 0 0"
+ ["500-5-0-0"]="500 5 0 0"
+ ["500-200-0-0"]="500 200 0 0"
+ ["100-1-0-0"]="100 1 0 0"
+ ["100-10-0-0"]="100 10 0 0"
+ ["100-15-0-0"]="100 15 0 0"
+ ["200-5-0-0"]="200 5 0 0"
+ ["300-5-0-0"]="300 5 0 0"
+ ["400-5-0-0"]="400 5 0 0"
+ ["1000-1-0-0"]="1000 1 0 0"
+ ["10-50-1-10"]="10 50 1 10"
+)
+
+function topology_exists() {
+ [[ "${topologies[*]}" =~ ${1} ]] && return 0 || return 1
+}
+
+function test_exists() {
+ [[ "${!tests[*]}" =~ ${1} ]] && return 0 || return 1
+}
+
+function conf_exists() {
+ [[ "${configurations[*]}" =~ ${1} ]] && return 0 || return 1
+}
+
+
+# test-name client/server link-model
+function setchannel() {
+ if ! conf_exists "${1}"; then
+ error "Error: topology does not exist."
+ fi
+
+ if ! test_exists "${1}"; then
+ error "Error: test does not exist."
+ fi
+ docker exec "${1}-${2}" bash -c "/workspace/tests/config.sh link set br0\
+ ${link_model[${3}]}"
+}
+# test-name client/server link-model
+function changechannel() {
+ if ! conf_exists "${1}"; then
+ error "Error: topology does not exist."
+ fi
+
+ if ! test_exists "${1}"; then
+ error "Error: test does not exist."
+ fi
+ docker exec "${1}-${2}" bash -c "/workspace/tests/config.sh link change br0\
+ ${link_model[${3}]}"
+}
+
+# channel set/change dev rate delay jitter lossrate
+function channel() {
+ DEV=${2}
+ RATE=${3}mbit
+ DELAY=${4}ms
+ JITTER=${5}ms
+ LOSS=${6}
+ if [[ $1 == set ]]; then
+ sudo tc qdisc add dev "${DEV}" root handle 1:0 htb default 1
+ sudo tc class add dev "${DEV}" parent 1:0 classid 1:1 htb rate "$RATE"
+ sudo tc qdisc add dev "${DEV}" parent 1:1 handle 2:0 netem delay "${DELAY}" \
+ "${JITTER}" loss random "${LOSS}"
+ echo "Dev: ${DEV}, rate: ${RATE}, delay: ${DELAY}, jitter: ${JITTER}, loss: ${LOSS}%"
+ elif [[ ${1} == change ]]; then
+ sudo tc qdisc change dev "$DEV" parent 1:1 handle 2:0 netem delay "$DELAY" \
+ "${JITTER}" loss random "${LOSS}"
+ echo "Dev: ${DEV}, rate: ${RATE}, delay: ${DELAY}, jitter: ${JITTER}, loss: ${LOSS}%"
+ else
+ sudo tc qdisc del dev "${DEV}" root
+ echo "set or change"
+ fi
+}
+function error() {
+ echo >&2 "${@}"
+ return 1
+}
+
+function build() {
+ docker-compose -f build.yml build
+ docker-compose -f build.yml up --force-recreate --remove-orphans >/dev/null
+}
+
+function setup() {
+ topology=${1}
+ conf=${2}
+
+ if ! topology_exists "${topology}"; then
+ error "Error: topology does not exist."
+ fi
+
+ if ! conf_exists "${conf}"; then
+ error "Error: topology does not exist."
+ fi
+
+ docker-compose -f "${topology}".yml -f "${topology}-${conf}".yml build
+ docker-compose -f "${topology}".yml -f "${topology}-${conf}".yml up --remove-orphans --force-recreate -d
+
+ sleep 10
+}
+
+function start() {
+ conf=${1}
+ test=${2}
+
+ if ! conf_exists "${conf}"; then
+ error "Error: configuration does not exist."
+ fi
+
+ TESTNAME="${conf}-${test}"
+
+ if ! test_exists "${TESTNAME}"; then
+ error "Error: test does not exist."
+ fi
+
+ docker exec "${1}"-client bash -c "/workspace/tests/config.sh runtest ${tests[${TESTNAME}]}"
+}
+
+function stop() {
+ topology="${1}"
+ conf="${2}"
+
+ if ! topology_exists "${topology}"; then
+ error "Error: topology does not exist."
+ fi
+
+ if ! conf_exists "${conf}"; then
+ error "Error: tect configuration does not exist."
+ fi
+
+ LOG_FILE="${SCRIPT_PATH}/${topology}-${conf}.log"
+ rm -f "${LOG_FILE}"
+ docker-compose -f "${topology}".yml -f "${topology}-${conf}".yml down || true
+}
+
+function stopall() {
+ for topology in "${topologies[@]}"; do
+ for conf in "${configurations[@]}"; do
+ stop "${topology}" "${conf}"
+ done
+ done
+}
+
+function runtest() {
+ echo "${@}" | sudo -i
+}
+
+while (("${#}")); do
+ case "$1" in
+ 'build')
+ build
+ shift
+ ;;
+ 'link')
+ shift
+ channel "$@"
+ shift 6
+ ;;
+ 'setchannel')
+ shift
+ setchannel "$@"
+ shift 4
+ ;;
+ 'changechannel')
+ shift
+ changechannel "$@"
+ shift 4
+ ;;
+ 'setup')
+ setup "${2}" "${3}"
+ shift 3
+ ;;
+ 'start')
+ start "${2}" "${3}"
+ shift 3
+ ;;
+ 'stop')
+ stop "${2}" "${3}"
+ shift 3
+ ;;
+ 'stopall')
+ stopall
+ shift
+ ;;
+ 'runtest')
+ runtest "${@:2}"
+ break
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+exit 0
diff --git a/tests/forwarder.robot b/tests/forwarder.robot
new file mode 100644
index 000000000..60345dbb0
--- /dev/null
+++ b/tests/forwarder.robot
@@ -0,0 +1,45 @@
+*** Settings ***
+Library Process
+Test Template Run Test
+Test Setup Setup
+Test Teardown Teardown
+Test Timeout 5 seconds
+
+*** Variables ***
+${cmd} bash test_forwarder.sh
+
+*** Test Cases ***
+# Commands
+Add listener test_add_listener
+Remove listener test_remove_listener
+Remove non-existing listener test_remove_non_existing_listener
+Add duplicated listener test_add_duplicated_listener
+List listeners test_list_listeners
+Commands from config file test_commands_from_config
+
+# Ping
+Ping one packet test_ping_one_packet
+Ping two packets test_ping_two_packets
+Ping using CS test_ping_using_cs
+Ping using CS different order test_ping_using_cs_different_order
+Ping timeout test_ping_timeout
+Ping aggregation test_ping_aggregation
+Ping with CS store disabled test_ping_with_cs_store_disabled
+Ping with CS serve disabled test_ping_with_cs_serve_disabled
+Ping with eviction test_ping_with_eviction
+Ping with zero data lifetime test_ping_with_zero_data_lifetime
+
+*** Keywords ***
+Setup
+ ${result}= Run Process ${cmd} set_up shell=True
+ Log Many stdout: ${result.stdout} stderr: ${result.stderr}
+
+Teardown
+ ${result}= Run Process ${cmd} tear_down shell=True
+ Log Many stdout: ${result.stdout} stderr: ${result.stderr}
+
+Run Test
+ [Arguments] ${test_name}
+ ${result}= Run Process ${cmd} ${test_name} shell=True
+ Log Many stdout: ${result.stdout} stderr: ${result.stderr}
+ Should Be Equal As Integers ${result.rc} 0 \ No newline at end of file
diff --git a/tests/functional-tests/2-nodes-hicn-light.robot b/tests/functional-tests/2-nodes-hicn-light.robot
new file mode 100644
index 000000000..9aa5923a0
--- /dev/null
+++ b/tests/functional-tests/2-nodes-hicn-light.robot
@@ -0,0 +1,24 @@
+*** Settings ***
+Resource resources/libraries/robot/runtest.robot
+Resource resources/libraries/robot/common.robot
+Suite Setup Run Keywords
+... Build Topology 2-nodes hicn-light AND
+... Check Environment
+Suite Teardown Run Keywords
+... Destroy Topology
+Resource resources/libraries/robot/runtest.robot
+
+*** Test Cases ***
+
+Throughput Testing Raaqm Mobile
+ Run Throughput Test Raaqm hicn-light 200 500 400
+
+Throughput Testing CBR Mobile
+ Run Throughput Test CBR hicn-light 200 500 400
+
+RTC Testing Mobile
+ Run RTC Test hicn-light 4 4 4
+
+Latency Testing Mobile
+ Set Link hicn-light 500 1 0 0
+ Run Latency Test hicn-light 3000 3000 3000
diff --git a/tests/functional-tests/2-nodes-vpp-bridge.robot b/tests/functional-tests/2-nodes-vpp-bridge.robot
new file mode 100644
index 000000000..83c8818ab
--- /dev/null
+++ b/tests/functional-tests/2-nodes-vpp-bridge.robot
@@ -0,0 +1,24 @@
+*** Settings ***
+Resource resources/libraries/robot/runtest.robot
+Resource resources/libraries/robot/common.robot
+Suite Setup Run Keywords
+... Build Topology 2-nodes vpp-bridge AND
+... Check Environment
+Suite Teardown Run Keywords
+... Destroy Topology
+Resource resources/libraries/robot/runtest.robot
+
+*** Test Cases ***
+
+Throughput Testing Raaqm Server VPP bridge
+ Run Throughput Test Raaqm vpp-bridge 500 500 500
+
+Throughput Testing CBR Server VPP bridge
+ Run Throughput Test CBR vpp-bridge 1000 1300 1200
+
+RTC Testing Server VPP bridge
+ Run RTC Test vpp-bridge 4 4 4
+
+Latency Testing Server VPP bridge
+ Set Link hicn-light 500 1 0 0
+ Run Latency Test vpp-bridge 3000 3000 3000
diff --git a/tests/functional-tests/2-nodes-vpp-memif-replication.robot b/tests/functional-tests/2-nodes-vpp-memif-replication.robot
new file mode 100644
index 000000000..8c13f4fb9
--- /dev/null
+++ b/tests/functional-tests/2-nodes-vpp-memif-replication.robot
@@ -0,0 +1,23 @@
+*** Settings ***
+Resource resources/libraries/robot/runtest.robot
+Resource resources/libraries/robot/common.robot
+Suite Setup Run Keywords
+... Build Topology 2-nodes vpp-memif-replication AND
+... Check Environment
+Suite Teardown Run Keywords
+... Destroy Topology
+Resource resources/libraries/robot/runtest.robot
+
+*** Test Cases ***
+
+Throughput Testing Raaqm Server VPP memif replication
+ Run Throughput Test Raaqm vpp-memif-replication 500 500 500
+
+Throughput Testing CBR Server VPP memif
+ Run Throughput Test CBR vpp-memif-replication 2000 2000 2000
+
+RTC Testing Server VPP memif replication
+ Run RTC Test vpp-memif-replication 4 4 4
+
+Latency Testing Server VPP memif replication
+ Run Latency Test vpp-memif-replication 3000 3000 3000
diff --git a/tests/functional-tests/2-nodes-vpp-memif.robot b/tests/functional-tests/2-nodes-vpp-memif.robot
new file mode 100644
index 000000000..1a69da787
--- /dev/null
+++ b/tests/functional-tests/2-nodes-vpp-memif.robot
@@ -0,0 +1,23 @@
+*** Settings ***
+Resource resources/libraries/robot/runtest.robot
+Resource resources/libraries/robot/common.robot
+Suite Setup Run Keywords
+... Build Topology 2-nodes vpp-memif AND
+... Check Environment
+Suite Teardown Run Keywords
+... Destroy Topology
+Resource resources/libraries/robot/runtest.robot
+
+*** Test Cases ***
+
+Throughput Testing Raaqm Server VPP memif
+ Run Throughput Test Raaqm vpp-memif 500 500 500
+
+Throughput Testing CBR Server VPP memif
+ Run Throughput Test CBR vpp-memif 2000 2000 2000
+
+RTC Testing Server VPP memif
+ Run RTC Test vpp-memif 4 4 4
+
+Latency Testing Server VPP memif
+ Run Latency Test vpp-memif 3000 3000 3000
diff --git a/tests/resources/libraries/robot/common.robot b/tests/resources/libraries/robot/common.robot
new file mode 100644
index 000000000..c1e3f20a4
--- /dev/null
+++ b/tests/resources/libraries/robot/common.robot
@@ -0,0 +1,23 @@
+*** Settings ***
+Library OperatingSystem
+Library Process
+Library String
+
+*** Variables ***
+
+*** Keywords ***
+
+Build Topology
+ [Arguments] ${TEST_TOPOLOGY}=${NONE} ${TEST_CONFIGURATION}=${NONE}
+ Log to console Building topology ${TEST_TOPOLOGY} ${TEST_CONFIGURATION}
+ ${result_setup} = Run Process ${EXECDIR}/config.sh build setup ${TEST_TOPOLOGY} ${TEST_CONFIGURATION}
+ Log to console Done
+ Log Many stdout: ${result_setup.stdout} stderr: ${result_setup.stderr}
+
+Check Environment
+ ${result} = Run Process docker ps
+ Log Many stdout: ${result.stdout} stderr: ${result.stderr}
+
+Destroy Topology
+ ${result_teardown} = Run Process ${EXECDIR}/config.sh stopall
+ Log Many stdout: ${result_teardown.stdout} stderr: ${result_teardown.stderr}
diff --git a/tests/resources/libraries/robot/runtest.robot b/tests/resources/libraries/robot/runtest.robot
new file mode 100644
index 000000000..d5201d765
--- /dev/null
+++ b/tests/resources/libraries/robot/runtest.robot
@@ -0,0 +1,92 @@
+*** Settings ***
+Library OperatingSystem
+Library Process
+Library String
+
+*** Variables ***
+
+*** Keywords ***
+
+Infra ${VALUE}
+ Run Process ${EXECDIR}/config.sh ${VALUE}
+
+Run Test
+ [Arguments] ${TEST_SETUP}=${NONE} ${TESTID}=${NONE} ${EXPECTED_MIN}=${NONE} ${EXPECTED_MAX}=${NONE} ${EXPECTED_AVG}=${NONE}
+ ${result_test} = Run Process ${EXECDIR}/config.sh start ${TEST_SETUP} ${TESTID} stdout=${TEMPDIR}/stdout.txt stderr=${TEMPDIR}/stderr.txt
+ Log Many stdout: ${result_test.stdout} stderr: ${result_test.stderr}
+ @{min_max_avg} = Split String ${result_test.stdout.strip()}
+ Log To Console Min Max Average Array: @{min_max_avg}
+ IF '${TESTID}' == 'rtc'
+ Should Be True ${min_max_avg}[0] == ${EXPECTED_MIN} msg="Min does not match (${min_max_avg}[0] != ${EXPECTED_MIN})"
+ Should Be True ${min_max_avg}[1] == ${EXPECTED_MAX} msg="Max does not match (${min_max_avg}[1] != ${EXPECTED_MAX})"
+ Should Be True ${min_max_avg}[2] == ${EXPECTED_AVG} msg="Avg does not match (${min_max_avg}[2] != ${EXPECTED_AVG})"
+ ELSE IF '${TESTID}' == 'requin'
+ Should Be True ${min_max_avg}[0] >= ${EXPECTED_MIN} msg="Min does not match (${min_max_avg}[0] < ${EXPECTED_MIN})"
+ Should Be True ${min_max_avg}[1] >= ${EXPECTED_MAX} msg="Max does not match (${min_max_avg}[1] < ${EXPECTED_MAX})"
+ Should Be True ${min_max_avg}[2] >= ${EXPECTED_AVG} msg="Avg does not match (${min_max_avg}[2] < ${EXPECTED_AVG})"
+ ELSE IF '${TESTID}' == 'latency'
+ Should Be True ${min_max_avg}[0] <= ${EXPECTED_MIN} msg="Min does not match (${min_max_avg}[0] > ${EXPECTED_MIN})"
+ Should Be True ${min_max_avg}[1] <= ${EXPECTED_MAX} msg="Max does not match (${min_max_avg}[1] > ${EXPECTED_MAX})"
+ Should Be True ${min_max_avg}[2] <= ${EXPECTED_AVG} msg="Avg does not match (${min_max_avg}[2] > ${EXPECTED_AVG})"
+ ELSE IF '${TESTID}' == 'cbr'
+ Should Be True ${min_max_avg}[0] >= ${EXPECTED_MIN} msg="Min does not match (${min_max_avg}[0] < ${EXPECTED_MIN})"
+ Should Be True ${min_max_avg}[1] >= ${EXPECTED_MAX} msg="Max does not match (${min_max_avg}[1] < ${EXPECTED_MAX})"
+ Should Be True ${min_max_avg}[2] >= ${EXPECTED_AVG} msg="Avg does not match (${min_max_avg}[2] < ${EXPECTED_AVG})"
+ ELSE
+ Fail "Provided Test ID does not exist"
+ END
+
+Set Link
+ [Documentation] Configure link rate/delay/jitter/loss
+ ... Arguments:
+ ... ${RATE} Rate of the link
+ ... ${DELAY} Delay of the link
+ ... ${JITTER} Jitter of the link
+ ... ${LOSS} Loss of the link
+ [Arguments] ${TEST_SETUP}=${NONE}
+ ... ${RATE}=${NONE}
+ ... ${DELAY}=${NONE}
+ ... ${JITTER}=${NONE}
+ ... ${LOSS}=${NONE}
+ ${result_link} = Run Process ${EXECDIR}/config.sh setchannel ${TEST_SETUP} server ${RATE}-${DELAY}-${JITTER}-${LOSS}
+ Log Many stdout: ${result_link.stdout} stderr: ${result_link.stderr}
+
+Run Latency Test
+ [Documentation] Run hicn-ping on the ${TEST_SETUP} topology and measure latency.
+ ... Arguments:
+ ... ${TEST_SETUP} The setup of the test.
+ ... ${EXPECTED_MIN} The expected min latency
+ ... ${EXPECTED_MAX} The expected max latency
+ ... ${EXPECTED_AVG} The expected avg latency
+ [Arguments] ${TEST_SETUP}=${NONE} ${EXPECTED_MIN}=${NONE} ${EXPECTED_MAX}=${NONE} ${EXPECTED_AVG}=${NONE}
+ Run Test ${TEST_SETUP} latency ${EXPECTED_MIN} ${EXPECTED_MAX} ${EXPECTED_AVG}
+
+Run Throughput Test Raaqm
+ [Documentation] Run hiperf on the ${TEST_SETUP} topology and measure throughput.
+ ... Arguments:
+ ... ${TEST_SETUP} The setup of the test.
+ ... ${EXPECTED_MIN} The expected min throughput
+ ... ${EXPECTED_MAX} The expected max throughput
+ ... ${EXPECTED_AVG} The expected avg throughput
+ [Arguments] ${TEST_SETUP}=${NONE} ${EXPECTED_MIN}=${NONE} ${EXPECTED_MAX}=${NONE} ${EXPECTED_AVG}=${NONE}
+ Run Test ${TEST_SETUP} requin ${EXPECTED_MIN} ${EXPECTED_MAX} ${EXPECTED_AVG}
+
+Run Throughput Test CBR
+ [Documentation] Run hiperf on the ${TEST_SETUP} topology and measure throughput.
+ ... Arguments:
+ ... ${TEST_SETUP} The setup of the test.
+ ... ${EXPECTED_MIN} The expected min throughput
+ ... ${EXPECTED_MAX} The expected max throughput
+ ... ${EXPECTED_AVG} The expected avg throughput
+ [Arguments] ${TEST_SETUP}=${NONE} ${EXPECTED_MIN}=${NONE} ${EXPECTED_MAX}=${NONE} ${EXPECTED_AVG}=${NONE}
+ Run Test ${TEST_SETUP} cbr ${EXPECTED_MIN} ${EXPECTED_MAX} ${EXPECTED_AVG}
+
+Run RTC Test
+ [Documentation] Run hiperf RTC on the ${TEST_SETUP} topology and check consumer syncs to producer bitrate.
+ ... Arguments:
+ ... ${TEST_SETUP} The setup of the test.
+ ... ${EXPECTED_MIN} The expected min bitrate
+ ... ${EXPECTED_MAX} The expected max bitrate
+ ... ${EXPECTED_AVG} The expected avg bitrate
+ [Arguments] ${TEST_SETUP}=${NONE} ${EXPECTED_MIN}=${NONE} ${EXPECTED_MAX}=${NONE} ${EXPECTED_AVG}=${NONE}
+ Run Test ${TEST_SETUP} rtc ${EXPECTED_MIN} ${EXPECTED_MAX} ${EXPECTED_AVG}
diff --git a/tests/run-functional.sh b/tests/run-functional.sh
new file mode 100644
index 000000000..b8dfbc437
--- /dev/null
+++ b/tests/run-functional.sh
@@ -0,0 +1,40 @@
+# Copyright (c) 2021 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SCRIPT_PATH=$(
+ cd "$(dirname "${BASH_SOURCE}")"
+ pwd -P
+)
+
+pushd "${SCRIPT_PATH}" || exit 1
+
+declare -a REPORTS
+
+for t in functional-tests/*; do
+ [[ -e "$t" ]] || break # handle the case of no test files
+
+ test=$(basename "$t")
+
+ robot --NoStatusRC \
+ --outputdir report_"${test}" \
+ -P ${PWD} functional-tests/"${test}"
+
+ REPORTS+=(report_"${test}"/output.xml)
+done
+
+rebot --output output.xml \
+ -l log.html -r report.html \
+ --nostatusrc \
+ "${REPORTS[@]}"
+
+popd || exit 1
diff --git a/tests/test_forwarder.sh b/tests/test_forwarder.sh
new file mode 100644
index 000000000..aba85d8d8
--- /dev/null
+++ b/tests/test_forwarder.sh
@@ -0,0 +1,437 @@
+#!/bin/bash
+
+############################################################################
+# CONSTANTS
+############################################################################
+INTERFACE_CMD="ip route get 1 | grep -Po '(?<=(dev )).*(?= src| proto)'"
+ADDRESS_CMD="ip route get 1 | sed -n '/src/{s/.*src *\([^ ]*\).*/\1/p;q}'"
+CTRL_CMD="docker exec test-hicn \
+ /hicn-build/build/build-root/bin/hicn-light-control"
+PING_SERVER_CMD="docker exec -d test-hicn \
+ /hicn-build/build/build-root/bin/hicn-ping-server \
+ -z hicnlightng_module"
+PING_CLIENT_CMD="docker exec test-hicn \
+ /hicn-build/build/build-root/bin/hicn-ping-client \
+ -z hicnlightng_module"
+PING_CLIENT_DETACHED_CMD="docker exec -d test-hicn \
+ /hicn-build/build/build-root/bin/hicn-ping-client \
+ -z hicnlightng_module"
+LISTENER_NAME="udp0"
+CONN_NAME="conn0"
+PREFIX="c001::/64"
+COST=1
+FIVE_SECONDS=5000
+
+############################################################################
+# UTILS
+############################################################################
+set_up() {
+ docker build -t hicn-dev .
+ run_forwarder
+}
+
+tear_down() {
+ docker stop --time 0 test-hicn
+}
+
+get_address() {
+ echo $(docker exec test-hicn sh -c "${ADDRESS_CMD}")
+}
+
+get_interface() {
+ echo $(docker exec test-hicn sh -c "${INTERFACE_CMD}")
+}
+
+#---------------------------------------------------------------------------
+# Exec
+#---------------------------------------------------------------------------
+run_forwarder() {
+ capacity=${1:-"100000"}
+ loglevel=${2:-"trace"}
+ config=${3:-""}
+
+ config_file_arg=""
+ if [[ $config != "" ]]; then
+ config_file_arg="--config ${config}"
+ fi
+
+ docker run --rm -d --name test-hicn \
+ -v $(pwd)/..:/hicn-build \
+ -e LD_LIBRARY_PATH=/hicn-build/build/build-root/lib \
+ hicn-dev \
+ /hicn-build/build/build-root/bin/hicn-light-daemon \
+ --log ${loglevel} --capacity ${capacity} $config_file_arg
+}
+
+exec_controller() {
+ command=$1
+
+ # Redirect stderr to stdout
+ output=$(${CTRL_CMD} ${command} 2>&1)
+ assert_exit_code
+ echo ${output}
+}
+
+exec_ping_server() {
+ data_lifetime=${1:-""}
+
+ lifetime_arg=""
+ if [[ $data_lifetime != "" ]]; then
+ lifetime_arg="-l ${data_lifetime}"
+ fi
+
+ ${PING_SERVER_CMD} ${lifetime_arg}
+}
+
+exec_ping_client() {
+ num_packets=$1
+
+ output=$(${PING_CLIENT_CMD} -m ${num_packets})
+ assert_exit_code
+ echo ${output}
+}
+
+exec_ping_client_detached() {
+ num_packets=$1
+ interest_lifetime=$2
+
+ ${PING_CLIENT_DETACHED_CMD} -m ${num_packets} -l ${interest_lifetime}
+}
+
+#---------------------------------------------------------------------------
+# Asserts
+#---------------------------------------------------------------------------
+assert_exit_code() {
+ if [[ $? -ne 0 ]]; then
+ exit_with_failure
+ fi
+}
+
+assert_forwarder() {
+ # Print forwarder logs for debug info
+ echo "******** Forwarder Logs ********"
+ docker logs test-hicn
+ echo "********************************"
+
+ output=$(docker logs test-hicn)
+ if [[ $output == "" ]]; then
+ exit_with_failure
+ fi
+
+ if [[ "${output}" == *"ERROR"* ]]; then
+ exit_with_failure
+ fi
+
+ if [[ "${output}" == *"Aborted (core dumped)"* ]]; then
+ exit_with_failure
+ fi
+}
+
+assert_ack() {
+ # Print controller logs for debug info
+ echo "******** Controller Logs ********"
+ echo $1
+ echo "********************************"
+
+ output=$1
+
+ if [[ "$output" == *"Error"* ]]; then
+ exit_with_failure
+ fi
+}
+
+assert_nack() {
+ # Print controller logs for debug info
+ echo "******** Controller Logs ********"
+ echo $1
+ echo "********************************"
+
+ output=$1
+
+ if [[ "$output" != *"Error"* ]]; then
+ exit_with_failure
+ fi
+}
+
+assert_ping_client() {
+ # Print ping client logs for debug info
+ echo "******** Ping Client Logs ********"
+ echo $1
+ echo "********************************"
+
+ ping_client_output=$1
+ pkts_sent=$2
+ pkts_recv=$3
+ pkts_timeout=$4
+
+ match_str="Sent: ${pkts_sent} Received: ${pkts_recv} Timeouts: ${pkts_timeout}"
+ if [[ ! ${ping_client_output} == *"${match_str}"* ]]; then
+ exit_with_failure
+ fi
+}
+
+assert_forwarder_stats() {
+ satisfied_from_cs=${1:-""}
+ no_route_in_fib=${2:-""}
+ aggregated=${3:-""}
+
+ fwder_stats=$(docker logs test-hicn | grep "Forwarder: received" | tail -n 1)
+
+ if [[ $satisfied_from_cs != "" &&
+ "${fwder_stats}" != *"satisfied_from_cs = ${satisfied_from_cs}"* ]]; then
+ exit_with_failure
+ fi
+
+ if [[ $no_route_in_fib != "" &&
+ "${fwder_stats}" != *"no_route_in_fib = ${no_route_in_fib}"* ]]; then
+ exit_with_failure
+ fi
+
+ if [[ $aggregated != "" &&
+ "${fwder_stats}" != *"aggregated = ${aggregated}"* ]]; then
+ exit_with_failure
+ fi
+}
+
+assert_pkt_cache_stats() {
+ total_size=${1:-""}
+ pit_size=${2:-""}
+ cs_size=${3:-""}
+
+ pkt_cache_stats=$(docker logs test-hicn | grep "Packet cache:" | tail -n 1)
+
+ if [[ $total_size != "" &&
+ "${pkt_cache_stats}" != *"total size = ${total_size}"* ]]; then
+ exit_with_failure
+ fi
+
+ if [[ $pit_size != "" &&
+ "${pkt_cache_stats}" != *"PIT size = ${pit_size}"* ]]; then
+ exit_with_failure
+ fi
+
+ if [[ $cs_size != "" &&
+ "${pkt_cache_stats}" != *"CS size = ${cs_size}"* ]]; then
+ exit_with_failure
+ fi
+}
+
+assert_cs_stats() {
+ evictions=${1:-""}
+
+ cs_stats=$(docker logs test-hicn | grep "Content store:" | tail -n 1)
+
+ if [[ $evictions != "" &&
+ "${cs_stats}" != *"evictions = ${evictions}"* ]]; then
+ exit_with_failure
+ fi
+}
+
+############################################################################
+# TEST SUITE
+############################################################################
+
+#---------------------------------------------------------------------------
+# Commands
+#---------------------------------------------------------------------------
+test_add_listener() {
+ # Exec hicn-light-control command and capture its output
+ INTERFACE=$(get_interface)
+ ADDRESS=$(get_address)
+ command="add listener udp ${LISTENER_NAME} ${ADDRESS} 9695 ${INTERFACE}"
+ ctrl_output=$(exec_controller "${command}")
+
+ # Check hicn-light-control and hicn-light-daemon outputs
+ assert_ack "$ctrl_output"
+ assert_forwarder
+}
+
+test_remove_listener() {
+ INTERFACE=$(get_interface)
+ ADDRESS=$(get_address)
+ command="add listener udp ${LISTENER_NAME} ${ADDRESS} 9695 ${INTERFACE}"
+ ctrl_output=$(exec_controller "${command}")
+ assert_ack "$ctrl_output"
+
+ command="remove listener udp0"
+ ctrl_output=$(exec_controller "${command}")
+
+ assert_ack "$ctrl_output"
+ assert_forwarder
+}
+
+test_remove_non_existing_listener() {
+ command="remove listener udp0"
+ ctrl_output=$(exec_controller "${command}")
+
+ assert_nack "$ctrl_output"
+ assert_forwarder
+}
+
+test_add_duplicated_listener() {
+ # Exec hicn-light-control command and capture its output
+ INTERFACE=$(get_interface)
+ ADDRESS=$(get_address)
+ command="add listener udp ${LISTENER_NAME} ${ADDRESS} 9695 ${INTERFACE}"
+ exec_controller "${command}"
+ ctrl_output=$(exec_controller "${command}")
+
+ # Check hicn-light-control and hicn-light-daemon outputs
+ assert_nack "$ctrl_output"
+ assert_forwarder
+}
+
+test_list_listeners() {
+ # Exec hicn-light-control command and capture its output
+ command="list listener"
+ ctrl_output=$(exec_controller "${command}")
+
+ # Check hicn-light-control and hicn-light-daemon outputs
+ assert_forwarder
+ # Only the local listener should be present
+ [[ "${ctrl_output}" =~ "inet4://127.0.0.1:9695" ]] && return 0 || exit_with_failure
+}
+
+test_commands_from_config() {
+ # Create config file
+ INTERFACE=$(get_interface)
+ ADDRESS=$(get_address)
+ echo "# Teset config file
+ add listener udp $LISTENER_NAME $ADDRESS 9695 ${INTERFACE}
+ add connection udp $CONN_NAME $ADDRESS 12345 $ADDRESS 9695 ${INTERFACE}
+ add route $CONN_NAME $PREFIX $COST
+ set strategy c001::/64 random
+ " >forwarder.conf
+
+ # Restart the forwarder specifying the config file
+ tear_down
+ run_forwarder "" "" "/hicn-build/tests/forwarder.conf"
+ rm forwarder.conf
+
+ # Check for errors in the output
+ assert_forwarder
+}
+
+#---------------------------------------------------------------------------
+# Ping
+#---------------------------------------------------------------------------
+test_ping_one_packet() {
+ # Exec hicn-ping-server
+ exec_ping_server
+ # Exec hicn-ping-client (w/ 1 packet) and capture its output
+ output=$(exec_ping_client 1)
+
+ # Check hicn-ping-client (1 pkt sent, 1 pkt received, 0 timeouts)
+ # and hicn-light-daemon outputs
+ assert_ping_client "${output}" 1 1 0
+ assert_forwarder
+}
+
+test_ping_two_packets() {
+ exec_ping_server
+ output=$(exec_ping_client 2)
+
+ assert_ping_client "${output}" 2 2 0
+ assert_forwarder
+}
+
+test_ping_using_cs() {
+ exec_ping_server
+ exec_ping_client 2
+ output=$(exec_ping_client 1)
+
+ assert_ping_client "${output}" 1 1 0
+ assert_forwarder
+ assert_forwarder_stats 1
+}
+
+test_ping_using_cs_different_order() {
+ exec_ping_server
+ exec_ping_client 1
+ output=$(exec_ping_client 2)
+
+ assert_ping_client "${output}" 2 2 0
+ assert_forwarder
+ assert_forwarder_stats 1
+}
+
+test_ping_timeout() {
+ # Send ping without the ping server being run
+ output=$(exec_ping_client 1)
+
+ assert_ping_client "${output}" 1 0 1
+ assert_forwarder
+ assert_forwarder_stats 0 1
+}
+
+test_ping_aggregation() {
+ # Send ping without server, waiting for a reply
+ exec_ping_client_detached 1 ${FIVE_SECONDS}
+ exec_ping_server
+ # This new ping interest will be aggregated with the previous one
+ # and the forwarder will reply to both ping clients
+ output=$(exec_ping_client 1)
+
+ assert_ping_client "${output}" 1 1 0
+ assert_forwarder
+ assert_forwarder_stats "" "" 1
+}
+
+test_ping_with_cs_store_disabled() {
+ command="store cache off"
+ exec_controller "${command}"
+
+ exec_ping_server
+ exec_ping_client 1
+ output=$(exec_ping_client 1)
+
+ assert_ping_client "${output}" 1 1 0
+ assert_forwarder
+ assert_forwarder_stats 0 "" ""
+ # The packet is not stored in the CS
+ assert_pkt_cache_stats "" "" 0
+}
+
+test_ping_with_cs_serve_disabled() {
+ command="serve cache off"
+ exec_controller "${command}"
+
+ exec_ping_server
+ exec_ping_client 1
+ output=$(exec_ping_client 1)
+
+ assert_ping_client "${output}" 1 1 0
+ assert_forwarder
+ assert_forwarder_stats 0 "" ""
+ # The packet is stored in the CS, but CS is not used
+ assert_pkt_cache_stats "" "" 1
+}
+
+test_ping_with_eviction() {
+ # Restart the forwarder with CS capacity = 1
+ tear_down
+ run_forwarder 1
+
+ exec_ping_server
+ exec_ping_client 1
+ output=$(exec_ping_client 2)
+
+ assert_ping_client "${output}" 2 2 0
+ assert_forwarder
+ # Check if eviction happened
+ assert_cs_stats 1
+ assert_pkt_cache_stats "" "" 1
+}
+
+test_ping_with_zero_data_lifetime() {
+ exec_ping_server 0
+ exec_ping_client 1
+ output=$(exec_ping_client 1)
+
+ assert_ping_client "${output}" 1 1 0
+ assert_forwarder
+ # The data is not taken from the CS because expired
+ assert_forwarder_stats 0 "" ""
+}
+
+"$@"