blob: 8b19fe385f02d3e408fc2cf518bcb4196280d5ac [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoa84f4642017-08-23 16:14:51 -04002/*
Davide Pesavento949075a2021-10-17 22:07:07 -04003 * Copyright (c) 2013-2021 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Yingdi Yuf50098d2014-02-26 14:26:29 -080020 */
21
Davide Pesavento7e780642018-11-24 15:51:34 -050022#include "ndn-cxx/util/io.hpp"
Yingdi Yuf50098d2014-02-26 14:26:29 -080023
Davide Pesavento7e780642018-11-24 15:51:34 -050024#include "tests/boost-test.hpp"
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -050025#include "tests/key-chain-fixture.hpp"
Davide Pesaventoa84f4642017-08-23 16:14:51 -040026
Junxiao Shic1779882016-08-17 01:59:23 +000027#include <boost/filesystem.hpp>
Davide Pesavento6c6e3852019-08-05 20:20:35 -040028#include <boost/mpl/vector.hpp>
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -070029
Yingdi Yuf50098d2014-02-26 14:26:29 -080030namespace ndn {
Spyridon Mastorakis429634f2015-02-19 17:35:33 -080031namespace tests {
Yingdi Yuf50098d2014-02-26 14:26:29 -080032
Davide Pesavento6c6e3852019-08-05 20:20:35 -040033BOOST_AUTO_TEST_SUITE(Util)
34BOOST_AUTO_TEST_SUITE(TestIo)
35
36struct NoEncoding
37{
38 const io::IoEncoding encoding{io::NO_ENCODING};
39 const std::vector<uint8_t> blob{0xd1, 0x0, 0xb0, 0x1a};
40 std::istringstream stream{std::string("\xd1\x00\xb0\x1a", 4), std::ios_base::binary};
41};
42
43struct Base64Encoding
44{
45 const io::IoEncoding encoding = io::BASE64;
46 const std::vector<uint8_t> blob{0x42, 0x61, 0x73, 0x65, 0x36, 0x34, 0x45, 0x6e, 0x63};
47 std::istringstream stream{"QmFzZTY0RW5j\n", std::ios_base::binary};
48};
49
50struct HexEncoding
51{
52 const io::IoEncoding encoding = io::HEX;
53 const std::vector<uint8_t> blob{0x48, 0x65, 0x78, 0x45, 0x6e, 0x63};
54 std::istringstream stream{"486578456E63", std::ios_base::binary};
55};
56
57using Encodings = boost::mpl::vector<NoEncoding, Base64Encoding, HexEncoding>;
58
59BOOST_AUTO_TEST_CASE_TEMPLATE(LoadBuffer, T, Encodings)
60{
61 T t;
62 shared_ptr<Buffer> buf = io::loadBuffer(t.stream, t.encoding);
63 BOOST_CHECK_EQUAL_COLLECTIONS(buf->begin(), buf->end(), t.blob.begin(), t.blob.end());
64}
65
66BOOST_AUTO_TEST_CASE_TEMPLATE(SaveBuffer, T, Encodings)
67{
68 T t;
69 std::ostringstream os(std::ios_base::binary);
70 io::saveBuffer(t.blob.data(), t.blob.size(), os, t.encoding);
71 BOOST_CHECK_EQUAL(os.str(), t.stream.str());
72}
73
74BOOST_AUTO_TEST_CASE(LoadBufferException)
75{
76 std::ifstream in("this-file-does-not-exist", std::ios_base::binary);
77 BOOST_CHECK_THROW(io::loadBuffer(in, io::NO_ENCODING), io::Error);
78}
79
80BOOST_AUTO_TEST_CASE(SaveBufferException)
81{
82 class NullStreambuf : public std::streambuf
83 {
84 };
85
86 NullStreambuf nullbuf;
87 std::ostream out(&nullbuf);
88 const Buffer buffer(1);
89 BOOST_CHECK_THROW(io::saveBuffer(buffer.data(), buffer.size(), out, io::NO_ENCODING), io::Error);
90}
91
92BOOST_AUTO_TEST_CASE(UnknownIoEncoding)
93{
94 std::stringstream ss;
Davide Pesavento949075a2021-10-17 22:07:07 -040095 BOOST_CHECK_THROW(io::loadTlv<Name>(ss, static_cast<io::IoEncoding>(5)), std::invalid_argument);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040096 BOOST_CHECK_THROW(io::loadBuffer(ss, static_cast<io::IoEncoding>(5)), std::invalid_argument);
97 BOOST_CHECK_THROW(io::saveBuffer(nullptr, 0, ss, static_cast<io::IoEncoding>(5)), std::invalid_argument);
98}
99
100class FileIoFixture
Yingdi Yuf50098d2014-02-26 14:26:29 -0800101{
Junxiao Shic1779882016-08-17 01:59:23 +0000102protected:
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400103 FileIoFixture()
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500104 : filepath(boost::filesystem::path(UNIT_TESTS_TMPDIR) / "TestIo")
Junxiao Shic1779882016-08-17 01:59:23 +0000105 , filename(filepath.string())
106 {
107 boost::filesystem::create_directories(filepath.parent_path());
108 }
109
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400110 ~FileIoFixture()
Junxiao Shic1779882016-08-17 01:59:23 +0000111 {
112 boost::system::error_code ec;
113 boost::filesystem::remove(filepath, ec); // ignore error
114 }
115
Davide Pesavento949075a2021-10-17 22:07:07 -0400116 /**
117 * \brief create a directory at `filepath`, so that it's neither readable nor writable as a file
Junxiao Shic1779882016-08-17 01:59:23 +0000118 */
119 void
120 mkdir() const
121 {
122 boost::filesystem::create_directory(filepath);
123 }
124
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400125 template<typename Container>
Junxiao Shic1779882016-08-17 01:59:23 +0000126 Container
127 readFile() const
128 {
129 Container container;
130 std::ifstream fs(filename, std::ios_base::binary);
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400131 BOOST_REQUIRE_MESSAGE(fs, "error opening file");
Junxiao Shic1779882016-08-17 01:59:23 +0000132 char ch;
133 while (fs.get(ch)) {
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400134 container.push_back(static_cast<typename Container::value_type>(ch));
Junxiao Shic1779882016-08-17 01:59:23 +0000135 }
136 return container;
137 }
138
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400139 template<typename Container>
Junxiao Shic1779882016-08-17 01:59:23 +0000140 void
141 writeFile(const Container& content) const
142 {
143 std::ofstream fs(filename, std::ios_base::binary);
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400144 BOOST_REQUIRE_MESSAGE(fs, "error opening file");
145 for (auto ch : content) {
Junxiao Shic1779882016-08-17 01:59:23 +0000146 fs.put(static_cast<char>(ch));
147 }
Junxiao Shic1779882016-08-17 01:59:23 +0000148 BOOST_REQUIRE_MESSAGE(fs, "error writing file");
149 }
150
151protected:
152 const boost::filesystem::path filepath;
153 const std::string filename;
154};
155
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400156BOOST_FIXTURE_TEST_SUITE(FileIo, FileIoFixture)
Junxiao Shic1779882016-08-17 01:59:23 +0000157
158class EncodableType
159{
160public:
Junxiao Shic1779882016-08-17 01:59:23 +0000161 Block
162 wireEncode() const
163 {
164 if (shouldThrow) {
Davide Pesavento923ba442019-02-12 22:00:38 -0500165 NDN_THROW(tlv::Error("encode error"));
Junxiao Shic1779882016-08-17 01:59:23 +0000166 }
167
168 // block will be 0xAA, 0x01, 0xDD
169 return makeNonNegativeIntegerBlock(0xAA, 0xDD);
170 }
171
172public:
173 bool shouldThrow = false;
174};
175
Davide Pesavento949075a2021-10-17 22:07:07 -0400176class DecodableType
Junxiao Shic1779882016-08-17 01:59:23 +0000177{
178public:
Davide Pesavento949075a2021-10-17 22:07:07 -0400179 DecodableType() = default;
Junxiao Shi435bb552016-09-04 03:14:47 +0000180
181 explicit
Davide Pesavento949075a2021-10-17 22:07:07 -0400182 DecodableType(const Block& block)
Junxiao Shic1779882016-08-17 01:59:23 +0000183 {
Davide Pesavento949075a2021-10-17 22:07:07 -0400184 wireDecode(block);
Junxiao Shi435bb552016-09-04 03:14:47 +0000185 }
Junxiao Shic1779882016-08-17 01:59:23 +0000186
187 void
Junxiao Shi435bb552016-09-04 03:14:47 +0000188 wireDecode(const Block& block)
Junxiao Shic1779882016-08-17 01:59:23 +0000189 {
Junxiao Shic1779882016-08-17 01:59:23 +0000190 // block must be 0xBB, 0x01, 0xEE
191 BOOST_CHECK_EQUAL(block.type(), 0xBB);
192 BOOST_REQUIRE_EQUAL(block.value_size(), 1);
193 BOOST_CHECK_EQUAL(block.value()[0], 0xEE);
194 }
Junxiao Shic1779882016-08-17 01:59:23 +0000195};
196
Davide Pesavento949075a2021-10-17 22:07:07 -0400197class DecodableTypeThrow
198{
199public:
200 DecodableTypeThrow() = default;
201
202 explicit
203 DecodableTypeThrow(const Block& block)
204 {
205 wireDecode(block);
206 }
207
208 void
209 wireDecode(const Block&)
210 {
211 NDN_THROW(tlv::Error("decode error"));
212 }
213};
Junxiao Shic1779882016-08-17 01:59:23 +0000214
215BOOST_AUTO_TEST_CASE(LoadNoEncoding)
216{
217 this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
218 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::NO_ENCODING);
219 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400220
221 std::ifstream ifs(filename);
222 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::NO_ENCODING));
Junxiao Shic1779882016-08-17 01:59:23 +0000223}
224
225BOOST_AUTO_TEST_CASE(LoadBase64)
226{
227 this->writeFile<std::string>("uwHu\n"); // printf '\xBB\x01\xEE' | base64
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000228 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::BASE64);
229 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400230
231 std::ifstream ifs(filename);
232 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000233}
234
235BOOST_AUTO_TEST_CASE(LoadBase64Newline64)
236{
237 this->writeFile<std::string>(
238 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
239 "AAAAAAAAAAAA\n");
240 // printf '\x08\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
241 // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
242 // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
243 // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | base64
244 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
245 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400246
247 std::ifstream ifs(filename);
248 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000249}
250
251BOOST_AUTO_TEST_CASE(LoadBase64Newline32)
252{
253 this->writeFile<std::string>(
254 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
255 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
256 "AAAAAAAAAAAA\n");
257 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
258 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400259
260 std::ifstream ifs(filename);
261 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000262}
263
264BOOST_AUTO_TEST_CASE(LoadBase64NewlineEnd)
265{
266 this->writeFile<std::string>(
267 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
268 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
269 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400270
271 std::ifstream ifs(filename);
272 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000273}
274
275BOOST_AUTO_TEST_CASE(LoadBase64NoNewline)
276{
277 this->writeFile<std::string>(
278 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
279 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
Junxiao Shic1779882016-08-17 01:59:23 +0000280 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400281
282 std::ifstream ifs(filename);
283 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shic1779882016-08-17 01:59:23 +0000284}
285
286BOOST_AUTO_TEST_CASE(LoadHex)
287{
288 this->writeFile<std::string>("BB01EE");
289 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::HEX);
290 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400291
292 std::ifstream ifs(filename);
293 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::HEX));
Junxiao Shic1779882016-08-17 01:59:23 +0000294}
295
Davide Pesavento949075a2021-10-17 22:07:07 -0400296BOOST_AUTO_TEST_CASE(LoadDecodeException)
Junxiao Shic1779882016-08-17 01:59:23 +0000297{
298 this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
299 shared_ptr<DecodableTypeThrow> decoded;
300 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableTypeThrow>(filename, io::NO_ENCODING));
301 BOOST_CHECK(decoded == nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400302
303 std::ifstream ifs(filename);
304 BOOST_CHECK_THROW(io::loadTlv<DecodableTypeThrow>(ifs, io::NO_ENCODING), io::Error);
Junxiao Shic1779882016-08-17 01:59:23 +0000305}
306
307BOOST_AUTO_TEST_CASE(LoadNotHex)
308{
309 this->writeFile<std::string>("not-hex");
310 shared_ptr<DecodableType> decoded;
311 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::HEX));
312 BOOST_CHECK(decoded == nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400313
314 std::ifstream ifs(filename);
315 BOOST_CHECK_THROW(io::loadTlv<DecodableType>(ifs, io::HEX), io::Error);
316}
317
318BOOST_AUTO_TEST_CASE(LoadEmpty)
319{
320 this->writeFile<std::vector<uint8_t>>({});
321 shared_ptr<DecodableType> decoded;
322 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::NO_ENCODING));
323 BOOST_CHECK(decoded == nullptr);
324
325 std::ifstream ifs(filename);
326 BOOST_CHECK_THROW(io::loadTlv<DecodableType>(ifs, io::NO_ENCODING), io::Error);
Junxiao Shic1779882016-08-17 01:59:23 +0000327}
328
329BOOST_AUTO_TEST_CASE(LoadFileNotReadable)
330{
Junxiao Shic1779882016-08-17 01:59:23 +0000331 shared_ptr<DecodableType> decoded;
332 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::NO_ENCODING));
333 BOOST_CHECK(decoded == nullptr);
334}
335
336BOOST_AUTO_TEST_CASE(SaveNoEncoding)
337{
338 EncodableType encoded;
339 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::NO_ENCODING));
340 auto content = this->readFile<std::vector<uint8_t>>();
341 uint8_t expected[] = {0xAA, 0x01, 0xDD};
342 BOOST_CHECK_EQUAL_COLLECTIONS(content.begin(), content.end(),
343 expected, expected + sizeof(expected));
344}
345
346BOOST_AUTO_TEST_CASE(SaveBase64)
347{
348 EncodableType encoded;
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000349 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::BASE64));
Junxiao Shic1779882016-08-17 01:59:23 +0000350 auto content = this->readFile<std::string>();
351 BOOST_CHECK_EQUAL(content, "qgHd\n"); // printf '\xAA\x01\xDD' | base64
352}
353
354BOOST_AUTO_TEST_CASE(SaveHex)
355{
356 EncodableType encoded;
357 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::HEX));
358 auto content = this->readFile<std::string>();
359 BOOST_CHECK_EQUAL(content, "AA01DD");
360}
361
362BOOST_AUTO_TEST_CASE(SaveException)
363{
364 EncodableType encoded;
365 encoded.shouldThrow = true;
366 BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
367}
368
369BOOST_AUTO_TEST_CASE(SaveFileNotWritable)
370{
371 this->mkdir();
372 EncodableType encoded;
373 encoded.shouldThrow = true;
374 BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
375}
376
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400377BOOST_AUTO_TEST_SUITE_END() // FileIo
378
379class IdCertFixture : public FileIoFixture
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500380 , public KeyChainFixture
Junxiao Shic1779882016-08-17 01:59:23 +0000381{
382};
383
384BOOST_FIXTURE_TEST_CASE(IdCert, IdCertFixture)
385{
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500386 auto identity = m_keyChain.createIdentity("/TestIo/IdCert", RsaKeyParams());
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800387 const auto& cert = identity.getDefaultKey().getDefaultCertificate();
388 io::save(cert, filename);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800389
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500390 auto readCert = io::load<security::Certificate>(filename);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800391
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000392 BOOST_REQUIRE(readCert != nullptr);
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800393 BOOST_CHECK_EQUAL(cert.getName(), readCert->getName());
Ashlesh Gawandee84d1eb2018-01-04 20:46:44 -0600394
395 this->writeFile<std::string>("");
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500396 readCert = io::load<security::Certificate>(filename);
Ashlesh Gawandee84d1eb2018-01-04 20:46:44 -0600397 BOOST_REQUIRE(readCert == nullptr);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800398}
399
Junxiao Shic1779882016-08-17 01:59:23 +0000400BOOST_AUTO_TEST_SUITE_END() // TestIo
401BOOST_AUTO_TEST_SUITE_END() // Util
Yingdi Yuf50098d2014-02-26 14:26:29 -0800402
Spyridon Mastorakis429634f2015-02-19 17:35:33 -0800403} // namespace tests
Yingdi Yuf50098d2014-02-26 14:26:29 -0800404} // namespace ndn