Bitcoin Core
32.1.0
P2P Digital Currency
Toggle main menu visibility
Loading...
Searching...
No Matches
src
test
util_string_tests.cpp
Go to the documentation of this file.
1
// Copyright (c) 2024-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5
#include <
util/strencodings.h
>
6
#include <
util/string.h
>
7
8
#include <boost/test/unit_test.hpp>
9
#include <
test/util/common.h
>
10
#include <
tinyformat.h
>
11
12
using namespace
util
;
13
using
util::detail::CheckNumFormatSpecifiers
;
14
15
BOOST_AUTO_TEST_SUITE(util_string_tests)
16
17
template
<
unsigned
NumArgs>
18
void
TfmFormatZeroes
(
const
std::string& fmt)
19
{
20
std::apply([&](
auto
...
args
) {
21
(void)
tfm::format
(
tfm::RuntimeFormat
{fmt},
args
...);
22
}, std::array<int, NumArgs>{});
23
}
24
25
// Helper to allow compile-time sanity checks while providing the number of
26
// args directly. Normally PassFmt<sizeof...(Args)> would be used.
27
template
<
unsigned
NumArgs>
28
void
PassFmt
(
ConstevalFormatString<NumArgs>
fmt)
29
{
30
// Execute compile-time check again at run-time to get code coverage stats
31
BOOST_CHECK_NO_THROW
(
CheckNumFormatSpecifiers<NumArgs>
(fmt.
fmt
));
32
33
// If ConstevalFormatString didn't throw above, make sure tinyformat doesn't
34
// throw either for the same format string and parameter count combination.
35
// Proves that we have some extent of protection from runtime errors
36
// (tinyformat may still throw for some type mismatches).
37
BOOST_CHECK_NO_THROW
(
TfmFormatZeroes<NumArgs>
(fmt.
fmt
));
38
}
39
template
<
unsigned
WrongNumArgs>
40
void
FailFmtWithError
(
const
char
* wrong_fmt, std::string_view error)
41
{
42
BOOST_CHECK_EXCEPTION
(
CheckNumFormatSpecifiers<WrongNumArgs>
(wrong_fmt),
const
char
*,
HasReason
{error});
43
}
44
45
BOOST_AUTO_TEST_CASE
(ConstevalFormatString_NumSpec)
46
{
47
PassFmt<0>
(
""
);
48
PassFmt<0>
(
"%%"
);
49
PassFmt<1>
(
"%s"
);
50
PassFmt<1>
(
"%c"
);
51
PassFmt<0>
(
"%%s"
);
52
PassFmt<0>
(
"s%%"
);
53
PassFmt<1>
(
"%%%s"
);
54
PassFmt<1>
(
"%s%%"
);
55
PassFmt<0>
(
" 1$s"
);
56
PassFmt<1>
(
"%1$s"
);
57
PassFmt<1>
(
"%1$s%1$s"
);
58
PassFmt<2>
(
"%2$s"
);
59
PassFmt<2>
(
"%2$s 4$s %2$s"
);
60
PassFmt<129>
(
"%129$s 999$s %2$s"
);
61
PassFmt<1>
(
"%02d"
);
62
PassFmt<1>
(
"%+2s"
);
63
PassFmt<1>
(
"%.6i"
);
64
PassFmt<1>
(
"%5.2f"
);
65
PassFmt<1>
(
"%5.f"
);
66
PassFmt<1>
(
"%.f"
);
67
PassFmt<1>
(
"%#x"
);
68
PassFmt<1>
(
"%1$5i"
);
69
PassFmt<1>
(
"%1$-5i"
);
70
PassFmt<1>
(
"%1$.5i"
);
71
// tinyformat accepts almost any "type" spec, even '%', or '_', or '\n'.
72
PassFmt<1>
(
"%123%"
);
73
PassFmt<1>
(
"%123%s"
);
74
PassFmt<1>
(
"%_"
);
75
PassFmt<1>
(
"%\n"
);
76
77
PassFmt<2>
(
"%*c"
);
78
PassFmt<2>
(
"%+*c"
);
79
PassFmt<2>
(
"%.*f"
);
80
PassFmt<3>
(
"%*.*f"
);
81
PassFmt<3>
(
"%2$*3$d"
);
82
PassFmt<3>
(
"%2$*3$.9d"
);
83
PassFmt<3>
(
"%2$.*3$d"
);
84
PassFmt<3>
(
"%2$9.*3$d"
);
85
PassFmt<3>
(
"%2$+9.*3$d"
);
86
PassFmt<4>
(
"%3$*2$.*4$f"
);
87
88
// Make sure multiple flag characters "- 0+" are accepted
89
PassFmt<3>
(
"'%- 0+*.*f'"
);
90
PassFmt<3>
(
"'%1$- 0+*3$.*2$f'"
);
91
92
auto
err_mix{
"Format specifiers must be all positional or all non-positional!"
};
93
FailFmtWithError<1>
(
"%s%1$s"
, err_mix);
94
FailFmtWithError<2>
(
"%2$*d"
, err_mix);
95
FailFmtWithError<2>
(
"%*2$d"
, err_mix);
96
FailFmtWithError<2>
(
"%.*3$d"
, err_mix);
97
FailFmtWithError<2>
(
"%2$.*d"
, err_mix);
98
99
auto
err_num{
"Format specifier count must match the argument count!"
};
100
FailFmtWithError<1>
(
""
, err_num);
101
FailFmtWithError<0>
(
"%s"
, err_num);
102
FailFmtWithError<2>
(
"%s"
, err_num);
103
FailFmtWithError<0>
(
"%1$s"
, err_num);
104
FailFmtWithError<2>
(
"%1$s"
, err_num);
105
FailFmtWithError<1>
(
"%*c"
, err_num);
106
107
auto
err_0_pos{
"Positional format specifier must have position of at least 1"
};
108
FailFmtWithError<1>
(
"%$s"
, err_0_pos);
109
FailFmtWithError<1>
(
"%$"
, err_0_pos);
110
FailFmtWithError<0>
(
"%0$"
, err_0_pos);
111
FailFmtWithError<0>
(
"%0$s"
, err_0_pos);
112
FailFmtWithError<2>
(
"%2$*$d"
, err_0_pos);
113
FailFmtWithError<2>
(
"%2$*0$d"
, err_0_pos);
114
FailFmtWithError<3>
(
"%3$*2$.*$f"
, err_0_pos);
115
FailFmtWithError<3>
(
"%3$*2$.*0$f"
, err_0_pos);
116
117
auto
err_term{
"Format specifier incorrectly terminated by end of string"
};
118
FailFmtWithError<1>
(
"%"
, err_term);
119
FailFmtWithError<1>
(
"%9"
, err_term);
120
FailFmtWithError<1>
(
"%9."
, err_term);
121
FailFmtWithError<1>
(
"%9.9"
, err_term);
122
FailFmtWithError<1>
(
"%*"
, err_term);
123
FailFmtWithError<1>
(
"%+*"
, err_term);
124
FailFmtWithError<1>
(
"%.*"
, err_term);
125
FailFmtWithError<1>
(
"%9.*"
, err_term);
126
FailFmtWithError<1>
(
"%1$"
, err_term);
127
FailFmtWithError<1>
(
"%1$9"
, err_term);
128
FailFmtWithError<2>
(
"%1$*2$"
, err_term);
129
FailFmtWithError<2>
(
"%1$.*2$"
, err_term);
130
FailFmtWithError<2>
(
"%1$9.*2$"
, err_term);
131
132
// Non-parity between tinyformat and ConstevalFormatString.
133
// tinyformat throws but ConstevalFormatString does not.
134
BOOST_CHECK_EXCEPTION
(
tfm::format
(
ConstevalFormatString<1>
{
"%n"
}, 0),
tfm::format_error
,
135
HasReason
{
"tinyformat: %n conversion spec not supported"
});
136
BOOST_CHECK_EXCEPTION
(
tfm::format
(
ConstevalFormatString<2>
{
"%*s"
},
"hi"
,
"hi"
),
tfm::format_error
,
137
HasReason
{
"tinyformat: Cannot convert from argument type to integer for use as variable width or precision"
});
138
BOOST_CHECK_EXCEPTION
(
tfm::format
(
ConstevalFormatString<2>
{
"%.*s"
},
"hi"
,
"hi"
),
tfm::format_error
,
139
HasReason
{
"tinyformat: Cannot convert from argument type to integer for use as variable width or precision"
});
140
141
// Ensure that tinyformat throws if format string contains wrong number
142
// of specifiers. PassFmt relies on this to verify tinyformat successfully
143
// formats the strings, and will need to be updated if tinyformat is changed
144
// not to throw on failure.
145
BOOST_CHECK_EXCEPTION
(
TfmFormatZeroes<2>
(
"%s"
),
tfm::format_error
,
146
HasReason
{
"tinyformat: Not enough conversion specifiers in format string"
});
147
BOOST_CHECK_EXCEPTION
(
TfmFormatZeroes<1>
(
"%s %s"
),
tfm::format_error
,
148
HasReason
{
"tinyformat: Too many conversion specifiers in format string"
});
149
}
150
151
BOOST_AUTO_TEST_CASE
(case_insensitive_equal_test)
152
{
153
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"A"
,
"B"
));
154
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"A"
,
"b"
));
155
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"a"
,
"B"
));
156
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"B"
,
"A"
));
157
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"B"
,
"a"
));
158
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"b"
,
"A"
));
159
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"A"
,
"AA"
));
160
BOOST_CHECK
(
CaseInsensitiveEqual
(
"A-A"
,
"a-a"
));
161
BOOST_CHECK
(
CaseInsensitiveEqual
(
"A"
,
"A"
));
162
BOOST_CHECK
(
CaseInsensitiveEqual
(
"A"
,
"a"
));
163
BOOST_CHECK
(
CaseInsensitiveEqual
(
"a"
,
"a"
));
164
BOOST_CHECK
(
CaseInsensitiveEqual
(
"B"
,
"b"
));
165
BOOST_CHECK
(
CaseInsensitiveEqual
(
"ab"
,
"aB"
));
166
BOOST_CHECK
(
CaseInsensitiveEqual
(
"Ab"
,
"aB"
));
167
BOOST_CHECK
(
CaseInsensitiveEqual
(
"AB"
,
"ab"
));
168
169
// Use a character with value > 127
170
// to ensure we don't trigger implicit-integer-sign-change
171
BOOST_CHECK
(!
CaseInsensitiveEqual
(
"a"
,
"\xe4"
));
172
}
173
174
BOOST_AUTO_TEST_CASE
(line_reader_test)
175
{
176
{
177
// Check three lines terminated by \n and \r\n, preserving whitespace
178
std::string_view input =
"once upon a time\n there was a dog \r\nwho liked food\n"
;
179
LineReader
reader
(input,
/*max_line_length=*/
128);
180
BOOST_CHECK_EQUAL
(
reader
.
Consumed
(), 0);
181
BOOST_CHECK_EQUAL
(
reader
.
Remaining
(), 51);
182
std::optional<std::string> line1{
reader
.
ReadLine
()};
183
BOOST_CHECK_EQUAL
(
reader
.
Consumed
(), 17);
184
BOOST_CHECK_EQUAL
(
reader
.
Remaining
(), 34);
185
std::optional<std::string> line2{
reader
.
ReadLine
()};
186
BOOST_CHECK_EQUAL
(
reader
.
Consumed
(), 36);
187
BOOST_CHECK_EQUAL
(
reader
.
Remaining
(), 15);
188
std::optional<std::string> line3{
reader
.
ReadLine
()};
189
std::optional<std::string> line4{
reader
.
ReadLine
()};
190
BOOST_CHECK
(line1);
191
BOOST_CHECK
(line2);
192
BOOST_CHECK
(line3);
193
BOOST_CHECK
(!line4);
194
BOOST_CHECK_EQUAL
(line1.value(),
"once upon a time"
);
195
BOOST_CHECK_EQUAL
(line2.value(),
" there was a dog "
);
196
BOOST_CHECK_EQUAL
(line3.value(),
"who liked food"
);
197
BOOST_CHECK_EQUAL
(
reader
.
Consumed
(), 51);
198
BOOST_CHECK_EQUAL
(
reader
.
Remaining
(), 0);
199
}
200
{
201
// Do not exceed max_line_length + 1 while searching for \n
202
// Test with 22-character line + \n + 23-character line + \n
203
std::string_view input =
"once upon a time there\nwas a dog who liked tea\n"
;
204
205
LineReader
reader1(input,
/*max_line_length=*/
22);
206
// First line is exactly the length of max_line_length
207
BOOST_CHECK_EQUAL
(reader1.
ReadLine
(),
"once upon a time there"
);
208
// Second line is +1 character too long
209
BOOST_CHECK_EXCEPTION
(reader1.
ReadLine
(), std::runtime_error,
HasReason
{
"max_line_length exceeded by LineReader"
});
210
211
// Increase max_line_length by 1
212
LineReader
reader2(input,
/*max_line_length=*/
23);
213
// Both lines fit within limit
214
BOOST_CHECK_EQUAL
(reader2.
ReadLine
(),
"once upon a time there"
);
215
BOOST_CHECK_EQUAL
(reader2.
ReadLine
(),
"was a dog who liked tea"
);
216
// End of buffer reached
217
BOOST_CHECK
(!reader2.
ReadLine
());
218
}
219
{
220
// Empty lines are empty
221
std::string_view input =
"\n"
;
222
LineReader
reader
(input,
/*max_line_length=*/
1024);
223
BOOST_CHECK_EQUAL
(
reader
.
ReadLine
(),
""
);
224
BOOST_CHECK
(!
reader
.
ReadLine
());
225
}
226
{
227
// Empty buffers are null
228
std::string_view input;
229
LineReader
reader
(input,
/*max_line_length=*/
1024);
230
BOOST_CHECK
(!
reader
.
ReadLine
());
231
}
232
{
233
// Don't trim any more than \r\n
234
std::string_view input =
"\r\r\n"
;
235
LineReader
reader
(input,
/*max_line_length=*/
1024);
236
BOOST_CHECK_EQUAL
(
reader
.
ReadLine
(),
"\r"
);
237
BOOST_CHECK
(!
reader
.
ReadLine
());
238
}
239
{
240
// Even one character is too long, if it's not \n
241
std::string_view input =
"ab\n"
;
242
LineReader
reader
(input,
/*max_line_length=*/
1);
243
// First line is +1 character too long
244
BOOST_CHECK_EXCEPTION
(
reader
.
ReadLine
(), std::runtime_error,
HasReason
{
"max_line_length exceeded by LineReader"
});
245
}
246
{
247
std::string_view input =
"a\nb\n"
;
248
LineReader
reader
(input,
/*max_line_length=*/
1);
249
BOOST_CHECK_EQUAL
(
reader
.
ReadLine
(),
"a"
);
250
BOOST_CHECK_EQUAL
(
reader
.
ReadLine
(),
"b"
);
251
BOOST_CHECK
(!
reader
.
ReadLine
());
252
}
253
{
254
// If ReadLine fails, the iterator is reset and we can ReadLength instead
255
std::string_view input =
"a\nbaboon\n"
;
256
LineReader
reader
(input,
/*max_line_length=*/
1);
257
BOOST_CHECK_EQUAL
(
reader
.
ReadLine
(),
"a"
);
258
// "baboon" is too long
259
BOOST_CHECK_EXCEPTION
(
reader
.
ReadLine
(), std::runtime_error,
HasReason
{
"max_line_length exceeded by LineReader"
});
260
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(1),
"b"
);
261
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(1),
"a"
);
262
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(2),
"bo"
);
263
// "on" is too long
264
BOOST_CHECK_EXCEPTION
(
reader
.
ReadLine
(), std::runtime_error,
HasReason
{
"max_line_length exceeded by LineReader"
});
265
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(1),
"o"
);
266
BOOST_CHECK_EQUAL
(
reader
.
ReadLine
(),
"n"
);
// now the remainder of the buffer fits in one line
267
BOOST_CHECK
(!
reader
.
ReadLine
());
268
}
269
{
270
// The end of the buffer (EOB) does not count as end of line \n
271
std::string_view input =
"once upon a time there"
;
272
273
LineReader
reader
(input,
/*max_line_length=*/
22);
274
// First line is exactly the length of max_line_length, but that doesn't matter because \n is missing
275
BOOST_CHECK
(!
reader
.
ReadLine
());
276
// Data can still be read using ReadLength
277
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(22),
"once upon a time there"
);
278
// End of buffer reached
279
BOOST_CHECK_EQUAL
(
reader
.
Remaining
(), 0);
280
}
281
{
282
// Read specific number of bytes regardless of max_line_length or \n unless buffer is too short
283
std::string_view input =
"once upon a time\n there was a dog \r\nwho liked food"
;
284
LineReader
reader
(input,
/*max_line_length=*/
1);
285
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(0),
""
);
286
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(3),
"onc"
);
287
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(8),
"e upon a"
);
288
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(8),
" time\n t"
);
289
BOOST_CHECK_EXCEPTION
(
reader
.
ReadLength
(128), std::runtime_error,
HasReason
{
"Not enough data in buffer"
});
290
// After the error the iterator is reset so we can try again
291
BOOST_CHECK_EQUAL
(
reader
.
ReadLength
(31),
"here was a dog \r\nwho liked food"
);
292
// End of buffer reached
293
BOOST_CHECK_EQUAL
(
reader
.
Remaining
(), 0);
294
}
295
}
296
297
BOOST_AUTO_TEST_SUITE_END
()
args
ArgsManager & args
Definition
bitcoind.cpp:280
HasReason
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
Definition
common.h:19
tinyformat::format_error
Definition
tinyformat.h:203
util::LineReader
Definition
string.h:270
util::LineReader::Consumed
size_t Consumed() const
Returns number of bytes already read from buffer.
Definition
string.cpp:85
util::LineReader::ReadLine
std::optional< std::string_view > ReadLine() LIFETIMEBOUND
Returns a string from current iterator position up to (but not including) next and advances iterator...
Definition
string.cpp:35
util::LineReader::Remaining
size_t Remaining() const
Returns remaining size of bytes in buffer.
Definition
string.cpp:80
util::LineReader::ReadLength
std::string_view ReadLength(size_t len) LIFETIMEBOUND
Returns string from current iterator position of specified length if possible and advances iterator o...
Definition
string.cpp:71
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_EXCEPTION
BOOST_CHECK_EXCEPTION(req.LoadBody(reader), std::runtime_error, HasReason{"Cannot parse Content-Length value"})
reader
LineReader reader("GET / HTTP/1.0\r\nHost: ", MAX_HEADERS_SIZE)
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(ipc_tests)
Definition
ipc_tests.cpp:202
tinyformat::format
void format(std::ostream &out, FormatStringCheck< sizeof...(Args)> fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
Definition
tinyformat.h:1079
util::detail::CheckNumFormatSpecifiers
constexpr void CheckNumFormatSpecifiers(const char *str)
Definition
string.h:26
util
Definition
httpserver.h:27
BOOST_CHECK_EQUAL
#define BOOST_CHECK_EQUAL(v1, v2)
Definition
object.cpp:17
BOOST_CHECK_NO_THROW
#define BOOST_CHECK_NO_THROW(stmt)
Definition
object.cpp:27
BOOST_CHECK
#define BOOST_CHECK(expr)
Definition
object.cpp:16
strencodings.h
string.h
tinyformat::RuntimeFormat
Definition
tinyformat.h:184
util::ConstevalFormatString
A wrapper for a compile-time partially validated format string.
Definition
string.h:96
util::ConstevalFormatString::fmt
const char *const fmt
Definition
string.h:97
common.h
tinyformat.h
CaseInsensitiveEqual
bool CaseInsensitiveEqual(std::string_view s1, std::string_view s2)
Locale-independent, ASCII-only comparator.
Definition
strencodings.cpp:430
TfmFormatZeroes
void TfmFormatZeroes(const std::string &fmt)
Definition
util_string_tests.cpp:18
CheckNumFormatSpecifiers
constexpr void CheckNumFormatSpecifiers(const char *str)
Definition
string.h:26
PassFmt
void PassFmt(ConstevalFormatString< NumArgs > fmt)
Definition
util_string_tests.cpp:28
FailFmtWithError
void FailFmtWithError(const char *wrong_fmt, std::string_view error)
Definition
util_string_tests.cpp:40
Generated on
for Bitcoin Core by
1.18.0