mgmt+face: move protocol-specific face creation checks into protocol factories

Also brought implementation of faces/create in line with design

refs #3731

Change-Id: I4f48079136b42c7fdbd4fdfba37116d2565f9dc1
diff --git a/AUTHORS.md b/AUTHORS.md
index 708eeb2..0c36dc7 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -73,7 +73,7 @@
 
 * Tai-Lin Chu             <https://www.linkedin.com/pub/tai-lin-chu/55/5b2/669>
 * Spyridon Mastorakis     <http://cs.ucla.edu/~mastorakis/>
-* Eric Newberry           <http://ericnewberry.com>
+* Eric Newberry           <http://cs.arizona.edu/~enewberry/>
 * Yukai Tu                <tuyukai1994@gmail.com>
 * João Pereira            <http://website.jpereira.co.uk>
 * Mathias Gibbens         <gibmat@cs.arizona.edu>
diff --git a/daemon/face/channel.hpp b/daemon/face/channel.hpp
index 640395a..19beb7f 100644
--- a/daemon/face/channel.hpp
+++ b/daemon/face/channel.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -41,7 +41,7 @@
  * \brief Prototype for the callback that is invoked when the face
  *        fails to be created
  */
-typedef function<void(const std::string& reason)> FaceCreationFailedCallback;
+typedef function<void(uint32_t status, const std::string& reason)> FaceCreationFailedCallback;
 
 
 class Channel : noncopyable
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index dd9d034..85f00f1 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -59,9 +59,9 @@
 EthernetFactory::createFace(const FaceUri& uri,
                             ndn::nfd::FacePersistency persistency,
                             const FaceCreatedCallback& onCreated,
-                            const FaceCreationFailedCallback& onConnectFailed)
+                            const FaceCreationFailedCallback& onFailure)
 {
-  BOOST_THROW_EXCEPTION(Error("EthernetFactory does not support 'createFace' operation"));
+  onFailure(406, "Unsupported protocol");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index b44c2b7..f29e09a 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -80,7 +80,7 @@
   createFace(const FaceUri& uri,
              ndn::nfd::FacePersistency persistency,
              const FaceCreatedCallback& onCreated,
-             const FaceCreationFailedCallback& onConnectFailed) override;
+             const FaceCreationFailedCallback& onFailure) override;
 
   virtual std::vector<shared_ptr<const Channel>>
   getChannels() const override;
diff --git a/daemon/face/tcp-channel.cpp b/daemon/face/tcp-channel.cpp
index 21943c8..163b0e9 100644
--- a/daemon/face/tcp-channel.cpp
+++ b/daemon/face/tcp-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -151,7 +151,7 @@
 
     NFD_LOG_DEBUG("[" << m_localEndpoint << "] Accept failed: " << error.message());
     if (onAcceptFailed)
-      onAcceptFailed(error.message());
+      onAcceptFailed(500, "Accept failed: " + error.message());
     return;
   }
 
@@ -189,7 +189,7 @@
     socket->close();
 
     if (onConnectFailed)
-      onConnectFailed(error.message());
+      onConnectFailed(504, "Connect failed: " + error.message());
     return;
   }
 
@@ -209,7 +209,7 @@
   socket->close(error);
 
   if (onConnectFailed)
-    onConnectFailed("Connect to remote endpoint timed out");
+    onConnectFailed(504, "Connect to remote endpoint timed out");
 }
 
 } // namespace nfd
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index a0fad93..5c96492 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -105,15 +105,24 @@
   BOOST_ASSERT(uri.isCanonical());
 
   if (persistency != ndn::nfd::FACE_PERSISTENCY_PERSISTENT) {
-    BOOST_THROW_EXCEPTION(Error("TcpFactory::createFace supports only FACE_PERSISTENCY_PERSISTENT"));
+    NFD_LOG_TRACE("createFace only supports FACE_PERSISTENCY_PERSISTENT");
+    onConnectFailed(406, "Outgoing TCP faces only support persistent persistency");
+    return;
   }
 
   tcp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
                          boost::lexical_cast<uint16_t>(uri.getPort()));
 
+  if (endpoint.address().is_multicast()) {
+   NFD_LOG_TRACE("createFace cannot create multicast faces");
+   onConnectFailed(406, "Cannot create multicast TCP faces");
+   return;
+  }
+
   if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
-    onConnectFailed("Requested endpoint is prohibited "
-                    "(reserved by this NFD or disallowed by face management protocol)");
+    NFD_LOG_TRACE("Requested endpoint is prohibited "
+                  "(reserved by NFD or disallowed by face management protocol)");
+    onConnectFailed(406, "Requested endpoint is prohibited");
     return;
   }
 
@@ -126,7 +135,8 @@
     }
   }
 
-  onConnectFailed("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  onConnectFailed(504, "No channels available to connect");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index e6d65df..9b66b0d 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -62,7 +62,7 @@
   catch (const boost::system::system_error& e) {
     NFD_LOG_WARN("[" << m_localEndpoint << "] Connect failed: " << e.what());
     if (onConnectFailed)
-      onConnectFailed(e.what());
+      onConnectFailed(504, std::string("Connect failed: ") + e.what());
     return;
   }
 
@@ -113,7 +113,7 @@
 
     NFD_LOG_DEBUG("[" << m_localEndpoint << "] Receive failed: " << error.message());
     if (onReceiveFailed)
-      onReceiveFailed(error.message());
+      onReceiveFailed(500, "Receive failed: " + error.message());
     return;
   }
 
@@ -128,7 +128,7 @@
     NFD_LOG_WARN("[" << m_localEndpoint << "] Failed to create face for peer "
                  << m_remoteEndpoint << ": " << e.what());
     if (onReceiveFailed)
-      onReceiveFailed(e.what());
+      onReceiveFailed(504, "Failed to create face for peer");
     return;
   }
 
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index e905a0b..499c72c 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -239,20 +239,24 @@
   BOOST_ASSERT(uri.isCanonical());
 
   if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
-    BOOST_THROW_EXCEPTION(Error("UdpFactory::createFace does not support FACE_PERSISTENCY_ON_DEMAND"));
+    NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
+    onConnectFailed(406, "Outgoing unicast UDP faces do not support on-demand persistency");
+    return;
   }
 
   udp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
                          boost::lexical_cast<uint16_t>(uri.getPort()));
 
   if (endpoint.address().is_multicast()) {
-    onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
+    NFD_LOG_TRACE("createFace does not support multicast faces");
+    onConnectFailed(406, "Cannot create multicast UDP faces");
     return;
   }
 
   if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
-    onConnectFailed("Requested endpoint is prohibited "
-                    "(reserved by this NFD or disallowed by face management protocol)");
+    NFD_LOG_TRACE("Requested endpoint is prohibited "
+                  "(reserved by this NFD or disallowed by face management protocol)");
+    onConnectFailed(406, "Requested endpoint is prohibited");
     return;
   }
 
@@ -265,7 +269,8 @@
     }
   }
 
-  onConnectFailed("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  onConnectFailed(504, "No channels available to connect");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/unix-stream-channel.cpp b/daemon/face/unix-stream-channel.cpp
index 5b56c6a..85416fd 100644
--- a/daemon/face/unix-stream-channel.cpp
+++ b/daemon/face/unix-stream-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -126,7 +126,7 @@
 
     NFD_LOG_DEBUG("[" << m_endpoint << "] Accept failed: " << error.message());
     if (onAcceptFailed)
-      onAcceptFailed(error.message());
+      onAcceptFailed(500, "Accept failed: " + error.message());
     return;
   }
 
diff --git a/daemon/face/unix-stream-factory.cpp b/daemon/face/unix-stream-factory.cpp
index 3de85b4..b85953e 100644
--- a/daemon/face/unix-stream-factory.cpp
+++ b/daemon/face/unix-stream-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -51,7 +51,7 @@
                               const FaceCreatedCallback& onCreated,
                               const FaceCreationFailedCallback& onConnectFailed)
 {
-  BOOST_THROW_EXCEPTION(Error("UnixStreamFactory does not support 'createFace' operation"));
+  onConnectFailed(406, "Unsupported protocol");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index bcb3970..cea0078 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -56,7 +56,7 @@
                              const FaceCreatedCallback& onCreated,
                              const FaceCreationFailedCallback& onConnectFailed)
 {
-  BOOST_THROW_EXCEPTION(Error("WebSocketFactory does not support 'createFace' operation"));
+  onConnectFailed(406, "Unsupported protocol");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 8b7057c..9cd0e07 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -91,17 +91,21 @@
   FaceUri uri;
   if (!uri.parse(parameters.getUri())) {
     NFD_LOG_TRACE("failed to parse URI");
-    return done(ControlResponse(400, "Malformed command"));
+    done(ControlResponse(400, "Malformed command"));
+    return;
   }
 
   if (!uri.isCanonical()) {
     NFD_LOG_TRACE("received non-canonical URI");
-    return done(ControlResponse(400, "Non-canonical URI"));
+    done(ControlResponse(400, "Non-canonical URI"));
+    return;
   }
 
   auto factory = m_factories.find(uri.getScheme());
   if (factory == m_factories.end()) {
-    return done(ControlResponse(501, "Unsupported protocol"));
+    NFD_LOG_TRACE("received create request for unsupported protocol");
+    done(ControlResponse(406, "Unsupported protocol"));
+    return;
   }
 
   try {
@@ -110,35 +114,50 @@
                                 bind(&FaceManager::afterCreateFaceSuccess,
                                      this, parameters, _1, done),
                                 bind(&FaceManager::afterCreateFaceFailure,
-                                     this, _1, done));
+                                     this, _1, _2, done));
   }
   catch (const std::runtime_error& error) {
-    std::string errorMessage = "Face creation failed: ";
-    errorMessage += error.what();
-
-    NFD_LOG_ERROR(errorMessage);
-    return done(ControlResponse(500, errorMessage));
+    NFD_LOG_ERROR("Face creation failed: " << error.what());
+    done(ControlResponse(500, "Face creation failed due to internal error"));
+    return;
   }
   catch (const std::logic_error& error) {
-    std::string errorMessage = "Face creation failed: ";
-    errorMessage += error.what();
-
-    NFD_LOG_ERROR(errorMessage);
-    return done(ControlResponse(500, errorMessage));
+    NFD_LOG_ERROR("Face creation failed: " << error.what());
+    done(ControlResponse(500, "Face creation failed due to internal error"));
+    return;
   }
 }
 
+/**
+ * \todo #3232
+ *       If the creation of this face would conflict with an existing face (e.g. same underlying
+ *       protocol and remote address, or a NIC-associated permanent face), the command will fail
+ *       with StatusCode 409.
+ */
 void
-FaceManager::afterCreateFaceSuccess(ControlParameters& parameters,
+FaceManager::afterCreateFaceSuccess(const ControlParameters& parameters,
                                     const shared_ptr<Face>& newFace,
                                     const ndn::mgmt::CommandContinuation& done)
 {
-  m_faceTable.add(newFace);
-  parameters.setFaceId(newFace->getId());
-  parameters.setUri(newFace->getRemoteUri().toString());
-  parameters.setFacePersistency(newFace->getPersistency());
+  ControlParameters response;
 
-  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
+  m_faceTable.add(newFace);
+
+  // Set ControlResponse parameters
+  response.setFaceId(newFace->getId());
+  response.setFacePersistency(newFace->getPersistency());
+
+  done(ControlResponse(200, "OK").setBody(response.wireEncode()));
+}
+
+void
+FaceManager::afterCreateFaceFailure(uint32_t status,
+                                    const std::string& reason,
+                                    const ndn::mgmt::CommandContinuation& done)
+{
+  NFD_LOG_DEBUG("Face creation failed: " << reason);
+
+  done(ControlResponse(status, reason));
 }
 
 void
@@ -155,15 +174,6 @@
 }
 
 void
-FaceManager::afterCreateFaceFailure(const std::string& reason,
-                                    const ndn::mgmt::CommandContinuation& done)
-{
-  NFD_LOG_DEBUG("Failed to create face: " << reason);
-
-  done(ControlResponse(408, "Failed to create face: " + reason));
-}
-
-void
 FaceManager::enableLocalControl(const Name& topPrefix, const Interest& interest,
                                 const ControlParameters& parameters,
                                 const ndn::mgmt::CommandContinuation& done)
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index f31f04b..094f288 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -77,12 +77,13 @@
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE: // helpers for ControlCommand
   void
-  afterCreateFaceSuccess(ControlParameters& parameters,
+  afterCreateFaceSuccess(const ControlParameters& parameters,
                          const shared_ptr<Face>& newFace,
                          const ndn::mgmt::CommandContinuation& done);
 
   void
-  afterCreateFaceFailure(const std::string& reason,
+  afterCreateFaceFailure(uint32_t status,
+                         const std::string& reason,
                          const ndn::mgmt::CommandContinuation& done);
 
   Face*
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index 6148b3b..b9e9cda 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,6 +25,7 @@
 
 #include "face/ethernet-factory.hpp"
 
+#include "factory-test-common.hpp"
 #include "network-interface-fixture.hpp"
 
 namespace nfd {
@@ -65,23 +66,20 @@
 {
   EthernetFactory factory;
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("ether://[08:00:27:01:01:01]"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERMANENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("ether://[08:00:27:01:01:01]"),
+             ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("ether://[08:00:27:01:01:01]"),
-                                       ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("ether://[08:00:27:01:01:01]"),
+             ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("ether://[08:00:27:01:01:01]"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("ether://[08:00:27:01:01:01]"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestEthernetFactory
diff --git a/tests/daemon/face/factory-test-common.hpp b/tests/daemon/face/factory-test-common.hpp
new file mode 100644
index 0000000..8676be5
--- /dev/null
+++ b/tests/daemon/face/factory-test-common.hpp
@@ -0,0 +1,68 @@
+/* -*- 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_FACTORY_TEST_COMMON_HPP
+#define NFD_TESTS_DAEMON_FACE_FACTORY_TEST_COMMON_HPP
+
+#include "face/protocol-factory.hpp"
+
+#include "tests/test-common.hpp"
+
+namespace nfd {
+namespace tests {
+
+struct CreateFaceExpectedResult
+{
+  enum { FAILURE, SUCCESS } result;
+  uint32_t status;
+  std::string reason;
+};
+
+inline void
+createFace(ProtocolFactory& factory,
+           const FaceUri& uri,
+           ndn::nfd::FacePersistency persistency,
+           const CreateFaceExpectedResult& expected)
+{
+  factory.createFace(uri, persistency,
+                     [expected] (const shared_ptr<Face>& newFace) {
+                       BOOST_CHECK_EQUAL(CreateFaceExpectedResult::SUCCESS, expected.result);
+                     },
+                     [expected] (uint32_t actualStatus, const std::string& actualReason) {
+                       BOOST_CHECK_EQUAL(CreateFaceExpectedResult::FAILURE, expected.result);
+                       BOOST_CHECK_EQUAL(actualStatus, expected.status);
+                       BOOST_CHECK_EQUAL(actualReason, expected.reason);
+                     });
+}
+
+inline void
+failIfError(uint32_t status, const std::string& reason) {
+  BOOST_FAIL("No error expected, but got: [" << status << ": " << reason << "]");
+}
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_FACE_FACTORY_TEST_COMMON_HPP
diff --git a/tests/daemon/face/tcp-channel.t.cpp b/tests/daemon/face/tcp-channel.t.cpp
index f829aa2..d04ea26 100644
--- a/tests/daemon/face/tcp-channel.t.cpp
+++ b/tests/daemon/face/tcp-channel.t.cpp
@@ -27,7 +27,7 @@
 
 #include "test-ip.hpp"
 #include "tests/limited-io.hpp"
-#include "tests/test-common.hpp"
+#include "factory-test-common.hpp"
 
 #include <boost/mpl/vector.hpp>
 
@@ -71,10 +71,7 @@
         listenerFaces.push_back(newFace);
         limitedIo.afterOp();
       },
-      [this] (const std::string& reason) {
-        BOOST_FAIL(reason);
-        limitedIo.afterOp();
-      });
+      &failIfError);
   }
 
   void
@@ -87,10 +84,7 @@
         clientFaces.push_back(newFace);
         limitedIo.afterOp();
       },
-      [this] (const std::string& reason) {
-        BOOST_FAIL(reason);
-        limitedIo.afterOp();
-      });
+      &failIfError);
   }
 
 protected:
@@ -188,7 +182,7 @@
       BOOST_FAIL("Connect succeeded when it should have failed");
       this->limitedIo.afterOp();
     },
-    [this] (const std::string& reason) {
+    [this] (uint32_t status, const std::string& reason) {
       BOOST_CHECK_EQUAL(reason.empty(), false);
       this->limitedIo.afterOp();
     },
diff --git a/tests/daemon/face/tcp-factory.t.cpp b/tests/daemon/face/tcp-factory.t.cpp
index ee796ab..7418b26 100644
--- a/tests/daemon/face/tcp-factory.t.cpp
+++ b/tests/daemon/face/tcp-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -26,7 +26,7 @@
 #include "face/tcp-factory.hpp"
 
 #include "core/network-interface.hpp"
-#include "tests/test-common.hpp"
+#include "factory-test-common.hpp"
 #include "tests/limited-io.hpp"
 
 namespace nfd {
@@ -72,38 +72,21 @@
   BOOST_CHECK_EQUAL(expectedChannels.size(), 0);
 }
 
-class FaceCreateFixture : public BaseFixture
-{
-public:
-  void
-  checkError(const std::string& errorActual, const std::string& errorExpected)
-  {
-    BOOST_CHECK_EQUAL(errorActual, errorExpected);
-  }
-
-  void
-  failIfError(const std::string& errorActual)
-  {
-    BOOST_FAIL("No error expected, but got: [" << errorActual << "]");
-  }
-};
-
-BOOST_FIXTURE_TEST_CASE(FaceCreate, FaceCreateFixture)
+BOOST_AUTO_TEST_CASE(FaceCreate)
 {
   TcpFactory factory;
 
-  factory.createFace(FaceUri("tcp4://127.0.0.1:6363"),
-                     ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-                     bind([]{}),
-                     bind(&FaceCreateFixture::checkError, this, _1,
-                          "No channels available to connect to 127.0.0.1:6363"));
+  createFace(factory,
+             FaceUri("tcp4://127.0.0.1:6363"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             {CreateFaceExpectedResult::FAILURE, 504, "No channels available to connect"});
 
   factory.createChannel("127.0.0.1", "20071");
 
-  factory.createFace(FaceUri("tcp4://127.0.0.1:20070"),
-                     ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-                     bind([]{}),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
+  createFace(factory,
+             FaceUri("tcp4://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
 BOOST_AUTO_TEST_CASE(UnsupportedFaceCreate)
@@ -113,17 +96,17 @@
   factory.createChannel("127.0.0.1", "20070");
   factory.createChannel("127.0.0.1", "20071");
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("tcp4://127.0.0.1:20070"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERMANENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("tcp4://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+             {CreateFaceExpectedResult::FAILURE, 406,
+               "Outgoing TCP faces only support persistent persistency"});
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("tcp4://127.0.0.1:20071"),
-                                       ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("tcp4://127.0.0.1:20071"),
+             ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
+             {CreateFaceExpectedResult::FAILURE, 406,
+               "Outgoing TCP faces only support persistent persistency"});
 }
 
 class FaceCreateTimeoutFixture : public BaseFixture
@@ -161,7 +144,7 @@
   factory.createFace(FaceUri("tcp4://192.0.2.1:20070"),
                      ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                      bind(&FaceCreateTimeoutFixture::onFaceCreated, this, _1),
-                     bind(&FaceCreateTimeoutFixture::onConnectFailed, this, _1));
+                     bind(&FaceCreateTimeoutFixture::onConnectFailed, this, _2));
 
   BOOST_CHECK_MESSAGE(limitedIo.run(1, time::seconds(10)) == LimitedIo::EXCEED_OPS,
                       "TcpChannel error: cannot connect or cannot accept connection");
diff --git a/tests/daemon/face/udp-channel.t.cpp b/tests/daemon/face/udp-channel.t.cpp
index d945012..1438b88 100644
--- a/tests/daemon/face/udp-channel.t.cpp
+++ b/tests/daemon/face/udp-channel.t.cpp
@@ -28,7 +28,7 @@
 
 #include "test-ip.hpp"
 #include "tests/limited-io.hpp"
-#include "tests/test-common.hpp"
+#include "factory-test-common.hpp"
 
 #include <boost/mpl/vector.hpp>
 
@@ -72,9 +72,7 @@
         listenerFaces.push_back(newFace);
         limitedIo.afterOp();
       },
-      [] (const std::string& reason) {
-        BOOST_FAIL(reason);
-      });
+      &failIfError);
   }
 
   void
@@ -90,9 +88,7 @@
           newFace->getTransport()->send(std::move(pkt));
           limitedIo.afterOp();
         },
-        [] (const std::string& reason) {
-          BOOST_FAIL(reason);
-        });
+        &failIfError);
     });
   }
 
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index be7a73b..eb27f3a 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -25,8 +25,8 @@
 
 #include "face/udp-factory.hpp"
 
+#include "factory-test-common.hpp"
 #include "core/network-interface.hpp"
-#include "tests/test-common.hpp"
 #include "tests/limited-io.hpp"
 
 namespace nfd {
@@ -144,61 +144,44 @@
                         });
 }
 
-class FaceCreateFixture : protected BaseFixture
-{
-public:
-  void
-  checkError(const std::string& errorActual, const std::string& errorExpected)
-  {
-    BOOST_CHECK_EQUAL(errorActual, errorExpected);
-  }
-
-  void
-  failIfError(const std::string& errorActual)
-  {
-    BOOST_FAIL("No error expected, but got: [" << errorActual << "]");
-  }
-};
-
-BOOST_FIXTURE_TEST_CASE(FaceCreate, FaceCreateFixture)
+BOOST_AUTO_TEST_CASE(FaceCreate)
 {
   UdpFactory factory = UdpFactory();
 
-  factory.createFace(FaceUri("udp4://127.0.0.1:6363"),
-                     ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-                     bind([]{}),
-                     bind(&FaceCreateFixture::checkError, this, _1,
-                          "No channels available to connect to 127.0.0.1:6363"));
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:6363"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             {CreateFaceExpectedResult::FAILURE, 504, "No channels available to connect"});
 
   factory.createChannel("127.0.0.1", "20071");
 
-  factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
-                     ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-                     bind([]{}),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             {CreateFaceExpectedResult::SUCCESS, 0, ""});
   //test the upgrade
-  factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
-                     ndn::nfd::FACE_PERSISTENCY_PERMANENT,
-                     bind([]{}),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+             {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
-  factory.createFace(FaceUri("udp4://127.0.0.1:20072"),
-                     ndn::nfd::FACE_PERSISTENCY_PERMANENT,
-                     bind([]{}),
-                     bind(&FaceCreateFixture::failIfError, this, _1));
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:20072"),
+             ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+             {CreateFaceExpectedResult::SUCCESS, 0, ""});
 }
 
 BOOST_AUTO_TEST_CASE(UnsupportedFaceCreate)
 {
-  UdpFactory factory;
+  UdpFactory factory = UdpFactory();
 
   factory.createChannel("127.0.0.1", "20070");
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("udp4://127.0.0.1:20070"),
-                                       ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
+             {CreateFaceExpectedResult::FAILURE, 406,
+               "Outgoing unicast UDP faces do not support on-demand persistency"});
 }
 
 class FakeNetworkInterfaceFixture : public BaseFixture
diff --git a/tests/daemon/face/unix-stream-channel.t.cpp b/tests/daemon/face/unix-stream-channel.t.cpp
index 6d523ff..37eae6d 100644
--- a/tests/daemon/face/unix-stream-channel.t.cpp
+++ b/tests/daemon/face/unix-stream-channel.t.cpp
@@ -26,7 +26,7 @@
 #include "face/unix-stream-channel.hpp"
 
 #include "tests/limited-io.hpp"
-#include "tests/test-common.hpp"
+#include "factory-test-common.hpp"
 
 #include <boost/filesystem.hpp>
 #include <fstream>
@@ -65,10 +65,7 @@
         listenerFaces.push_back(newFace);
         limitedIo.afterOp();
       },
-      [this] (const std::string& reason) {
-        BOOST_FAIL(reason);
-        limitedIo.afterOp();
-      });
+      &failIfError);
   }
 
   void
diff --git a/tests/daemon/face/unix-stream-factory.t.cpp b/tests/daemon/face/unix-stream-factory.t.cpp
index 2be2536..1c33122 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-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,7 +25,7 @@
 
 #include "face/unix-stream-factory.hpp"
 
-#include "tests/test-common.hpp"
+#include "factory-test-common.hpp"
 #include "tests/limited-io.hpp"
 
 namespace nfd {
@@ -77,23 +77,20 @@
 {
   UnixStreamFactory factory;
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("unix:///var/run/nfd.sock"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERMANENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("unix:///var/run/nfd.sock"),
+             ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("unix:///var/run/nfd.sock"),
-                                       ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("unix:///var/run/nfd.sock"),
+             ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("unix:///var/run/nfd.sock"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("unix:///var/run/nfd.sock"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestUnixStreamFactory
diff --git a/tests/daemon/face/websocket-factory.t.cpp b/tests/daemon/face/websocket-factory.t.cpp
index 9e469e8..cb00922 100644
--- a/tests/daemon/face/websocket-factory.t.cpp
+++ b/tests/daemon/face/websocket-factory.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,7 +25,7 @@
 
 #include "face/websocket-factory.hpp"
 
-#include "tests/test-common.hpp"
+#include "factory-test-common.hpp"
 #include "tests/limited-io.hpp"
 
 namespace nfd {
@@ -59,23 +59,20 @@
 {
   WebSocketFactory factory;
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("ws://127.0.0.1:20070"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERMANENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("ws://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_PERMANENT,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("ws://127.0.0.1:20070"),
-                                       ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("ws://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
-  BOOST_CHECK_THROW(factory.createFace(FaceUri("ws://127.0.0.1:20070"),
-                                       ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
-                                       bind([]{}),
-                                       bind([]{})),
-                    ProtocolFactory::Error);
+  createFace(factory,
+             FaceUri("ws://127.0.0.1:20070"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestWebSocketFactory
diff --git a/tests/daemon/mgmt/face-manager-command-fixture.cpp b/tests/daemon/mgmt/face-manager-command-fixture.cpp
new file mode 100644
index 0000000..1b73841
--- /dev/null
+++ b/tests/daemon/mgmt/face-manager-command-fixture.cpp
@@ -0,0 +1,100 @@
+/* -*- 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-manager-command-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+FaceManagerCommandNode::FaceManagerCommandNode(ndn::KeyChain& keyChain, uint16_t port)
+  : face(getGlobalIoService(), keyChain, {true, true})
+  , dispatcher(face, keyChain, ndn::security::SigningInfo())
+  , authenticator(CommandAuthenticator::create())
+  , manager(faceTable, dispatcher, *authenticator)
+{
+  dispatcher.addTopPrefix("/localhost/nfd");
+
+  std::string config =
+    "face_system\n"
+    "{\n"
+    "  tcp\n"
+    "  {\n"
+    "    port " + to_string(port) + "\n"
+    "  }\n"
+    "  udp\n"
+    "  {\n"
+    "    port " + to_string(port) + "\n"
+    "    mcast no\n"
+    "  }\n"
+    "  ether\n"
+    "  {\n"
+    "    mcast no\n"
+    "  }\n"
+    "}\n"
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile any\n"
+    "    privileges\n"
+    "    {\n"
+    "      faces\n"
+    "    }\n"
+    "  }\n"
+    "}\n"
+    "\n";
+
+  ConfigFile cf;
+  manager.setConfigFile(cf);
+  authenticator->setConfigFile(cf);
+  cf.parse(config, false, "dummy-config");
+}
+
+FaceManagerCommandNode::~FaceManagerCommandNode()
+{
+  // Explicitly closing faces is necessary. Otherwise, in a subsequent test case,
+  // incoming packets may be delivered to an old socket from a previous test case.
+  // This should be handled by the FaceTable destructor - see #2517
+  std::vector<std::reference_wrapper<Face>> facesToClose;
+  std::copy(faceTable.begin(), faceTable.end(), std::back_inserter(facesToClose));
+  for (Face& face : facesToClose) {
+    face.close();
+  }
+}
+
+FaceManagerCommandFixture::FaceManagerCommandFixture()
+  : node1(m_keyChain, 16363)
+  , node2(m_keyChain, 26363)
+{
+  advanceClocks(time::milliseconds(1), 5);
+}
+
+FaceManagerCommandFixture::~FaceManagerCommandFixture()
+{
+  advanceClocks(time::milliseconds(1), 5);
+}
+
+} // namespace tests
+} // namespace nfd
diff --git a/tests/daemon/mgmt/face-manager-command-fixture.hpp b/tests/daemon/mgmt/face-manager-command-fixture.hpp
new file mode 100644
index 0000000..fcb89a4
--- /dev/null
+++ b/tests/daemon/mgmt/face-manager-command-fixture.hpp
@@ -0,0 +1,71 @@
+/* -*- 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_MGMT_FACE_MANAGER_COMMAND_FIXTURE_HPP
+#define NFD_TESTS_DAEMON_MGMT_FACE_MANAGER_COMMAND_FIXTURE_HPP
+
+#include "mgmt/face-manager.hpp"
+#include "fw/face-table.hpp"
+
+#include <ndn-cxx/mgmt/dispatcher.hpp>
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include "tests/identity-management-fixture.hpp"
+
+namespace nfd {
+namespace tests {
+
+class FaceManagerCommandNode
+{
+public:
+  explicit
+  FaceManagerCommandNode(ndn::KeyChain& keyChain, uint16_t port);
+
+  ~FaceManagerCommandNode();
+
+public:
+  FaceTable faceTable;
+  ndn::util::DummyClientFace face;
+  ndn::mgmt::Dispatcher dispatcher;
+  shared_ptr<CommandAuthenticator> authenticator;
+  FaceManager manager;
+};
+
+class FaceManagerCommandFixture : public IdentityManagementTimeFixture
+{
+public:
+  FaceManagerCommandFixture();
+
+  ~FaceManagerCommandFixture();
+
+public:
+  FaceManagerCommandNode node1; // used to test FaceManager
+  FaceManagerCommandNode node2; // acts as a remote endpoint
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_DAEMON_MGMT_FACE_MANAGER_COMMAND_FIXTURE_HPP
diff --git a/tests/daemon/mgmt/face-manager-create-face.t.cpp b/tests/daemon/mgmt/face-manager-create-face.t.cpp
index ef59037..052b89c 100644
--- a/tests/daemon/mgmt/face-manager-create-face.t.cpp
+++ b/tests/daemon/mgmt/face-manager-create-face.t.cpp
@@ -23,16 +23,10 @@
  * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "mgmt/face-manager.hpp"
-#include "fw/face-table.hpp"
-#include <ndn-cxx/mgmt/dispatcher.hpp>
-#include <ndn-cxx/util/dummy-client-face.hpp>
+#include "face-manager-command-fixture.hpp"
+#include "nfd-manager-common-fixture.hpp"
 
 #include <thread>
-#include <boost/property_tree/info_parser.hpp>
-
-#include "tests/test-common.hpp"
-#include "tests/identity-management-fixture.hpp"
 
 namespace nfd {
 namespace tests {
@@ -42,98 +36,6 @@
 
 BOOST_FIXTURE_TEST_SUITE(CreateFace, BaseFixture)
 
-class FaceManagerNode
-{
-public:
-  FaceManagerNode(ndn::KeyChain& keyChain, const std::string& port = "6363")
-    : face(getGlobalIoService(), keyChain, {true, true})
-    , dispatcher(face, keyChain, ndn::security::SigningInfo())
-    , authenticator(CommandAuthenticator::create())
-    , manager(faceTable, dispatcher, *authenticator)
-  {
-    dispatcher.addTopPrefix("/localhost/nfd");
-
-    std::string basicConfig =
-      "face_system\n"
-      "{\n"
-      "  tcp\n"
-      "  {\n"
-      "    port " + port + "\n"
-      "  }\n"
-      "  udp\n"
-      "  {\n"
-      "    port " + port + "\n"
-      "    mcast no\n"
-      "  }\n"
-      "  ether\n"
-      "  {\n"
-      "    mcast no\n"
-      "  }\n"
-      "}\n"
-      "authorizations\n"
-      "{\n"
-      "  authorize\n"
-      "  {\n"
-      "    certfile any\n"
-      "    privileges\n"
-      "    {\n"
-      "      faces\n"
-      "    }\n"
-      "  }\n"
-      "}\n"
-      "\n";
-    std::istringstream input(basicConfig);
-    nfd::ConfigSection configSection;
-    boost::property_tree::read_info(input, configSection);
-
-    ConfigFile config;
-    manager.setConfigFile(config);
-    authenticator->setConfigFile(config);
-    config.parse(configSection, false, "dummy-config");
-  }
-
-  void
-  closeFaces()
-  {
-    std::vector<std::reference_wrapper<Face>> facesToClose;
-    std::copy(faceTable.begin(), faceTable.end(), std::back_inserter(facesToClose));
-    for (Face& face : facesToClose) {
-      face.close();
-    }
-  }
-
-public:
-  FaceTable faceTable;
-  ndn::util::DummyClientFace face;
-  ndn::mgmt::Dispatcher dispatcher;
-  shared_ptr<CommandAuthenticator> authenticator;
-  FaceManager manager;
-};
-
-class FaceManagerFixture : public IdentityManagementTimeFixture
-{
-public:
-  FaceManagerFixture()
-    : node1(m_keyChain, "16363")
-    , node2(m_keyChain, "26363")
-  {
-    advanceClocks(time::milliseconds(1), 5);
-  }
-
-  ~FaceManagerFixture()
-  {
-    // Explicitly closing faces is necessary. Otherwise, in a subsequent test case,
-    // incoming packets may be delivered to an old socket from previous test cases.
-    node1.closeFaces();
-    node2.closeFaces();
-    advanceClocks(time::milliseconds(1), 5);
-  }
-
-public:
-  FaceManagerNode node1; // used to test FaceManager
-  FaceManagerNode node2; // acts as a remote endpoint
-};
-
 class TcpFaceOnDemand
 {
 public:
@@ -182,7 +84,8 @@
   }
 };
 
-class UdpFaceCannotConnect // face that will cause afterCreateFaceFailure to be invoked
+class UdpFaceConnectToSelf // face that will cause afterCreateFaceFailure to be invoked
+                           // fails because remote endpoint is prohibited
 {
 public:
   ControlParameters
@@ -245,16 +148,16 @@
 
 namespace mpl = boost::mpl;
 
-// pairs of CreateCommand and Success status
-typedef mpl::vector<mpl::pair<TcpFaceOnDemand, Failure<500>>,
+// pairs of CreateCommand and Success/Failure status
+typedef mpl::vector<mpl::pair<TcpFaceOnDemand, Failure<406>>,
                     mpl::pair<TcpFacePersistent, Success>,
-                    mpl::pair<TcpFacePermanent, Failure<500>>,
-                    mpl::pair<UdpFaceOnDemand, Failure<500>>,
+                    mpl::pair<TcpFacePermanent, Failure<406>>,
+                    mpl::pair<UdpFaceOnDemand, Failure<406>>,
                     mpl::pair<UdpFacePersistent, Success>,
                     mpl::pair<UdpFacePermanent, Success>,
-                    mpl::pair<UdpFaceCannotConnect, Failure<408>>> Faces;
+                    mpl::pair<UdpFaceConnectToSelf, Failure<406>>> Faces;
 
-BOOST_FIXTURE_TEST_CASE_TEMPLATE(NewFace, T, Faces, FaceManagerFixture)
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(NewFace, T, Faces, FaceManagerCommandFixture)
 {
   typedef typename T::first FaceType;
   typedef typename T::second CreateResult;
@@ -267,24 +170,28 @@
 
   bool hasCallbackFired = false;
   this->node1.face.onSendData.connect([this, command, &hasCallbackFired] (const Data& response) {
-      if (!command->getName().isPrefixOf(response.getName())) {
-        return;
-      }
+    if (!command->getName().isPrefixOf(response.getName())) {
+      return;
+    }
 
-      ControlResponse actual(response.getContent().blockFromValue());
-      ControlResponse expected(CreateResult().getExpected());
-      BOOST_CHECK_EQUAL(expected.getCode(), actual.getCode());
-      BOOST_TEST_MESSAGE(actual.getText());
+    ControlResponse actual(response.getContent().blockFromValue());
+    ControlResponse expected(CreateResult().getExpected());
+    BOOST_CHECK_EQUAL(expected.getCode(), actual.getCode());
+    BOOST_TEST_MESSAGE(actual.getText());
 
-      if (actual.getBody().hasWire()) {
-        ControlParameters expectedParams(FaceType().getParameters());
-        ControlParameters actualParams(actual.getBody());
+    if (actual.getBody().hasWire()) {
+      ControlParameters expectedParams(FaceType().getParameters());
+      ControlParameters actualParams(actual.getBody());
 
+      BOOST_CHECK(actualParams.hasFaceId());
+      BOOST_CHECK_EQUAL(expectedParams.getFacePersistency(), actualParams.getFacePersistency());
+
+      if (actual.getCode() != 200) {
         BOOST_CHECK_EQUAL(expectedParams.getUri(), actualParams.getUri());
-        BOOST_CHECK_EQUAL(expectedParams.getFacePersistency(), actualParams.getFacePersistency());
       }
-      hasCallbackFired = true;
-    });
+    }
+    hasCallbackFired = true;
+  });
 
   this->node1.face.receive(*command);
   this->advanceClocks(time::milliseconds(1), 5);
@@ -299,7 +206,7 @@
                     mpl::pair<mpl::pair<UdpFacePermanent, UdpFacePersistent>, UdpFacePermanent>> FaceTransitions;
 
 
-BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExistingFace, T, FaceTransitions, FaceManagerFixture)
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExistingFace, T, FaceTransitions, FaceManagerCommandFixture)
 {
   typedef typename T::first::first FaceType1;
   typedef typename T::first::second FaceType2;
@@ -339,6 +246,7 @@
 
         ControlParameters expectedParams(FinalFaceType().getParameters());
         ControlParameters actualParams(actual.getBody());
+        BOOST_REQUIRE(!actualParams.hasUri()); // Uri parameter is only included on command failure
         BOOST_CHECK_EQUAL(expectedParams.getFacePersistency(), actualParams.getFacePersistency());
 
         hasCallbackFired = true;
@@ -374,7 +282,7 @@
                     mpl::pair<UdpFace, UdpFacePermanent>> OnDemandFaceTransitions;
 
 // need a slightly different logic to test transitions from OnDemand state
-BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExistingFaceOnDemand, T, OnDemandFaceTransitions, FaceManagerFixture)
+BOOST_FIXTURE_TEST_CASE_TEMPLATE(ExistingFaceOnDemand, T, OnDemandFaceTransitions, FaceManagerCommandFixture)
 {
   typedef typename T::first  OtherNodeFace;
   typedef typename T::second FaceType;
@@ -390,20 +298,21 @@
 
     ndn::util::signal::ScopedConnection connection =
       this->node2.face.onSendData.connect([this, command] (const Data& response) {
-          if (!command->getName().isPrefixOf(response.getName())) {
-            return;
-          }
+        if (!command->getName().isPrefixOf(response.getName())) {
+          return;
+        }
 
-          ControlResponse controlResponse(response.getContent().blockFromValue());
-          BOOST_REQUIRE_EQUAL(controlResponse.getText(), "OK");
-          BOOST_REQUIRE_EQUAL(controlResponse.getCode(), 200);
-          uint64_t faceId = ControlParameters(controlResponse.getBody()).getFaceId();
-          auto face = this->node2.faceTable.get(static_cast<FaceId>(faceId));
+        ControlResponse controlResponse(response.getContent().blockFromValue());
+        BOOST_REQUIRE_EQUAL(controlResponse.getText(), "OK");
+        BOOST_REQUIRE_EQUAL(controlResponse.getCode(), 200);
+        BOOST_REQUIRE(!ControlParameters(controlResponse.getBody()).hasUri());
+        uint64_t faceId = ControlParameters(controlResponse.getBody()).getFaceId();
+        auto face = this->node2.faceTable.get(static_cast<FaceId>(faceId));
 
-          // to force creation of on-demand face
-          auto dummyInterest = make_shared<Interest>("/hello/world");
-          face->sendInterest(*dummyInterest);
-        });
+        // to force creation of on-demand face
+        auto dummyInterest = make_shared<Interest>("/hello/world");
+        face->sendInterest(*dummyInterest);
+      });
 
     this->node2.face.receive(*command);
     this->advanceClocks(time::milliseconds(1), 5); // let node2 process command and send Interest
@@ -444,6 +353,7 @@
 
         ControlParameters expectedParams(FaceType().getParameters());
         ControlParameters actualParams(actual.getBody());
+        BOOST_REQUIRE(!actualParams.hasUri());
         BOOST_CHECK_EQUAL(actualParams.getFacePersistency(), expectedParams.getFacePersistency());
         BOOST_CHECK_EQUAL(actualParams.getFaceId(), foundFace->getId());
         BOOST_CHECK_EQUAL(foundFace->getPersistency(), expectedParams.getFacePersistency());
diff --git a/tests/other/face-benchmark.cpp b/tests/other/face-benchmark.cpp
index 1dc6c37..c468bf1 100644
--- a/tests/other/face-benchmark.cpp
+++ b/tests/other/face-benchmark.cpp
@@ -54,11 +54,11 @@
     parseConfig(configFileName);
 
     m_tcpChannel.listen(bind(&FaceBenchmark::onLeftFaceCreated, this, _1),
-                        bind(&FaceBenchmark::onFaceCreationFailed, _1));
+                        bind(&FaceBenchmark::onFaceCreationFailed, _1, _2));
     std::clog << "Listening on " << m_tcpChannel.getUri() << std::endl;
 
     m_udpChannel.listen(bind(&FaceBenchmark::onLeftFaceCreated, this, _1),
-                        bind(&FaceBenchmark::onFaceCreationFailed, _1));
+                        bind(&FaceBenchmark::onFaceCreationFailed, _1, _2));
     std::clog << "Listening on " << m_udpChannel.getUri() << std::endl;
   }
 
@@ -129,13 +129,13 @@
     if (uriR.getScheme() == "tcp4") {
       m_tcpChannel.connect(tcp::Endpoint(addr, port),
                            bind(&FaceBenchmark::onRightFaceCreated, this, faceL, _1),
-                           bind(&FaceBenchmark::onFaceCreationFailed, _1));
+                           bind(&FaceBenchmark::onFaceCreationFailed, _1, _2));
     }
     else if (uriR.getScheme() == "udp4") {
       m_udpChannel.connect(udp::Endpoint(addr, port),
                            ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                            bind(&FaceBenchmark::onRightFaceCreated, this, faceL, _1),
-                           bind(&FaceBenchmark::onFaceCreationFailed, _1));
+                           bind(&FaceBenchmark::onFaceCreationFailed, _1, _2));
     }
   }
 
@@ -158,9 +158,9 @@
   }
 
   static void
-  onFaceCreationFailed(const std::string& reason)
+  onFaceCreationFailed(uint32_t status, const std::string& reason)
   {
-    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create face: " + reason));
+    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to create face: " + to_string(status) + ": " + reason));
   }
 
 private: