blob: 2dc303db101650d9294731c8dc080970d25ce5ee [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 Pesavento0c526032024-01-31 21:14:01 -05003 * Copyright (c) 2013-2024 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 Pesavento49e1e872023-11-11 00:45:23 -050028#include <boost/mp11/list.hpp>
Alexander Afanasyevb1db7c62014-04-03 14:57:25 -070029
Davide Pesavento47ce2ee2023-05-09 01:33:33 -040030namespace ndn::tests {
Yingdi Yuf50098d2014-02-26 14:26:29 -080031
Davide Pesavento6c6e3852019-08-05 20:20:35 -040032BOOST_AUTO_TEST_SUITE(Util)
33BOOST_AUTO_TEST_SUITE(TestIo)
34
35struct NoEncoding
36{
Davide Pesavento0c526032024-01-31 21:14:01 -050037 static constexpr io::IoEncoding encoding = io::NO_ENCODING;
38 static inline const std::vector<uint8_t> blob{0xd1, 0x0, 0xb0, 0x1a};
Davide Pesavento6c6e3852019-08-05 20:20:35 -040039 std::istringstream stream{std::string("\xd1\x00\xb0\x1a", 4), std::ios_base::binary};
40};
41
42struct Base64Encoding
43{
Davide Pesavento0c526032024-01-31 21:14:01 -050044 static constexpr io::IoEncoding encoding = io::BASE64;
45 static inline const std::vector<uint8_t> blob{0x42, 0x61, 0x73, 0x65, 0x36, 0x34, 0x45, 0x6e, 0x63};
Davide Pesavento6c6e3852019-08-05 20:20:35 -040046 std::istringstream stream{"QmFzZTY0RW5j\n", std::ios_base::binary};
47};
48
49struct HexEncoding
50{
Davide Pesavento0c526032024-01-31 21:14:01 -050051 static constexpr io::IoEncoding encoding = io::HEX;
52 static inline const std::vector<uint8_t> blob{0x48, 0x65, 0x78, 0x45, 0x6e, 0x63};
Davide Pesavento6c6e3852019-08-05 20:20:35 -040053 std::istringstream stream{"486578456E63", std::ios_base::binary};
54};
55
Davide Pesavento49e1e872023-11-11 00:45:23 -050056using Encodings = boost::mp11::mp_list<NoEncoding, Base64Encoding, HexEncoding>;
Davide Pesavento6c6e3852019-08-05 20:20:35 -040057
58BOOST_AUTO_TEST_CASE_TEMPLATE(LoadBuffer, T, Encodings)
59{
60 T t;
Davide Pesavento0c526032024-01-31 21:14:01 -050061 shared_ptr<Buffer> buf = io::loadBuffer(t.stream, T::encoding);
62 BOOST_CHECK_EQUAL_COLLECTIONS(buf->begin(), buf->end(), T::blob.begin(), T::blob.end());
Davide Pesavento6c6e3852019-08-05 20:20:35 -040063}
64
65BOOST_AUTO_TEST_CASE_TEMPLATE(SaveBuffer, T, Encodings)
66{
67 T t;
68 std::ostringstream os(std::ios_base::binary);
Davide Pesavento0c526032024-01-31 21:14:01 -050069 io::saveBuffer(T::blob, os, T::encoding);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040070 BOOST_CHECK_EQUAL(os.str(), t.stream.str());
71}
72
73BOOST_AUTO_TEST_CASE(LoadBufferException)
74{
75 std::ifstream in("this-file-does-not-exist", std::ios_base::binary);
76 BOOST_CHECK_THROW(io::loadBuffer(in, io::NO_ENCODING), io::Error);
77}
78
79BOOST_AUTO_TEST_CASE(SaveBufferException)
80{
81 class NullStreambuf : public std::streambuf
82 {
83 };
84
85 NullStreambuf nullbuf;
86 std::ostream out(&nullbuf);
87 const Buffer buffer(1);
Davide Pesaventofbea4fc2022-02-08 07:26:04 -050088 BOOST_CHECK_THROW(io::saveBuffer(buffer, out, io::NO_ENCODING), io::Error);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040089}
90
91BOOST_AUTO_TEST_CASE(UnknownIoEncoding)
92{
93 std::stringstream ss;
Davide Pesavento949075a2021-10-17 22:07:07 -040094 BOOST_CHECK_THROW(io::loadTlv<Name>(ss, static_cast<io::IoEncoding>(5)), std::invalid_argument);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040095 BOOST_CHECK_THROW(io::loadBuffer(ss, static_cast<io::IoEncoding>(5)), std::invalid_argument);
Davide Pesaventofbea4fc2022-02-08 07:26:04 -050096 BOOST_CHECK_THROW(io::saveBuffer({}, ss, static_cast<io::IoEncoding>(5)), std::invalid_argument);
Davide Pesavento6c6e3852019-08-05 20:20:35 -040097}
98
99class FileIoFixture
Yingdi Yuf50098d2014-02-26 14:26:29 -0800100{
Junxiao Shic1779882016-08-17 01:59:23 +0000101protected:
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400102 FileIoFixture()
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500103 : filepath(boost::filesystem::path(UNIT_TESTS_TMPDIR) / "TestIo")
Junxiao Shic1779882016-08-17 01:59:23 +0000104 , filename(filepath.string())
105 {
106 boost::filesystem::create_directories(filepath.parent_path());
107 }
108
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400109 ~FileIoFixture()
Junxiao Shic1779882016-08-17 01:59:23 +0000110 {
111 boost::system::error_code ec;
112 boost::filesystem::remove(filepath, ec); // ignore error
113 }
114
Davide Pesavento949075a2021-10-17 22:07:07 -0400115 /**
Davide Pesaventoc860bd12022-10-04 03:23:43 -0400116 * \brief Create a directory at `filepath`, so that it's neither readable nor writable as a file.
Junxiao Shic1779882016-08-17 01:59:23 +0000117 */
118 void
119 mkdir() const
120 {
121 boost::filesystem::create_directory(filepath);
122 }
123
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400124 template<typename Container>
Junxiao Shic1779882016-08-17 01:59:23 +0000125 Container
126 readFile() const
127 {
128 Container container;
129 std::ifstream fs(filename, std::ios_base::binary);
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400130 BOOST_REQUIRE_MESSAGE(fs, "error opening file");
Junxiao Shic1779882016-08-17 01:59:23 +0000131 char ch;
132 while (fs.get(ch)) {
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400133 container.push_back(static_cast<typename Container::value_type>(ch));
Junxiao Shic1779882016-08-17 01:59:23 +0000134 }
135 return container;
136 }
137
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400138 template<typename Container>
Junxiao Shic1779882016-08-17 01:59:23 +0000139 void
140 writeFile(const Container& content) const
141 {
142 std::ofstream fs(filename, std::ios_base::binary);
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400143 BOOST_REQUIRE_MESSAGE(fs, "error opening file");
144 for (auto ch : content) {
Junxiao Shic1779882016-08-17 01:59:23 +0000145 fs.put(static_cast<char>(ch));
146 }
Junxiao Shic1779882016-08-17 01:59:23 +0000147 BOOST_REQUIRE_MESSAGE(fs, "error writing file");
148 }
149
150protected:
151 const boost::filesystem::path filepath;
152 const std::string filename;
153};
154
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400155BOOST_FIXTURE_TEST_SUITE(FileIo, FileIoFixture)
Junxiao Shic1779882016-08-17 01:59:23 +0000156
157class EncodableType
158{
159public:
Junxiao Shic1779882016-08-17 01:59:23 +0000160 Block
161 wireEncode() const
162 {
163 if (shouldThrow) {
Davide Pesavento923ba442019-02-12 22:00:38 -0500164 NDN_THROW(tlv::Error("encode error"));
Junxiao Shic1779882016-08-17 01:59:23 +0000165 }
166
167 // block will be 0xAA, 0x01, 0xDD
168 return makeNonNegativeIntegerBlock(0xAA, 0xDD);
169 }
170
171public:
172 bool shouldThrow = false;
173};
174
Davide Pesavento949075a2021-10-17 22:07:07 -0400175class DecodableType
Junxiao Shic1779882016-08-17 01:59:23 +0000176{
177public:
Davide Pesavento949075a2021-10-17 22:07:07 -0400178 DecodableType() = default;
Junxiao Shi435bb552016-09-04 03:14:47 +0000179
180 explicit
Davide Pesavento949075a2021-10-17 22:07:07 -0400181 DecodableType(const Block& block)
Junxiao Shic1779882016-08-17 01:59:23 +0000182 {
Davide Pesavento949075a2021-10-17 22:07:07 -0400183 wireDecode(block);
Junxiao Shi435bb552016-09-04 03:14:47 +0000184 }
Junxiao Shic1779882016-08-17 01:59:23 +0000185
186 void
Junxiao Shi435bb552016-09-04 03:14:47 +0000187 wireDecode(const Block& block)
Junxiao Shic1779882016-08-17 01:59:23 +0000188 {
Davide Pesavento258d51a2022-02-27 21:26:28 -0500189 BOOST_TEST(block == "BB01EE"_block);
Junxiao Shic1779882016-08-17 01:59:23 +0000190 }
Junxiao Shic1779882016-08-17 01:59:23 +0000191};
192
Davide Pesavento949075a2021-10-17 22:07:07 -0400193class DecodableTypeThrow
194{
195public:
196 DecodableTypeThrow() = default;
197
198 explicit
199 DecodableTypeThrow(const Block& block)
200 {
201 wireDecode(block);
202 }
203
204 void
205 wireDecode(const Block&)
206 {
207 NDN_THROW(tlv::Error("decode error"));
208 }
209};
Junxiao Shic1779882016-08-17 01:59:23 +0000210
211BOOST_AUTO_TEST_CASE(LoadNoEncoding)
212{
213 this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
214 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::NO_ENCODING);
215 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400216
217 std::ifstream ifs(filename);
218 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::NO_ENCODING));
Junxiao Shic1779882016-08-17 01:59:23 +0000219}
220
221BOOST_AUTO_TEST_CASE(LoadBase64)
222{
223 this->writeFile<std::string>("uwHu\n"); // printf '\xBB\x01\xEE' | base64
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000224 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::BASE64);
225 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400226
227 std::ifstream ifs(filename);
228 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000229}
230
231BOOST_AUTO_TEST_CASE(LoadBase64Newline64)
232{
233 this->writeFile<std::string>(
234 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
235 "AAAAAAAAAAAA\n");
236 // printf '\x08\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00
237 // \x00\x00\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' | base64
240 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
241 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400242
243 std::ifstream ifs(filename);
244 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000245}
246
247BOOST_AUTO_TEST_CASE(LoadBase64Newline32)
248{
249 this->writeFile<std::string>(
250 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
251 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n"
252 "AAAAAAAAAAAA\n");
253 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
254 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400255
256 std::ifstream ifs(filename);
257 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000258}
259
260BOOST_AUTO_TEST_CASE(LoadBase64NewlineEnd)
261{
262 this->writeFile<std::string>(
263 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n");
264 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
265 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400266
267 std::ifstream ifs(filename);
268 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000269}
270
271BOOST_AUTO_TEST_CASE(LoadBase64NoNewline)
272{
273 this->writeFile<std::string>(
274 "CEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
275 shared_ptr<name::Component> decoded = io::load<name::Component>(filename, io::BASE64);
Junxiao Shic1779882016-08-17 01:59:23 +0000276 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400277
278 std::ifstream ifs(filename);
279 BOOST_CHECK_NO_THROW(io::loadTlv<name::Component>(ifs, io::BASE64));
Junxiao Shic1779882016-08-17 01:59:23 +0000280}
281
282BOOST_AUTO_TEST_CASE(LoadHex)
283{
284 this->writeFile<std::string>("BB01EE");
285 shared_ptr<DecodableType> decoded = io::load<DecodableType>(filename, io::HEX);
286 BOOST_CHECK(decoded != nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400287
288 std::ifstream ifs(filename);
289 BOOST_CHECK_NO_THROW(io::loadTlv<DecodableType>(ifs, io::HEX));
Junxiao Shic1779882016-08-17 01:59:23 +0000290}
291
Davide Pesavento949075a2021-10-17 22:07:07 -0400292BOOST_AUTO_TEST_CASE(LoadDecodeException)
Junxiao Shic1779882016-08-17 01:59:23 +0000293{
294 this->writeFile<std::vector<uint8_t>>({0xBB, 0x01, 0xEE});
295 shared_ptr<DecodableTypeThrow> decoded;
296 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableTypeThrow>(filename, io::NO_ENCODING));
297 BOOST_CHECK(decoded == nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400298
299 std::ifstream ifs(filename);
300 BOOST_CHECK_THROW(io::loadTlv<DecodableTypeThrow>(ifs, io::NO_ENCODING), io::Error);
Junxiao Shic1779882016-08-17 01:59:23 +0000301}
302
303BOOST_AUTO_TEST_CASE(LoadNotHex)
304{
305 this->writeFile<std::string>("not-hex");
306 shared_ptr<DecodableType> decoded;
307 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::HEX));
308 BOOST_CHECK(decoded == nullptr);
Davide Pesavento949075a2021-10-17 22:07:07 -0400309
310 std::ifstream ifs(filename);
311 BOOST_CHECK_THROW(io::loadTlv<DecodableType>(ifs, io::HEX), io::Error);
312}
313
314BOOST_AUTO_TEST_CASE(LoadEmpty)
315{
316 this->writeFile<std::vector<uint8_t>>({});
317 shared_ptr<DecodableType> decoded;
318 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::NO_ENCODING));
319 BOOST_CHECK(decoded == nullptr);
320
321 std::ifstream ifs(filename);
322 BOOST_CHECK_THROW(io::loadTlv<DecodableType>(ifs, io::NO_ENCODING), io::Error);
Junxiao Shic1779882016-08-17 01:59:23 +0000323}
324
325BOOST_AUTO_TEST_CASE(LoadFileNotReadable)
326{
Junxiao Shic1779882016-08-17 01:59:23 +0000327 shared_ptr<DecodableType> decoded;
328 BOOST_CHECK_NO_THROW(decoded = io::load<DecodableType>(filename, io::NO_ENCODING));
329 BOOST_CHECK(decoded == nullptr);
330}
331
332BOOST_AUTO_TEST_CASE(SaveNoEncoding)
333{
334 EncodableType encoded;
335 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::NO_ENCODING));
336 auto content = this->readFile<std::vector<uint8_t>>();
337 uint8_t expected[] = {0xAA, 0x01, 0xDD};
338 BOOST_CHECK_EQUAL_COLLECTIONS(content.begin(), content.end(),
339 expected, expected + sizeof(expected));
340}
341
342BOOST_AUTO_TEST_CASE(SaveBase64)
343{
344 EncodableType encoded;
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000345 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::BASE64));
Junxiao Shic1779882016-08-17 01:59:23 +0000346 auto content = this->readFile<std::string>();
347 BOOST_CHECK_EQUAL(content, "qgHd\n"); // printf '\xAA\x01\xDD' | base64
348}
349
350BOOST_AUTO_TEST_CASE(SaveHex)
351{
352 EncodableType encoded;
353 BOOST_CHECK_NO_THROW(io::save(encoded, filename, io::HEX));
354 auto content = this->readFile<std::string>();
355 BOOST_CHECK_EQUAL(content, "AA01DD");
356}
357
358BOOST_AUTO_TEST_CASE(SaveException)
359{
360 EncodableType encoded;
361 encoded.shouldThrow = true;
362 BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
363}
364
365BOOST_AUTO_TEST_CASE(SaveFileNotWritable)
366{
367 this->mkdir();
368 EncodableType encoded;
369 encoded.shouldThrow = true;
370 BOOST_CHECK_THROW(io::save(encoded, filename, io::NO_ENCODING), io::Error);
371}
372
Davide Pesavento6c6e3852019-08-05 20:20:35 -0400373BOOST_AUTO_TEST_SUITE_END() // FileIo
374
375class IdCertFixture : public FileIoFixture
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500376 , public KeyChainFixture
Junxiao Shic1779882016-08-17 01:59:23 +0000377{
378};
379
380BOOST_FIXTURE_TEST_CASE(IdCert, IdCertFixture)
381{
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500382 auto identity = m_keyChain.createIdentity("/TestIo/IdCert", RsaKeyParams());
Davide Pesaventod09d52b2023-02-15 14:15:06 -0500383 auto key = identity.getDefaultKey();
384 const auto& cert = key.getDefaultCertificate();
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800385 io::save(cert, filename);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800386
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500387 auto readCert = io::load<security::Certificate>(filename);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800388
Junxiao Shi4ce0bcf2016-09-03 07:09:03 +0000389 BOOST_REQUIRE(readCert != nullptr);
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800390 BOOST_CHECK_EQUAL(cert.getName(), readCert->getName());
Ashlesh Gawandee84d1eb2018-01-04 20:46:44 -0600391
392 this->writeFile<std::string>("");
Davide Pesavento4c1ad4c2020-11-16 21:12:02 -0500393 readCert = io::load<security::Certificate>(filename);
Ashlesh Gawandee84d1eb2018-01-04 20:46:44 -0600394 BOOST_REQUIRE(readCert == nullptr);
Yingdi Yuf50098d2014-02-26 14:26:29 -0800395}
396
Junxiao Shic1779882016-08-17 01:59:23 +0000397BOOST_AUTO_TEST_SUITE_END() // TestIo
398BOOST_AUTO_TEST_SUITE_END() // Util
Yingdi Yuf50098d2014-02-26 14:26:29 -0800399
Davide Pesavento47ce2ee2023-05-09 01:33:33 -0400400} // namespace ndn::tests