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 : // tested file
20 : //
21 : #include "versiontheca/basic.h"
22 :
23 :
24 : // self
25 : //
26 : #include "catch_main.h"
27 :
28 :
29 :
30 : // versiontheca
31 : //
32 : #include "versiontheca/exception.h"
33 : #include "versiontheca/versiontheca.h"
34 :
35 :
36 : // C++
37 : //
38 : #include <cstring>
39 : #include <iomanip>
40 : #include <stdexcept>
41 :
42 :
43 :
44 :
45 : namespace
46 : {
47 :
48 :
49 :
50 10018 : versiontheca::versiontheca::pointer_t create(char const * version, char const * verify = nullptr)
51 : {
52 20036 : versiontheca::basic::pointer_t t(std::make_shared<versiontheca::basic>());
53 10018 : versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
54 10018 : CATCH_REQUIRE(v->get_version() == (verify == nullptr ? version : verify));
55 20036 : return v;
56 : }
57 :
58 :
59 22 : void invalid_version(char const * version, char const * errmsg)
60 : {
61 44 : versiontheca::basic::pointer_t t(std::make_shared<versiontheca::basic>());
62 44 : versiontheca::versiontheca::pointer_t v(std::make_shared<versiontheca::versiontheca>(t, version));
63 22 : CATCH_REQUIRE_FALSE(v->is_valid());
64 22 : CATCH_REQUIRE(v->get_last_error(false) == errmsg);
65 22 : CATCH_REQUIRE(v->get_last_error() == errmsg);
66 22 : CATCH_REQUIRE(v->get_last_error().empty());
67 22 : }
68 :
69 :
70 130000 : std::string generate_number()
71 : {
72 130000 : versiontheca::part_integer_t value;
73 130000 : SNAP_CATCH2_NAMESPACE::random(value);
74 130000 : return std::to_string(value);
75 : }
76 :
77 :
78 10000 : std::string generate_version(std::size_t max)
79 : {
80 10000 : std::string v;
81 140000 : for(std::size_t i(0); i < max; ++i)
82 : {
83 130000 : if(!v.empty())
84 : {
85 120000 : v += '.';
86 : }
87 130000 : v += generate_number();
88 : }
89 10000 : return v;
90 : }
91 :
92 :
93 :
94 : }
95 : // no name namespace
96 :
97 :
98 5 : CATCH_TEST_CASE("basic_versions", "[valid]")
99 : {
100 6 : CATCH_START_SECTION("basic_versions: verify test checker for version 1.0")
101 : {
102 1 : create("1.0");
103 : }
104 : CATCH_END_SECTION()
105 :
106 6 : CATCH_START_SECTION("basic_versions: verify that canonicalization happens")
107 : {
108 : {
109 2 : versiontheca::versiontheca::pointer_t v(create("3", "3.0"));
110 1 : CATCH_REQUIRE(v->get_major() == 3);
111 1 : CATCH_REQUIRE(v->get_minor() == 0);
112 1 : CATCH_REQUIRE(v->get_patch() == 0);
113 1 : CATCH_REQUIRE(v->get_build() == 0);
114 : }
115 : {
116 2 : versiontheca::versiontheca::pointer_t v(create("1.0.0", "1.0"));
117 1 : CATCH_REQUIRE(v->get_major() == 1);
118 1 : CATCH_REQUIRE(v->get_minor() == 0);
119 1 : CATCH_REQUIRE(v->get_patch() == 0);
120 1 : CATCH_REQUIRE(v->get_build() == 0);
121 : }
122 : {
123 2 : versiontheca::versiontheca::pointer_t v(create("1.0.0.0", "1.0"));
124 1 : CATCH_REQUIRE(v->get_major() == 1);
125 1 : CATCH_REQUIRE(v->get_minor() == 0);
126 1 : CATCH_REQUIRE(v->get_patch() == 0);
127 1 : CATCH_REQUIRE(v->get_build() == 0);
128 : }
129 : }
130 : CATCH_END_SECTION()
131 :
132 6 : CATCH_START_SECTION("basic_versions: many valid versions")
133 : {
134 : // many valid versions generated randomly to increase the likelyhood
135 : // of things I would otherwise not think of
136 : //
137 10001 : for(int i(0); i < 10'000; ++i)
138 : {
139 10000 : int const parts(i % 25 + 1);
140 10000 : if(parts == 1)
141 : {
142 800 : std::string v(generate_version(parts));
143 400 : create(v.c_str(), (v + ".0").c_str());
144 : }
145 : else
146 : {
147 19200 : std::string v(generate_version(parts));
148 9600 : create(v.c_str());
149 : }
150 : }
151 : }
152 : CATCH_END_SECTION()
153 3 : }
154 :
155 :
156 3 : CATCH_TEST_CASE("next_previous_basic_versions", "[valid][next][previous]")
157 : {
158 2 : CATCH_START_SECTION("next_previous_basic_versions: next/previous at level 4, 3, 2, 1, 0")
159 : {
160 : {
161 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
162 1 : CATCH_REQUIRE(a->next(4));
163 1 : CATCH_REQUIRE(a->get_version() == "1.3.2.0.1");
164 1 : CATCH_REQUIRE(a->previous(4));
165 1 : CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1, back to original
166 1 : CATCH_REQUIRE(a->previous(4));
167 1 : CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295.4294967295");
168 1 : CATCH_REQUIRE(a->get_major() == 1);
169 1 : CATCH_REQUIRE(a->get_minor() == 3);
170 1 : CATCH_REQUIRE(a->get_patch() == 1);
171 1 : CATCH_REQUIRE(a->get_build() == 4294967295);
172 1 : CATCH_REQUIRE(a->next(4));
173 1 : CATCH_REQUIRE(a->get_version() == "1.3.2"); // +1 -1 -1 +1, back to original
174 : }
175 :
176 : {
177 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
178 1 : CATCH_REQUIRE(a->next(3));
179 1 : CATCH_REQUIRE(a->get_version() == "1.3.2.1");
180 1 : CATCH_REQUIRE(a->previous(3));
181 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
182 1 : CATCH_REQUIRE(a->previous(3));
183 1 : CATCH_REQUIRE(a->get_version() == "1.3.1.4294967295");
184 1 : CATCH_REQUIRE(a->next(3));
185 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
186 : }
187 :
188 : {
189 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
190 1 : CATCH_REQUIRE(a->next(2));
191 1 : CATCH_REQUIRE(a->get_version() == "1.3.3");
192 1 : CATCH_REQUIRE(a->previous(2));
193 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
194 1 : CATCH_REQUIRE(a->previous(2));
195 1 : CATCH_REQUIRE(a->get_version() == "1.3.1");
196 1 : CATCH_REQUIRE(a->next(2));
197 1 : CATCH_REQUIRE(a->get_version() == "1.3.2");
198 : }
199 :
200 : {
201 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
202 1 : CATCH_REQUIRE(a->next(1));
203 1 : CATCH_REQUIRE(a->get_version() == "1.4");
204 1 : CATCH_REQUIRE(a->previous(1));
205 1 : CATCH_REQUIRE(a->get_version() == "1.3");
206 1 : CATCH_REQUIRE(a->previous(1));
207 1 : CATCH_REQUIRE(a->get_version() == "1.2");
208 1 : CATCH_REQUIRE(a->next(1));
209 1 : CATCH_REQUIRE(a->get_version() == "1.3");
210 : }
211 :
212 : {
213 2 : versiontheca::versiontheca::pointer_t a(create("1.3.2"));
214 1 : CATCH_REQUIRE(a->next(0));
215 1 : CATCH_REQUIRE(a->get_version() == "2.0");
216 1 : CATCH_REQUIRE(a->previous(0));
217 1 : CATCH_REQUIRE(a->get_version() == "1.0");
218 1 : CATCH_REQUIRE(a->previous(0));
219 1 : CATCH_REQUIRE(a->get_version() == "0.0");
220 1 : CATCH_REQUIRE(a->next(0));
221 1 : CATCH_REQUIRE(a->get_version() == "1.0");
222 : }
223 : }
224 : CATCH_END_SECTION()
225 1 : }
226 :
227 :
228 3 : CATCH_TEST_CASE("compare_basic_versions", "[valid][compare]")
229 : {
230 2 : CATCH_START_SECTION("compare_basic_versions: compare many versions")
231 : {
232 2 : versiontheca::versiontheca::pointer_t a(create("1.2"));
233 2 : versiontheca::versiontheca::pointer_t b(create("1.1"));
234 2 : versiontheca::versiontheca::pointer_t c(create("1.2.0.0", "1.2")); // the zero are ignored by the compare
235 :
236 1 : CATCH_REQUIRE(a->is_valid());
237 1 : CATCH_REQUIRE(b->is_valid());
238 1 : CATCH_REQUIRE(c->is_valid());
239 :
240 1 : CATCH_REQUIRE(*a == *a);
241 1 : CATCH_REQUIRE_FALSE(*a != *a);
242 1 : CATCH_REQUIRE_FALSE(*a > *a);
243 1 : CATCH_REQUIRE(*a >= *a);
244 1 : CATCH_REQUIRE_FALSE(*a < *a);
245 1 : CATCH_REQUIRE(*a <= *a);
246 :
247 1 : CATCH_REQUIRE_FALSE(*a == *b);
248 1 : CATCH_REQUIRE(*a != *b);
249 1 : CATCH_REQUIRE(*a > *b);
250 1 : CATCH_REQUIRE(*a >= *b);
251 1 : CATCH_REQUIRE_FALSE(*a < *b);
252 1 : CATCH_REQUIRE_FALSE(*a <= *b);
253 :
254 1 : CATCH_REQUIRE_FALSE(*b == *a);
255 1 : CATCH_REQUIRE(*b != *a);
256 1 : CATCH_REQUIRE_FALSE(*b > *a);
257 1 : CATCH_REQUIRE_FALSE(*b >= *a);
258 1 : CATCH_REQUIRE(*b < *a);
259 1 : CATCH_REQUIRE(*b <= *a);
260 :
261 1 : CATCH_REQUIRE(*a == *c);
262 1 : CATCH_REQUIRE_FALSE(*a != *c);
263 1 : CATCH_REQUIRE_FALSE(*a > *c);
264 1 : CATCH_REQUIRE(*a >= *c);
265 1 : CATCH_REQUIRE_FALSE(*a < *c);
266 1 : CATCH_REQUIRE(*a <= *c);
267 :
268 1 : CATCH_REQUIRE(*c == *a);
269 1 : CATCH_REQUIRE_FALSE(*c != *a);
270 1 : CATCH_REQUIRE_FALSE(*c > *a);
271 1 : CATCH_REQUIRE(*c >= *a);
272 1 : CATCH_REQUIRE_FALSE(*c < *a);
273 1 : CATCH_REQUIRE(*c <= *a);
274 :
275 : {
276 2 : std::stringstream ss;
277 1 : ss << *a;
278 1 : CATCH_REQUIRE(ss.str() == "1.2");
279 : }
280 : {
281 2 : std::stringstream ss;
282 1 : ss << *b;
283 1 : CATCH_REQUIRE(ss.str() == "1.1");
284 : }
285 : {
286 2 : std::stringstream ss;
287 1 : ss << *c;
288 1 : CATCH_REQUIRE(ss.str() == "1.2");
289 : }
290 : }
291 : CATCH_END_SECTION()
292 1 : }
293 :
294 :
295 6 : CATCH_TEST_CASE("invalid_basic_versions", "[invalid]")
296 : {
297 8 : CATCH_START_SECTION("invalid_basic_versions: empty")
298 : {
299 : // empty
300 : //
301 : // note: the empty version is "invalid" as far as versions go,
302 : // but it does not generetate an error message
303 : //
304 2 : versiontheca::basic::pointer_t t(std::make_shared<versiontheca::basic>());
305 2 : versiontheca::versiontheca v(t, "");
306 1 : CATCH_REQUIRE_FALSE(v.is_valid());
307 1 : CATCH_REQUIRE(v.get_last_error().empty());
308 :
309 1 : CATCH_REQUIRE(v.get_version().empty());
310 1 : CATCH_REQUIRE(v.get_last_error() == "no parts to output.");
311 : }
312 : CATCH_END_SECTION()
313 :
314 8 : CATCH_START_SECTION("invalid_basic_versions: no support for ':' or '-' or '#' or '$'...")
315 : {
316 1 : invalid_version("3A3:1.2.3-pre55", "basic versions only support integers separated by periods (.).");
317 1 : invalid_version("33:-55", "basic versions only support integers separated by periods (.).");
318 1 : invalid_version(":", "basic versions only support integers separated by periods (.).");
319 1 : invalid_version("a:", "basic versions only support integers separated by periods (.).");
320 1 : invalid_version("-10:", "basic versions only support integers separated by periods (.).");
321 1 : invalid_version("99999999999999999:", "integer too large for a valid version.");
322 1 : invalid_version("3:", "basic versions only support integers separated by periods (.).");
323 1 : invalid_version("-751", "basic versions only support integers separated by periods (.).");
324 1 : invalid_version("-", "basic versions only support integers separated by periods (.).");
325 1 : invalid_version("--", "basic versions only support integers separated by periods (.).");
326 1 : invalid_version("+-", "basic versions only support integers separated by periods (.).");
327 1 : invalid_version("#-", "basic versions only support integers separated by periods (.).");
328 1 : invalid_version("55:435123-", "basic versions only support integers separated by periods (.).");
329 1 : invalid_version("-a", "basic versions only support integers separated by periods (.).");
330 1 : invalid_version("-0", "basic versions only support integers separated by periods (.).");
331 1 : invalid_version("-+", "basic versions only support integers separated by periods (.).");
332 1 : invalid_version("-3$7", "basic versions only support integers separated by periods (.).");
333 1 : invalid_version("32:1.2.55-3:7", "basic versions only support integers separated by periods (.).");
334 1 : invalid_version("-3.7", "basic versions only support integers separated by periods (.).");
335 1 : invalid_version("3.7#", "basic versions only support integers separated by periods (.).");
336 1 : invalid_version("3$7", "basic versions only support integers separated by periods (.).");
337 1 : invalid_version("3;7", "basic versions only support integers separated by periods (.).");
338 : }
339 : CATCH_END_SECTION()
340 :
341 8 : CATCH_START_SECTION("invalid_basic_versions: max + 1 fails")
342 : {
343 2 : versiontheca::versiontheca::pointer_t a(create("4294967295.4294967295.4294967295"));
344 1 : CATCH_REQUIRE(a->is_valid());
345 1 : CATCH_REQUIRE_FALSE(a->next(2));
346 1 : CATCH_REQUIRE_FALSE(a->is_valid());
347 1 : CATCH_REQUIRE(a->get_last_error() == "maximum limit reached; cannot increment version any further.");
348 : }
349 : CATCH_END_SECTION()
350 :
351 8 : CATCH_START_SECTION("invalid_basic_versions: min - 1 fails")
352 : {
353 2 : versiontheca::versiontheca::pointer_t a(create("0.0"));
354 1 : CATCH_REQUIRE(a->is_valid());
355 1 : CATCH_REQUIRE_FALSE(a->previous(2));
356 1 : CATCH_REQUIRE_FALSE(a->is_valid());
357 1 : CATCH_REQUIRE(a->get_last_error() == "minimum limit reached; cannot decrement version any further.");
358 : }
359 : CATCH_END_SECTION()
360 4 : }
361 :
362 :
363 8 : CATCH_TEST_CASE("bad_basic_calls", "[invalid]")
364 : {
365 12 : CATCH_START_SECTION("bad_basic_calls: next without a version")
366 : {
367 2 : versiontheca::basic::pointer_t t(std::make_shared<versiontheca::basic>());
368 2 : versiontheca::versiontheca v(t);
369 1 : CATCH_REQUIRE(v.next(0));
370 1 : CATCH_REQUIRE(v.get_last_error() == "");
371 1 : CATCH_REQUIRE(v.get_version() == "1.0");
372 : }
373 : CATCH_END_SECTION()
374 :
375 12 : CATCH_START_SECTION("bad_basic_calls: previous without a version")
376 : {
377 2 : versiontheca::basic::pointer_t t(std::make_shared<versiontheca::basic>());
378 2 : versiontheca::versiontheca v(t);
379 1 : CATCH_REQUIRE_FALSE(v.previous(0));
380 1 : CATCH_REQUIRE(v.get_last_error() == "minimum limit reached; cannot decrement version any further.");
381 : }
382 : CATCH_END_SECTION()
383 :
384 12 : CATCH_START_SECTION("bad_basic_calls: next out of bounds")
385 : {
386 2 : versiontheca::versiontheca::pointer_t a(create("1.5.3"));
387 101 : for(int p(-100); p < 0; ++p)
388 : {
389 100 : CATCH_REQUIRE_THROWS_MATCHES(
390 : a->next(p)
391 : , versiontheca::invalid_parameter
392 : , Catch::Matchers::ExceptionMessage(
393 : "versiontheca_exception: position calling next() cannot be a negative number."));
394 : }
395 101 : for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
396 : {
397 100 : CATCH_REQUIRE_THROWS_MATCHES(
398 : a->next(p)
399 : , versiontheca::invalid_parameter
400 : , Catch::Matchers::ExceptionMessage(
401 : "versiontheca_exception: position calling next() cannot be more than "
402 : + std::to_string(versiontheca::MAX_PARTS)
403 : + "."));
404 : }
405 : }
406 : CATCH_END_SECTION()
407 :
408 12 : CATCH_START_SECTION("bad_basic_calls: previous out of bounds")
409 : {
410 2 : versiontheca::versiontheca::pointer_t a(create("1.5.3"));
411 101 : for(int p(-100); p < 0; ++p)
412 : {
413 100 : CATCH_REQUIRE_THROWS_MATCHES(
414 : a->previous(p)
415 : , versiontheca::invalid_parameter
416 : , Catch::Matchers::ExceptionMessage(
417 : "versiontheca_exception: position calling previous() cannot be a negative number."));
418 : }
419 101 : for(int p(versiontheca::MAX_PARTS); p < static_cast<int>(versiontheca::MAX_PARTS + 100); ++p)
420 : {
421 100 : CATCH_REQUIRE_THROWS_MATCHES(
422 : a->previous(p)
423 : , versiontheca::invalid_parameter
424 : , Catch::Matchers::ExceptionMessage(
425 : "versiontheca_exception: position calling previous() cannot be more than "
426 : + std::to_string(versiontheca::MAX_PARTS)
427 : + "."));
428 : }
429 : }
430 : CATCH_END_SECTION()
431 :
432 12 : CATCH_START_SECTION("bad_basic_calls: compare against an empty (invalid) version")
433 : {
434 2 : versiontheca::versiontheca::pointer_t a(create("1.2"));
435 2 : versiontheca::basic::pointer_t t(std::make_shared<versiontheca::basic>());
436 2 : versiontheca::versiontheca empty(t, "");
437 :
438 1 : CATCH_REQUIRE(a->is_valid());
439 1 : CATCH_REQUIRE_FALSE(empty.is_valid());
440 :
441 1 : CATCH_REQUIRE_THROWS_MATCHES(
442 : a->compare(empty)
443 : , versiontheca::invalid_version
444 : , Catch::Matchers::ExceptionMessage(
445 : "versiontheca_exception: one or both of the input versions are not valid."));
446 :
447 1 : CATCH_REQUIRE_THROWS_MATCHES(
448 : a->get_trait()->compare(t)
449 : , versiontheca::empty_version
450 : , Catch::Matchers::ExceptionMessage(
451 : "versiontheca_exception: one or both of the input versions are empty."));
452 : }
453 : CATCH_END_SECTION()
454 :
455 12 : CATCH_START_SECTION("bad_basic_calls: compare using an empty (invalid) version")
456 : {
457 2 : versiontheca::basic::pointer_t t(std::make_shared<versiontheca::basic>());
458 2 : versiontheca::versiontheca empty(t, "");
459 2 : versiontheca::versiontheca::pointer_t b(create("5.3"));
460 :
461 1 : CATCH_REQUIRE_FALSE(empty.is_valid());
462 1 : CATCH_REQUIRE(b->is_valid());
463 :
464 1 : CATCH_REQUIRE(empty.get_major() == 0);
465 1 : CATCH_REQUIRE(empty.get_minor() == 0);
466 1 : CATCH_REQUIRE(empty.get_patch() == 0);
467 1 : CATCH_REQUIRE(empty.get_build() == 0);
468 :
469 1 : CATCH_REQUIRE_THROWS_MATCHES(
470 : empty.compare(*b)
471 : , versiontheca::invalid_version
472 : , Catch::Matchers::ExceptionMessage(
473 : "versiontheca_exception: one or both of the input versions are not valid."));
474 :
475 1 : CATCH_REQUIRE_THROWS_MATCHES(
476 : t->compare(b->get_trait())
477 : , versiontheca::empty_version
478 : , Catch::Matchers::ExceptionMessage(
479 : "versiontheca_exception: one or both of the input versions are empty."));
480 : }
481 : CATCH_END_SECTION()
482 12 : }
483 :
484 :
485 :
486 : // vim: ts=4 sw=4 et
|