tests: expand test coverage of EthernetFactory and UdpFactory

Change-Id: I469ac8682502ca7a0e360703b462bfb119eaedf1
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 9ffd16e..1908cac 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -271,13 +271,13 @@
     return nullptr;
   }
 
-  if (netif->getType() != ndn::net::InterfaceType::ETHERNET) {
-    NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": incompatible netif type");
+  if (!netif->isUp()) {
+    NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": netif is down");
     return nullptr;
   }
 
-  if (!netif->isUp()) {
-    NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": netif is down");
+  if (netif->getType() != ndn::net::InterfaceType::ETHERNET) {
+    NFD_LOG_DEBUG("Not creating channel on " << netif->getName() << ": incompatible netif type");
     return nullptr;
   }
 
@@ -305,18 +305,13 @@
     return nullptr;
   }
 
-  if (netif.getType() != ndn::net::InterfaceType::ETHERNET) {
-    NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": incompatible netif type");
-    return nullptr;
-  }
-
   if (!netif.isUp()) {
     NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": netif is down");
     return nullptr;
   }
 
-  if (!netif.canMulticast()) {
-    NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": netif cannot multicast");
+  if (netif.getType() != ndn::net::InterfaceType::ETHERNET) {
+    NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": incompatible netif type");
     return nullptr;
   }
 
@@ -325,6 +320,11 @@
     return nullptr;
   }
 
+  if (!netif.canMulticast()) {
+    NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": netif cannot multicast");
+    return nullptr;
+  }
+
   if (!m_mcastConfig.netifPredicate(netif)) {
     NFD_LOG_DEBUG("Not creating multicast face on " << netif.getName() << ": rejected by whitelist/blacklist");
     return nullptr;
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index 0ee4562..d09c7df 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -39,11 +39,6 @@
                              , public FaceSystemFactoryFixture<EthernetFactory>
 {
 protected:
-  EthernetFactoryFixture()
-  {
-    this->copyRealNetifsToNetmon();
-  }
-
   std::set<std::string>
   listUrisOfAvailableNetifs() const
   {
@@ -59,17 +54,37 @@
     return listFacesByScheme("ether", linkType);
   }
 
-  size_t
-  countEtherMcastFaces(ndn::nfd::LinkType linkType = ndn::nfd::LINK_TYPE_MULTI_ACCESS) const
+  shared_ptr<ndn::net::NetworkInterface>
+  makeFakeNetif()
   {
-    return listEtherMcastFaces(linkType).size();
+    static int counter = 0;
+    ++counter;
+
+    auto netif = netmon->makeNetworkInterface();
+    netif->setIndex(1000 + counter);
+    netif->setName("ethdummy" + std::to_string(counter));
+    netif->setType(ndn::net::InterfaceType::ETHERNET);
+    netif->setFlags(IFF_MULTICAST | IFF_UP);
+    netif->setState(ndn::net::InterfaceState::RUNNING);
+    netif->setMtu(1500);
+    netif->setEthernetAddress(ethernet::Address{0x00, 0x00, 0x5e, 0x00, 0x53, 0x5e});
+    return netif;
+  }
+};
+
+class EthernetFactoryFixtureWithRealNetifs : public EthernetFactoryFixture
+{
+protected:
+  EthernetFactoryFixtureWithRealNetifs()
+  {
+    copyRealNetifsToNetmon();
   }
 };
 
 BOOST_AUTO_TEST_SUITE(Face)
-BOOST_FIXTURE_TEST_SUITE(TestEthernetFactory, EthernetFactoryFixture)
+BOOST_AUTO_TEST_SUITE(TestEthernetFactory)
 
-BOOST_AUTO_TEST_SUITE(ProcessConfig)
+BOOST_FIXTURE_TEST_SUITE(ProcessConfig, EthernetFactoryFixtureWithRealNetifs)
 
 BOOST_AUTO_TEST_CASE(Defaults)
 {
@@ -79,9 +94,6 @@
     face_system
     {
       ether
-      {
-        mcast no
-      }
     }
   )CONFIG";
 
@@ -92,10 +104,10 @@
   auto channels = factory.getChannels();
   BOOST_CHECK(std::all_of(channels.begin(), channels.end(),
                           [] (const auto& ch) { return ch->isListening(); }));
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), netifs.size());
 }
 
-BOOST_AUTO_TEST_CASE(DisableListen)
+BOOST_AUTO_TEST_CASE(DisableListenAndMcast)
 {
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
 
@@ -118,13 +130,11 @@
   auto channels = factory.getChannels();
   BOOST_CHECK(std::none_of(channels.begin(), channels.end(),
                            [] (const auto& ch) { return ch->isListening(); }));
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(McastNormal)
 {
-  SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
-
   const std::string CONFIG = R"CONFIG(
     face_system
     {
@@ -148,8 +158,9 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), netifs.size());
-  for (const auto& face : this->listEtherMcastFaces()) {
+  auto etherMcastFaces = this->listEtherMcastFaces();
+  BOOST_CHECK_EQUAL(etherMcastFaces.size(), netifs.size());
+  for (const auto* face : etherMcastFaces) {
     BOOST_REQUIRE(face->getChannel().lock());
     // not universal, but for Ethernet, local URI of a mcast face matches URI of the associated channel
     BOOST_CHECK_EQUAL(face->getLocalUri(), face->getChannel().lock()->getUri());
@@ -180,17 +191,17 @@
   )CONFIG";
 
   parseConfig(CONFIG_WITHOUT_MCAST, false);
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
 
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
 
   parseConfig(CONFIG_WITH_MCAST, false);
   g_io.poll();
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), netifs.size());
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), netifs.size());
 
   parseConfig(CONFIG_WITHOUT_MCAST, false);
   g_io.poll();
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(McastAdHoc)
@@ -203,15 +214,14 @@
       ether
       {
         listen no
-        mcast yes
         mcast_ad_hoc yes
       }
     }
   )CONFIG";
 
   parseConfig(CONFIG, false);
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(ndn::nfd::LINK_TYPE_MULTI_ACCESS), 0);
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(ndn::nfd::LINK_TYPE_AD_HOC), netifs.size());
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces(ndn::nfd::LINK_TYPE_MULTI_ACCESS).size(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces(ndn::nfd::LINK_TYPE_AD_HOC).size(), netifs.size());
 }
 
 BOOST_AUTO_TEST_CASE(ChangeMcastGroup)
@@ -238,6 +248,7 @@
   )CONFIG";
 
   parseConfig(CONFIG1, false);
+  g_io.poll();
   auto etherMcastFaces = this->listEtherMcastFaces();
   BOOST_REQUIRE_EQUAL(etherMcastFaces.size(), netifs.size());
   BOOST_CHECK_EQUAL(etherMcastFaces.front()->getRemoteUri(),
@@ -317,7 +328,7 @@
   parseConfig(CONFIG, true);
   parseConfig(CONFIG, false);
 
-  BOOST_CHECK_EQUAL(this->countEtherMcastFaces(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
 }
 
 BOOST_AUTO_TEST_CASE(BadListen)
@@ -432,7 +443,7 @@
 
 BOOST_AUTO_TEST_SUITE_END() // ProcessConfig
 
-BOOST_AUTO_TEST_CASE(GetChannels)
+BOOST_FIXTURE_TEST_CASE(GetChannels, EthernetFactoryFixtureWithRealNetifs)
 {
   BOOST_CHECK_EQUAL(factory.getChannels().empty(), true);
 
@@ -442,22 +453,23 @@
   checkChannelListEqual(factory, {FaceUri::fromDev(netifs.front()->getName()).toString()});
 }
 
-BOOST_AUTO_TEST_CASE(CreateChannel)
+BOOST_FIXTURE_TEST_CASE(CreateChannel, EthernetFactoryFixtureWithRealNetifs)
 {
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
 
   auto channel1 = factory.createChannel(netifs.front(), 1_min);
   auto channel1a = factory.createChannel(netifs.front(), 5_min);
   BOOST_CHECK_EQUAL(channel1, channel1a);
-  BOOST_CHECK_EQUAL(channel1->getUri().toString(), "dev://" + netifs.front()->getName());
+  checkChannelListEqual(factory, {FaceUri::fromDev(netifs.front()->getName()).toString()});
 
   SKIP_IF_ETHERNET_NETIF_COUNT_LT(2);
 
   auto channel2 = factory.createChannel(netifs.back(), 1_min);
   BOOST_CHECK_NE(channel1, channel2);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
 }
 
-BOOST_AUTO_TEST_CASE(CreateFace)
+BOOST_FIXTURE_TEST_CASE(CreateFace, EthernetFactoryFixtureWithRealNetifs)
 {
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
@@ -505,7 +517,7 @@
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
-BOOST_AUTO_TEST_CASE(UnsupportedCreateFace)
+BOOST_FIXTURE_TEST_CASE(CreateFaceInvalidRequest, EthernetFactoryFixture)
 {
   createFace(factory,
              FaceUri("ether://[00:00:5e:00:53:5e]"),
@@ -541,8 +553,119 @@
              {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, true, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Local fields can only be enabled on faces with local scope"});
+
+  createFace(factory,
+             FaceUri("ether://[00:00:5e:00:53:5e]"),
+             FaceUri("dev://eth0"),
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, 42, false, false, false},
+             {CreateFaceExpectedResult::FAILURE, 406,
+              "Override MTU cannot be less than 64"});
 }
 
+BOOST_FIXTURE_TEST_SUITE(OnInterfaceAdded, EthernetFactoryFixture)
+
+BOOST_AUTO_TEST_CASE(EligibleForChannelAndMcast)
+{
+  parseConfig(R"CONFIG(
+    face_system
+    {
+      ether
+    }
+  )CONFIG", false);
+  g_io.poll();
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+
+  // Add a fake interface that satisfies both unicast and multicast criteria.
+  netmon->addInterface(this->makeFakeNetif());
+  // The channel fails to listen because the interface is fake.
+  // However, it's not removed from the channel list (issue #4400).
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 1);
+  // Multicast face creation fails because the interface is fake.
+  // This test is to ensure that the factory handles failures gracefully.
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+
+  SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+  // Now add a real interface: both channel and multicast face should be created successfully.
+  netmon->addInterface(const_pointer_cast<ndn::net::NetworkInterface>(netifs.front()));
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
+  auto etherMcastFaces = this->listEtherMcastFaces();
+  BOOST_REQUIRE_EQUAL(etherMcastFaces.size(), 1);
+  BOOST_CHECK_EQUAL(etherMcastFaces.front()->getLocalUri(), FaceUri::fromDev(netifs.front()->getName()));
+}
+
+BOOST_AUTO_TEST_CASE(EligibleForChannelOnly)
+{
+  SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+  parseConfig(R"CONFIG(
+    face_system
+    {
+      ether
+      {
+        listen no
+      }
+    }
+  )CONFIG", false);
+  g_io.poll();
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+
+  // Add an interface that satisfies only the unicast criteria.
+  auto netif = const_pointer_cast<ndn::net::NetworkInterface>(netifs.front());
+  netif->setFlags(netif->getFlags() & ~IFF_MULTICAST);
+  netmon->addInterface(netif);
+  checkChannelListEqual(factory, {FaceUri::fromDev(netifs.front()->getName()).toString()});
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Ineligible)
+{
+  parseConfig(R"CONFIG(
+    face_system
+    {
+      ether
+    }
+  )CONFIG", false);
+  g_io.poll();
+
+  // netif is down
+  auto netif = this->makeFakeNetif();
+  netif->setFlags(netif->getFlags() & ~IFF_UP);
+  netmon->addInterface(netif);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+
+  // incompatible netif type
+  netif = this->makeFakeNetif();
+  netif->setType(ndn::net::InterfaceType::LOOPBACK);
+  netmon->addInterface(netif);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+
+  // invalid Ethernet address
+  netif = this->makeFakeNetif();
+  netif->setEthernetAddress(ethernet::Address{});
+  netmon->addInterface(netif);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Disabled)
+{
+  SKIP_IF_ETHERNET_NETIF_COUNT_LT(1);
+
+  parseConfig("", false);
+  g_io.poll();
+
+  netmon->addInterface(const_pointer_cast<ndn::net::NetworkInterface>(netifs.front()));
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 0);
+  BOOST_CHECK_EQUAL(this->listEtherMcastFaces().size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // OnInterfaceAdded
+
 BOOST_AUTO_TEST_SUITE_END() // TestEthernetFactory
 BOOST_AUTO_TEST_SUITE_END() // Face
 
diff --git a/tests/daemon/face/tcp-factory.t.cpp b/tests/daemon/face/tcp-factory.t.cpp
index e2aa73d..d7c6a67 100644
--- a/tests/daemon/face/tcp-factory.t.cpp
+++ b/tests/daemon/face/tcp-factory.t.cpp
@@ -350,13 +350,16 @@
   auto channel1a = createChannel("127.0.0.1", "20070");
   BOOST_CHECK_EQUAL(channel1, channel1a);
   BOOST_CHECK_EQUAL(channel1->getUri().toString(), "tcp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 1);
 
   auto channel2 = createChannel("127.0.0.1", "20071");
   BOOST_CHECK_NE(channel1, channel2);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
 
   auto channel3 = createChannel("::1", "20071");
   BOOST_CHECK_NE(channel2, channel3);
   BOOST_CHECK_EQUAL(channel3->getUri().toString(), "tcp6://[::1]:20071");
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 3);
 }
 
 BOOST_AUTO_TEST_CASE(CreateFace)
@@ -400,10 +403,8 @@
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
-BOOST_AUTO_TEST_CASE(UnsupportedCreateFace)
+BOOST_AUTO_TEST_CASE(CreateFaceInvalidRequest)
 {
-  createChannel("127.0.0.1", "20071");
-
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20072"),
              FaceUri("tcp4://127.0.0.1:20071"),
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index fd83656..55d601c 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -32,6 +32,7 @@
 
 namespace nfd::tests {
 
+namespace ip = boost::asio::ip;
 using face::UdpChannel;
 using face::UdpFactory;
 using ndn::net::NetworkInterface;
@@ -42,7 +43,7 @@
   shared_ptr<UdpChannel>
   createChannel(const std::string& localIp, uint16_t localPort)
   {
-    udp::Endpoint endpoint(boost::asio::ip::make_address(localIp), localPort);
+    udp::Endpoint endpoint(ip::make_address(localIp), localPort);
     return factory.createChannel(endpoint, 5_min);
   }
 };
@@ -53,7 +54,7 @@
   UdpFactoryMcastFixture()
   {
     for (const auto& netif : collectNetworkInterfaces()) {
-      // same filtering logic as UdpFactory::applyMcastConfigToNetif()
+      // similar filtering logic to UdpFactory::applyMcastConfigToNetif()
       if (netif->isUp() && !netif->isLoopback() && netif->canMulticast()) {
         bool hasValidIpAddress = false;
         if (hasAddressFamily(*netif, ndn::net::AddressFamily::V4)) {
@@ -69,38 +70,6 @@
         }
       }
     }
-    copyRealNetifsToNetmon();
-  }
-
-  shared_ptr<Face>
-  createMulticastFace(const std::string& localIp, const std::string& mcastIp, uint16_t mcastPort)
-  {
-    auto localAddress = boost::asio::ip::make_address(localIp);
-    udp::Endpoint mcastEndpoint(boost::asio::ip::make_address(mcastIp), mcastPort);
-
-    if (localAddress.is_v4()) {
-      BOOST_ASSERT(!netifsV4.empty());
-      return factory.createMulticastFace(*netifsV4.front(), localAddress, mcastEndpoint);
-    }
-    else {
-      BOOST_ASSERT(!netifsV6.empty());
-      return factory.createMulticastFace(*netifsV6.front(), localAddress, mcastEndpoint);
-    }
-  }
-
-  /** \brief Returns a non-loopback IP address suitable for the creation of a UDP multicast face.
-   */
-  boost::asio::ip::address
-  findNonLoopbackAddressForMulticastFace(ndn::net::AddressFamily af) const
-  {
-    const auto& netifList = af == ndn::net::AddressFamily::V4 ? netifsV4 : netifsV6;
-    for (const auto& netif : netifList) {
-      for (const auto& a : netif->getNetworkAddresses()) {
-        if (a.getFamily() == af && !a.getIp().is_loopback())
-          return a.getIp();
-      }
-    }
-    return {};
   }
 
   std::vector<const Face*>
@@ -115,6 +84,35 @@
     return listFacesByScheme("udp6", linkType);
   }
 
+  shared_ptr<NetworkInterface>
+  makeFakeNetif(bool addNetworkAddresses = true)
+  {
+    using namespace ndn::net;
+
+    static int counter = 0;
+    ++counter;
+
+    auto netif = netmon->makeNetworkInterface();
+    netif->setIndex(1000 + counter);
+    netif->setName("ethdummy" + std::to_string(counter));
+    netif->setType(InterfaceType::ETHERNET);
+    netif->setFlags(IFF_MULTICAST | IFF_UP);
+    netif->setState(InterfaceState::RUNNING);
+    netif->setMtu(1500);
+    netif->setEthernetAddress(ethernet::Address{0x3e, 0x15, 0xc2, 0x8b, 0x65, 0x00});
+    if (addNetworkAddresses) {
+      netif->addNetworkAddress(NetworkAddress(AddressFamily::V4,
+                                              ip::make_address_v4("192.168.2.1"),
+                                              ip::make_address_v4("192.168.2.255"),
+                                              24, AddressScope::GLOBAL, 0));
+      netif->addNetworkAddress(NetworkAddress(AddressFamily::V6,
+                                              ip::make_address_v6("2001:db8:2::1"),
+                                              ip::make_address_v6("2001:db8:2::ffff:ffff:ffff:ffff"),
+                                              64, AddressScope::GLOBAL, 0));
+    }
+    return netif;
+  }
+
   /**
    * \brief Determine whether \p netif has at least one IP address of the given family.
    */
@@ -131,9 +129,9 @@
   static bool
   isFaceOnNetif(const Face& face, const NetworkInterface& netif)
   {
-    auto ip = boost::asio::ip::make_address(face.getLocalUri().getHost());
+    auto faceIp = ip::make_address(face.getLocalUri().getHost());
     return std::any_of(netif.getNetworkAddresses().begin(), netif.getNetworkAddresses().end(),
-                       [ip] (const auto& a) { return a.getIp() == ip; });
+                       [faceIp] (const auto& a) { return a.getIp() == faceIp; });
   }
 
 protected:
@@ -152,6 +150,47 @@
   std::vector<shared_ptr<const NetworkInterface>> netifsV6;
 };
 
+class UdpFactoryMcastFixtureWithRealNetifs : public UdpFactoryMcastFixture
+{
+protected:
+  UdpFactoryMcastFixtureWithRealNetifs()
+  {
+    copyRealNetifsToNetmon();
+  }
+
+  shared_ptr<Face>
+  createMulticastFace(const std::string& localIp, const std::string& mcastIp, uint16_t mcastPort)
+  {
+    auto localAddress = ip::make_address(localIp);
+    udp::Endpoint mcastEndpoint(ip::make_address(mcastIp), mcastPort);
+
+    if (localAddress.is_v4()) {
+      BOOST_ASSERT(!netifsV4.empty());
+      return factory.createMulticastFace(*netifsV4.front(), localAddress, mcastEndpoint);
+    }
+    else {
+      BOOST_ASSERT(!netifsV6.empty());
+      return factory.createMulticastFace(*netifsV6.front(), localAddress, mcastEndpoint);
+    }
+  }
+
+  /**
+   * \brief Returns a non-loopback IP address suitable for the creation of a UDP multicast face.
+   */
+  ip::address
+  findNonLoopbackAddressForMulticastFace(ndn::net::AddressFamily af) const
+  {
+    const auto& netifList = af == ndn::net::AddressFamily::V4 ? netifsV4 : netifsV6;
+    for (const auto& netif : netifList) {
+      for (const auto& a : netif->getNetworkAddresses()) {
+        if (a.getFamily() == af && !a.getIp().is_loopback())
+          return a.getIp();
+      }
+    }
+    return {};
+  }
+};
+
 #define SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(n) \
   do { \
     if (this->netifs.size() < (n)) { \
@@ -180,11 +219,9 @@
   } while (false)
 
 BOOST_AUTO_TEST_SUITE(Face)
-BOOST_FIXTURE_TEST_SUITE(TestUdpFactory, UdpFactoryFixture)
+BOOST_AUTO_TEST_SUITE(TestUdpFactory)
 
-BOOST_AUTO_TEST_SUITE(ProcessConfig)
-
-using nfd::Face;
+BOOST_FIXTURE_TEST_SUITE(ProcessConfig, UdpFactoryFixture)
 
 BOOST_AUTO_TEST_CASE(Defaults)
 {
@@ -278,7 +315,7 @@
   }
 }
 
-BOOST_FIXTURE_TEST_CASE(EnableDisableMcast, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(EnableDisableMcast, UdpFactoryMcastFixtureWithRealNetifs)
 {
   const std::string CONFIG_WITH_MCAST = R"CONFIG(
     face_system
@@ -313,13 +350,13 @@
   BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), netifsV4.size());
   BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), netifsV6.size());
 
-  BOOST_REQUIRE_EQUAL(factory.getChannels().size(), 2);
-  for (const auto& face : this->listUdp4McastFaces()) {
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
+  for (const auto* face : this->listUdp4McastFaces()) {
     BOOST_REQUIRE(face->getChannel().lock());
     BOOST_CHECK_EQUAL(face->getChannel().lock()->getUri().getScheme(), "udp4");
   }
 
-  for (const auto& face : this->listUdp6McastFaces()) {
+  for (const auto* face : this->listUdp6McastFaces()) {
     BOOST_REQUIRE(face->getChannel().lock());
     BOOST_CHECK_EQUAL(face->getChannel().lock()->getUri().getScheme(), "udp6");
   }
@@ -330,7 +367,7 @@
   BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
 }
 
-BOOST_FIXTURE_TEST_CASE(McastAdHoc, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(McastAdHoc, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -349,11 +386,13 @@
   )CONFIG";
 
   parseConfig(CONFIG, false);
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces(ndn::nfd::LINK_TYPE_MULTI_ACCESS).size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces(ndn::nfd::LINK_TYPE_MULTI_ACCESS).size(), 0);
   BOOST_CHECK_EQUAL(this->listUdp4McastFaces(ndn::nfd::LINK_TYPE_AD_HOC).size(), netifsV4.size());
   BOOST_CHECK_EQUAL(this->listUdp6McastFaces(ndn::nfd::LINK_TYPE_AD_HOC).size(), netifsV6.size());
 }
 
-BOOST_FIXTURE_TEST_CASE(ChangeMcastEndpointV4, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(ChangeMcastEndpointV4, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -383,6 +422,7 @@
   )CONFIG";
 
   parseConfig(CONFIG1, false);
+  g_io.poll();
   auto udpMcastFaces = this->listUdp4McastFaces();
   BOOST_REQUIRE_EQUAL(udpMcastFaces.size(), netifsV4.size());
   BOOST_CHECK_EQUAL(udpMcastFaces.front()->getRemoteUri(), FaceUri("udp4://239.66.30.1:7011"));
@@ -394,7 +434,7 @@
   BOOST_CHECK_EQUAL(udpMcastFaces.front()->getRemoteUri(), FaceUri("udp4://239.66.30.2:7012"));
 }
 
-BOOST_FIXTURE_TEST_CASE(ChangeMcastEndpointV6, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(ChangeMcastEndpointV6, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -424,14 +464,15 @@
   )CONFIG";
 
   parseConfig(CONFIG1, false);
+  g_io.poll();
   auto udpMcastFaces = this->listUdp6McastFaces();
   BOOST_REQUIRE_EQUAL(udpMcastFaces.size(), netifsV6.size());
   auto uri = udpMcastFaces.front()->getRemoteUri();
   BOOST_CHECK_EQUAL(uri.getScheme(), "udp6");
   // check the address ignoring the scope id
-  auto addr = boost::asio::ip::make_address_v6(uri.getHost());
+  auto addr = ip::make_address_v6(uri.getHost());
   addr.scope_id(0);
-  BOOST_CHECK_EQUAL(addr, boost::asio::ip::make_address_v6("ff02::1101"));
+  BOOST_CHECK_EQUAL(addr, ip::make_address_v6("ff02::1101"));
   BOOST_CHECK_EQUAL(uri.getPort(), "7011");
 
   parseConfig(CONFIG2, false);
@@ -441,13 +482,13 @@
   uri = udpMcastFaces.front()->getRemoteUri();
   BOOST_CHECK_EQUAL(uri.getScheme(), "udp6");
   // check the address ignoring the scope id
-  addr = boost::asio::ip::make_address_v6(uri.getHost());
+  addr = ip::make_address_v6(uri.getHost());
   addr.scope_id(0);
-  BOOST_CHECK_EQUAL(addr, boost::asio::ip::make_address_v6("ff02::1102"));
+  BOOST_CHECK_EQUAL(addr, ip::make_address_v6("ff02::1102"));
   BOOST_CHECK_EQUAL(uri.getPort(), "7012");
 }
 
-BOOST_FIXTURE_TEST_CASE(Whitelist, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(Whitelist, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -478,10 +519,10 @@
   udpMcastFaces.insert(udpMcastFaces.end(), udpMcastFacesV6.begin(), udpMcastFacesV6.end());
   BOOST_CHECK_GE(udpMcastFaces.size(), 1);
   BOOST_CHECK(std::all_of(udpMcastFaces.begin(), udpMcastFaces.end(),
-                          [this] (const Face* face) { return isFaceOnNetif(*face, *netifs.front()); }));
+                          [this] (const auto* face) { return isFaceOnNetif(*face, *netifs.front()); }));
 }
 
-BOOST_FIXTURE_TEST_CASE(Blacklist, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(Blacklist, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -514,10 +555,10 @@
   udpMcastFaces.insert(udpMcastFaces.end(), udpMcastFacesV6.begin(), udpMcastFacesV6.end());
   BOOST_CHECK_LT(udpMcastFaces.size(), netifsV4.size() + netifsV6.size());
   BOOST_CHECK(std::none_of(udpMcastFaces.begin(), udpMcastFaces.end(),
-                           [this] (const Face* face) { return isFaceOnNetif(*face, *netifs.front()); }));
+                           [this] (const auto* face) { return isFaceOnNetif(*face, *netifs.front()); }));
 }
 
-BOOST_FIXTURE_TEST_CASE(ChangePredicate, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(ChangePredicate, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -542,13 +583,14 @@
   boost::replace_first(CONFIG2, "%ifname", netifs.back()->getName());
 
   parseConfig(CONFIG1, false);
+  g_io.poll();
 
   auto udpMcastFaces = this->listUdp4McastFaces();
   auto udpMcastFacesV6 = this->listUdp6McastFaces();
   udpMcastFaces.insert(udpMcastFaces.end(), udpMcastFacesV6.begin(), udpMcastFacesV6.end());
   BOOST_CHECK_GE(udpMcastFaces.size(), 1);
   BOOST_CHECK(std::all_of(udpMcastFaces.begin(), udpMcastFaces.end(),
-                          [this] (const Face* face) { return isFaceOnNetif(*face, *netifs.front()); }));
+                          [this] (const auto* face) { return isFaceOnNetif(*face, *netifs.front()); }));
 
   parseConfig(CONFIG2, false);
   g_io.poll();
@@ -558,7 +600,7 @@
   udpMcastFaces.insert(udpMcastFaces.end(), udpMcastFacesV6.begin(), udpMcastFacesV6.end());
   BOOST_CHECK_GE(udpMcastFaces.size(), 1);
   BOOST_CHECK(std::all_of(udpMcastFaces.begin(), udpMcastFaces.end(),
-                          [this] (const Face* face) { return isFaceOnNetif(*face, *netifs.back()); }));
+                          [this] (const auto* face) { return isFaceOnNetif(*face, *netifs.back()); }));
 }
 
 BOOST_AUTO_TEST_CASE(Omitted)
@@ -914,7 +956,7 @@
 
 BOOST_AUTO_TEST_SUITE_END() // ProcessConfig
 
-BOOST_AUTO_TEST_CASE(GetChannels)
+BOOST_FIXTURE_TEST_CASE(GetChannels, UdpFactoryFixture)
 {
   BOOST_CHECK_EQUAL(factory.getChannels().empty(), true);
 
@@ -925,47 +967,49 @@
   checkChannelListEqual(factory, expected);
 }
 
-BOOST_FIXTURE_TEST_CASE(CreateChannel, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(CreateChannel, UdpFactoryMcastFixtureWithRealNetifs)
 {
   auto channel1 = createChannel("127.0.0.1", 20070);
   auto channel1a = createChannel("127.0.0.1", 20070);
   BOOST_CHECK_EQUAL(channel1, channel1a);
   BOOST_CHECK_EQUAL(channel1->getUri().toString(), "udp4://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 1);
 
   auto channel2 = createChannel("127.0.0.1", 20071);
   BOOST_CHECK_NE(channel1, channel2);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
 
   auto channel3 = createChannel("::1", 20071);
   BOOST_CHECK_NE(channel2, channel3);
   BOOST_CHECK_EQUAL(channel3->getUri().toString(), "udp6://[::1]:20071");
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 3);
 
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
   SKIP_IF_NOT_SUPERUSER();
 #endif // __linux__
+  SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(1);
 
   // createChannel with a local endpoint that has already been allocated to a UDP multicast face
   if (!netifsV4.empty()) {
     auto mcastFace = createMulticastFace("127.0.0.1", "224.0.0.254", 20072);
     BOOST_CHECK_EXCEPTION(createChannel("127.0.0.1", 20072), UdpFactory::Error,
                           [] (const UdpFactory::Error& e) {
-                            return strcmp(e.what(),
-                                          "Cannot create UDP channel on 127.0.0.1:20072, "
-                                          "endpoint already allocated to a UDP multicast face") == 0;
+                            return e.what() == "Cannot create UDP channel on 127.0.0.1:20072, "
+                                               "endpoint already allocated to a UDP multicast face"s;
                           });
   }
   if (!netifsV6.empty()) {
     auto mcastFace = createMulticastFace("::1", "ff02::114", 20072);
     BOOST_CHECK_EXCEPTION(createChannel("::1", 20072), UdpFactory::Error,
                           [] (const UdpFactory::Error& e) {
-                            return strcmp(e.what(),
-                                          "Cannot create UDP channel on [::1]:20072, "
-                                          "endpoint already allocated to a UDP multicast face") == 0;
+                            return e.what() == "Cannot create UDP channel on [::1]:20072, "
+                                               "endpoint already allocated to a UDP multicast face"s;
                           });
   }
 }
 
-BOOST_FIXTURE_TEST_CASE(CreateMulticastFaceV4, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(CreateMulticastFaceV4, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -990,21 +1034,19 @@
   auto channel = createChannel("127.0.0.1", 20071);
   BOOST_CHECK_EXCEPTION(createMulticastFace("127.0.0.1", "224.0.0.254", 20071), UdpFactory::Error,
                         [] (const UdpFactory::Error& e) {
-                          return strcmp(e.what(),
-                                        "Cannot create UDP multicast face on 127.0.0.1:20071, "
-                                        "endpoint already allocated to a UDP channel") == 0;
+                          return e.what() == "Cannot create UDP multicast face on 127.0.0.1:20071, "
+                                             "endpoint already allocated to a UDP channel"s;
                         });
 
   // create with a local endpoint already used by a multicast face on a different multicast group
   BOOST_CHECK_EXCEPTION(createMulticastFace("127.0.0.1", "224.0.0.42", 20070), UdpFactory::Error,
                         [] (const UdpFactory::Error& e) {
-                          return strcmp(e.what(),
-                                        "Cannot create UDP multicast face on 127.0.0.1:20070, "
-                                        "endpoint already allocated to a different UDP multicast face") == 0;
+                          return e.what() == "Cannot create UDP multicast face on 127.0.0.1:20070, "
+                                             "endpoint already allocated to a different UDP multicast face"s;
                         });
 }
 
-BOOST_FIXTURE_TEST_CASE(CreateMulticastFaceV6, UdpFactoryMcastFixture)
+BOOST_FIXTURE_TEST_CASE(CreateMulticastFaceV6, UdpFactoryMcastFixtureWithRealNetifs)
 {
 #ifdef __linux__
   // need superuser privileges to create multicast faces on Linux
@@ -1029,21 +1071,19 @@
   auto channel = createChannel("::1", 20071);
   BOOST_CHECK_EXCEPTION(createMulticastFace("::1", "ff02::114", 20071), UdpFactory::Error,
                         [] (const UdpFactory::Error& e) {
-                          return strcmp(e.what(),
-                                        "Cannot create UDP multicast face on [::1]:20071, "
-                                        "endpoint already allocated to a UDP channel") == 0;
+                          return e.what() == "Cannot create UDP multicast face on [::1]:20071, "
+                                             "endpoint already allocated to a UDP channel"s;
                         });
 
   // create with a local endpoint already used by a multicast face on a different multicast group
   BOOST_CHECK_EXCEPTION(createMulticastFace("::1", "ff02::42", 20070), UdpFactory::Error,
                         [] (const UdpFactory::Error& e) {
-                          return strcmp(e.what(),
-                                        "Cannot create UDP multicast face on [::1]:20070, "
-                                        "endpoint already allocated to a different UDP multicast face") == 0;
+                          return e.what() == "Cannot create UDP multicast face on [::1]:20070, "
+                                             "endpoint already allocated to a different UDP multicast face"s;
                         });
 }
 
-BOOST_AUTO_TEST_CASE(CreateFace)
+BOOST_FIXTURE_TEST_CASE(CreateFace, UdpFactoryFixture)
 {
   createFace(factory,
              FaceUri("udp4://127.0.0.1:6363"),
@@ -1090,10 +1130,8 @@
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
-BOOST_AUTO_TEST_CASE(UnsupportedCreateFace)
+BOOST_FIXTURE_TEST_CASE(CreateFaceInvalidRequest, UdpFactoryFixture)
 {
-  createChannel("127.0.0.1", 20071);
-
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20072"),
              FaceUri("udp4://127.0.0.1:20071"),
@@ -1121,8 +1159,132 @@
              {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, {}, true, false, false},
              {CreateFaceExpectedResult::FAILURE, 406,
               "Local fields can only be enabled on faces with local scope"});
+
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:20072"),
+             {},
+             {ndn::nfd::FACE_PERSISTENCY_PERSISTENT, {}, {}, 42, false, false, false},
+             {CreateFaceExpectedResult::FAILURE, 406,
+              "Override MTU cannot be less than 64"});
 }
 
+BOOST_FIXTURE_TEST_SUITE(OnInterfaceAdded, UdpFactoryMcastFixture)
+
+BOOST_AUTO_TEST_CASE(EligibleV4)
+{
+#ifdef __linux__
+  // need superuser privileges to create multicast faces on Linux
+  SKIP_IF_NOT_SUPERUSER();
+#endif // __linux__
+  SKIP_IF_UDP_MCAST_V4_NETIF_COUNT_LT(1);
+
+  parseConfig(R"CONFIG(
+    face_system
+    {
+      udp
+    }
+  )CONFIG", false);
+  g_io.poll();
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+
+  netmon->addInterface(const_pointer_cast<NetworkInterface>(netifsV4.front()));
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 1);
+  BOOST_CHECK_LE(this->listUdp6McastFaces().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(EligibleV6)
+{
+#ifdef __linux__
+  // need superuser privileges to create multicast faces on Linux
+  SKIP_IF_NOT_SUPERUSER();
+#endif // __linux__
+  SKIP_IF_UDP_MCAST_V6_NETIF_COUNT_LT(1);
+
+  parseConfig(R"CONFIG(
+    face_system
+    {
+      udp
+    }
+  )CONFIG", false);
+  g_io.poll();
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+
+  netmon->addInterface(const_pointer_cast<NetworkInterface>(netifsV6.front()));
+  BOOST_CHECK_LE(this->listUdp4McastFaces().size(), 1);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(EligibleFailure)
+{
+  parseConfig(R"CONFIG(
+    face_system
+    {
+      udp
+    }
+  )CONFIG", false);
+  g_io.poll();
+
+  // Add a fake interface that satisfies the multicast criteria.
+  netmon->addInterface(this->makeFakeNetif());
+  // Creation of multicast faces fails because the interface does not actually exist.
+  // This test is to ensure that the factory handles failures gracefully.
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Ineligible)
+{
+  parseConfig(R"CONFIG(
+    face_system
+    {
+      udp
+    }
+  )CONFIG", false);
+  g_io.poll();
+
+  // netif is down
+  auto netif = this->makeFakeNetif();
+  netif->setFlags(netif->getFlags() & ~IFF_UP);
+  netmon->addInterface(netif);
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+
+  // netif is loopback
+  netif = this->makeFakeNetif();
+  netif->setFlags(netif->getFlags() | IFF_LOOPBACK);
+  netmon->addInterface(netif);
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+
+  // netif cannot multicast
+  netif = this->makeFakeNetif();
+  netif->setFlags(netif->getFlags() & ~IFF_MULTICAST);
+  netmon->addInterface(netif);
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+
+  // no viable IP address
+  netmon->addInterface(this->makeFakeNetif(false));
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(Disabled)
+{
+  SKIP_IF_UDP_MCAST_NETIF_COUNT_LT(1);
+
+  parseConfig("", false);
+  g_io.poll();
+
+  netmon->addInterface(const_pointer_cast<NetworkInterface>(netifs.front()));
+  BOOST_CHECK_EQUAL(this->listUdp4McastFaces().size(), 0);
+  BOOST_CHECK_EQUAL(this->listUdp6McastFaces().size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // OnInterfaceAdded
+
 BOOST_AUTO_TEST_SUITE_END() // TestUdpFactory
 BOOST_AUTO_TEST_SUITE_END() // Face
 
diff --git a/tests/daemon/face/unix-stream-factory.t.cpp b/tests/daemon/face/unix-stream-factory.t.cpp
index 2554b25..53a8687 100644
--- a/tests/daemon/face/unix-stream-factory.t.cpp
+++ b/tests/daemon/face/unix-stream-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  Regents of the University of California,
+ * Copyright (c) 2014-2023,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -110,6 +110,7 @@
   auto channel1 = factory.createChannel(CHANNEL_PATH1);
   auto channel1a = factory.createChannel(CHANNEL_PATH1);
   BOOST_CHECK_EQUAL(channel1, channel1a);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 1);
 
   const auto& uri = channel1->getUri();
   BOOST_CHECK_EQUAL(uri.getScheme(), "unix");
@@ -119,9 +120,10 @@
 
   auto channel2 = factory.createChannel(CHANNEL_PATH2);
   BOOST_CHECK_NE(channel1, channel2);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
 }
 
-BOOST_AUTO_TEST_CASE(UnsupportedCreateFace)
+BOOST_AUTO_TEST_CASE(CreateFace)
 {
   createFace(factory,
              FaceUri("unix:///var/run/nfd.sock"),
diff --git a/tests/daemon/face/websocket-factory.t.cpp b/tests/daemon/face/websocket-factory.t.cpp
index c133671..b5104ea 100644
--- a/tests/daemon/face/websocket-factory.t.cpp
+++ b/tests/daemon/face/websocket-factory.t.cpp
@@ -286,16 +286,19 @@
   auto channel1a = createChannel("127.0.0.1", "20070");
   BOOST_CHECK_EQUAL(channel1, channel1a);
   BOOST_CHECK_EQUAL(channel1->getUri().toString(), "ws://127.0.0.1:20070");
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 1);
 
   auto channel2 = createChannel("127.0.0.1", "20071");
   BOOST_CHECK_NE(channel1, channel2);
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 2);
 
   auto channel3 = createChannel("::1", "20071");
   BOOST_CHECK_NE(channel2, channel3);
   BOOST_CHECK_EQUAL(channel3->getUri().toString(), "ws://[::1]:20071");
+  BOOST_CHECK_EQUAL(factory.getChannels().size(), 3);
 }
 
-BOOST_AUTO_TEST_CASE(UnsupportedCreateFace)
+BOOST_AUTO_TEST_CASE(CreateFace)
 {
   createFace(factory,
              FaceUri("ws://127.0.0.1:20070"),