Line data Source code
1 : // Copyright (c) 2023 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/versiontheca
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : #define protected public
20 :
21 : // tested file
22 : //
23 : #include "versiontheca/unicode.h"
24 :
25 :
26 : // self
27 : //
28 : #include "catch_main.h"
29 :
30 :
31 :
32 : // versiontheca
33 : //
34 : #include "versiontheca/exception.h"
35 : #include "versiontheca/versiontheca.h"
36 :
37 :
38 : // libutf8
39 : //
40 : #include "libutf8/libutf8.h"
41 :
42 :
43 : // C++
44 : //
45 : #include <cstring>
46 : #include <iomanip>
47 : #include <stdexcept>
48 :
49 :
50 :
51 :
52 : namespace
53 : {
54 :
55 :
56 :
57 10147 : versiontheca::versiontheca::pointer_t create(char const * version, char const * verify = nullptr)
58 : {
59 20294 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
60 10147 : versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
61 10048 : if(verify == nullptr)
62 : {
63 9641 : verify = version;
64 : }
65 10048 : CATCH_REQUIRE(v->get_version() == verify);
66 20096 : return v;
67 : }
68 :
69 :
70 2115 : versiontheca::versiontheca::pointer_t invalid_version(char const * version, char const * errmsg)
71 : {
72 4230 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
73 2115 : versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
74 2115 : if(v->is_valid())
75 : {
76 0 : std::cerr << "--- testing invalid versions, but [" << version << "] is considered valid!\n";
77 : }
78 :
79 2115 : CATCH_REQUIRE_FALSE(v->is_valid());
80 2115 : CATCH_REQUIRE(v->get_last_error(false) == errmsg);
81 2115 : CATCH_REQUIRE(v->get_last_error() == errmsg);
82 2115 : CATCH_REQUIRE(v->get_last_error().empty());
83 :
84 4230 : return v;
85 : }
86 :
87 :
88 130000 : std::string generate_number()
89 : {
90 130000 : versiontheca::part_integer_t value;
91 130000 : SNAP_CATCH2_NAMESPACE::random(value);
92 130000 : return std::to_string(value);
93 : }
94 :
95 :
96 10000 : std::string generate_version(std::size_t max)
97 : {
98 10000 : std::string v;
99 140000 : for(std::size_t i(0); i < max; ++i)
100 : {
101 130000 : if(!v.empty())
102 : {
103 120000 : v += '.';
104 : }
105 130000 : v += generate_number();
106 : }
107 10000 : return v;
108 : }
109 :
110 :
111 : // a version of wctombs which encodes anything including invalid unicode
112 : // characters to make sure the library detects such as expected
113 : //
114 2134 : std::string wctombs(char32_t wc)
115 : {
116 2134 : char mb[4];
117 2134 : if(wc < 0x80)
118 : {
119 : /* this will also encode '\0'... */
120 32 : mb[0] = static_cast<char>(wc);
121 32 : return std::string(mb, 1);
122 : }
123 2102 : if(wc < 0x800)
124 : {
125 32 : mb[0] = static_cast<char>((wc >> 6) | 0xC0);
126 32 : mb[1] = (wc & 0x3F) | 0x80;
127 32 : return std::string(mb, 2);
128 : }
129 2070 : if(wc < 0x10000)
130 : {
131 2070 : mb[0] = static_cast<char>((wc >> 12) | 0xE0);
132 2070 : mb[1] = ((wc >> 6) & 0x3F) | 0x80;
133 2070 : mb[2] = (wc & 0x3F) | 0x80;
134 2070 : return std::string(mb, 3);
135 : }
136 0 : mb[0] = static_cast<char>((wc >> 18) | 0xF0);
137 0 : mb[1] = ((wc >> 12) & 0x3F) | 0x80;
138 0 : mb[2] = ((wc >> 6) & 0x3F) | 0x80;
139 0 : mb[3] = (wc & 0x3F) | 0x80;
140 0 : return std::string(mb, 4);
141 : }
142 :
143 : }
144 : // no name namespace
145 :
146 :
147 5 : CATCH_TEST_CASE("unicode_versions", "[valid]")
148 : {
149 6 : CATCH_START_SECTION("unicode_versions: verify test checker for version 1.0")
150 : {
151 1 : create("1.0");
152 : }
153 : CATCH_END_SECTION()
154 :
155 6 : CATCH_START_SECTION("unicode_versions: verify that canonicalization happens")
156 : {
157 : {
158 2 : versiontheca::versiontheca::pointer_t v(create("3", "3.0"));
159 1 : CATCH_REQUIRE(v->get_major() == 3);
160 1 : CATCH_REQUIRE(v->get_minor() == 0);
161 1 : CATCH_REQUIRE(v->get_patch() == 0);
162 1 : CATCH_REQUIRE(v->get_build() == 0);
163 : }
164 : {
165 2 : versiontheca::versiontheca::pointer_t v(create("1.0.0", "1.0"));
166 1 : CATCH_REQUIRE(v->get_major() == 1);
167 1 : CATCH_REQUIRE(v->get_minor() == 0);
168 1 : CATCH_REQUIRE(v->get_patch() == 0);
169 1 : CATCH_REQUIRE(v->get_build() == 0);
170 : }
171 : {
172 2 : versiontheca::versiontheca::pointer_t v(create("1.0.0.0", "1.0"));
173 1 : CATCH_REQUIRE(v->get_major() == 1);
174 1 : CATCH_REQUIRE(v->get_minor() == 0);
175 1 : CATCH_REQUIRE(v->get_patch() == 0);
176 1 : CATCH_REQUIRE(v->get_build() == 0);
177 : }
178 : {
179 2 : versiontheca::versiontheca::pointer_t v(create("A.A.A", "A.A"));
180 1 : CATCH_REQUIRE(v->get_major() == 0);
181 1 : CATCH_REQUIRE(v->get_minor() == 0);
182 1 : CATCH_REQUIRE(v->get_patch() == 0);
183 1 : CATCH_REQUIRE(v->get_build() == 0);
184 : }
185 : {
186 2 : versiontheca::versiontheca::pointer_t v(create("C.A.I"));
187 1 : CATCH_REQUIRE(v->get_major() == 0);
188 1 : CATCH_REQUIRE(v->get_minor() == 0);
189 1 : CATCH_REQUIRE(v->get_patch() == 0);
190 1 : CATCH_REQUIRE(v->get_build() == 0);
191 : }
192 : }
193 : CATCH_END_SECTION()
194 :
195 6 : CATCH_START_SECTION("unicode_versions: many valid versions")
196 : {
197 : // many valid versions generated randomly to increase the likelyhood
198 : // of things I would otherwise not think of
199 : //
200 10001 : for(int i(0); i < 10'000; ++i)
201 : {
202 10000 : int const parts(i % 25 + 1);
203 10000 : if(parts == 1)
204 : {
205 800 : std::string v(generate_version(parts));
206 400 : create(v.c_str(), (v + ".0").c_str());
207 : }
208 : else
209 : {
210 19200 : std::string v(generate_version(parts));
211 9600 : create(v.c_str());
212 : }
213 : }
214 : }
215 : CATCH_END_SECTION()
216 3 : }
217 :
218 :
219 3 : CATCH_TEST_CASE("next_previous_unicode_versions", "[valid][next][previous]")
220 : {
221 2 : CATCH_START_SECTION("next_previous_unicode_versions: next/previous at level 4, 3, 2, 1, 0")
222 : {
223 : {
224 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
225 1 : CATCH_REQUIRE(a->next(4));
226 1 : CATCH_REQUIRE(a->get_version() == "1.3.2.0.1");
227 1 : CATCH_REQUIRE(a->previous(4));
228 1 : CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1, back to original
229 1 : CATCH_REQUIRE(a->previous(4));
230 1 : CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295.4294967295");
231 1 : CATCH_REQUIRE(a->get_major() == 1);
232 1 : CATCH_REQUIRE(a->get_minor() == 3);
233 1 : CATCH_REQUIRE(a->get_patch() == 1);
234 1 : CATCH_REQUIRE(a->get_build() == 4294967295);
235 1 : CATCH_REQUIRE(a->next(4));
236 1 : CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1 -1 +1, back to original
237 : }
238 :
239 : {
240 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
241 1 : CATCH_REQUIRE(a->next(3));
242 1 : CATCH_REQUIRE(a->get_version() == "1.3.2.1");
243 1 : CATCH_REQUIRE(a->previous(3));
244 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
245 1 : CATCH_REQUIRE(a->previous(3));
246 1 : CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295");
247 1 : CATCH_REQUIRE(a->next(3));
248 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
249 : }
250 :
251 : {
252 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
253 1 : CATCH_REQUIRE(a->next(2));
254 1 : CATCH_REQUIRE(a->get_version() == "1.3.3");
255 1 : CATCH_REQUIRE(a->previous(2));
256 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
257 1 : CATCH_REQUIRE(a->previous(2));
258 1 : CATCH_REQUIRE(a->get_version() == "1.3.1");
259 1 : CATCH_REQUIRE(a->next(2));
260 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
261 : }
262 :
263 : {
264 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
265 1 : CATCH_REQUIRE(a->next(1));
266 1 : CATCH_REQUIRE(a->get_version() == "1.4");
267 1 : CATCH_REQUIRE(a->previous(1));
268 1 : CATCH_REQUIRE(a->get_version() == "1.3");
269 1 : CATCH_REQUIRE(a->previous(1));
270 1 : CATCH_REQUIRE(a->get_version() == "1.2");
271 1 : CATCH_REQUIRE(a->next(1));
272 1 : CATCH_REQUIRE(a->get_version() == "1.3");
273 : }
274 :
275 : {
276 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
277 1 : CATCH_REQUIRE(a->next(0));
278 1 : CATCH_REQUIRE(a->get_version() == "2.0");
279 1 : CATCH_REQUIRE(a->previous(0));
280 1 : CATCH_REQUIRE(a->get_version() == "1.0");
281 1 : CATCH_REQUIRE(a->previous(0));
282 1 : CATCH_REQUIRE(a->get_version() == "0.0");
283 1 : CATCH_REQUIRE(a->next(0));
284 1 : CATCH_REQUIRE(a->get_version() == "1.0");
285 : }
286 :
287 : {
288 2 : versiontheca::versiontheca::pointer_t a(create("1.3"));
289 2 : versiontheca::versiontheca::pointer_t f(create("9.9z"));
290 1 : a->set_format(*f);
291 1 : CATCH_REQUIRE(a->next(2));
292 1 : CATCH_REQUIRE(a->get_version() == "1.3B");
293 : }
294 : }
295 : CATCH_END_SECTION()
296 1 : }
297 :
298 :
299 3 : CATCH_TEST_CASE("compare_unicode_versions", "[valid][compare]")
300 : {
301 2 : CATCH_START_SECTION("compare_unicode_versions: compare many versions")
302 : {
303 2 : versiontheca::versiontheca::pointer_t a(create("1.2"));
304 2 : versiontheca::versiontheca::pointer_t b(create("1.1"));
305 2 : versiontheca::versiontheca::pointer_t c(create("1.2.0.0", "1.2")); // the zero are ignored by the compare
306 2 : versiontheca::versiontheca::pointer_t d(create("1.2.5", "1.2.5"));
307 :
308 1 : CATCH_REQUIRE(a->is_valid());
309 1 : CATCH_REQUIRE(b->is_valid());
310 1 : CATCH_REQUIRE(c->is_valid());
311 :
312 1 : CATCH_REQUIRE(*a == *a);
313 1 : CATCH_REQUIRE_FALSE(*a != *a);
314 1 : CATCH_REQUIRE_FALSE(*a > *a);
315 1 : CATCH_REQUIRE(*a >= *a);
316 1 : CATCH_REQUIRE_FALSE(*a < *a);
317 1 : CATCH_REQUIRE(*a <= *a);
318 :
319 1 : CATCH_REQUIRE_FALSE(*a == *b);
320 1 : CATCH_REQUIRE(*a != *b);
321 1 : CATCH_REQUIRE(*a > *b);
322 1 : CATCH_REQUIRE(*a >= *b);
323 1 : CATCH_REQUIRE_FALSE(*a < *b);
324 1 : CATCH_REQUIRE_FALSE(*a <= *b);
325 :
326 1 : CATCH_REQUIRE_FALSE(*b == *a);
327 1 : CATCH_REQUIRE(*b != *a);
328 1 : CATCH_REQUIRE_FALSE(*b > *a);
329 1 : CATCH_REQUIRE_FALSE(*b >= *a);
330 1 : CATCH_REQUIRE(*b < *a);
331 1 : CATCH_REQUIRE(*b <= *a);
332 :
333 1 : CATCH_REQUIRE(*a == *c);
334 1 : CATCH_REQUIRE_FALSE(*a != *c);
335 1 : CATCH_REQUIRE_FALSE(*a > *c);
336 1 : CATCH_REQUIRE(*a >= *c);
337 1 : CATCH_REQUIRE_FALSE(*a < *c);
338 1 : CATCH_REQUIRE(*a <= *c);
339 :
340 1 : CATCH_REQUIRE(*c == *a);
341 1 : CATCH_REQUIRE_FALSE(*c != *a);
342 1 : CATCH_REQUIRE_FALSE(*c > *a);
343 1 : CATCH_REQUIRE(*c >= *a);
344 1 : CATCH_REQUIRE_FALSE(*c < *a);
345 1 : CATCH_REQUIRE(*c <= *a);
346 :
347 1 : CATCH_REQUIRE_FALSE(*a == *d);
348 1 : CATCH_REQUIRE(*a != *d);
349 1 : CATCH_REQUIRE_FALSE(*a > *d);
350 1 : CATCH_REQUIRE_FALSE(*a >= *d);
351 1 : CATCH_REQUIRE(*a < *d);
352 1 : CATCH_REQUIRE(*a <= *d);
353 :
354 1 : CATCH_REQUIRE_FALSE(*d == *a);
355 1 : CATCH_REQUIRE(*d != *a);
356 1 : CATCH_REQUIRE(*d > *a);
357 1 : CATCH_REQUIRE(*d >= *a);
358 1 : CATCH_REQUIRE_FALSE(*d < *a);
359 1 : CATCH_REQUIRE_FALSE(*d <= *a);
360 :
361 : {
362 2 : std::stringstream ss;
363 1 : ss << *a;
364 1 : CATCH_REQUIRE(ss.str() == "1.2");
365 : }
366 : {
367 2 : std::stringstream ss;
368 1 : ss << *b;
369 1 : CATCH_REQUIRE(ss.str() == "1.1");
370 : }
371 : {
372 2 : std::stringstream ss;
373 1 : ss << *c;
374 1 : CATCH_REQUIRE(ss.str() == "1.2");
375 : }
376 : }
377 : CATCH_END_SECTION()
378 1 : }
379 :
380 :
381 7 : CATCH_TEST_CASE("invalid_unicode_versions", "[invalid]")
382 : {
383 10 : CATCH_START_SECTION("invalid_unicode_versions: empty")
384 : {
385 : // empty
386 : //
387 : // note: the empty version is "invalid" as far as versions go,
388 : // but it does not generetate an error message
389 : //
390 2 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
391 2 : versiontheca::versiontheca v(t, "");
392 1 : CATCH_REQUIRE_FALSE(v.is_valid());
393 1 : CATCH_REQUIRE(v.get_last_error().empty());
394 :
395 1 : CATCH_REQUIRE(v.get_version().empty());
396 1 : CATCH_REQUIRE(v.get_last_error() == "no parts to output.");
397 : }
398 : CATCH_END_SECTION()
399 :
400 10 : CATCH_START_SECTION("invalid_unicode_versions: two periods, period at the start or end")
401 : {
402 : // most characters are valid in Unicode versions
403 : // we still have a few cases of invalid entry
404 : //
405 1 : invalid_version("3A3:1.2..3-pre55", "a version value cannot be an empty string.");
406 1 : invalid_version(".33:-55", "a version value cannot be an empty string.");
407 1 : invalid_version(":.", "a version value cannot be an empty string.");
408 : }
409 : CATCH_END_SECTION()
410 :
411 10 : CATCH_START_SECTION("invalid_unicode_versions: randomized")
412 : {
413 : // check all invalid unicode characters
414 : //
415 1114112 : for(char32_t c(1); c < 0x110000; ++c)
416 : {
417 1114111 : if(libutf8::is_valid_unicode(c, false))
418 : {
419 : // skip valid characters
420 : //
421 1111999 : continue;
422 : }
423 2112 : std::size_t const max_parts(rand() % (versiontheca::MAX_PARTS - 1) + 1);
424 2112 : std::size_t bad_pos(rand() % (max_parts * 5));
425 4224 : std::string v;
426 2112 : std::size_t count(0);
427 2112 : if(count == bad_pos)
428 : {
429 64 : v += wctombs(c);
430 : }
431 28892 : for(std::size_t part_no(0); part_no < max_parts; ++part_no)
432 : {
433 26780 : if(part_no != 0)
434 : {
435 24668 : v += '.';
436 : }
437 26780 : std::size_t const length(rand() % 10 + 1);
438 174131 : for(std::size_t l(0); l < length; ++l)
439 : {
440 147351 : char32_t wc(U'\0');
441 8 : do
442 : {
443 147359 : wc = SNAP_CATCH2_NAMESPACE::random_char(SNAP_CATCH2_NAMESPACE::character_t::CHARACTER_UNICODE);
444 : }
445 147359 : while(wc == '.' || wc < 0x20 || (wc >= 0x7F && wc <= 0x9F));
446 147351 : v += libutf8::to_u8string(wc);
447 147351 : ++count;
448 147351 : if(count == bad_pos)
449 : {
450 1977 : v += wctombs(c);
451 : }
452 : }
453 : }
454 2112 : if(bad_pos >= count)
455 : {
456 93 : v += wctombs(c);
457 : }
458 4224 : std::stringstream last_error;
459 2112 : if(c >= 0xD800 && c <= 0xDFFF)
460 : {
461 2048 : last_error << "input string includes an invalid code not representing a valid UTF-8 character.";
462 : }
463 : else
464 : {
465 64 : last_error << "found unexpected character: \\U"
466 64 : << std::hex << std::uppercase << std::setfill('0')
467 64 : << std::setw(6) << static_cast<int>(c)
468 64 : << " in input.";
469 : }
470 4224 : versiontheca::versiontheca::pointer_t a(invalid_version(v.c_str(), last_error.str().c_str()));
471 2112 : if(max_parts < versiontheca::MAX_PARTS - 1)
472 : {
473 : // make sure the parse_value() function catches invalid Unicode
474 : //
475 2026 : CATCH_REQUIRE_FALSE(a->get_trait()->parse_value(v, U'.'));
476 : }
477 : }
478 : }
479 : CATCH_END_SECTION()
480 :
481 10 : CATCH_START_SECTION("invalid_unicode_versions: max + 1 fails")
482 : {
483 2 : versiontheca::versiontheca::pointer_t a(create("4294967295.4294967295.4294967295"));
484 1 : CATCH_REQUIRE(a->is_valid());
485 1 : CATCH_REQUIRE_FALSE(a->next(2));
486 1 : CATCH_REQUIRE_FALSE(a->is_valid());
487 1 : CATCH_REQUIRE(a->get_last_error() == "maximum limit reached; cannot increment version any further.");
488 : }
489 : CATCH_END_SECTION()
490 :
491 10 : CATCH_START_SECTION("invalid_unicode_versions: min - 1 fails")
492 : {
493 2 : versiontheca::versiontheca::pointer_t a(create("0.0"));
494 1 : CATCH_REQUIRE(a->is_valid());
495 1 : CATCH_REQUIRE_FALSE(a->previous(2));
496 1 : CATCH_REQUIRE_FALSE(a->is_valid());
497 1 : CATCH_REQUIRE(a->get_last_error() == "minimum limit reached; cannot decrement version any further.");
498 : }
499 : CATCH_END_SECTION()
500 5 : }
501 :
502 :
503 10 : CATCH_TEST_CASE("bad_unicode_calls", "[invalid]")
504 : {
505 16 : CATCH_START_SECTION("bad_unicode_calls: next without a version")
506 : {
507 2 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
508 2 : versiontheca::versiontheca v(t);
509 1 : CATCH_REQUIRE(v.next(0));
510 1 : CATCH_REQUIRE(v.get_last_error() == "");
511 1 : CATCH_REQUIRE(v.get_version() == "1.0");
512 : }
513 : CATCH_END_SECTION()
514 :
515 16 : CATCH_START_SECTION("bad_unicode_calls: previous without a version")
516 : {
517 2 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
518 2 : versiontheca::versiontheca v(t);
519 1 : CATCH_REQUIRE_FALSE(v.previous(0));
520 1 : CATCH_REQUIRE(v.get_last_error() == "minimum limit reached; cannot decrement version any further.");
521 : }
522 : CATCH_END_SECTION()
523 :
524 16 : CATCH_START_SECTION("bad_unicode_calls: next out of bounds")
525 : {
526 2 : versiontheca::versiontheca::pointer_t a(create("1.5.3"));
527 101 : for(int p(-100); p < 0; ++p)
528 : {
529 100 : CATCH_REQUIRE_THROWS_MATCHES(
530 : a->next(p)
531 : , versiontheca::invalid_parameter
532 : , Catch::Matchers::ExceptionMessage(
533 : "versiontheca_exception: position calling next() cannot be a negative number."));
534 : }
535 101 : for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
536 : {
537 100 : CATCH_REQUIRE_THROWS_MATCHES(
538 : a->next(p)
539 : , versiontheca::invalid_parameter
540 : , Catch::Matchers::ExceptionMessage(
541 : "versiontheca_exception: position calling next() cannot be more than "
542 : + std::to_string(versiontheca::MAX_PARTS)
543 : + "."));
544 : }
545 : }
546 : CATCH_END_SECTION()
547 :
548 16 : CATCH_START_SECTION("bad_unicode_calls: previous out of bounds")
549 : {
550 2 : versiontheca::versiontheca::pointer_t a(create("1.5.3"));
551 101 : for(int p(-100); p < 0; ++p)
552 : {
553 100 : CATCH_REQUIRE_THROWS_MATCHES(
554 : a->previous(p)
555 : , versiontheca::invalid_parameter
556 : , Catch::Matchers::ExceptionMessage(
557 : "versiontheca_exception: position calling previous() cannot be a negative number."));
558 : }
559 101 : for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
560 : {
561 100 : CATCH_REQUIRE_THROWS_MATCHES(
562 : a->previous(p)
563 : , versiontheca::invalid_parameter
564 : , Catch::Matchers::ExceptionMessage(
565 : "versiontheca_exception: position calling previous() cannot be more than "
566 : + std::to_string(versiontheca::MAX_PARTS)
567 : + "."));
568 : }
569 : }
570 : CATCH_END_SECTION()
571 :
572 16 : CATCH_START_SECTION("bad_unicode_calls: compare against an empty (invalid) version")
573 : {
574 2 : versiontheca::versiontheca::pointer_t a(create("1.2"));
575 2 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
576 2 : versiontheca::versiontheca empty(t, "");
577 :
578 1 : CATCH_REQUIRE(a->is_valid());
579 1 : CATCH_REQUIRE_FALSE(empty.is_valid());
580 :
581 1 : CATCH_REQUIRE_THROWS_MATCHES(
582 : a->compare(empty)
583 : , versiontheca::invalid_version
584 : , Catch::Matchers::ExceptionMessage(
585 : "versiontheca_exception: one or both of the input versions are not valid."));
586 :
587 1 : CATCH_REQUIRE_THROWS_MATCHES(
588 : a->get_trait()->compare(t)
589 : , versiontheca::empty_version
590 : , Catch::Matchers::ExceptionMessage(
591 : "versiontheca_exception: one or both of the input versions are empty."));
592 : }
593 : CATCH_END_SECTION()
594 :
595 16 : CATCH_START_SECTION("bad_unicode_calls: compare using an empty (invalid) version")
596 : {
597 2 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
598 2 : versiontheca::versiontheca empty(t, "");
599 2 : versiontheca::versiontheca::pointer_t b(create("5.3"));
600 :
601 1 : CATCH_REQUIRE_FALSE(empty.is_valid());
602 1 : CATCH_REQUIRE(b->is_valid());
603 :
604 1 : CATCH_REQUIRE(empty.get_major() == 0);
605 1 : CATCH_REQUIRE(empty.get_minor() == 0);
606 1 : CATCH_REQUIRE(empty.get_patch() == 0);
607 1 : CATCH_REQUIRE(empty.get_build() == 0);
608 :
609 1 : CATCH_REQUIRE_THROWS_MATCHES(
610 : empty.compare(*b)
611 : , versiontheca::invalid_version
612 : , Catch::Matchers::ExceptionMessage(
613 : "versiontheca_exception: one or both of the input versions are not valid."));
614 :
615 1 : CATCH_REQUIRE_THROWS_MATCHES(
616 : t->compare(b->get_trait())
617 : , versiontheca::empty_version
618 : , Catch::Matchers::ExceptionMessage(
619 : "versiontheca_exception: one or both of the input versions are empty."));
620 : }
621 : CATCH_END_SECTION()
622 :
623 16 : CATCH_START_SECTION("bad_unicode_calls: too many parts")
624 : {
625 : // up to MAX_PARTS, it works
626 : //
627 2 : std::string version("1"); // version "1" is a special case
628 1 : create(version.c_str(), "1.0");
629 1 : std::size_t idx(2);
630 49 : for(; idx <= versiontheca::MAX_PARTS; ++idx)
631 : {
632 24 : version += '.';
633 24 : version += std::to_string(idx);
634 24 : create(version.c_str());
635 : }
636 :
637 : // when more than MAX_PARTS, it throws
638 : //
639 199 : for(; idx < versiontheca::MAX_PARTS + 100; ++idx)
640 : {
641 99 : version += '.';
642 99 : version += std::to_string(idx);
643 99 : CATCH_REQUIRE_THROWS_MATCHES(
644 : create(version.c_str())
645 : , versiontheca::invalid_parameter
646 : , Catch::Matchers::ExceptionMessage(
647 : "versiontheca_exception: trying to append more parts when maximum was already reached."));
648 : }
649 : }
650 : CATCH_END_SECTION()
651 :
652 16 : CATCH_START_SECTION("bad_unicode_calls: next/erase out of bounds")
653 : {
654 2 : versiontheca::unicode::pointer_t t(std::make_shared<versiontheca::unicode>());
655 1 : CATCH_REQUIRE_FALSE(t->parse(std::string()));
656 1 : CATCH_REQUIRE(t->get_last_error() == "an empty input string cannot represent a valid version.");
657 1 : CATCH_REQUIRE(t->get_last_error().empty());
658 : }
659 : CATCH_END_SECTION()
660 14 : }
661 :
662 :
663 :
664 : // vim: ts=4 sw=4 et
|