Add test nack scenario for best-route, and a framework to support tests tools
refs #3157
Change-Id: I12d0241bd45e1af6796765a03751e445a6a23525
diff --git a/test_nack/README.md b/test_nack/README.md
new file mode 100644
index 0000000..6577105
--- /dev/null
+++ b/test_nack/README.md
@@ -0,0 +1,41 @@
+# Test Nack scenario
+
+## Topology
+
+ B
+ |
+ A--D
+ |
+ C
+
+## Steps
+
+1. start NFD on A,B,C,D
+2. configure NFD on A,C,D to use best-route strategy
+3. configure NFD on B to use best-route v1 strategy, which will not transmit or process Nack
+4. on A, create a route for prefix ndn:/P toward B and C,
+ set the cost toward B to be 10.
+ set the cost toward C to be 20.
+5. on D, create a route for prefix ndn:/P toward A
+6. on D, express an Interest for ndn:/P/1 with InterestLifetime=4000ms and no consumer
+ retransmission; wait 8000ms since expressing Interest, fail the scenario
+ if Nack comes back;
+7. on D, express an Interest for ndn:/P/2 with InterestLifetime=4000ms, and retransmit
+ with a new Nonce after 200ms;
+ wait 8000ms since expressing first Interest, fail the scenario if Nack comes back;
+ reason: A shouldn't return a Nack when some upstream times out, even if some other
+ upstream returns Nack.
+8. configure NFD on B to use best-route strategy
+9. on B and C, configure unidirectional link delay toward A as 500ms
+10. on D, express an Interest for ndn:/P/3 with InterestLifetime=4000ms, and retransmit
+ with nonce2 after 200ms;
+ wait 3000ms since expressing first Interest, expect exactly one Nack comes back
+ where Reason is NoRoute and Nonce equals nonce2, otherwise fail the scenario;
+ reason: A should return a Nack after both upstreams have returned Nack
+11. on B, configure unidirectional link delay toward A as 1000ms
+12. on D, express an Interest for ndn:/P/4 with InterestLifetime=4000ms, and
+ retransmit with nonce2 after 200ms;
+ wait 3000ms since expressing first Interest, expect exactly one Nack comes back
+ where Reason is NoRoute and Nonce equals nonce2, otherwise fail the scenario;
+ reason: the Nack should carry the latest incoming Nonce from downstream,
+ not the Nonce from last incoming Nack.
\ No newline at end of file
diff --git a/test_nack/nack-test.sh b/test_nack/nack-test.sh
new file mode 100755
index 0000000..1f3a313
--- /dev/null
+++ b/test_nack/nack-test.sh
@@ -0,0 +1,162 @@
+#!/usr/bin/env bash
+source ../multi-host.conf
+workDir=$(pwd)
+logDir=$workDir/logs
+testLogA=$logDir/nack-scenario-A.log
+testLogB=$logDir/nack-scenario-B.log
+testLogC=$logDir/nack-scenario-C.log
+testLogD=$logDir/nack-scenario-D.log
+mkdir -p $workDir/logs
+
+clean_up() {
+
+ dataB=$1
+ dataC=$2
+ echo "killing nfd on B, C and D..."
+ ssh $CTRL_B "sudo killall nfd >> $testLogB 2>&1 &\
+ sudo /sbin/tc qdisc del dev $dataB root"
+ ssh $CTRL_C "sudo killall nfd >> $testLogC 2>&1 &\
+ sudo /sbin/tc qdisc del dev $dataC root"
+ ssh $CTRL_D "sudo killall nfd >> $testLogD 2>&1"
+
+ sudo killall nfd 2>&1
+ sleep 2
+}
+
+echo "Starting nfd on host A" > $testLogA
+
+# start nfd on localhost (A) and set best-route strategy
+nfd-start &> $logDir/nfdA.log
+sleep 2
+nfdc set-strategy / /localhost/nfd/strategy/best-route >> $testLogA
+
+echo "Configure host B..."
+# start nfd on hostB and set best-route-v1
+ssh $CTRL_B "mkdir -p $logDir ; nfd-start &> $logDir/nfdB.log &\
+ sleep 2 ; echo 'Starting nfd on host B' > $testLogB &\
+ nfdc set-strategy / /localhost/nfd/strategy/best-route/%FD%01 >> $testLogB"
+
+echo "Configure host C..."
+# start nfd on hostC and set best-route strategy
+ssh $CTRL_C "mkdir -p $logDir ; nfd-start &> $logDir/nfdC.log &\
+ sleep 2 ; echo 'Starting nfd on host C' > $testLogC &\
+ nfdc set-strategy / /localhost/nfd/strategy/best-route >> $testLogC"
+
+echo "Configure host D..."
+# start nfd on hostD and set best-route strategy
+ssh $CTRL_D "mkdir -p $logDir ; nfd-start &> $logDir/nfdD.log &\
+ sleep 2 ; echo 'Starting nfd on host D' > $testLogD &\
+ nfdc set-strategy / /localhost/nfd/strategy/best-route >> $testLogD"
+
+# A: create a route for prefix ndn:/P toward B and C,
+# where the cost toward B (10) is lower than the cost toward C(20)
+nfdc register -c 10 ndn:/P udp4://$IP4_B1:6363 >> $testLogA
+nfdc register -c 20 ndn:/P udp4://$IP4_C1:6363 >> $testLogA
+
+# D: create a route for prefix ndn:/P toward A
+ssh $CTRL_D "echo 'creating a route for namespace ndn:/P toward A' >> $testLogD &\
+ nfdc register -c 20 ndn:/P udp4://$IP4_A2:6363 >> $testLogD &"
+
+echo "Start test 1..."
+# D: express an Interest for ndn:/P/1, no retransmissions, wait 8000ms
+ssh $CTRL_D "echo 'express an Interest for ndn:/P/1' >> $testLogD &\
+ test-nack-consumer -p ndn:/P/1 -w 8000 > $logDir/test1.log"
+
+scp $CTRL_D:$logDir/test1.log $logDir/test1.log
+
+echo "Analyzing result of test 1..."
+received=$(tail -n 1 $logDir/test1.log | awk '{print $1}')
+if [[ "$received" == "NACK" ]]
+then
+ echo "FAIL: A returned unexpected Nack while Nack is not expected if not all &\
+ upstreams return Nack. The test doesn't retransmit the Interest, and &\
+ therefore A didn't try all possible upstreams to return Nack from all. &\
+ Check test1 log for details."
+ clean_up
+exit 1
+fi
+
+echo "Start test 2..."
+# D: express an Interest for ndn:/P/2, retransmit with new nonce, wait 8000ms
+ssh $CTRL_D "echo 'express an Interest for ndn:/P/2' >> $testLogD &\
+ test-nack-consumer -p ndn:/P/2 -r 2000 -w 8000 > $logDir/test2.log"
+
+scp $CTRL_D:$logDir/test2.log $logDir/test2.log
+
+echo "Analyzing result of test 2..."
+received=$(tail -n 1 $logDir/test2.log | awk '{print $1}')
+if [[ "$received" == "NACK" ]]
+then
+echo "FAIL: A returned unexpected Nack. Nack is not expected when one or &\
+ upstreams time out, even if some upstreams return Nack. &\
+ Check test2 log for details."
+clean_up
+exit 1
+fi
+
+echo "Setup test 3..."
+# get data interface of host B
+dataB=$(ssh $CTRL_B "netstat -ie | grep -B1 $IP4_B1 | head -n1" | awk '{print $1}')
+
+# B: configure best-route strategy and get the set link delay to 500 ms
+ssh $CTRL_B "nfdc set-strategy / /localhost/nfd/strategy/best-route >> $testLogB &\
+ sudo /sbin/tc qdisc add dev $dataB root netem delay 500ms >> $testLogB "
+
+# get data interface of host C
+dataC=$(ssh $CTRL_C "netstat -ie | grep -B1 $IP4_C1 | head -n1" | awk '{print $1}')
+
+# B: configure link delay to 500 ms
+ssh $CTRL_C "sudo /sbin/tc qdisc add dev $dataC root netem delay 500ms >> $testLogC"
+
+echo "Start test 3..."
+# D: express an Interest for ndn:/P/3, retransmit with new nonce after 200ms, wait 3000ms
+ssh $CTRL_D "echo 'express an Interest for ndn:/P/3' >> $testLogD &\
+ test-nack-consumer -p ndn:/P/3 -r 200 -w 3000 > $logDir/test3.log"
+
+scp $CTRL_D:$logDir/test3.log $logDir/test3.log
+
+echo "Analyzing result of test 3..."
+reason=$(grep NACK $logDir/test3.log | awk '{print $NF}')
+NACK_count=$(grep NACK $logDir/test3.log | wc -l)
+
+if [ $NACK_count -ne 1 ] || [[ "$reason" != "NoRoute" ]]
+then
+ echo "FAIL: A should return exactly one Nack after both upstreams have returned Nack. &\
+ Reason should be 'NoRoute'. Test returned $NACK_count Nacks, with reason $reason. &\
+ Check test3 log for more details."
+ clean_up $dataB $dataC
+ exit 1
+fi
+
+echo "Setup test 4..."
+
+# B: configure link delay to 1000 ms
+ssh $CTRL_B "nfdc set-strategy / /localhost/nfd/strategy/best-route >> $testLogB&\
+ sudo /sbin/tc qdisc change dev $dataB root netem delay 1000ms >> $testLogB"
+
+# D: express an Interest for ndn:/P/4, retransmit with new nonce after 200ms, wait 3000ms
+ssh $CTRL_D "echo 'express an Interest for ndn:/P/4' >> $testLogD &\
+ test-nack-consumer -p ndn:/P/4 -r 200 -w 3000 > $logDir/test4.log"
+
+scp $CTRL_D:$logDir/test4.log $logDir/test4.log
+
+echo "Analyzing result of test 4"
+reason=$(grep NACK $logDir/test4.log | awk '{print $NF}')
+NACK_count=$(grep NACK $logDir/test4.log | wc -l)
+nonce2=$(grep "Sending Interest 2:" $logDir/test4.log | awk '{print $NF}')
+NACK_nonce=$(grep NACK $logDir/test4.log | awk '{for(i=1;i<=NF;i++){if($i~/^Nonce/){print $(i+1)}}}'
+)
+
+if [ $NACK_count -ne 1 ] || [[ "$reason" != "NoRoute" ]] || [ "$NACK_nonce" -ne "$nonce2" ]
+then
+ echo "FAIL: A should return exactly one Nack that should carry the latest incoming Nonce \
+ from downstream. Reason should be 'NoRoute'. Test returned $NACK_count Nacks, with nonce &\
+ $NACK_nonce and reason $reason. Check test4 log for test details."
+ clean_up $dataB $dataC
+exit 1
+fi
+
+# cleanup
+clean_up $dataB $dataC
+echo "Test passed successfully"
+exit 0
\ No newline at end of file
diff --git a/test_nack/test_nack.py b/test_nack/test_nack.py
new file mode 100644
index 0000000..465cbc7
--- /dev/null
+++ b/test_nack/test_nack.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python2
+
+import os
+import unittest
+import subprocess
+
+class test_nack(unittest.TestCase):
+ """Test case for testing nack"""
+
+ def setUp(self):
+ print "\nTesting nack with best-route"
+ print "*****************************"
+ os.chdir("test_nack")
+ os.system("mkdir -p logs")
+
+ def tearDown(self):
+ print "********************************"
+ os.chdir("..")
+
+ def test_nack(self):
+ print ">>> test nack <<<"
+
+ ret = subprocess.call(['./nack-test.sh'], shell=True)
+
+ if (ret != 0):
+ self.fail(" >> TEST NACK FAILED")
+ else:
+ print ">> TEST NACK PROCEDURE PASSED SUCCESSFULLY"
+