tools: refactor ndn-autoconfig

Hub face creation and prefix registrations are moved into a new
Procedure class. Main function is simplified.

refs #4158

Change-Id: I15b660e3b8a1bde89498a1cb549a87788de46c7a
diff --git a/tests/tools/mock-nfd-mgmt-fixture.hpp b/tests/tools/mock-nfd-mgmt-fixture.hpp
new file mode 100644
index 0000000..4eeab41
--- /dev/null
+++ b/tests/tools/mock-nfd-mgmt-fixture.hpp
@@ -0,0 +1,226 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2017,  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_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
+#define NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
+
+#include <ndn-cxx/util/dummy-client-face.hpp>
+
+#include "tests/test-common.hpp"
+#include "tests/identity-management-fixture.hpp"
+
+namespace nfd {
+namespace tools {
+namespace tests {
+
+using namespace nfd::tests;
+using ndn::nfd::ControlParameters;
+
+/** \brief fixture to emulate NFD management
+ */
+class MockNfdMgmtFixture : public IdentityManagementTimeFixture
+{
+protected:
+  MockNfdMgmtFixture()
+    : face(g_io, m_keyChain,
+           {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
+  {
+    face.onSendInterest.connect([=] (const Interest& interest) {
+      g_io.post([=] {
+        if (processInterest != nullptr) {
+          processInterest(interest);
+        }
+      });
+    });
+  }
+
+protected: // ControlCommand
+  /** \brief check the Interest is a command with specified prefix
+   *  \retval nullopt last Interest is not the expected command
+   *  \return command parameters
+   */
+  static ndn::optional<ControlParameters>
+  parseCommand(const Interest& interest, const Name& expectedPrefix)
+  {
+    if (!expectedPrefix.isPrefixOf(interest.getName())) {
+      return ndn::nullopt;
+    }
+    return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
+  }
+
+  /** \brief send successful response to a command Interest
+   */
+  void
+  succeedCommand(const Interest& interest, const ControlParameters& parameters)
+  {
+    this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
+  }
+
+  /** \brief send failure response to a command Interest
+   */
+  void
+  failCommand(const Interest& interest, uint32_t code, const std::string& text)
+  {
+    this->sendCommandReply(interest, {code, text});
+  }
+
+  /** \brief send failure response to a command Interest
+   */
+  void
+  failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
+  {
+    this->sendCommandReply(interest, code, text, body.wireEncode());
+  }
+
+protected: // StatusDataset
+  /** \brief send an empty dataset in reply to StatusDataset request
+   *  \param prefix dataset prefix without version and segment
+   *  \pre Interest for dataset has been expressed, sendDataset has not been invoked
+   */
+  void
+  sendEmptyDataset(const Name& prefix)
+  {
+    this->sendDatasetReply(prefix, nullptr, 0);
+  }
+
+  /** \brief send one WireEncodable in reply to StatusDataset request
+   *  \param prefix dataset prefix without version and segment
+   *  \param payload payload block
+   *  \note payload must fit in one Data
+   *  \pre Interest for dataset has been expressed, sendDataset has not been invoked
+   */
+  template<typename T>
+  void
+  sendDataset(const Name& prefix, const T& payload)
+  {
+    BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
+
+    this->sendDatasetReply(prefix, payload.wireEncode());
+  }
+
+  /** \brief send two WireEncodables in reply to StatusDataset request
+   *  \param prefix dataset prefix without version and segment
+   *  \param payload1 first vector item
+   *  \param payload2 second vector item
+   *  \note all payloads must fit in one Data
+   *  \pre Interest for dataset has been expressed, sendDataset has not been invoked
+   */
+  template<typename T1, typename T2>
+  void
+  sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
+  {
+    BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
+    BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
+
+    ndn::encoding::EncodingBuffer buffer;
+    payload2.wireEncode(buffer);
+    payload1.wireEncode(buffer);
+
+    this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
+  }
+
+private:
+  virtual void
+  processEventsOverride(time::milliseconds timeout)
+  {
+    if (timeout <= time::milliseconds::zero()) {
+      // give enough time to finish execution
+      timeout = time::seconds(30);
+    }
+    this->advanceClocks(time::milliseconds(100), timeout);
+  }
+
+  void
+  sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
+  {
+    auto data = makeData(interest.getName());
+    data->setContent(resp.wireEncode());
+    face.receive(*data);
+  }
+
+  void
+  sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
+                   const Block& body)
+  {
+    this->sendCommandReply(interest,
+                           ndn::nfd::ControlResponse(code, text).setBody(body));
+  }
+
+  /** \brief send a payload in reply to StatusDataset request
+   *  \param name dataset prefix without version and segment
+   *  \param contentArgs passed to Data::setContent
+   */
+  template<typename ...ContentArgs>
+  void
+  sendDatasetReply(Name name, ContentArgs&&... contentArgs)
+  {
+    name.appendVersion().appendSegment(0);
+
+    // These warnings assist in debugging when nfdc does not receive StatusDataset.
+    // They usually indicate a misspelled prefix or incorrect timing in the test case.
+    if (face.sentInterests.empty()) {
+      BOOST_WARN_MESSAGE(false, "no Interest expressed");
+    }
+    else {
+      BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
+                         "last Interest " << face.sentInterests.back().getName() <<
+                         " cannot be satisfied by this Data " << name);
+    }
+
+    auto data = make_shared<Data>(name);
+    data->setFinalBlockId(name[-1]);
+    data->setContent(std::forward<ContentArgs>(contentArgs)...);
+    this->signDatasetReply(*data);
+    face.receive(*data);
+  }
+
+  virtual void
+  signDatasetReply(Data& data)
+  {
+    signData(data);
+  }
+
+protected:
+  ndn::util::DummyClientFace face;
+  std::function<void(const Interest&)> processInterest;
+};
+
+} // namespace tests
+} // namespace tools
+} // namespace nfd
+
+/** \brief require the command in \p interest has expected prefix
+ *  \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
+ *  \return ControlParameters, or nullopt if \p interest does match \p expectedPrefix
+ */
+#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
+  [interest] { \
+    auto params = parseCommand(interest, (expectedPrefix)); \
+    BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
+                          " does not match command prefix " << (expectedPrefix)); \
+    return *params; \
+  } ()
+
+#endif // NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
diff --git a/tests/tools/ndn-autoconfig/procedure.t.cpp b/tests/tools/ndn-autoconfig/procedure.t.cpp
new file mode 100644
index 0000000..27d9dcf
--- /dev/null
+++ b/tests/tools/ndn-autoconfig/procedure.t.cpp
@@ -0,0 +1,286 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2017,  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 "ndn-autoconfig/procedure.hpp"
+
+#include "../mock-nfd-mgmt-fixture.hpp"
+#include <boost/logic/tribool.hpp>
+
+namespace ndn {
+namespace tools {
+namespace autoconfig {
+namespace tests {
+
+using namespace ::nfd::tests;
+using nfd::ControlParameters;
+
+template<typename ProcedureClass>
+class ProcedureFixture : public ::nfd::tools::tests::MockNfdMgmtFixture
+{
+public:
+  void
+  initialize(const Options& options)
+  {
+    procedure = make_unique<ProcedureClass>(face, m_keyChain);
+    procedure->initialize(options);
+  }
+
+  bool
+  runOnce()
+  {
+    BOOST_ASSERT(procedure != nullptr);
+    boost::logic::tribool result;
+    procedure->onComplete.connectSingleShot([&] (bool result1) { result = result1; });
+    procedure->runOnce();
+    face.processEvents();
+    BOOST_CHECK_MESSAGE(!boost::logic::indeterminate(result), "onComplete is not invoked");
+    return result;
+  }
+
+public:
+  unique_ptr<ProcedureClass> procedure;
+};
+
+class DummyStage : public Stage
+{
+public:
+  /** \param stageName stage name
+   *  \param nCalls pointer to a variable which is incremented each time doStart is invoked
+   *  \param result expected result, nullopt to cause a failued
+   *  \param io io_service to asynchronously post the result
+   */
+  DummyStage(const std::string& stageName, int* nCalls, const optional<FaceUri>& result, boost::asio::io_service& io)
+    : m_stageName(stageName)
+    , m_nCalls(nCalls)
+    , m_result(result)
+    , m_io(io)
+  {
+  }
+
+  const std::string&
+  getName() const override
+  {
+    return m_stageName;
+  }
+
+private:
+  void
+  doStart() override
+  {
+    if (m_nCalls != nullptr) {
+      ++(*m_nCalls);
+    }
+    m_io.post([this] {
+      if (m_result) {
+        this->succeed(*m_result);
+      }
+      else {
+        this->fail("DUMMY-STAGE-FAIL");
+      }
+    });
+  }
+
+private:
+  std::string m_stageName;
+  int* m_nCalls;
+  optional<FaceUri> m_result;
+  boost::asio::io_service& m_io;
+};
+
+/** \brief two-stage Procedure where the first stage succeeds and the second stage fails
+ *
+ *  But the second stage shouldn't be invoked after the first stage succeeds.
+ */
+class ProcedureSuccessFailure : public Procedure
+{
+public:
+  ProcedureSuccessFailure(Face& face, KeyChain& keyChain)
+    : Procedure(face, keyChain)
+    , m_io(face.getIoService())
+  {
+  }
+
+private:
+  void
+  makeStages(const Options& options) override
+  {
+    m_stages.push_back(make_unique<DummyStage>("first", &nCalls1, FaceUri("udp://188.7.60.95"), m_io));
+    m_stages.push_back(make_unique<DummyStage>("second", &nCalls2, nullopt, m_io));
+  }
+
+public:
+  int nCalls1 = 0;
+  int nCalls2 = 0;
+
+private:
+  boost::asio::io_service& m_io;
+};
+
+/** \brief two-stage Procedure where the first stage fails and the second stage succeeds
+ */
+class ProcedureFailureSuccess : public Procedure
+{
+public:
+  ProcedureFailureSuccess(Face& face, KeyChain& keyChain)
+    : Procedure(face, keyChain)
+    , m_io(face.getIoService())
+  {
+  }
+
+private:
+  void
+  makeStages(const Options& options) override
+  {
+    m_stages.push_back(make_unique<DummyStage>("first", &nCalls1, nullopt, m_io));
+    m_stages.push_back(make_unique<DummyStage>("second", &nCalls2, FaceUri("tcp://40.23.174.71"), m_io));
+  }
+
+public:
+  int nCalls1 = 0;
+  int nCalls2 = 0;
+
+private:
+  boost::asio::io_service& m_io;
+};
+
+BOOST_AUTO_TEST_SUITE(NdnAutoconfig)
+BOOST_AUTO_TEST_SUITE(TestProcedure)
+
+BOOST_FIXTURE_TEST_CASE(Normal, ProcedureFixture<ProcedureSuccessFailure>)
+{
+  this->initialize(Options{});
+
+  int nRegisterNdn = 0, nRegisterLocalhopNfd = 0;
+  this->processInterest = [&] (const Interest& interest) {
+    optional<ControlParameters> req = parseCommand(interest, "/localhost/nfd/faces/create");
+    if (req) {
+      BOOST_REQUIRE(req->hasUri());
+      BOOST_CHECK_EQUAL(req->getUri(), "udp4://188.7.60.95:6363");
+
+      ControlParameters resp;
+      resp.setFaceId(1178)
+          .setUri("udp4://188.7.60.95:6363")
+          .setLocalUri("udp4://110.69.164.68:23197")
+          .setFacePersistency(nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+          .setFlags(0);
+      this->succeedCommand(interest, resp);
+      return;
+    }
+
+    req = parseCommand(interest, "/localhost/nfd/rib/register");
+    if (req) {
+      BOOST_REQUIRE(req->hasFaceId());
+      BOOST_CHECK_EQUAL(req->getFaceId(), 1178);
+      BOOST_REQUIRE(req->hasOrigin());
+      BOOST_CHECK_EQUAL(req->getOrigin(), nfd::ROUTE_ORIGIN_AUTOCONF);
+      BOOST_REQUIRE(req->hasName());
+      if (req->getName() == "/ndn") {
+        ++nRegisterNdn;
+      }
+      else if (req->getName() == "/localhop/nfd") {
+        ++nRegisterLocalhopNfd;
+      }
+      else {
+        BOOST_ERROR("unexpected prefix registration " << req->getName());
+      }
+
+      ControlParameters resp;
+      resp.setName(req->getName())
+          .setFaceId(1178)
+          .setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
+          .setCost(1)
+          .setFlags(0);
+      this->succeedCommand(interest, resp);
+      return;
+    }
+
+    BOOST_FAIL("unrecognized command Interest " << interest);
+  };
+
+  BOOST_CHECK_EQUAL(this->runOnce(), true);
+  BOOST_CHECK_EQUAL(procedure->nCalls1, 1);
+  BOOST_CHECK_EQUAL(procedure->nCalls2, 0);
+  BOOST_CHECK_EQUAL(nRegisterNdn, 1);
+  BOOST_CHECK_EQUAL(nRegisterLocalhopNfd, 1);
+}
+
+BOOST_FIXTURE_TEST_CASE(ExistingFace, ProcedureFixture<ProcedureFailureSuccess>)
+{
+  this->initialize(Options{});
+
+  int nRegisterNdn = 0, nRegisterLocalhopNfd = 0;
+  this->processInterest = [&] (const Interest& interest) {
+    optional<ControlParameters> req = parseCommand(interest, "/localhost/nfd/faces/create");
+    if (req) {
+      ControlParameters resp;
+      resp.setFaceId(3146)
+          .setUri("tcp4://40.23.174.71:6363")
+          .setLocalUri("tcp4://34.213.69.67:14445")
+          .setFacePersistency(nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+          .setFlags(0);
+      this->failCommand(interest, 409, "conflict-409", resp);
+      return;
+    }
+
+    req = parseCommand(interest, "/localhost/nfd/rib/register");
+    if (req) {
+      BOOST_REQUIRE(req->hasName());
+      if (req->getName() == "/ndn") {
+        ++nRegisterNdn;
+      }
+      else if (req->getName() == "/localhop/nfd") {
+        ++nRegisterLocalhopNfd;
+      }
+      else {
+        BOOST_ERROR("unexpected prefix registration " << req->getName());
+      }
+
+      ControlParameters resp;
+      resp.setName(req->getName())
+          .setFaceId(3146)
+          .setOrigin(nfd::ROUTE_ORIGIN_AUTOCONF)
+          .setCost(1)
+          .setFlags(0);
+      this->succeedCommand(interest, resp);
+      return;
+    }
+
+    BOOST_FAIL("unrecognized command Interest " << interest);
+  };
+
+  BOOST_CHECK_EQUAL(this->runOnce(), true);
+  BOOST_CHECK_EQUAL(procedure->nCalls1, 1);
+  BOOST_CHECK_EQUAL(procedure->nCalls2, 1);
+  BOOST_CHECK_EQUAL(nRegisterNdn, 1);
+  BOOST_CHECK_EQUAL(nRegisterLocalhopNfd, 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // TestProcedure
+BOOST_AUTO_TEST_SUITE_END() // NdnAutoconfig
+
+} // namespace tests
+} // namespace autoconfig
+} // namespace tools
+} // namespace ndn
diff --git a/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp b/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp
index fd309bf..9841de8 100644
--- a/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp
+++ b/tests/tools/nfdc/mock-nfd-mgmt-fixture.hpp
@@ -1,5 +1,5 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
+/*
  * Copyright (c) 2014-2017,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
@@ -26,11 +26,7 @@
 #ifndef NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP
 #define NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP
 
-#include <ndn-cxx/mgmt/nfd/face-query-filter.hpp>
-#include <ndn-cxx/util/dummy-client-face.hpp>
-
-#include "tests/test-common.hpp"
-#include "tests/identity-management-fixture.hpp"
+#include "../mock-nfd-mgmt-fixture.hpp"
 
 namespace nfd {
 namespace tools {
@@ -38,111 +34,12 @@
 namespace tests {
 
 using namespace nfd::tests;
-using ndn::nfd::ControlParameters;
 
 /** \brief fixture to emulate NFD management
  */
-class MockNfdMgmtFixture : public IdentityManagementTimeFixture
+class MockNfdMgmtFixture : public nfd::tools::tests::MockNfdMgmtFixture
 {
 protected:
-  MockNfdMgmtFixture()
-    : face(g_io, m_keyChain,
-           {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
-  {
-    face.onSendInterest.connect([=] (const Interest& interest) {
-      g_io.post([=] {
-        if (processInterest != nullptr) {
-          processInterest(interest);
-        }
-      });
-    });
-  }
-
-protected: // ControlCommand
-  /** \brief check the Interest is a command with specified prefix
-   *  \retval nullopt last Interest is not the expected command
-   *  \return command parameters
-   */
-  static ndn::optional<ControlParameters>
-  parseCommand(const Interest& interest, const Name& expectedPrefix)
-  {
-    if (!expectedPrefix.isPrefixOf(interest.getName())) {
-      return ndn::nullopt;
-    }
-    return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
-  }
-
-  /** \brief send successful response to a command Interest
-   */
-  void
-  succeedCommand(const Interest& interest, const ControlParameters& parameters)
-  {
-    this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
-  }
-
-  /** \brief send failure response to a command Interest
-   */
-  void
-  failCommand(const Interest& interest, uint32_t code, const std::string& text)
-  {
-    this->sendCommandReply(interest, {code, text});
-  }
-
-  /** \brief send failure response to a command Interest
-   */
-  void
-  failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
-  {
-    this->sendCommandReply(interest, code, text, body.wireEncode());
-  }
-
-protected: // StatusDataset
-  /** \brief send an empty dataset in reply to StatusDataset request
-   *  \param prefix dataset prefix without version and segment
-   *  \pre Interest for dataset has been expressed, sendDataset has not been invoked
-   */
-  void
-  sendEmptyDataset(const Name& prefix)
-  {
-    this->sendDatasetReply(prefix, nullptr, 0);
-  }
-
-  /** \brief send one WireEncodable in reply to StatusDataset request
-   *  \param prefix dataset prefix without version and segment
-   *  \param payload payload block
-   *  \note payload must fit in one Data
-   *  \pre Interest for dataset has been expressed, sendDataset has not been invoked
-   */
-  template<typename T>
-  void
-  sendDataset(const Name& prefix, const T& payload)
-  {
-    BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
-
-    this->sendDatasetReply(prefix, payload.wireEncode());
-  }
-
-  /** \brief send two WireEncodables in reply to StatusDataset request
-   *  \param prefix dataset prefix without version and segment
-   *  \param payload1 first vector item
-   *  \param payload2 second vector item
-   *  \note all payloads must fit in one Data
-   *  \pre Interest for dataset has been expressed, sendDataset has not been invoked
-   */
-  template<typename T1, typename T2>
-  void
-  sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
-  {
-    BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
-    BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
-
-    ndn::encoding::EncodingBuffer buffer;
-    payload2.wireEncode(buffer);
-    payload1.wireEncode(buffer);
-
-    this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
-  }
-
   /** \brief respond to specific FaceQuery requests
    *  \retval true the Interest matches one of the defined patterns and is responded
    *  \retval false the Interest is not responded
@@ -201,71 +98,6 @@
 
     return false;
   }
-
-private:
-  virtual void
-  processEventsOverride(time::milliseconds timeout)
-  {
-    if (timeout <= time::milliseconds::zero()) {
-      // give enough time to finish execution
-      timeout = time::seconds(30);
-    }
-    this->advanceClocks(time::milliseconds(100), timeout);
-  }
-
-  void
-  sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
-  {
-    auto data = makeData(interest.getName());
-    data->setContent(resp.wireEncode());
-    face.receive(*data);
-  }
-
-  void
-  sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
-                   const Block& body)
-  {
-    this->sendCommandReply(interest,
-                           ndn::nfd::ControlResponse(code, text).setBody(body));
-  }
-
-  /** \brief send a payload in reply to StatusDataset request
-   *  \param name dataset prefix without version and segment
-   *  \param contentArgs passed to Data::setContent
-   */
-  template<typename ...ContentArgs>
-  void
-  sendDatasetReply(Name name, ContentArgs&&... contentArgs)
-  {
-    name.appendVersion().appendSegment(0);
-
-    // These warnings assist in debugging when nfdc does not receive StatusDataset.
-    // They usually indicate a misspelled prefix or incorrect timing in the test case.
-    if (face.sentInterests.empty()) {
-      BOOST_WARN_MESSAGE(false, "no Interest expressed");
-    }
-    else {
-      BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
-                         "last Interest " << face.sentInterests.back().getName() <<
-                         " cannot be satisfied by this Data " << name);
-    }
-
-    auto data = make_shared<Data>(name);
-    data->setFinalBlockId(name[-1]);
-    data->setContent(std::forward<ContentArgs>(contentArgs)...);
-    this->signDatasetReply(*data);
-    face.receive(*data);
-  }
-
-  virtual void
-  signDatasetReply(Data& data)
-  {
-    signData(data);
-  }
-
-protected:
-  ndn::util::DummyClientFace face;
-  std::function<void(const Interest&)> processInterest;
 };
 
 } // namespace tests
@@ -273,16 +105,4 @@
 } // namespace tools
 } // namespace nfd
 
-/** \brief require the command in \p interest has expected prefix
- *  \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
- *  \return ControlParameters, or nullopt if \p interest does match \p expectedPrefix
- */
-#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
-  [interest] { \
-    auto params = parseCommand(interest, (expectedPrefix)); \
-    BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
-                          " does not match command prefix " << (expectedPrefix)); \
-    return *params; \
-  } ()
-
 #endif // NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP