blob: d1b8274d57d661edd7c6987fc9fcaa8abf66d89f [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
62 /** \brief check the last Interest is a command with specified prefix
63 * \retval nullopt last Interest is not the expected command
64 * \return command parameters
65 */
66 ndn::optional<ControlParameters>
Yanbiao Li58ba3f92017-02-15 14:27:18 +000067 getCommand(const Name& expectedPrefix) const
Junxiao Shi1d7fef52017-02-02 05:33:14 +000068 {
69 if (face.sentInterests.empty() ||
70 !expectedPrefix.isPrefixOf(face.sentInterests.back().getName())) {
71 return ndn::nullopt;
72 }
73 return ControlParameters(face.sentInterests.back().getName()
74 .at(expectedPrefix.size()).blockFromValue());
75 }
76
77 /** \brief respond to the last command
78 * \pre last Interest is a command
79 */
80 void
81 succeedCommand(const ControlParameters& parameters)
82 {
83 ndn::nfd::ControlResponse resp(200, "OK");
84 resp.setBody(parameters.wireEncode());
85 this->sendCommandReply(resp);
86 }
87
88 /** \brief respond to the last command
89 * \pre last Interest is a command
90 */
91 void
92 failCommand(uint32_t code, const std::string& text)
93 {
94 this->sendCommandReply({code, text});
95 }
96
Yanbiao Li58ba3f92017-02-15 14:27:18 +000097 void
98 failCommand(uint32_t code, const std::string& text, const ControlParameters& resp)
99 {
100 this->sendCommandReply(ndn::nfd::ControlResponse(code, text).setBody(resp.wireEncode()));
101 }
102
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000103protected: // StatusDataset
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000104 /** \brief send an empty dataset in reply to StatusDataset request
105 * \param prefix dataset prefix without version and segment
106 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
107 */
108 void
109 sendEmptyDataset(const Name& prefix)
110 {
111 this->sendDatasetReply(prefix, nullptr, 0);
112 }
113
114 /** \brief send one WireEncodable in reply to StatusDataset request
115 * \param prefix dataset prefix without version and segment
116 * \param payload payload block
117 * \note payload must fit in one Data
118 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
119 */
120 template<typename T>
121 void
122 sendDataset(const Name& prefix, const T& payload)
123 {
124 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
125
126 this->sendDatasetReply(prefix, payload.wireEncode());
127 }
128
129 /** \brief send two WireEncodables in reply to StatusDataset request
130 * \param prefix dataset prefix without version and segment
131 * \param payload1 first vector item
132 * \param payload2 second vector item
133 * \note all payloads must fit in one Data
134 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
135 */
136 template<typename T1, typename T2>
137 void
138 sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
139 {
140 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
141 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
142
143 ndn::encoding::EncodingBuffer buffer;
144 payload2.wireEncode(buffer);
145 payload1.wireEncode(buffer);
146
147 this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
148 }
149
Junxiao Shi918e5d42017-02-25 03:58:21 +0000150 /** \brief respond to specific FaceQuery requests
151 * \retval true the Interest matches one of the defined patterns and is responded
152 * \retval false the Interest is not responded
153 */
154 bool
155 respondFaceQuery(const Interest& interest)
156 {
157 using ndn::nfd::FacePersistency;
158 using ndn::nfd::FaceQueryFilter;
159 using ndn::nfd::FaceStatus;
160
161 if (!Name("/localhost/nfd/faces/query").isPrefixOf(interest.getName())) {
162 return false;
163 }
164 BOOST_CHECK_EQUAL(interest.getName().size(), 5);
165 FaceQueryFilter filter(interest.getName().at(4).blockFromValue());
166
167 if (filter == FaceQueryFilter().setFaceId(10156)) {
168 FaceStatus faceStatus;
169 faceStatus.setFaceId(10156)
170 .setLocalUri("tcp4://151.26.163.27:22967")
171 .setRemoteUri("tcp4://198.57.27.40:6363")
172 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
173 this->sendDataset(interest.getName(), faceStatus);
174 return true;
175 }
176
177 if (filter == FaceQueryFilter().setRemoteUri("tcp4://32.121.182.82:6363")) {
178 FaceStatus faceStatus;
179 faceStatus.setFaceId(2249)
180 .setLocalUri("tcp4://30.99.87.98:31414")
181 .setRemoteUri("tcp4://32.121.182.82:6363")
182 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
183 this->sendDataset(interest.getName(), faceStatus);
184 return true;
185 }
186
187 if (filter == FaceQueryFilter().setFaceId(23728)) {
188 this->sendEmptyDataset(interest.getName());
189 return true;
190 }
191
192 if (filter == FaceQueryFilter().setRemoteUri("udp4://225.131.75.231:56363")) {
193 FaceStatus faceStatus1, faceStatus2;
194 faceStatus1.setFaceId(6720)
195 .setLocalUri("udp4://202.83.168.28:56363")
196 .setRemoteUri("udp4://225.131.75.231:56363")
197 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT);
198 faceStatus2.setFaceId(31066)
199 .setLocalUri("udp4://25.90.26.32:56363")
200 .setRemoteUri("udp4://225.131.75.231:56363")
201 .setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERMANENT);
202 this->sendDataset(interest.getName(), faceStatus1, faceStatus2);
203 return true;
204 }
205
206 return false;
207 }
208
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000209private:
210 virtual void
211 processEventsOverride(time::milliseconds timeout)
212 {
213 if (timeout <= time::milliseconds::zero()) {
214 // give enough time to finish execution
215 timeout = time::seconds(30);
216 }
217 this->advanceClocks(time::milliseconds(100), timeout);
218 }
219
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000220 void
221 sendCommandReply(const ndn::nfd::ControlResponse& resp)
222 {
223 auto data = makeData(face.sentInterests.back().getName());
224 data->setContent(resp.wireEncode());
225 face.receive(*data);
226 }
227
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000228 /** \brief send a payload in reply to StatusDataset request
229 * \param name dataset prefix without version and segment
230 * \param contentArgs passed to Data::setContent
231 */
232 template<typename ...ContentArgs>
233 void
234 sendDatasetReply(Name name, ContentArgs&&... contentArgs)
235 {
236 name.appendVersion().appendSegment(0);
237
238 // These warnings assist in debugging when nfdc does not receive StatusDataset.
239 // They usually indicate a misspelled prefix or incorrect timing in the test case.
240 if (face.sentInterests.empty()) {
241 BOOST_WARN_MESSAGE(false, "no Interest expressed");
242 }
243 else {
244 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
245 "last Interest " << face.sentInterests.back().getName() <<
246 " cannot be satisfied by this Data " << name);
247 }
248
249 auto data = make_shared<Data>(name);
250 data->setFinalBlockId(name[-1]);
251 data->setContent(std::forward<ContentArgs>(contentArgs)...);
252 this->signDatasetReply(*data);
253 face.receive(*data);
254 }
255
256 virtual void
257 signDatasetReply(Data& data)
258 {
259 signData(data);
260 }
261
262protected:
263 ndn::util::DummyClientFace face;
264 std::function<void(const Interest&)> processInterest;
265};
266
267} // namespace tests
268} // namespace nfdc
269} // namespace tools
270} // namespace nfd
271
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000272#define MOCK_NFD_MGMT_REQUIRE_LAST_COMMAND_IS(expectedPrefix) \
273 [this] { \
274 BOOST_REQUIRE_MESSAGE(!face.sentInterests.empty(), "no Interest expressed"); \
275 auto params = this->getCommand(expectedPrefix); \
276 BOOST_REQUIRE_MESSAGE(params, "last Interest " << face.sentInterests.back().getName() << \
277 " does not match command prefix " << expectedPrefix); \
278 return *params; \
279 } ()
280
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000281#endif // NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP