Add: Overlay cargo icon in vehicle/depot list when holding shift+ctrl. (#12938)
[openttd-github.git] / src / tests / string_func.cpp
blob6086cad0754a22ede40599f8aa2cafa0b8df4a65
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
8 /** @file string_func.cpp Test functionality from string_func. */
10 #include "../stdafx.h"
12 #include "../3rdparty/catch2/catch.hpp"
14 #include "../string_func.h"
16 /**** String compare/equals *****/
18 TEST_CASE("StrCompareIgnoreCase - std::string")
20 /* Same string, with different cases. */
21 CHECK(StrCompareIgnoreCase(std::string{""}, std::string{""}) == 0);
22 CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"a"}) == 0);
23 CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"A"}) == 0);
24 CHECK(StrCompareIgnoreCase(std::string{"A"}, std::string{"a"}) == 0);
25 CHECK(StrCompareIgnoreCase(std::string{"A"}, std::string{"A"}) == 0);
27 /* Not the same string. */
28 CHECK(StrCompareIgnoreCase(std::string{""}, std::string{"b"}) < 0);
29 CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{""}) > 0);
31 CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"b"}) < 0);
32 CHECK(StrCompareIgnoreCase(std::string{"b"}, std::string{"a"}) > 0);
33 CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"B"}) < 0);
34 CHECK(StrCompareIgnoreCase(std::string{"b"}, std::string{"A"}) > 0);
35 CHECK(StrCompareIgnoreCase(std::string{"A"}, std::string{"b"}) < 0);
36 CHECK(StrCompareIgnoreCase(std::string{"B"}, std::string{"a"}) > 0);
38 CHECK(StrCompareIgnoreCase(std::string{"a"}, std::string{"aa"}) < 0);
39 CHECK(StrCompareIgnoreCase(std::string{"aa"}, std::string{"a"}) > 0);
42 TEST_CASE("StrCompareIgnoreCase - char pointer")
44 /* Same string, with different cases. */
45 CHECK(StrCompareIgnoreCase("", "") == 0);
46 CHECK(StrCompareIgnoreCase("a", "a") == 0);
47 CHECK(StrCompareIgnoreCase("a", "A") == 0);
48 CHECK(StrCompareIgnoreCase("A", "a") == 0);
49 CHECK(StrCompareIgnoreCase("A", "A") == 0);
51 /* Not the same string. */
52 CHECK(StrCompareIgnoreCase("", "b") < 0);
53 CHECK(StrCompareIgnoreCase("a", "") > 0);
55 CHECK(StrCompareIgnoreCase("a", "b") < 0);
56 CHECK(StrCompareIgnoreCase("b", "a") > 0);
57 CHECK(StrCompareIgnoreCase("a", "B") < 0);
58 CHECK(StrCompareIgnoreCase("b", "A") > 0);
59 CHECK(StrCompareIgnoreCase("A", "b") < 0);
60 CHECK(StrCompareIgnoreCase("B", "a") > 0);
62 CHECK(StrCompareIgnoreCase("a", "aa") < 0);
63 CHECK(StrCompareIgnoreCase("aa", "a") > 0);
66 TEST_CASE("StrCompareIgnoreCase - std::string_view")
69 * With std::string_view the only way to access the data is via .data(),
70 * which does not guarantee the termination that would be required by
71 * things such as stricmp/strcasecmp. So, just passing .data() into stricmp
72 * or strcasecmp would fail if it does not account for the length of the
73 * view. Thus, contrary to the string/char* tests, this uses the same base
74 * string but gets different sections to trigger these corner cases.
76 std::string_view base{"aaAbB"};
78 /* Same string, with different cases. */
79 CHECK(StrCompareIgnoreCase(base.substr(0, 0), base.substr(1, 0)) == 0); // Different positions
80 CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(1, 1)) == 0); // Different positions
81 CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(2, 1)) == 0);
82 CHECK(StrCompareIgnoreCase(base.substr(2, 1), base.substr(1, 1)) == 0);
83 CHECK(StrCompareIgnoreCase(base.substr(2, 1), base.substr(2, 1)) == 0);
85 /* Not the same string. */
86 CHECK(StrCompareIgnoreCase(base.substr(3, 0), base.substr(3, 1)) < 0); // Same position, different lengths
87 CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(0, 0)) > 0); // Same position, different lengths
89 CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(3, 1)) < 0);
90 CHECK(StrCompareIgnoreCase(base.substr(3, 1), base.substr(0, 1)) > 0);
91 CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(4, 1)) < 0);
92 CHECK(StrCompareIgnoreCase(base.substr(3, 1), base.substr(2, 1)) > 0);
93 CHECK(StrCompareIgnoreCase(base.substr(2, 1), base.substr(3, 1)) < 0);
94 CHECK(StrCompareIgnoreCase(base.substr(4, 1), base.substr(0, 1)) > 0);
96 CHECK(StrCompareIgnoreCase(base.substr(0, 1), base.substr(0, 2)) < 0); // Same position, different lengths
97 CHECK(StrCompareIgnoreCase(base.substr(0, 2), base.substr(0, 1)) > 0); // Same position, different lengths
100 TEST_CASE("StrEqualsIgnoreCase - std::string")
102 /* Same string, with different cases. */
103 CHECK(StrEqualsIgnoreCase(std::string{""}, std::string{""}));
104 CHECK(StrEqualsIgnoreCase(std::string{"a"}, std::string{"a"}));
105 CHECK(StrEqualsIgnoreCase(std::string{"a"}, std::string{"A"}));
106 CHECK(StrEqualsIgnoreCase(std::string{"A"}, std::string{"a"}));
107 CHECK(StrEqualsIgnoreCase(std::string{"A"}, std::string{"A"}));
109 /* Not the same string. */
110 CHECK(!StrEqualsIgnoreCase(std::string{""}, std::string{"b"}));
111 CHECK(!StrEqualsIgnoreCase(std::string{"a"}, std::string{""}));
112 CHECK(!StrEqualsIgnoreCase(std::string{"a"}, std::string{"b"}));
113 CHECK(!StrEqualsIgnoreCase(std::string{"b"}, std::string{"a"}));
114 CHECK(!StrEqualsIgnoreCase(std::string{"a"}, std::string{"aa"}));
115 CHECK(!StrEqualsIgnoreCase(std::string{"aa"}, std::string{"a"}));
118 TEST_CASE("StrEqualsIgnoreCase - char pointer")
120 /* Same string, with different cases. */
121 CHECK(StrEqualsIgnoreCase("", ""));
122 CHECK(StrEqualsIgnoreCase("a", "a"));
123 CHECK(StrEqualsIgnoreCase("a", "A"));
124 CHECK(StrEqualsIgnoreCase("A", "a"));
125 CHECK(StrEqualsIgnoreCase("A", "A"));
127 /* Not the same string. */
128 CHECK(!StrEqualsIgnoreCase("", "b"));
129 CHECK(!StrEqualsIgnoreCase("a", ""));
130 CHECK(!StrEqualsIgnoreCase("a", "b"));
131 CHECK(!StrEqualsIgnoreCase("b", "a"));
132 CHECK(!StrEqualsIgnoreCase("a", "aa"));
133 CHECK(!StrEqualsIgnoreCase("aa", "a"));
136 TEST_CASE("StrEqualsIgnoreCase - std::string_view")
139 * With std::string_view the only way to access the data is via .data(),
140 * which does not guarantee the termination that would be required by
141 * things such as stricmp/strcasecmp. So, just passing .data() into stricmp
142 * or strcasecmp would fail if it does not account for the length of the
143 * view. Thus, contrary to the string/char* tests, this uses the same base
144 * string but gets different sections to trigger these corner cases.
146 std::string_view base{"aaAb"};
148 /* Same string, with different cases. */
149 CHECK(StrEqualsIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
150 CHECK(StrEqualsIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
151 CHECK(StrEqualsIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
152 CHECK(StrEqualsIgnoreCase(base.substr(2, 1), base.substr(1, 1)));
153 CHECK(StrEqualsIgnoreCase(base.substr(2, 1), base.substr(2, 1)));
155 /* Not the same string. */
156 CHECK(!StrEqualsIgnoreCase(base.substr(3, 0), base.substr(3, 1))); // Same position, different lengths
157 CHECK(!StrEqualsIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
158 CHECK(!StrEqualsIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
159 CHECK(!StrEqualsIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
160 CHECK(!StrEqualsIgnoreCase(base.substr(0, 1), base.substr(0, 2))); // Same position, different lengths
161 CHECK(!StrEqualsIgnoreCase(base.substr(0, 2), base.substr(0, 1))); // Same position, different lengths
164 /**** String starts with *****/
166 TEST_CASE("StrStartsWithIgnoreCase - std::string")
168 /* Everything starts with an empty prefix. */
169 CHECK(StrStartsWithIgnoreCase(std::string{""}, std::string{""}));
170 CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{""}));
172 /* Equals string, ignoring case. */
173 CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{"a"}));
174 CHECK(StrStartsWithIgnoreCase(std::string{"a"}, std::string{"A"}));
175 CHECK(StrStartsWithIgnoreCase(std::string{"A"}, std::string{"a"}));
176 CHECK(StrStartsWithIgnoreCase(std::string{"A"}, std::string{"A"}));
178 /* Starts with same, ignoring case. */
179 CHECK(StrStartsWithIgnoreCase(std::string{"ab"}, std::string{"a"}));
180 CHECK(StrStartsWithIgnoreCase(std::string{"ab"}, std::string{"A"}));
181 CHECK(StrStartsWithIgnoreCase(std::string{"Ab"}, std::string{"a"}));
182 CHECK(StrStartsWithIgnoreCase(std::string{"Ab"}, std::string{"A"}));
184 /* Does not start the same. */
185 CHECK(!StrStartsWithIgnoreCase(std::string{""}, std::string{"b"}));
186 CHECK(!StrStartsWithIgnoreCase(std::string{"a"}, std::string{"b"}));
187 CHECK(!StrStartsWithIgnoreCase(std::string{"b"}, std::string{"a"}));
188 CHECK(!StrStartsWithIgnoreCase(std::string{"a"}, std::string{"aa"}));
191 TEST_CASE("StrStartsWithIgnoreCase - char pointer")
193 /* Everything starts with an empty prefix. */
194 CHECK(StrStartsWithIgnoreCase("", ""));
195 CHECK(StrStartsWithIgnoreCase("a", ""));
197 /* Equals string, ignoring case. */
198 CHECK(StrStartsWithIgnoreCase("a", "a"));
199 CHECK(StrStartsWithIgnoreCase("a", "A"));
200 CHECK(StrStartsWithIgnoreCase("A", "a"));
201 CHECK(StrStartsWithIgnoreCase("A", "A"));
203 /* Starts with same, ignoring case. */
204 CHECK(StrStartsWithIgnoreCase("ab", "a"));
205 CHECK(StrStartsWithIgnoreCase("ab", "A"));
206 CHECK(StrStartsWithIgnoreCase("Ab", "a"));
207 CHECK(StrStartsWithIgnoreCase("Ab", "A"));
209 /* Does not start the same. */
210 CHECK(!StrStartsWithIgnoreCase("", "b"));
211 CHECK(!StrStartsWithIgnoreCase("a", "b"));
212 CHECK(!StrStartsWithIgnoreCase("b", "a"));
213 CHECK(!StrStartsWithIgnoreCase("a", "aa"));
216 TEST_CASE("StrStartsWithIgnoreCase - std::string_view")
219 * With std::string_view the only way to access the data is via .data(),
220 * which does not guarantee the termination that would be required by
221 * things such as stricmp/strcasecmp. So, just passing .data() into stricmp
222 * or strcasecmp would fail if it does not account for the length of the
223 * view. Thus, contrary to the string/char* tests, this uses the same base
224 * string but gets different sections to trigger these corner cases.
226 std::string_view base{"aabAb"};
228 /* Everything starts with an empty prefix. */
229 CHECK(StrStartsWithIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
230 CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
232 /* Equals string, ignoring case. */
233 CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
234 CHECK(StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
235 CHECK(StrStartsWithIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
236 CHECK(StrStartsWithIgnoreCase(base.substr(3, 1), base.substr(3, 1)));
238 /* Starts with same, ignoring case. */
239 CHECK(StrStartsWithIgnoreCase(base.substr(1, 2), base.substr(0, 1)));
240 CHECK(StrStartsWithIgnoreCase(base.substr(1, 2), base.substr(3, 1)));
241 CHECK(StrStartsWithIgnoreCase(base.substr(3, 2), base.substr(0, 1)));
242 CHECK(StrStartsWithIgnoreCase(base.substr(3, 2), base.substr(3, 1)));
244 /* Does not start the same. */
245 CHECK(!StrStartsWithIgnoreCase(base.substr(2, 0), base.substr(2, 1)));
246 CHECK(!StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
247 CHECK(!StrStartsWithIgnoreCase(base.substr(2, 1), base.substr(0, 1)));
248 CHECK(!StrStartsWithIgnoreCase(base.substr(0, 1), base.substr(0, 2)));
251 /**** String ends with *****/
253 TEST_CASE("StrEndsWithIgnoreCase - std::string")
255 /* Everything ends with an empty prefix. */
256 CHECK(StrEndsWithIgnoreCase(std::string{""}, std::string{""}));
257 CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{""}));
259 /* Equals string, ignoring case. */
260 CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{"a"}));
261 CHECK(StrEndsWithIgnoreCase(std::string{"a"}, std::string{"A"}));
262 CHECK(StrEndsWithIgnoreCase(std::string{"A"}, std::string{"a"}));
263 CHECK(StrEndsWithIgnoreCase(std::string{"A"}, std::string{"A"}));
265 /* Ends with same, ignoring case. */
266 CHECK(StrEndsWithIgnoreCase(std::string{"ba"}, std::string{"a"}));
267 CHECK(StrEndsWithIgnoreCase(std::string{"ba"}, std::string{"A"}));
268 CHECK(StrEndsWithIgnoreCase(std::string{"bA"}, std::string{"a"}));
269 CHECK(StrEndsWithIgnoreCase(std::string{"bA"}, std::string{"A"}));
271 /* Does not end the same. */
272 CHECK(!StrEndsWithIgnoreCase(std::string{""}, std::string{"b"}));
273 CHECK(!StrEndsWithIgnoreCase(std::string{"a"}, std::string{"b"}));
274 CHECK(!StrEndsWithIgnoreCase(std::string{"b"}, std::string{"a"}));
275 CHECK(!StrEndsWithIgnoreCase(std::string{"a"}, std::string{"aa"}));
278 TEST_CASE("StrEndsWithIgnoreCase - char pointer")
280 /* Everything ends with an empty prefix. */
281 CHECK(StrEndsWithIgnoreCase("", ""));
282 CHECK(StrEndsWithIgnoreCase("a", ""));
284 /* Equals string, ignoring case. */
285 CHECK(StrEndsWithIgnoreCase("a", "a"));
286 CHECK(StrEndsWithIgnoreCase("a", "A"));
287 CHECK(StrEndsWithIgnoreCase("A", "a"));
288 CHECK(StrEndsWithIgnoreCase("A", "A"));
290 /* Ends with same, ignoring case. */
291 CHECK(StrEndsWithIgnoreCase("ba", "a"));
292 CHECK(StrEndsWithIgnoreCase("ba", "A"));
293 CHECK(StrEndsWithIgnoreCase("bA", "a"));
294 CHECK(StrEndsWithIgnoreCase("bA", "A"));
296 /* Does not end the same. */
297 CHECK(!StrEndsWithIgnoreCase("", "b"));
298 CHECK(!StrEndsWithIgnoreCase("a", "b"));
299 CHECK(!StrEndsWithIgnoreCase("b", "a"));
300 CHECK(!StrEndsWithIgnoreCase("a", "aa"));
303 TEST_CASE("StrEndsWithIgnoreCase - std::string_view")
306 * With std::string_view the only way to access the data is via .data(),
307 * which does not guarantee the termination that would be required by
308 * things such as stricmp/strcasecmp. So, just passing .data() into stricmp
309 * or strcasecmp would fail if it does not account for the length of the
310 * view. Thus, contrary to the string/char* tests, this uses the same base
311 * string but gets different sections to trigger these corner cases.
313 std::string_view base{"aabAba"};
315 /* Everything ends with an empty prefix. */
316 CHECK(StrEndsWithIgnoreCase(base.substr(0, 0), base.substr(1, 0))); // Different positions
317 CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(0, 0)));
319 /* Equals string, ignoring case. */
320 CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(1, 1))); // Different positions
321 CHECK(StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(3, 1)));
322 CHECK(StrEndsWithIgnoreCase(base.substr(3, 1), base.substr(0, 1)));
323 CHECK(StrEndsWithIgnoreCase(base.substr(3, 1), base.substr(3, 1)));
325 /* Ends with same, ignoring case. */
326 CHECK(StrEndsWithIgnoreCase(base.substr(2, 2), base.substr(0, 1)));
327 CHECK(StrEndsWithIgnoreCase(base.substr(2, 2), base.substr(3, 1)));
328 CHECK(StrEndsWithIgnoreCase(base.substr(4, 2), base.substr(0, 1)));
329 CHECK(StrEndsWithIgnoreCase(base.substr(4, 2), base.substr(3, 1)));
331 /* Does not end the same. */
332 CHECK(!StrEndsWithIgnoreCase(base.substr(2, 0), base.substr(2, 1)));
333 CHECK(!StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(2, 1)));
334 CHECK(!StrEndsWithIgnoreCase(base.substr(2, 1), base.substr(0, 1)));
335 CHECK(!StrEndsWithIgnoreCase(base.substr(0, 1), base.substr(0, 2)));
339 TEST_CASE("FormatArrayAsHex")
341 CHECK(FormatArrayAsHex(std::array<uint8_t, 0>{}) == "");
342 CHECK(FormatArrayAsHex(std::array<uint8_t, 1>{0x12}) == "12");
343 CHECK(FormatArrayAsHex(std::array<uint8_t, 4>{0x13, 0x38, 0x42, 0xAF}) == "133842AF");
346 TEST_CASE("ConvertHexToBytes")
348 CHECK(ConvertHexToBytes("", {}) == true);
349 CHECK(ConvertHexToBytes("1", {}) == false);
350 CHECK(ConvertHexToBytes("12", {}) == false);
352 std::array<uint8_t, 1> bytes1;
353 CHECK(ConvertHexToBytes("1", bytes1) == false);
354 CHECK(ConvertHexToBytes("12", bytes1) == true);
355 CHECK(bytes1[0] == 0x12);
356 CHECK(ConvertHexToBytes("123", bytes1) == false);
357 CHECK(ConvertHexToBytes("1g", bytes1) == false);
358 CHECK(ConvertHexToBytes("g1", bytes1) == false);
360 std::array<uint8_t, 2> bytes2;
361 CHECK(ConvertHexToBytes("12", bytes2) == false);
362 CHECK(ConvertHexToBytes("1234", bytes2) == true);
363 CHECK(bytes2[0] == 0x12);
364 CHECK(bytes2[1] == 0x34);
366 std::array<uint8_t, 8> bytes3;
367 CHECK(ConvertHexToBytes("123456789abcdef0", bytes3) == true);
368 CHECK(bytes3[0] == 0x12);
369 CHECK(bytes3[1] == 0x34);
370 CHECK(bytes3[2] == 0x56);
371 CHECK(bytes3[3] == 0x78);
372 CHECK(bytes3[4] == 0x9a);
373 CHECK(bytes3[5] == 0xbc);
374 CHECK(bytes3[6] == 0xde);
375 CHECK(bytes3[7] == 0xf0);
377 CHECK(ConvertHexToBytes("123456789ABCDEF0", bytes3) == true);
378 CHECK(bytes3[0] == 0x12);
379 CHECK(bytes3[1] == 0x34);
380 CHECK(bytes3[2] == 0x56);
381 CHECK(bytes3[3] == 0x78);
382 CHECK(bytes3[4] == 0x9a);
383 CHECK(bytes3[5] == 0xbc);
384 CHECK(bytes3[6] == 0xde);
385 CHECK(bytes3[7] == 0xf0);
388 static const std::vector<std::pair<std::string, std::string>> _str_trim_testcases = {
389 {"a", "a"},
390 {" a", "a"},
391 {"a ", "a"},
392 {" a ", "a"},
393 {" a b c ", "a b c"},
394 {" ", ""}
397 TEST_CASE("StrTrimInPlace")
399 for (auto [input, expected] : _str_trim_testcases) {
400 StrTrimInPlace(input);
401 CHECK(input == expected);
405 TEST_CASE("StrTrimView") {
406 for (const auto& [input, expected] : _str_trim_testcases) {
407 CHECK(StrTrimView(input) == expected);