blob: 485b03033ee2859c033cbd0a15ce4917693aa0ed [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
29#include <ndn-cxx/util/dummy-client-face.hpp>
30
31#include "tests/test-common.hpp"
32#include "tests/identity-management-fixture.hpp"
33
34namespace nfd {
35namespace tools {
36namespace nfdc {
37namespace tests {
38
39using namespace nfd::tests;
Junxiao Shi1d7fef52017-02-02 05:33:14 +000040using ndn::nfd::ControlParameters;
Junxiao Shi1f481fa2017-01-26 15:14:43 +000041
42/** \brief fixture to emulate NFD management
43 */
44class MockNfdMgmtFixture : public IdentityManagementTimeFixture
45{
46protected:
47 MockNfdMgmtFixture()
48 : face(g_io, m_keyChain,
49 {true, false, bind(&MockNfdMgmtFixture::processEventsOverride, this, _1)})
50 {
51 face.onSendInterest.connect([=] (const Interest& interest) {
52 g_io.post([=] {
53 if (processInterest != nullptr) {
54 processInterest(interest);
55 }
56 });
57 });
58 }
59
Junxiao Shi1d7fef52017-02-02 05:33:14 +000060protected: // ControlCommand
61 /** \brief check the last Interest is a command with specified prefix
62 * \retval nullopt last Interest is not the expected command
63 * \return command parameters
64 */
65 ndn::optional<ControlParameters>
Yanbiao Li58ba3f92017-02-15 14:27:18 +000066 getCommand(const Name& expectedPrefix) const
Junxiao Shi1d7fef52017-02-02 05:33:14 +000067 {
68 if (face.sentInterests.empty() ||
69 !expectedPrefix.isPrefixOf(face.sentInterests.back().getName())) {
70 return ndn::nullopt;
71 }
72 return ControlParameters(face.sentInterests.back().getName()
73 .at(expectedPrefix.size()).blockFromValue());
74 }
75
76 /** \brief respond to the last command
77 * \pre last Interest is a command
78 */
79 void
80 succeedCommand(const ControlParameters& parameters)
81 {
82 ndn::nfd::ControlResponse resp(200, "OK");
83 resp.setBody(parameters.wireEncode());
84 this->sendCommandReply(resp);
85 }
86
87 /** \brief respond to the last command
88 * \pre last Interest is a command
89 */
90 void
91 failCommand(uint32_t code, const std::string& text)
92 {
93 this->sendCommandReply({code, text});
94 }
95
Yanbiao Li58ba3f92017-02-15 14:27:18 +000096 void
97 failCommand(uint32_t code, const std::string& text, const ControlParameters& resp)
98 {
99 this->sendCommandReply(ndn::nfd::ControlResponse(code, text).setBody(resp.wireEncode()));
100 }
101
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000102protected: // StatusDataset
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000103 /** \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 {
153 if (timeout <= time::milliseconds::zero()) {
154 // give enough time to finish execution
155 timeout = time::seconds(30);
156 }
157 this->advanceClocks(time::milliseconds(100), timeout);
158 }
159
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000160 void
161 sendCommandReply(const ndn::nfd::ControlResponse& resp)
162 {
163 auto data = makeData(face.sentInterests.back().getName());
164 data->setContent(resp.wireEncode());
165 face.receive(*data);
166 }
167
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000168 /** \brief send a payload in reply to StatusDataset request
169 * \param name dataset prefix without version and segment
170 * \param contentArgs passed to Data::setContent
171 */
172 template<typename ...ContentArgs>
173 void
174 sendDatasetReply(Name name, ContentArgs&&... contentArgs)
175 {
176 name.appendVersion().appendSegment(0);
177
178 // These warnings assist in debugging when nfdc does not receive StatusDataset.
179 // They usually indicate a misspelled prefix or incorrect timing in the test case.
180 if (face.sentInterests.empty()) {
181 BOOST_WARN_MESSAGE(false, "no Interest expressed");
182 }
183 else {
184 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
185 "last Interest " << face.sentInterests.back().getName() <<
186 " cannot be satisfied by this Data " << name);
187 }
188
189 auto data = make_shared<Data>(name);
190 data->setFinalBlockId(name[-1]);
191 data->setContent(std::forward<ContentArgs>(contentArgs)...);
192 this->signDatasetReply(*data);
193 face.receive(*data);
194 }
195
196 virtual void
197 signDatasetReply(Data& data)
198 {
199 signData(data);
200 }
201
202protected:
203 ndn::util::DummyClientFace face;
204 std::function<void(const Interest&)> processInterest;
205};
206
207} // namespace tests
208} // namespace nfdc
209} // namespace tools
210} // namespace nfd
211
Junxiao Shi1d7fef52017-02-02 05:33:14 +0000212#define MOCK_NFD_MGMT_REQUIRE_LAST_COMMAND_IS(expectedPrefix) \
213 [this] { \
214 BOOST_REQUIRE_MESSAGE(!face.sentInterests.empty(), "no Interest expressed"); \
215 auto params = this->getCommand(expectedPrefix); \
216 BOOST_REQUIRE_MESSAGE(params, "last Interest " << face.sentInterests.back().getName() << \
217 " does not match command prefix " << expectedPrefix); \
218 return *params; \
219 } ()
220
Junxiao Shi1f481fa2017-01-26 15:14:43 +0000221#endif // NFD_TESTS_TOOLS_NFDC_MOCK_NFD_MGMT_FIXTURE_HPP