face: support non-listening UDP channels

This commit also improves coverage of the ProcessConfig
test suite for the various protocol factories.

Change-Id: Id9dbe2ca914e273f9225cd93db41bf714c714591
Refs: #4098
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index 74b6974..c392b51 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -60,11 +60,11 @@
 {
   // udp
   // {
+  //   listen yes
   //   port 6363
   //   enable_v4 yes
   //   enable_v6 yes
   //   idle_timeout 600
-  //   keep_alive_interval 25 ; acceptable but ignored
   //   mcast yes
   //   mcast_group 224.0.23.170
   //   mcast_port 56363
@@ -82,6 +82,7 @@
 
   m_wantCongestionMarking = context.generalConfig.wantCongestionMarking;
 
+  bool wantListen = true;
   uint16_t port = 6363;
   bool enableV4 = false;
   bool enableV6 = false;
@@ -96,7 +97,10 @@
       const std::string& key = pair.first;
       const ConfigSection& value = pair.second;
 
-      if (key == "port") {
+      if (key == "listen") {
+        wantListen = ConfigFile::parseYesNo(pair, "face_system.udp");
+      }
+      else if (key == "port") {
         port = ConfigFile::parseNumber<uint16_t>(pair, "face_system.udp");
       }
       else if (key == "enable_v4") {
@@ -175,7 +179,7 @@
   if (enableV4) {
     udp::Endpoint endpoint(ip::udp::v4(), port);
     shared_ptr<UdpChannel> v4Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
-    if (!v4Channel->isListening()) {
+    if (wantListen && !v4Channel->isListening()) {
       v4Channel->listen(this->addFace, nullptr);
     }
     providedSchemes.insert("udp");
@@ -188,7 +192,7 @@
   if (enableV6) {
     udp::Endpoint endpoint(ip::udp::v6(), port);
     shared_ptr<UdpChannel> v6Channel = this->createChannel(endpoint, time::seconds(idleTimeout));
-    if (!v6Channel->isListening()) {
+    if (wantListen && !v6Channel->isListening()) {
       v6Channel->listen(this->addFace, nullptr);
     }
     providedSchemes.insert("udp");
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index 02ef8d3..10b49c3 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -86,7 +86,7 @@
     enable_congestion_marking no ; set to 'yes' to perform congestion marking on supported faces, default 'no'
   }
 
-  ; The unix section contains settings of Unix stream faces and channels.
+  ; The unix section contains settings for Unix stream faces and channels.
   ; A Unix channel is always listening; delete the unix section to disable
   ; Unix stream faces and channels.
   ;
@@ -99,7 +99,7 @@
     path /var/run/nfd.sock ; Unix stream listener path
   }
 
-  ; The tcp section contains settings of TCP faces and channels.
+  ; The tcp section contains settings for TCP faces and channels.
   tcp
   {
     listen yes ; set to 'no' to disable TCP listener, default 'yes'
@@ -108,12 +108,12 @@
     enable_v6 yes ; set to 'no' to disable IPv6 channels, default 'yes'
   }
 
-  ; The udp section contains settings of UDP faces and channels.
+  ; The udp section contains settings for UDP faces and channels.
   udp
   {
     ; UDP unicast settings.
-    ; UDP channels are always listening; delete the udp section to disable them
-    port 6363 ; UDP unicast port number
+    listen yes ; set to 'no' to disable UDP listener, default 'yes'
+    port 6363 ; UDP listener port number
     enable_v4 yes ; set to 'no' to disable IPv4 channels, default 'yes'
     enable_v6 yes ; set to 'no' to disable IPv6 channels, default 'yes'
 
@@ -122,8 +122,6 @@
     ; The default is 600 (10 minutes).
     idle_timeout 600
 
-    keep_alive_interval 25; interval (seconds) between keep-alive refreshes
-
     ; UDP multicast settings.
     ; By default, NFD creates one UDP multicast face per NIC.
     ;
@@ -154,7 +152,7 @@
     }
   }
 
-  ; The ether section contains settings of Ethernet faces and channels.
+  ; The ether section contains settings for Ethernet faces and channels.
   ; These settings will NOT work without root or setting the appropriate
   ; permissions:
   ;
@@ -208,7 +206,7 @@
   @IF_HAVE_LIBPCAP@  }
   @IF_HAVE_LIBPCAP@}
 
-  ; The websocket section contains settings of WebSocket faces and channels.
+  ; The websocket section contains settings for WebSocket faces and channels.
   @IF_HAVE_WEBSOCKET@websocket
   @IF_HAVE_WEBSOCKET@{
   @IF_HAVE_WEBSOCKET@  listen yes ; set to 'no' to disable WebSocket listener, default 'yes'
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index 70a9d5f..9fe867f 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -24,14 +24,12 @@
  */
 
 #include "face/ethernet-factory.hpp"
-#include "face/face.hpp"
 
 #include "ethernet-fixture.hpp"
 #include "face-system-fixture.hpp"
 #include "factory-test-common.hpp"
 
 #include <boost/algorithm/string/replace.hpp>
-#include <boost/range/algorithm/count_if.hpp>
 
 namespace nfd {
 namespace face {
@@ -73,11 +71,9 @@
 BOOST_AUTO_TEST_SUITE(Face)
 BOOST_FIXTURE_TEST_SUITE(TestEthernetFactory, EthernetFactoryFixture)
 
-using nfd::Face;
-
 BOOST_AUTO_TEST_SUITE(ProcessConfig)
 
-BOOST_AUTO_TEST_CASE(ListeningChannels)
+BOOST_AUTO_TEST_CASE(Defaults)
 {
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
 
@@ -86,8 +82,6 @@
     {
       ether
       {
-        listen yes
-        idle_timeout 60
         mcast no
       }
     }
@@ -96,16 +90,14 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  auto& factory = this->getFactoryById<EthernetFactory>("ether");
   checkChannelListEqual(factory, this->listUrisOfAvailableNetifs());
-  for (const auto& channel : factory.getChannels()) {
-    BOOST_CHECK_EQUAL(channel->isListening(), true);
-  }
-
+  auto channels = factory.getChannels();
+  BOOST_CHECK(std::all_of(channels.begin(), channels.end(),
+                          [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
   BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
 }
 
-BOOST_AUTO_TEST_CASE(NonListeningChannels)
+BOOST_AUTO_TEST_CASE(DisableListen)
 {
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
 
@@ -124,31 +116,13 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  auto& factory = this->getFactoryById<EthernetFactory>("ether");
   checkChannelListEqual(factory, this->listUrisOfAvailableNetifs());
-  for (const auto& channel : factory.getChannels()) {
-    BOOST_CHECK_EQUAL(channel->isListening(), false);
-  }
-
+  auto channels = factory.getChannels();
+  BOOST_CHECK(std::none_of(channels.begin(), channels.end(),
+                           [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
   BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
 }
 
-BOOST_AUTO_TEST_CASE(BadListen)
-{
-  const std::string CONFIG = R"CONFIG(
-    face_system
-    {
-      ether
-      {
-        listen hello
-      }
-    }
-  )CONFIG";
-
-  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
-  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
-}
-
 BOOST_AUTO_TEST_CASE(McastNormal)
 {
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
@@ -323,9 +297,11 @@
 
   auto etherMcastFaces = this->listEtherMcastFaces();
   BOOST_CHECK_EQUAL(etherMcastFaces.size(), netifs.size() - 1);
-  BOOST_CHECK_EQUAL(boost::count_if(etherMcastFaces, [ifname] (const Face* face) {
-    return face->getLocalUri() == FaceUri::fromDev(ifname);
-  }), 0);
+  BOOST_CHECK(std::none_of(etherMcastFaces.begin(), etherMcastFaces.end(),
+    [ifname] (const nfd::Face* face) {
+      return face->getLocalUri() == FaceUri::fromDev(ifname);
+    }
+  ));
 }
 
 BOOST_AUTO_TEST_CASE(Omitted)
@@ -342,6 +318,54 @@
   BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
 }
 
+BOOST_AUTO_TEST_CASE(BadListen)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      ether
+      {
+        listen hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadIdleTimeout, 2) // Bug #4489
+BOOST_AUTO_TEST_CASE(BadIdleTimeout)
+{
+  // not a number
+  const std::string CONFIG1 = R"CONFIG(
+    face_system
+    {
+      ether
+      {
+        idle_timeout hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, false), ConfigFile::Error);
+
+  // negative number
+  const std::string CONFIG2 = R"CONFIG(
+    face_system
+    {
+      ether
+      {
+        idle_timeout -15
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
+}
+
 BOOST_AUTO_TEST_CASE(BadMcast)
 {
   const std::string CONFIG = R"CONFIG(
@@ -360,36 +384,33 @@
 
 BOOST_AUTO_TEST_CASE(BadMcastGroup)
 {
-  const std::string CONFIG = R"CONFIG(
+  // not an address
+  const std::string CONFIG1 = R"CONFIG(
     face_system
     {
       ether
       {
-        mcast yes
         mcast_group hello
       }
     }
   )CONFIG";
 
-  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
-  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
-}
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, false), ConfigFile::Error);
 
-BOOST_AUTO_TEST_CASE(UnicastMcastGroup)
-{
-  const std::string CONFIG = R"CONFIG(
+  // non-multicast address
+  const std::string CONFIG2 = R"CONFIG(
     face_system
     {
       ether
       {
-        mcast yes
         mcast_group 00:00:5e:00:53:5e
       }
     }
   )CONFIG";
 
-  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
-  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(UnknownOption)
diff --git a/tests/daemon/face/tcp-factory.t.cpp b/tests/daemon/face/tcp-factory.t.cpp
index 3785111..77b8066 100644
--- a/tests/daemon/face/tcp-factory.t.cpp
+++ b/tests/daemon/face/tcp-factory.t.cpp
@@ -50,20 +50,57 @@
 BOOST_AUTO_TEST_SUITE(Face)
 BOOST_FIXTURE_TEST_SUITE(TestTcpFactory, TcpFactoryFixture)
 
-using nfd::Face;
-
 BOOST_AUTO_TEST_SUITE(ProcessConfig)
 
-BOOST_AUTO_TEST_CASE(Normal)
+BOOST_AUTO_TEST_CASE(Defaults)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      tcp
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, true);
+  parseConfig(CONFIG, false);
+
+  checkChannelListEqual(factory, {"tcp4://0.0.0.0:6363", "tcp6://[::]:6363"});
+  auto channels = factory.getChannels();
+  BOOST_CHECK(std::all_of(channels.begin(), channels.end(),
+                          [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
+}
+
+BOOST_AUTO_TEST_CASE(DisableListen)
 {
   const std::string CONFIG = R"CONFIG(
     face_system
     {
       tcp
       {
-        listen yes
-        port 16363
-        enable_v4 yes
+        listen no
+        port 7001
+      }
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, true);
+  parseConfig(CONFIG, false);
+
+  checkChannelListEqual(factory, {"tcp4://0.0.0.0:7001", "tcp6://[::]:7001"});
+  auto channels = factory.getChannels();
+  BOOST_CHECK(std::none_of(channels.begin(), channels.end(),
+                           [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
+}
+
+BOOST_AUTO_TEST_CASE(DisableV4)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      tcp
+      {
+        port 7001
+        enable_v4 no
         enable_v6 yes
       }
     }
@@ -72,7 +109,27 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
+  checkChannelListEqual(factory, {"tcp6://[::]:7001"});
+}
+
+BOOST_AUTO_TEST_CASE(DisableV6)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      tcp
+      {
+        port 7001
+        enable_v4 yes
+        enable_v6 no
+      }
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, true);
+  parseConfig(CONFIG, false);
+
+  checkChannelListEqual(factory, {"tcp4://0.0.0.0:7001"});
 }
 
 BOOST_AUTO_TEST_CASE(Omitted)
@@ -89,6 +146,23 @@
   BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
 }
 
+BOOST_AUTO_TEST_CASE(AllDisabled)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      tcp
+      {
+        enable_v4 no
+        enable_v6 no
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
 BOOST_AUTO_TEST_CASE(BadListen)
 {
   const std::string CONFIG = R"CONFIG(
@@ -105,22 +179,50 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
 }
 
-BOOST_AUTO_TEST_CASE(ChannelsDisabled)
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadPort, 2) // Bug #4489
+BOOST_AUTO_TEST_CASE(BadPort)
 {
-  const std::string CONFIG = R"CONFIG(
+  // not a number
+  const std::string CONFIG1 = R"CONFIG(
     face_system
     {
       tcp
       {
-        port 6363
-        enable_v4 no
-        enable_v6 no
+        port hello
       }
     }
   )CONFIG";
 
-  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
-  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, false), ConfigFile::Error);
+
+  // negative number
+  const std::string CONFIG2 = R"CONFIG(
+    face_system
+    {
+      tcp
+      {
+        port -1
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
+
+  // out of range
+  const std::string CONFIG3 = R"CONFIG(
+    face_system
+    {
+      tcp
+      {
+        port 65536
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, false), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(UnknownOption)
@@ -238,7 +340,7 @@
 {
 public:
   void
-  onFaceCreated(const shared_ptr<Face>& newFace)
+  onFaceCreated(const shared_ptr<nfd::Face>& newFace)
   {
     BOOST_CHECK_MESSAGE(false, "Timeout expected");
     face = newFace;
@@ -256,7 +358,7 @@
 
 public:
   LimitedIo limitedIo;
-  shared_ptr<Face> face;
+  shared_ptr<nfd::Face> face;
 };
 
 BOOST_FIXTURE_TEST_CASE(CreateFaceTimeout, CreateFaceTimeoutFixture)
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index ee5a3f4..75767d5 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -42,7 +42,7 @@
   createChannel(const std::string& localIp, uint16_t localPort)
   {
     udp::Endpoint endpoint(ndn::ip::addressFromString(localIp), localPort);
-    return factory.createChannel(endpoint, time::minutes(5));
+    return factory.createChannel(endpoint, 5_min);
   }
 };
 
@@ -179,21 +179,37 @@
 BOOST_AUTO_TEST_SUITE(Face)
 BOOST_FIXTURE_TEST_SUITE(TestUdpFactory, UdpFactoryFixture)
 
-using nfd::Face;
-
 BOOST_AUTO_TEST_SUITE(ProcessConfig)
 
-BOOST_AUTO_TEST_CASE(Channels)
+using nfd::Face;
+
+BOOST_AUTO_TEST_CASE(Defaults)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      udp
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, true);
+  parseConfig(CONFIG, false);
+
+  checkChannelListEqual(factory, {"udp4://0.0.0.0:6363", "udp6://[::]:6363"});
+  auto channels = factory.getChannels();
+  BOOST_CHECK(std::all_of(channels.begin(), channels.end(),
+                          [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
+}
+
+BOOST_AUTO_TEST_CASE(DisableListen)
 {
   const std::string CONFIG = R"CONFIG(
     face_system
     {
       udp
       {
+        listen no
         port 7001
-        enable_v4 yes
-        enable_v6 yes
-        idle_timeout 30
         mcast no
       }
     }
@@ -203,30 +219,12 @@
   parseConfig(CONFIG, false);
 
   checkChannelListEqual(factory, {"udp4://0.0.0.0:7001", "udp6://[::]:7001"});
+  auto channels = factory.getChannels();
+  BOOST_CHECK(std::none_of(channels.begin(), channels.end(),
+                           [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
 }
 
-BOOST_AUTO_TEST_CASE(ChannelV4)
-{
-  const std::string CONFIG = R"CONFIG(
-    face_system
-    {
-      udp
-      {
-        port 7001
-        enable_v4 yes
-        enable_v6 no
-        mcast no
-      }
-    }
-  )CONFIG";
-
-  parseConfig(CONFIG, true);
-  parseConfig(CONFIG, false);
-
-  checkChannelListEqual(factory, {"udp4://0.0.0.0:7001"});
-}
-
-BOOST_AUTO_TEST_CASE(ChannelV6)
+BOOST_AUTO_TEST_CASE(DisableV4)
 {
   const std::string CONFIG = R"CONFIG(
     face_system
@@ -247,6 +245,27 @@
   checkChannelListEqual(factory, {"udp6://[::]:7001"});
 }
 
+BOOST_AUTO_TEST_CASE(DisableV6)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        port 7001
+        enable_v4 yes
+        enable_v6 no
+        mcast no
+      }
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, true);
+  parseConfig(CONFIG, false);
+
+  checkChannelListEqual(factory, {"udp4://0.0.0.0:7001"});
+}
+
 BOOST_FIXTURE_TEST_CASE(EnableDisableMcast, UdpFactoryMcastFixture)
 {
   const std::string CONFIG_WITH_MCAST = R"CONFIG(
@@ -545,20 +564,98 @@
   BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
 }
 
-BOOST_AUTO_TEST_CASE(BadIdleTimeout)
+BOOST_AUTO_TEST_CASE(BadListen)
 {
   const std::string CONFIG = R"CONFIG(
     face_system
     {
       udp
       {
+        listen hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadPort, 2) // Bug #4489
+BOOST_AUTO_TEST_CASE(BadPort)
+{
+  // not a number
+  const std::string CONFIG1 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        port hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, false), ConfigFile::Error);
+
+  // negative number
+  const std::string CONFIG2 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        port -1
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
+
+  // out of range
+  const std::string CONFIG3 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        port 65536
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadIdleTimeout, 2) // Bug #4489
+BOOST_AUTO_TEST_CASE(BadIdleTimeout)
+{
+  // not a number
+  const std::string CONFIG1 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
         idle_timeout hello
       }
     }
   )CONFIG";
 
-  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
-  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, false), ConfigFile::Error);
+
+  // negative number
+  const std::string CONFIG2 = R"CONFIG(
+    face_system
+    {
+      udp
+      {
+        idle_timeout -15
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
 }
 
 BOOST_AUTO_TEST_CASE(BadMcast)
diff --git a/tests/daemon/face/unix-stream-factory.t.cpp b/tests/daemon/face/unix-stream-factory.t.cpp
index b07e5bf..886a649 100644
--- a/tests/daemon/face/unix-stream-factory.t.cpp
+++ b/tests/daemon/face/unix-stream-factory.t.cpp
@@ -32,15 +32,13 @@
 namespace face {
 namespace tests {
 
-#define CHANNEL_PATH1 "unix-stream-test.1.sock"
-#define CHANNEL_PATH2 "unix-stream-test.2.sock"
-
 using UnixStreamFactoryFixture = FaceSystemFactoryFixture<UnixStreamFactory>;
 
 BOOST_AUTO_TEST_SUITE(Face)
 BOOST_FIXTURE_TEST_SUITE(TestUnixStreamFactory, UnixStreamFactoryFixture)
 
-using nfd::Face;
+static const std::string CHANNEL_PATH1("unix-stream-test.1.sock");
+static const std::string CHANNEL_PATH2("unix-stream-test.2.sock");
 
 BOOST_AUTO_TEST_SUITE(ProcessConfig)
 
@@ -51,7 +49,7 @@
     {
       unix
       {
-        path /tmp/nfd.sock
+        path /tmp/nfd-test.sock
       }
     }
   )CONFIG";
@@ -59,8 +57,10 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  auto& factory = this->getFactoryById<UnixStreamFactory>("unix");
-  BOOST_CHECK_EQUAL(factory.getChannels().size(), 1);
+  BOOST_REQUIRE_EQUAL(factory.getChannels().size(), 1);
+  const auto& uri = factory.getChannels().front()->getUri();
+  BOOST_CHECK_EQUAL(uri.getScheme(), "unix");
+  BOOST_CHECK_NE(uri.getPath().find("nfd-test.sock"), std::string::npos);
 }
 
 BOOST_AUTO_TEST_CASE(Omitted)
@@ -74,7 +74,6 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  auto& factory = this->getFactoryById<UnixStreamFactory>("unix");
   BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
 }
 
@@ -111,10 +110,12 @@
   auto channel1 = factory.createChannel(CHANNEL_PATH1);
   auto channel1a = factory.createChannel(CHANNEL_PATH1);
   BOOST_CHECK_EQUAL(channel1, channel1a);
-  std::string uri = channel1->getUri().toString();
-  BOOST_CHECK_EQUAL(uri.find("unix:///"), 0); // third '/' is the path separator
-  BOOST_CHECK_EQUAL(uri.rfind(CHANNEL_PATH1),
-                    uri.size() - std::string(CHANNEL_PATH1).size());
+
+  const auto& uri = channel1->getUri();
+  BOOST_CHECK_EQUAL(uri.getScheme(), "unix");
+  BOOST_CHECK_EQUAL(uri.getHost(), "");
+  BOOST_CHECK_EQUAL(uri.getPort(), "");
+  BOOST_CHECK_EQUAL(uri.getPath().rfind(CHANNEL_PATH1), uri.getPath().size() - CHANNEL_PATH1.size());
 
   auto channel2 = factory.createChannel(CHANNEL_PATH2);
   BOOST_CHECK_NE(channel1, channel2);
diff --git a/tests/daemon/face/websocket-factory.t.cpp b/tests/daemon/face/websocket-factory.t.cpp
index 9ba5d5d..b1c38a6 100644
--- a/tests/daemon/face/websocket-factory.t.cpp
+++ b/tests/daemon/face/websocket-factory.t.cpp
@@ -51,18 +51,12 @@
 
 BOOST_AUTO_TEST_SUITE(ProcessConfig)
 
-BOOST_AUTO_TEST_CASE(Normal)
+BOOST_AUTO_TEST_CASE(Defaults)
 {
   const std::string CONFIG = R"CONFIG(
     face_system
     {
       websocket
-      {
-        listen yes
-        port 9696
-        enable_v4 yes
-        enable_v6 yes
-      }
     }
   )CONFIG";
 
@@ -70,17 +64,56 @@
   parseConfig(CONFIG, false);
 
   checkChannelListEqual(factory, {"ws://[::]:9696"});
+  auto channels = factory.getChannels();
+  BOOST_CHECK(std::all_of(channels.begin(), channels.end(),
+                          [] (const shared_ptr<const Channel>& ch) { return ch->isListening(); }));
 }
 
-BOOST_AUTO_TEST_CASE(EnableIpv4Only)
+BOOST_AUTO_TEST_CASE(DisableListen)
 {
   const std::string CONFIG = R"CONFIG(
     face_system
     {
       websocket
       {
-        listen yes
-        port 9696
+        listen no
+        port 7001
+      }
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, true);
+  parseConfig(CONFIG, false);
+
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(DisableV4)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        port 7001
+        enable_v4 no
+        enable_v6 yes
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DisableV6)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        port 7001
         enable_v4 yes
         enable_v6 no
       }
@@ -90,63 +123,7 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  checkChannelListEqual(factory, {"ws://0.0.0.0:9696"});
-}
-
-BOOST_AUTO_TEST_CASE(UnsupportedIpv6Only)
-{
-  const std::string CONFIG = R"CONFIG(
-    face_system
-    {
-      websocket
-      {
-        listen yes
-        port 9696
-        enable_v4 no
-        enable_v6 yes
-      }
-    }
-  )CONFIG";
-
-  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
-  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
-}
-
-BOOST_AUTO_TEST_CASE(ChannelsDisabled)
-{
-  const std::string CONFIG = R"CONFIG(
-    face_system
-    {
-      websocket
-      {
-        listen yes
-        port 9696
-        enable_v4 no
-        enable_v6 no
-      }
-    }
-  )CONFIG";
-
-  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
-  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
-}
-
-BOOST_AUTO_TEST_CASE(NoListen)
-{
-  const std::string CONFIG = R"CONFIG(
-    face_system
-    {
-      websocket
-      {
-        listen no
-        port 9696
-        enable_v4 yes
-        enable_v6 yes
-      }
-    }
-  )CONFIG";
-
-  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+  checkChannelListEqual(factory, {"ws://0.0.0.0:7001"});
 }
 
 BOOST_AUTO_TEST_CASE(ChangeEndpoint)
@@ -178,6 +155,115 @@
   checkChannelListEqual(factory, {"ws://[::]:9001", "ws://[::]:9002"});
 }
 
+BOOST_AUTO_TEST_CASE(Omitted)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+    }
+  )CONFIG";
+
+  parseConfig(CONFIG, true);
+  parseConfig(CONFIG, false);
+
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(AllDisabled)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        enable_v4 no
+        enable_v6 no
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(BadListen)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        listen hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(BadPort, 2) // Bug #4489
+BOOST_AUTO_TEST_CASE(BadPort)
+{
+  // not a number
+  const std::string CONFIG1 = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        port hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG1, false), ConfigFile::Error);
+
+  // negative number
+  const std::string CONFIG2 = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        port -1
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG2, false), ConfigFile::Error);
+
+  // out of range
+  const std::string CONFIG3 = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        port 65536
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG3, false), ConfigFile::Error);
+}
+
+BOOST_AUTO_TEST_CASE(UnknownOption)
+{
+  const std::string CONFIG = R"CONFIG(
+    face_system
+    {
+      websocket
+      {
+        hello
+      }
+    }
+  )CONFIG";
+
+  BOOST_CHECK_THROW(parseConfig(CONFIG, true), ConfigFile::Error);
+  BOOST_CHECK_THROW(parseConfig(CONFIG, false), ConfigFile::Error);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // ProcessConfig
 
 BOOST_AUTO_TEST_CASE(GetChannels)