blob: 1e1d592101706370d1de87f39f49ccd96cc0ff9b [file] [log] [blame]
Junxiao Shicb766862017-07-07 22:21:04 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Davide Pesavento1d12d2f2019-03-22 12:44:14 -04003 * Copyright (c) 2014-2019, Regents of the University of California,
Junxiao Shicb766862017-07-07 22:21:04 +00004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#ifndef NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
27#define NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
28
Davide Pesaventob7703ad2019-03-23 21:12:56 -040029#include "tests/clock-fixture.hpp"
Davide Pesavento1d12d2f2019-03-22 12:44:14 -040030#include "tests/key-chain-fixture.hpp"
Davide Pesaventob7703ad2019-03-23 21:12:56 -040031#include "tests/test-common.hpp"
Junxiao Shicb766862017-07-07 22:21:04 +000032
Davide Pesavento17057442018-04-20 15:21:31 -040033#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
34#include <ndn-cxx/mgmt/nfd/control-response.hpp>
Alexander Afanasyev847de402017-09-21 18:57:30 -040035#include <ndn-cxx/util/dummy-client-face.hpp>
36
Junxiao Shicb766862017-07-07 22:21:04 +000037namespace nfd {
38namespace tools {
39namespace tests {
40
41using namespace nfd::tests;
42using ndn::nfd::ControlParameters;
43
Davide Pesaventob7703ad2019-03-23 21:12:56 -040044/** \brief Fixture to emulate NFD management.
Junxiao Shicb766862017-07-07 22:21:04 +000045 */
Davide Pesaventob7703ad2019-03-23 21:12:56 -040046class MockNfdMgmtFixture : public ClockFixture, public KeyChainFixture
Junxiao Shicb766862017-07-07 22:21:04 +000047{
48protected:
49 MockNfdMgmtFixture()
Davide Pesaventob7703ad2019-03-23 21:12:56 -040050 : ClockFixture(m_io)
51 , face(m_io, m_keyChain,
Junxiao Shicb766862017-07-07 22:21:04 +000052 {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
53 {
Davide Pesaventob7703ad2019-03-23 21:12:56 -040054 face.onSendInterest.connect([this] (const Interest& interest) {
55 if (processInterest) {
56 m_io.post([=] { processInterest(interest); });
57 }
Junxiao Shicb766862017-07-07 22:21:04 +000058 });
59 }
60
Junxiao Shicb766862017-07-07 22:21:04 +000061protected: // ControlCommand
62 /** \brief check the Interest is a command with specified prefix
63 * \retval nullopt last Interest is not the expected command
64 * \return command parameters
65 */
Davide Pesavento87fc0f82018-04-11 23:43:51 -040066 static optional<ControlParameters>
Junxiao Shicb766862017-07-07 22:21:04 +000067 parseCommand(const Interest& interest, const Name& expectedPrefix)
68 {
69 if (!expectedPrefix.isPrefixOf(interest.getName())) {
Davide Pesavento87fc0f82018-04-11 23:43:51 -040070 return nullopt;
Junxiao Shicb766862017-07-07 22:21:04 +000071 }
72 return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
73 }
74
75 /** \brief send successful response to a command Interest
76 */
77 void
78 succeedCommand(const Interest& interest, const ControlParameters& parameters)
79 {
80 this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
81 }
82
83 /** \brief send failure response to a command Interest
84 */
85 void
86 failCommand(const Interest& interest, uint32_t code, const std::string& text)
87 {
88 this->sendCommandReply(interest, {code, text});
89 }
90
91 /** \brief send failure response to a command Interest
92 */
93 void
94 failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
95 {
96 this->sendCommandReply(interest, code, text, body.wireEncode());
97 }
98
99protected: // StatusDataset
100 /** \brief send an empty dataset in reply to StatusDataset request
101 * \param prefix dataset prefix without version and segment
102 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
103 */
104 void
105 sendEmptyDataset(const Name& prefix)
106 {
107 this->sendDatasetReply(prefix, nullptr, 0);
108 }
109
110 /** \brief send one WireEncodable in reply to StatusDataset request
111 * \param prefix dataset prefix without version and segment
112 * \param payload payload block
113 * \note payload must fit in one Data
114 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
115 */
116 template<typename T>
117 void
118 sendDataset(const Name& prefix, const T& payload)
119 {
120 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
121
122 this->sendDatasetReply(prefix, payload.wireEncode());
123 }
124
125 /** \brief send two WireEncodables in reply to StatusDataset request
126 * \param prefix dataset prefix without version and segment
127 * \param payload1 first vector item
128 * \param payload2 second vector item
129 * \note all payloads must fit in one Data
130 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
131 */
132 template<typename T1, typename T2>
133 void
134 sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
135 {
136 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
137 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
138
139 ndn::encoding::EncodingBuffer buffer;
140 payload2.wireEncode(buffer);
141 payload1.wireEncode(buffer);
142
143 this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
144 }
145
146private:
147 virtual void
148 processEventsOverride(time::milliseconds timeout)
149 {
Davide Pesavento17057442018-04-20 15:21:31 -0400150 if (timeout <= 0_ms) {
Junxiao Shicb766862017-07-07 22:21:04 +0000151 // give enough time to finish execution
Davide Pesavento17057442018-04-20 15:21:31 -0400152 timeout = 30_s;
Junxiao Shicb766862017-07-07 22:21:04 +0000153 }
Davide Pesavento17057442018-04-20 15:21:31 -0400154 this->advanceClocks(100_ms, timeout);
Junxiao Shicb766862017-07-07 22:21:04 +0000155 }
156
157 void
158 sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
159 {
160 auto data = makeData(interest.getName());
161 data->setContent(resp.wireEncode());
162 face.receive(*data);
163 }
164
165 void
166 sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
167 const Block& body)
168 {
Davide Pesavento17057442018-04-20 15:21:31 -0400169 this->sendCommandReply(interest, ndn::nfd::ControlResponse(code, text).setBody(body));
Junxiao Shicb766862017-07-07 22:21:04 +0000170 }
171
172 /** \brief send a payload in reply to StatusDataset request
173 * \param name dataset prefix without version and segment
174 * \param contentArgs passed to Data::setContent
175 */
176 template<typename ...ContentArgs>
177 void
178 sendDatasetReply(Name name, ContentArgs&&... contentArgs)
179 {
180 name.appendVersion().appendSegment(0);
181
182 // These warnings assist in debugging when nfdc does not receive StatusDataset.
183 // They usually indicate a misspelled prefix or incorrect timing in the test case.
184 if (face.sentInterests.empty()) {
185 BOOST_WARN_MESSAGE(false, "no Interest expressed");
186 }
187 else {
188 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
189 "last Interest " << face.sentInterests.back().getName() <<
190 " cannot be satisfied by this Data " << name);
191 }
192
193 auto data = make_shared<Data>(name);
Davide Pesavento17057442018-04-20 15:21:31 -0400194 data->setFinalBlock(name[-1]);
Junxiao Shicb766862017-07-07 22:21:04 +0000195 data->setContent(std::forward<ContentArgs>(contentArgs)...);
196 this->signDatasetReply(*data);
197 face.receive(*data);
198 }
199
200 virtual void
201 signDatasetReply(Data& data)
202 {
203 signData(data);
204 }
205
Davide Pesaventob7703ad2019-03-23 21:12:56 -0400206private:
207 boost::asio::io_service m_io;
208
Junxiao Shicb766862017-07-07 22:21:04 +0000209protected:
210 ndn::util::DummyClientFace face;
211 std::function<void(const Interest&)> processInterest;
212};
213
214} // namespace tests
215} // namespace tools
216} // namespace nfd
217
218/** \brief require the command in \p interest has expected prefix
219 * \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
220 * \return ControlParameters, or nullopt if \p interest does match \p expectedPrefix
221 */
222#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
223 [interest] { \
224 auto params = parseCommand(interest, (expectedPrefix)); \
225 BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
226 " does not match command prefix " << (expectedPrefix)); \
227 return *params; \
228 } ()
229
230#endif // NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP