blob: 9658d049d10044235cef5c3685c7e69407a3ac63 [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 Pesaventod09d52b2023-02-15 14:15:06 -05003 * Copyright (c) 2013-2023 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);
Davide Pesaventofbea4fc2022-02-08 07:26:04 -050070 io::saveBuffer(t.blob, os, t.encoding);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040071 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);
Davide Pesaventofbea4fc2022-02-08 07:26:04 -050089 BOOST_CHECK_THROW(io::saveBuffer(buffer, out, io::NO_ENCODING), io::Error);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040090}
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);
Davide Pesaventofbea4fc2022-02-08 07:26:04 -050097 BOOST_CHECK_THROW(io::saveBuffer({}, ss, static_cast<io::IoEncoding>(5)), std::invalid_argument);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040098}
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 /**
Davide Pesaventoc860bd12022-10-04 03:23:43 -0400117 * \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 {
Davide Pesavento258d51a2022-02-27 21:26:28 -0500190 BOOST_TEST(block == "BB01EE"_block);
Junxiao Shic1779882016-08-17 01:59:23 +0000191 }
Junxiao Shic1779882016-08-17 01:59:23 +0000192};
193
Davide Pesavento949075a2021-10-17 22:07:07 -0400194class DecodableTypeThrow
195{
196public:
197 DecodableTypeThrow() = default;
198
199 explicit
200 DecodableTypeThrow(const Block& block)
201 {
202 wireDecode(block);
203 }
204
205 void
206 wireDecode(const Block&)
207 {
208 NDN_THROW(tlv::Error("decode error"));
209 }
210};
Junxiao Shic1779882016-08-17 01:59:23 +0000211
212BOOST_AUTO_TEST_CASE(LoadNoEncoding)
213{
214 this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
215 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::NO_ENCODING);
216 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400217
218 std::ifstream ifs(filename);
219 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::NO_ENCODING));
Junxiao Shic1779882016-08-17 01:59:23 +0000220}
221
222BOOST_AUTO_TEST_CASE(LoadBase64)
223{
224 this->writeFile<std::string>("uwHu\n"); // printf '\xBB\x01\xEE' | base64
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000225 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::BASE64);
226 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400227
228 std::ifstream ifs(filename);
229 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000230}
231
232BOOST_AUTO_TEST_CASE(LoadBase64Newline64)
233{
234 this->writeFile<std::string>(
235 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
236 "AAAAAAAAAAAA\n");
237 // printf '\x08\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
238 // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
239 // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
240 // \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' | base64
241 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
242 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400243
244 std::ifstream ifs(filename);
245 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000246}
247
248BOOST_AUTO_TEST_CASE(LoadBase64Newline32)
249{
250 this->writeFile<std::string>(
251 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
252 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
253 "AAAAAAAAAAAA\n");
254 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
255 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400256
257 std::ifstream ifs(filename);
258 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000259}
260
261BOOST_AUTO_TEST_CASE(LoadBase64NewlineEnd)
262{
263 this->writeFile<std::string>(
264 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
265 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
266 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400267
268 std::ifstream ifs(filename);
269 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000270}
271
272BOOST_AUTO_TEST_CASE(LoadBase64NoNewline)
273{
274 this->writeFile<std::string>(
275 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
276 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
Junxiao Shic1779882016-08-17 01:59:23 +0000277 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400278
279 std::ifstream ifs(filename);
280 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shic1779882016-08-17 01:59:23 +0000281}
282
283BOOST_AUTO_TEST_CASE(LoadHex)
284{
285 this->writeFile<std::string>("BB01EE");
286 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::HEX);
287 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400288
289 std::ifstream ifs(filename);
290 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::HEX));
Junxiao Shic1779882016-08-17 01:59:23 +0000291}
292
Davide Pesavento949075a2021-10-17 22:07:07 -0400293BOOST_AUTO_TEST_CASE(LoadDecodeException)
Junxiao Shic1779882016-08-17 01:59:23 +0000294{
295 this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
296 shared_ptr<DecodableTypeThrow> decoded;
297 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableTypeThrow>(filename, io::NO_ENCODING));
298 BOOST_CHECK(decoded == nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400299
300 std::ifstream ifs(filename);
301 BOOST_CHECK_THROW(io::loadTlv<DecodableTypeThrow>(ifs, io::NO_ENCODING), io::Error);
Junxiao Shic1779882016-08-17 01:59:23 +0000302}
303
304BOOST_AUTO_TEST_CASE(LoadNotHex)
305{
306 this->writeFile<std::string>("not-hex");
307 shared_ptr<DecodableType> decoded;
308 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::HEX));
309 BOOST_CHECK(decoded == nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400310
311 std::ifstream ifs(filename);
312 BOOST_CHECK_THROW(io::loadTlv<DecodableType>(ifs, io::HEX), io::Error);
313}
314
315BOOST_AUTO_TEST_CASE(LoadEmpty)
316{
317 this->writeFile<std::vector<uint8_t>>({});
318 shared_ptr<DecodableType> decoded;
319 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::NO_ENCODING));
320 BOOST_CHECK(decoded == nullptr);
321
322 std::ifstream ifs(filename);
323 BOOST_CHECK_THROW(io::loadTlv<DecodableType>(ifs, io::NO_ENCODING), io::Error);
Junxiao Shic1779882016-08-17 01:59:23 +0000324}
325
326BOOST_AUTO_TEST_CASE(LoadFileNotReadable)
327{
Junxiao Shic1779882016-08-17 01:59:23 +0000328 shared_ptr<DecodableType> decoded;
329 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::NO_ENCODING));
330 BOOST_CHECK(decoded == nullptr);
331}
332
333BOOST_AUTO_TEST_CASE(SaveNoEncoding)
334{
335 EncodableType encoded;
336 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::NO_ENCODING));
337 auto content = this->readFile<std::vector<uint8_t>>();
338 uint8_t expected[] = {0xAA, 0x01, 0xDD};
339 BOOST_CHECK_EQUAL_COLLECTIONS(content.begin(), content.end(),
340 expected, expected + sizeof(expected));
341}
342
343BOOST_AUTO_TEST_CASE(SaveBase64)
344{
345 EncodableType encoded;
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000346 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::BASE64));
Junxiao Shic1779882016-08-17 01:59:23 +0000347 auto content = this->readFile<std::string>();
348 BOOST_CHECK_EQUAL(content, "qgHd\n"); // printf '\xAA\x01\xDD' | base64
349}
350
351BOOST_AUTO_TEST_CASE(SaveHex)
352{
353 EncodableType encoded;
354 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::HEX));
355 auto content = this->readFile<std::string>();
356 BOOST_CHECK_EQUAL(content, "AA01DD");
357}
358
359BOOST_AUTO_TEST_CASE(SaveException)
360{
361 EncodableType encoded;
362 encoded.shouldThrow = true;
363 BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
364}
365
366BOOST_AUTO_TEST_CASE(SaveFileNotWritable)
367{
368 this->mkdir();
369 EncodableType encoded;
370 encoded.shouldThrow = true;
371 BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
372}
373
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400374BOOST_AUTO_TEST_SUITE_END() // FileIo
375
376class IdCertFixture : public FileIoFixture
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500377 , public KeyChainFixture
Junxiao Shic1779882016-08-17 01:59:23 +0000378{
379};
380
381BOOST_FIXTURE_TEST_CASE(IdCert, IdCertFixture)
382{
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500383 auto identity = m_keyChain.createIdentity("/TestIo/IdCert", RsaKeyParams());
Davide Pesaventod09d52b2023-02-15 14:15:06 -0500384 auto key = identity.getDefaultKey();
385 const auto& cert = key.getDefaultCertificate();
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800386 io::save(cert, filename);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800387
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500388 auto readCert = io::load<security::Certificate>(filename);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800389
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000390 BOOST_REQUIRE(readCert != nullptr);
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800391 BOOST_CHECK_EQUAL(cert.getName(), readCert->getName());
Ashlesh Gawandee84d1eb2018-01-04 20:46:44 -0600392
393 this->writeFile<std::string>("");
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500394 readCert = io::load<security::Certificate>(filename);
Ashlesh Gawandee84d1eb2018-01-04 20:46:44 -0600395 BOOST_REQUIRE(readCert == nullptr);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800396}
397
Junxiao Shic1779882016-08-17 01:59:23 +0000398BOOST_AUTO_TEST_SUITE_END() // TestIo
399BOOST_AUTO_TEST_SUITE_END() // Util
Yingdi Yuf50098d2014-02-26 14:26:29 -0800400
Spyridon Mastorakis429634f2015-02-19 17:35:33 -0800401} // namespace tests
Yingdi Yuf50098d2014-02-26 14:26:29 -0800402} // namespace ndn