blob: fd309bfad29c87767c3b3b517a35f97cc22d953a [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
Junxiao Shi1a25a6e2017-03-06 03:09:47 +000075 /** \brief send successful response to a command Interest
Junxiao Shi1d7fef52017-02-02 05:33:14 +000076 */
77 void
Junxiao Shi084b7952017-02-26 22:00:53 +000078 succeedCommand(const Interest& interest, const ControlParameters& parameters)
Junxiao Shi1d7fef52017-02-02 05:33:14 +000079 {
Junxiao Shi1a25a6e2017-03-06 03:09:47 +000080 this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
Junxiao Shi084b7952017-02-26 22:00:53 +000081 }
82
Junxiao Shi1a25a6e2017-03-06 03:09:47 +000083 /** \brief send failure response to a command Interest
Junxiao Shi1d7fef52017-02-02 05:33:14 +000084 */
85 void
Junxiao Shi084b7952017-02-26 22:00:53 +000086 failCommand(const Interest& interest, uint32_t code, const std::string& text)
Junxiao Shi1d7fef52017-02-02 05:33:14 +000087 {
Junxiao Shi084b7952017-02-26 22:00:53 +000088 this->sendCommandReply(interest, {code, text});
89 }
90
Junxiao Shi1a25a6e2017-03-06 03:09:47 +000091 /** \brief send failure response to a command Interest
92 */
Yanbiao Li58ba3f92017-02-15 14:27:18 +000093 void
Junxiao Shi084b7952017-02-26 22:00:53 +000094 failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
Yanbiao Li58ba3f92017-02-15 14:27:18 +000095 {
Junxiao Shi084b7952017-02-26 22:00:53 +000096 this->sendCommandReply(interest, code, text, body.wireEncode());
97 }
98
Junxiao Shi1d7fef52017-02-02 05:33:14 +000099protected: // StatusDataset
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000100 /** \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
Junxiao Shi918e5d42017-02-25 03:58:21 +0000146 /** \brief respond to specific FaceQuery requests
147 * \retval true the Interest matches one of the defined patterns and is responded
148 * \retval false the Interest is not responded
149 */
150 bool
151 respondFaceQuery(const Interest& interest)
152 {
153 using ndn::nfd::FacePersistency;
154 using ndn::nfd::FaceQueryFilter;
155 using ndn::nfd::FaceStatus;
156
157 if (!Name("/localhost/nfd/faces/query").isPrefixOf(interest.getName())) {
158 return false;
159 }
160 BOOST_CHECK_EQUAL(interest.getName().size(), 5);
161 FaceQueryFilter filter(interest.getName().at(4).blockFromValue());
162
163 if (filter == FaceQueryFilter().setFaceId(10156)) {
164 FaceStatus faceStatus;
165 faceStatus.setFaceId(10156)
166 .setLocalUri("tcp4://151.26.163.27:22967")
167 .setRemoteUri("tcp4://198.57.27.40:6363")
168 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
169 this->sendDataset(interest.getName(), faceStatus);
170 return true;
171 }
172
173 if (filter == FaceQueryFilter().setRemoteUri("tcp4://32.121.182.82:6363")) {
174 FaceStatus faceStatus;
175 faceStatus.setFaceId(2249)
176 .setLocalUri("tcp4://30.99.87.98:31414")
177 .setRemoteUri("tcp4://32.121.182.82:6363")
178 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
179 this->sendDataset(interest.getName(), faceStatus);
180 return true;
181 }
182
183 if (filter == FaceQueryFilter().setFaceId(23728)) {
184 this->sendEmptyDataset(interest.getName());
185 return true;
186 }
187
188 if (filter == FaceQueryFilter().setRemoteUri("udp4://225.131.75.231:56363")) {
189 FaceStatus faceStatus1, faceStatus2;
190 faceStatus1.setFaceId(6720)
191 .setLocalUri("udp4://202.83.168.28:56363")
192 .setRemoteUri("udp4://225.131.75.231:56363")
193 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT);
194 faceStatus2.setFaceId(31066)
195 .setLocalUri("udp4://25.90.26.32:56363")
196 .setRemoteUri("udp4://225.131.75.231:56363")
197 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT);
198 this->sendDataset(interest.getName(), faceStatus1, faceStatus2);
199 return true;
200 }
201
202 return false;
203 }
204
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000205private:
206 virtual void
207 processEventsOverride(time::milliseconds timeout)
208 {
209 if (timeout <= time::milliseconds::zero()) {
210 // give enough time to finish execution
211 timeout = time::seconds(30);
212 }
213 this->advanceClocks(time::milliseconds(100), timeout);
214 }
215
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000216 void
Junxiao Shi084b7952017-02-26 22:00:53 +0000217 sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000218 {
Junxiao Shi084b7952017-02-26 22:00:53 +0000219 auto data = makeData(interest.getName());
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000220 data->setContent(resp.wireEncode());
221 face.receive(*data);
222 }
223
Junxiao Shi084b7952017-02-26 22:00:53 +0000224 void
225 sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
226 const Block& body)
227 {
228 this->sendCommandReply(interest,
229 ndn::nfd::ControlResponse(code, text).setBody(body));
230 }
231
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000232 /** \brief send a payload in reply to StatusDataset request
233 * \param name dataset prefix without version and segment
234 * \param contentArgs passed to Data::setContent
235 */
236 template<typename ...ContentArgs>
237 void
238 sendDatasetReply(Name name, ContentArgs&&... contentArgs)
239 {
240 name.appendVersion().appendSegment(0);
241
242 // These warnings assist in debugging when nfdc does not receive StatusDataset.
243 // They usually indicate a misspelled prefix or incorrect timing in the test case.
244 if (face.sentInterests.empty()) {
245 BOOST_WARN_MESSAGE(false, "no Interest expressed");
246 }
247 else {
248 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
249 "last Interest " << face.sentInterests.back().getName() <<
250 " cannot be satisfied by this Data " << name);
251 }
252
253 auto data = make_shared<Data>(name);
254 data->setFinalBlockId(name[-1]);
255 data->setContent(std::forward<ContentArgs>(contentArgs)...);
256 this->signDatasetReply(*data);
257 face.receive(*data);
258 }
259
260 virtual void
261 signDatasetReply(Data& data)
262 {
263 signData(data);
264 }
265
266protected:
267 ndn::util::DummyClientFace face;
268 std::function<void(const Interest&)> processInterest;
269};
270
271} // namespace tests
272} // namespace nfdc
273} // namespace tools
274} // namespace nfd
275
Junxiao Shi084b7952017-02-26 22:00:53 +0000276/** \brief require the command in \p interest has expected prefix
277 * \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
Junxiao Shi1a25a6e2017-03-06 03:09:47 +0000278 * \return ControlParameters, or nullopt if \p interest does match \p expectedPrefix
Junxiao Shi084b7952017-02-26 22:00:53 +0000279 */
280#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
281 [interest] { \
282 auto params = parseCommand(interest, (expectedPrefix)); \
283 BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
284 " does not match command prefix " << (expectedPrefix)); \
285 return *params; \
286 } ()
287
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000288#endif // NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP