blob: 38d0d641c2e0971019e4e48d47847d17845ead77 [file] [log] [blame]
Junxiao Shi38f4ce92016-08-04 10:01:52 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2016, 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_NFD_STATUS_MODULE_FIXTURE_HPP
27#define NFD_TESTS_TOOLS_NFD_STATUS_MODULE_FIXTURE_HPP
28
29#include "nfd-status/module.hpp"
30#include <ndn-cxx/security/validator-null.hpp>
31#include <ndn-cxx/util/dummy-client-face.hpp>
32
33#include "tests/test-common.hpp"
34#include "tests/identity-management-fixture.hpp"
35
36namespace nfd {
37namespace tools {
38namespace nfd_status {
39namespace tests {
40
41using namespace nfd::tests;
42using ndn::Face;
43using ndn::KeyChain;
44using ndn::Validator;
45using ndn::ValidatorNull;
46using ndn::util::DummyClientFace;
47using boost::test_tools::output_test_stream;
48
49class MakeValidatorNull
50{
51public:
52 unique_ptr<ValidatorNull>
53 operator()(Face&, KeyChain&) const
54 {
55 return make_unique<ValidatorNull>();
56 };
57};
58
59/** \brief fixture to test a \p Module
60 * \tparam MODULE a subclass of \p Module
61 * \tparam MakeValidator a callable to make a Validator for use in \p controller;
62 * MakeValidator()(Face&, KeyChain&) should return a unique_ptr
63 * to Validator or its subclass
64 */
65template<typename MODULE, typename MakeValidator = MakeValidatorNull>
66class ModuleFixture : public UnitTestTimeFixture
67 , public IdentityManagementFixture
68{
69protected:
70 typedef typename std::result_of<MakeValidator(Face&, KeyChain&)>::type ValidatorUniquePtr;
71
72 ModuleFixture()
73 : face(g_io, m_keyChain)
74 , validator(MakeValidator()(face, m_keyChain))
75 , controller(face, m_keyChain, *validator)
76 , nFetchStatusSuccess(0)
77 {
78 }
79
80protected: // status fetching
81 /** \brief start fetching status
82 *
83 * A test case should call \p fetchStatus, \p sendDataset, and \p prepareStatusOutput
84 * in this order, and then check \p statusXml and \p statusText contain the correct outputs.
85 * No advanceClocks is needed in between, as they are handled by the fixture.
86 */
87 void
88 fetchStatus()
89 {
90 nFetchStatusSuccess = 0;
91 module.fetchStatus(controller, [this] { ++nFetchStatusSuccess; },
92 [this] (uint32_t code, const std::string& reason) {
93 BOOST_FAIL("fetchStatus failure " << code << " " << reason);
94 },
95 CommandOptions());
96 this->advanceClocks(time::milliseconds(1));
97 }
98
99 /** \brief send one WireEncodable in reply to StatusDataset request
100 * \param prefix dataset prefix without version and segment
101 * \param payload payload block
102 * \note payload must fit in one Data
103 * \pre fetchStatus has been invoked, sendDataset has not been invoked
104 */
105 template<typename T>
106 void
107 sendDataset(const Name& prefix, const T& payload)
108 {
109 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T>));
110
111 this->sendDatasetReply(prefix, payload.wireEncode());
112 }
113
114 /** \brief send two WireEncodables in reply to StatusDataset request
115 * \param prefix dataset prefix without version and segment
116 * \param payload1 first vector item
117 * \param payload2 second vector item
118 * \note all payloads must fit in one Data
119 * \pre fetchStatus has been invoked, sendDataset has not been invoked
120 */
121 template<typename T1, typename T2>
122 void
123 sendDataset(const Name& prefix, const T1& payload1, const T2& payload2)
124 {
125 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T1>));
126 BOOST_CONCEPT_ASSERT((ndn::WireEncodable<T2>));
127
128 ndn::encoding::EncodingBuffer buffer;
129 payload2.wireEncode(buffer);
130 payload1.wireEncode(buffer);
131
132 this->sendDatasetReply(prefix, buffer.buf(), buffer.size());
133 }
134
135 /** \brief prepare status output as XML and text
136 * \pre sendDataset has been invoked
137 */
138 void
139 prepareStatusOutput()
140 {
141 this->advanceClocks(time::milliseconds(1));
142 BOOST_REQUIRE_EQUAL(nFetchStatusSuccess, 1);
143
144 statusXml.str("");
145 module.formatStatusXml(statusXml);
146 statusText.str("");
147 module.formatStatusText(statusText);
148 }
149
150private:
151 /** \brief send a payload in reply to StatusDataset request
152 * \param prefix dataset prefix without version and segment
153 * \param contentArgs passed to Data::setContent
154 */
155 template<typename ...ContentArgs>
156 void
157 sendDatasetReply(const Name& prefix, ContentArgs&&...contentArgs)
158 {
159 Name name = prefix;
160 name.appendVersion().appendSegment(0);
161
162 // These warnings assist in debugging a `nFetchStatusSuccess != 1` check failure.
163 // They usually indicate a misspelled prefix or incorrect timing in the test case.
164 if (face.sentInterests.size() < 1) {
165 BOOST_WARN_MESSAGE(false, "no Interest expressed");
166 }
167 else {
168 BOOST_WARN_MESSAGE(face.sentInterests.back().getName().isPrefixOf(name),
169 "last Interest " << face.sentInterests.back().getName() <<
170 " cannot be satisfied by this Data " << name);
171 }
172
173 auto data = make_shared<Data>(name);
174 data->setFinalBlockId(name[-1]);
175 data->setContent(std::forward<ContentArgs>(contentArgs)...);
176 this->signDatasetReply(*data);
177 face.receive(*data);
178 }
179
180 virtual void
181 signDatasetReply(Data& data)
182 {
183 signData(data);
184 }
185
186protected:
187 DummyClientFace face;
188 ValidatorUniquePtr validator;
189 Controller controller;
190
191 MODULE module;
192
193 int nFetchStatusSuccess;
194 output_test_stream statusXml;
195 output_test_stream statusText;
196};
197
198/** \brief strips leading spaces on every line in expected XML
199 *
200 * This allows expected XML to be written as:
201 * \code
202 * const std::string STATUS_XML = stripXmlSpaces(R"XML(
203 * <rootElement>
204 * <element>value</element>
205 * </rootElement>
206 * )XML");
207 * \endcode
208 * And \p STATUS_XML would be assigned:
209 * \code
210 * "<rootElement><element>value</element></rootElement>"
211 * \endcode
212 */
213inline std::string
214stripXmlSpaces(const std::string& xml)
215{
216 std::string s;
217 bool isSkipping = true;
218 std::copy_if(xml.begin(), xml.end(), std::back_inserter(s),
219 [&isSkipping] (char ch) {
220 if (ch == '\n') {
221 isSkipping = true;
222 }
223 else if (ch != ' ') {
224 isSkipping = false;
225 }
226 return !isSkipping;
227 });
228 return s;
229}
230
231} // namespace tests
232} // namespace nfd_status
233} // namespace tools
234} // namespace nfd
235
236#endif // NFD_TESTS_TOOLS_NFD_STATUS_MODULE_FIXTURE_HPP