From c46e5df56b67bb8ea7a068d39324c640084ead2b Mon Sep 17 00:00:00 2001 From: Luca Muscariello Date: Wed, 30 Mar 2022 22:29:28 +0200 Subject: feat: boostrap hicn 22.02 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Co-authored-by: Jordan Augé Co-authored-by: Michele Papalini Co-authored-by: Angelo Mantellini Co-authored-by: Jacques Samain Co-authored-by: Olivier Roques Co-authored-by: Enrico Loparco Co-authored-by: Giulio Grassi Change-Id: I75d0ef70f86d921e3ef503c99271216ff583c215 Signed-off-by: Luca Muscariello Signed-off-by: Mauro Sardara --- tests/.env | 13 + tests/2-nodes-hicn-light.yml | 65 +++ tests/2-nodes-vpp-bridge.yml | 107 +++++ tests/2-nodes-vpp-memif-replication.yml | 143 +++++++ tests/2-nodes-vpp-memif.yml | 116 ++++++ tests/2-nodes.yml | 43 ++ tests/Dockerfile.ci | 5 + tests/channel.sh | 22 ++ tests/config.sh | 290 ++++++++++++++ tests/forwarder.robot | 45 +++ tests/functional-tests/2-nodes-hicn-light.robot | 24 ++ tests/functional-tests/2-nodes-vpp-bridge.robot | 24 ++ .../2-nodes-vpp-memif-replication.robot | 23 ++ tests/functional-tests/2-nodes-vpp-memif.robot | 23 ++ tests/resources/libraries/robot/common.robot | 23 ++ tests/resources/libraries/robot/runtest.robot | 92 +++++ tests/run-functional.sh | 40 ++ tests/test_forwarder.sh | 437 +++++++++++++++++++++ 18 files changed, 1535 insertions(+) create mode 100644 tests/.env create mode 100644 tests/2-nodes-hicn-light.yml create mode 100644 tests/2-nodes-vpp-bridge.yml create mode 100644 tests/2-nodes-vpp-memif-replication.yml create mode 100644 tests/2-nodes-vpp-memif.yml create mode 100644 tests/2-nodes.yml create mode 100644 tests/Dockerfile.ci create mode 100755 tests/channel.sh create mode 100755 tests/config.sh create mode 100644 tests/forwarder.robot create mode 100644 tests/functional-tests/2-nodes-hicn-light.robot create mode 100644 tests/functional-tests/2-nodes-vpp-bridge.robot create mode 100644 tests/functional-tests/2-nodes-vpp-memif-replication.robot create mode 100644 tests/functional-tests/2-nodes-vpp-memif.robot create mode 100644 tests/resources/libraries/robot/common.robot create mode 100644 tests/resources/libraries/robot/runtest.robot create mode 100644 tests/run-functional.sh create mode 100644 tests/test_forwarder.sh (limited to 'tests') 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 <&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 "" "" +} + +"$@" -- cgit 1.2.3-korg