face: support IPv6-only WebSocket channels

Change-Id: Ib9ba27d04611c13882e50995d701fc1b707cea5f
Refs: #4710
diff --git a/tests/daemon/face/websocket-channel.t.cpp b/tests/daemon/face/websocket-channel.t.cpp
index 5c4f433..d4d9c74 100644
--- a/tests/daemon/face/websocket-channel.t.cpp
+++ b/tests/daemon/face/websocket-channel.t.cpp
@@ -23,154 +23,35 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "face/websocket-channel.hpp"
+#include "websocket-channel-fixture.hpp"
 #include "face/websocket-transport.hpp"
 
-#include "channel-fixture.hpp"
 #include "test-ip.hpp"
+#include <boost/mpl/vector.hpp>
 
 namespace nfd {
 namespace face {
 namespace tests {
 
-namespace ip = boost::asio::ip;
-
-class WebSocketChannelFixture : public ChannelFixture<WebSocketChannel, websocket::Endpoint>
-{
-protected:
-  unique_ptr<WebSocketChannel>
-  makeChannel(const ip::address& addr, uint16_t port = 0) final
-  {
-    if (port == 0)
-      port = getNextPort();
-
-    return make_unique<WebSocketChannel>(websocket::Endpoint(addr, port));
-  }
-
-  void
-  listen(const ip::address& addr,
-         const time::milliseconds& pingInterval = 10_s,
-         const time::milliseconds& pongTimeout = 1_s)
-  {
-    listenerEp = websocket::Endpoint(addr, 20030);
-    listenerChannel = makeChannel(addr, 20030);
-    listenerChannel->setPingInterval(pingInterval);
-    listenerChannel->setPongTimeout(pongTimeout);
-    listenerChannel->listen(bind(&WebSocketChannelFixture::listenerOnFaceCreated, this, _1));
-  }
-
-  void
-  clientConnect(websocket::Client& client)
-  {
-    client.clear_access_channels(websocketpp::log::alevel::all);
-    client.clear_error_channels(websocketpp::log::elevel::all);
-
-    client.init_asio(&g_io);
-    client.set_open_handler(bind(&WebSocketChannelFixture::clientHandleOpen, this, _1));
-    client.set_message_handler(bind(&WebSocketChannelFixture::clientHandleMessage, this, _1, _2));
-    client.set_ping_handler(bind(&WebSocketChannelFixture::clientHandlePing, this, _1, _2));
-
-    std::string uri = "ws://" + listenerEp.address().to_string() + ":" + to_string(listenerEp.port());
-    websocketpp::lib::error_code ec;
-    auto con = client.get_connection(uri, ec);
-    BOOST_REQUIRE_EQUAL(ec, websocketpp::lib::error_code());
-
-    client.connect(con);
-  }
-
-  void
-  initialize(const ip::address& addr,
-             const time::milliseconds& pingInterval = 10_s,
-             const time::milliseconds& pongTimeout = 1_s)
-  {
-    listen(addr, pingInterval, pongTimeout);
-    clientConnect(client);
-    BOOST_REQUIRE_EQUAL(limitedIo.run(2, // listenerOnFaceCreated, clientHandleOpen
-                                      1_s), LimitedIo::EXCEED_OPS);
-    BOOST_REQUIRE_EQUAL(listenerChannel->size(), 1);
-  }
-
-  void
-  clientSendInterest(const Interest& interest)
-  {
-    const Block& payload = interest.wireEncode();
-    client.send(clientHandle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
-  }
-
-private:
-  void
-  listenerOnFaceCreated(const shared_ptr<Face>& newFace)
-  {
-    BOOST_REQUIRE(newFace != nullptr);
-    newFace->afterReceiveInterest.connect(bind(&WebSocketChannelFixture::faceAfterReceiveInterest, this, _1));
-    connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
-    listenerFaces.push_back(newFace);
-    limitedIo.afterOp();
-  }
-
-  void
-  faceAfterReceiveInterest(const Interest& interest)
-  {
-    faceReceivedInterests.push_back(interest);
-    limitedIo.afterOp();
-  }
-
-  void
-  clientHandleOpen(websocketpp::connection_hdl hdl)
-  {
-    clientHandle = hdl;
-    limitedIo.afterOp();
-  }
-
-  void
-  clientHandleMessage(websocketpp::connection_hdl, websocket::Client::message_ptr msg)
-  {
-    clientReceivedMessages.push_back(msg->get_payload());
-    limitedIo.afterOp();
-  }
-
-  bool
-  clientHandlePing(websocketpp::connection_hdl, std::string)
-  {
-    auto now = time::steady_clock::now();
-    if (m_prevPingRecvTime != time::steady_clock::TimePoint()) {
-      measuredPingInterval = now - m_prevPingRecvTime;
-    }
-    m_prevPingRecvTime = now;
-
-    limitedIo.afterOp();
-    return clientShouldPong;
-  }
-
-protected:
-  std::vector<Interest> faceReceivedInterests;
-
-  websocket::Client client;
-  websocketpp::connection_hdl clientHandle;
-  std::vector<std::string> clientReceivedMessages;
-
-  time::steady_clock::Duration measuredPingInterval;
-  // set clientShouldPong to false to disable the pong response,
-  // which will eventually cause a timeout in listenerChannel
-  bool clientShouldPong = true;
-
-private:
-  time::steady_clock::TimePoint m_prevPingRecvTime;
-};
-
 BOOST_AUTO_TEST_SUITE(Face)
 BOOST_FIXTURE_TEST_SUITE(TestWebSocketChannel, WebSocketChannelFixture)
 
-BOOST_AUTO_TEST_CASE(Uri)
+using AddressFamilies = boost::mpl::vector<
+  std::integral_constant<AddressFamily, AddressFamily::V4>,
+  std::integral_constant<AddressFamily, AddressFamily::V6>>;
+
+BOOST_AUTO_TEST_CASE_TEMPLATE(Uri, F, AddressFamilies)
 {
-  websocket::Endpoint ep(ip::address_v4::loopback(), 20070);
-  auto channel = makeChannel(ep.address(), ep.port());
+  using Address = typename IpAddressFromFamily<F::value>::type;
+  websocket::Endpoint ep(Address::loopback(), 20070);
+  auto channel = this->makeChannel(ep.address(), ep.port());
   BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(ep, "ws"));
 }
 
-BOOST_AUTO_TEST_CASE(Listen)
+BOOST_AUTO_TEST_CASE_TEMPLATE(Listen, F, AddressFamilies)
 {
-  auto channel = makeChannel(ip::address_v4());
+  using Address = typename IpAddressFromFamily<F::value>::type;
+  auto channel = this->makeChannel(Address());
   BOOST_CHECK_EQUAL(channel->isListening(), false);
 
   channel->listen(nullptr);
@@ -181,9 +62,9 @@
   BOOST_CHECK_EQUAL(channel->isListening(), true);
 }
 
-BOOST_AUTO_TEST_CASE(MultipleAccepts)
+BOOST_AUTO_TEST_CASE_TEMPLATE(MultipleAccepts, F, AddressFamilies)
 {
-  auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+  auto address = getTestIp(F::value, AddressScope::Loopback);
   SKIP_IF_IP_UNAVAILABLE(address);
   this->listen(address);
 
@@ -212,12 +93,12 @@
   }
 }
 
-BOOST_AUTO_TEST_CASE(Send)
+BOOST_AUTO_TEST_CASE_TEMPLATE(Send, F, AddressFamilies)
 {
-  auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+  auto address = getTestIp(F::value, AddressScope::Loopback);
   SKIP_IF_IP_UNAVAILABLE(address);
   this->initialize(address);
-  auto transport = listenerFaces.front()->getTransport();
+  auto transport = listenerFaces.at(0)->getTransport();
 
   Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
   transport->send(Transport::Packet(Block(pkt1)));
@@ -240,9 +121,9 @@
     pkt2.begin(), pkt2.end());
 }
 
-BOOST_AUTO_TEST_CASE(Receive)
+BOOST_AUTO_TEST_CASE_TEMPLATE(Receive, F, AddressFamilies)
 {
-  auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+  auto address = getTestIp(F::value, AddressScope::Loopback);
   SKIP_IF_IP_UNAVAILABLE(address);
   this->initialize(address);
 
@@ -251,11 +132,11 @@
   auto interest1 = makeInterest("ndn:/TpnzGvW9R");
   auto interest2 = makeInterest("ndn:/QWiIMfj5sL");
 
-  clientSendInterest(*interest1);
+  this->clientSendInterest(*interest1);
   BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
                                   1_s), LimitedIo::EXCEED_OPS);
 
-  clientSendInterest(*interest2);
+  this->clientSendInterest(*interest2);
   BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
                                   1_s), LimitedIo::EXCEED_OPS);
 
@@ -264,19 +145,19 @@
   BOOST_CHECK_EQUAL(faceReceivedInterests[1].getName(), interest2->getName());
 }
 
-BOOST_AUTO_TEST_CASE(FaceClosure)
+BOOST_AUTO_TEST_CASE_TEMPLATE(FaceClosure, F, AddressFamilies)
 {
-  auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+  auto address = getTestIp(F::value, AddressScope::Loopback);
   SKIP_IF_IP_UNAVAILABLE(address);
   this->initialize(address);
 
-  listenerFaces.front()->close();
+  listenerFaces.at(0)->close();
   BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
 }
 
-BOOST_AUTO_TEST_CASE(RemoteClose)
+BOOST_AUTO_TEST_CASE_TEMPLATE(RemoteClose, F, AddressFamilies)
 {
-  auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+  auto address = getTestIp(F::value, AddressScope::Loopback);
   SKIP_IF_IP_UNAVAILABLE(address);
   this->initialize(address);
 
@@ -286,9 +167,9 @@
   BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
 }
 
-BOOST_AUTO_TEST_CASE(SetPingInterval)
+BOOST_AUTO_TEST_CASE_TEMPLATE(SetPingInterval, F, AddressFamilies)
 {
-  auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+  auto address = getTestIp(F::value, AddressScope::Loopback);
   SKIP_IF_IP_UNAVAILABLE(address);
   const auto pingInterval = 1200_ms;
   this->initialize(address, pingInterval);
@@ -299,18 +180,18 @@
   BOOST_CHECK_GE(measuredPingInterval, pingInterval * 0.9);
 }
 
-BOOST_AUTO_TEST_CASE(SetPongTimeOut)
+BOOST_AUTO_TEST_CASE_TEMPLATE(SetPongTimeOut, F, AddressFamilies)
 {
-  auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
+  auto address = getTestIp(F::value, AddressScope::Loopback);
   SKIP_IF_IP_UNAVAILABLE(address);
   this->initialize(address, 600_ms, 300_ms);
-  clientShouldPong = false;
+  this->clientShouldPong = false;
 
   BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, faceClosedSignal
                                   2_s), LimitedIo::EXCEED_OPS);
   BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
 
-  auto transport = static_cast<WebSocketTransport*>(listenerFaces.front()->getTransport());
+  auto transport = static_cast<WebSocketTransport*>(listenerFaces.at(0)->getTransport());
   BOOST_CHECK(transport->getState() == TransportState::FAILED ||
               transport->getState() == TransportState::CLOSED);
   BOOST_CHECK_GE(transport->getCounters().nOutPings, 1);