fw: Check out record exists before calculating RTT in ASF

Change-Id: Ie7f2db50b21d314a5df998e4220f64de14861d3e
refs: #3829
diff --git a/daemon/fw/asf-measurements.cpp b/daemon/fw/asf-measurements.cpp
index c98ca86..e8bc8fd 100644
--- a/daemon/fw/asf-measurements.cpp
+++ b/daemon/fw/asf-measurements.cpp
@@ -114,6 +114,12 @@
 {
   // Calculate RTT
   pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(inFace);
+
+  if (outRecord == pitEntry->out_end()) { // no out-record
+    NFD_LOG_TRACE(pitEntry->getInterest() << " dataFrom inFace=" << inFace.getId() << " no-out-record");
+    return;
+  }
+
   time::steady_clock::Duration steadyRtt = time::steady_clock::now() - outRecord->getLastRenewed();
   RttEstimator::Duration durationRtt = time::duration_cast<RttEstimator::Duration>(steadyRtt);
 
diff --git a/tests/daemon/fw/asf-strategy.t.cpp b/tests/daemon/fw/asf-strategy.t.cpp
index 601083d..f3765c3 100644
--- a/tests/daemon/fw/asf-strategy.t.cpp
+++ b/tests/daemon/fw/asf-strategy.t.cpp
@@ -181,6 +181,111 @@
   BOOST_CHECK_GE(linkAD->getFace(nodeA).getCounters().nOutInterests, 2);
 }
 
+BOOST_AUTO_TEST_CASE(NoPitOutRecord)
+{
+  /*                 +---------+
+  *                  |  nodeD  |
+  *                  +---------+
+  *                       |
+  *                       | 80ms
+  *                       |
+  *                       |
+  *                  +---------+
+  *           +----->|  nodeB  |<------+
+  *           |      +---------+       |
+  *      15ms |                        | 16ms
+  *           v                        v
+  *      +---------+              +---------+
+  *      |  nodeA  |--------------|  nodeC  |
+  *      +---------+     14ms      +---------+
+  */
+
+  const Name PRODUCER_PREFIX = "/ndn/edu/nodeD/ping";
+
+  TopologyTester topo;
+  TopologyNode nodeA = topo.addForwarder("A"),
+               nodeB = topo.addForwarder("B"),
+               nodeC = topo.addForwarder("C"),
+               nodeD = topo.addForwarder("D");
+
+  for (TopologyNode node : {nodeA, nodeB, nodeC, nodeD}) {
+    topo.setStrategy<fw::AsfStrategy>(node);
+  }
+
+  shared_ptr<TopologyLink> linkAB = topo.addLink("AB", time::milliseconds(15), {nodeA, nodeB}),
+                           linkAC = topo.addLink("AC", time::milliseconds(14), {nodeA, nodeC}),
+                           linkBC = topo.addLink("BC", time::milliseconds(16), {nodeB, nodeC}),
+                           linkBD = topo.addLink("BD", time::milliseconds(80), {nodeB, nodeD});
+
+  shared_ptr<TopologyAppLink> ping = topo.addAppFace("c", nodeA),
+                        pingServer = topo.addAppFace("p", nodeD, PRODUCER_PREFIX);
+  topo.addEchoProducer(pingServer->getClientFace());
+
+  // Register prefixes
+  topo.registerPrefix(nodeA, linkAB->getFace(nodeA), PRODUCER_PREFIX, 15);
+  topo.registerPrefix(nodeA, linkAC->getFace(nodeA), PRODUCER_PREFIX, 14);
+  topo.registerPrefix(nodeC, linkBC->getFace(nodeC), PRODUCER_PREFIX, 16);
+  topo.registerPrefix(nodeB, linkBD->getFace(nodeB), PRODUCER_PREFIX, 80);
+
+  // Send 6 interest since probes can be scheduled b/w 0-5 seconds
+  for (int i = 1; i < 7; i++) {
+    // Send ping number i
+    Name name(PRODUCER_PREFIX);
+    name.appendTimestamp();
+    shared_ptr<Interest> interest = makeInterest(name);
+    ping->getClientFace().expressInterest(*interest, nullptr, nullptr, nullptr);
+
+    // Don't know when the probe will be triggered since it is random between 0-5 seconds
+    // or whether it will be triggered for this interest
+    int j = 1;
+    while (linkAB->getFace(nodeA).getCounters().nOutInterests != 1) {
+      this->advanceClocks(time::milliseconds(1));
+      ++j;
+      // Probe was not scheduled with this ping interest
+      if (j > 1000) {
+        break;
+      }
+    }
+
+    // Check if probe is sent to B else send another ping
+    if (linkAB->getFace(nodeA).getCounters().nOutInterests == 1) {
+      // B should not have received the probe interest yet
+      BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 0);
+
+      // i-1 interests through B when no probe
+      BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i - 1);
+
+      // After 15ms, B should get the probe interest
+      this->advanceClocks(time::milliseconds(1), time::milliseconds(15));
+      BOOST_CHECK_EQUAL(linkAB->getFace(nodeB).getCounters().nInInterests, 1);
+      BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nOutInterests, i);
+
+      shared_ptr<pit::Entry> pitEntry = topo.getForwarder(nodeB).getPit().find(*interest);
+
+      // Get outRecord associated with face towards D.
+      pit::OutRecordCollection::const_iterator outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB));
+
+      BOOST_CHECK(outRecord != pitEntry->out_end());
+
+      // RTT between B and D
+      this->advanceClocks(time::milliseconds(5), time::milliseconds(160));
+      outRecord = pitEntry->getOutRecord(linkBD->getFace(nodeB));
+
+      BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i);
+
+      BOOST_CHECK(outRecord == pitEntry->out_end());
+
+      // Data is returned for the ping after 15 ms - will result in false measurement
+      // 14+16-15 = 15ms
+      // Since outRecord == pitEntry->out_end()
+      this->advanceClocks(time::milliseconds(1), time::milliseconds(15));
+      BOOST_CHECK_EQUAL(linkBD->getFace(nodeB).getCounters().nInData, i+1);
+
+      break;
+    }
+  }
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestAsfStrategy
 BOOST_AUTO_TEST_SUITE_END() // Fw