Add nfdc and nfd-status integration test

refs: #1388

Change-Id: Ic9b181dfb333a9543b1d319e03713325d70c933e
diff --git a/test_nfdc/README.md b/test_nfdc/README.md
new file mode 100644
index 0000000..25f3d89
--- /dev/null
+++ b/test_nfdc/README.md
@@ -0,0 +1,66 @@
+Test Case - nfdc and nfd-status
+=====================
+
+## Topology ##
+A-B connected on same IPv4 subnet and same IPv6 subnet. Both are NDN nodes.
+
+Script is invoked on host A.
+multi-host.conf defines: CTRL_B, IP4_A1, IP6_A1, IP4_B1, IP6_B1
+
+
+## Description ##
+The script runs 3 test cases:
+A: Test nfdc create / add-nexthop / destroy test case
+B: Test nfdc add-nexthop / remove-nexthop test case
+C: Test nfdc register / unregister test case
+
+In case of a failure, the detailed log can be found under:
+A: test_nfdc/log/nfdc_test_A.log
+B: test_nfdc/log/nfdc_test_B.log
+C: test_nfdc/log/nfdc_test_C.log
+
+## Test case A: Test nfdc create / add-nexthop / destroy test case ##
+Start NFD on host A and B.
+On host A, run the following steps:
+
+1. Execute nfd-status. Check the existence of udp4/udp6/ether multicast faces.
+2. Invoke nfdc create tcp://$IP4_B1. Pause 2 seconds for connection establishment.
+3. Execute nfd-status. Check the existence of new tcp4 face.
+4. Invoke nfdc create tcp://[$IP6_B1]. Pause 2 seconds for connection establishment.
+5. Execute nfd-status. Check the existence of new tcp6 face.
+6. Invoke nfdc create udp://$IP4_B1. Pause 2 seconds for connection establishment.
+7. Execute nfd-status. Check the existence of new udp4 face.
+8. Invoke nfdc create udp://[$IP6_B1]. Pause 2 seconds for connection establishment.
+9. Execute nfd-status. Check the existence of new udp6 face.
+10. Invoke nfdc to add a nexthop for ndn:/test-nfdc with cost 24 toward FaceId created in step 3.
+11. Invoke nfdc to add a nexthop for ndn:/test-nfdc with cost 26 toward FaceId created in step 5.
+12. Invoke nfdc to add a nexthop for ndn:/test-nfdc with cost 14 toward FaceId created in step 7.
+13. Invoke nfdc to add a nexthop for ndn:/test-nfdc with cost 16 toward FaceId created in step 9.
+14. Execute nfd-status. Check the existence of ndn:/test-nfdc FIB entry, and the correctness of cost on nexthop records.
+15. Invoke nfdc to choose BroadcastStrategy for ndn:/ namespace.
+16. Invoke ndn-tlv-ping to send 100 Interests under ndn:/test-nfdc prefix.
+17. Pause 50ms before sending each Interest.
+18. Execute nfd-status. Verify NOutInterests counters of faces created in step 3,5,7,9 are greater than 60.
+19. On host B, execute nfd-status. Check the existence of tcp4, tcp6, udp4, udp6 faces with host A's address. Verify NInInterests counters for those faces are greater than 60.
+20. Invoke nfdc to destroy FaceIds created in step 3,5.
+21. Execute nfd-status. Check the non-existence of tcp4 and tcp6 faces.
+
+## Test case B: Test nfdc add-nexthop / remove-nexthop test case ##
+Start NFD on host A and B.
+On host A, run the following steps:
+
+1. Invoke nfdc add-nexthop -c 44 ndn:/test-nfdc udp4://$IP4_B1. Pause 2 seconds for connection establishment.
+2. Execute nfd-status. Check the existence of new udp4 face. Check the existence of ndn:/test-nfdc FIB entry, and the correctness of cost on nexthop records.
+3. Invoke nfdc to remove the nexthop for FaceId seen in step 3.
+4. Execute nfd-status. Check the non-existence of ndn:/test-nfdc FIB entry.
+
+## Test case C: Test nfdc register / unregister test case ##
+Start NFD on host A and B.
+On host A, run the following steps:
+1. run nrd on host A
+2. Invoke nfdc register -c 55 ndn:/test-nfdc udp4://$IP4_B1. Pause 2 seconds for connection establishment.
+3. Execute nfd-status. Check the existence of new udp4 face. Check the existence of ndn:/test-nfdc FIB entry, and the correctness of cost on nexthop records.
+4. Invoke nfdc to unregister the prefix ndn:/test-nfdc for FaceId seen in step 3.
+5. Execute nfd-status. Check the non-existence of ndn:/test-nfdc FIB entry.
+
+
diff --git a/test_nfdc/__init__.py b/test_nfdc/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test_nfdc/__init__.py
diff --git a/test_nfdc/include.sh b/test_nfdc/include.sh
new file mode 100755
index 0000000..5f1c491
--- /dev/null
+++ b/test_nfdc/include.sh
@@ -0,0 +1,120 @@
+#!/usr/bin/env bash
+NUM_OF_PINGS=10
+
+# clean up - kill nfd
+clean_up() {
+  r=$(ssh $CTRL_B "sudo killall nfd" 2>&1)
+  r=$(sudo killall nfd 2>&1)
+
+  if [[ $testCase == 'C' ]]; then
+    r=$(sudo killall nrd 2>&1)
+  fi
+}
+
+start_nfd() {
+  # start nfd on localhost
+  sudo nfd &> $workdir/logs/nfd.log &
+
+  # start nfd on hostB
+  ssh $CTRL_B "mkdir -p $workdir/logs ; sudo nfd &> $workdir/logs/nfd.log &\
+    sleep 2"
+  sleep 2
+}
+
+start_nrd() {
+  # start nfd on localhost
+  sudo nrd &> $workdir/logs/nrd.log &
+
+  sleep 2
+}
+
+
+check_nfd_status_counters() {
+  faceId=$1
+  faceUri=$2
+  counterType=$3
+  threshold=$4
+
+  outCounter=$(nfd-status | grep -iF 'faceid='$faceId' remote='$faceUri | grep -Po $counterType={[0-9]+ | sed 's/'$counterType'={//')
+
+
+  if [[ $outCounter -lt $threshold ]]; then
+    if [[ "out" = $counterType ]]; then
+      echo "nfd-status: out counter of face=$faceId is less than $threshold " >> $testLog
+      echo "nfd-status is:" >> $testLog
+      nfd-status >> $testLog
+      clean_up
+    fi
+    exit 2
+
+  fi
+
+}
+
+# check for the existence of the face
+# param list: $1 - face Id
+#             $2 - face Uri
+check_nfd_status_face_existence() {
+  faceId=$1
+  faceUri=$2
+
+  # check for the existence of the face
+  # First, get the face line
+  # Second, extract the face id attached to the fetched line
+  face=$(nfd-status | grep -iF remote=$faceUri | grep -Po faceid=\[0-9\]+ | sed 's/faceid=//')
+
+  if [[ "none" != $faceId ]]; then
+    if [[ $face != $faceId ]]; then
+      echo "nfd-status: Failed to find face $faceId of $faceUri" >> $testLog
+      echo "nfd-status is:" >> $testLog
+      nfd-status >> $testLog
+      echo "-1"
+    else
+      echo $face
+    fi
+  else
+    echo $face
+  fi
+
+}
+
+
+# This function checks for the existence of a face and a fib entry
+# including nexthop records
+# param list: $1 - face Id
+#             $2 - face Uri
+#             $3 - prefix
+#             $4 - cost
+check_nfd_status_correctness() {
+  faceId=$1
+  faceUri=$2
+  prefix=$3
+  cost=$4
+
+  face=$(check_nfd_status_face_existence $faceId $faceUri)
+  if [[ "-1" = $face ]]; then
+    clean_up
+    exit 2
+  fi
+
+  # check the existence of prefix FIB entry
+  fibEntry=$(nfd-status | grep $prefix)
+  if [[ -z "$fibEntry" ]]; then
+    echo "nfd-status: Failed to find $prefix in FIB table" >> $testLog
+    echo "nfd-status is:" >> $testLog
+    nfd-status >> $testLog
+    clean_up
+    exit 2
+  fi
+
+  # check the correctness of cost on nexthop records
+  fibEntryCost=$(echo $fibEntry | grep -Po 'faceid='$faceId'.\(cost=[0-9]+\)' | sed 's/faceid='$faceId' //' | sed 's/(cost=//' | sed 's/)//')
+
+  if [[ $cost != $fibEntryCost ]]; then
+    echo "nfd-status: nexthop records are incorrect, expected cost is $cost, cost in records $fibEntryCost" >> $testLog
+    echo "nfd-status is:" >> $testLog
+    nfd-status >> $testLog
+    clean_up
+    exit 2
+  fi
+}
\ No newline at end of file
diff --git a/test_nfdc/nfdc-test.sh b/test_nfdc/nfdc-test.sh
new file mode 100755
index 0000000..24966cd
--- /dev/null
+++ b/test_nfdc/nfdc-test.sh
@@ -0,0 +1,181 @@
+#!/usr/bin/env bash
+source ../multi-host.conf
+source include.sh
+workdir=$(pwd)
+mkdir -p $workdir/logs
+testCase=$1
+testLog=$workdir/logs/nfdc_test_$testCase.log
+
+echo "TEST START" > $testLog
+
+start_nfd
+
+# run test case A: test nfdc create / add-nexthop / destroy test case
+if [[ $testCase == 'A' ]]; then
+
+  # Check for the existence of mcast address udp4://224.0.23.170
+  udp4Mcast=$(nfd-status | grep udp4://224.0.23.170)
+  udp6Mcast=$(nfd-status | grep -iF udp6://[ff00)
+  ethv4=$(nfd-status | grep -iF ether://[01:00:5E:)
+  ethv6=$(nfd-status | grep -iF ether://[33:33:)
+
+  if [[ -z "$udp4Mcast" ]] && [[ -z "$udp6Mcast" ]] && [[ -z "$ethv4" ]] && [[ -z "$ethv6" ]]; then
+    echo "nfd-status: Failed to find udp4/udp6/ether multicast faces" >> $testLog
+    echo "nfd-status is:" >> $testLog
+    nfd-status >> $testLog
+    clean_up
+    exit 2
+  fi
+
+  # Invoke nfdc create tcp://$IP4_B1.
+  faceIdTcp4=$(nfdc create tcp4://$IP4_B1 | grep -Po 'FaceId: .*?,' | sed 's/FaceId: //' | sed 's/,//')
+  sleep 2
+  face=$(check_nfd_status_face_existence $faceIdTcp4 tcp4://$IP4_B1)
+  if [ "-1" = $face ]; then
+    clean_up
+    exit 2
+  fi
+
+  #compress IPv6
+  IP6_B1=$(python -c "import sys; import socket; result = socket.getaddrinfo('$IP6_B1', None); family, socktype, proto, canonname,(address, port, flow_info, scope_id) = result[0]; print address")
+
+  # Invoke nfdc create tcp://$IP6_B1.
+  faceIdTcp6=$(nfdc create tcp6://[$IP6_B1] | grep -Po 'FaceId: .*?,' | sed 's/FaceId: //' | sed 's/,//')
+  sleep 2
+  face=$(check_nfd_status_face_existence $faceIdTcp6 tcp6://[$IP6_B1])
+  if [ "-1" = $face ]; then
+    clean_up
+    exit 2
+  fi
+
+  # Invoke nfdc create udp://$IP4_B1.
+  faceIdUdp4=$(nfdc create udp4://$IP4_B1 | grep -Po 'FaceId: .*?,' | sed 's/FaceId: //' | sed 's/,//')
+  sleep 2
+
+  face=$(check_nfd_status_face_existence $faceIdUdp4 udp4://$IP4_B1)
+  if [ "-1" = $face ]; then
+    clean_up
+    exit 2
+  fi
+
+
+  # Invoke nfdc create udp://$IP6_B1.
+  faceIdUdp6=$(nfdc create udp6://[$IP6_B1] | grep -Po 'FaceId: .*?,' | sed 's/FaceId: //' | sed 's/,//')
+  sleep 2
+  face=$(check_nfd_status_face_existence $faceIdUdp6 udp6://[$IP6_B1])
+  if [ "-1" = $face ]; then
+    clean_up
+    exit 2
+  fi
+
+  # Invoke nfdc to add next hops
+  nfdc add-nexthop -c 24 ndn:/test-nfdc $faceIdTcp4
+  nfdc add-nexthop -c 26 ndn:/test-nfdc $faceIdTcp6
+  nfdc add-nexthop -c 14 ndn:/test-nfdc $faceIdUdp4
+  nfdc add-nexthop -c 16 ndn:/test-nfdc $faceIdUdp6
+
+  # check correctness of add-nexthop -c 24 ndn:/test-nfdc $faceIdTcp4
+  check_nfd_status_correctness $faceIdTcp4 tcp4://$IP4_B1 /test-nfdc 24
+
+  # check correctness of add-nexthop -c 26 ndn:/test-nfdc $faceIdTcp6
+  check_nfd_status_correctness $faceIdTcp6 tcp6://[$IP6_B1] /test-nfdc 26
+
+  # check correctness of add-nexthop -c 14 ndn:/test-nfdc $faceIdUdp4
+  check_nfd_status_correctness $faceIdUdp4 udp4://$IP4_B1 /test-nfdc 14
+
+  # check correctness of add-nexthop -c 16 ndn:/test-nfdc $faceIdUdp6
+  check_nfd_status_correctness $faceIdUdp6 udp6://[$IP6_B1] /test-nfdc 16
+
+  # Invoke nfdc to choose BroadcastStrategy for ndn:/ namespace.
+  nfdc set-strategy ndn:/ ndn:/localhost/nfd/strategy/broadcast
+
+  ndnping ndn:/test-nfdc -c $NUM_OF_PINGS
+
+  check_nfd_status_counters $faceIdTcp4 tcp4://$IP4_B1 out $NUM_OF_PINGS
+  check_nfd_status_counters $faceIdTcp6 tcp6://[$IP6_B1] out $NUM_OF_PINGS
+  check_nfd_status_counters $faceIdUdp4 udp4://$IP4_B1 out $NUM_OF_PINGS
+  check_nfd_status_counters $faceIdUdp6 udp6://[$IP6_B1] out $NUM_OF_PINGS
+
+  ssh $CTRL_B "$workdir/test-B.sh $workdir"
+  if [[ $? -ne 0 ]]; then
+    echo "Failed to verify correctness on node B" >> $testLog
+    clean_up
+    exit 3
+  fi
+
+  nfdc destroy $faceIdTcp4
+  echo "after nfdc destroy $faceIdTcp4" >> $testLog
+  face=$(check_nfd_status_face_existence $faceIdTcp4 tcp4://$IP4_B1)
+  if [ "-1" != $face ]; then
+    echo "face $faceIdTcp4 still exists after nfdc destory" >> $testLog
+    clean_up
+    exit 2
+  fi
+
+  nfdc destroy $faceIdTcp6
+  echo "after nfdc destroy $faceIdTcp6" >> $testLog
+  face=$(check_nfd_status_face_existence $faceIdTcp6 tcp6://[$IP6_B1])
+  if [ "-1" != $face ]; then
+    clean_up
+    exit 2
+  fi
+
+  clean_up
+
+# run test case B: nfdc add-nexthop / remove-nexthop test case
+elif [[ $testCase == 'B' ]]; then
+
+  # Invoke nfdc to add next hop
+  faceId=$(nfdc add-nexthop -c 44 ndn:/test-nfdc udp4://$IP4_B1 | grep -Po 'FaceId: .*?,' | sed 's/FaceId: //' | sed 's/,//')
+  if [[ "ERROR" == $faceId* ]]
+  then
+    echo "nfdc: Failed to add nexthop for $IP4_B1" >> $testLog
+    clean_up
+    exit 1
+  fi
+
+  check_nfd_status_correctness $faceId udp4://$IP4_B1 /test-nfdc 44
+
+  # invoke nfdc to remove the nexthop for created FaceId
+  removeNextHop=$(nfdc remove-nexthop ndn:/test-nfdc $faceId)
+
+  # check the existence of ndn:/test-nfdc FIB entry
+  fibEntry=$(nfd-status | grep /test-nfdc)
+  if [ ! -z "$fibEntry" ]; then
+    echo "nfd-status: Failed to delete ndn:/test-nfdc FIB entry" >> $testLog
+    clean_up
+    exit 2
+  fi
+  clean_up
+
+# run test case C: test nfdc register / unregister test case
+elif [[ $testCase == 'C' ]]; then
+
+  # run nrd
+  start_nrd
+
+  # Invoke nfdc register a new prefix
+  faceId=$(nfdc register -c 55 ndn:/test-nfdc udp4://$IP4_B1 | grep -Po 'FaceId: .*?,' | sed 's/FaceId: //' | sed 's/,//')
+  if [[ "ERROR" == $faceId* ]]; then
+    echo "nfdc: Failed to register ndn:/test-nfdc for $IP4_B1" >> $testLog
+    clean_up
+    exit 1
+  fi
+  check_nfd_status_correctness $faceId udp4://$IP4_B1 /test-nfdc 55
+
+  # Invoke nfdc to unregister the prefix
+  unregisterPrefix=$(nfdc unregister ndn:/test-nfdc $faceId)
+
+  # check the existence of ndn:/test-nfdc FIB entry
+  fibEntry=$(nfd-status | grep /test-nfdc)
+  if [ ! -z "$fibEntry" ]; then
+    echo "nfd-status: Failed to unregister prefix" >> $testLog
+    clean_up
+    exit 2
+  fi
+  clean_up
+fi
+
+clean_up
+exit 0
+
diff --git a/test_nfdc/test-B.sh b/test_nfdc/test-B.sh
new file mode 100755
index 0000000..fad09d0
--- /dev/null
+++ b/test_nfdc/test-B.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+workDir=$1
+source $workDir/../multi-host.conf
+source $workDir/include.sh
+
+#compress IPv6
+IP6_A1=$(python -c "import sys; import socket; result = socket.getaddrinfo('$IP6_A1', None); family, socktype, proto, canonname,(address, port, flow_info, scope_id) = result[0]; print address")
+
+remoteFaceIdUdp4=$(check_nfd_status_face_existence none udp4://$IP4_A1)
+if [[ -z "$remoteFaceIdUdp4" ]]; then
+  exit 3
+fi
+
+remoteFaceIdTcp4=$(check_nfd_status_face_existence none tcp4://$IP4_A1)
+if [[ -z "$remoteFaceIdTcp4" ]]; then
+  exit 3
+fi
+
+remoteFaceIdUdp6=$(check_nfd_status_face_existence none udp6://[$IP6_A1])
+if [[ -z "$remoteFaceIdUdp6" ]]; then
+  exit 3
+fi
+
+remoteFaceIdTcp6=$(check_nfd_status_face_existence none tcp6://[$IP6_A1])
+if [[ -z "$remoteFaceIdTcp6" ]]; then
+  exit 3
+fi
+
+check_nfd_status_counters $remoteFaceIdUdp4 udp4://$IP4_A1 'in' $NUM_OF_PINGS
+if [[ $? -ne 0 ]]; then
+  exit 3
+fi
+
+check_nfd_status_counters $remoteFaceIdTcp4 tcp4://$IP4_A1 'in' $NUM_OF_PINGS
+if [[ $? -ne 0 ]]; then
+  exit 3
+fi
+
+check_nfd_status_counters $remoteFaceIdUdp6 udp6://[$IP6_A1] 'in' $NUM_OF_PINGS
+if [[ $? -ne 0 ]]; then
+  exit 3
+fi
+
+check_nfd_status_counters $remoteFaceIdTcp6 tcp6://[$IP6_A1] 'in' $NUM_OF_PINGS
+if [[ $? -ne 0 ]]; then
+  exit 3
+fi
+
+
+exit 0
diff --git a/test_nfdc/test_nfdc.py b/test_nfdc/test_nfdc.py
new file mode 100644
index 0000000..79afcc4
--- /dev/null
+++ b/test_nfdc/test_nfdc.py
@@ -0,0 +1,48 @@
+#!/usr/bin/python2
+# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+#
+# Copyright (C) 2014 Washington University in St. Louis
+# Author: Hila Ben Abraham <hila@wustl.edu>
+# See COPYING for copyright and distribution information.
+#
+
+import os
+import unittest
+import subprocess
+
+class test_nfdc(unittest.TestCase):
+    """Test case for testing nfdc and nfd-status applications"""
+
+    def setUp(self):
+        print "\nTesting nfdc and nfd-status"
+        print "*****************************"
+        os.chdir("test_nfdc")
+        os.system("mkdir -p logs")
+
+    def tearDown(self):
+        print "********************************"
+        os.chdir("..")
+
+    def test_nfdc_A(self):
+        print ">>> TEST CASE A: test nfdc create / add-nexthop / destroy test case <<<"
+        ret = subprocess.call(["./nfdc-test.sh A"], shell=True)
+        if ret != 0:
+            self.fail(" >> TEST CASE A FAILED")
+        else:
+            print ">> TEST CASE A PASSED SUCCESSFULLY"
+
+    def test_nfdc_B(self):
+        print ">>> TEST CASE B: test nfdc add-nexthop / remove-nexthop test case <<<"
+        ret = subprocess.call(["./nfdc-test.sh B"], shell=True)
+        if ret != 0:
+            self.fail(" >> TEST CASE B FAILED")
+        else:
+            print ">> TEST CASE B PASSED SUCCESSFULLY"
+
+    def test_nfdc_C(self):
+        print ">>> TEST CASE C: test nfdc register / unregister test case <<<"
+        ret = subprocess.call(["./nfdc-test.sh C"], shell=True)
+        if ret != 0:
+            self.fail(" >> TEST CASE C FAILED")
+        else:
+            print ">> TEST CASE C PASSED SUCCESSFULLY"
\ No newline at end of file