aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
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 "" ""
+}
+
+"$@"