blob: 319a9f129b4567425cf35c03d10eee81a87bd230 [file] [log] [blame]
Junxiao Shi1f481fa2017-01-26 15:14:43 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2017, Regents of the University of California,
4 * 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_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP
27#define NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP
28
Junxiao Shi918e5d42017-02-25 03:58:21 +000029#include <ndn-cxx/mgmt/nfd/face-query-filter.hpp>
Junxiao Shi1f481fa2017-01-26 15:14:43 +000030#include <ndn-cxx/util/dummy-client-face.hpp>
31
32#include "tests/test-common.hpp"
33#include "tests/identity-management-fixture.hpp"
34
35namespace nfd {
36namespace tools {
37namespace nfdc {
38namespace tests {
39
40using namespace nfd::tests;
Junxiao Shi1d7fef52017-02-02 05:33:14 +000041using ndn::nfd::ControlParameters;
Junxiao Shi1f481fa2017-01-26 15:14:43 +000042
43/** \brief fixture to emulate NFD management
44 */
45class MockNfdMgmtFixture : public IdentityManagementTimeFixture
46{
47protected:
48 MockNfdMgmtFixture()
49 : face(g_io, m_keyChain,
50 {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
51 {
52 face.onSendInterest.connect([=] (const Interest& interest) {
53 g_io.post([=] {
54 if (processInterest != nullptr) {
55 processInterest(interest);
56 }
57 });
58 });
59 }
60
Junxiao Shi1d7fef52017-02-02 05:33:14 +000061protected: // ControlCommand
Junxiao Shi084b7952017-02-26 22:00:53 +000062 /** \brief check the Interest is a command with specified prefix
Junxiao Shi1d7fef52017-02-02 05:33:14 +000063 * \retval nullopt last Interest is not the expected command
64 * \return command parameters
65 */
Junxiao Shi084b7952017-02-26 22:00:53 +000066 static ndn::optional<ControlParameters>
67 parseCommand(const Interest& interest, const Name& expectedPrefix)
Junxiao Shi1d7fef52017-02-02 05:33:14 +000068 {
Junxiao Shi084b7952017-02-26 22:00:53 +000069 if (!expectedPrefix.isPrefixOf(interest.getName())) {
Junxiao Shi1d7fef52017-02-02 05:33:14 +000070 return ndn::nullopt;
71 }
Junxiao Shi084b7952017-02-26 22:00:53 +000072 return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
73 }
74
75 DEPRECATED(
76 ndn::optional<ControlParameters>
77 getCommand(const Name& expectedPrefix) const)
78 {
79 if (face.sentInterests.empty()) {
80 return ndn::nullopt;
81 }
82 return parseCommand(face.sentInterests.back(), expectedPrefix);
Junxiao Shi1d7fef52017-02-02 05:33:14 +000083 }
84
85 /** \brief respond to the last command
Junxiao Shi1d7fef52017-02-02 05:33:14 +000086 */
87 void
Junxiao Shi084b7952017-02-26 22:00:53 +000088 succeedCommand(const Interest& interest, const ControlParameters& parameters)
Junxiao Shi1d7fef52017-02-02 05:33:14 +000089 {
90 ndn::nfd::ControlResponse resp(200, "OK");
91 resp.setBody(parameters.wireEncode());
Junxiao Shi084b7952017-02-26 22:00:53 +000092 this->sendCommandReply(interest, resp);
93 }
94
95 DEPRECATED(
96 void
97 succeedCommand(const ControlParameters& parameters))
98 {
99 this->succeedCommand(face.sentInterests.back(), parameters);
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000100 }
101
102 /** \brief respond to the last command
103 * \pre last Interest is a command
104 */
105 void
Junxiao Shi084b7952017-02-26 22:00:53 +0000106 failCommand(const Interest& interest, uint32_t code, const std::string& text)
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000107 {
Junxiao Shi084b7952017-02-26 22:00:53 +0000108 this->sendCommandReply(interest, {code, text});
109 }
110
111 DEPRECATED(
112 void
113 failCommand(uint32_t code, const std::string& text))
114 {
115 this->sendCommandReply(face.sentInterests.back(), {code, text});
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000116 }
117
Yanbiao Li58ba3f92017-02-15 14:27:18 +0000118 void
Junxiao Shi084b7952017-02-26 22:00:53 +0000119 failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
Yanbiao Li58ba3f92017-02-15 14:27:18 +0000120 {
Junxiao Shi084b7952017-02-26 22:00:53 +0000121 this->sendCommandReply(interest, code, text, body.wireEncode());
122 }
123
124 DEPRECATED(
125 void
126 failCommand(uint32_t code, const std::string& text, const ControlParameters& body))
127 {
128 this->failCommand(face.sentInterests.back(), code, text, body);
Yanbiao Li58ba3f92017-02-15 14:27:18 +0000129 }
130
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000131protected: // StatusDataset
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000132 /** \brief send an empty dataset in reply to StatusDataset request
133 * \param prefix dataset prefix without version and segment
134 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
135 */
136 void
137 sendEmptyDataset(const Name& prefix)
138 {
139 this->sendDatasetReply(prefix, nullptr, 0);
140 }
141
142 /** \brief send one WireEncodable in reply to StatusDataset request
143 * \param prefix dataset prefix without version and segment
144 * \param payload payload block
145 * \note payload must fit in one Data
146 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
147 */
148 template<typename T>
149 void
150 sendDataset(const Name& prefix, const T& payload)
151 {
152 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
153
154 this->sendDatasetReply(prefix, payload.wireEncode());
155 }
156
157 /** \brief send two WireEncodables in reply to StatusDataset request
158 * \param prefix dataset prefix without version and segment
159 * \param payload1 first vector item
160 * \param payload2 second vector item
161 * \note all payloads must fit in one Data
162 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
163 */
164 template<typename T1, typename T2>
165 void
166 sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
167 {
168 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
169 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
170
171 ndn::encoding::EncodingBuffer buffer;
172 payload2.wireEncode(buffer);
173 payload1.wireEncode(buffer);
174
175 this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
176 }
177
Junxiao Shi918e5d42017-02-25 03:58:21 +0000178 /** \brief respond to specific FaceQuery requests
179 * \retval true the Interest matches one of the defined patterns and is responded
180 * \retval false the Interest is not responded
181 */
182 bool
183 respondFaceQuery(const Interest& interest)
184 {
185 using ndn::nfd::FacePersistency;
186 using ndn::nfd::FaceQueryFilter;
187 using ndn::nfd::FaceStatus;
188
189 if (!Name("/localhost/nfd/faces/query").isPrefixOf(interest.getName())) {
190 return false;
191 }
192 BOOST_CHECK_EQUAL(interest.getName().size(), 5);
193 FaceQueryFilter filter(interest.getName().at(4).blockFromValue());
194
195 if (filter == FaceQueryFilter().setFaceId(10156)) {
196 FaceStatus faceStatus;
197 faceStatus.setFaceId(10156)
198 .setLocalUri("tcp4://151.26.163.27:22967")
199 .setRemoteUri("tcp4://198.57.27.40:6363")
200 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
201 this->sendDataset(interest.getName(), faceStatus);
202 return true;
203 }
204
205 if (filter == FaceQueryFilter().setRemoteUri("tcp4://32.121.182.82:6363")) {
206 FaceStatus faceStatus;
207 faceStatus.setFaceId(2249)
208 .setLocalUri("tcp4://30.99.87.98:31414")
209 .setRemoteUri("tcp4://32.121.182.82:6363")
210 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
211 this->sendDataset(interest.getName(), faceStatus);
212 return true;
213 }
214
215 if (filter == FaceQueryFilter().setFaceId(23728)) {
216 this->sendEmptyDataset(interest.getName());
217 return true;
218 }
219
220 if (filter == FaceQueryFilter().setRemoteUri("udp4://225.131.75.231:56363")) {
221 FaceStatus faceStatus1, faceStatus2;
222 faceStatus1.setFaceId(6720)
223 .setLocalUri("udp4://202.83.168.28:56363")
224 .setRemoteUri("udp4://225.131.75.231:56363")
225 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT);
226 faceStatus2.setFaceId(31066)
227 .setLocalUri("udp4://25.90.26.32:56363")
228 .setRemoteUri("udp4://225.131.75.231:56363")
229 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT);
230 this->sendDataset(interest.getName(), faceStatus1, faceStatus2);
231 return true;
232 }
233
234 return false;
235 }
236
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000237private:
238 virtual void
239 processEventsOverride(time::milliseconds timeout)
240 {
241 if (timeout <= time::milliseconds::zero()) {
242 // give enough time to finish execution
243 timeout = time::seconds(30);
244 }
245 this->advanceClocks(time::milliseconds(100), timeout);
246 }
247
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000248 void
Junxiao Shi084b7952017-02-26 22:00:53 +0000249 sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000250 {
Junxiao Shi084b7952017-02-26 22:00:53 +0000251 auto data = makeData(interest.getName());
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000252 data->setContent(resp.wireEncode());
253 face.receive(*data);
254 }
255
Junxiao Shi084b7952017-02-26 22:00:53 +0000256 void
257 sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
258 const Block& body)
259 {
260 this->sendCommandReply(interest,
261 ndn::nfd::ControlResponse(code, text).setBody(body));
262 }
263
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000264 /** \brief send a payload in reply to StatusDataset request
265 * \param name dataset prefix without version and segment
266 * \param contentArgs passed to Data::setContent
267 */
268 template<typename ...ContentArgs>
269 void
270 sendDatasetReply(Name name, ContentArgs&&... contentArgs)
271 {
272 name.appendVersion().appendSegment(0);
273
274 // These warnings assist in debugging when nfdc does not receive StatusDataset.
275 // They usually indicate a misspelled prefix or incorrect timing in the test case.
276 if (face.sentInterests.empty()) {
277 BOOST_WARN_MESSAGE(false, "no Interest expressed");
278 }
279 else {
280 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
281 "last Interest " << face.sentInterests.back().getName() <<
282 " cannot be satisfied by this Data " << name);
283 }
284
285 auto data = make_shared<Data>(name);
286 data->setFinalBlockId(name[-1]);
287 data->setContent(std::forward<ContentArgs>(contentArgs)...);
288 this->signDatasetReply(*data);
289 face.receive(*data);
290 }
291
292 virtual void
293 signDatasetReply(Data& data)
294 {
295 signData(data);
296 }
297
298protected:
299 ndn::util::DummyClientFace face;
300 std::function<void(const Interest&)> processInterest;
301};
302
303} // namespace tests
304} // namespace nfdc
305} // namespace tools
306} // namespace nfd
307
Junxiao Shi084b7952017-02-26 22:00:53 +0000308/** \brief require the command in \p interest has expected prefix
309 * \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
310 */
311#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
312 [interest] { \
313 auto params = parseCommand(interest, (expectedPrefix)); \
314 BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
315 " does not match command prefix " << (expectedPrefix)); \
316 return *params; \
317 } ()
318
319///\deprecated use MOCK_NFD_MGMT_REQUIRE_COMMAND_IS
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000320#define MOCK_NFD_MGMT_REQUIRE_LAST_COMMAND_IS(expectedPrefix) \
321 [this] { \
322 BOOST_REQUIRE_MESSAGE(!face.sentInterests.empty(), "no Interest expressed"); \
Junxiao Shi084b7952017-02-26 22:00:53 +0000323 const Interest& interest = face.sentInterests.back(); \
324 return MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix); \
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000325 } ()
326
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000327#endif // NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP