management: convert nfd::Controller to use ControlCommand
refs #1397
Change-Id: I4106c167e15b7cf4951687b3d18c4807c334d502
diff --git a/tests/management/nfd-controller.cpp b/tests/management/nfd-controller.cpp
new file mode 100644
index 0000000..9176690
--- /dev/null
+++ b/tests/management/nfd-controller.cpp
@@ -0,0 +1,174 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "management/nfd-controller.hpp"
+// Having a separate compilation unit is necessary to ensure .hpp can compile on its own.
+#include "management/nfd-control-response.hpp"
+
+#include "../transport/dummy-face.hpp"
+#include <boost/test/unit_test.hpp>
+
+namespace ndn {
+namespace nfd {
+
+BOOST_AUTO_TEST_SUITE(NfdController)
+
+class CommandFixture
+{
+protected:
+ CommandFixture()
+ : face(makeDummyFace())
+ , controller(*face)
+ , commandSucceedCallback(bind(&CommandFixture::onCommandSucceed, this, _1))
+ , commandFailCallback(bind(&CommandFixture::onCommandFail, this, _1, _2))
+ {
+ }
+
+private:
+ void
+ onCommandSucceed(const ControlParameters& parameters)
+ {
+ commandSucceedHistory.push_back(boost::make_tuple(parameters));
+ }
+
+ void
+ onCommandFail(uint32_t code, const std::string& reason)
+ {
+ commandFailHistory.push_back(boost::make_tuple(code, reason));
+ }
+
+protected:
+ shared_ptr<DummyFace> face;
+ Controller controller;
+ KeyChain keyChain;
+
+ Controller::CommandSucceedCallback commandSucceedCallback;
+ typedef boost::tuple<ControlParameters> CommandSucceedArgs;
+ std::vector<CommandSucceedArgs> commandSucceedHistory;
+
+ Controller::CommandFailCallback commandFailCallback;
+ typedef boost::tuple<uint32_t,std::string> CommandFailArgs;
+ std::vector<CommandFailArgs> commandFailHistory;
+};
+
+BOOST_FIXTURE_TEST_CASE(CommandSuccess, CommandFixture)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://example.com");
+
+ BOOST_CHECK_NO_THROW(controller.start<FaceCreateCommand>(
+ parameters,
+ commandSucceedCallback,
+ commandFailCallback));
+ face->processEvents(time::milliseconds(1));
+
+ BOOST_REQUIRE_EQUAL(face->m_sentInterests.size(), 1);
+ const Interest& commandInterest = face->m_sentInterests[0];
+
+ FaceCreateCommand command;
+ BOOST_CHECK(command.getPrefix().isPrefixOf(commandInterest.getName()));
+ // 9 components: ndn:/localhost/nfd/face/create/<parameters>/<command Interest signature x4>
+ BOOST_REQUIRE_EQUAL(commandInterest.getName().size(), 9);
+ ControlParameters request;
+ // 4th component: <parameters>
+ BOOST_REQUIRE_NO_THROW(request.wireDecode(commandInterest.getName().at(4).blockFromValue()));
+ BOOST_CHECK_NO_THROW(command.validateRequest(request));
+ BOOST_CHECK_EQUAL(request.getUri(), parameters.getUri());
+
+ ControlParameters responseBody;
+ responseBody.setUri("tcp4://192.0.2.1:6363")
+ .setFaceId(22);
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+
+ Data responseData(commandInterest.getName());
+ responseData.setContent(responsePayload.wireEncode());
+ keyChain.sign(responseData);
+ face->receive(responseData);
+ face->processEvents(time::milliseconds(1));
+
+ BOOST_CHECK_EQUAL(commandFailHistory.size(), 0);
+ BOOST_REQUIRE_EQUAL(commandSucceedHistory.size(), 1);
+ const ControlParameters& response = commandSucceedHistory[0].get<0>();
+ BOOST_CHECK_EQUAL(response.getUri(), responseBody.getUri());
+ BOOST_CHECK_EQUAL(response.getFaceId(), responseBody.getFaceId());
+}
+
+BOOST_FIXTURE_TEST_CASE(CommandInvalidRequest, CommandFixture)
+{
+ ControlParameters parameters;
+ parameters.setName("ndn:/should-not-have-this-field");
+ // Uri is missing
+
+ BOOST_CHECK_THROW(controller.start<FaceCreateCommand>(
+ parameters,
+ commandSucceedCallback,
+ commandFailCallback),
+ ControlCommand::ArgumentError);
+}
+
+BOOST_FIXTURE_TEST_CASE(CommandErrorCode, CommandFixture)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://example.com");
+
+ BOOST_CHECK_NO_THROW(controller.start<FaceCreateCommand>(
+ parameters,
+ commandSucceedCallback,
+ commandFailCallback));
+ face->processEvents(time::milliseconds(1));
+
+ BOOST_REQUIRE_EQUAL(face->m_sentInterests.size(), 1);
+ const Interest& commandInterest = face->m_sentInterests[0];
+
+ ControlResponse responsePayload(401, "Not Authenticated");
+
+ Data responseData(commandInterest.getName());
+ responseData.setContent(responsePayload.wireEncode());
+ keyChain.sign(responseData);
+ face->receive(responseData);
+ face->processEvents(time::milliseconds(1));
+
+ BOOST_CHECK_EQUAL(commandSucceedHistory.size(), 0);
+ BOOST_REQUIRE_EQUAL(commandFailHistory.size(), 1);
+ BOOST_CHECK_EQUAL(commandFailHistory[0].get<0>(), 401);
+}
+
+BOOST_FIXTURE_TEST_CASE(CommandInvalidResponse, CommandFixture)
+{
+ ControlParameters parameters;
+ parameters.setUri("tcp://example.com");
+
+ BOOST_CHECK_NO_THROW(controller.start<FaceCreateCommand>(
+ parameters,
+ commandSucceedCallback,
+ commandFailCallback));
+ face->processEvents(time::milliseconds(1));
+
+ BOOST_REQUIRE_EQUAL(face->m_sentInterests.size(), 1);
+ const Interest& commandInterest = face->m_sentInterests[0];
+
+ ControlParameters responseBody;
+ responseBody.setUri("tcp4://192.0.2.1:6363")
+ .setName("ndn:/should-not-have-this-field");
+ // FaceId is missing
+ ControlResponse responsePayload(201, "created");
+ responsePayload.setBody(responseBody.wireEncode());
+
+ Data responseData(commandInterest.getName());
+ responseData.setContent(responsePayload.wireEncode());
+ keyChain.sign(responseData);
+ face->receive(responseData);
+ face->processEvents(time::milliseconds(1));
+
+ BOOST_CHECK_EQUAL(commandSucceedHistory.size(), 0);
+ BOOST_REQUIRE_EQUAL(commandFailHistory.size(), 1);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nfd
+} // namespace ndn
diff --git a/tests/security/identity-fixture.cpp b/tests/security/identity-fixture.cpp
new file mode 100644
index 0000000..2cc7e4f
--- /dev/null
+++ b/tests/security/identity-fixture.cpp
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * @author: Yingdi Yu <yingdi0@cs.ucla.edu>
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "security/key-chain.hpp"
+#include <boost/test/unit_test.hpp>
+
+namespace ndn {
+
+// OSX KeyChain, when used on a headless server,
+// forbids usage of a private key if that key isn't created by the calling process.
+// Therefore, unit testing must create its own key pair.
+
+class IdentityFixture
+{
+public:
+ IdentityFixture()
+ {
+ // save the old default identity
+ m_oldDefaultIdentity = m_keyChain.getDefaultIdentity();
+
+ m_newIdentity.set("/ndn-cpp-dev-test-identity");
+ m_newIdentity.appendVersion();
+
+ // create the new identity and self-signed certificate
+ m_keyChain.createIdentity(m_newIdentity);
+
+ // set the new identity as default identity,
+ // and the corresponding certificate becomes the default certificate
+ m_keyChain.setDefaultIdentity(m_newIdentity);
+ }
+
+ ~IdentityFixture()
+ {
+ // recover the old default setting
+ m_keyChain.setDefaultIdentity(m_oldDefaultIdentity);
+
+ // remove the temporarily created identity and certificates
+ m_keyChain.deleteIdentity(m_newIdentity);
+ }
+
+private:
+ KeyChain m_keyChain;
+ Name m_oldDefaultIdentity;
+ Name m_newIdentity;
+};
+
+BOOST_GLOBAL_FIXTURE(IdentityFixture);
+
+} // namespace ndn
diff --git a/tests/transport/dummy-face.hpp b/tests/transport/dummy-face.hpp
new file mode 100644
index 0000000..1ac3cc9
--- /dev/null
+++ b/tests/transport/dummy-face.hpp
@@ -0,0 +1,98 @@
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NDN_TESTS_DUMMY_FACE_HPP
+#define NDN_TESTS_DUMMY_FACE_HPP
+
+#include "face.hpp"
+
+namespace ndn {
+
+class DummyTransport : public Transport
+{
+public:
+ void
+ receive(const Block& block)
+ {
+ m_receiveCallback(block);
+ }
+
+ virtual void
+ close()
+ {
+ }
+
+ virtual void
+ pause()
+ {
+ }
+
+ virtual void
+ resume()
+ {
+ }
+
+ virtual void
+ send(const Block& wire)
+ {
+ if (wire.type() == Tlv::Interest) {
+ m_sentInterests->push_back(Interest(wire));
+ }
+ else if (wire.type() == Tlv::Data) {
+ m_sentDatas->push_back(Data(wire));
+ }
+ }
+
+ virtual void
+ send(const Block& header, const Block& payload)
+ {
+ this->send(payload);
+ }
+
+public:
+ std::vector<Interest>* m_sentInterests;
+ std::vector<Data>* m_sentDatas;
+};
+
+
+/** \brief a Face for unit testing
+ */
+class DummyFace : public Face
+{
+public:
+ explicit
+ DummyFace(shared_ptr<DummyTransport> transport)
+ : Face(transport)
+ , m_transport(transport)
+ {
+ m_transport->m_sentInterests = &m_sentInterests;
+ m_transport->m_sentDatas = &m_sentDatas;
+ }
+
+ /** \brief cause the Face to receive a packet
+ */
+ template<typename Packet>
+ void
+ receive(const Packet& packet)
+ {
+ m_transport->receive(packet.wireEncode());
+ }
+
+public:
+ std::vector<Interest> m_sentInterests;
+ std::vector<Data> m_sentDatas;
+
+private:
+ shared_ptr<DummyTransport> m_transport;
+};
+
+inline shared_ptr<DummyFace>
+makeDummyFace()
+{
+ return make_shared<DummyFace>(make_shared<DummyTransport>());
+}
+
+} // namespace ndn
+#endif // NDN_TESTS_DUMMY_FACE_HPP