2022-12-24 23:49:50 +00:00
|
|
|
// Copyright (c) 2012-2022 The Bitcoin Core developers
|
2015-09-07 15:22:23 -07:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <streams.h>
|
2023-01-22 09:57:19 -08:00
|
|
|
#include <test/util/random.h>
|
2019-11-05 15:18:59 -05:00
|
|
|
#include <test/util/setup_common.h>
|
2023-03-15 11:18:06 +01:00
|
|
|
#include <util/fs.h>
|
2023-06-12 18:30:23 +02:00
|
|
|
#include <util/strencodings.h>
|
2015-09-07 15:22:23 -07:00
|
|
|
|
|
|
|
#include <boost/test/unit_test.hpp>
|
2016-12-05 16:03:53 +09:00
|
|
|
|
2022-02-03 19:45:45 +01:00
|
|
|
using namespace std::string_literals;
|
|
|
|
|
2015-09-07 15:22:23 -07:00
|
|
|
BOOST_FIXTURE_TEST_SUITE(streams_tests, BasicTestingSetup)
|
|
|
|
|
2023-06-12 18:30:23 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(xor_file)
|
|
|
|
{
|
|
|
|
fs::path xor_path{m_args.GetDataDirBase() / "test_xor.bin"};
|
|
|
|
auto raw_file{[&](const auto& mode) { return fsbridge::fopen(xor_path, mode); }};
|
|
|
|
const std::vector<uint8_t> test1{1, 2, 3};
|
|
|
|
const std::vector<uint8_t> test2{4, 5};
|
|
|
|
const std::vector<std::byte> xor_pat{std::byte{0xff}, std::byte{0x00}};
|
|
|
|
{
|
|
|
|
// Check errors for missing file
|
|
|
|
AutoFile xor_file{raw_file("rb"), xor_pat};
|
|
|
|
BOOST_CHECK_EXCEPTION(xor_file << std::byte{}, std::ios_base::failure, HasReason{"AutoFile::write: file handle is nullpt"});
|
|
|
|
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: file handle is nullpt"});
|
|
|
|
BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: file handle is nullpt"});
|
|
|
|
}
|
|
|
|
{
|
2024-01-31 17:14:52 +00:00
|
|
|
#ifdef __MINGW64__
|
|
|
|
// Our usage of mingw-w64 and the msvcrt runtime does not support
|
|
|
|
// the x modifier for the _wfopen().
|
|
|
|
const char* mode = "wb";
|
|
|
|
#else
|
|
|
|
const char* mode = "wbx";
|
|
|
|
#endif
|
|
|
|
AutoFile xor_file{raw_file(mode), xor_pat};
|
2023-06-12 18:30:23 +02:00
|
|
|
xor_file << test1 << test2;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Read raw from disk
|
|
|
|
AutoFile non_xor_file{raw_file("rb")};
|
|
|
|
std::vector<std::byte> raw(7);
|
|
|
|
non_xor_file >> Span{raw};
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(raw), "fc01fd03fd04fa");
|
|
|
|
// Check that no padding exists
|
|
|
|
BOOST_CHECK_EXCEPTION(non_xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: end of file"});
|
|
|
|
}
|
|
|
|
{
|
|
|
|
AutoFile xor_file{raw_file("rb"), xor_pat};
|
|
|
|
std::vector<std::byte> read1, read2;
|
|
|
|
xor_file >> read1 >> read2;
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(read1), HexStr(test1));
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(read2), HexStr(test2));
|
|
|
|
// Check that eof was reached
|
|
|
|
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: end of file"});
|
|
|
|
}
|
|
|
|
{
|
|
|
|
AutoFile xor_file{raw_file("rb"), xor_pat};
|
|
|
|
std::vector<std::byte> read2;
|
|
|
|
// Check that ignore works
|
|
|
|
xor_file.ignore(4);
|
|
|
|
xor_file >> read2;
|
|
|
|
BOOST_CHECK_EQUAL(HexStr(read2), HexStr(test2));
|
|
|
|
// Check that ignore and read fail now
|
|
|
|
BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: end of file"});
|
|
|
|
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: end of file"});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_vector_writer)
|
|
|
|
{
|
|
|
|
unsigned char a(1);
|
|
|
|
unsigned char b(2);
|
|
|
|
unsigned char bytes[] = { 3, 4, 5, 6 };
|
|
|
|
std::vector<unsigned char> vch;
|
|
|
|
|
|
|
|
// Each test runs twice. Serializing a second time at the same starting
|
|
|
|
// point should yield the same results, even if the first test grew the
|
|
|
|
// vector.
|
|
|
|
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 0, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 0, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
|
|
|
|
vch.clear();
|
|
|
|
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 2, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 2, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
|
|
|
|
vch.clear();
|
|
|
|
|
|
|
|
vch.resize(5, 0);
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 2, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 2, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
|
|
|
|
vch.clear();
|
|
|
|
|
|
|
|
vch.resize(4, 0);
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 3, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 3, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
|
|
|
|
vch.clear();
|
|
|
|
|
|
|
|
vch.resize(4, 0);
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 4, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 4, a, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
|
|
|
|
vch.clear();
|
|
|
|
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 0, bytes};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 0, bytes};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
|
|
|
|
vch.clear();
|
|
|
|
|
|
|
|
vch.resize(4, 8);
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 2, a, bytes, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
|
2023-11-16 12:59:43 +01:00
|
|
|
VectorWriter{vch, 2, a, bytes, b};
|
2016-11-10 17:05:23 -05:00
|
|
|
BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
|
|
|
|
vch.clear();
|
|
|
|
}
|
|
|
|
|
2018-04-11 20:51:44 -07:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_vector_reader)
|
|
|
|
{
|
|
|
|
std::vector<unsigned char> vch = {1, 255, 3, 4, 5, 6};
|
|
|
|
|
2023-11-17 16:32:29 +01:00
|
|
|
SpanReader reader{vch};
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(reader.size(), 6U);
|
2018-04-11 20:51:44 -07:00
|
|
|
BOOST_CHECK(!reader.empty());
|
|
|
|
|
|
|
|
// Read a single byte as an unsigned char.
|
|
|
|
unsigned char a;
|
|
|
|
reader >> a;
|
|
|
|
BOOST_CHECK_EQUAL(a, 1);
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(reader.size(), 5U);
|
2018-04-11 20:51:44 -07:00
|
|
|
BOOST_CHECK(!reader.empty());
|
|
|
|
|
2024-04-18 10:53:32 +01:00
|
|
|
// Read a single byte as a int8_t.
|
|
|
|
int8_t b;
|
2018-04-11 20:51:44 -07:00
|
|
|
reader >> b;
|
|
|
|
BOOST_CHECK_EQUAL(b, -1);
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(reader.size(), 4U);
|
2018-04-11 20:51:44 -07:00
|
|
|
BOOST_CHECK(!reader.empty());
|
|
|
|
|
|
|
|
// Read a 4 bytes as an unsigned int.
|
|
|
|
unsigned int c;
|
|
|
|
reader >> c;
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(c, 100992003U); // 3,4,5,6 in little-endian base-256
|
|
|
|
BOOST_CHECK_EQUAL(reader.size(), 0U);
|
2018-04-11 20:51:44 -07:00
|
|
|
BOOST_CHECK(reader.empty());
|
|
|
|
|
|
|
|
// Reading after end of byte vector throws an error.
|
|
|
|
signed int d;
|
|
|
|
BOOST_CHECK_THROW(reader >> d, std::ios_base::failure);
|
|
|
|
|
|
|
|
// Read a 4 bytes as a signed int from the beginning of the buffer.
|
2023-11-17 16:32:29 +01:00
|
|
|
SpanReader new_reader{vch};
|
2018-10-03 14:52:08 +02:00
|
|
|
new_reader >> d;
|
2018-04-11 20:51:44 -07:00
|
|
|
BOOST_CHECK_EQUAL(d, 67370753); // 1,255,3,4 in little-endian base-256
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(new_reader.size(), 2U);
|
2018-10-03 14:52:08 +02:00
|
|
|
BOOST_CHECK(!new_reader.empty());
|
2018-04-11 20:51:44 -07:00
|
|
|
|
|
|
|
// Reading after end of byte vector throws an error even if the reader is
|
|
|
|
// not totally empty.
|
2018-10-03 14:52:08 +02:00
|
|
|
BOOST_CHECK_THROW(new_reader >> d, std::ios_base::failure);
|
2018-04-11 20:51:44 -07:00
|
|
|
}
|
|
|
|
|
2021-04-03 13:42:55 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_vector_reader_rvalue)
|
|
|
|
{
|
|
|
|
std::vector<uint8_t> data{0x82, 0xa7, 0x31};
|
2023-11-17 16:32:29 +01:00
|
|
|
SpanReader reader{data};
|
2021-04-03 13:42:55 +02:00
|
|
|
uint32_t varint = 0;
|
|
|
|
// Deserialize into r-value
|
|
|
|
reader >> VARINT(varint);
|
test: Correct outstanding -Werror=sign-compare errors
e.g.:
In file included from /usr/local/include/boost/test/test_tools.hpp:46:
/usr/local/include/boost/test/tools/old/impl.hpp:107:17: error: comparison of integers of different signs: 'const unsigned int' and 'const int' [-Werror,-Wsign-compare]
return left == right;
~~~~ ^ ~~~~~
/usr/local/include/boost/test/tools/old/impl.hpp:130:16: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::equal_impl<unsigned int, int>' requested here
return equal_impl( left, right );
^
/usr/local/include/boost/test/tools/old/impl.hpp:145:16: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::equal_impl_frwd::call_impl<unsigned int, int>' requested here
return call_impl( left, right, left_is_array() );
^
/usr/local/include/boost/test/tools/old/impl.hpp:92:50: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::equal_impl_frwd::operator()<unsigned int, int>' requested here
BOOST_PP_REPEAT( BOOST_TEST_MAX_PREDICATE_ARITY, IMPL_FRWD, _ )
^
/usr/local/include/boost/preprocessor/repetition/repeat.hpp:30:26: note: expanded from macro 'BOOST_PP_REPEAT'
^
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
^
/usr/local/include/boost/preprocessor/cat.hpp:29:34: note: expanded from macro 'BOOST_PP_CAT_I'
^
<scratch space>:153:1: note: expanded from here
BOOST_PP_REPEAT_1
^
test/streams_tests.cpp:122:5: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::check_frwd<boost::test_tools::tt_detail::equal_impl_frwd, unsigned int, int>' requested here
BOOST_CHECK_EQUAL(varint, 54321);
^
/usr/local/include/boost/test/tools/old/impl.hpp:107:17: error: comparison of integers of different signs: 'const unsigned long long' and 'const long' [-Werror,-Wsign-compare]
return left == right;
~~~~ ^ ~~~~~
/usr/local/include/boost/test/tools/old/impl.hpp:130:16: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::equal_impl<unsigned long long, long>' requested here
return equal_impl( left, right );
^
/usr/local/include/boost/test/tools/old/impl.hpp:145:16: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::equal_impl_frwd::call_impl<unsigned long long, long>' requested here
return call_impl( left, right, left_is_array() );
^
/usr/local/include/boost/test/tools/old/impl.hpp:92:50: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::equal_impl_frwd::operator()<unsigned long long, long>' requested here
BOOST_PP_REPEAT( BOOST_TEST_MAX_PREDICATE_ARITY, IMPL_FRWD, _ )
^
/usr/local/include/boost/preprocessor/repetition/repeat.hpp:30:26: note: expanded from macro 'BOOST_PP_REPEAT'
^
/usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro 'BOOST_PP_CAT'
^
/usr/local/include/boost/preprocessor/cat.hpp:29:34: note: expanded from macro 'BOOST_PP_CAT_I'
^
<scratch space>:161:1: note: expanded from here
BOOST_PP_REPEAT_1
^
test/serfloat_tests.cpp:41:5: note: in instantiation of function template specialization 'boost::test_tools::tt_detail::check_frwd<boost::test_tools::tt_detail::equal_impl_frwd, unsigned long long, long>' requested here
BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits<double>::infinity()), 0x7ff0000000000000);
^
2021-06-06 16:13:00 -04:00
|
|
|
BOOST_CHECK_EQUAL(varint, 54321U);
|
2021-04-03 13:42:55 +02:00
|
|
|
BOOST_CHECK(reader.empty());
|
|
|
|
}
|
|
|
|
|
2018-04-17 02:19:36 -07:00
|
|
|
BOOST_AUTO_TEST_CASE(bitstream_reader_writer)
|
|
|
|
{
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream data{};
|
2018-04-17 02:19:36 -07:00
|
|
|
|
2023-01-03 13:21:44 +01:00
|
|
|
BitStreamWriter bit_writer{data};
|
2018-04-17 02:19:36 -07:00
|
|
|
bit_writer.Write(0, 1);
|
|
|
|
bit_writer.Write(2, 2);
|
|
|
|
bit_writer.Write(6, 3);
|
|
|
|
bit_writer.Write(11, 4);
|
|
|
|
bit_writer.Write(1, 5);
|
|
|
|
bit_writer.Write(32, 6);
|
|
|
|
bit_writer.Write(7, 7);
|
|
|
|
bit_writer.Write(30497, 16);
|
|
|
|
bit_writer.Flush();
|
|
|
|
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream data_copy{data};
|
2018-04-17 02:19:36 -07:00
|
|
|
uint32_t serialized_int1;
|
|
|
|
data >> serialized_int1;
|
2021-12-18 12:50:58 -05:00
|
|
|
BOOST_CHECK_EQUAL(serialized_int1, uint32_t{0x7700C35A}); // NOTE: Serialized as LE
|
2018-04-17 02:19:36 -07:00
|
|
|
uint16_t serialized_int2;
|
|
|
|
data >> serialized_int2;
|
2021-12-18 12:50:58 -05:00
|
|
|
BOOST_CHECK_EQUAL(serialized_int2, uint16_t{0x1072}); // NOTE: Serialized as LE
|
2018-04-17 02:19:36 -07:00
|
|
|
|
2023-01-03 13:21:44 +01:00
|
|
|
BitStreamReader bit_reader{data_copy};
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(1), 0U);
|
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(2), 2U);
|
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(3), 6U);
|
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(4), 11U);
|
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(5), 1U);
|
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(6), 32U);
|
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(7), 7U);
|
|
|
|
BOOST_CHECK_EQUAL(bit_reader.Read(16), 30497U);
|
2018-04-17 02:19:36 -07:00
|
|
|
BOOST_CHECK_THROW(bit_reader.Read(8), std::ios_base::failure);
|
|
|
|
}
|
|
|
|
|
2015-09-07 15:22:23 -07:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
|
|
|
|
{
|
2022-01-02 11:31:25 +01:00
|
|
|
std::vector<std::byte> in;
|
2015-09-07 15:22:23 -07:00
|
|
|
|
|
|
|
// Degenerate case
|
2022-02-03 19:36:19 +01:00
|
|
|
{
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream ds{in};
|
2022-02-03 19:36:19 +01:00
|
|
|
ds.Xor({0x00, 0x00});
|
|
|
|
BOOST_CHECK_EQUAL(""s, ds.str());
|
|
|
|
}
|
2015-09-07 15:22:23 -07:00
|
|
|
|
2022-01-02 11:31:25 +01:00
|
|
|
in.push_back(std::byte{0x0f});
|
|
|
|
in.push_back(std::byte{0xf0});
|
2018-07-24 16:59:49 +01:00
|
|
|
|
2015-09-07 15:22:23 -07:00
|
|
|
// Single character key
|
2022-02-03 19:36:19 +01:00
|
|
|
{
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream ds{in};
|
2022-02-03 19:36:19 +01:00
|
|
|
ds.Xor({0xff});
|
|
|
|
BOOST_CHECK_EQUAL("\xf0\x0f"s, ds.str());
|
|
|
|
}
|
2018-07-24 16:59:49 +01:00
|
|
|
|
2015-09-07 15:22:23 -07:00
|
|
|
// Multi character key
|
|
|
|
|
|
|
|
in.clear();
|
2022-01-02 11:31:25 +01:00
|
|
|
in.push_back(std::byte{0xf0});
|
|
|
|
in.push_back(std::byte{0x0f});
|
2018-07-24 16:59:49 +01:00
|
|
|
|
2022-02-03 19:36:19 +01:00
|
|
|
{
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream ds{in};
|
2022-02-03 19:36:19 +01:00
|
|
|
ds.Xor({0xff, 0x0f});
|
|
|
|
BOOST_CHECK_EQUAL("\x0f\x00"s, ds.str());
|
|
|
|
}
|
2018-07-24 16:59:49 +01:00
|
|
|
}
|
2015-09-07 15:22:23 -07:00
|
|
|
|
2019-09-10 07:53:09 -06:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_buffered_file)
|
|
|
|
{
|
2021-11-04 18:46:57 +01:00
|
|
|
fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
|
2023-11-15 13:10:13 +10:00
|
|
|
AutoFile file{fsbridge::fopen(streams_test_filename, "w+b")};
|
2021-11-04 18:46:57 +01:00
|
|
|
|
2019-09-10 07:53:09 -06:00
|
|
|
// The value at each offset is the offset.
|
|
|
|
for (uint8_t j = 0; j < 40; ++j) {
|
2023-07-04 18:59:49 +02:00
|
|
|
file << j;
|
2019-09-10 07:53:09 -06:00
|
|
|
}
|
2023-07-04 18:59:49 +02:00
|
|
|
std::rewind(file.Get());
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// The buffer size (second arg) must be greater than the rewind
|
|
|
|
// amount (third arg).
|
|
|
|
try {
|
2023-07-04 17:09:13 +02:00
|
|
|
BufferedFile bfbad{file, 25, 25};
|
2019-09-10 07:53:09 -06:00
|
|
|
BOOST_CHECK(false);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
BOOST_CHECK(strstr(e.what(),
|
|
|
|
"Rewind limit must be less than buffer size") != nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The buffer is 25 bytes, allow rewinding 10 bytes.
|
2023-07-04 17:09:13 +02:00
|
|
|
BufferedFile bf{file, 25, 10};
|
2019-09-10 07:53:09 -06:00
|
|
|
BOOST_CHECK(!bf.eof());
|
|
|
|
|
|
|
|
uint8_t i;
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 0);
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 1);
|
|
|
|
|
|
|
|
// After reading bytes 0 and 1, we're positioned at 2.
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 2U);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// Rewind to offset 0, ok (within the 10 byte window).
|
|
|
|
BOOST_CHECK(bf.SetPos(0));
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 0);
|
|
|
|
|
|
|
|
// We can go forward to where we've been, but beyond may fail.
|
|
|
|
BOOST_CHECK(bf.SetPos(2));
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 2);
|
|
|
|
|
|
|
|
// If you know the maximum number of bytes that should be
|
|
|
|
// read to deserialize the variable, you can limit the read
|
|
|
|
// extent. The current file offset is 3, so the following
|
|
|
|
// SetLimit() allows zero bytes to be read.
|
|
|
|
BOOST_CHECK(bf.SetLimit(3));
|
|
|
|
try {
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK(false);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
BOOST_CHECK(strstr(e.what(),
|
2020-07-13 17:11:37 -06:00
|
|
|
"Attempt to position past buffer limit") != nullptr);
|
2019-09-10 07:53:09 -06:00
|
|
|
}
|
|
|
|
// The default argument removes the limit completely.
|
|
|
|
BOOST_CHECK(bf.SetLimit());
|
|
|
|
// The read position should still be at 3 (no change).
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 3U);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// Read from current offset, 3, forward until position 10.
|
|
|
|
for (uint8_t j = 3; j < 10; ++j) {
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, j);
|
|
|
|
}
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 10U);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// We're guaranteed (just barely) to be able to rewind to zero.
|
|
|
|
BOOST_CHECK(bf.SetPos(0));
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 0U);
|
2019-09-10 07:53:09 -06:00
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 0);
|
|
|
|
|
|
|
|
// We can set the position forward again up to the farthest
|
|
|
|
// into the stream we've been, but no farther. (Attempting
|
|
|
|
// to go farther may succeed, but it's not guaranteed.)
|
|
|
|
BOOST_CHECK(bf.SetPos(10));
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 10);
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 11U);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// Now it's only guaranteed that we can rewind to offset 1
|
|
|
|
// (current read position, 11, minus rewind amount, 10).
|
|
|
|
BOOST_CHECK(bf.SetPos(1));
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 1U);
|
2019-09-10 07:53:09 -06:00
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 1);
|
|
|
|
|
|
|
|
// We can stream into large variables, even larger than
|
|
|
|
// the buffer size.
|
|
|
|
BOOST_CHECK(bf.SetPos(11));
|
|
|
|
{
|
|
|
|
uint8_t a[40 - 11];
|
|
|
|
bf >> a;
|
|
|
|
for (uint8_t j = 0; j < sizeof(a); ++j) {
|
|
|
|
BOOST_CHECK_EQUAL(a[j], 11 + j);
|
|
|
|
}
|
|
|
|
}
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 40U);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// We've read the entire file, the next read should throw.
|
|
|
|
try {
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK(false);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
BOOST_CHECK(strstr(e.what(),
|
2023-09-12 12:23:49 +02:00
|
|
|
"BufferedFile::Fill: end of file") != nullptr);
|
2019-09-10 07:53:09 -06:00
|
|
|
}
|
|
|
|
// Attempting to read beyond the end sets the EOF indicator.
|
|
|
|
BOOST_CHECK(bf.eof());
|
|
|
|
|
|
|
|
// Still at offset 40, we can go back 10, to 30.
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 40U);
|
2019-09-10 07:53:09 -06:00
|
|
|
BOOST_CHECK(bf.SetPos(30));
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 30);
|
2020-02-27 15:20:31 -08:00
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 31U);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// We're too far to rewind to position zero.
|
|
|
|
BOOST_CHECK(!bf.SetPos(0));
|
|
|
|
// But we should now be positioned at least as far back as allowed
|
|
|
|
// by the rewind window (relative to our farthest read position, 40).
|
2020-07-13 17:11:37 -06:00
|
|
|
BOOST_CHECK(bf.GetPos() <= 30U);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
// We can explicitly close the file, or the destructor will do it.
|
2023-07-04 18:59:49 +02:00
|
|
|
file.fclose();
|
2019-09-10 07:53:09 -06:00
|
|
|
|
2021-11-04 18:46:57 +01:00
|
|
|
fs::remove(streams_test_filename);
|
2019-09-10 07:53:09 -06:00
|
|
|
}
|
|
|
|
|
2020-07-13 17:11:37 -06:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_buffered_file_skip)
|
|
|
|
{
|
|
|
|
fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
|
2023-11-15 13:10:13 +10:00
|
|
|
AutoFile file{fsbridge::fopen(streams_test_filename, "w+b")};
|
2020-07-13 17:11:37 -06:00
|
|
|
// The value at each offset is the byte offset (e.g. byte 1 in the file has the value 0x01).
|
|
|
|
for (uint8_t j = 0; j < 40; ++j) {
|
2023-07-04 18:59:49 +02:00
|
|
|
file << j;
|
2020-07-13 17:11:37 -06:00
|
|
|
}
|
2023-07-04 18:59:49 +02:00
|
|
|
std::rewind(file.Get());
|
2020-07-13 17:11:37 -06:00
|
|
|
|
|
|
|
// The buffer is 25 bytes, allow rewinding 10 bytes.
|
2023-07-04 17:09:13 +02:00
|
|
|
BufferedFile bf{file, 25, 10};
|
2020-07-13 17:11:37 -06:00
|
|
|
|
|
|
|
uint8_t i;
|
|
|
|
// This is like bf >> (7-byte-variable), in that it will cause data
|
|
|
|
// to be read from the file into memory, but it's not copied to us.
|
|
|
|
bf.SkipTo(7);
|
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 7U);
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 7);
|
|
|
|
|
|
|
|
// The bytes in the buffer up to offset 7 are valid and can be read.
|
|
|
|
BOOST_CHECK(bf.SetPos(0));
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 0);
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 1);
|
|
|
|
|
|
|
|
bf.SkipTo(11);
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, 11);
|
|
|
|
|
|
|
|
// SkipTo() honors the transfer limit; we can't position beyond the limit.
|
|
|
|
bf.SetLimit(13);
|
|
|
|
try {
|
|
|
|
bf.SkipTo(14);
|
|
|
|
BOOST_CHECK(false);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
BOOST_CHECK(strstr(e.what(), "Attempt to position past buffer limit") != nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can position exactly to the transfer limit.
|
|
|
|
bf.SkipTo(13);
|
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), 13U);
|
|
|
|
|
2023-07-04 18:59:49 +02:00
|
|
|
file.fclose();
|
2020-07-13 17:11:37 -06:00
|
|
|
fs::remove(streams_test_filename);
|
|
|
|
}
|
|
|
|
|
2019-09-10 07:53:09 -06:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
|
|
|
|
{
|
|
|
|
// Make this test deterministic.
|
tests: overhaul deterministic test randomness
The existing code provides two randomness mechanisms for test purposes:
- g_insecure_rand_ctx (with its wrappers InsecureRand*), which during tests is
initialized using either zeros (SeedRand::ZEROS), or using environment-provided
randomness (SeedRand::SEED).
- g_mock_deterministic_tests, which controls some (but not all) of the normal
randomness output if set, but then makes it extremely predictable (identical
output repeatedly).
Replace this with a single mechanism, which retains the SeedRand modes to control
all randomness. There is a new internal deterministic PRNG inside the random
module, which is used in GetRandBytes() when in test mode, and which is also used
to initialize g_insecure_rand_ctx. This means that during tests, all random numbers
are made deterministic. There is one exception, GetStrongRandBytes(), which even
in test mode still uses the normal PRNG state.
This probably opens the door to removing a lot of the ad-hoc "deterministic" mode
functions littered through the codebase (by simply running relevant tests in
SeedRand::ZEROS mode), but this isn't done yet.
2024-03-10 19:49:42 -04:00
|
|
|
SeedRandomForTest(SeedRand::ZEROS);
|
2019-09-10 07:53:09 -06:00
|
|
|
|
2021-11-04 18:46:57 +01:00
|
|
|
fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
|
2019-09-10 07:53:09 -06:00
|
|
|
for (int rep = 0; rep < 50; ++rep) {
|
2023-11-15 13:10:13 +10:00
|
|
|
AutoFile file{fsbridge::fopen(streams_test_filename, "w+b")};
|
2019-09-10 07:53:09 -06:00
|
|
|
size_t fileSize = InsecureRandRange(256);
|
|
|
|
for (uint8_t i = 0; i < fileSize; ++i) {
|
2023-07-04 18:59:49 +02:00
|
|
|
file << i;
|
2019-09-10 07:53:09 -06:00
|
|
|
}
|
2023-07-04 18:59:49 +02:00
|
|
|
std::rewind(file.Get());
|
2019-09-10 07:53:09 -06:00
|
|
|
|
|
|
|
size_t bufSize = InsecureRandRange(300) + 1;
|
|
|
|
size_t rewindSize = InsecureRandRange(bufSize);
|
2023-07-04 17:09:13 +02:00
|
|
|
BufferedFile bf{file, bufSize, rewindSize};
|
2019-09-10 07:53:09 -06:00
|
|
|
size_t currentPos = 0;
|
|
|
|
size_t maxPos = 0;
|
|
|
|
for (int step = 0; step < 100; ++step) {
|
|
|
|
if (currentPos >= fileSize)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// We haven't read to the end of the file yet.
|
|
|
|
BOOST_CHECK(!bf.eof());
|
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), currentPos);
|
|
|
|
|
|
|
|
// Pretend the file consists of a series of objects of varying
|
|
|
|
// sizes; the boundaries of the objects can interact arbitrarily
|
|
|
|
// with the CBufferFile's internal buffer. These first three
|
|
|
|
// cases simulate objects of various sizes (1, 2, 5 bytes).
|
2020-07-13 17:11:37 -06:00
|
|
|
switch (InsecureRandRange(6)) {
|
2019-09-10 07:53:09 -06:00
|
|
|
case 0: {
|
|
|
|
uint8_t a[1];
|
|
|
|
if (currentPos + 1 > fileSize)
|
|
|
|
continue;
|
|
|
|
bf.SetLimit(currentPos + 1);
|
|
|
|
bf >> a;
|
|
|
|
for (uint8_t i = 0; i < 1; ++i) {
|
|
|
|
BOOST_CHECK_EQUAL(a[i], currentPos);
|
|
|
|
currentPos++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1: {
|
|
|
|
uint8_t a[2];
|
|
|
|
if (currentPos + 2 > fileSize)
|
|
|
|
continue;
|
|
|
|
bf.SetLimit(currentPos + 2);
|
|
|
|
bf >> a;
|
|
|
|
for (uint8_t i = 0; i < 2; ++i) {
|
|
|
|
BOOST_CHECK_EQUAL(a[i], currentPos);
|
|
|
|
currentPos++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2: {
|
|
|
|
uint8_t a[5];
|
|
|
|
if (currentPos + 5 > fileSize)
|
|
|
|
continue;
|
|
|
|
bf.SetLimit(currentPos + 5);
|
|
|
|
bf >> a;
|
|
|
|
for (uint8_t i = 0; i < 5; ++i) {
|
|
|
|
BOOST_CHECK_EQUAL(a[i], currentPos);
|
|
|
|
currentPos++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3: {
|
2020-07-13 17:11:37 -06:00
|
|
|
// SkipTo is similar to the "read" cases above, except
|
|
|
|
// we don't receive the data.
|
|
|
|
size_t skip_length{static_cast<size_t>(InsecureRandRange(5))};
|
|
|
|
if (currentPos + skip_length > fileSize) continue;
|
|
|
|
bf.SetLimit(currentPos + skip_length);
|
|
|
|
bf.SkipTo(currentPos + skip_length);
|
|
|
|
currentPos += skip_length;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 4: {
|
2019-09-10 07:53:09 -06:00
|
|
|
// Find a byte value (that is at or ahead of the current position).
|
|
|
|
size_t find = currentPos + InsecureRandRange(8);
|
|
|
|
if (find >= fileSize)
|
|
|
|
find = fileSize - 1;
|
2020-08-10 15:23:03 -06:00
|
|
|
bf.FindByte(std::byte(find));
|
2019-09-10 07:53:09 -06:00
|
|
|
// The value at each offset is the offset.
|
|
|
|
BOOST_CHECK_EQUAL(bf.GetPos(), find);
|
|
|
|
currentPos = find;
|
|
|
|
|
|
|
|
bf.SetLimit(currentPos + 1);
|
|
|
|
uint8_t i;
|
|
|
|
bf >> i;
|
|
|
|
BOOST_CHECK_EQUAL(i, currentPos);
|
|
|
|
currentPos++;
|
|
|
|
break;
|
|
|
|
}
|
2020-07-13 17:11:37 -06:00
|
|
|
case 5: {
|
2019-09-10 07:53:09 -06:00
|
|
|
size_t requestPos = InsecureRandRange(maxPos + 4);
|
|
|
|
bool okay = bf.SetPos(requestPos);
|
|
|
|
// The new position may differ from the requested position
|
|
|
|
// because we may not be able to rewind beyond the rewind
|
|
|
|
// window, and we may not be able to move forward beyond the
|
|
|
|
// farthest position we've reached so far.
|
|
|
|
currentPos = bf.GetPos();
|
|
|
|
BOOST_CHECK_EQUAL(okay, currentPos == requestPos);
|
|
|
|
// Check that we can position within the rewind window.
|
|
|
|
if (requestPos <= maxPos &&
|
|
|
|
maxPos > rewindSize &&
|
|
|
|
requestPos >= maxPos - rewindSize) {
|
|
|
|
// We requested a position within the rewind window.
|
|
|
|
BOOST_CHECK(okay);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (maxPos < currentPos)
|
|
|
|
maxPos = currentPos;
|
|
|
|
}
|
|
|
|
}
|
2021-11-04 18:46:57 +01:00
|
|
|
fs::remove(streams_test_filename);
|
2019-09-10 07:53:09 -06:00
|
|
|
}
|
|
|
|
|
2023-01-15 20:18:11 -05:00
|
|
|
BOOST_AUTO_TEST_CASE(streams_hashed)
|
|
|
|
{
|
2023-08-25 18:10:53 +02:00
|
|
|
DataStream stream{};
|
2023-01-15 20:18:11 -05:00
|
|
|
HashedSourceWriter hash_writer{stream};
|
|
|
|
const std::string data{"bitcoin"};
|
|
|
|
hash_writer << data;
|
|
|
|
|
2023-08-25 18:10:53 +02:00
|
|
|
HashVerifier hash_verifier{stream};
|
2023-01-15 20:18:11 -05:00
|
|
|
std::string result;
|
|
|
|
hash_verifier >> result;
|
|
|
|
BOOST_CHECK_EQUAL(data, result);
|
|
|
|
BOOST_CHECK_EQUAL(hash_writer.GetHash(), hash_verifier.GetHash());
|
|
|
|
}
|
|
|
|
|
2015-09-07 15:22:23 -07:00
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|