blob: d0498e5e78eed31130c7c087cf84160694e9faf0 [file] [log] [blame]
Junxiao Shicb766862017-07-07 22:21:04 +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_MOCK_NFD_MGMT_FIXTURE_HPP
27#define NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP
28
Junxiao Shicb766862017-07-07 22:21:04 +000029#include "tests/test-common.hpp"
30#include "tests/identity-management-fixture.hpp"
31
Alexander Afanasyev847de402017-09-21 18:57:30 -040032#include <ndn-cxx/util/dummy-client-face.hpp>
33
Junxiao Shicb766862017-07-07 22:21:04 +000034namespace nfd {
35namespace tools {
36namespace tests {
37
38using namespace nfd::tests;
39using ndn::nfd::ControlParameters;
40
41/** \brief fixture to emulate NFD management
42 */
43class MockNfdMgmtFixture : public IdentityManagementTimeFixture
44{
45protected:
46 MockNfdMgmtFixture()
47 : face(g_io, m_keyChain,
48 {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
49 {
50 face.onSendInterest.connect([=] (const Interest& interest) {
51 g_io.post([=] {
52 if (processInterest != nullptr) {
53 processInterest(interest);
54 }
55 });
56 });
57 }
58
Alexander Afanasyev847de402017-09-21 18:57:30 -040059 virtual
60 ~MockNfdMgmtFixture() = default;
61
Junxiao Shicb766862017-07-07 22:21:04 +000062protected: // ControlCommand
63 /** \brief check the Interest is a command with specified prefix
64 * \retval nullopt last Interest is not the expected command
65 * \return command parameters
66 */
67 static ndn::optional<ControlParameters>
68 parseCommand(const Interest& interest, const Name& expectedPrefix)
69 {
70 if (!expectedPrefix.isPrefixOf(interest.getName())) {
71 return ndn::nullopt;
72 }
73 return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
74 }
75
76 /** \brief send successful response to a command Interest
77 */
78 void
79 succeedCommand(const Interest& interest, const ControlParameters& parameters)
80 {
81 this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
82 }
83
84 /** \brief send failure response to a command Interest
85 */
86 void
87 failCommand(const Interest& interest, uint32_t code, const std::string& text)
88 {
89 this->sendCommandReply(interest, {code, text});
90 }
91
92 /** \brief send failure response to a command Interest
93 */
94 void
95 failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
96 {
97 this->sendCommandReply(interest, code, text, body.wireEncode());
98 }
99
100protected: // StatusDataset
101 /** \brief send an empty dataset in reply to StatusDataset request
102 * \param prefix dataset prefix without version and segment
103 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
104 */
105 void
106 sendEmptyDataset(const Name& prefix)
107 {
108 this->sendDatasetReply(prefix, nullptr, 0);
109 }
110
111 /** \brief send one WireEncodable in reply to StatusDataset request
112 * \param prefix dataset prefix without version and segment
113 * \param payload payload block
114 * \note payload must fit in one Data
115 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
116 */
117 template<typename T>
118 void
119 sendDataset(const Name& prefix, const T& payload)
120 {
121 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
122
123 this->sendDatasetReply(prefix, payload.wireEncode());
124 }
125
126 /** \brief send two WireEncodables in reply to StatusDataset request
127 * \param prefix dataset prefix without version and segment
128 * \param payload1 first vector item
129 * \param payload2 second vector item
130 * \note all payloads must fit in one Data
131 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
132 */
133 template<typename T1, typename T2>
134 void
135 sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
136 {
137 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
138 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
139
140 ndn::encoding::EncodingBuffer buffer;
141 payload2.wireEncode(buffer);
142 payload1.wireEncode(buffer);
143
144 this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
145 }
146
147private:
148 virtual void
149 processEventsOverride(time::milliseconds timeout)
150 {
151 if (timeout <= time::milliseconds::zero()) {
152 // give enough time to finish execution
153 timeout = time::seconds(30);
154 }
155 this->advanceClocks(time::milliseconds(100), timeout);
156 }
157
158 void
159 sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
160 {
161 auto data = makeData(interest.getName());
162 data->setContent(resp.wireEncode());
163 face.receive(*data);
164 }
165
166 void
167 sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
168 const Block& body)
169 {
170 this->sendCommandReply(interest,
171 ndn::nfd::ControlResponse(code, text).setBody(body));
172 }
173
174 /** \brief send a payload in reply to StatusDataset request
175 * \param name dataset prefix without version and segment
176 * \param contentArgs passed to Data::setContent
177 */
178 template<typename ...ContentArgs>
179 void
180 sendDatasetReply(Name name, ContentArgs&&... contentArgs)
181 {
182 name.appendVersion().appendSegment(0);
183
184 // These warnings assist in debugging when nfdc does not receive StatusDataset.
185 // They usually indicate a misspelled prefix or incorrect timing in the test case.
186 if (face.sentInterests.empty()) {
187 BOOST_WARN_MESSAGE(false, "no Interest expressed");
188 }
189 else {
190 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
191 "last Interest " << face.sentInterests.back().getName() <<
192 " cannot be satisfied by this Data " << name);
193 }
194
195 auto data = make_shared<Data>(name);
196 data->setFinalBlockId(name[-1]);
197 data->setContent(std::forward<ContentArgs>(contentArgs)...);
198 this->signDatasetReply(*data);
199 face.receive(*data);
200 }
201
202 virtual void
203 signDatasetReply(Data& data)
204 {
205 signData(data);
206 }
207
208protected:
209 ndn::util::DummyClientFace face;
210 std::function<void(const Interest&)> processInterest;
211};
212
213} // namespace tests
214} // namespace tools
215} // namespace nfd
216
217/** \brief require the command in \p interest has expected prefix
218 * \note This must be used in processInterest lambda, and the Interest must be named 'interest'.
219 * \return ControlParameters, or nullopt if \p interest does match \p expectedPrefix
220 */
221#define MOCK_NFD_MGMT_REQUIRE_COMMAND_IS(expectedPrefix) \
222 [interest] { \
223 auto params = parseCommand(interest, (expectedPrefix)); \
224 BOOST_REQUIRE_MESSAGE(params, "Interest " << interest.getName() << \
225 " does not match command prefix " << (expectedPrefix)); \
226 return *params; \
227 } ()
228
229#endif // NFD_TESTS_TOOLS_MOCK_NFD_MGMT_FIXTURE_HPP