| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2013-2017 Regents of the University of California. |
| * |
| * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions). |
| * |
| * ndn-cxx library is free software: you can redistribute it and/or modify it under the |
| * terms of the GNU Lesser General Public License as published by the Free Software |
| * Foundation, either version 3 of the License, or (at your option) any later version. |
| * |
| * ndn-cxx library 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 Lesser General Public License for more details. |
| * |
| * You should have received copies of the GNU General Public License and GNU Lesser |
| * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see |
| * <http://www.gnu.org/licenses/>. |
| * |
| * See AUTHORS.md for complete list of ndn-cxx authors and contributors. |
| */ |
| |
| #define BOOST_TEST_MAIN 1 |
| #define BOOST_TEST_DYN_LINK 1 |
| #define BOOST_TEST_MODULE ndn-cxx Integrated Tests (Face) |
| |
| #include "face.hpp" |
| #include "util/scheduler.hpp" |
| |
| #include "identity-management-fixture.hpp" |
| #include "boost-test.hpp" |
| |
| #include <stdio.h> |
| #include <thread> |
| #include <mutex> |
| #include <condition_variable> |
| |
| namespace ndn { |
| namespace tests { |
| |
| struct PibDirWithDefaultTpm |
| { |
| const std::string PATH = "build/keys-with-default-tpm"; |
| }; |
| |
| class FacesFixture : public IdentityManagementFixture |
| { |
| public: |
| FacesFixture() |
| : nData(0) |
| , nNacks(0) |
| , nTimeouts(0) |
| , regPrefixId(0) |
| , nInInterests(0) |
| , nInInterests2(0) |
| , nRegFailures(0) |
| { |
| } |
| |
| void |
| onData() |
| { |
| ++nData; |
| } |
| |
| void |
| onNack() |
| { |
| ++nNacks; |
| } |
| |
| void |
| onTimeout() |
| { |
| ++nTimeouts; |
| } |
| |
| void |
| onInterest(Face& face, |
| const Name&, const Interest&) |
| { |
| ++nInInterests; |
| |
| face.unsetInterestFilter(regPrefixId); |
| } |
| |
| void |
| onInterest2(Face& face, |
| const Name&, const Interest&) |
| { |
| ++nInInterests2; |
| |
| face.unsetInterestFilter(regPrefixId2); |
| } |
| |
| void |
| onInterestRegex(Face& face, |
| const InterestFilter&, const Interest&) |
| { |
| ++nInInterests; |
| } |
| |
| void |
| onInterestRegexError(Face& face, |
| const Name&, const Interest&) |
| { |
| BOOST_FAIL("InterestFilter::Error should have been triggered"); |
| } |
| |
| void |
| onRegFailed() |
| { |
| ++nRegFailures; |
| } |
| |
| void |
| expressInterest(Face& face, const Name& name) |
| { |
| Interest i(name); |
| i.setInterestLifetime(time::milliseconds(50)); |
| face.expressInterest(i, |
| bind(&FacesFixture::onData, this), |
| bind(&FacesFixture::onNack, this), |
| bind(&FacesFixture::onTimeout, this)); |
| } |
| |
| void |
| terminate(Face& face) |
| { |
| face.getIoService().stop(); |
| } |
| |
| uint32_t nData; |
| uint32_t nNacks; |
| uint32_t nTimeouts; |
| |
| const RegisteredPrefixId* regPrefixId; |
| const RegisteredPrefixId* regPrefixId2; |
| uint32_t nInInterests; |
| uint32_t nInInterests2; |
| uint32_t nRegFailures; |
| }; |
| |
| BOOST_FIXTURE_TEST_SUITE(TestFaces, FacesFixture) |
| |
| BOOST_AUTO_TEST_CASE(Unix) |
| { |
| Face face; |
| |
| face.expressInterest(Interest("/", time::milliseconds(1000)), |
| bind(&FacesFixture::onData, this), |
| bind(&FacesFixture::onNack, this), |
| bind(&FacesFixture::onTimeout, this)); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nData, 1); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nTimeouts, 0); |
| |
| face.expressInterest(Interest("/localhost/non-existing/data/should/not/exist/anywhere", |
| time::milliseconds(50)), |
| bind(&FacesFixture::onData, this), |
| bind(&FacesFixture::onNack, this), |
| bind(&FacesFixture::onTimeout, this)); |
| |
| Name veryLongName; |
| for (size_t i = 0; i <= MAX_NDN_PACKET_SIZE / 10; i++) { |
| veryLongName.append("0123456789"); |
| } |
| |
| BOOST_CHECK_THROW(face.expressInterest(Interest(veryLongName), nullptr, nullptr, nullptr), |
| Face::Error); |
| |
| shared_ptr<Data> data = make_shared<Data>(veryLongName); |
| data->setContent(reinterpret_cast<const uint8_t*>("01234567890"), 10); |
| m_keyChain.sign(*data); |
| BOOST_CHECK_THROW(face.put(*data), Face::Error); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nData, 1); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nTimeouts, 1); |
| } |
| |
| BOOST_AUTO_TEST_CASE(Tcp) |
| { |
| Face face("localhost"); |
| |
| face.expressInterest(Interest("/", time::milliseconds(1000)), |
| bind(&FacesFixture::onData, this), |
| bind(&FacesFixture::onNack, this), |
| bind(&FacesFixture::onTimeout, this)); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nData, 1); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nTimeouts, 0); |
| |
| face.expressInterest(Interest("/localhost/non-existing/data/should/not/exist/anywhere", |
| time::milliseconds(50)), |
| bind(&FacesFixture::onData, this), |
| bind(&FacesFixture::onNack, this), |
| bind(&FacesFixture::onTimeout, this)); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nData, 1); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nTimeouts, 1); |
| } |
| |
| |
| BOOST_AUTO_TEST_CASE(SetFilter) |
| { |
| Face face; |
| Face face2(face.getIoService()); |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(4), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| regPrefixId = face.setInterestFilter("/Hello/World", |
| bind(&FacesFixture::onInterest, this, ref(face), _1, _2), |
| nullptr, |
| bind(&FacesFixture::onRegFailed, this)); |
| |
| scheduler.scheduleEvent(time::milliseconds(200), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/!"))); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nRegFailures, 0); |
| BOOST_CHECK_EQUAL(nInInterests, 1); |
| BOOST_CHECK_EQUAL(nTimeouts, 1); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nData, 0); |
| } |
| |
| BOOST_AUTO_TEST_CASE(SetTwoFilters) |
| { |
| Face face; |
| Face face2(face.getIoService()); |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(6), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| regPrefixId = face.setInterestFilter("/Hello/World", |
| bind(&FacesFixture::onInterest, this, ref(face), _1, _2), |
| nullptr, |
| bind(&FacesFixture::onRegFailed, this)); |
| |
| regPrefixId2 = face.setInterestFilter("/Los/Angeles/Lakers", |
| bind(&FacesFixture::onInterest2, this, ref(face), _1, _2), |
| nullptr, |
| bind(&FacesFixture::onRegFailed, this)); |
| |
| |
| scheduler.scheduleEvent(time::seconds(2), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/!"))); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nRegFailures, 0); |
| BOOST_CHECK_EQUAL(nInInterests, 1); |
| BOOST_CHECK_EQUAL(nInInterests2, 0); |
| BOOST_CHECK_EQUAL(nTimeouts, 1); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nData, 0); |
| } |
| |
| BOOST_AUTO_TEST_CASE(SetRegexFilterError) |
| { |
| Face face; |
| Face face2(face.getIoService()); |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(4), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| regPrefixId = face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"), |
| bind(&FacesFixture::onInterestRegexError, this, |
| ref(face), _1, _2), |
| nullptr, |
| bind(&FacesFixture::onRegFailed, this)); |
| |
| scheduler.scheduleEvent(time::milliseconds(300), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/XXX/b/c"))); // should match |
| |
| BOOST_REQUIRE_THROW(face.processEvents(), InterestFilter::Error); |
| } |
| |
| BOOST_AUTO_TEST_CASE(SetRegexFilter) |
| { |
| Face face; |
| Face face2(face.getIoService()); |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(4), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| regPrefixId = face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"), |
| bind(&FacesFixture::onInterestRegex, this, |
| ref(face), _1, _2), |
| nullptr, |
| bind(&FacesFixture::onRegFailed, this)); |
| |
| scheduler.scheduleEvent(time::milliseconds(200), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a"))); // shouldn't match |
| |
| scheduler.scheduleEvent(time::milliseconds(300), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b"))); // should match |
| |
| scheduler.scheduleEvent(time::milliseconds(400), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b/c"))); // should match |
| |
| scheduler.scheduleEvent(time::milliseconds(500), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b/d"))); // should not match |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nRegFailures, 0); |
| BOOST_CHECK_EQUAL(nInInterests, 2); |
| BOOST_CHECK_EQUAL(nTimeouts, 4); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nData, 0); |
| } |
| |
| class FacesFixture2 : public FacesFixture |
| { |
| public: |
| void |
| checkPrefix(bool shouldExist) |
| { |
| // Boost.Test fails if a child process exits with non-zero. |
| // http://stackoverflow.com/q/5325202 |
| std::string output = this->executeCommand("nfdc route list | grep /Hello/World || true"); |
| |
| if (shouldExist) { |
| BOOST_CHECK_NE(output.size(), 0); |
| } |
| else { |
| BOOST_CHECK_EQUAL(output.size(), 0); |
| } |
| } |
| |
| protected: |
| std::string |
| executeCommand(const std::string& cmd) |
| { |
| std::string output; |
| char buf[256]; |
| FILE* pipe = popen(cmd.c_str(), "r"); |
| BOOST_REQUIRE_MESSAGE(pipe != nullptr, "cannot execute '" << cmd << "'"); |
| while (fgets(buf, sizeof(buf), pipe) != nullptr) { |
| output += buf; |
| } |
| pclose(pipe); |
| return output; |
| } |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(RegisterUnregisterPrefix, FacesFixture2) |
| { |
| Face face; |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(4), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| regPrefixId = face.setInterestFilter(InterestFilter("/Hello/World"), |
| bind(&FacesFixture::onInterest, this, |
| ref(face), _1, _2), |
| nullptr, |
| bind(&FacesFixture::onRegFailed, this)); |
| |
| scheduler.scheduleEvent(time::milliseconds(500), |
| bind(&FacesFixture2::checkPrefix, this, true)); |
| |
| scheduler.scheduleEvent(time::seconds(1), |
| bind(static_cast<void(Face::*)(const RegisteredPrefixId*)>(&Face::unsetInterestFilter), |
| &face, |
| regPrefixId)); // shouldn't match |
| |
| scheduler.scheduleEvent(time::milliseconds(2000), |
| bind(&FacesFixture2::checkPrefix, this, false)); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| } |
| |
| |
| class FacesFixture3 : public FacesFixture2 |
| { |
| public: |
| FacesFixture3() |
| : nRegSuccesses(0) |
| , nUnregSuccesses(0) |
| , nUnregFailures(0) |
| { |
| } |
| |
| void |
| onRegSucceeded() |
| { |
| ++nRegSuccesses; |
| } |
| |
| void |
| onUnregSucceeded() |
| { |
| ++nUnregSuccesses; |
| } |
| |
| void |
| onUnregFailed() |
| { |
| ++nUnregFailures; |
| } |
| |
| public: |
| uint64_t nRegSuccesses; |
| uint64_t nUnregSuccesses; |
| uint64_t nUnregFailures; |
| }; |
| |
| BOOST_FIXTURE_TEST_CASE(RegisterPrefix, FacesFixture3) |
| { |
| Face face; |
| Face face2(face.getIoService()); |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(6), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| scheduler.scheduleEvent(time::seconds(2), |
| bind(&FacesFixture2::checkPrefix, this, true)); |
| |
| regPrefixId = face.registerPrefix("/Hello/World", |
| bind(&FacesFixture3::onRegSucceeded, this), |
| bind(&FacesFixture3::onRegFailed, this)); |
| |
| scheduler.scheduleEvent(time::seconds(4), |
| bind(&Face::unregisterPrefix, &face, |
| regPrefixId, |
| static_cast<UnregisterPrefixSuccessCallback>(bind(&FacesFixture3::onUnregSucceeded, this)), |
| static_cast<UnregisterPrefixFailureCallback>(bind(&FacesFixture3::onUnregFailed, this)))); |
| |
| scheduler.scheduleEvent(time::milliseconds(6500), |
| bind(&FacesFixture2::checkPrefix, this, false)); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nRegFailures, 0); |
| BOOST_CHECK_EQUAL(nRegSuccesses, 1); |
| |
| BOOST_CHECK_EQUAL(nUnregFailures, 0); |
| BOOST_CHECK_EQUAL(nUnregSuccesses, 1); |
| } |
| |
| BOOST_AUTO_TEST_CASE(SetRegexFilterButNoRegister) |
| { |
| Face face; |
| Face face2(face.getIoService()); |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(2), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"), |
| bind(&FacesFixture::onInterestRegex, this, |
| ref(face), _1, _2)); |
| |
| // prefix is not registered, and also does not match regex |
| scheduler.scheduleEvent(time::milliseconds(200), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a"))); |
| |
| // matches regex, but prefix is not registered |
| scheduler.scheduleEvent(time::milliseconds(300), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b"))); |
| |
| // matches regex, but prefix is not registered |
| scheduler.scheduleEvent(time::milliseconds(400), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b/c"))); |
| |
| // prefix is not registered, and also does not match regex |
| scheduler.scheduleEvent(time::milliseconds(500), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b/d"))); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nRegFailures, 0); |
| BOOST_CHECK_EQUAL(nInInterests, 0); |
| BOOST_CHECK_EQUAL(nTimeouts, 0); |
| BOOST_CHECK_EQUAL(nNacks, 4); |
| BOOST_CHECK_EQUAL(nData, 0); |
| } |
| |
| |
| BOOST_FIXTURE_TEST_CASE(SetRegexFilterAndRegister, FacesFixture3) |
| { |
| Face face; |
| Face face2(face.getIoService()); |
| Scheduler scheduler(face.getIoService()); |
| scheduler.scheduleEvent(time::seconds(4), |
| bind(&FacesFixture::terminate, this, ref(face))); |
| |
| face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"), |
| bind(&FacesFixture::onInterestRegex, this, |
| ref(face), _1, _2)); |
| |
| face.registerPrefix("/Hello/World", |
| bind(&FacesFixture3::onRegSucceeded, this), |
| bind(&FacesFixture3::onRegFailed, this)); |
| |
| scheduler.scheduleEvent(time::milliseconds(200), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a"))); // shouldn't match |
| |
| scheduler.scheduleEvent(time::milliseconds(300), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b"))); // should match |
| |
| scheduler.scheduleEvent(time::milliseconds(400), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b/c"))); // should match |
| |
| scheduler.scheduleEvent(time::milliseconds(500), |
| bind(&FacesFixture::expressInterest, this, |
| ref(face2), Name("/Hello/World/a/b/d"))); // should not match |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents()); |
| |
| BOOST_CHECK_EQUAL(nRegFailures, 0); |
| BOOST_CHECK_EQUAL(nRegSuccesses, 1); |
| |
| BOOST_CHECK_EQUAL(nInInterests, 2); |
| BOOST_CHECK_EQUAL(nTimeouts, 4); |
| BOOST_CHECK_EQUAL(nNacks, 0); |
| BOOST_CHECK_EQUAL(nData, 0); |
| } |
| |
| BOOST_AUTO_TEST_CASE(ShutdownWhileSendInProgress) // Bug #3136 |
| { |
| Face face; |
| face.expressInterest(Interest("/Hello/World/!"), nullptr, nullptr, nullptr); |
| BOOST_REQUIRE_NO_THROW(face.processEvents(time::seconds(1))); |
| |
| face.expressInterest(Interest("/Bye/World/1"), nullptr, nullptr, nullptr); |
| face.expressInterest(Interest("/Bye/World/2"), nullptr, nullptr, nullptr); |
| face.expressInterest(Interest("/Bye/World/3"), nullptr, nullptr, nullptr); |
| face.shutdown(); |
| |
| BOOST_REQUIRE_NO_THROW(face.processEvents(time::seconds(1))); |
| // should not segfault |
| } |
| |
| BOOST_AUTO_TEST_CASE(LargeDelayBetweenFaceConstructorAndProcessEvents) // Bug #2742 |
| { |
| ndn::Face face; |
| |
| ::sleep(5); // simulate setup workload |
| |
| BOOST_CHECK_NO_THROW(face.processEvents(time::seconds(1))); |
| } |
| |
| BOOST_AUTO_TEST_CASE(ProcessEventsBlocksForeverWhenNothingScheduled) // Bug #3957 |
| { |
| ndn::Face face; |
| std::mutex m; |
| std::condition_variable cv; |
| bool processEventsFinished = false; |
| |
| std::thread faceThread([&] { |
| face.processEvents(); |
| |
| processEventsFinished = true; |
| std::lock_guard<std::mutex> lk(m); |
| cv.notify_one(); |
| }); |
| |
| { |
| std::unique_lock<std::mutex> lk(m); |
| cv.wait_for(lk, std::chrono::seconds(5), [&] { return processEventsFinished; }); |
| } |
| |
| BOOST_CHECK_EQUAL(processEventsFinished, true); |
| if (!processEventsFinished) { |
| face.shutdown(); |
| } |
| faceThread.join(); |
| } |
| |
| BOOST_AUTO_TEST_SUITE_END() |
| |
| } // namespace tests |
| } // namespace ndn |