face: Implementing close operation and many related fixes in TcpFace and TcpChannel

Change-Id: Ib6b751e80454e149bf94f3867663d5e705cbf4a0
refs: #1250, #1248
diff --git a/tests/face/dummy-face.hpp b/tests/face/dummy-face.hpp
index c7b5d76..e78f4cc 100644
--- a/tests/face/dummy-face.hpp
+++ b/tests/face/dummy-face.hpp
@@ -31,6 +31,11 @@
   sendData(const Data& data)
   {
   }
+
+  virtual void
+  close()
+  {
+  }
 };
 
 } // namespace nfd
diff --git a/tests/face/tcp.cpp b/tests/face/tcp.cpp
index 66c0a69..d64d1ce 100644
--- a/tests/face/tcp.cpp
+++ b/tests/face/tcp.cpp
@@ -40,6 +40,8 @@
       bind(&EndToEndFixture::face1_onReceiveInterest, this, _1);
     m_face1->onReceiveData +=
       bind(&EndToEndFixture::face1_onReceiveData, this, _1);
+    m_face1->onFail += 
+      bind(&EndToEndFixture::face1_onFail, this);
 
     this->afterIo();
   }
@@ -69,6 +71,13 @@
   }
 
   void
+  face1_onFail()
+  {
+    m_face1.reset();
+    this->afterIo();
+  }
+
+  void
   channel2_onFaceCreated(const shared_ptr<TcpFace>& newFace)
   {
     BOOST_CHECK(!static_cast<bool>(m_face2));
@@ -77,6 +86,8 @@
       bind(&EndToEndFixture::face2_onReceiveInterest, this, _1);
     m_face2->onReceiveData +=
       bind(&EndToEndFixture::face2_onReceiveData, this, _1);
+    m_face2->onFail += 
+      bind(&EndToEndFixture::face2_onFail, this);
 
     this->afterIo();
   }
@@ -106,6 +117,13 @@
   }
 
   void
+  face2_onFail()
+  {
+    m_face2.reset();
+    this->afterIo();
+  }
+
+  void
   channel_onFaceCreated(const shared_ptr<TcpFace>& newFace)
   {
     m_faces.push_back(newFace);
@@ -230,7 +248,7 @@
   Scheduler scheduler(m_ioService); // to limit the amount of time the test may take
 
   EventId abortEvent =
-    scheduler.scheduleEvent(time::seconds(1),
+    scheduler.scheduleEvent(time::seconds(4),
                             bind(&EndToEndFixture::abortTestCase, this,
                                  "TcpChannel error: cannot connect or cannot accept connection"));
   
@@ -277,7 +295,7 @@
   
   m_ioRemaining = 4; // 2 connects and 2 accepts
   abortEvent = 
-    scheduler.scheduleEvent(time::seconds(1),
+    scheduler.scheduleEvent(time::seconds(4),
                             bind(&EndToEndFixture::abortTestCase, this,
                                  "TcpChannel error: cannot connect or cannot accept multiple connections"));
 
@@ -290,6 +308,61 @@
   BOOST_CHECK_EQUAL(m_faces.size(), 6);
 }
 
+
+BOOST_FIXTURE_TEST_CASE(FaceClosing, EndToEndFixture)
+{
+  TcpChannelFactory factory(m_ioService);
+  Scheduler scheduler(m_ioService); // to limit the amount of time the test may take
+
+  EventId abortEvent =
+    scheduler.scheduleEvent(time::seconds(4),
+                            bind(&EndToEndFixture::abortTestCase, this,
+                                 "TcpChannel error: cannot connect or cannot accept connection"));
+  
+  shared_ptr<TcpChannel> channel1 = factory.create("127.0.0.1", "20070");
+  shared_ptr<TcpChannel> channel2 = factory.create("127.0.0.1", "20071");
+  
+  channel1->listen(bind(&EndToEndFixture::channel1_onFaceCreated,   this, _1),
+                   bind(&EndToEndFixture::channel1_onConnectFailed, this, _1));
+  
+  channel2->connect("127.0.0.1", "20070",
+                    bind(&EndToEndFixture::channel2_onFaceCreated, this, _1),
+                    bind(&EndToEndFixture::channel2_onConnectFailed, this, _1),
+                    time::milliseconds(100)); // very short timeout
+
+  m_ioRemaining = 2;
+  BOOST_REQUIRE_NO_THROW(m_ioService.run());
+  m_ioService.reset();
+  scheduler.cancelEvent(abortEvent);
+
+  BOOST_CHECK_EQUAL(channel1->size(), 1);
+  BOOST_CHECK_EQUAL(channel2->size(), 1);
+
+  abortEvent =
+    scheduler.scheduleEvent(time::seconds(4),
+                            bind(&EndToEndFixture::abortTestCase, this,
+                                 "FaceClosing error: cannot properly close faces"));
+
+  BOOST_REQUIRE(static_cast<bool>(m_face1));
+  BOOST_CHECK(static_cast<bool>(m_face2));
+
+  m_ioRemaining = 2;
+  // just double check that we are calling the virtual method
+  static_pointer_cast<Face>(m_face1)->close();
+  
+  BOOST_REQUIRE_NO_THROW(m_ioService.run());
+  m_ioService.reset();
+  scheduler.cancelEvent(abortEvent);
+
+  // both faces should get closed
+  BOOST_CHECK(!static_cast<bool>(m_face1));
+  BOOST_CHECK(!static_cast<bool>(m_face2));
+  
+  BOOST_CHECK_EQUAL(channel1->size(), 0);
+  BOOST_CHECK_EQUAL(channel2->size(), 0);
+}
+
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace nfd
diff --git a/tests/fw/forwarder.cpp b/tests/fw/forwarder.cpp
index 23d8828..27653bc 100644
--- a/tests/fw/forwarder.cpp
+++ b/tests/fw/forwarder.cpp
@@ -32,6 +32,11 @@
     m_ioService.stop();
   }
 
+  virtual void
+  close()
+  {
+  }
+
   void
   receiveInterest(const Interest& interest)
   {
diff --git a/tests/mgmt/internal-face.cpp b/tests/mgmt/internal-face.cpp
index 0c08190..52eaa23 100644
--- a/tests/mgmt/internal-face.cpp
+++ b/tests/mgmt/internal-face.cpp
@@ -96,6 +96,8 @@
   face->put(testData);
 
   BOOST_REQUIRE(didPutData);
+
+  BOOST_CHECK_THROW(face->close(), InternalFace::Error);
 }
 
 BOOST_FIXTURE_TEST_CASE(SendInterestHitEnd, InternalFaceFixture)