tests: sync common testing infrastructure with ndn-cxx and NFD

And upgrade waf to version 2.0.21

Change-Id: Id713e0f3035badd97ca7fc03f76e50085b9a266c
diff --git a/tests/chunks/consumer.t.cpp b/tests/chunks/consumer.t.cpp
index a2bd683..32e6914 100644
--- a/tests/chunks/consumer.t.cpp
+++ b/tests/chunks/consumer.t.cpp
@@ -28,6 +28,7 @@
 #include "tools/chunks/catchunks/pipeline-interests.hpp"
 
 #include "tests/test-common.hpp"
+#include "tests/io-fixture.hpp"
 
 #include <ndn-cxx/security/validator-null.hpp>
 #include <ndn-cxx/util/dummy-client-face.hpp>
@@ -151,10 +152,9 @@
   bool isPipelineRunning = false;
 };
 
-BOOST_FIXTURE_TEST_CASE(RunBasic, UnitTestTimeFixture)
+BOOST_FIXTURE_TEST_CASE(RunBasic, IoFixture)
 {
-  boost::asio::io_service io;
-  util::DummyClientFace face(io);
+  util::DummyClientFace face(m_io);
   Options options;
   Consumer consumer(security::getAcceptAllValidator());
 
@@ -166,7 +166,7 @@
   BOOST_CHECK_EQUAL(pipelinePtr->isPipelineRunning, false);
 
   consumer.run(std::move(discover), std::move(pipeline));
-  this->advanceClocks(io, 1_ms);
+  this->advanceClocks(1_ms);
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 0); // no discovery Interests are issued
   BOOST_CHECK_EQUAL(pipelinePtr->isPipelineRunning, true);
diff --git a/tests/chunks/discover-version.t.cpp b/tests/chunks/discover-version.t.cpp
index 9c3a844..79315cc 100644
--- a/tests/chunks/discover-version.t.cpp
+++ b/tests/chunks/discover-version.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2019, Regents of the University of California,
+ * Copyright (c) 2016-2020, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
  *
@@ -26,7 +26,8 @@
 #include "tools/chunks/catchunks/discover-version.hpp"
 
 #include "tests/test-common.hpp"
-#include "tests/identity-management-fixture.hpp"
+#include "tests/io-fixture.hpp"
+#include "tests/key-chain-fixture.hpp"
 
 #include <ndn-cxx/metadata-object.hpp>
 #include <ndn-cxx/util/dummy-client-face.hpp>
@@ -37,8 +38,7 @@
 
 using namespace ndn::tests;
 
-class DiscoverVersionFixture : public UnitTestTimeFixture,
-                               public IdentityManagementFixture
+class DiscoverVersionFixture : public IoFixture, public KeyChainFixture
 {
 public:
   void
@@ -56,14 +56,13 @@
     });
 
     discover->run();
-    advanceClocks(io, 1_ns);
+    advanceClocks(1_ns);
   }
 
 protected:
   const Name name = "/ndn/chunks/test";
   const uint64_t version = 1449227841747;
-  boost::asio::io_service io;
-  util::DummyClientFace face{io};
+  util::DummyClientFace face{m_io};
   Options opt;
   unique_ptr<DiscoverVersion> discover;
   optional<Name> discoveredName;
@@ -113,7 +112,7 @@
   MetadataObject mobject;
   mobject.setVersionedName(Name(name).appendVersion(version));
   face.receive(mobject.makeData(lastInterest.getName(), m_keyChain));
-  advanceClocks(io, 1_ns);
+  advanceClocks(1_ns);
 
   BOOST_CHECK_EQUAL(discoveredVersion.value(), version);
 }
@@ -163,11 +162,11 @@
   // timeout or nack discovery Interests
   for (int retries = 0; retries < opt.maxRetriesOnTimeoutOrNack * 2; ++retries) {
     if (retries % 2 == 0) {
-      advanceClocks(io, opt.interestLifetime);
+      advanceClocks(opt.interestLifetime);
     }
     else {
       face.receive(makeNack(face.sentInterests.back(), lp::NackReason::DUPLICATE));
-      advanceClocks(io, 1_ns);
+      advanceClocks(1_ns);
     }
 
     BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
@@ -175,7 +174,7 @@
   }
 
   // timeout the last sent Interest
-  advanceClocks(io, opt.interestLifetime);
+  advanceClocks(opt.interestLifetime);
 
   // finish discovery process without a resolved version number
   BOOST_CHECK_EQUAL(isDiscoveryFinished, true);
@@ -193,11 +192,11 @@
   // timeout or nack discovery Interests
   for (int retries = 0; retries < opt.maxRetriesOnTimeoutOrNack * 2; ++retries) {
     if (retries % 2 == 0) {
-      advanceClocks(io, opt.interestLifetime);
+      advanceClocks(opt.interestLifetime);
     }
     else {
       face.receive(makeNack(face.sentInterests.back(), lp::NackReason::DUPLICATE));
-      advanceClocks(io, 1_ns);
+      advanceClocks(1_ns);
     }
 
     BOOST_CHECK_EQUAL(isDiscoveryFinished, false);
@@ -208,7 +207,7 @@
   MetadataObject mobject;
   mobject.setVersionedName(Name(name).appendVersion(version));
   face.receive(mobject.makeData(face.sentInterests.back().getName(), m_keyChain));
-  advanceClocks(io, 1_ns);
+  advanceClocks(1_ns);
 
   BOOST_CHECK_EQUAL(discoveredVersion.value(), version);
 }
diff --git a/tests/chunks/pipeline-interests-aimd.t.cpp b/tests/chunks/pipeline-interests-aimd.t.cpp
index f38251a..feed933 100644
--- a/tests/chunks/pipeline-interests-aimd.t.cpp
+++ b/tests/chunks/pipeline-interests-aimd.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2019, Regents of the University of California,
+ * Copyright (c) 2016-2020, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
  *
@@ -88,12 +88,12 @@
 
   double preCwnd = pipeline->m_cwnd;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
     BOOST_CHECK_CLOSE(pipeline->m_cwnd - preCwnd, 1, MARGIN);
     preCwnd = pipeline->m_cwnd;
   }
@@ -109,12 +109,12 @@
 
   double preCwnd = pipeline->m_cwnd;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   for (uint64_t i = 0; i < pipeline->m_ssthresh; ++i) { // slow start
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
     preCwnd = pipeline->m_cwnd;
   }
 
@@ -122,7 +122,7 @@
 
   for (uint64_t i = pipeline->m_ssthresh; i < nDataSegments - 1; ++i) { // congestion avoidance
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
     BOOST_CHECK_CLOSE(pipeline->m_cwnd - preCwnd, opt.aiStep / floor(preCwnd), MARGIN);
     preCwnd = pipeline->m_cwnd;
   }
@@ -137,28 +137,28 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segment 0, 1, and 2
   for (uint64_t i = 0; i < 3; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 3);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.25, MARGIN);
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 7); // request for segment 7 has been sent
 
-  advanceClocks(io, time::milliseconds(100));
+  advanceClocks(time::milliseconds(100));
 
   // receive segment 4
   face.receive(*makeDataWithSegment(4));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 5
   face.receive(*makeDataWithSegment(5));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.75, MARGIN);
@@ -172,7 +172,7 @@
   BOOST_CHECK_EQUAL(pipeline->m_nCongMarks, 0);
 
   // timeout segment 3 & 6
-  advanceClocks(io, time::milliseconds(150));
+  advanceClocks(time::milliseconds(150));
   BOOST_CHECK_EQUAL(pipeline->m_nTimeouts, 2);
   BOOST_CHECK_EQUAL(pipeline->m_nRetransmitted, 1);
   BOOST_CHECK_EQUAL(pipeline->m_nLossDecr, 1);
@@ -184,7 +184,7 @@
 
   // receive segment 6, retransmit 3
   face.receive(*makeDataWithSegment(6));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 2.875, MARGIN); // congestion avoidance
@@ -205,13 +205,13 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segments 0 to 4
   for (uint64_t i = 0; i < 5; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
@@ -219,7 +219,7 @@
 
   // receive segment 5 with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(5));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 2.375, MARGIN); // window size drops to 1/2 of previous size
@@ -227,7 +227,7 @@
 
   // receive the last segment with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 2.375, MARGIN); // conservative window adaption (window size should not decrease)
@@ -251,13 +251,13 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segments 0 to 4
   for (uint64_t i = 0; i < 5; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
@@ -265,7 +265,7 @@
 
   // receive segment 5 with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(5));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 2.375, MARGIN); // window size drops to 1/2 of previous size
@@ -273,7 +273,7 @@
 
   // receive the last segment with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, PipelineInterestsAdaptive::MIN_SSTHRESH,
@@ -298,13 +298,13 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segments 0 to 5
   for (uint64_t i = 0; i < 6; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
@@ -313,7 +313,7 @@
 
   // receive the last segment with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 5.2, MARGIN); // window size increases
@@ -331,13 +331,13 @@
   nDataSegments = 5;
   pipeline->m_cwnd = 10.0;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   face.receive(*makeDataWithSegment(0));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   face.receive(*makeDataWithSegment(1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 10);
@@ -345,7 +345,7 @@
   // receive a nack with NackReason::DUPLICATE for segment 1
   auto nack1 = makeNack(face.sentInterests[1], lp::NackReason::DUPLICATE);
   face.receive(nack1);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // nack1 is ignored
   BOOST_CHECK_EQUAL(hasFailed, false);
@@ -355,7 +355,7 @@
   // receive a nack with NackReason::CONGESTION for segment 2
   auto nack2 = makeNack(face.sentInterests[2], lp::NackReason::CONGESTION);
   face.receive(nack2);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // segment 2 is retransmitted
   BOOST_CHECK_EQUAL(pipeline->m_retxCount[2], 1);
@@ -363,7 +363,7 @@
   // receive a nack with NackReason::NONE for segment 3
   auto nack3 = makeNack(face.sentInterests[3], lp::NackReason::NONE);
   face.receive(nack3);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // Other types of Nack will trigger a failure
   BOOST_CHECK_EQUAL(hasFailed, true);
@@ -375,11 +375,11 @@
   nDataSegments = 4;
   pipeline->m_cwnd = 4;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0 without FinalBlockId
   face.receive(*makeDataWithSegment(0, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // interests for segment 0 - 5 have been sent
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 6);
@@ -390,7 +390,7 @@
 
   // receive segment 1 with FinalBlockId
   face.receive(*makeDataWithSegment(1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
   BOOST_CHECK_EQUAL(pipeline->m_hasFinalBlockId, true);
 
@@ -407,15 +407,15 @@
   nDataSegments = 4;
   pipeline->m_cwnd = 4;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0 without FinalBlockId
   face.receive(*makeDataWithSegment(0, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 1 without FinalBlockId
   face.receive(*makeDataWithSegment(1, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // interests for segment 0 - 7 have been sent
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 8);
@@ -423,7 +423,7 @@
   // receive nack with NackReason::NONE for segment 3
   auto nack = makeNack(face.sentInterests[3], lp::NackReason::NONE);
   face.receive(nack);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // error not triggered
   // pending interests for segment > 3 haven been removed
@@ -432,7 +432,7 @@
 
   // receive segment 2 with FinalBlockId
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // error triggered since segment 3 is part of the content
   BOOST_CHECK_EQUAL(hasFailed, true);
@@ -447,15 +447,15 @@
   nDataSegments = 4;
   pipeline->m_cwnd = 4;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0 without FinalBlockId
   face.receive(*makeDataWithSegment(0, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 1 without FinalBlockId
   face.receive(*makeDataWithSegment(1, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // interests for segment 0 - 7 have been sent
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 8);
@@ -463,7 +463,7 @@
   // receive nack with NackReason::NONE for segment 4
   auto nack = makeNack(face.sentInterests[4], lp::NackReason::NONE);
   face.receive(nack);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // error not triggered
   // pending interests for segment > 3 have been removed
@@ -472,17 +472,17 @@
 
   // receive segment 2 with FinalBlockId
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // timeout segment 3
-  advanceClocks(io, time::seconds(1));
+  advanceClocks(time::seconds(1));
 
   // segment 3 is retransmitted
   BOOST_CHECK_EQUAL(pipeline->m_retxCount[3], 1);
 
   // receive segment 3
   face.receive(*makeDataWithSegment(3));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(hasFailed, false);
 }
@@ -495,15 +495,15 @@
   nDataSegments = 3;
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0
   face.receive(*makeDataWithSegment(0));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 1
   face.receive(*makeDataWithSegment(1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
 
@@ -513,7 +513,7 @@
   BOOST_CHECK(it->second.state == SegmentState::FirstTimeSent);
 
   // timeout segment 2 twice
-  advanceClocks(io, time::milliseconds(400), 3);
+  advanceClocks(time::milliseconds(400), 3);
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 5);
 
@@ -527,7 +527,7 @@
 
   // receive segment 2 the first time
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // check if segment 2 was erased from m_segmentInfo
   it = pipeline->m_segmentInfo.find(2);
@@ -538,7 +538,7 @@
 
   // receive segment 2 the second time
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // nothing changed
   it = pipeline->m_segmentInfo.find(2);
@@ -582,10 +582,10 @@
   nDataSegments = 1;
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   face.receive(*makeDataWithSegment(0));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_hasFinalBlockId, true);
   BOOST_CHECK_EQUAL(pipeline->m_segmentInfo.size(), 0);
diff --git a/tests/chunks/pipeline-interests-cubic.t.cpp b/tests/chunks/pipeline-interests-cubic.t.cpp
index 51d5be2..fc53343 100644
--- a/tests/chunks/pipeline-interests-cubic.t.cpp
+++ b/tests/chunks/pipeline-interests-cubic.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2019, Regents of the University of California,
+ * Copyright (c) 2016-2020, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
  *
@@ -87,12 +87,12 @@
 
   double preCwnd = pipeline->m_cwnd;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
     BOOST_CHECK_CLOSE(pipeline->m_cwnd - preCwnd, 1, MARGIN);
     preCwnd = pipeline->m_cwnd;
   }
@@ -107,28 +107,28 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segment 0, 1, and 2
   for (uint64_t i = 0; i < 3; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 3);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 5, MARGIN);
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 8); // request for segment #7 has been sent
 
-  advanceClocks(io, time::milliseconds(100));
+  advanceClocks(time::milliseconds(100));
 
   // receive segment 4
   face.receive(*makeDataWithSegment(4));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 5
   face.receive(*makeDataWithSegment(5));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 7.0, MARGIN);
@@ -142,7 +142,7 @@
   BOOST_CHECK_EQUAL(pipeline->m_nCongMarks, 0);
 
   // timeout segment 3 & 6
-  advanceClocks(io, time::milliseconds(150));
+  advanceClocks(time::milliseconds(150));
   BOOST_CHECK_EQUAL(pipeline->m_nTimeouts, 3);
   BOOST_CHECK_EQUAL(pipeline->m_nRetransmitted, 3);
   BOOST_CHECK_EQUAL(pipeline->m_nLossDecr, 1);
@@ -154,7 +154,7 @@
 
   // receive segment 6, retransmit 3
   face.receive(*makeDataWithSegment(6));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.9, MARGIN); // congestion avoidance
@@ -174,13 +174,13 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segments 0 to 4
   for (uint64_t i = 0; i < 5; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
@@ -188,7 +188,7 @@
 
   // receive segment 5 with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(5));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.9, MARGIN); // window size drops to 0.7x of previous size
@@ -196,7 +196,7 @@
 
   // receive the last segment with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.9, MARGIN); // conservative window adaption (window size should not decrease)
@@ -219,13 +219,13 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segments 0 to 4
   for (uint64_t i = 0; i < 5; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 5);
@@ -233,7 +233,7 @@
 
   // receive segment 5 with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(5));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 4.9, MARGIN); // window size drops to 0.7x of previous size
@@ -241,7 +241,7 @@
 
   // receive the last segment with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 3.43, MARGIN); // window size should decrease, as cwa is disabled
@@ -264,13 +264,13 @@
   BOOST_REQUIRE_CLOSE(pipeline->m_cwnd, 2, MARGIN);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 2);
 
   // receive segments 0 to 5
   for (uint64_t i = 0; i < 6; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1));
+    advanceClocks(time::nanoseconds(1));
   }
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 6);
@@ -279,7 +279,7 @@
 
   // receive the last segment with congestion mark
   face.receive(*makeDataWithSegmentAndCongMark(nDataSegments - 1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments);
   BOOST_CHECK_CLOSE(pipeline->m_cwnd, 9.0, MARGIN); // window size increases
@@ -297,13 +297,13 @@
   nDataSegments = 5;
   pipeline->m_cwnd = 10.0;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   face.receive(*makeDataWithSegment(0));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   face.receive(*makeDataWithSegment(1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 10);
@@ -311,7 +311,7 @@
   // receive a nack with NackReason::DUPLICATE for segment 1
   auto nack1 = makeNack(face.sentInterests[1], lp::NackReason::DUPLICATE);
   face.receive(nack1);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // nack1 is ignored
   BOOST_CHECK_EQUAL(hasFailed, false);
@@ -321,7 +321,7 @@
   // receive a nack with NackReason::CONGESTION for segment 2
   auto nack2 = makeNack(face.sentInterests[2], lp::NackReason::CONGESTION);
   face.receive(nack2);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // segment 2 is retransmitted
   BOOST_CHECK_EQUAL(pipeline->m_retxCount[2], 1);
@@ -329,7 +329,7 @@
   // receive a nack with NackReason::NONE for segment 3
   auto nack3 = makeNack(face.sentInterests[3], lp::NackReason::NONE);
   face.receive(nack3);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // Other types of Nack will trigger a failure
   BOOST_CHECK_EQUAL(hasFailed, true);
@@ -341,11 +341,11 @@
   nDataSegments = 4;
   pipeline->m_cwnd = 4;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0 without FinalBlockId
   face.receive(*makeDataWithSegment(0, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // interests for segment 0 - 5 have been sent
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 6);
@@ -356,7 +356,7 @@
 
   // receive segment 1 with FinalBlockId
   face.receive(*makeDataWithSegment(1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 2);
   BOOST_CHECK_EQUAL(pipeline->m_hasFinalBlockId, true);
 
@@ -373,15 +373,15 @@
   nDataSegments = 4;
   pipeline->m_cwnd = 4;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0 without FinalBlockId
   face.receive(*makeDataWithSegment(0, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 1 without FinalBlockId
   face.receive(*makeDataWithSegment(1, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // interests for segment 0 - 7 have been sent
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 8);
@@ -389,7 +389,7 @@
   // receive nack with NackReason::NONE for segment 3
   auto nack = makeNack(face.sentInterests[3], lp::NackReason::NONE);
   face.receive(nack);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // error not triggered
   // pending interests for segment > 3 haven been removed
@@ -398,7 +398,7 @@
 
   // receive segment 2 with FinalBlockId
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // error triggered since segment 3 is part of the content
   BOOST_CHECK_EQUAL(hasFailed, true);
@@ -413,15 +413,15 @@
   nDataSegments = 4;
   pipeline->m_cwnd = 4;
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0 without FinalBlockId
   face.receive(*makeDataWithSegment(0, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 1 without FinalBlockId
   face.receive(*makeDataWithSegment(1, false));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // interests for segment 0 - 7 have been sent
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 8);
@@ -429,7 +429,7 @@
   // receive nack with NackReason::NONE for segment 4
   auto nack = makeNack(face.sentInterests[4], lp::NackReason::NONE);
   face.receive(nack);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // error not triggered
   // pending interests for segment > 3 have been removed
@@ -438,17 +438,17 @@
 
   // receive segment 2 with FinalBlockId
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // timeout segment 3
-  advanceClocks(io, time::seconds(1));
+  advanceClocks(time::seconds(1));
 
   // segment 3 is retransmitted
   BOOST_CHECK_EQUAL(pipeline->m_retxCount[3], 1);
 
   // receive segment 3
   face.receive(*makeDataWithSegment(3));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(hasFailed, false);
 }
@@ -461,15 +461,15 @@
   nDataSegments = 3;
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 0
   face.receive(*makeDataWithSegment(0));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // receive segment 1
   face.receive(*makeDataWithSegment(1));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 3);
 
@@ -479,7 +479,7 @@
   BOOST_CHECK(it->second.state == SegmentState::FirstTimeSent);
 
   // timeout segment 2 twice
-  advanceClocks(io, time::milliseconds(400), 3);
+  advanceClocks(time::milliseconds(400), 3);
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 5);
 
@@ -493,7 +493,7 @@
 
   // receive segment 2 the first time
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // check if segment 2 was erased from m_segmentInfo
   it = pipeline->m_segmentInfo.find(2);
@@ -504,7 +504,7 @@
 
   // receive segment 2 the second time
   face.receive(*makeDataWithSegment(2));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   // nothing changed
   it = pipeline->m_segmentInfo.find(2);
@@ -548,10 +548,10 @@
   nDataSegments = 1;
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   face.receive(*makeDataWithSegment(0));
-  advanceClocks(io, time::nanoseconds(1));
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_hasFinalBlockId, true);
   BOOST_CHECK_EQUAL(pipeline->m_segmentInfo.size(), 0);
diff --git a/tests/chunks/pipeline-interests-fixed.t.cpp b/tests/chunks/pipeline-interests-fixed.t.cpp
index f4979a2..872fe4e 100644
--- a/tests/chunks/pipeline-interests-fixed.t.cpp
+++ b/tests/chunks/pipeline-interests-fixed.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2019, Regents of the University of California,
+ * Copyright (c) 2016-2020, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
  *
@@ -67,12 +67,12 @@
   BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1), 1);
+  advanceClocks(time::nanoseconds(1));
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
 
   for (uint64_t i = 0; i < nDataSegments - 1; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1), 1);
+    advanceClocks(time::nanoseconds(1));
     BOOST_CHECK_EQUAL(pipeline->m_nReceived, i + 1);
 
     if (i < nDataSegments - opt.maxPipelineSize) {
@@ -92,7 +92,7 @@
 
   BOOST_CHECK_EQUAL(hasFailed, false);
 
-  advanceClocks(io, ndn::DEFAULT_INTEREST_LIFETIME, opt.maxRetriesOnTimeoutOrNack + 1);
+  advanceClocks(ndn::DEFAULT_INTEREST_LIFETIME, opt.maxRetriesOnTimeoutOrNack + 1);
   BOOST_CHECK_EQUAL(hasFailed, true);
 }
 
@@ -102,11 +102,11 @@
   BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1), 1);
+  advanceClocks(time::nanoseconds(1));
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
 
   for (int i = 0; i < opt.maxRetriesOnTimeoutOrNack; ++i) {
-    advanceClocks(io, opt.interestLifetime, 1);
+    advanceClocks(opt.interestLifetime);
     BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * (i + 2));
     BOOST_CHECK_EQUAL(pipeline->m_nReceived, 0);
 
@@ -117,7 +117,7 @@
     }
   }
 
-  advanceClocks(io, opt.interestLifetime, 1);
+  advanceClocks(opt.interestLifetime);
   BOOST_CHECK_EQUAL(hasFailed, true);
 }
 
@@ -130,23 +130,23 @@
   BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1), 1);
+  advanceClocks(time::nanoseconds(1));
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
 
   // send a single segment for each pipeline element but not the first element
-  advanceClocks(io, opt.interestLifetime, 1);
+  advanceClocks(opt.interestLifetime);
   for (uint64_t i = 1; i < opt.maxPipelineSize; ++i) {
     face.receive(*makeDataWithSegment(i));
-    advanceClocks(io, time::nanoseconds(1), 1);
+    advanceClocks(time::nanoseconds(1));
   }
 
   // send a single data packet for each pipeline element
-  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
+  advanceClocks(opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
   for (uint64_t i = 0; i < opt.maxPipelineSize; ++i) {
     face.receive(*makeDataWithSegment(opt.maxPipelineSize + i));
-    advanceClocks(io, time::nanoseconds(1), 1);
+    advanceClocks(time::nanoseconds(1));
   }
-  advanceClocks(io, opt.interestLifetime, 1);
+  advanceClocks(opt.interestLifetime);
 
   size_t interestAfterFailure = face.sentInterests.size();
 
@@ -154,14 +154,14 @@
   BOOST_CHECK_EQUAL(hasFailed, true);
 
   // these new segments should not generate new interests
-  advanceClocks(io, opt.interestLifetime, 1);
+  advanceClocks(opt.interestLifetime);
   for (uint64_t i = 0; i < opt.maxPipelineSize; ++i) {
     face.receive(*makeDataWithSegment(opt.maxPipelineSize * 2 + i - 1));
-    advanceClocks(io, time::nanoseconds(1), 1);
+    advanceClocks(time::nanoseconds(1));
   }
 
   // no more interests after a failure
-  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
+  advanceClocks(opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
   BOOST_CHECK_EQUAL(interestAfterFailure, face.sentInterests.size());
   BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
 }
@@ -175,13 +175,13 @@
   BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1), 1);
+  advanceClocks(time::nanoseconds(1));
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
 
-  advanceClocks(io, opt.interestLifetime, 1);
+  advanceClocks(opt.interestLifetime);
   for (uint64_t i = 2; i < opt.maxPipelineSize; ++i) {
     face.receive(*makeDataWithSegment(i, false));
-    advanceClocks(io, time::nanoseconds(1), 1);
+    advanceClocks(time::nanoseconds(1));
 
     const auto& lastInterest = face.sentInterests.back();
     BOOST_CHECK_EQUAL(getSegmentFromPacket(lastInterest), opt.maxPipelineSize + i - 2);
@@ -195,7 +195,7 @@
 
   // all the pipeline elements are two retries near the timeout error, but not the
   // second (segment #1) that is only one retry near the timeout
-  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
+  advanceClocks(opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack - 1);
   BOOST_CHECK_EQUAL(hasFailed, false);
 
   // data for the first pipeline element (segment #0)
@@ -210,10 +210,10 @@
     else {
       face.receive(*makeDataWithSegment(i, false));
     }
-    advanceClocks(io, time::nanoseconds(1), 1);
+    advanceClocks(time::nanoseconds(1));
   }
   // timeout for the second pipeline element (segment #1), this should trigger an error
-  advanceClocks(io, opt.interestLifetime, 1);
+  advanceClocks(opt.interestLifetime);
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, nDataSegments - 1);
   BOOST_CHECK_EQUAL(hasFailed, true);
@@ -228,10 +228,10 @@
   BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1), 1);
+  advanceClocks(time::nanoseconds(1));
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
 
-  advanceClocks(io, opt.interestLifetime, 1);
+  advanceClocks(opt.interestLifetime);
 
   // nack for the first pipeline element (segment #0)
   auto nack = make_shared<lp::Nack>(face.sentInterests[opt.maxPipelineSize]);
@@ -240,13 +240,13 @@
   BOOST_CHECK_EQUAL(hasFailed, false);
 
   // timeout for all the pipeline elements, but not the first (segment #0)
-  advanceClocks(io, opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
+  advanceClocks(opt.interestLifetime, opt.maxRetriesOnTimeoutOrNack);
   BOOST_CHECK_EQUAL(hasFailed, false);
 
   // data for the first pipeline element (segment #0), this should trigger an error because the
   // other pipeline elements failed
   face.receive(*makeDataWithSegment(0, false));
-  advanceClocks(io, time::nanoseconds(1), 1);
+  advanceClocks(time::nanoseconds(1));
 
   BOOST_CHECK_EQUAL(pipeline->m_nReceived, 1);
   BOOST_CHECK_EQUAL(hasFailed, true);
@@ -258,7 +258,7 @@
   BOOST_ASSERT(nDataSegments > opt.maxPipelineSize);
 
   run(name);
-  advanceClocks(io, time::nanoseconds(1), 1);
+  advanceClocks(time::nanoseconds(1));
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize);
 
   // send nack for all the pipeline elements first interest
@@ -266,7 +266,7 @@
     auto nack = make_shared<lp::Nack>(face.sentInterests[i]);
     nack->setReason(lp::NackReason::CONGESTION);
     face.receive(*nack);
-    advanceClocks(io, time::nanoseconds(1), 1);
+    advanceClocks(time::nanoseconds(1));
   }
 
   // send nack for all the pipeline elements interests after the first
@@ -275,7 +275,7 @@
     if (backoffTime > DataFetcher::MAX_CONGESTION_BACKOFF_TIME)
       backoffTime = DataFetcher::MAX_CONGESTION_BACKOFF_TIME;
 
-    advanceClocks(io, backoffTime, 1);
+    advanceClocks(backoffTime);
     BOOST_REQUIRE_EQUAL(face.sentInterests.size(), opt.maxPipelineSize * (i +1));
 
     // A single retry for every pipeline element
@@ -288,7 +288,7 @@
       auto nack = make_shared<lp::Nack>(face.sentInterests[(opt.maxPipelineSize * i) + j]);
       nack->setReason(lp::NackReason::CONGESTION);
       face.receive(*nack);
-      advanceClocks(io, time::nanoseconds(1), 1);
+      advanceClocks(time::nanoseconds(1));
     }
   }
 
diff --git a/tests/chunks/pipeline-interests-fixture.hpp b/tests/chunks/pipeline-interests-fixture.hpp
index dc7eabd..643047a 100644
--- a/tests/chunks/pipeline-interests-fixture.hpp
+++ b/tests/chunks/pipeline-interests-fixture.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2016-2019, Regents of the University of California,
+ * Copyright (c) 2016-2020, Regents of the University of California,
  *                          Colorado State University,
  *                          University Pierre & Marie Curie, Sorbonne University.
  *
@@ -32,6 +32,7 @@
 #include "tools/chunks/catchunks/pipeline-interests.hpp"
 
 #include "tests/test-common.hpp"
+#include "tests/io-fixture.hpp"
 
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
@@ -41,7 +42,7 @@
 
 using namespace ndn::tests;
 
-class PipelineInterestsFixture : public UnitTestTimeFixture
+class PipelineInterestsFixture : public IoFixture
 {
 protected:
   void
@@ -78,8 +79,7 @@
   }
 
 protected:
-  boost::asio::io_service io;
-  util::DummyClientFace face{io};
+  util::DummyClientFace face{m_io};
   Name name{"/ndn/chunks/test"};
   uint64_t nDataSegments = 0;
   bool hasFailed = false;
diff --git a/tests/chunks/producer.t.cpp b/tests/chunks/producer.t.cpp
index 6b46320..6934fa4 100644
--- a/tests/chunks/producer.t.cpp
+++ b/tests/chunks/producer.t.cpp
@@ -26,7 +26,7 @@
 #include "tools/chunks/putchunks/producer.hpp"
 
 #include "tests/test-common.hpp"
-#include "tests/identity-management-fixture.hpp"
+#include "tests/key-chain-fixture.hpp"
 
 #include <ndn-cxx/metadata-object.hpp>
 #include <ndn-cxx/security/pib/identity.hpp>
@@ -42,7 +42,7 @@
 
 using namespace ndn::tests;
 
-class ProducerFixture : public IdentityManagementFixture
+class ProducerFixture : public KeyChainFixture
 {
 protected:
   ProducerFixture()
diff --git a/tests/clock-fixture.cpp b/tests/clock-fixture.cpp
new file mode 100644
index 0000000..564f128
--- /dev/null
+++ b/tests/clock-fixture.cpp
@@ -0,0 +1,60 @@
+/* -*- 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 ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tests/clock-fixture.hpp"
+
+namespace ndn {
+namespace tests {
+
+ClockFixture::ClockFixture()
+  : m_steadyClock(make_shared<time::UnitTestSteadyClock>())
+  , m_systemClock(make_shared<time::UnitTestSystemClock>())
+{
+  time::setCustomClocks(m_steadyClock, m_systemClock);
+}
+
+ClockFixture::~ClockFixture()
+{
+  time::setCustomClocks(nullptr, nullptr);
+}
+
+void
+ClockFixture::advanceClocks(time::nanoseconds tick, time::nanoseconds total)
+{
+  BOOST_ASSERT(tick > time::nanoseconds::zero());
+  BOOST_ASSERT(total >= time::nanoseconds::zero());
+
+  while (total > time::nanoseconds::zero()) {
+    auto t = std::min(tick, total);
+    m_steadyClock->advance(t);
+    m_systemClock->advance(t);
+    total -= t;
+
+    afterTick();
+  }
+}
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/clock-fixture.hpp b/tests/clock-fixture.hpp
new file mode 100644
index 0000000..f05babe
--- /dev/null
+++ b/tests/clock-fixture.hpp
@@ -0,0 +1,89 @@
+/* -*- 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 ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDN_TOOLS_TESTS_CLOCK_FIXTURE_HPP
+#define NDN_TOOLS_TESTS_CLOCK_FIXTURE_HPP
+
+#include <ndn-cxx/util/time-unit-test-clock.hpp>
+
+namespace ndn {
+namespace tests {
+
+/** \brief A test fixture that overrides steady clock and system clock.
+ */
+class ClockFixture
+{
+public:
+  virtual
+  ~ClockFixture();
+
+  /** \brief Advance steady and system clocks.
+   *
+   *  Clocks are advanced in increments of \p tick for \p nTicks ticks.
+   *  afterTick() is called after each tick.
+   *
+   *  Exceptions thrown during I/O events are propagated to the caller.
+   *  Clock advancement will stop in the event of an exception.
+   */
+  void
+  advanceClocks(time::nanoseconds tick, size_t nTicks = 1)
+  {
+    advanceClocks(tick, tick * nTicks);
+  }
+
+  /** \brief Advance steady and system clocks.
+   *
+   *  Clocks are advanced in increments of \p tick for \p total time.
+   *  The last increment might be shorter than \p tick.
+   *  afterTick() is called after each tick.
+   *
+   *  Exceptions thrown during I/O events are propagated to the caller.
+   *  Clock advancement will stop in the event of an exception.
+   */
+  void
+  advanceClocks(time::nanoseconds tick, time::nanoseconds total);
+
+protected:
+  ClockFixture();
+
+private:
+  /** \brief Called by advanceClocks() after each clock advancement (tick).
+   *
+   *  The base class implementation is a no-op.
+   */
+  virtual void
+  afterTick()
+  {
+  }
+
+protected:
+  shared_ptr<time::UnitTestSteadyClock> m_steadyClock;
+  shared_ptr<time::UnitTestSystemClock> m_systemClock;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TOOLS_TESTS_CLOCK_FIXTURE_HPP
diff --git a/tests/dissect-wireshark/bug3603.pcap b/tests/dissect-wireshark/bug3603.pcap
old mode 100755
new mode 100644
Binary files differ
diff --git a/tests/dump/ndndump.t.cpp b/tests/dump/ndndump.t.cpp
index 44e2998..f266d92 100644
--- a/tests/dump/ndndump.t.cpp
+++ b/tests/dump/ndndump.t.cpp
@@ -20,7 +20,6 @@
 
 #include "tools/dump/ndndump.hpp"
 
-#include "tests/identity-management-fixture.hpp"
 #include "tests/test-common.hpp"
 
 #include <net/ethernet.h>
@@ -66,7 +65,7 @@
   std::streambuf* originalBuffer;
 };
 
-class NdnDumpFixture : public IdentityManagementFixture
+class NdnDumpFixture
 {
 protected:
   NdnDumpFixture()
diff --git a/tests/global-configuration.cpp b/tests/global-configuration.cpp
index d4ee782..f0c136a 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-2018,  Regents of the University of California.
+ * Copyright (c) 2014-2020,  Regents of the University of California.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -17,14 +17,15 @@
  * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "boost-test.hpp"
+#include "core/common.hpp"
+
+#include "tests/boost-test.hpp"
 
 #include <boost/filesystem.hpp>
 #include <fstream>
 #include <stdlib.h>
 
 namespace ndn {
-namespace tools {
 namespace tests {
 
 class GlobalConfiguration
@@ -36,19 +37,22 @@
     if (envHome)
       m_home = envHome;
 
-    boost::filesystem::path dir{TMP_TESTS_PATH};
-    dir /= "test-home";
-    ::setenv("HOME", dir.c_str(), 1);
+    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"));
 
-    boost::filesystem::create_directories(dir);
-    std::ofstream clientConf((dir / ".ndn" / "client.conf").c_str());
+    boost::filesystem::create_directories(testHome);
+
+    std::ofstream clientConf((testHome / ".ndn" / "client.conf").c_str());
     clientConf << "pib=pib-sqlite3" << std::endl
                << "tpm=tpm-file" << std::endl;
   }
 
-  ~GlobalConfiguration()
+  ~GlobalConfiguration() noexcept
   {
-    if (!m_home.empty())
+    if (m_home.empty())
+      ::unsetenv("HOME");
+    else
       ::setenv("HOME", m_home.data(), 1);
   }
 
@@ -65,5 +69,4 @@
 #endif
 
 } // namespace tests
-} // namespace tools
 } // namespace ndn
diff --git a/tests/identity-management-fixture.cpp b/tests/identity-management-fixture.cpp
deleted file mode 100644
index b4097d2..0000000
--- a/tests/identity-management-fixture.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2014-2020,  Regents of the University of California.
- *
- * This file is part of ndn-tools (Named Data Networking Essential Tools).
- * See AUTHORS.md for complete list of ndn-tools authors and contributors.
- *
- * ndn-tools 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.
- *
- * ndn-tools 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
- * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "identity-management-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/util/io.hpp>
-
-#include <boost/filesystem.hpp>
-
-namespace ndn {
-namespace tests {
-
-IdentityManagementFixture::IdentityManagementFixture()
-  : m_keyChain("pib-memory:", "tpm-memory:")
-{
-}
-
-IdentityManagementFixture::~IdentityManagementFixture()
-{
-  boost::system::error_code ec;
-  for (const auto& certFile : m_certFiles) {
-    boost::filesystem::remove(certFile, ec);
-  }
-}
-
-bool
-IdentityManagementFixture::addIdentity(const Name& identity, const KeyParams& params)
-{
-  try {
-    m_keyChain.createIdentity(identity, params);
-    return true;
-  }
-  catch (const std::runtime_error&) {
-    return false;
-  }
-}
-
-bool
-IdentityManagementFixture::saveIdentityCertificate(const Name& identity, const std::string& filename, bool wantAdd)
-{
-  security::Certificate cert;
-  try {
-    cert = m_keyChain.getPib().getIdentity(identity).getDefaultKey().getDefaultCertificate();
-  }
-  catch (const security::Pib::Error&) {
-    if (wantAdd && this->addIdentity(identity)) {
-      return this->saveIdentityCertificate(identity, filename, false);
-    }
-    return false;
-  }
-
-  m_certFiles.push_back(filename);
-  try {
-    io::save(cert, filename);
-    return true;
-  }
-  catch (const io::Error&) {
-    return false;
-  }
-}
-
-} // namespace tests
-} // namespace ndn
diff --git a/tests/identity-management-fixture.hpp b/tests/identity-management-fixture.hpp
deleted file mode 100644
index a0fac2e..0000000
--- a/tests/identity-management-fixture.hpp
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/*
- * Copyright (c) 2014-2018,  Regents of the University of California.
- *
- * This file is part of ndn-tools (Named Data Networking Essential Tools).
- * See AUTHORS.md for complete list of ndn-tools authors and contributors.
- *
- * ndn-tools 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.
- *
- * ndn-tools 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
- * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef NDN_TOOLS_TESTS_IDENTITY_MANAGEMENT_FIXTURE_HPP
-#define NDN_TOOLS_TESTS_IDENTITY_MANAGEMENT_FIXTURE_HPP
-
-#include "tests/test-common.hpp"
-
-namespace ndn {
-namespace tests {
-
-/** \brief a fixture providing an in-memory KeyChain
- */
-class IdentityManagementFixture
-{
-public:
-  IdentityManagementFixture();
-
-  /** \brief deletes saved certificate files
-   */
-  ~IdentityManagementFixture();
-
-  /** \brief add identity
-   *  \return whether successful
-   */
-  bool
-  addIdentity(const Name& identity, const KeyParams& params = KeyChain::getDefaultKeyParams());
-
-  /** \brief save identity certificate to a file
-   *  \param identity identity name
-   *  \param filename file name, should be writable
-   *  \param wantAdd if true, add new identity when necessary
-   *  \return whether successful
-   */
-  bool
-  saveIdentityCertificate(const Name& identity, const std::string& filename, bool wantAdd = false);
-
-protected:
-  KeyChain m_keyChain;
-
-private:
-  std::vector<std::string> m_certFiles;
-};
-
-/** \brief convenience base class for inheriting from both UnitTestTimeFixture
- *         and IdentityManagementFixture
- */
-class IdentityManagementTimeFixture : public UnitTestTimeFixture
-                                    , public IdentityManagementFixture
-{
-};
-
-} // namespace tests
-} // namespace ndn
-
-#endif // NDN_TOOLS_TESTS_IDENTITY_MANAGEMENT_FIXTURE_HPP
diff --git a/tests/io-fixture.hpp b/tests/io-fixture.hpp
new file mode 100644
index 0000000..881065c
--- /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 ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDN_TOOLS_TESTS_IO_FIXTURE_HPP
+#define NDN_TOOLS_TESTS_IO_FIXTURE_HPP
+
+#include "tests/clock-fixture.hpp"
+
+#include <boost/asio/io_service.hpp>
+
+namespace ndn {
+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 ndn
+
+#endif // NDN_TOOLS_TESTS_IO_FIXTURE_HPP
diff --git a/tests/key-chain-fixture.cpp b/tests/key-chain-fixture.cpp
new file mode 100644
index 0000000..b4911df
--- /dev/null
+++ b/tests/key-chain-fixture.cpp
@@ -0,0 +1,117 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2020,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tests/key-chain-fixture.hpp"
+
+#include <ndn-cxx/util/io.hpp>
+
+#include <boost/filesystem.hpp>
+
+namespace ndn {
+namespace tests {
+
+using namespace ndn::security;
+
+KeyChainFixture::KeyChainFixture()
+  : m_keyChain("pib-memory:", "tpm-memory:")
+{
+}
+
+KeyChainFixture::~KeyChainFixture()
+{
+  boost::system::error_code ec;
+  for (const auto& certFile : m_certFiles) {
+    boost::filesystem::remove(certFile, ec); // ignore error
+  }
+}
+
+Certificate
+KeyChainFixture::makeCert(const Key& key, const std::string& issuer, const Key& signingKey)
+{
+  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::saveCert(const Data& cert, const std::string& filename)
+{
+  m_certFiles.push_back(filename);
+  try {
+    ndn::io::save(cert, filename);
+    return true;
+  }
+  catch (const ndn::io::Error&) {
+    return false;
+  }
+}
+
+bool
+KeyChainFixture::saveIdentityCert(const Identity& identity, const std::string& filename)
+{
+  Certificate cert;
+  try {
+    cert = identity.getDefaultKey().getDefaultCertificate();
+  }
+  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);
+    }
+  }
+
+  if (!id) {
+    return false;
+  }
+
+  return saveIdentityCert(id, filename);
+}
+
+} // namespace tests
+} // namespace ndn
diff --git a/tests/key-chain-fixture.hpp b/tests/key-chain-fixture.hpp
new file mode 100644
index 0000000..f86de19
--- /dev/null
+++ b/tests/key-chain-fixture.hpp
@@ -0,0 +1,93 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2020,  Regents of the University of California.
+ *
+ * This file is part of ndn-tools (Named Data Networking Essential Tools).
+ * See AUTHORS.md for complete list of ndn-tools authors and contributors.
+ *
+ * ndn-tools 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.
+ *
+ * ndn-tools 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
+ * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef NDN_TOOLS_TESTS_KEY_CHAIN_FIXTURE_HPP
+#define NDN_TOOLS_TESTS_KEY_CHAIN_FIXTURE_HPP
+
+#include <ndn-cxx/security/key-chain.hpp>
+#include <ndn-cxx/security/signing-helpers.hpp>
+
+namespace ndn {
+namespace tests {
+
+/**
+ * @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 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
+  saveCert(const Data& cert, const std::string& filename);
+
+  /**
+   * @brief Saves the default certificate of @p identity to a file
+   * @return true if successful, false otherwise
+   */
+  bool
+  saveIdentityCert(const Identity& identity, const std::string& filename);
+
+  /**
+   * @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
+   */
+  bool
+  saveIdentityCert(const Name& identityName, const std::string& filename,
+                   bool allowCreate = false);
+
+protected:
+  KeyChainFixture();
+
+  ~KeyChainFixture();
+
+protected:
+  ndn::KeyChain m_keyChain;
+
+private:
+  std::vector<std::string> m_certFiles;
+};
+
+} // namespace tests
+} // namespace ndn
+
+#endif // NDN_TOOLS_TESTS_KEY_CHAIN_FIXTURE_HPP
diff --git a/tests/peek/ndnpeek.t.cpp b/tests/peek/ndnpeek.t.cpp
index 0e0840a..9951e5c 100644
--- a/tests/peek/ndnpeek.t.cpp
+++ b/tests/peek/ndnpeek.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Arizona Board of Regents.
+ * Copyright (c) 2014-2020,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -20,6 +20,7 @@
 #include "tools/peek/ndnpeek/ndnpeek.hpp"
 
 #include "tests/test-common.hpp"
+#include "tests/io-fixture.hpp"
 
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
@@ -63,7 +64,7 @@
   return opt;
 }
 
-class NdnPeekFixture : public UnitTestTimeFixture
+class NdnPeekFixture : public IoFixture
 {
 protected:
   void
@@ -73,8 +74,7 @@
   }
 
 protected:
-  boost::asio::io_service io;
-  ndn::util::DummyClientFace face{io};
+  ndn::util::DummyClientFace face{m_io};
   output_test_stream output;
   unique_ptr<NdnPeek> peek;
 };
@@ -149,7 +149,7 @@
   {
     CoutRedirector redir(output);
     peek->start();
-    this->advanceClocks(io, 25_ms, 4);
+    this->advanceClocks(25_ms, 4);
     face.receive(*data);
   }
 
@@ -181,7 +181,7 @@
   {
     CoutRedirector redir(output);
     peek->start();
-    this->advanceClocks(io, 25_ms, 4);
+    this->advanceClocks(25_ms, 4);
     face.receive(*data);
   }
 
@@ -205,7 +205,7 @@
   {
     CoutRedirector redir(output);
     peek->start();
-    this->advanceClocks(io, 25_ms, 4);
+    this->advanceClocks(25_ms, 4);
     nack = makeNack(face.sentInterests.at(0), lp::NackReason::NO_ROUTE);
     face.receive(nack);
   }
@@ -224,7 +224,7 @@
   {
     CoutRedirector redir(output);
     peek->start();
-    this->advanceClocks(io, 25_ms, 4);
+    this->advanceClocks(25_ms, 4);
     nack = makeNack(face.sentInterests.at(0), lp::NackReason::NONE);
     face.receive(nack);
   }
@@ -241,7 +241,7 @@
   initialize(options);
 
   peek->start();
-  this->advanceClocks(io, 25_ms, 4);
+  this->advanceClocks(25_ms, 4);
 
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentInterests.back().getCanBePrefix(), false);
@@ -262,12 +262,12 @@
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
 
   peek->start();
-  this->advanceClocks(io, 100_ms, 9);
+  this->advanceClocks(100_ms, 9);
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.getNPendingInterests(), 1);
   BOOST_CHECK(peek->getResult() == NdnPeek::Result::UNKNOWN);
 
-  this->advanceClocks(io, 100_ms, 2);
+  this->advanceClocks(100_ms, 2);
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
   BOOST_CHECK(peek->getResult() == NdnPeek::Result::TIMEOUT);
@@ -283,7 +283,7 @@
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
 
   peek->start();
-  this->advanceClocks(io, 25_ms, 6);
+  this->advanceClocks(25_ms, 6);
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
@@ -300,7 +300,7 @@
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
 
   peek->start();
-  this->advanceClocks(io, 25_ms, 4);
+  this->advanceClocks(25_ms, 4);
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.getNPendingInterests(), 0);
@@ -314,7 +314,7 @@
   initialize(options);
 
   peek->start();
-  BOOST_CHECK_THROW(this->advanceClocks(io, 1_ms, 10), Face::OversizedPacketError);
+  BOOST_CHECK_THROW(this->advanceClocks(1_ms, 10), Face::OversizedPacketError);
 
   BOOST_CHECK_EQUAL(face.sentInterests.size(), 0);
 }
diff --git a/tests/peek/ndnpoke.t.cpp b/tests/peek/ndnpoke.t.cpp
index 09a562c..617b516 100644
--- a/tests/peek/ndnpoke.t.cpp
+++ b/tests/peek/ndnpoke.t.cpp
@@ -26,7 +26,8 @@
 #include "tools/peek/ndnpoke/ndnpoke.hpp"
 
 #include "tests/test-common.hpp"
-#include "tests/identity-management-fixture.hpp"
+#include "tests/io-fixture.hpp"
+#include "tests/key-chain-fixture.hpp"
 
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
@@ -37,7 +38,7 @@
 using namespace ndn::tests;
 
 template<bool WANT_PREFIX_REG_REPLY = true>
-class NdnPokeFixture : public IdentityManagementTimeFixture
+class NdnPokeFixture : public IoFixture, public KeyChainFixture
 {
 protected:
   NdnPokeFixture()
@@ -60,8 +61,7 @@
   }
 
 protected:
-  boost::asio::io_service io;
-  ndn::util::DummyClientFace face{io, m_keyChain, {true, WANT_PREFIX_REG_REPLY}};
+  ndn::util::DummyClientFace face{m_io, m_keyChain, {true, WANT_PREFIX_REG_REPLY}};
   std::stringstream payload{"Hello, world!\n"};
   unique_ptr<NdnPoke> poke;
 };
@@ -74,14 +74,14 @@
   initialize();
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   // Check for prefix registration
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentInterests.front().getName().getPrefix(4), "/localhost/nfd/rib/register");
 
   face.receive(*makeInterest("/poke/test"));
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::DATA_SENT);
   BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
@@ -102,16 +102,16 @@
   initialize();
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   face.receive(*makeInterest("/poke/test/foo"));
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::UNKNOWN);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
 
   face.receive(*makeInterest("/poke/test"));
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::DATA_SENT);
   BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
@@ -125,10 +125,10 @@
   initialize(options);
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   face.receive(*makeInterest("/poke/test"));
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::DATA_SENT);
   BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
@@ -146,10 +146,10 @@
   initialize(options);
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   face.receive(*makeInterest(options.name));
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::DATA_SENT);
   BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
@@ -167,10 +167,10 @@
   initialize(options);
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   face.receive(*makeInterest("/poke/test"));
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::DATA_SENT);
   BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
@@ -187,7 +187,7 @@
   initialize(options);
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::DATA_SENT);
   BOOST_REQUIRE_EQUAL(face.sentData.size(), 1);
@@ -204,13 +204,13 @@
   initialize(options);
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   // Check for prefix registration
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentInterests.front().getName().getPrefix(4), "/localhost/nfd/rib/register");
 
-  this->advanceClocks(io, 1_s, 4);
+  this->advanceClocks(1_s, 4);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::TIMEOUT);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
@@ -225,13 +225,13 @@
   initialize();
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   // Check for prefix registration
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 1);
   BOOST_CHECK_EQUAL(face.sentInterests.front().getName().getPrefix(4), "/localhost/nfd/rib/register");
 
-  this->advanceClocks(io, 1_s, 10);
+  this->advanceClocks(1_s, 10);
 
   BOOST_CHECK(poke->getResult() == NdnPoke::Result::PREFIX_REG_FAIL);
   BOOST_CHECK_EQUAL(face.sentData.size(), 0);
@@ -243,7 +243,7 @@
   initialize();
 
   poke->start();
-  this->advanceClocks(io, 1_ms, 10);
+  this->advanceClocks(1_ms, 10);
 
   face.receive(*makeInterest("/poke/test"));
   BOOST_CHECK_THROW(face.processEvents(), Face::OversizedPacketError);
diff --git a/tests/ping/client/ping.t.cpp b/tests/ping/client/ping.t.cpp
index 67d7ddc..cef62d6 100644
--- a/tests/ping/client/ping.t.cpp
+++ b/tests/ping/client/ping.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2019,  Arizona Board of Regents.
+ * Copyright (c) 2014-2020,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -20,6 +20,8 @@
 #include "tools/ping/client/ping.hpp"
 
 #include "tests/test-common.hpp"
+#include "tests/io-fixture.hpp"
+
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
 namespace ndn {
@@ -34,20 +36,18 @@
 
 using ping::client::Ping;
 
-BOOST_FIXTURE_TEST_CASE(Basic, UnitTestTimeFixture)
+BOOST_FIXTURE_TEST_CASE(Basic, IoFixture)
 {
+  util::DummyClientFace face(m_io, {true, true});
   Options pingOptions;
-  pingOptions.prefix = "ndn:/test-prefix";
+  pingOptions.prefix = "/test-prefix";
   pingOptions.shouldAllowStaleData = false;
   pingOptions.shouldGenerateRandomSeq = false;
   pingOptions.shouldPrintTimestamp = false;
   pingOptions.nPings = 4;
-  pingOptions.interval = time::milliseconds(100);
-  pingOptions.timeout = time::milliseconds(2000);
+  pingOptions.interval = 100_ms;
+  pingOptions.timeout = 2_s;
   pingOptions.startSeq = 1000;
-
-  boost::asio::io_service io;
-  util::DummyClientFace face(io, {true, true});
   Ping ping(face, pingOptions);
 
   int nFinishSignals = 0;
@@ -73,29 +73,24 @@
 
   ping.start();
 
-  this->advanceClocks(io, time::milliseconds(1), 500);
+  this->advanceClocks(1_ms, 500);
   BOOST_REQUIRE_EQUAL(face.sentInterests.size(), 4);
 
   auto data = makeData("/test-prefix/ping/1000");
   data->setFreshnessPeriod(1_s);
   face.receive(*data);
 
-  lp::Nack nack(face.sentInterests[1]);
-  nack.setReason(lp::NackReason::DUPLICATE);
-  face.receive(nack);
+  face.receive(makeNack(face.sentInterests[1], lp::NackReason::DUPLICATE));
 
   data = makeData("/test-prefix/ping/1002");
   data->setFreshnessPeriod(1_s);
   face.receive(*data);
 
-  this->advanceClocks(io, time::milliseconds(100), 20);
+  this->advanceClocks(100_ms, 20);
 
-  // ndn:/test-prefix/ping/1003 is unanswered and will timeout
+  // /test-prefix/ping/1003 is unanswered and will timeout
 
   BOOST_CHECK_EQUAL(nFinishSignals, 1);
-
-  face.shutdown();
-  io.stop();
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestPing
diff --git a/tests/ping/integrated.t.cpp b/tests/ping/integrated.t.cpp
index 84c13e5..eafc52b 100644
--- a/tests/ping/integrated.t.cpp
+++ b/tests/ping/integrated.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2015-2018,  Arizona Board of Regents.
+ * Copyright (c) 2015-2020,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -17,10 +17,13 @@
  * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "tools/ping/server/ping-server.hpp"
 #include "tools/ping/client/ping.hpp"
+#include "tools/ping/server/ping-server.hpp"
 
-#include "tests/identity-management-fixture.hpp"
+#include "tests/test-common.hpp"
+#include "tests/io-fixture.hpp"
+#include "tests/key-chain-fixture.hpp"
+
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
 namespace ndn {
@@ -29,25 +32,25 @@
 
 using namespace ndn::tests;
 
-class PingIntegratedFixture : public IdentityManagementTimeFixture
+class PingIntegratedFixture : public IoFixture, public KeyChainFixture
 {
 public:
   PingIntegratedFixture()
-    : serverFace(io, m_keyChain, {false, true})
-    , clientFace(io, m_keyChain, {false, true})
+    : serverFace(m_io, m_keyChain, {false, true})
+    , clientFace(m_io, m_keyChain, {false, true})
     , wantLoss(false)
   {
     serverFace.onSendInterest.connect([this] (const Interest& interest) {
-      io.post([=] { if (!wantLoss) { clientFace.receive(interest); } });
+      m_io.post([=] { if (!wantLoss) { clientFace.receive(interest); } });
     });
     clientFace.onSendInterest.connect([this] (const Interest& interest) {
-      io.post([=] { if (!wantLoss) { serverFace.receive(interest); } });
+      m_io.post([=] { if (!wantLoss) { serverFace.receive(interest); } });
     });
     serverFace.onSendData.connect([this] (const Data& data) {
-      io.post([=] { if (!wantLoss) { clientFace.receive(data); } });
+      m_io.post([=] { if (!wantLoss) { clientFace.receive(data); } });
     });
     clientFace.onSendData.connect([this] (const Data& data) {
-      io.post([=] { if (!wantLoss) { serverFace.receive(data); } });
+      m_io.post([=] { if (!wantLoss) { serverFace.receive(data); } });
     });
   }
 
@@ -55,11 +58,10 @@
   {
     serverFace.shutdown();
     clientFace.shutdown();
-    io.stop();
+    m_io.stop();
   }
 
 public:
-  boost::asio::io_service io;
   util::DummyClientFace serverFace;
   util::DummyClientFace clientFace;
   std::unique_ptr<server::PingServer> server;
@@ -92,11 +94,11 @@
   clientOpts.timeout = 2_s;
   clientOpts.startSeq = 1000;
   client = make_unique<client::Ping>(clientFace, clientOpts);
-  client->afterFinish.connect(bind(&PingIntegratedFixture::onFinish, this));
+  client->afterFinish.connect([this] { onFinish(); });
   client->start();
 
-  advanceClocks(io, 1_ms, 400);
-  io.run();
+  advanceClocks(1_ms, 400);
+  m_io.run();
 
   BOOST_CHECK_EQUAL(4, server->getNPings());
 }
@@ -125,11 +127,11 @@
   clientOpts.timeout = 500_ms;
   clientOpts.startSeq = 1000;
   client = make_unique<client::Ping>(clientFace, clientOpts);
-  client->afterFinish.connect(bind(&PingIntegratedFixture::onFinish, this));
+  client->afterFinish.connect([this] { onFinish(); });
   client->start();
 
-  advanceClocks(io, 1_ms, 1000);
-  io.run();
+  advanceClocks(1_ms, 1000);
+  m_io.run();
 
   BOOST_CHECK_EQUAL(0, server->getNPings());
 }
diff --git a/tests/ping/server/ping-server.t.cpp b/tests/ping/server/ping-server.t.cpp
index 1fe5b35..f7ff66f 100644
--- a/tests/ping/server/ping-server.t.cpp
+++ b/tests/ping/server/ping-server.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2018,  Arizona Board of Regents.
+ * Copyright (c) 2014-2020,  Arizona Board of Regents.
  *
  * This file is part of ndn-tools (Named Data Networking Essential Tools).
  * See AUTHORS.md for complete list of ndn-tools authors and contributors.
@@ -20,7 +20,8 @@
 #include "tools/ping/server/ping-server.hpp"
 
 #include "tests/test-common.hpp"
-#include "../../identity-management-fixture.hpp"
+#include "tests/io-fixture.hpp"
+#include "tests/key-chain-fixture.hpp"
 
 #include <ndn-cxx/util/dummy-client-face.hpp>
 
@@ -34,11 +35,11 @@
 BOOST_AUTO_TEST_SUITE(Ping)
 BOOST_AUTO_TEST_SUITE(TestPingServer)
 
-class CreatePingServerFixture : public IdentityManagementTimeFixture
+class CreatePingServerFixture : public IoFixture, public KeyChainFixture
 {
 protected:
   CreatePingServerFixture()
-    : face(io, m_keyChain, {false, true})
+    : face(m_io, m_keyChain, {false, true})
     , pingOptions(makeOptions())
     , pingServer(face, m_keyChain, pingOptions)
   {
@@ -72,7 +73,6 @@
   }
 
 protected:
-  boost::asio::io_service io;
   util::DummyClientFace face;
   Options pingOptions;
   PingServer pingServer;
@@ -83,12 +83,12 @@
   BOOST_REQUIRE_EQUAL(0, pingServer.getNPings());
   pingServer.start();
 
-  advanceClocks(io, 1_ms, 200);
+  advanceClocks(1_ms, 200);
 
   face.receive(makePingInterest(1000));
   face.receive(makePingInterest(1001));
 
-  io.run();
+  m_io.run();
 
   BOOST_CHECK_EQUAL(2, pingServer.getNPings());
 }
diff --git a/tests/test-case.t.cpp.sample b/tests/test-case.t.cpp.sample
index 44223a6..8f6584b 100644
--- a/tests/test-case.t.cpp.sample
+++ b/tests/test-case.t.cpp.sample
@@ -23,6 +23,8 @@
 //#include "unit-under-test.hpp"
 
 #include "tests/test-common.hpp"
+#include "tests/io-fixture.hpp"           // optional, for IoFixture
+//#include "tests/key-chain-fixture.hpp"  // optional, for KeyChainFixture
 
 namespace ndn {
 namespace tool_name {
@@ -46,12 +48,11 @@
   BOOST_CHECK_EQUAL(i, 1);
 }
 
-// Use UnitTestTimeFixture to mock clocks.
-BOOST_FIXTURE_TEST_CASE(Test2, UnitTestTimeFixture)
+// Use ClockFixture or IoFixture to mock clocks.
+BOOST_FIXTURE_TEST_CASE(Test2, IoFixture)
 {
   // advanceClocks() increments mock clocks.
-  boost::asio::io_service io;
-  this->advanceClocks(io, time::milliseconds(500));
+  advanceClocks(500_ms);
 }
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/tests/test-common.cpp b/tests/test-common.cpp
index e1b2431..98fa188 100644
--- a/tests/test-common.cpp
+++ b/tests/test-common.cpp
@@ -23,64 +23,20 @@
  * ndn-tools, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "test-common.hpp"
-
-#include <ndn-cxx/security/signature-sha256-with-rsa.hpp>
+#include "tests/test-common.hpp"
 
 namespace ndn {
 namespace tests {
 
-UnitTestTimeFixture::UnitTestTimeFixture()
-  : steadyClock(make_shared<time::UnitTestSteadyClock>())
-  , systemClock(make_shared<time::UnitTestSystemClock>())
-{
-  time::setCustomClocks(steadyClock, systemClock);
-}
-
-UnitTestTimeFixture::~UnitTestTimeFixture()
-{
-  time::setCustomClocks(nullptr, nullptr);
-}
-
-void
-UnitTestTimeFixture::advanceClocks(boost::asio::io_service& io,
-                                   time::nanoseconds tick, size_t nTicks)
-{
-  this->advanceClocks(io, tick, tick * nTicks);
-}
-
-void
-UnitTestTimeFixture::advanceClocks(boost::asio::io_service& io,
-                                   time::nanoseconds tick, time::nanoseconds total)
-{
-  BOOST_ASSERT(tick > time::nanoseconds::zero());
-  BOOST_ASSERT(total >= time::nanoseconds::zero());
-
-  time::nanoseconds remaining = total;
-  while (remaining > time::nanoseconds::zero()) {
-    if (remaining >= tick) {
-      steadyClock->advance(tick);
-      systemClock->advance(tick);
-      remaining -= tick;
-    }
-    else {
-      steadyClock->advance(remaining);
-      systemClock->advance(remaining);
-      remaining = time::nanoseconds::zero();
-    }
-
-    if (io.stopped())
-      io.reset();
-    io.poll();
-  }
-}
-
 shared_ptr<Interest>
-makeInterest(const Name& name, bool canBePrefix, time::milliseconds lifetime,
+makeInterest(const Name& name, bool canBePrefix, optional<time::milliseconds> lifetime,
              optional<Interest::Nonce> nonce)
 {
-  auto interest = make_shared<Interest>(name, lifetime);
+  auto interest = std::make_shared<Interest>(name);
   interest->setCanBePrefix(canBePrefix);
+  if (lifetime) {
+    interest->setInterestLifetime(*lifetime);
+  }
   interest->setNonce(nonce);
   return interest;
 }
@@ -88,16 +44,15 @@
 shared_ptr<Data>
 makeData(const Name& name)
 {
-  auto data = make_shared<Data>(name);
+  auto data = std::make_shared<Data>(name);
   return signData(data);
 }
 
 Data&
 signData(Data& data)
 {
-  ndn::SignatureSha256WithRsa fakeSignature;
-  fakeSignature.setValue(ndn::encoding::makeEmptyBlock(tlv::SignatureValue));
-  data.setSignature(fakeSignature);
+  data.setSignatureInfo(SignatureInfo(tlv::NullSignature));
+  data.setSignatureValue(std::make_shared<Buffer>());
   data.wireEncode();
   return data;
 }
diff --git a/tests/test-common.hpp b/tests/test-common.hpp
index 7cf5671..5fa2a8d 100644
--- a/tests/test-common.hpp
+++ b/tests/test-common.hpp
@@ -26,73 +26,36 @@
 #ifndef NDN_TOOLS_TESTS_TEST_COMMON_HPP
 #define NDN_TOOLS_TESTS_TEST_COMMON_HPP
 
-#include "boost-test.hpp"
 #include "core/common.hpp"
-
-#include <ndn-cxx/util/time-unit-test-clock.hpp>
+#include "tests/boost-test.hpp"
 
 namespace ndn {
 namespace tests {
 
-/** \brief a test fixture that overrides steady clock and system clock
- */
-class UnitTestTimeFixture
-{
-protected:
-  UnitTestTimeFixture();
-
-  ~UnitTestTimeFixture();
-
-  /** \brief advance steady and system clocks
-   *
-   *  Clocks are advanced in increments of \p tick for \p nTicks ticks.
-   *  After each tick, the supplied io_service is polled to process pending I/O events.
-   *
-   *  Exceptions thrown during I/O events are propagated to the caller.
-   *  Clock advancing would stop in case of an exception.
-   */
-  void
-  advanceClocks(boost::asio::io_service& io,
-                time::nanoseconds tick, size_t nTicks = 1);
-
-  /** \brief advance steady and system clocks
-   *
-   *  Clocks are advanced in increments of \p tick for \p total time.
-   *  The last increment might be shorter than \p tick.
-   *  After each tick, the supplied io_service is polled to process pending I/O events.
-   *
-   *  Exceptions thrown during I/O events are propagated to the caller.
-   *  Clock advancing would stop in case of an exception.
-   */
-  void
-  advanceClocks(boost::asio::io_service& io,
-                time::nanoseconds tick, time::nanoseconds total);
-
-protected:
-  shared_ptr<time::UnitTestSteadyClock> steadyClock;
-  shared_ptr<time::UnitTestSystemClock> systemClock;
-};
-
-/** \brief create an Interest
+/**
+ * \brief Create an Interest
  */
 shared_ptr<Interest>
 makeInterest(const Name& name, bool canBePrefix = false,
-             time::milliseconds lifetime = DEFAULT_INTEREST_LIFETIME,
+             optional<time::milliseconds> lifetime = nullopt,
              optional<Interest::Nonce> nonce = nullopt);
 
-/** \brief create a Data with fake signature
- *  \note Data may be modified afterwards without losing the fake signature.
- *        If a real signature is desired, sign again with KeyChain.
+/**
+ * \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`.
  */
 shared_ptr<Data>
 makeData(const Name& name);
 
-/** \brief add a fake signature to Data
+/**
+ * \brief Add a null signature to \p data
  */
 Data&
 signData(Data& data);
 
-/** \brief add a fake signature to Data
+/**
+ * \brief Add a null signature to \p data
  */
 inline shared_ptr<Data>
 signData(shared_ptr<Data> data)
@@ -101,11 +64,27 @@
   return data;
 }
 
-/** \brief create a Nack
+/**
+ * \brief Create a Nack
  */
 lp::Nack
 makeNack(Interest interest, lp::NackReason reason);
 
+/**
+ * \brief Replace a name component in a packet
+ * \param[inout] pkt the packet
+ * \param index the index of the name component to replace
+ * \param args arguments to name::Component constructor
+ */
+template<typename Packet, typename ...Args>
+void
+setNameComponent(Packet& pkt, ssize_t index, Args&& ...args)
+{
+  Name name = pkt.getName();
+  name.set(index, name::Component(std::forward<Args>(args)...));
+  pkt.setName(name);
+}
+
 } // namespace tests
 } // namespace ndn
 
diff --git a/tests/wscript b/tests/wscript
index 32c89f4..19bb8e7 100644
--- a/tests/wscript
+++ b/tests/wscript
@@ -5,11 +5,11 @@
     if not bld.env.WITH_TESTS:
         return
 
-    tmp_path = 'TMP_TESTS_PATH="%s"' % bld.bldnode.make_node('tmp-files')
+    tmpdir = 'UNIT_TESTS_TMPDIR="%s"' % bld.bldnode.make_node('tmp-files')
     bld.program(
         target='../unit-tests',
         name='unit-tests',
         source=bld.path.ant_glob(['*.cpp'] + ['%s/**/*.cpp' % tool for tool in bld.env.BUILD_TOOLS]),
         use=['core-objects'] + ['%s-objects' % tool for tool in bld.env.BUILD_TOOLS],
-        defines=[tmp_path],
+        defines=[tmpdir],
         install_path=None)