tests: merge TcpChannel and UdpChannel test suites using test case templates

Change-Id: Id56bb036db53494d4d250f9ea68a3ded78b33ddb
Refs: #3514
diff --git a/tests/daemon/face/channel-fixture.hpp b/tests/daemon/face/channel-fixture.hpp
new file mode 100644
index 0000000..a8e619d
--- /dev/null
+++ b/tests/daemon/face/channel-fixture.hpp
@@ -0,0 +1,109 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TESTS_DAEMON_FACE_CHANNEL_FIXTURE_HPP
+#define NFD_TESTS_DAEMON_FACE_CHANNEL_FIXTURE_HPP
+
+#include "face/channel.hpp"
+#include "tests/limited-io.hpp"
+
+#include <type_traits>
+
+namespace nfd {
+namespace tests {
+
+template<class ChannelT, class EndpointT>
+class ChannelFixture : public BaseFixture
+{
+  static_assert(std::is_base_of<Channel, ChannelT>::value,
+                "ChannelFixture must be instantiated with a type derived from Channel");
+
+public:
+  virtual
+  ~ChannelFixture() = default;
+
+  static void
+  unexpectedFailure(uint32_t status, const std::string& reason)
+  {
+    BOOST_FAIL("No error expected, but got: [" << status << ": " << reason << "]");
+  }
+
+protected:
+  uint16_t
+  getNextPort()
+  {
+    return m_nextPort++;
+  }
+
+  virtual unique_ptr<ChannelT>
+  makeChannel()
+  {
+    BOOST_THROW_EXCEPTION(std::logic_error("unimplemented"));
+  }
+
+  /**
+   * if port == 0, use the port number returned by getNextPort()
+   */
+  virtual unique_ptr<ChannelT>
+  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0)
+  {
+    BOOST_THROW_EXCEPTION(std::logic_error("unimplemented"));
+  }
+
+  void
+  listen(const boost::asio::ip::address& addr)
+  {
+    listenerEp = EndpointT{addr, 7030};
+    listenerChannel = makeChannel(addr, 7030);
+    listenerChannel->listen(
+      [this] (const shared_ptr<Face>& newFace) {
+        BOOST_REQUIRE(newFace != nullptr);
+        connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
+        listenerFaces.push_back(newFace);
+        limitedIo.afterOp();
+      },
+      ChannelFixture::unexpectedFailure);
+  }
+
+  virtual void
+  connect(ChannelT&)
+  {
+    BOOST_THROW_EXCEPTION(std::logic_error("unimplemented"));
+  }
+
+protected:
+  LimitedIo limitedIo;
+  EndpointT listenerEp;
+  unique_ptr<ChannelT> listenerChannel;
+  std::vector<shared_ptr<Face>> listenerFaces;
+
+private:
+  uint16_t m_nextPort = 7050;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_CHANNEL_FIXTURE_HPP
diff --git a/tests/daemon/face/factory-test-common.hpp b/tests/daemon/face/factory-test-common.hpp
index e842ced..f7d9535 100644
--- a/tests/daemon/face/factory-test-common.hpp
+++ b/tests/daemon/face/factory-test-common.hpp
@@ -48,7 +48,7 @@
            const CreateFaceExpectedResult& expected)
 {
   factory.createFace(uri, persistency, wantLocalFieldsEnabled,
-                     [expected] (const shared_ptr<Face>& newFace) {
+                     [expected] (const shared_ptr<Face>&) {
                        BOOST_CHECK_EQUAL(CreateFaceExpectedResult::SUCCESS, expected.result);
                      },
                      [expected] (uint32_t actualStatus, const std::string& actualReason) {
@@ -58,11 +58,6 @@
                      });
 }
 
-inline void
-failIfError(uint32_t status, const std::string& reason) {
-  BOOST_FAIL("No error expected, but got: [" << status << ": " << reason << "]");
-}
-
 } // namespace tests
 } // namespace nfd
 
diff --git a/tests/daemon/face/tcp-channel-fixture.hpp b/tests/daemon/face/tcp-channel-fixture.hpp
new file mode 100644
index 0000000..869a76c
--- /dev/null
+++ b/tests/daemon/face/tcp-channel-fixture.hpp
@@ -0,0 +1,70 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TESTS_DAEMON_FACE_TCP_CHANNEL_FIXTURE_HPP
+#define NFD_TESTS_DAEMON_FACE_TCP_CHANNEL_FIXTURE_HPP
+
+#include "face/tcp-channel.hpp"
+
+#include "channel-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+class TcpChannelFixture : public ChannelFixture<TcpChannel, tcp::Endpoint>
+{
+protected:
+  virtual unique_ptr<TcpChannel>
+  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
+  {
+    if (port == 0)
+      port = getNextPort();
+
+    return make_unique<TcpChannel>(tcp::Endpoint(addr, port));
+  }
+
+  virtual void
+  connect(TcpChannel& channel) final
+  {
+    g_io.post([&] {
+      channel.connect(listenerEp, false,
+        [this] (const shared_ptr<Face>& newFace) {
+          BOOST_REQUIRE(newFace != nullptr);
+          connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
+          clientFaces.push_back(newFace);
+          limitedIo.afterOp();
+        },
+        ChannelFixture::unexpectedFailure);
+    });
+  }
+
+protected:
+  std::vector<shared_ptr<Face>> clientFaces;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_TCP_CHANNEL_FIXTURE_HPP
diff --git a/tests/daemon/face/tcp-channel.t.cpp b/tests/daemon/face/tcp-channel.t.cpp
index f8dae5d..6b7dc36 100644
--- a/tests/daemon/face/tcp-channel.t.cpp
+++ b/tests/daemon/face/tcp-channel.t.cpp
@@ -23,11 +23,9 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "face/tcp-channel.hpp"
+#include "tcp-channel-fixture.hpp"
 
 #include "test-ip.hpp"
-#include "tests/limited-io.hpp"
-#include "factory-test-common.hpp"
 
 #include <boost/mpl/vector.hpp>
 
@@ -35,140 +33,10 @@
 namespace tests {
 
 BOOST_AUTO_TEST_SUITE(Face)
-
-using nfd::Face;
-namespace ip = boost::asio::ip;
-
-typedef boost::mpl::vector<ip::address_v4,
-                           ip::address_v6> AddressFamilies;
-
-class TcpChannelFixture : public BaseFixture
-{
-protected:
-  TcpChannelFixture()
-    : m_nextPort(7050)
-  {
-  }
-
-  unique_ptr<TcpChannel>
-  makeChannel(const ip::address& addr, uint16_t port = 0)
-  {
-    if (port == 0)
-      port = m_nextPort++;
-
-    return make_unique<TcpChannel>(tcp::Endpoint(addr, port));
-  }
-
-  void
-  listen(const ip::address& addr)
-  {
-    listenerEp = tcp::Endpoint(addr, 7030);
-    listenerChannel = makeChannel(addr, 7030);
-    listenerChannel->listen(
-      [this] (const shared_ptr<Face>& newFace) {
-        BOOST_REQUIRE(newFace != nullptr);
-        connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
-        listenerFaces.push_back(newFace);
-        limitedIo.afterOp();
-      },
-      &failIfError);
-  }
-
-  void
-  connect(TcpChannel& channel)
-  {
-    channel.connect(listenerEp, false,
-      [this] (const shared_ptr<Face>& newFace) {
-        BOOST_REQUIRE(newFace != nullptr);
-        connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
-        clientFaces.push_back(newFace);
-        limitedIo.afterOp();
-      },
-      &failIfError);
-  }
-
-protected:
-  LimitedIo limitedIo;
-  tcp::Endpoint listenerEp;
-  unique_ptr<TcpChannel> listenerChannel;
-  std::vector<shared_ptr<Face>> listenerFaces;
-  std::vector<shared_ptr<Face>> clientFaces;
-
-private:
-  uint16_t m_nextPort;
-};
-
 BOOST_FIXTURE_TEST_SUITE(TestTcpChannel, TcpChannelFixture)
 
-BOOST_AUTO_TEST_CASE_TEMPLATE(Uri, A, AddressFamilies)
-{
-  tcp::Endpoint ep(A::loopback(), 7050);
-  auto channel = makeChannel(ep.address(), ep.port());
-  BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(ep));
-}
-
-BOOST_AUTO_TEST_CASE(Listen)
-{
-  auto channel = makeChannel(ip::address_v4());
-  BOOST_CHECK_EQUAL(channel->isListening(), false);
-
-  channel->listen(nullptr, nullptr);
-  BOOST_CHECK_EQUAL(channel->isListening(), true);
-
-  // listen() is idempotent
-  BOOST_CHECK_NO_THROW(channel->listen(nullptr, nullptr));
-  BOOST_CHECK_EQUAL(channel->isListening(), true);
-}
-
-BOOST_AUTO_TEST_CASE_TEMPLATE(MultipleAccepts, A, AddressFamilies)
-{
-  auto address = getTestIp<A>(LoopbackAddress::Yes);
-  SKIP_IF_IP_UNAVAILABLE(address);
-  this->listen(address);
-
-  BOOST_CHECK_EQUAL(listenerChannel->isListening(), true);
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
-
-  auto ch1 = makeChannel(A());
-  this->connect(*ch1);
-
-  BOOST_CHECK(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 1);
-  BOOST_CHECK_EQUAL(ch1->size(), 1);
-  BOOST_CHECK_EQUAL(ch1->isListening(), false);
-
-  auto ch2 = makeChannel(A());
-  auto ch3 = makeChannel(A());
-  this->connect(*ch2);
-  this->connect(*ch3);
-
-  BOOST_CHECK(limitedIo.run(4, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
-  BOOST_CHECK_EQUAL(ch1->size(), 1);
-  BOOST_CHECK_EQUAL(ch2->size(), 1);
-  BOOST_CHECK_EQUAL(ch3->size(), 1);
-  BOOST_CHECK_EQUAL(clientFaces.size(), 3);
-
-  // check face persistency
-  for (const auto& face : listenerFaces) {
-    BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
-  }
-  for (const auto& face : clientFaces) {
-    BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
-  }
-
-  // connect twice to the same endpoint
-  this->connect(*ch3);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
-  BOOST_CHECK_EQUAL(ch1->size(), 1);
-  BOOST_CHECK_EQUAL(ch2->size(), 1);
-  BOOST_CHECK_EQUAL(ch3->size(), 1);
-  BOOST_CHECK_EQUAL(clientFaces.size(), 4);
-  BOOST_CHECK_EQUAL(clientFaces.at(2), clientFaces.at(3));
-}
+using AddressFamilies = boost::mpl::vector<boost::asio::ip::address_v4,
+                                           boost::asio::ip::address_v6>;
 
 BOOST_AUTO_TEST_CASE_TEMPLATE(ConnectTimeout, A, AddressFamilies)
 {
@@ -176,44 +44,22 @@
   SKIP_IF_IP_UNAVAILABLE(address);
   // do not listen
 
-  auto channel = makeChannel(A());
+  auto channel = this->makeChannel(A());
   channel->connect(tcp::Endpoint(address, 7040), false,
-    [this] (const shared_ptr<Face>&) {
+    [this] (const shared_ptr<nfd::Face>&) {
       BOOST_FAIL("Connect succeeded when it should have failed");
       this->limitedIo.afterOp();
     },
-    [this] (uint32_t status, const std::string& reason) {
+    [this] (uint32_t, const std::string& reason) {
       BOOST_CHECK_EQUAL(reason.empty(), false);
       this->limitedIo.afterOp();
     },
     time::seconds(1));
 
-  BOOST_CHECK(limitedIo.run(1, time::seconds(2)) == LimitedIo::EXCEED_OPS);
+  BOOST_CHECK_EQUAL(this->limitedIo.run(1, time::seconds(2)), LimitedIo::EXCEED_OPS);
   BOOST_CHECK_EQUAL(channel->size(), 0);
 }
 
-BOOST_AUTO_TEST_CASE_TEMPLATE(FaceClosure, A, AddressFamilies)
-{
-  auto address = getTestIp<A>(LoopbackAddress::Yes);
-  SKIP_IF_IP_UNAVAILABLE(address);
-  this->listen(address);
-
-  auto clientChannel = makeChannel(A());
-  this->connect(*clientChannel);
-
-  BOOST_CHECK(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 1);
-  BOOST_CHECK_EQUAL(clientChannel->size(), 1);
-
-  clientFaces.at(0)->close();
-
-  BOOST_CHECK(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
-  BOOST_CHECK_EQUAL(clientChannel->size(), 0);
-}
-
 BOOST_AUTO_TEST_SUITE_END() // TestTcpChannel
 BOOST_AUTO_TEST_SUITE_END() // Face
 
diff --git a/tests/daemon/face/tcp-udp-channel.t.cpp b/tests/daemon/face/tcp-udp-channel.t.cpp
new file mode 100644
index 0000000..eebf790
--- /dev/null
+++ b/tests/daemon/face/tcp-udp-channel.t.cpp
@@ -0,0 +1,151 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tcp-channel-fixture.hpp"
+#include "udp-channel-fixture.hpp"
+
+#include "test-ip.hpp"
+
+#include <boost/mpl/vector.hpp>
+
+namespace nfd {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(Face)
+BOOST_AUTO_TEST_SUITE(TestTcpUdpChannel)
+
+template<typename F, typename A>
+struct FixtureAndAddress
+{
+  using Fixture = F;
+  using Address = A;
+};
+
+using FixtureAndAddressList = boost::mpl::vector<
+  FixtureAndAddress<TcpChannelFixture, boost::asio::ip::address_v4>,
+  FixtureAndAddress<TcpChannelFixture, boost::asio::ip::address_v6>,
+  FixtureAndAddress<UdpChannelFixture, boost::asio::ip::address_v4>,
+  FixtureAndAddress<UdpChannelFixture, boost::asio::ip::address_v6>
+>;
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Uri, T, FixtureAndAddressList, T::Fixture)
+{
+  decltype(this->listenerEp) ep(T::Address::loopback(), 7050);
+  auto channel = this->makeChannel(ep.address(), ep.port());
+  BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(ep));
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(Listen, T, FixtureAndAddressList, T::Fixture)
+{
+  auto channel = this->makeChannel(typename T::Address());
+  BOOST_CHECK_EQUAL(channel->isListening(), false);
+
+  channel->listen(nullptr, nullptr);
+  BOOST_CHECK_EQUAL(channel->isListening(), true);
+
+  // listen() is idempotent
+  BOOST_CHECK_NO_THROW(channel->listen(nullptr, nullptr));
+  BOOST_CHECK_EQUAL(channel->isListening(), true);
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(MultipleAccepts, T, FixtureAndAddressList, T::Fixture)
+{
+  auto address = getTestIp<typename T::Address>(LoopbackAddress::Yes);
+  SKIP_IF_IP_UNAVAILABLE(address);
+  this->listen(address);
+
+  BOOST_CHECK_EQUAL(this->listenerChannel->isListening(), true);
+  BOOST_CHECK_EQUAL(this->listenerChannel->size(), 0);
+
+  auto ch1 = this->makeChannel(typename T::Address());
+  this->connect(*ch1);
+
+  BOOST_CHECK_EQUAL(this->limitedIo.run(2, time::seconds(1)), LimitedIo::EXCEED_OPS);
+
+  BOOST_CHECK_EQUAL(this->listenerChannel->size(), 1);
+  BOOST_CHECK_EQUAL(ch1->size(), 1);
+  BOOST_CHECK_EQUAL(ch1->isListening(), false);
+
+  auto ch2 = this->makeChannel(typename T::Address());
+  auto ch3 = this->makeChannel(typename T::Address());
+  this->connect(*ch2);
+  this->connect(*ch3);
+
+  BOOST_CHECK_EQUAL(this->limitedIo.run(4, time::seconds(2)), LimitedIo::EXCEED_OPS);
+
+  BOOST_CHECK_EQUAL(this->listenerChannel->size(), 3);
+  BOOST_CHECK_EQUAL(ch1->size(), 1);
+  BOOST_CHECK_EQUAL(ch2->size(), 1);
+  BOOST_CHECK_EQUAL(ch3->size(), 1);
+  BOOST_CHECK_EQUAL(this->clientFaces.size(), 3);
+
+  // check face persistency
+  for (const auto& face : this->listenerFaces) {
+    BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
+  }
+  for (const auto& face : this->clientFaces) {
+    BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
+  }
+
+  // connect twice to the same endpoint
+  this->connect(*ch3);
+
+  BOOST_CHECK_EQUAL(this->limitedIo.run(1, time::seconds(1)), LimitedIo::EXCEED_OPS);
+
+  BOOST_CHECK_EQUAL(this->listenerChannel->size(), 3);
+  BOOST_CHECK_EQUAL(ch1->size(), 1);
+  BOOST_CHECK_EQUAL(ch2->size(), 1);
+  BOOST_CHECK_EQUAL(ch3->size(), 1);
+  BOOST_CHECK_EQUAL(this->clientFaces.size(), 4);
+  BOOST_CHECK_EQUAL(this->clientFaces.at(2), this->clientFaces.at(3));
+}
+
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(FaceClosure, T, FixtureAndAddressList, T::Fixture)
+{
+  auto address = getTestIp<typename T::Address>(LoopbackAddress::Yes);
+  SKIP_IF_IP_UNAVAILABLE(address);
+  this->listen(address);
+
+  auto clientChannel = this->makeChannel(typename T::Address());
+  this->connect(*clientChannel);
+
+  BOOST_CHECK_EQUAL(this->limitedIo.run(2, time::seconds(1)), LimitedIo::EXCEED_OPS);
+
+  BOOST_CHECK_EQUAL(this->listenerChannel->size(), 1);
+  BOOST_CHECK_EQUAL(clientChannel->size(), 1);
+
+  this->clientFaces.at(0)->close();
+
+  BOOST_CHECK_EQUAL(this->limitedIo.run(2, time::seconds(5)), LimitedIo::EXCEED_OPS);
+
+  BOOST_CHECK_EQUAL(this->listenerChannel->size(), 0);
+  BOOST_CHECK_EQUAL(clientChannel->size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestTcpUdpChannel
+BOOST_AUTO_TEST_SUITE_END() // Face
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/face/udp-channel-fixture.hpp b/tests/daemon/face/udp-channel-fixture.hpp
new file mode 100644
index 0000000..5e76be1
--- /dev/null
+++ b/tests/daemon/face/udp-channel-fixture.hpp
@@ -0,0 +1,73 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014-2016,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NFD_TESTS_DAEMON_FACE_UDP_CHANNEL_FIXTURE_HPP
+#define NFD_TESTS_DAEMON_FACE_UDP_CHANNEL_FIXTURE_HPP
+
+#include "face/udp-channel.hpp"
+#include "face/transport.hpp"
+
+#include "channel-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+class UdpChannelFixture : public ChannelFixture<UdpChannel, udp::Endpoint>
+{
+protected:
+  virtual unique_ptr<UdpChannel>
+  makeChannel(const boost::asio::ip::address& addr, uint16_t port = 0) final
+  {
+    if (port == 0)
+      port = getNextPort();
+
+    return make_unique<UdpChannel>(udp::Endpoint(addr, port), time::seconds(2));
+  }
+
+  virtual void
+  connect(UdpChannel& channel) final
+  {
+    g_io.post([&] {
+      channel.connect(listenerEp, ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+        [this] (const shared_ptr<Face>& newFace) {
+          BOOST_REQUIRE(newFace != nullptr);
+          connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
+          clientFaces.push_back(newFace);
+          face::Transport::Packet pkt(ndn::encoding::makeStringBlock(300, "hello"));
+          newFace->getTransport()->send(std::move(pkt));
+          limitedIo.afterOp();
+        },
+        ChannelFixture::unexpectedFailure);
+    });
+  }
+
+protected:
+  std::vector<shared_ptr<Face>> clientFaces;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_UDP_CHANNEL_FIXTURE_HPP
diff --git a/tests/daemon/face/udp-channel.t.cpp b/tests/daemon/face/udp-channel.t.cpp
deleted file mode 100644
index 1438b88..0000000
--- a/tests/daemon/face/udp-channel.t.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2014-2016,  Regents of the University of California,
- *                           Arizona Board of Regents,
- *                           Colorado State University,
- *                           University Pierre & Marie Curie, Sorbonne University,
- *                           Washington University in St. Louis,
- *                           Beijing Institute of Technology,
- *                           The University of Memphis.
- *
- * This file is part of NFD (Named Data Networking Forwarding Daemon).
- * See AUTHORS.md for complete list of NFD authors and contributors.
- *
- * NFD is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
- * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- * PURPOSE.  See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "face/udp-channel.hpp"
-#include "face/transport.hpp"
-
-#include "test-ip.hpp"
-#include "tests/limited-io.hpp"
-#include "factory-test-common.hpp"
-
-#include <boost/mpl/vector.hpp>
-
-namespace nfd {
-namespace tests {
-
-BOOST_AUTO_TEST_SUITE(Face)
-
-using nfd::Face;
-namespace ip = boost::asio::ip;
-
-typedef boost::mpl::vector<ip::address_v4,
-                           ip::address_v6> AddressFamilies;
-
-class UdpChannelFixture : public BaseFixture
-{
-protected:
-  UdpChannelFixture()
-    : nextPort(7050)
-  {
-  }
-
-  unique_ptr<UdpChannel>
-  makeChannel(const ip::address& addr, uint16_t port = 0)
-  {
-    if (port == 0)
-      port = nextPort++;
-
-    return make_unique<UdpChannel>(udp::Endpoint(addr, port), time::seconds(2));
-  }
-
-  void
-  listen(const ip::address& addr)
-  {
-    listenerEp = udp::Endpoint(addr, 7030);
-    listenerChannel = makeChannel(addr, 7030);
-    listenerChannel->listen(
-      [this] (const shared_ptr<Face>& newFace) {
-        BOOST_REQUIRE(newFace != nullptr);
-        connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
-        listenerFaces.push_back(newFace);
-        limitedIo.afterOp();
-      },
-      &failIfError);
-  }
-
-  void
-  connect(UdpChannel& channel)
-  {
-    g_io.post([&] {
-      channel.connect(listenerEp, ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-        [this] (const shared_ptr<Face>& newFace) {
-          BOOST_REQUIRE(newFace != nullptr);
-          connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
-          clientFaces.push_back(newFace);
-          face::Transport::Packet pkt(ndn::encoding::makeStringBlock(300, "hello"));
-          newFace->getTransport()->send(std::move(pkt));
-          limitedIo.afterOp();
-        },
-        &failIfError);
-    });
-  }
-
-protected:
-  LimitedIo limitedIo;
-  udp::Endpoint listenerEp;
-  unique_ptr<UdpChannel> listenerChannel;
-  std::vector<shared_ptr<Face>> listenerFaces;
-  std::vector<shared_ptr<Face>> clientFaces;
-
-private:
-  uint16_t nextPort;
-};
-
-BOOST_FIXTURE_TEST_SUITE(TestUdpChannel, UdpChannelFixture)
-
-BOOST_AUTO_TEST_CASE_TEMPLATE(Uri, A, AddressFamilies)
-{
-  udp::Endpoint ep(A::loopback(), 7050);
-  auto channel = makeChannel(ep.address(), ep.port());
-  BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(ep));
-}
-
-BOOST_AUTO_TEST_CASE(Listen)
-{
-  auto channel = makeChannel(ip::address_v4());
-  BOOST_CHECK_EQUAL(channel->isListening(), false);
-
-  channel->listen(nullptr, nullptr);
-  BOOST_CHECK_EQUAL(channel->isListening(), true);
-
-  // listen() is idempotent
-  BOOST_CHECK_NO_THROW(channel->listen(nullptr, nullptr));
-  BOOST_CHECK_EQUAL(channel->isListening(), true);
-}
-
-BOOST_AUTO_TEST_CASE_TEMPLATE(MultipleAccepts, A, AddressFamilies)
-{
-  auto address = getTestIp<A>(LoopbackAddress::Yes);
-  SKIP_IF_IP_UNAVAILABLE(address);
-  this->listen(address);
-
-  BOOST_CHECK_EQUAL(listenerChannel->isListening(), true);
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
-
-  auto ch1 = makeChannel(A());
-  this->connect(*ch1);
-
-  BOOST_CHECK(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 1);
-  BOOST_CHECK_EQUAL(ch1->size(), 1);
-  BOOST_CHECK_EQUAL(ch1->isListening(), false);
-
-  auto ch2 = makeChannel(A());
-  auto ch3 = makeChannel(A());
-  this->connect(*ch2);
-  this->connect(*ch3);
-
-  BOOST_CHECK(limitedIo.run(4, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
-  BOOST_CHECK_EQUAL(ch1->size(), 1);
-  BOOST_CHECK_EQUAL(ch2->size(), 1);
-  BOOST_CHECK_EQUAL(ch3->size(), 1);
-  BOOST_CHECK_EQUAL(clientFaces.size(), 3);
-
-  // check face persistency
-  for (const auto& face : listenerFaces) {
-    BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
-  }
-  for (const auto& face : clientFaces) {
-    BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_PERSISTENT);
-  }
-
-  // connect twice to the same endpoint
-  this->connect(*ch3);
-
-  BOOST_CHECK(limitedIo.run(1, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
-  BOOST_CHECK_EQUAL(ch1->size(), 1);
-  BOOST_CHECK_EQUAL(ch2->size(), 1);
-  BOOST_CHECK_EQUAL(ch3->size(), 1);
-  BOOST_CHECK_EQUAL(clientFaces.size(), 4);
-  BOOST_CHECK_EQUAL(clientFaces.at(2), clientFaces.at(3));
-}
-
-BOOST_AUTO_TEST_CASE_TEMPLATE(FaceClosure, A, AddressFamilies)
-{
-  auto address = getTestIp<A>(LoopbackAddress::Yes);
-  SKIP_IF_IP_UNAVAILABLE(address);
-  this->listen(address);
-
-  auto clientChannel = makeChannel(A());
-  this->connect(*clientChannel);
-
-  BOOST_CHECK(limitedIo.run(2, time::seconds(1)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 1);
-  BOOST_CHECK_EQUAL(clientChannel->size(), 1);
-
-  clientFaces.at(0)->close();
-
-  BOOST_CHECK(limitedIo.run(2, time::seconds(5)) == LimitedIo::EXCEED_OPS);
-
-  BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
-  BOOST_CHECK_EQUAL(clientChannel->size(), 0);
-}
-
-BOOST_AUTO_TEST_SUITE_END() // TestUdpChannel
-BOOST_AUTO_TEST_SUITE_END() // Face
-
-} // namespace tests
-} // namespace nfd
diff --git a/tests/daemon/face/unix-stream-channel.t.cpp b/tests/daemon/face/unix-stream-channel.t.cpp
index 37eae6d..dcd0270 100644
--- a/tests/daemon/face/unix-stream-channel.t.cpp
+++ b/tests/daemon/face/unix-stream-channel.t.cpp
@@ -25,8 +25,7 @@
 
 #include "face/unix-stream-channel.hpp"
 
-#include "tests/limited-io.hpp"
-#include "factory-test-common.hpp"
+#include "channel-fixture.hpp"
 
 #include <boost/filesystem.hpp>
 #include <fstream>
@@ -34,22 +33,19 @@
 namespace nfd {
 namespace tests {
 
-BOOST_AUTO_TEST_SUITE(Face)
-
-using nfd::Face;
 namespace fs = boost::filesystem;
 namespace local = boost::asio::local;
 
-class UnixStreamChannelFixture : public BaseFixture
+class UnixStreamChannelFixture : public ChannelFixture<UnixStreamChannel, unix_stream::Endpoint>
 {
 protected:
   UnixStreamChannelFixture()
-    : listenerEp("nfd-test-unix-stream-channel.sock")
   {
+    listenerEp = unix_stream::Endpoint("nfd-test-unix-stream-channel.sock");
   }
 
-  unique_ptr<UnixStreamChannel>
-  makeChannel()
+  virtual unique_ptr<UnixStreamChannel>
+  makeChannel() final
   {
     return make_unique<UnixStreamChannel>(listenerEp);
   }
@@ -65,11 +61,11 @@
         listenerFaces.push_back(newFace);
         limitedIo.afterOp();
       },
-      &failIfError);
+      ChannelFixture::unexpectedFailure);
   }
 
   void
-  connect(local::stream_protocol::socket& client)
+  clientConnect(local::stream_protocol::socket& client)
   {
     client.async_connect(listenerEp,
       [this] (const boost::system::error_code& error) {
@@ -77,16 +73,17 @@
         limitedIo.afterOp();
       });
   }
-
-protected:
-  LimitedIo limitedIo;
-  unix_stream::Endpoint listenerEp;
-  unique_ptr<UnixStreamChannel> listenerChannel;
-  std::vector<shared_ptr<Face>> listenerFaces;
 };
 
+BOOST_AUTO_TEST_SUITE(Face)
 BOOST_FIXTURE_TEST_SUITE(TestUnixStreamChannel, UnixStreamChannelFixture)
 
+BOOST_AUTO_TEST_CASE(Uri)
+{
+  auto channel = makeChannel();
+  BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(listenerEp));
+}
+
 BOOST_AUTO_TEST_CASE(Listen)
 {
   auto channel = makeChannel();
@@ -108,15 +105,15 @@
   BOOST_CHECK_EQUAL(listenerFaces.size(), 0);
 
   local::stream_protocol::socket client1(g_io);
-  this->connect(client1);
+  this->clientConnect(client1);
 
   BOOST_CHECK_EQUAL(limitedIo.run(2, time::seconds(1)), LimitedIo::EXCEED_OPS);
   BOOST_CHECK_EQUAL(listenerFaces.size(), 1);
 
   local::stream_protocol::socket client2(g_io);
   local::stream_protocol::socket client3(g_io);
-  this->connect(client2);
-  this->connect(client3);
+  this->clientConnect(client2);
+  this->clientConnect(client3);
 
   BOOST_CHECK_EQUAL(limitedIo.run(4, time::seconds(1)), LimitedIo::EXCEED_OPS);
   BOOST_CHECK_EQUAL(listenerFaces.size(), 3);
diff --git a/tests/daemon/face/websocket-channel.t.cpp b/tests/daemon/face/websocket-channel.t.cpp
index 2474300..c0ee2f8 100644
--- a/tests/daemon/face/websocket-channel.t.cpp
+++ b/tests/daemon/face/websocket-channel.t.cpp
@@ -26,32 +26,22 @@
 #include "face/websocket-channel.hpp"
 #include "face/websocket-transport.hpp"
 
+#include "channel-fixture.hpp"
 #include "test-ip.hpp"
-#include "tests/limited-io.hpp"
-#include "tests/test-common.hpp"
 
 namespace nfd {
 namespace tests {
 
-BOOST_AUTO_TEST_SUITE(Face)
-
-using nfd::Face;
 namespace ip = boost::asio::ip;
 
-class WebSocketChannelFixture : public BaseFixture
+class WebSocketChannelFixture : public ChannelFixture<WebSocketChannel, websocket::Endpoint>
 {
 protected:
-  WebSocketChannelFixture()
-    : clientShouldPong(true)
-    , m_nextPort(20070)
-  {
-  }
-
-  unique_ptr<WebSocketChannel>
-  makeChannel(const ip::address& addr, uint16_t port = 0)
+  virtual unique_ptr<WebSocketChannel>
+  makeChannel(const ip::address& addr, uint16_t port = 0) final
   {
     if (port == 0)
-      port = m_nextPort++;
+      port = getNextPort();
 
     return make_unique<WebSocketChannel>(websocket::Endpoint(addr, port));
   }
@@ -81,7 +71,7 @@
 
     std::string uri = "ws://" + listenerEp.address().to_string() + ":" + to_string(listenerEp.port());
     websocketpp::lib::error_code ec;
-    websocket::Client::connection_ptr con = client.get_connection(uri, ec);
+    auto con = client.get_connection(uri, ec);
     BOOST_REQUIRE_EQUAL(ec, websocketpp::lib::error_code());
 
     client.connect(con);
@@ -152,25 +142,21 @@
   }
 
 protected:
-  LimitedIo limitedIo;
-
-  websocket::Endpoint listenerEp;
-  unique_ptr<WebSocketChannel> listenerChannel;
-  std::vector<shared_ptr<Face>> listenerFaces;
   std::vector<Interest> faceReceivedInterests;
 
   websocket::Client client;
   websocketpp::connection_hdl clientHandle;
   std::vector<std::string> clientReceivedMessages;
+
   time::steady_clock::Duration measuredPingInterval;
-  bool clientShouldPong; // set clientShouldPong false to disable the pong response,
-                         // which will cause timeout in listenerChannel
+  bool clientShouldPong = true; // set clientShouldPong false to disable the pong response,
+                                // which will cause timeout in listenerChannel
 
 private:
-  uint16_t m_nextPort;
   time::steady_clock::TimePoint m_prevPingRecvTime;
 };
 
+BOOST_AUTO_TEST_SUITE(Face)
 BOOST_FIXTURE_TEST_SUITE(TestWebSocketChannel, WebSocketChannelFixture)
 
 BOOST_AUTO_TEST_CASE(Uri)