Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2024-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <util/strencodings.h>6#include <util/string.h>8#include <boost/test/unit_test.hpp>9#include <test/util/common.h>10#include <tinyformat.h>12using namespace util;13using util::detail::CheckNumFormatSpecifiers;15BOOST_AUTO_TEST_SUITE(util_string_tests)17template <unsigned NumArgs>18void TfmFormatZeroes(const std::string& fmt)19{20std::apply([&](auto... args) {21(void)tfm::format(tfm::RuntimeFormat{fmt}, args...);22}, std::array<int, NumArgs>{});23}25// Helper to allow compile-time sanity checks while providing the number of26// args directly. Normally PassFmt<sizeof...(Args)> would be used.27template <unsigned NumArgs>28void PassFmt(ConstevalFormatString<NumArgs> fmt)29{30// Execute compile-time check again at run-time to get code coverage stats31BOOST_CHECK_NO_THROW(CheckNumFormatSpecifiers<NumArgs>(fmt.fmt));33// If ConstevalFormatString didn't throw above, make sure tinyformat doesn't34// throw either for the same format string and parameter count combination.35// Proves that we have some extent of protection from runtime errors36// (tinyformat may still throw for some type mismatches).37BOOST_CHECK_NO_THROW(TfmFormatZeroes<NumArgs>(fmt.fmt));38}39template <unsigned WrongNumArgs>40void FailFmtWithError(const char* wrong_fmt, std::string_view error)41{42BOOST_CHECK_EXCEPTION(CheckNumFormatSpecifiers<WrongNumArgs>(wrong_fmt), const char*, HasReason{error});43}45BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec)46{47PassFmt<0>("");48PassFmt<0>("%%");49PassFmt<1>("%s");50PassFmt<1>("%c");51PassFmt<0>("%%s");52PassFmt<0>("s%%");53PassFmt<1>("%%%s");54PassFmt<1>("%s%%");55PassFmt<0>(" 1$s");56PassFmt<1>("%1$s");57PassFmt<1>("%1$s%1$s");58PassFmt<2>("%2$s");59PassFmt<2>("%2$s 4$s %2$s");60PassFmt<129>("%129$s 999$s %2$s");61PassFmt<1>("%02d");62PassFmt<1>("%+2s");63PassFmt<1>("%.6i");64PassFmt<1>("%5.2f");65PassFmt<1>("%5.f");66PassFmt<1>("%.f");67PassFmt<1>("%#x");68PassFmt<1>("%1$5i");69PassFmt<1>("%1$-5i");70PassFmt<1>("%1$.5i");71// tinyformat accepts almost any "type" spec, even '%', or '_', or '\n'.72PassFmt<1>("%123%");73PassFmt<1>("%123%s");74PassFmt<1>("%_");75PassFmt<1>("%\n");77PassFmt<2>("%*c");78PassFmt<2>("%+*c");79PassFmt<2>("%.*f");80PassFmt<3>("%*.*f");81PassFmt<3>("%2$*3$d");82PassFmt<3>("%2$*3$.9d");83PassFmt<3>("%2$.*3$d");84PassFmt<3>("%2$9.*3$d");85PassFmt<3>("%2$+9.*3$d");86PassFmt<4>("%3$*2$.*4$f");88// Make sure multiple flag characters "- 0+" are accepted89PassFmt<3>("'%- 0+*.*f'");90PassFmt<3>("'%1$- 0+*3$.*2$f'");92auto err_mix{"Format specifiers must be all positional or all non-positional!"};93FailFmtWithError<1>("%s%1$s", err_mix);94FailFmtWithError<2>("%2$*d", err_mix);95FailFmtWithError<2>("%*2$d", err_mix);96FailFmtWithError<2>("%.*3$d", err_mix);97FailFmtWithError<2>("%2$.*d", err_mix);99auto err_num{"Format specifier count must match the argument count!"};100FailFmtWithError<1>("", err_num);101FailFmtWithError<0>("%s", err_num);102FailFmtWithError<2>("%s", err_num);103FailFmtWithError<0>("%1$s", err_num);104FailFmtWithError<2>("%1$s", err_num);105FailFmtWithError<1>("%*c", err_num);107auto err_0_pos{"Positional format specifier must have position of at least 1"};108FailFmtWithError<1>("%$s", err_0_pos);109FailFmtWithError<1>("%$", err_0_pos);110FailFmtWithError<0>("%0$", err_0_pos);111FailFmtWithError<0>("%0$s", err_0_pos);112FailFmtWithError<2>("%2$*$d", err_0_pos);113FailFmtWithError<2>("%2$*0$d", err_0_pos);114FailFmtWithError<3>("%3$*2$.*$f", err_0_pos);115FailFmtWithError<3>("%3$*2$.*0$f", err_0_pos);117auto err_term{"Format specifier incorrectly terminated by end of string"};118FailFmtWithError<1>("%", err_term);119FailFmtWithError<1>("%9", err_term);120FailFmtWithError<1>("%9.", err_term);121FailFmtWithError<1>("%9.9", err_term);122FailFmtWithError<1>("%*", err_term);123FailFmtWithError<1>("%+*", err_term);124FailFmtWithError<1>("%.*", err_term);125FailFmtWithError<1>("%9.*", err_term);126FailFmtWithError<1>("%1$", err_term);127FailFmtWithError<1>("%1$9", err_term);128FailFmtWithError<2>("%1$*2$", err_term);129FailFmtWithError<2>("%1$.*2$", err_term);130FailFmtWithError<2>("%1$9.*2$", err_term);132// Non-parity between tinyformat and ConstevalFormatString.133// tinyformat throws but ConstevalFormatString does not.134BOOST_CHECK_EXCEPTION(tfm::format(ConstevalFormatString<1>{"%n"}, 0), tfm::format_error,135HasReason{"tinyformat: %n conversion spec not supported"});136BOOST_CHECK_EXCEPTION(tfm::format(ConstevalFormatString<2>{"%*s"}, "hi", "hi"), tfm::format_error,137HasReason{"tinyformat: Cannot convert from argument type to integer for use as variable width or precision"});138BOOST_CHECK_EXCEPTION(tfm::format(ConstevalFormatString<2>{"%.*s"}, "hi", "hi"), tfm::format_error,139HasReason{"tinyformat: Cannot convert from argument type to integer for use as variable width or precision"});141// Ensure that tinyformat throws if format string contains wrong number142// of specifiers. PassFmt relies on this to verify tinyformat successfully143// formats the strings, and will need to be updated if tinyformat is changed144// not to throw on failure.145BOOST_CHECK_EXCEPTION(TfmFormatZeroes<2>("%s"), tfm::format_error,146HasReason{"tinyformat: Not enough conversion specifiers in format string"});147BOOST_CHECK_EXCEPTION(TfmFormatZeroes<1>("%s %s"), tfm::format_error,148HasReason{"tinyformat: Too many conversion specifiers in format string"});149}151BOOST_AUTO_TEST_CASE(case_insensitive_equal_test)152{153BOOST_CHECK(!CaseInsensitiveEqual("A", "B"));154BOOST_CHECK(!CaseInsensitiveEqual("A", "b"));155BOOST_CHECK(!CaseInsensitiveEqual("a", "B"));156BOOST_CHECK(!CaseInsensitiveEqual("B", "A"));157BOOST_CHECK(!CaseInsensitiveEqual("B", "a"));158BOOST_CHECK(!CaseInsensitiveEqual("b", "A"));159BOOST_CHECK(!CaseInsensitiveEqual("A", "AA"));160BOOST_CHECK(CaseInsensitiveEqual("A-A", "a-a"));161BOOST_CHECK(CaseInsensitiveEqual("A", "A"));162BOOST_CHECK(CaseInsensitiveEqual("A", "a"));163BOOST_CHECK(CaseInsensitiveEqual("a", "a"));164BOOST_CHECK(CaseInsensitiveEqual("B", "b"));165BOOST_CHECK(CaseInsensitiveEqual("ab", "aB"));166BOOST_CHECK(CaseInsensitiveEqual("Ab", "aB"));167BOOST_CHECK(CaseInsensitiveEqual("AB", "ab"));169// Use a character with value > 127170// to ensure we don't trigger implicit-integer-sign-change171BOOST_CHECK(!CaseInsensitiveEqual("a", "\xe4"));172}174BOOST_AUTO_TEST_CASE(line_reader_test)175{176{177// Check three lines terminated by \n and \r\n, preserving whitespace178std::string_view input = "once upon a time\n there was a dog \r\nwho liked food\n";179LineReader reader(input, /*max_line_length=*/128);180BOOST_CHECK_EQUAL(reader.Consumed(), 0);181BOOST_CHECK_EQUAL(reader.Remaining(), 51);182std::optional<std::string> line1{reader.ReadLine()};183BOOST_CHECK_EQUAL(reader.Consumed(), 17);184BOOST_CHECK_EQUAL(reader.Remaining(), 34);185std::optional<std::string> line2{reader.ReadLine()};186BOOST_CHECK_EQUAL(reader.Consumed(), 36);187BOOST_CHECK_EQUAL(reader.Remaining(), 15);188std::optional<std::string> line3{reader.ReadLine()};189std::optional<std::string> line4{reader.ReadLine()};190BOOST_CHECK(line1);191BOOST_CHECK(line2);192BOOST_CHECK(line3);193BOOST_CHECK(!line4);194BOOST_CHECK_EQUAL(line1.value(), "once upon a time");195BOOST_CHECK_EQUAL(line2.value(), " there was a dog ");196BOOST_CHECK_EQUAL(line3.value(), "who liked food");197BOOST_CHECK_EQUAL(reader.Consumed(), 51);198BOOST_CHECK_EQUAL(reader.Remaining(), 0);199}200{201// Do not exceed max_line_length + 1 while searching for \n202// Test with 22-character line + \n + 23-character line + \n203std::string_view input = "once upon a time there\nwas a dog who liked tea\n";205LineReader reader1(input, /*max_line_length=*/22);206// First line is exactly the length of max_line_length207BOOST_CHECK_EQUAL(reader1.ReadLine(), "once upon a time there");208// Second line is +1 character too long209BOOST_CHECK_EXCEPTION(reader1.ReadLine(), std::runtime_error, HasReason{"max_line_length exceeded by LineReader"});211// Increase max_line_length by 1212LineReader reader2(input, /*max_line_length=*/23);213// Both lines fit within limit214BOOST_CHECK_EQUAL(reader2.ReadLine(), "once upon a time there");215BOOST_CHECK_EQUAL(reader2.ReadLine(), "was a dog who liked tea");216// End of buffer reached217BOOST_CHECK(!reader2.ReadLine());218}219{220// Empty lines are empty221std::string_view input = "\n";222LineReader reader(input, /*max_line_length=*/1024);223BOOST_CHECK_EQUAL(reader.ReadLine(), "");224BOOST_CHECK(!reader.ReadLine());225}226{227// Empty buffers are null228std::string_view input;229LineReader reader(input, /*max_line_length=*/1024);230BOOST_CHECK(!reader.ReadLine());231}232{233// Don't trim any more than \r\n234std::string_view input = "\r\r\n";235LineReader reader(input, /*max_line_length=*/1024);236BOOST_CHECK_EQUAL(reader.ReadLine(), "\r");237BOOST_CHECK(!reader.ReadLine());238}239{240// Even one character is too long, if it's not \n241std::string_view input = "ab\n";242LineReader reader(input, /*max_line_length=*/1);243// First line is +1 character too long244BOOST_CHECK_EXCEPTION(reader.ReadLine(), std::runtime_error, HasReason{"max_line_length exceeded by LineReader"});245}246{247std::string_view input = "a\nb\n";248LineReader reader(input, /*max_line_length=*/1);249BOOST_CHECK_EQUAL(reader.ReadLine(), "a");250BOOST_CHECK_EQUAL(reader.ReadLine(), "b");
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.