blob: 9e8bd53970c2c5dbde7955d3c2598b97c6bd974c [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
Junxiao Shicb766862017-07-07 22:21:04 +000029#include "tests/test-common.hpp"
Davide Pesavento1d12d2f2019-03-22 12:44:14 -040030#include "tests/key-chain-fixture.hpp"
Junxiao Shicb766862017-07-07 22:21:04 +000031
Davide Pesavento17057442018-04-20 15:21:31 -040032#include <ndn-cxx/mgmt/nfd/control-parameters.hpp>
33#include <ndn-cxx/mgmt/nfd/control-response.hpp>
Alexander Afanasyev847de402017-09-21 18:57:30 -040034#include <ndn-cxx/util/dummy-client-face.hpp>
35
Junxiao Shicb766862017-07-07 22:21:04 +000036namespace nfd {
37namespace tools {
38namespace tests {
39
40using namespace nfd::tests;
41using ndn::nfd::ControlParameters;
42
43/** \brief fixture to emulate NFD management
44 */
Davide Pesavento1d12d2f2019-03-22 12:44:14 -040045class MockNfdMgmtFixture : public UnitTestTimeFixture, public KeyChainFixture
Junxiao Shicb766862017-07-07 22:21:04 +000046{
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
Alexander Afanasyev847de402017-09-21 18:57:30 -040061 virtual
62 ~MockNfdMgmtFixture() = default;
63
Junxiao Shicb766862017-07-07 22:21:04 +000064protected: // ControlCommand
65 /** \brief check the Interest is a command with specified prefix
66 * \retval nullopt last Interest is not the expected command
67 * \return command parameters
68 */
Davide Pesavento87fc0f82018-04-11 23:43:51 -040069 static optional<ControlParameters>
Junxiao Shicb766862017-07-07 22:21:04 +000070 parseCommand(const Interest& interest, const Name& expectedPrefix)
71 {
72 if (!expectedPrefix.isPrefixOf(interest.getName())) {
Davide Pesavento87fc0f82018-04-11 23:43:51 -040073 return nullopt;
Junxiao Shicb766862017-07-07 22:21:04 +000074 }
75 return ControlParameters(interest.getName().at(expectedPrefix.size()).blockFromValue());
76 }
77
78 /** \brief send successful response to a command Interest
79 */
80 void
81 succeedCommand(const Interest& interest, const ControlParameters& parameters)
82 {
83 this->sendCommandReply(interest, 200, "OK", parameters.wireEncode());
84 }
85
86 /** \brief send failure response to a command Interest
87 */
88 void
89 failCommand(const Interest& interest, uint32_t code, const std::string& text)
90 {
91 this->sendCommandReply(interest, {code, text});
92 }
93
94 /** \brief send failure response to a command Interest
95 */
96 void
97 failCommand(const Interest& interest, uint32_t code, const std::string& text, const ControlParameters& body)
98 {
99 this->sendCommandReply(interest, code, text, body.wireEncode());
100 }
101
102protected: // StatusDataset
103 /** \brief send an empty dataset in reply to StatusDataset request
104 * \param prefix dataset prefix without version and segment
105 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
106 */
107 void
108 sendEmptyDataset(const Name& prefix)
109 {
110 this->sendDatasetReply(prefix, nullptr, 0);
111 }
112
113 /** \brief send one WireEncodable in reply to StatusDataset request
114 * \param prefix dataset prefix without version and segment
115 * \param payload payload block
116 * \note payload must fit in one Data
117 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
118 */
119 template<typename T>
120 void
121 sendDataset(const Name& prefix, const T& payload)
122 {
123 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
124
125 this->sendDatasetReply(prefix, payload.wireEncode());
126 }
127
128 /** \brief send two WireEncodables in reply to StatusDataset request
129 * \param prefix dataset prefix without version and segment
130 * \param payload1 first vector item
131 * \param payload2 second vector item
132 * \note all payloads must fit in one Data
133 * \pre Interest for dataset has been expressed, sendDataset has not been invoked
134 */
135 template<typename T1, typename T2>
136 void
137 sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
138 {
139 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
140 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
141
142 ndn::encoding::EncodingBuffer buffer;
143 payload2.wireEncode(buffer);
144 payload1.wireEncode(buffer);
145
146 this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
147 }
148
149private:
150 virtual void
151 processEventsOverride(time::milliseconds timeout)
152 {
Davide Pesavento17057442018-04-20 15:21:31 -0400153 if (timeout <= 0_ms) {
Junxiao Shicb766862017-07-07 22:21:04 +0000154 // give enough time to finish execution
Davide Pesavento17057442018-04-20 15:21:31 -0400155 timeout = 30_s;
Junxiao Shicb766862017-07-07 22:21:04 +0000156 }
Davide Pesavento17057442018-04-20 15:21:31 -0400157 this->advanceClocks(100_ms, timeout);
Junxiao Shicb766862017-07-07 22:21:04 +0000158 }
159
160 void
161 sendCommandReply(const Interest& interest, const ndn::nfd::ControlResponse& resp)
162 {
163 auto data = makeData(interest.getName());
164 data->setContent(resp.wireEncode());
165 face.receive(*data);
166 }
167
168 void
169 sendCommandReply(const Interest& interest, uint32_t code, const std::string& text,
170 const Block& body)
171 {
Davide Pesavento17057442018-04-20 15:21:31 -0400172 this->sendCommandReply(interest, ndn::nfd::ControlResponse(code, text).setBody(body));
Junxiao Shicb766862017-07-07 22:21:04 +0000173 }
174
175 /** \brief send a payload in reply to StatusDataset request
176 * \param name dataset prefix without version and segment
177 * \param contentArgs passed to Data::setContent
178 */
179 template<typename ...ContentArgs>
180 void
181 sendDatasetReply(Name name, ContentArgs&&... contentArgs)
182 {
183 name.appendVersion().appendSegment(0);
184
185 // These warnings assist in debugging when nfdc does not receive StatusDataset.
186 // They usually indicate a misspelled prefix or incorrect timing in the test case.
187 if (face.sentInterests.empty()) {
188 BOOST_WARN_MESSAGE(false, "no Interest expressed");
189 }
190 else {
191 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
192 "last Interest " << face.sentInterests.back().getName() <<
193 " cannot be satisfied by this Data " << name);
194 }
195
196 auto data = make_shared<Data>(name);
Davide Pesavento17057442018-04-20 15:21:31 -0400197 data->setFinalBlock(name[-1]);
Junxiao Shicb766862017-07-07 22:21:04 +0000198 data->setContent(std::forward<ContentArgs>(contentArgs)...);
199 this->signDatasetReply(*data);
200 face.receive(*data);
201 }
202
203 virtual void
204 signDatasetReply(Data& data)
205 {
206 signData(data);
207 }
208
209protected:
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