tests: sync common testing infrastructure with ndn-cxx

Change-Id: I6feab5247231abc35b8daa96bca21ad17c9cc4b3
diff --git a/tests/check-typeid.hpp b/tests/check-typeid.hpp
index 5e79289..35dd4d8 100644
--- a/tests/check-typeid.hpp
+++ b/tests/check-typeid.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2018,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -24,13 +24,13 @@
  */
 
 /** \file
- *  \brief provides unit testing tools to validate runtime type information
+ *  \brief Provides unit testing tools to validate runtime type information.
  */
 
 #ifndef NFD_TESTS_CHECK_TYPEID_HPP
 #define NFD_TESTS_CHECK_TYPEID_HPP
 
-#include "boost-test.hpp"
+#include "tests/boost-test.hpp"
 
 #include <boost/core/demangle.hpp>
 #include <typeinfo>
diff --git a/tests/clock-fixture.cpp b/tests/clock-fixture.cpp
index de55cf3..1691f89 100644
--- a/tests/clock-fixture.cpp
+++ b/tests/clock-fixture.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -28,10 +28,9 @@
 namespace nfd {
 namespace tests {
 
-ClockFixture::ClockFixture(boost::asio::io_service& io)
+ClockFixture::ClockFixture()
   : m_steadyClock(make_shared<time::UnitTestSteadyClock>())
   , m_systemClock(make_shared<time::UnitTestSystemClock>())
-  , m_io(io)
 {
   time::setCustomClocks(m_steadyClock, m_systemClock);
 }
@@ -53,17 +52,9 @@
     m_systemClock->advance(t);
     total -= t;
 
-    pollAfterClockTick();
+    afterTick();
   }
 }
 
-void
-ClockFixture::pollAfterClockTick()
-{
-  if (m_io.stopped())
-    m_io.reset();
-  m_io.poll();
-}
-
 } // namespace tests
 } // namespace nfd
diff --git a/tests/clock-fixture.hpp b/tests/clock-fixture.hpp
index 5c69334..4243b62 100644
--- a/tests/clock-fixture.hpp
+++ b/tests/clock-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -44,10 +44,10 @@
   /** \brief Advance steady and system clocks.
    *
    *  Clocks are advanced in increments of \p tick for \p nTicks ticks.
-   *  After each tick, global io_service is polled to process pending I/O events.
+   *  afterTick() is called after each tick.
    *
    *  Exceptions thrown during I/O events are propagated to the caller.
-   *  Clock advancing would stop in case of an exception.
+   *  Clock advancement will stop in the event of an exception.
    */
   void
   advanceClocks(time::nanoseconds tick, size_t nTicks = 1)
@@ -59,30 +59,30 @@
    *
    *  Clocks are advanced in increments of \p tick for \p total time.
    *  The last increment might be shorter than \p tick.
-   *  After each tick, global io_service is polled to process pending I/O events.
+   *  afterTick() is called after each tick.
    *
    *  Exceptions thrown during I/O events are propagated to the caller.
-   *  Clock advancing would stop in case of an exception.
+   *  Clock advancement will stop in the event of an exception.
    */
   void
   advanceClocks(time::nanoseconds tick, time::nanoseconds total);
 
 protected:
-  explicit
-  ClockFixture(boost::asio::io_service& io);
+  ClockFixture();
 
 private:
   /** \brief Called by advanceClocks() after each clock advancement (tick).
+   *
+   *  The base class implementation is a no-op.
    */
   virtual void
-  pollAfterClockTick();
+  afterTick()
+  {
+  }
 
 protected:
   shared_ptr<time::UnitTestSteadyClock> m_steadyClock;
   shared_ptr<time::UnitTestSystemClock> m_systemClock;
-
-private:
-  boost::asio::io_service& m_io;
 };
 
 } // namespace tests
diff --git a/tests/daemon/face/generic-link-service.t.cpp b/tests/daemon/face/generic-link-service.t.cpp
index 568dafc..a11d524 100644
--- a/tests/daemon/face/generic-link-service.t.cpp
+++ b/tests/daemon/face/generic-link-service.t.cpp
@@ -34,7 +34,6 @@
 #include <ndn-cxx/lp/empty-value.hpp>
 #include <ndn-cxx/lp/prefix-announcement-header.hpp>
 #include <ndn-cxx/lp/tags.hpp>
-#include <ndn-cxx/security/signing-helpers.hpp>
 
 namespace nfd {
 namespace face {
diff --git a/tests/daemon/global-io-fixture.cpp b/tests/daemon/global-io-fixture.cpp
index e4f40fe..3a85e29 100644
--- a/tests/daemon/global-io-fixture.cpp
+++ b/tests/daemon/global-io-fixture.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -52,9 +52,10 @@
   return g_io.poll();
 }
 
-GlobalIoTimeFixture::GlobalIoTimeFixture()
-  : ClockFixture(g_io)
+void
+GlobalIoTimeFixture::afterTick()
 {
+  pollIo();
 }
 
 } // namespace tests
diff --git a/tests/daemon/global-io-fixture.hpp b/tests/daemon/global-io-fixture.hpp
index 544c466..7ac4f74 100644
--- a/tests/daemon/global-io-fixture.hpp
+++ b/tests/daemon/global-io-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -56,10 +56,11 @@
 
 /** \brief GlobalIoFixture that also overrides steady clock and system clock.
  */
-class GlobalIoTimeFixture : public GlobalIoFixture, public ClockFixture
+class GlobalIoTimeFixture : public ClockFixture, public GlobalIoFixture
 {
-protected:
-  GlobalIoTimeFixture();
+private:
+  void
+  afterTick() final;
 };
 
 } // namespace tests
diff --git a/tests/daemon/mgmt/command-authenticator.t.cpp b/tests/daemon/mgmt/command-authenticator.t.cpp
index fc24ccd..4415b11 100644
--- a/tests/daemon/mgmt/command-authenticator.t.cpp
+++ b/tests/daemon/mgmt/command-authenticator.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -106,9 +106,9 @@
   Name id0("/localhost/CommandAuthenticator/0");
   Name id1("/localhost/CommandAuthenticator/1");
   Name id2("/localhost/CommandAuthenticator/2");
-  BOOST_REQUIRE(addIdentity(id0));
-  BOOST_REQUIRE(saveIdentityCertificate(id1, "1.ndncert", true));
-  BOOST_REQUIRE(saveIdentityCertificate(id2, "2.ndncert", true));
+  BOOST_REQUIRE(m_keyChain.createIdentity(id0));
+  BOOST_REQUIRE(saveIdentityCert(id1, "1.ndncert", true));
+  BOOST_REQUIRE(saveIdentityCert(id2, "2.ndncert", true));
 
   makeModules({"module0", "module1", "module2", "module3", "module4", "module5", "module6", "module7"});
   const std::string& config = R"CONFIG(
@@ -196,8 +196,8 @@
 {
   Name id0("/localhost/CommandAuthenticator/0");
   Name id1("/localhost/CommandAuthenticator/1");
-  BOOST_REQUIRE(addIdentity(id0));
-  BOOST_REQUIRE(saveIdentityCertificate(id1, "1.ndncert", true));
+  BOOST_REQUIRE(m_keyChain.createIdentity(id0));
+  BOOST_REQUIRE(saveIdentityCert(id1, "1.ndncert", true));
 
   makeModules({"module0", "module1"});
   const std::string& config = R"CONFIG(
@@ -241,7 +241,7 @@
   IdentityAuthorizedFixture()
     : id1("/localhost/CommandAuthenticator/1")
   {
-    BOOST_REQUIRE(saveIdentityCertificate(id1, "1.ndncert", true));
+    BOOST_REQUIRE(saveIdentityCert(id1, "1.ndncert", true));
 
     makeModules({"module1"});
     const std::string& config = R"CONFIG(
@@ -322,7 +322,7 @@
 BOOST_AUTO_TEST_CASE(NotAuthorized)
 {
   Name id0("/localhost/CommandAuthenticator/0");
-  BOOST_REQUIRE(addIdentity(id0));
+  BOOST_REQUIRE(m_keyChain.createIdentity(id0));
 
   BOOST_CHECK_EQUAL(authorize("module1", id0), false);
   BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::STATUS403);
@@ -357,7 +357,7 @@
 BOOST_FIXTURE_TEST_CASE(MissingAuthorizationsSection, CommandAuthenticatorFixture)
 {
   Name id0("/localhost/CommandAuthenticator/0");
-  BOOST_REQUIRE(addIdentity(id0));
+  BOOST_REQUIRE(m_keyChain.createIdentity(id0));
 
   makeModules({"module42"});
   loadConfig("");
diff --git a/tests/daemon/mgmt/manager-common-fixture.cpp b/tests/daemon/mgmt/manager-common-fixture.cpp
index ebead64..67ac46b 100644
--- a/tests/daemon/mgmt/manager-common-fixture.cpp
+++ b/tests/daemon/mgmt/manager-common-fixture.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -25,8 +25,6 @@
 
 #include "manager-common-fixture.hpp"
 
-#include <ndn-cxx/security/signing-helpers.hpp>
-
 namespace nfd {
 namespace tests {
 
@@ -35,7 +33,7 @@
 CommandInterestSignerFixture::CommandInterestSignerFixture()
   : m_commandInterestSigner(m_keyChain)
 {
-  BOOST_REQUIRE(this->addIdentity(DEFAULT_COMMAND_SIGNER_IDENTITY));
+  BOOST_REQUIRE(m_keyChain.createIdentity(DEFAULT_COMMAND_SIGNER_IDENTITY));
 }
 
 Interest
@@ -196,7 +194,7 @@
 void
 ManagerFixtureWithAuthenticator::setPrivilege(const std::string& privilege)
 {
-  saveIdentityCertificate(DEFAULT_COMMAND_SIGNER_IDENTITY, "ManagerCommonFixture.ndncert");
+  saveIdentityCert(DEFAULT_COMMAND_SIGNER_IDENTITY, "ManagerCommonFixture.ndncert");
 
   const std::string& config = R"CONFIG(
     authorizations
diff --git a/tests/daemon/mgmt/rib-manager-sl-announce.t.cpp b/tests/daemon/mgmt/rib-manager-sl-announce.t.cpp
index 9c8b87a..616b6e8 100644
--- a/tests/daemon/mgmt/rib-manager-sl-announce.t.cpp
+++ b/tests/daemon/mgmt/rib-manager-sl-announce.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -30,6 +30,9 @@
 #include "tests/daemon/global-io-fixture.hpp"
 #include "tests/daemon/rib/fib-updates-common.hpp"
 
+#include <ndn-cxx/security/transform/base64-encode.hpp>
+#include <ndn-cxx/security/transform/buffer-source.hpp>
+#include <ndn-cxx/security/transform/stream-sink.hpp>
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
 #include <boost/property_tree/info_parser.hpp>
@@ -137,7 +140,7 @@
   /** \brief Lookup a route with PREFIXANN origin.
    */
   Route*
-  findAnnRoute(const Name& name, uint64_t faceId)
+  findAnnRoute(const Name& name, uint64_t faceId) const
   {
     Route routeQuery;
     routeQuery.faceId = faceId;
@@ -145,6 +148,21 @@
     return rib.find(name, routeQuery);
   }
 
+  /** \brief Retrieve an identity certificate as a base64 string.
+   */
+  std::string
+  getIdentityCertificateBase64(const Name& identity) const
+  {
+    auto cert = m_keyChain.getPib().getIdentity(identity).getDefaultKey().getDefaultCertificate();
+    const auto& block = cert.wireEncode();
+
+    namespace tr = ndn::security::transform;
+    std::ostringstream oss;
+    tr::bufferSource(block.wire(), block.size()) >> tr::base64Encode(false) >> tr::streamSink(oss);
+
+    return oss.str();
+  }
+
 private:
   void
   loadDefaultPaConfig()
@@ -158,7 +176,7 @@
     manager->applyPaConfig(makeSection(CONFIG), "default");
   }
 
-public:
+protected:
   rib::Rib rib;
   unique_ptr<RibManager> manager;
 
diff --git a/tests/daemon/mgmt/rib-manager.t.cpp b/tests/daemon/mgmt/rib-manager.t.cpp
index 6ed5b0a..b688996 100644
--- a/tests/daemon/mgmt/rib-manager.t.cpp
+++ b/tests/daemon/mgmt/rib-manager.t.cpp
@@ -31,7 +31,6 @@
 #include <ndn-cxx/lp/tags.hpp>
 #include <ndn-cxx/mgmt/nfd/face-status.hpp>
 #include <ndn-cxx/mgmt/nfd/rib-entry.hpp>
-#include <ndn-cxx/security/signing-helpers.hpp>
 
 #include <boost/property_tree/info_parser.hpp>
 
@@ -129,8 +128,8 @@
     , m_fibUpdater(m_rib, m_nfdController)
     , m_manager(m_rib, m_face, m_keyChain, m_nfdController, m_dispatcher)
   {
-    addIdentity(m_anchorId);
-    addIdentity(m_derivedId);
+    m_keyChain.createIdentity(m_anchorId);
+    m_keyChain.createIdentity(m_derivedId);
 
     m_derivedCert = m_keyChain.getPib().getIdentity(m_derivedId).getDefaultKey().getDefaultCertificate();
     ndn::SignatureInfo signatureInfo;
@@ -138,7 +137,7 @@
     ndn::security::SigningInfo signingInfo(ndn::security::SigningInfo::SIGNER_TYPE_ID,
                                            m_anchorId, signatureInfo);
     m_keyChain.sign(m_derivedCert, signingInfo);
-    saveIdentityCertificate(m_anchorId, "signer.ndncert", true);
+    saveIdentityCert(m_anchorId, "signer.ndncert", true);
 
     if (m_status.isLocalhostConfigured) {
       m_manager.applyLocalhostConfig(getValidatorConfigSection(), "test");
diff --git a/tests/daemon/rib-io-fixture.cpp b/tests/daemon/rib-io-fixture.cpp
index a0c8048..66b347b 100644
--- a/tests/daemon/rib-io-fixture.cpp
+++ b/tests/daemon/rib-io-fixture.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -24,8 +24,8 @@
  */
 
 #include "tests/daemon/rib-io-fixture.hpp"
-#include "tests/test-common.hpp"
 #include "common/global.hpp"
+#include "tests/boost-test.hpp"
 
 #include <boost/exception/diagnostic_information.hpp>
 
@@ -122,13 +122,8 @@
   } while (nHandlersRun > 0);
 }
 
-RibIoTimeFixture::RibIoTimeFixture()
-  : ClockFixture(g_io)
-{
-}
-
 void
-RibIoTimeFixture::pollAfterClockTick()
+RibIoTimeFixture::afterTick()
 {
   poll();
 }
diff --git a/tests/daemon/rib-io-fixture.hpp b/tests/daemon/rib-io-fixture.hpp
index 5fcefb6..f8660fd 100644
--- a/tests/daemon/rib-io-fixture.hpp
+++ b/tests/daemon/rib-io-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -79,14 +79,11 @@
 
 /** \brief RibIoFixture that also overrides steady clock and system clock.
  */
-class RibIoTimeFixture : public RibIoFixture, public ClockFixture
+class RibIoTimeFixture : public ClockFixture, public RibIoFixture
 {
-protected:
-  RibIoTimeFixture();
-
 private:
   void
-  pollAfterClockTick() override;
+  afterTick() final;
 };
 
 } // namespace tests
diff --git a/tests/daemon/rib/readvertise/host-to-gateway-readvertise-policy.t.cpp b/tests/daemon/rib/readvertise/host-to-gateway-readvertise-policy.t.cpp
index 494eac2..c0d088a 100644
--- a/tests/daemon/rib/readvertise/host-to-gateway-readvertise-policy.t.cpp
+++ b/tests/daemon/rib/readvertise/host-to-gateway-readvertise-policy.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -29,8 +29,6 @@
 #include "tests/key-chain-fixture.hpp"
 #include "tests/daemon/global-io-fixture.hpp"
 
-#include <ndn-cxx/security/signing-helpers.hpp>
-
 namespace nfd {
 namespace rib {
 namespace tests {
@@ -63,9 +61,9 @@
 
 BOOST_AUTO_TEST_CASE(PrefixToAdvertise)
 {
-  BOOST_REQUIRE(addIdentity("/A"));
-  BOOST_REQUIRE(addIdentity("/A/B"));
-  BOOST_REQUIRE(addIdentity("/C/nrd"));
+  BOOST_REQUIRE(m_keyChain.createIdentity("/A"));
+  BOOST_REQUIRE(m_keyChain.createIdentity("/A/B"));
+  BOOST_REQUIRE(m_keyChain.createIdentity("/C/nrd"));
 
   auto test = [this] (Name routeName, optional<ReadvertiseAction> expectedAction) {
     auto policy = makePolicy();
diff --git a/tests/global-configuration.cpp b/tests/global-configuration.cpp
index 9a56347..a82937c 100644
--- a/tests/global-configuration.cpp
+++ b/tests/global-configuration.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -43,7 +43,7 @@
     if (envHome)
       m_home = envHome;
 
-    auto testHome = boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "test-home";
+    auto testHome = boost::filesystem::path(UNIT_TESTS_TMPDIR) / "test-home";
     if (::setenv("HOME", testHome.c_str(), 1) != 0)
       NDN_THROW(std::runtime_error("setenv() failed"));
 
diff --git a/tests/io-fixture.hpp b/tests/io-fixture.hpp
new file mode 100644
index 0000000..af9fc84
--- /dev/null
+++ b/tests/io-fixture.hpp
@@ -0,0 +1,59 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2020,  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_IO_FIXTURE_HPP
+#define NFD_TESTS_IO_FIXTURE_HPP
+
+#include "tests/clock-fixture.hpp"
+
+#include <boost/asio/io_service.hpp>
+
+namespace nfd {
+namespace tests {
+
+class IoFixture : public ClockFixture
+{
+private:
+  void
+  afterTick() final
+  {
+    if (m_io.stopped()) {
+#if BOOST_VERSION >= 106600
+      m_io.restart();
+#else
+      m_io.reset();
+#endif
+    }
+    m_io.poll();
+  }
+
+protected:
+  boost::asio::io_service m_io;
+};
+
+} // namespace tests
+} // namespace nfd
+
+#endif // NFD_TESTS_IO_FIXTURE_HPP
diff --git a/tests/key-chain-fixture.cpp b/tests/key-chain-fixture.cpp
index 9e20dd3..c71f175 100644
--- a/tests/key-chain-fixture.cpp
+++ b/tests/key-chain-fixture.cpp
@@ -25,11 +25,6 @@
 
 #include "tests/key-chain-fixture.hpp"
 
-#include <ndn-cxx/security/certificate.hpp>
-#include <ndn-cxx/security/pib/identity.hpp>
-#include <ndn-cxx/security/pib/key.hpp>
-#include <ndn-cxx/security/pib/pib.hpp>
-#include <ndn-cxx/security/transform.hpp>
 #include <ndn-cxx/util/io.hpp>
 
 #include <boost/filesystem.hpp>
@@ -37,10 +32,11 @@
 namespace nfd {
 namespace tests {
 
+using namespace ndn::security;
+
 KeyChainFixture::KeyChainFixture()
   : m_keyChain("pib-memory:", "tpm-memory:")
 {
-  m_keyChain.createIdentity("/DEFAULT");
 }
 
 KeyChainFixture::~KeyChainFixture()
@@ -51,32 +47,33 @@
   }
 }
 
-bool
-KeyChainFixture::addIdentity(const Name& identity, const ndn::KeyParams& params)
+Certificate
+KeyChainFixture::makeCert(const Key& key, const std::string& issuer, const Key& signingKey)
 {
-  try {
-    m_keyChain.createIdentity(identity, params);
-    return true;
-  }
-  catch (const std::runtime_error&) {
-    return false;
-  }
+  Certificate cert;
+  cert.setName(Name(key.getName())
+               .append(issuer)
+               .appendVersion());
+
+  // set metainfo
+  cert.setContentType(tlv::ContentType_Key);
+  cert.setFreshnessPeriod(1_h);
+
+  // set content
+  cert.setContent(key.getPublicKey().data(), key.getPublicKey().size());
+
+  // set signature info
+  ndn::SignatureInfo info;
+  auto now = time::system_clock::now();
+  info.setValidityPeriod(ValidityPeriod(now - 30_days, now + 30_days));
+
+  m_keyChain.sign(cert, signingByKey(signingKey ? signingKey : key).setSignatureInfo(info));
+  return cert;
 }
 
 bool
-KeyChainFixture::saveIdentityCertificate(const Name& identity, const std::string& filename, bool allowAdd)
+KeyChainFixture::saveCert(const Data& cert, const std::string& filename)
 {
-  ndn::security::Certificate cert;
-  try {
-    cert = m_keyChain.getPib().getIdentity(identity).getDefaultKey().getDefaultCertificate();
-  }
-  catch (const ndn::security::Pib::Error&) {
-    if (allowAdd && addIdentity(identity)) {
-      return saveIdentityCertificate(identity, filename, false);
-    }
-    return false;
-  }
-
   m_certFiles.push_back(filename);
   try {
     ndn::io::save(cert, filename);
@@ -87,26 +84,39 @@
   }
 }
 
-std::string
-KeyChainFixture::getIdentityCertificateBase64(const Name& identity, bool allowAdd)
+bool
+KeyChainFixture::saveIdentityCert(const Identity& identity, const std::string& filename)
 {
-  ndn::security::Certificate cert;
+  Certificate cert;
   try {
-    cert = m_keyChain.getPib().getIdentity(identity).getDefaultKey().getDefaultCertificate();
+    cert = identity.getDefaultKey().getDefaultCertificate();
   }
-  catch (const ndn::security::Pib::Error&) {
-    if (!allowAdd) {
-      NDN_THROW_NESTED(std::runtime_error("Identity does not exist"));
+  catch (const Pib::Error&) {
+    return false;
+  }
+
+  return saveCert(cert, filename);
+}
+
+bool
+KeyChainFixture::saveIdentityCert(const Name& identityName, const std::string& filename,
+                                  bool allowCreate)
+{
+  Identity id;
+  try {
+    id = m_keyChain.getPib().getIdentity(identityName);
+  }
+  catch (const Pib::Error&) {
+    if (allowCreate) {
+      id = m_keyChain.createIdentity(identityName);
     }
-    cert = m_keyChain.createIdentity(identity).getDefaultKey().getDefaultCertificate();
   }
 
-  const auto& block = cert.wireEncode();
+  if (!id) {
+    return false;
+  }
 
-  namespace tr = ndn::security::transform;
-  std::ostringstream oss;
-  tr::bufferSource(block.wire(), block.size()) >> tr::base64Encode(false) >> tr::streamSink(oss);
-  return oss.str();
+  return saveIdentityCert(id, filename);
 }
 
 } // namespace tests
diff --git a/tests/key-chain-fixture.hpp b/tests/key-chain-fixture.hpp
index 18ba4fc..9f03ea0 100644
--- a/tests/key-chain-fixture.hpp
+++ b/tests/key-chain-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -29,44 +29,63 @@
 #include "core/common.hpp"
 
 #include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/security/signing-helpers.hpp>
 
 namespace nfd {
 namespace tests {
 
-/** \brief A fixture providing an in-memory KeyChain.
+/**
+ * @brief A fixture providing an in-memory KeyChain.
+ *
+ * Test cases can use this fixture to create identities. Identities, certificates, and
+ * saved certificates are automatically removed during test teardown.
  */
 class KeyChainFixture
 {
+protected:
+  using Certificate = ndn::security::Certificate;
+  using Identity    = ndn::security::Identity;
+  using Key         = ndn::security::Key;
+
 public:
-  /** \brief add identity
-   *  \return whether successful
+  /**
+   * @brief Creates and returns a certificate for a given key
+   * @param key The key for which to make a certificate
+   * @param issuer The IssuerId to include in the certificate name
+   * @param signingKey The key with which to sign the certificate; if not provided, the
+   *                   certificate will be self-signed
+   */
+  Certificate
+  makeCert(const Key& key, const std::string& issuer, const Key& signingKey = Key());
+
+  /**
+   * @brief Saves an NDN certificate to a file
+   * @return true if successful, false otherwise
    */
   bool
-  addIdentity(const Name& identity,
-              const ndn::KeyParams& params = ndn::KeyChain::getDefaultKeyParams());
+  saveCert(const Data& cert, const std::string& filename);
 
-  /** \brief save identity certificate to a file
-   *  \param identity identity name
-   *  \param filename file name, must be writable
-   *  \param allowAdd if true, add new identity when necessary
-   *  \return whether successful
+  /**
+   * @brief Saves the default certificate of @p identity to a file
+   * @return true if successful, false otherwise
    */
   bool
-  saveIdentityCertificate(const Name& identity, const std::string& filename, bool allowAdd = false);
+  saveIdentityCert(const Identity& identity, const std::string& filename);
 
-  /** \brief retrieve identity certificate as base64 string
-   *  \param identity identity name
-   *  \param allowAdd if true, add new identity when necessary
-   *  \throw std::runtime_error identity does not exist and \p allowAdd is false
+  /**
+   * @brief Saves the default certificate of the identity named @p identityName to a file
+   * @param identityName Name of the identity
+   * @param filename File name, must be writable
+   * @param allowCreate If true, create the identity if it does not exist
+   * @return true if successful, false otherwise
    */
-  std::string
-  getIdentityCertificateBase64(const Name& identity, bool allowAdd = false);
+  bool
+  saveIdentityCert(const Name& identityName, const std::string& filename,
+                   bool allowCreate = false);
 
 protected:
   KeyChainFixture();
 
-  /** \brief deletes saved certificate files
-   */
   ~KeyChainFixture();
 
 protected:
diff --git a/tests/other/wscript b/tests/other/wscript
index 3393a15..0d94bd6 100644
--- a/tests/other/wscript
+++ b/tests/other/wscript
@@ -37,8 +37,7 @@
         bld.program(name=module,
                     target='../../%s' % module,
                     source=bld.path.ant_glob('%s*.cpp' % module),
-                    use='daemon-objects tests-common other-tests-%s-main' % module,
-                    defines=['UNIT_TEST_CONFIG_PATH="%s"' % bld.bldnode.make_node('tmp-files')],
+                    use='daemon-objects other-tests-%s-main' % module,
                     install_path=None)
 
     # face-benchmark does not rely on Boost.Test
diff --git a/tests/test-common.hpp b/tests/test-common.hpp
index 8d37568..9d4edbd 100644
--- a/tests/test-common.hpp
+++ b/tests/test-common.hpp
@@ -58,7 +58,7 @@
 /**
  * \brief Create a Data with a null (i.e., empty) signature
  *
- * If a real signature is desired, use KeyChainFixture and sign again with `m_keyChain`.
+ * If a "real" signature is desired, use KeyChainFixture and sign again with `m_keyChain`.
  */
 shared_ptr<Data>
 makeData(const Name& name);
diff --git a/tests/tools/mock-nfd-mgmt-fixture.hpp b/tests/tools/mock-nfd-mgmt-fixture.hpp
index 40dc3ba..7c02720 100644
--- a/tests/tools/mock-nfd-mgmt-fixture.hpp
+++ b/tests/tools/mock-nfd-mgmt-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -26,7 +26,7 @@
 #ifndef NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
 #define NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
 
-#include "tests/clock-fixture.hpp"
+#include "tests/io-fixture.hpp"
 #include "tests/key-chain-fixture.hpp"
 #include "tests/test-common.hpp"
 
@@ -45,12 +45,11 @@
 
 /** \brief Fixture to emulate NFD management.
  */
-class MockNfdMgmtFixture : public ClockFixture, public KeyChainFixture
+class MockNfdMgmtFixture : public IoFixture, public KeyChainFixture
 {
 protected:
   MockNfdMgmtFixture()
-    : ClockFixture(m_io)
-    , face(m_io, m_keyChain,
+    : face(m_io, m_keyChain,
            {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
   {
     face.onSendInterest.connect([this] (const Interest& interest) {
@@ -206,9 +205,6 @@
     signData(data);
   }
 
-private:
-  boost::asio::io_service m_io;
-
 protected:
   ndn::util::DummyClientFace face;
   std::function<void(const Interest&)> processInterest;
diff --git a/tests/tools/nfdc/forwarder-general-module.t.cpp b/tests/tools/nfdc/forwarder-general-module.t.cpp
index ad282a3..5d2255b 100644
--- a/tests/tools/nfdc/forwarder-general-module.t.cpp
+++ b/tests/tools/nfdc/forwarder-general-module.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -27,8 +27,6 @@
 
 #include "status-fixture.hpp"
 
-#include <ndn-cxx/security/signing-helpers.hpp>
-
 namespace nfd {
 namespace tools {
 namespace nfdc {
diff --git a/tests/tools/nfdc/status-report.t.cpp b/tests/tools/nfdc/status-report.t.cpp
index 5041fb3..2ab5c3e 100644
--- a/tests/tools/nfdc/status-report.t.cpp
+++ b/tests/tools/nfdc/status-report.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Regents of the University of California,
+ * Copyright (c) 2014-2020,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -120,12 +120,11 @@
   std::function<void()> processEventsFunc;
 };
 
-class StatusReportModulesFixture : public ClockFixture, public KeyChainFixture
+class StatusReportModulesFixture : public IoFixture, public KeyChainFixture
 {
 protected:
   StatusReportModulesFixture()
-    : ClockFixture(m_io)
-    , face(m_io, m_keyChain)
+    : face(m_io, m_keyChain)
     , controller(face, m_keyChain, validator)
     , res(0)
   {
@@ -141,9 +140,7 @@
   void
   collect(time::nanoseconds tick, size_t nTicks)
   {
-    report.processEventsFunc = [=] {
-      this->advanceClocks(tick, nTicks);
-    };
+    report.processEventsFunc = [=] { advanceClocks(tick, nTicks); };
     res = report.collect(face, m_keyChain, validator, CommandOptions());
 
     if (res == 0) {
@@ -154,9 +151,6 @@
     }
   }
 
-private:
-  boost::asio::io_service m_io;
-
 protected:
   ndn::util::DummyClientFace face;
   ValidatorNull validator;
diff --git a/tests/wscript b/tests/wscript
index a5b9a8a..cf28718 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -28,7 +28,7 @@
 def build(bld):
     # Unit tests
     if bld.env.WITH_TESTS:
-        config_path = 'UNIT_TEST_CONFIG_PATH="%s"' % bld.bldnode.make_node('tmp-files')
+        tmpdir = 'UNIT_TESTS_TMPDIR="%s"' % bld.bldnode.make_node('tmp-files')
 
         # common test objects
         bld.objects(
@@ -37,7 +37,7 @@
             source=bld.path.ant_glob('*.cpp', excl='main.cpp'),
             use='core-objects',
             headers='../core/common.hpp boost-test.hpp',
-            defines=[config_path])
+            defines=[tmpdir])
 
         for module in ['core', 'daemon', 'rib', 'tools']:
             # main() for the module
@@ -70,7 +70,7 @@
                         target='../unit-tests-%s' % module,
                         source=src,
                         use='%s-objects tests-common unit-tests-%s-main' % (objmod, module),
-                        defines=[config_path],
+                        defines=[tmpdir],
                         install_path=None)
 
     # Other tests (e.g., stress tests and benchmarks that can be enabled even if unit tests are disabled)