LCOV - code coverage report
Current view: top level - tests - catch_timespec_ex.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.0 % 804 796
Test Date: 2026-09-06 14:21:58 Functions: 100.0 % 8 8
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2018-2026  Made to Order Software Corp.  All Rights Reserved
       2              : //
       3              : // https://snapwebsites.org/project/snapdev
       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              : /** \file
      20              :  * \brief Verify that the timespec_ex operators function as expected.
      21              :  *
      22              :  * This file implements tests for the timespec_ex operator and other
      23              :  * functions.
      24              :  */
      25              : 
      26              : // self
      27              : //
      28              : #include    <snapdev/timespec_ex.h>
      29              : 
      30              : #include    "catch_main.h"
      31              : 
      32              : 
      33              : // snapdev
      34              : //
      35              : #include    <snapdev/not_reached.h>
      36              : 
      37              : 
      38              : // C++
      39              : //
      40              : #include    <iomanip>
      41              : 
      42              : 
      43              : // last include
      44              : //
      45              : #include    <snapdev/poison.h>
      46              : 
      47              : 
      48              : 
      49              : namespace
      50              : {
      51              : 
      52              : 
      53              : 
      54              : int g_state(-1);
      55              : int g_error(0);
      56              : 
      57            6 : char * wrap_nl_langinfo(nl_item item)
      58              : {
      59            6 :     switch(g_state++)
      60              :     {
      61            1 :     case 0:
      62            1 :         if(item != D_T_FMT)
      63              :         {
      64            0 :             g_error = 1;
      65              :         }
      66            1 :         return const_cast<char *>(""); // exercise the empty string
      67              : 
      68            1 :     case 10:
      69            1 :         if(item != D_T_FMT)
      70              :         {
      71            0 :             g_error = 1;
      72              :         }
      73            1 :         return const_cast<char *>("%T");
      74              : 
      75            1 :     case 20:
      76            1 :         if(item != D_T_FMT)
      77              :         {
      78            0 :             g_error = 1;
      79              :         }
      80            1 :         return const_cast<char *>("%r %X %EX");
      81              : 
      82            1 :     case 21:
      83            1 :         if(item != T_FMT_AMPM)
      84              :         {
      85            0 :             g_error = 1;
      86              :         }
      87            1 :         return const_cast<char *>("%I:%M:%S %p");
      88              : 
      89            1 :     case 22:
      90            1 :         if(item != T_FMT)
      91              :         {
      92            0 :             g_error = 1;
      93              :         }
      94            1 :         return const_cast<char *>("%H:%M:%S");
      95              : 
      96            1 :     case 23:
      97            1 :         if(item != ERA_T_FMT)
      98              :         {
      99            0 :             g_error = 1;
     100              :         }
     101            1 :         return const_cast<char *>("%H.%M.%S");
     102              : 
     103            0 :     default:
     104            0 :         throw std::logic_error("invalid state encountered.");
     105              : 
     106              :     }
     107              : 
     108              :     snapdev::NOT_REACHED();
     109              : }
     110              : 
     111              : 
     112              : 
     113              : }
     114              : // no name namespace
     115              : 
     116              : 
     117              : 
     118            3 : CATCH_TEST_CASE("timespec_ex_basics", "[time]")
     119              : {
     120            3 :     CATCH_START_SECTION("timespec_ex_basics: minimum")
     121              :     {
     122            1 :         snapdev::timespec_ex a(snapdev::timespec_ex::min());
     123              : 
     124            1 :         CATCH_REQUIRE(a.tv_sec == std::numeric_limits<decltype(a.tv_sec)>::min());
     125            1 :         CATCH_REQUIRE(a.tv_nsec == 0);
     126              :     }
     127            3 :     CATCH_END_SECTION()
     128              : 
     129            3 :     CATCH_START_SECTION("timespec_ex_basics: maximum")
     130              :     {
     131            1 :         snapdev::timespec_ex a(snapdev::timespec_ex::max());
     132              : 
     133            1 :         CATCH_REQUIRE(a.tv_sec == std::numeric_limits<decltype(a.tv_sec)>::max());
     134            1 :         CATCH_REQUIRE(a.tv_nsec == 999'999'999);
     135              :     }
     136            3 :     CATCH_END_SECTION()
     137              : 
     138            3 :     CATCH_START_SECTION("timespec_ex_basics: time is in the future")
     139              :     {
     140            1 :         snapdev::timespec_ex a(snapdev::now());
     141              : 
     142            1 :         CATCH_REQUIRE_FALSE(a.is_in_the_future());
     143              : 
     144            1 :         a += static_cast<std::int64_t>(10'000'000'000); // +10s
     145            1 :         CATCH_REQUIRE(a.is_in_the_future());
     146              :     }
     147            3 :     CATCH_END_SECTION()
     148            3 : }
     149              : 
     150              : 
     151           11 : CATCH_TEST_CASE("timespec_ex_math", "[time][math]")
     152              : {
     153           11 :     CATCH_START_SECTION("timespec_ex_math: simple add")
     154              :     {
     155            1 :         snapdev::timespec_ex a(timespec{  5L, 345L });
     156              : 
     157            1 :         snapdev::timespec_ex b;
     158            1 :         timespec raw{ 13L, 701L };
     159            1 :         b = raw;
     160              : 
     161            1 :         CATCH_REQUIRE(a.valid());
     162            1 :         CATCH_REQUIRE(b.valid());
     163            1 :         CATCH_REQUIRE_FALSE(a.negative());
     164            1 :         CATCH_REQUIRE_FALSE(b.negative());
     165              : 
     166            1 :         snapdev::timespec_ex c(a + b);
     167              : 
     168            1 :         a += b;
     169              : 
     170            1 :         CATCH_REQUIRE(a.tv_sec == 5L + 13L);
     171            1 :         CATCH_REQUIRE(a.tv_nsec == 345L + 701L);
     172              : 
     173            1 :         CATCH_REQUIRE(b.tv_sec == 13L);
     174            1 :         CATCH_REQUIRE(b.tv_nsec == 701L);
     175              : 
     176            1 :         CATCH_REQUIRE(c.tv_sec == 5L + 13L);
     177            1 :         CATCH_REQUIRE(c.tv_nsec == 345L + 701L);
     178            1 :         CATCH_REQUIRE(a == c);
     179            1 :         CATCH_REQUIRE(c == a);
     180              : 
     181            1 :         timespec d{ 3L, 55L };
     182              : 
     183            1 :         a += d;
     184              : 
     185            1 :         CATCH_REQUIRE(a.tv_sec == 5L + 13L + 3L);
     186            1 :         CATCH_REQUIRE(a.tv_nsec == 345L + 701L + 55L);
     187              : 
     188            1 :         a += 301L;
     189              : 
     190            1 :         CATCH_REQUIRE(a.tv_sec == 5L + 13L + 3L);
     191            1 :         CATCH_REQUIRE(a.tv_nsec == 345L + 701L + 55L + 301L);
     192              : 
     193            1 :         a -= 1'000'000'259L;
     194              : 
     195            1 :         CATCH_REQUIRE(a.tv_sec == 5L + 13L + 3L - 1L);
     196            1 :         CATCH_REQUIRE(a.tv_nsec == 345L + 701L + 55L + 301L - 259L);
     197              :     }
     198           11 :     CATCH_END_SECTION()
     199              : 
     200           11 :     CATCH_START_SECTION("timespec_ex_math: simple subtract")
     201              :     {
     202            1 :         snapdev::timespec_ex a(timespec{ 25L, 1'345L });
     203            1 :         snapdev::timespec_ex b(timespec{ 13L,   701L });
     204              : 
     205            1 :         CATCH_REQUIRE(a.valid());
     206            1 :         CATCH_REQUIRE(b.valid());
     207              : 
     208            1 :         snapdev::timespec_ex c(a - b);
     209            1 :         snapdev::timespec_ex d(-b);
     210            1 :         snapdev::timespec_ex e(a + d);
     211            1 :         snapdev::timespec_ex f(a - 1'000L);   // -1us
     212              : 
     213            1 :         a -= b;
     214              : 
     215            1 :         CATCH_REQUIRE(a.tv_sec == 25L - 13L);
     216            1 :         CATCH_REQUIRE(a.tv_nsec == 1'345L - 701L);
     217              : 
     218            1 :         CATCH_REQUIRE(b.tv_sec == 13L);
     219            1 :         CATCH_REQUIRE(b.tv_nsec == 701L);
     220              : 
     221            1 :         CATCH_REQUIRE(c.tv_sec == 25L - 13L);
     222            1 :         CATCH_REQUIRE(c.tv_nsec == 1'345L - 701L);
     223            1 :         CATCH_REQUIRE(a == c);
     224            1 :         CATCH_REQUIRE(c == a);
     225            1 :         CATCH_REQUIRE(a == e);
     226            1 :         CATCH_REQUIRE(e == c);
     227              : 
     228            1 :         CATCH_REQUIRE(f.tv_sec == 25L);
     229            1 :         CATCH_REQUIRE(f.tv_nsec == 1'345L - 1'000L);
     230              :     }
     231           11 :     CATCH_END_SECTION()
     232              : 
     233           11 :     CATCH_START_SECTION("timespec_ex_math: add \"minus one day\"")
     234              :     {
     235            1 :         snapdev::timespec_ex now(timespec{ 1629652541L, 345L });
     236            1 :         snapdev::timespec_ex backward(timespec{ -86400L, 0L }); // -1 day
     237              : 
     238            1 :         CATCH_REQUIRE(!!now);
     239            1 :         CATCH_REQUIRE_FALSE(!backward);
     240            1 :         CATCH_REQUIRE(backward.negative());
     241              : 
     242            1 :         now += backward;
     243              : 
     244            1 :         CATCH_REQUIRE(now.tv_sec == 1629652541L - 86400L);
     245            1 :         CATCH_REQUIRE(now.tv_nsec == 345L);
     246              : 
     247            1 :         CATCH_REQUIRE(backward.tv_sec == -86400L);
     248            1 :         CATCH_REQUIRE(backward.tv_nsec == 0L);
     249              :     }
     250           11 :     CATCH_END_SECTION()
     251              : 
     252           11 :     CATCH_START_SECTION("timespec_ex_math: add with nano overflow")
     253              :     {
     254            1 :         snapdev::timespec_ex now(timespec{ 1629652541L, 913'788'345L });
     255            1 :         snapdev::timespec_ex backward(timespec{ 86400L, 500'000'000L }); // +1 day and 0.5 seconds
     256              : 
     257            1 :         CATCH_REQUIRE(!!now);
     258            1 :         CATCH_REQUIRE(!!backward);
     259              : 
     260            1 :         now += backward;
     261              : 
     262            1 :         CATCH_REQUIRE(now.tv_sec == 1629652541L + 86400L + 1L);  // +1 for the overflow
     263            1 :         CATCH_REQUIRE(now.tv_nsec == 913'788'345L + 500'000'000L - 1'000'000'000L);
     264              : 
     265            1 :         CATCH_REQUIRE(backward.tv_sec == 86400L);
     266            1 :         CATCH_REQUIRE(backward.tv_nsec == 500'000'000L);
     267              :     }
     268           11 :     CATCH_END_SECTION()
     269              : 
     270           11 :     CATCH_START_SECTION("timespec_ex_math: subtract with nano underflow")
     271              :     {
     272            1 :         snapdev::timespec_ex a(13L,   701L);
     273            1 :         snapdev::timespec_ex b(25L, 1'345L);
     274              : 
     275            1 :         CATCH_REQUIRE(a.valid());
     276            1 :         CATCH_REQUIRE(b.valid());
     277              : 
     278            1 :         a -= b;
     279              : 
     280            1 :         CATCH_REQUIRE(a.tv_sec == 13L - 25L - 1L);
     281            1 :         CATCH_REQUIRE(a.tv_nsec == 701L - 1'345L + 1'000'000'000L);
     282              : 
     283            1 :         CATCH_REQUIRE(b.tv_sec == 25L);
     284            1 :         CATCH_REQUIRE(b.tv_nsec == 1'345L);
     285              :     }
     286           11 :     CATCH_END_SECTION()
     287              : 
     288           11 :     CATCH_START_SECTION("timespec_ex_math: -1, 0, +1")
     289              :     {
     290            1 :         snapdev::timespec_ex a = {};
     291              : 
     292            1 :         CATCH_REQUIRE(!a);
     293            1 :         CATCH_REQUIRE_FALSE(a.negative());
     294              : 
     295            1 :         --a;
     296              : 
     297            1 :         CATCH_REQUIRE_FALSE(!a);
     298            1 :         CATCH_REQUIRE(a.negative());
     299            1 :         CATCH_REQUIRE(a.tv_sec == -1L);
     300            1 :         CATCH_REQUIRE(a.tv_nsec == 999'999'999L);
     301              : 
     302            1 :         ++a;
     303              : 
     304            1 :         CATCH_REQUIRE(!a);
     305            1 :         CATCH_REQUIRE(a.tv_sec == 0L);
     306            1 :         CATCH_REQUIRE(a.tv_nsec == 0L);
     307              : 
     308            1 :         ++a;
     309              : 
     310            1 :         CATCH_REQUIRE(!!a);
     311            1 :         CATCH_REQUIRE_FALSE(a.negative());
     312            1 :         CATCH_REQUIRE(a.tv_sec == 0L);
     313            1 :         CATCH_REQUIRE(a.tv_nsec == 1L);
     314              : 
     315            1 :         --a;
     316              : 
     317            1 :         CATCH_REQUIRE(!a);
     318            1 :         CATCH_REQUIRE(a.tv_sec == 0L);
     319            1 :         CATCH_REQUIRE(a.tv_nsec == 0L);
     320              :     }
     321           11 :     CATCH_END_SECTION()
     322              : 
     323           11 :     CATCH_START_SECTION("timespec_ex_math: add nanos")
     324              :     {
     325            1 :         snapdev::timespec_ex now(1629652541L, 913'788'345L);
     326            1 :         std::int64_t nsec(500'000'000L);
     327              : 
     328            1 :         CATCH_REQUIRE(!!now);
     329              : 
     330            1 :         snapdev::timespec_ex sum(now + nsec);
     331              : 
     332            1 :         now += nsec;
     333              : 
     334            1 :         CATCH_REQUIRE(now.tv_sec == 1629652541L + 1L);  // +1 for the overflow
     335            1 :         CATCH_REQUIRE(now.tv_nsec == 913'788'345L + 500'000'000L - 1'000'000'000L);
     336              : 
     337            1 :         CATCH_REQUIRE(now == sum);
     338            1 :         CATCH_REQUIRE_FALSE(now != sum);
     339            1 :         CATCH_REQUIRE_FALSE(now < sum);
     340            1 :         CATCH_REQUIRE(now <= sum);
     341            1 :         CATCH_REQUIRE_FALSE(now > sum);
     342            1 :         CATCH_REQUIRE(now >= sum);
     343              : 
     344            1 :         CATCH_REQUIRE(nsec == 500'000'000L);
     345              : 
     346            1 :         nsec += 86400L * 1'000'000'000L;
     347              : 
     348            1 :         snapdev::timespec_ex total(sum + nsec);
     349              : 
     350            1 :         now += nsec;
     351              : 
     352            1 :         CATCH_REQUIRE(now.tv_sec == 1629652541L + 1L + 86400L);  // +1 for the overflow above
     353            1 :         CATCH_REQUIRE(now.tv_nsec == 913'788'345L + 500'000'000L - 1'000'000'000L + 500'000'000L);
     354              : 
     355            1 :         CATCH_REQUIRE(nsec == 500'000'000L + 86400L * 1'000'000'000L);
     356              : 
     357            1 :         CATCH_REQUIRE_FALSE(now == sum);
     358            1 :         CATCH_REQUIRE(now != sum);
     359            1 :         CATCH_REQUIRE_FALSE(now < sum);
     360            1 :         CATCH_REQUIRE_FALSE(now <= sum);
     361            1 :         CATCH_REQUIRE(now > sum);
     362            1 :         CATCH_REQUIRE(now >= sum);
     363              : 
     364            1 :         CATCH_REQUIRE(now == total);
     365            1 :         CATCH_REQUIRE_FALSE(now != total);
     366            1 :         CATCH_REQUIRE_FALSE(now < total);
     367            1 :         CATCH_REQUIRE(now <= total);
     368            1 :         CATCH_REQUIRE_FALSE(now > total);
     369            1 :         CATCH_REQUIRE(now >= total);
     370              : 
     371            1 :         snapdev::timespec_ex pre(++total);
     372              : 
     373            1 :         CATCH_REQUIRE(pre == total);
     374            1 :         CATCH_REQUIRE_FALSE(pre != total);
     375            1 :         CATCH_REQUIRE_FALSE(pre < total);
     376            1 :         CATCH_REQUIRE(pre <= total);
     377            1 :         CATCH_REQUIRE_FALSE(pre > total);
     378            1 :         CATCH_REQUIRE(pre >= total);
     379              : 
     380            1 :         CATCH_REQUIRE_FALSE(now == total);
     381            1 :         CATCH_REQUIRE(now != total);
     382            1 :         CATCH_REQUIRE(now < total);
     383            1 :         CATCH_REQUIRE(now <= total);
     384            1 :         CATCH_REQUIRE_FALSE(now > total);
     385            1 :         CATCH_REQUIRE_FALSE(now >= total);
     386              : 
     387            1 :         snapdev::timespec_ex post(now++);
     388              : 
     389            1 :         CATCH_REQUIRE_FALSE(post == total);
     390            1 :         CATCH_REQUIRE(post != total);
     391            1 :         CATCH_REQUIRE(post < total);
     392            1 :         CATCH_REQUIRE(post <= total);
     393            1 :         CATCH_REQUIRE_FALSE(post > total);
     394            1 :         CATCH_REQUIRE_FALSE(post >= total);
     395              : 
     396            1 :         CATCH_REQUIRE(now == total);
     397            1 :         CATCH_REQUIRE_FALSE(now != total);
     398            1 :         CATCH_REQUIRE_FALSE(now < total);
     399            1 :         CATCH_REQUIRE(now <= total);
     400            1 :         CATCH_REQUIRE_FALSE(now > total);
     401            1 :         CATCH_REQUIRE(now >= total);
     402              : 
     403            1 :         pre = --total;
     404              : 
     405            1 :         CATCH_REQUIRE(pre == total);
     406            1 :         CATCH_REQUIRE_FALSE(pre != total);
     407            1 :         CATCH_REQUIRE_FALSE(pre < total);
     408            1 :         CATCH_REQUIRE(pre <= total);
     409            1 :         CATCH_REQUIRE_FALSE(pre > total);
     410            1 :         CATCH_REQUIRE(pre >= total);
     411              : 
     412            1 :         CATCH_REQUIRE_FALSE(now == total);
     413            1 :         CATCH_REQUIRE(now != total);
     414            1 :         CATCH_REQUIRE_FALSE(now < total);
     415            1 :         CATCH_REQUIRE_FALSE(now <= total);
     416            1 :         CATCH_REQUIRE(now > total);
     417            1 :         CATCH_REQUIRE(now >= total);
     418              : 
     419            1 :         post = now--;
     420              : 
     421            1 :         CATCH_REQUIRE_FALSE(post == total);
     422            1 :         CATCH_REQUIRE(post != total);
     423            1 :         CATCH_REQUIRE_FALSE(post < total);
     424            1 :         CATCH_REQUIRE_FALSE(post <= total);
     425            1 :         CATCH_REQUIRE(post > total);
     426            1 :         CATCH_REQUIRE(post >= total);
     427              : 
     428            1 :         CATCH_REQUIRE(now == total);
     429            1 :         CATCH_REQUIRE_FALSE(now != total);
     430            1 :         CATCH_REQUIRE_FALSE(now < total);
     431            1 :         CATCH_REQUIRE(now <= total);
     432            1 :         CATCH_REQUIRE_FALSE(now > total);
     433            1 :         CATCH_REQUIRE(now >= total);
     434              : 
     435              :     }
     436           11 :     CATCH_END_SECTION()
     437              : 
     438           11 :     CATCH_START_SECTION("timespec_ex_math: load/save")
     439              :     {
     440            1 :         snapdev::timespec_ex now(1629652549L, 913'788'345L);
     441              : 
     442            1 :         std::int64_t nsec(now.to_nsec());
     443            1 :         CATCH_REQUIRE(nsec == 1629652549L * 1'000'000'000L + 913'788'345L);
     444              : 
     445            1 :         std::int64_t usec(now.to_usec());
     446            1 :         CATCH_REQUIRE(usec == 1629652549L * 1'000'000 + 913'788L);
     447              : 
     448            1 :         snapdev::timespec_ex save;
     449            1 :         CATCH_REQUIRE(!save);
     450            1 :         CATCH_REQUIRE(save.valid());
     451            1 :         save = nsec;
     452              : 
     453            1 :         CATCH_REQUIRE(nsec == 1629652549L * 1'000'000'000L + 913'788'345L);
     454            1 :         CATCH_REQUIRE(save.tv_sec == 1629652549L);
     455            1 :         CATCH_REQUIRE(save.tv_nsec == 913'788'345L);
     456              : 
     457            1 :         double seconds(33.0);
     458            1 :         save = seconds;
     459            1 :         CATCH_REQUIRE(save.tv_sec == 33L);
     460            1 :         CATCH_REQUIRE(save.tv_nsec == 0L);
     461              : 
     462            1 :         double precise_time(save.to_sec());
     463            1 :         bool precise_result(precise_time >= 33.0 && precise_time <= 33.0);
     464            1 :         CATCH_REQUIRE(precise_result);
     465              : 
     466            1 :         seconds = 81.325611932;
     467            1 :         save = seconds;
     468            1 :         CATCH_REQUIRE(save.tv_sec == 81L);
     469            1 :         CATCH_REQUIRE(save.tv_nsec == 325'611'932L);
     470              : 
     471            1 :         precise_time = save.to_sec();
     472            1 :         precise_result = precise_time >= 81.325611932 && precise_time <= 81.325611932;
     473            1 :         CATCH_REQUIRE(precise_result);
     474            1 :         CATCH_REQUIRE(save == 81.325611932);
     475            1 :         CATCH_REQUIRE_FALSE(save == 83.5);
     476            1 :         CATCH_REQUIRE(save != 83.5);
     477            1 :         CATCH_REQUIRE_FALSE(save != 81.325611932);
     478            1 :         CATCH_REQUIRE(save < 83.5);
     479            1 :         CATCH_REQUIRE(save <= 81.33);
     480            1 :         CATCH_REQUIRE(save <= 81.325611932);
     481            1 :         CATCH_REQUIRE(save > 71.5);
     482            1 :         CATCH_REQUIRE(save >= 81.324);
     483            1 :         CATCH_REQUIRE(save >= 81.325611932);
     484              : 
     485            1 :         snapdev::timespec_ex plus(save + 3.000101);
     486            1 :         precise_time = plus.to_sec();
     487            1 :         precise_result = precise_time >= 84.325712930 && precise_time <= 84.325712931; // we lose some precision here
     488              : //std::cerr << "precise = " << std::setprecision(20) << std::setw(9) << precise_time
     489              : //<< " expected " << 81.325611932 + 3.000101 << "\n";
     490            1 :         CATCH_REQUIRE(precise_result);
     491              : 
     492            1 :         save += 3.000101;
     493            1 :         precise_time = save.to_sec();
     494            1 :         precise_result = precise_time >= 84.325712930 && precise_time <= 84.325712931; // we lose some precision here
     495              : //std::cerr << "precise = " << std::setprecision(20) << std::setw(9) << precise_time
     496              : //<< " expected " << 81.325611932 + 3.000101 << "\n";
     497            1 :         CATCH_REQUIRE(precise_result);
     498            1 :         CATCH_REQUIRE(save == plus);
     499              : 
     500            1 :         snapdev::timespec_ex minus(save - 1.20050001);
     501            1 :         precise_time = minus.to_sec();
     502            1 :         precise_result = precise_time >= 83.125212920 && precise_time <= 83.125212921;
     503              : //std::cerr << "precise = " << std::setprecision(20) << std::setw(9) << precise_time
     504              : //<< " expected " << 81.325611932 + 3.000101 - 1.20050001 << "\n";
     505            1 :         CATCH_REQUIRE(precise_result);
     506              : 
     507            1 :         save -= 1.20050001;
     508            1 :         precise_time = save.to_sec();
     509            1 :         precise_result = precise_time >= 83.125212920 && precise_time <= 83.125212921;
     510              : //std::cerr << "precise = " << std::setprecision(20) << std::setw(9) << precise_time
     511              : //<< " expected " << 81.325611932 + 3.000101 - 1.20050001 << "\n";
     512            1 :         CATCH_REQUIRE(precise_result);
     513            1 :         CATCH_REQUIRE(save == minus);
     514              : 
     515            1 :         double neg(-701.445123421);
     516            1 :         save = neg;
     517            1 :         CATCH_REQUIRE(save.tv_sec == -702L);
     518            2 :         precise_result = save.tv_nsec >= -445'123'420L + 1'000'000'000L
     519            1 :                       || save.tv_nsec <= -445'123'422L + 1'000'000'000L;
     520            1 :         CATCH_REQUIRE(precise_result);
     521              :     }
     522           11 :     CATCH_END_SECTION()
     523              : 
     524           11 :     CATCH_START_SECTION("timespec_ex_math: negative + negative")
     525              :     {
     526            1 :         snapdev::timespec_ex pa(4511L, 913'788'345L);
     527            1 :         snapdev::timespec_ex pb( 311L, 301'225'198L);
     528              : 
     529            1 :         snapdev::timespec_ex na(-pa);
     530            1 :         snapdev::timespec_ex nb(-pb);
     531              : 
     532            1 :         snapdev::timespec_ex sum_ab(na + nb);
     533            1 :         snapdev::timespec_ex sum_ba(nb + na);
     534            1 :         snapdev::timespec_ex diff_ab(na - nb);
     535            1 :         snapdev::timespec_ex diff_ba(nb - na);
     536              : 
     537            1 :         CATCH_REQUIRE(sum_ab.tv_sec != -4511L + -311L);
     538            1 :         CATCH_REQUIRE(sum_ab.tv_sec == -4511L + -1L + -311L + -1L);
     539            1 :         CATCH_REQUIRE(sum_ab.tv_nsec != -913'788'345L + -301'225'198L);
     540            1 :         CATCH_REQUIRE(sum_ab.tv_nsec == (1'000'000'000 - 913'788'345L) + (1'000'000'000L - 301'225'198L));
     541              : 
     542            1 :         CATCH_REQUIRE(sum_ab       == (-4511L + -1L + -311L + -1L) * 1'000'000'000 + (1'000'000'000 - 913'788'345L) + (1'000'000'000L - 301'225'198L));
     543            1 :         CATCH_REQUIRE_FALSE(sum_ab != (-4511L + -1L + -311L + -1L) * 1'000'000'000 + (1'000'000'000 - 913'788'345L) + (1'000'000'000L - 301'225'198L));
     544            1 :         CATCH_REQUIRE_FALSE(sum_ab == (-4511L +  0L + -311L + -1L) * 1'000'000'000 + (1'000'000'000 - 913'788'345L) + (1'000'000'000L - 301'225'198L));
     545            1 :         CATCH_REQUIRE(sum_ab       != (-4511L + -1L + -311L + -1L) * 1'000'000'000 + (1'000'000'000 - 913'788'344L) + (1'000'000'000L - 301'225'198L));
     546              : 
     547            1 :         CATCH_REQUIRE(sum_ba.tv_sec != -311L + -4511L);
     548            1 :         CATCH_REQUIRE(sum_ba.tv_sec == -311L + -1L + -4511L + -1L);
     549            1 :         CATCH_REQUIRE(sum_ba.tv_nsec != -301'225'198L + -913'788'345L);
     550            1 :         CATCH_REQUIRE(sum_ba.tv_nsec == (1'000'000'000L - 301'225'198L) + (1'000'000'000 - 913'788'345L));
     551              : 
     552            1 :         CATCH_REQUIRE(diff_ab.tv_sec == -4511L + -1L + 311L);
     553            1 :         CATCH_REQUIRE(diff_ab.tv_nsec == (1'000'000'000 - 913'788'345L) + 301'225'198L);
     554              : 
     555            1 :         CATCH_REQUIRE(diff_ab       >= (-4511L + -1L + 311L) * 1'000'000'000 + (1'000'000'000 - 913'788'345L) + 301'225'198L);
     556            1 :         CATCH_REQUIRE_FALSE(diff_ab >  (-4511L + -1L + 311L) * 1'000'000'000 + (1'000'000'000 - 913'788'345L) + 301'225'198L);
     557            1 :         CATCH_REQUIRE(diff_ab       <= (-4511L + -1L + 311L) * 1'000'000'000 + (1'000'000'000 - 913'788'345L) + 301'225'198L);
     558            1 :         CATCH_REQUIRE_FALSE(diff_ab <  (-4511L + -1L + 311L) * 1'000'000'000 + (1'000'000'000 - 913'788'345L) + 301'225'198L);
     559              : 
     560            1 :         CATCH_REQUIRE(diff_ba.tv_sec == -311L + -1L + 4511L + 1L);
     561            1 :         CATCH_REQUIRE(diff_ba.tv_nsec == (((1'000'000'000 - 301'225'198L) + 913'788'345L) - 1'000'000'000));
     562              : 
     563            1 :         CATCH_REQUIRE(diff_ba       >= (-311L + -1L + 4511L + 1L) * 1'000'000'000 + (((1'000'000'000 - 301'225'198L) + 913'788'345L) - 1'000'000'000));
     564            1 :         CATCH_REQUIRE_FALSE(diff_ba >  (-311L + -1L + 4511L + 1L) * 1'000'000'000 + (((1'000'000'000 - 301'225'198L) + 913'788'345L) - 1'000'000'000));
     565            1 :         CATCH_REQUIRE(diff_ba       <= (-311L + -1L + 4511L + 1L) * 1'000'000'000 + (((1'000'000'000 - 301'225'198L) + 913'788'345L) - 1'000'000'000));
     566            1 :         CATCH_REQUIRE_FALSE(diff_ba <  (-311L + -1L + 4511L + 1L) * 1'000'000'000 + (((1'000'000'000 - 301'225'198L) + 913'788'345L) - 1'000'000'000));
     567              :     }
     568           11 :     CATCH_END_SECTION()
     569              : 
     570           11 :     CATCH_START_SECTION("timespec_ex_math: system time (gettime() function)")
     571              :     {
     572            1 :         snapdev::timespec_ex now;
     573            1 :         timespec verify = {};
     574              : 
     575            1 :         now = snapdev::timespec_ex::gettime();
     576            1 :         clock_gettime(CLOCK_REALTIME, &verify);
     577              : 
     578            1 :         snapdev::timespec_ex const diff(now - verify);
     579            1 :         snapdev::timespec_ex const max_diff(100L);
     580              : 
     581            1 :         CATCH_REQUIRE(diff < max_diff);
     582              :     }
     583           11 :     CATCH_END_SECTION()
     584              : 
     585           11 :     CATCH_START_SECTION("timespec_ex_math: system time (snapdev::now() function)")
     586              :     {
     587            1 :         timespec verify = {};
     588              : 
     589            1 :         snapdev::timespec_ex const now(snapdev::now());
     590            1 :         clock_gettime(CLOCK_REALTIME, &verify);
     591              : 
     592            1 :         snapdev::timespec_ex const diff(now - verify);
     593            1 :         snapdev::timespec_ex const max_diff(0, 100L);
     594              : 
     595            1 :         CATCH_REQUIRE(diff < max_diff);
     596              :     }
     597           11 :     CATCH_END_SECTION()
     598           11 : }
     599              : 
     600              : 
     601            2 : CATCH_TEST_CASE("timespec_ex_tm", "[time]")
     602              : {
     603            2 :     CATCH_START_SECTION("timespec_ex_tm: tm is canonical")
     604              :     {
     605         1001 :         for(int count(0); count < 1'000; ++count)
     606              :         {
     607         1000 :             time_t const org(snapdev::random(-1'000'000LL, 2'000'000'000LL));
     608              : 
     609         1000 :             struct tm t;
     610         1000 :             gmtime_r(&org, &t);
     611              : 
     612              :             // a brand new tm is always canonical
     613              :             //
     614         1000 :             CATCH_REQUIRE(snapdev::is_canonical_tm(t));
     615              : 
     616              :             // tweak second
     617              :             //
     618         1000 :             struct tm sec(t);
     619         1000 :             sec.tm_sec += 100;
     620         1000 :             CATCH_REQUIRE_FALSE(snapdev::is_canonical_tm(sec));
     621              : 
     622              :             // tweak minute
     623              :             //
     624         1000 :             struct tm min(t);
     625         1000 :             min.tm_min += 100;
     626         1000 :             CATCH_REQUIRE_FALSE(snapdev::is_canonical_tm(min));
     627              : 
     628              :             // tweak hour
     629              :             //
     630         1000 :             struct tm hour(t);
     631         1000 :             hour.tm_hour += 100;
     632         1000 :             CATCH_REQUIRE_FALSE(snapdev::is_canonical_tm(hour));
     633              : 
     634              :             // tweak mday
     635              :             //
     636         1000 :             struct tm mday(t);
     637         1000 :             mday.tm_mday += 100;
     638         1000 :             CATCH_REQUIRE_FALSE(snapdev::is_canonical_tm(mday));
     639              : 
     640              :             // tweak month
     641              :             //
     642         1000 :             struct tm mon(t);
     643         1000 :             mon.tm_mon += 100;
     644         1000 :             CATCH_REQUIRE_FALSE(snapdev::is_canonical_tm(mon));
     645              : 
     646              :             // tweak years, no effect
     647              :             //
     648         1000 :             struct tm year(t);
     649         1000 :             year.tm_year += 100;
     650         1000 :             CATCH_REQUIRE(snapdev::is_canonical_tm(year));
     651              : 
     652              :             // tweak wday, no effect
     653              :             //
     654         1000 :             struct tm wday(t);
     655         1000 :             wday.tm_wday += 100;
     656         1000 :             CATCH_REQUIRE(snapdev::is_canonical_tm(wday));
     657              : 
     658              :             // tweak yday, no effect
     659              :             //
     660         1000 :             struct tm yday(t);
     661         1000 :             yday.tm_yday += 100;
     662         1000 :             CATCH_REQUIRE(snapdev::is_canonical_tm(yday));
     663              :         }
     664              :     }
     665            2 :     CATCH_END_SECTION()
     666              : 
     667            2 :     CATCH_START_SECTION("timespec_ex_tm: compare tm")
     668              :     {
     669         1001 :         for(int count(0); count < 1'000; ++count)
     670              :         {
     671         1000 :             time_t const a(snapdev::random(-1'000'000LL, 2'000'000'000LL));
     672         1000 :             time_t const b(snapdev::random(-1'000'000LL, 2'000'000'000LL));
     673              : 
     674              :             // canonical values
     675              :             //
     676         1000 :             struct tm ta = {};
     677         1000 :             struct tm tb = {};
     678              : 
     679         1000 :             gmtime_r(&a, &ta);
     680         1000 :             gmtime_r(&b, &tb);
     681              : 
     682              :             // tweak second
     683              :             //
     684         1000 :             struct tm ta_sec(ta);
     685         1000 :             ta_sec.tm_sec += 100;
     686         1000 :             struct tm tb_sec(tb);
     687         1000 :             tb_sec.tm_sec += 100;
     688              : 
     689              :             // tweak minute
     690              :             //
     691         1000 :             struct tm ta_min(ta);
     692         1000 :             ta_min.tm_min += 100;
     693         1000 :             struct tm tb_min(tb);
     694         1000 :             tb_min.tm_min += 100;
     695              : 
     696              :             // tweak hour
     697              :             //
     698         1000 :             struct tm ta_hour(ta);
     699         1000 :             ta_hour.tm_hour += 100;
     700         1000 :             struct tm tb_hour(tb);
     701         1000 :             tb_hour.tm_hour += 100;
     702              : 
     703              :             // tweak mday
     704              :             //
     705         1000 :             struct tm ta_mday(ta);
     706         1000 :             ta_mday.tm_mday += 100;
     707         1000 :             struct tm tb_mday(tb);
     708         1000 :             tb_mday.tm_mday += 100;
     709              : 
     710              :             // tweak month
     711              :             //
     712         1000 :             struct tm ta_mon(ta);
     713         1000 :             ta_mon.tm_mon += 100;
     714         1000 :             struct tm tb_mon(tb);
     715         1000 :             tb_mon.tm_mon += 100;
     716              : 
     717         1000 :             if(a < b)
     718              :             {
     719          496 :                 CATCH_REQUIRE(snapdev::compare_tm(ta, tb) == -1);
     720          496 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_sec, tb_sec) == -1);
     721          496 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_min, tb_min) == -1);
     722          496 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_hour, tb_hour) == -1);
     723          496 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mday, tb_mday) == -1);
     724          496 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mon, tb_mon) == -1);
     725              : 
     726          496 :                 CATCH_REQUIRE(ta < tb);
     727          496 :                 CATCH_REQUIRE(ta <= tb);
     728          496 :                 CATCH_REQUIRE_FALSE(ta > tb);
     729          496 :                 CATCH_REQUIRE_FALSE(ta >= tb);
     730          496 :                 CATCH_REQUIRE_FALSE(ta == tb);
     731          496 :                 CATCH_REQUIRE(ta != tb);
     732              :             }
     733          504 :             else if(a > b)
     734              :             {
     735          504 :                 CATCH_REQUIRE(snapdev::compare_tm(ta, tb) == 1);
     736          504 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_sec, tb_sec) == 1);
     737          504 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_min, tb_min) == 1);
     738          504 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_hour, tb_hour) == 1);
     739          504 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mday, tb_mday) == 1);
     740          504 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mon, tb_mon) == 1);
     741              : 
     742          504 :                 CATCH_REQUIRE_FALSE(ta < tb);
     743          504 :                 CATCH_REQUIRE_FALSE(ta <= tb);
     744          504 :                 CATCH_REQUIRE(ta > tb);
     745          504 :                 CATCH_REQUIRE(ta >= tb);
     746          504 :                 CATCH_REQUIRE_FALSE(ta == tb);
     747          504 :                 CATCH_REQUIRE(ta != tb);
     748              :             }
     749              :             // else -- we handle equality below anyway...
     750              : 
     751              :             // verify equality
     752              :             //
     753         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta, ta) == 0);
     754         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_sec, ta_sec) == 0);
     755         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_min, ta_min) == 0);
     756         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_hour, ta_hour) == 0);
     757         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_mday, ta_mday) == 0);
     758         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_mon, ta_mon) == 0);
     759              : 
     760         1000 :             CATCH_REQUIRE_FALSE(ta < ta);
     761         1000 :             CATCH_REQUIRE(ta <= ta);
     762         1000 :             CATCH_REQUIRE_FALSE(ta > ta);
     763         1000 :             CATCH_REQUIRE(ta >= ta);
     764         1000 :             CATCH_REQUIRE(ta == ta);
     765         1000 :             CATCH_REQUIRE_FALSE(ta != ta);
     766              : 
     767         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb, tb) == 0);
     768         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_sec, tb_sec) == 0);
     769         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_min, tb_min) == 0);
     770         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_hour, tb_hour) == 0);
     771         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_mday, tb_mday) == 0);
     772         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_mon, tb_mon) == 0);
     773              : 
     774         1000 :             CATCH_REQUIRE_FALSE(tb < tb);
     775         1000 :             CATCH_REQUIRE(tb <= tb);
     776         1000 :             CATCH_REQUIRE_FALSE(tb > tb);
     777         1000 :             CATCH_REQUIRE(tb >= tb);
     778         1000 :             CATCH_REQUIRE(tb == tb);
     779         1000 :             CATCH_REQUIRE_FALSE(tb != tb);
     780              : 
     781              :             // to get full coverage, we need to also make sure the
     782              :             // seconds, minutes, hours, etc. are equal on both sides
     783              :             //
     784              :             {
     785         1000 :                 tm tb_same(tb);
     786              : 
     787         1000 :                 auto compare = [a, ta, &tb_same]()
     788              :                 {
     789         6000 :                     tm cb(tb_same);
     790         6000 :                     time_t const b_same(timegm(&cb));
     791         6000 :                     if(a < b_same)
     792              :                     {
     793         2471 :                         CATCH_REQUIRE(snapdev::compare_tm(ta, tb_same, false) == -1);
     794              : 
     795         2471 :                         CATCH_REQUIRE(ta < tb_same);
     796         2471 :                         CATCH_REQUIRE(a < tb_same);
     797         2471 :                         CATCH_REQUIRE(ta < b_same);
     798         2471 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) < tb_same);
     799         2471 :                         CATCH_REQUIRE(ta < snapdev::timespec_ex(tb_same));
     800              : 
     801         2471 :                         CATCH_REQUIRE(ta <= tb_same);
     802         2471 :                         CATCH_REQUIRE(a <= tb_same);
     803         2471 :                         CATCH_REQUIRE(ta <= b_same);
     804         2471 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) <= tb_same);
     805         2471 :                         CATCH_REQUIRE(ta <= snapdev::timespec_ex(tb_same));
     806              : 
     807         2471 :                         CATCH_REQUIRE_FALSE(ta > tb_same);
     808         2471 :                         CATCH_REQUIRE_FALSE(a > tb_same);
     809         2471 :                         CATCH_REQUIRE_FALSE(ta > b_same);
     810         2471 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) > tb_same);
     811         2471 :                         CATCH_REQUIRE_FALSE(ta > snapdev::timespec_ex(tb_same));
     812              : 
     813         2471 :                         CATCH_REQUIRE_FALSE(ta >= tb_same);
     814         2471 :                         CATCH_REQUIRE_FALSE(a >= tb_same);
     815         2471 :                         CATCH_REQUIRE_FALSE(ta >= b_same);
     816         2471 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) >= tb_same);
     817         2471 :                         CATCH_REQUIRE_FALSE(ta >= snapdev::timespec_ex(tb_same));
     818              : 
     819         2471 :                         CATCH_REQUIRE_FALSE(ta == tb_same);
     820         2471 :                         CATCH_REQUIRE_FALSE(a == tb_same);
     821         2471 :                         CATCH_REQUIRE_FALSE(ta == b_same);
     822         2471 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) == tb_same);
     823         2471 :                         CATCH_REQUIRE_FALSE(ta == snapdev::timespec_ex(tb_same));
     824              : 
     825         2471 :                         CATCH_REQUIRE(ta != tb_same);
     826         2471 :                         CATCH_REQUIRE(a != tb_same);
     827         2471 :                         CATCH_REQUIRE(ta != b_same);
     828         2471 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) != tb_same);
     829         2471 :                         CATCH_REQUIRE(ta != snapdev::timespec_ex(tb_same));
     830              :                     }
     831         3529 :                     else if(a > b_same)
     832              :                     {
     833         2515 :                         CATCH_REQUIRE(snapdev::compare_tm(ta, tb_same, false) == 1);
     834              : 
     835         2515 :                         CATCH_REQUIRE_FALSE(ta < tb_same);
     836         2515 :                         CATCH_REQUIRE_FALSE(a < tb_same);
     837         2515 :                         CATCH_REQUIRE_FALSE(ta < b_same);
     838         2515 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) < tb_same);
     839         2515 :                         CATCH_REQUIRE_FALSE(ta < snapdev::timespec_ex(tb_same));
     840              : 
     841         2515 :                         CATCH_REQUIRE_FALSE(ta <= tb_same);
     842         2515 :                         CATCH_REQUIRE_FALSE(a <= tb_same);
     843         2515 :                         CATCH_REQUIRE_FALSE(ta <= b_same);
     844         2515 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) <= tb_same);
     845         2515 :                         CATCH_REQUIRE_FALSE(ta <= snapdev::timespec_ex(tb_same));
     846              : 
     847         2515 :                         CATCH_REQUIRE(ta > tb_same);
     848         2515 :                         CATCH_REQUIRE(a > tb_same);
     849         2515 :                         CATCH_REQUIRE(ta > b_same);
     850         2515 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) > tb_same);
     851         2515 :                         CATCH_REQUIRE(ta > snapdev::timespec_ex(tb_same));
     852              : 
     853         2515 :                         CATCH_REQUIRE(ta >= tb_same);
     854         2515 :                         CATCH_REQUIRE(a >= tb_same);
     855         2515 :                         CATCH_REQUIRE(ta >= b_same);
     856         2515 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) >= tb_same);
     857         2515 :                         CATCH_REQUIRE(ta >= snapdev::timespec_ex(tb_same));
     858              : 
     859         2515 :                         CATCH_REQUIRE_FALSE(ta == tb_same);
     860         2515 :                         CATCH_REQUIRE_FALSE(a == tb_same);
     861         2515 :                         CATCH_REQUIRE_FALSE(ta == b_same);
     862         2515 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) == tb_same);
     863         2515 :                         CATCH_REQUIRE_FALSE(ta == snapdev::timespec_ex(tb_same));
     864              : 
     865         2515 :                         CATCH_REQUIRE(ta != tb_same);
     866         2515 :                         CATCH_REQUIRE(a != tb_same);
     867         2515 :                         CATCH_REQUIRE(ta != b_same);
     868         2515 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) != tb_same);
     869         2515 :                         CATCH_REQUIRE(ta != snapdev::timespec_ex(tb_same));
     870              :                     }
     871              :                     else
     872              :                     {
     873         1014 :                         CATCH_REQUIRE(snapdev::compare_tm(ta, tb_same, false) == 0);
     874              : 
     875         1014 :                         CATCH_REQUIRE_FALSE(ta < tb_same);
     876         1014 :                         CATCH_REQUIRE_FALSE(a < tb_same);
     877         1014 :                         CATCH_REQUIRE_FALSE(ta < b_same);
     878         1014 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) < tb_same);
     879         1014 :                         CATCH_REQUIRE_FALSE(ta < snapdev::timespec_ex(tb_same));
     880              : 
     881         1014 :                         CATCH_REQUIRE(ta <= tb_same);
     882         1014 :                         CATCH_REQUIRE(a <= tb_same);
     883         1014 :                         CATCH_REQUIRE(ta <= b_same);
     884         1014 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) <= tb_same);
     885         1014 :                         CATCH_REQUIRE(ta <= snapdev::timespec_ex(tb_same));
     886              : 
     887         1014 :                         CATCH_REQUIRE_FALSE(ta > tb_same);
     888         1014 :                         CATCH_REQUIRE_FALSE(a > tb_same);
     889         1014 :                         CATCH_REQUIRE_FALSE(ta > b_same);
     890         1014 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) > tb_same);
     891         1014 :                         CATCH_REQUIRE_FALSE(ta > snapdev::timespec_ex(tb_same));
     892              : 
     893         1014 :                         CATCH_REQUIRE(ta >= tb_same);
     894         1014 :                         CATCH_REQUIRE(a >= tb_same);
     895         1014 :                         CATCH_REQUIRE(ta >= b_same);
     896         1014 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) >= tb_same);
     897         1014 :                         CATCH_REQUIRE(ta >= snapdev::timespec_ex(tb_same));
     898              : 
     899         1014 :                         CATCH_REQUIRE(ta == tb_same);
     900         1014 :                         CATCH_REQUIRE(a == tb_same);
     901         1014 :                         CATCH_REQUIRE(ta == b_same);
     902         1014 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) == tb_same);
     903         1014 :                         CATCH_REQUIRE(ta == snapdev::timespec_ex(tb_same));
     904              : 
     905         1014 :                         CATCH_REQUIRE_FALSE(ta != tb_same);
     906         1014 :                         CATCH_REQUIRE_FALSE(a != tb_same);
     907         1014 :                         CATCH_REQUIRE_FALSE(ta != b_same);
     908         1014 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) != tb_same);
     909         1014 :                         CATCH_REQUIRE_FALSE(ta != snapdev::timespec_ex(tb_same));
     910              :                     }
     911         6000 :                 };
     912              : 
     913         1000 :                 tb_same.tm_sec = ta.tm_sec;
     914         1000 :                 compare();
     915         1000 :                 tb_same.tm_min = ta.tm_min;
     916         1000 :                 compare();
     917         1000 :                 tb_same.tm_hour = ta.tm_hour;
     918         1000 :                 compare();
     919         1000 :                 tb_same.tm_mday = ta.tm_mday;
     920         1000 :                 compare();
     921         1000 :                 tb_same.tm_mon = ta.tm_mon;
     922         1000 :                 compare();
     923         1000 :                 tb_same.tm_year = ta.tm_year;
     924         1000 :                 compare();
     925              :             }
     926              : 
     927              :             // same with the largest value equal first
     928              :             //
     929              :             // note that we have to make sure mday is not too large because
     930              :             // here we first change the month, then mday so it could go over
     931              :             // (as far as canonicalization is concerned and its important
     932              :             // to make sure the test is a full coverage test)
     933              :             {
     934         1000 :                 tm ta_same(ta);
     935         1000 :                 tm tb_same(tb);
     936              : 
     937         1000 :                 if(ta_same.tm_mday > 28 || tb_same.tm_mday > 28)
     938              :                 {
     939          171 :                     ta_same.tm_mday = 15;
     940          171 :                     tb_same.tm_mday = 15;
     941              :                 }
     942              : 
     943         1000 :                 time_t const a_same(timegm(&ta_same));
     944              : 
     945         1000 :                 auto compare = [a_same, ta_same, &tb_same]()
     946              :                 {
     947         6000 :                     CATCH_REQUIRE(snapdev::is_canonical_tm(ta_same));
     948         6000 :                     CATCH_REQUIRE(snapdev::is_canonical_tm(tb_same));
     949              : 
     950         6000 :                     tm cb(tb_same);
     951         6000 :                     time_t const b_same(timegm(&cb));
     952              : 
     953         6000 :                     if(a_same < b_same)
     954              :                     {
     955         2544 :                         CATCH_REQUIRE(snapdev::compare_tm(ta_same, tb_same, false) == -1);
     956              : 
     957         2544 :                         CATCH_REQUIRE(ta_same < tb_same);
     958         2544 :                         CATCH_REQUIRE(ta_same <= tb_same);
     959         2544 :                         CATCH_REQUIRE_FALSE(ta_same > tb_same);
     960         2544 :                         CATCH_REQUIRE_FALSE(ta_same >= tb_same);
     961         2544 :                         CATCH_REQUIRE_FALSE(ta_same == tb_same);
     962         2544 :                         CATCH_REQUIRE(ta_same != tb_same);
     963              :                     }
     964         3456 :                     else if(a_same > b_same)
     965              :                     {
     966         2433 :                         CATCH_REQUIRE(snapdev::compare_tm(ta_same, tb_same, false) == 1);
     967              : 
     968         2433 :                         CATCH_REQUIRE_FALSE(ta_same < tb_same);
     969         2433 :                         CATCH_REQUIRE_FALSE(ta_same <= tb_same);
     970         2433 :                         CATCH_REQUIRE(ta_same > tb_same);
     971         2433 :                         CATCH_REQUIRE(ta_same >= tb_same);
     972         2433 :                         CATCH_REQUIRE_FALSE(ta_same == tb_same);
     973         2433 :                         CATCH_REQUIRE(ta_same != tb_same);
     974              :                     }
     975              :                     else
     976              :                     {
     977         1023 :                         CATCH_REQUIRE(snapdev::compare_tm(ta_same, tb_same, false) == 0);
     978              : 
     979         1023 :                         CATCH_REQUIRE_FALSE(ta_same < tb_same);
     980         1023 :                         CATCH_REQUIRE(ta_same <= tb_same);
     981         1023 :                         CATCH_REQUIRE_FALSE(ta_same > tb_same);
     982         1023 :                         CATCH_REQUIRE(ta_same >= tb_same);
     983         1023 :                         CATCH_REQUIRE(ta_same == tb_same);
     984         1023 :                         CATCH_REQUIRE_FALSE(ta_same != tb_same);
     985              :                     }
     986         6000 :                 };
     987              : 
     988         1000 :                 tb_same.tm_year = ta_same.tm_year;
     989         1000 :                 compare();
     990         1000 :                 tb_same.tm_mon = ta_same.tm_mon;
     991         1000 :                 compare();
     992         1000 :                 tb_same.tm_mday = ta_same.tm_mday;
     993         1000 :                 compare();
     994         1000 :                 tb_same.tm_hour = ta_same.tm_hour;
     995         1000 :                 compare();
     996         1000 :                 tb_same.tm_min = ta_same.tm_min;
     997         1000 :                 compare();
     998         1000 :                 tb_same.tm_sec = ta_same.tm_sec;
     999         1000 :                 compare();
    1000              :             }
    1001              : 
    1002              :         }
    1003              :     }
    1004            2 :     CATCH_END_SECTION()
    1005            2 : }
    1006              : 
    1007              : 
    1008            7 : CATCH_TEST_CASE("timespec_ex_string", "[time][string]")
    1009              : {
    1010            7 :     CATCH_START_SECTION("timespec_ex_string: ostream")
    1011              :     {
    1012              :         // 4511.913788345
    1013            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1014            1 :         snapdev::timespec_ex b;
    1015              : 
    1016            1 :         std::stringstream ss;
    1017            1 :         ss << a;
    1018              : 
    1019            1 :         CATCH_REQUIRE(ss.str() == "4511.913788345");
    1020            1 :         CATCH_REQUIRE(a.to_timestamp() == "4511.913788345");
    1021            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "4511.913788345");
    1022              : 
    1023            1 :         b.set(ss.str());
    1024            1 :         CATCH_REQUIRE(a == b);
    1025              : 
    1026            3 :         b = "4511.913788345144763"; // ignore digits after 9 decimals
    1027            1 :         CATCH_REQUIRE(a == b);
    1028              : 
    1029            3 :         b = "4511.91378834598771"; // try again with an odd number of digits
    1030            1 :         CATCH_REQUIRE(a == b);
    1031              : 
    1032              :         // 83207.000000000
    1033            1 :         ss.str(std::string());
    1034            1 :         a.tv_sec = 83207L;
    1035            1 :         a.tv_nsec = 0;
    1036            1 :         ss << a;
    1037              : 
    1038            1 :         CATCH_REQUIRE(ss.str() == "83207");
    1039            1 :         CATCH_REQUIRE(a.to_timestamp() == "83207.000000000");
    1040            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "83207");
    1041              : 
    1042            1 :         b.set(ss.str());
    1043            1 :         CATCH_REQUIRE(a == b);
    1044            3 :         b.set("   83207.000000000  ");
    1045            1 :         CATCH_REQUIRE(a == b);
    1046            3 :         b.set("   83207.0  ");
    1047            1 :         CATCH_REQUIRE(a == b);
    1048            3 :         b.set("   83207.  ");
    1049            1 :         CATCH_REQUIRE(a == b);
    1050            3 :         b.set("   83207  ");
    1051            1 :         CATCH_REQUIRE(a == b);
    1052            3 :         b.set("   +83207s  ");
    1053            1 :         CATCH_REQUIRE(a == b);
    1054            3 :         b.set("   83207.s  ");
    1055            1 :         CATCH_REQUIRE(a == b);
    1056            3 :         b.set("   83207.0s  ");
    1057            1 :         CATCH_REQUIRE(a == b);
    1058              : 
    1059            3 :         snapdev::timespec_ex c("+83207.0s");
    1060            1 :         CATCH_REQUIRE(a == c);
    1061              : 
    1062              :         // 0.000000101
    1063            1 :         ss.str(std::string());
    1064            1 :         a.tv_sec = 0L;
    1065            1 :         a.tv_nsec = 101;
    1066            1 :         ss << a;
    1067              : 
    1068            1 :         CATCH_REQUIRE(ss.str() == "0.000000101");
    1069            1 :         CATCH_REQUIRE(a.to_timestamp() == "0.000000101");
    1070            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "0.000000101");
    1071              : 
    1072            1 :         b.set(ss.str());
    1073            1 :         CATCH_REQUIRE(a == b);
    1074              : 
    1075              :         // 1000000000.781200000
    1076            1 :         ss.str(std::string());
    1077            1 :         a.tv_sec = 1000000000L;
    1078            1 :         a.tv_nsec = 781200000;
    1079            1 :         ss << snapdev::setremovetrailingzeroes(false) << a;
    1080              : 
    1081            1 :         CATCH_REQUIRE(ss.str() == "1000000000.781200000");
    1082            1 :         CATCH_REQUIRE(a.to_timestamp() == "1000000000.781200000");
    1083            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "1000000000.7812");
    1084              : 
    1085            1 :         b.set(ss.str());
    1086            1 :         CATCH_REQUIRE(a == b);
    1087              : 
    1088              :         // -259.900000000
    1089            1 :         ss.str(std::string());
    1090            1 :         a.tv_sec = -259L;
    1091            1 :         a.tv_nsec = 900000000;
    1092            1 :         ss << a;
    1093              : 
    1094            1 :         CATCH_REQUIRE(ss.str() == "-259.900000000");
    1095            1 :         CATCH_REQUIRE(a.to_timestamp() == "-259.900000000");
    1096            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "-259.9");
    1097              : 
    1098            1 :         b.set(ss.str());
    1099            1 :         CATCH_REQUIRE(a == b);
    1100            3 :         b.set("    -259.900000000   ");
    1101            1 :         CATCH_REQUIRE(a == b);
    1102            1 :     }
    1103            7 :     CATCH_END_SECTION()
    1104              : 
    1105            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string")
    1106              :     {
    1107            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1108              : 
    1109              : //        struct tm date_and_time = {};
    1110              : //        localtime_r(&a.tv_sec, &date_and_time);
    1111              : //std::cerr << "a.tv_sec = " << a.tv_sec << " but localtime.tm_sec = " << date_and_time.tm_sec;
    1112              : //        gmtime_r(&a.tv_sec, &date_and_time);
    1113              : //std::cerr << " and gmtime.tm_sec = " << date_and_time.tm_sec << "\n";
    1114              : 
    1115              :         // for these tests, we change the timezone to UTC so localtime_r()
    1116              :         // and gmtime_r() both return the same thing
    1117              :         //
    1118            3 :         std::string out(a.to_string("%s.%N", true));
    1119            1 :         CATCH_REQUIRE(out == "4511.913788345");
    1120              : 
    1121            3 :         out = a.to_string("%s.%N", false);
    1122            1 :         CATCH_REQUIRE(out == "4511.913788345");
    1123              : 
    1124            1 :         out = a.to_string();
    1125              : //std::cerr << "seconds misplaced in: " << out << "?\n";
    1126            1 :         CATCH_REQUIRE(out.find("11.913788345") != std::string::npos);
    1127              : 
    1128            1 :         a.tv_nsec = 913'000'000;
    1129            3 :         out = a.to_string("%s.%N");
    1130            1 :         CATCH_REQUIRE(out == "4511.913000000");
    1131            3 :         out = a.to_string("%s.%EN");
    1132            1 :         CATCH_REQUIRE(out == "4511.913");
    1133              : 
    1134            1 :         a.tv_nsec = 0;
    1135            3 :         out = a.to_string("%s.%N");
    1136            1 :         CATCH_REQUIRE(out == "4511.000000000");
    1137            3 :         out = a.to_string("%s.%EN");
    1138            1 :         CATCH_REQUIRE(out == "4511.0");
    1139            1 :     }
    1140            7 :     CATCH_END_SECTION()
    1141              : 
    1142            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string when nl_langinfo(\"D_T_FMT\") returns \"\".")
    1143              :     {
    1144            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1145              : 
    1146            1 :         time_t date(4511L);
    1147            1 :         struct tm t = *gmtime(&date);
    1148            1 :         char expected[256];
    1149            1 :         strftime(expected, sizeof(expected), "%a %b %e %H:%M:%S.913788345 %Y", &t);
    1150              : 
    1151            1 :         g_state = 0;
    1152            1 :         g_error = 0;
    1153            1 :         std::string out(a.to_string<wrap_nl_langinfo>());
    1154            1 :         CATCH_REQUIRE(g_error == 0);
    1155            1 :         CATCH_REQUIRE(out == expected);
    1156              : 
    1157            1 :     }
    1158            7 :     CATCH_END_SECTION()
    1159              : 
    1160            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string when nl_langinfo(\"D_T_FMT\") returns \"%T\".")
    1161              :     {
    1162            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1163              : 
    1164            1 :         time_t date(4511L);
    1165            1 :         struct tm t = *gmtime(&date);
    1166            1 :         char expected[256];
    1167            1 :         strftime(expected, sizeof(expected), "%T.913788345", &t);
    1168              : 
    1169            1 :         g_state = 10;
    1170            1 :         g_error = 0;
    1171            1 :         std::string out(a.to_string<wrap_nl_langinfo>());
    1172            1 :         CATCH_REQUIRE(g_error == 0);
    1173            1 :         CATCH_REQUIRE(out == expected);
    1174              : 
    1175            1 :     }
    1176            7 :     CATCH_END_SECTION()
    1177              : 
    1178            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string when nl_langinfo(\"D_T_FMT\") returns \"%r %X %EX\".")
    1179              :     {
    1180            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1181              : 
    1182            1 :         time_t date(4511L);
    1183            1 :         struct tm t = *gmtime(&date);
    1184            1 :         char expected[256];
    1185            1 :         strftime(
    1186              :               expected
    1187              :             , sizeof(expected)
    1188              :             , "%I:%M:%S.913788345 %p"       // %r
    1189              :               " %H:%M:%S.913788345"         // %X
    1190              :               " %H.%M.%S.913788345"         // %EX
    1191              :             , &t);
    1192              : 
    1193            1 :         g_state = 20;
    1194            1 :         g_error = 0;
    1195            1 :         std::string out(a.to_string<wrap_nl_langinfo>());
    1196            1 :         CATCH_REQUIRE(g_error == 0);
    1197            1 :         CATCH_REQUIRE(out == expected);
    1198            1 :     }
    1199            7 :     CATCH_END_SECTION()
    1200              : 
    1201            7 :     CATCH_START_SECTION("timespec_ex_string: to and from string.")
    1202              :     {
    1203            1 :         snapdev::timespec_ex a(timespec{ rand(), rand() % 1'000'000'000 });
    1204            1 :         snapdev::timespec_ex b;
    1205              : 
    1206            3 :         std::string const format("%D %T");
    1207            1 :         std::string const s(a.to_string(format));
    1208            1 :         b.from_string(s, format);
    1209              : 
    1210              :         // we do not have support for %N just yet in the "from_string()"
    1211              :         // so here we instead just test the seconds
    1212              :         //
    1213            1 :         int const diff(labs(a.tv_sec - b.tv_sec));
    1214            1 :         bool const result(diff == 0 || diff == 3600);
    1215            1 :         CATCH_REQUIRE(result); // this is not correct, many countries are "off" the timezone by 30 or 15 min.
    1216            1 :     }
    1217            7 :     CATCH_END_SECTION()
    1218              : 
    1219            7 :     CATCH_START_SECTION("timespec_ex_string: to and from string with %N.")
    1220              :     {
    1221            1 :         snapdev::timespec_ex a(timespec{ rand(), rand() % 1'000'000'000 });
    1222            1 :         snapdev::timespec_ex b;
    1223              : 
    1224            3 :         std::string format("%D %T.%N");
    1225            1 :         std::string s(a.to_string(format));
    1226              :         //b.from_string(s, format);
    1227            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    1228              :                   b.from_string(s, format)
    1229              :                 , libexcept::fixme
    1230              :                 , Catch::Matchers::ExceptionMessage(
    1231              :                           "fixme: the from_string() %N extension is not yet implemented."));
    1232              : 
    1233              :         // once it works:
    1234              :         //
    1235              :         //CATCH_REQUIRE(a == b);
    1236            1 :     }
    1237            7 :     CATCH_END_SECTION()
    1238            7 : }
    1239              : 
    1240              : 
    1241            5 : CATCH_TEST_CASE("timespec_ex_from_string_error", "[time][string][error]")
    1242              : {
    1243            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: string does not start with a sign or digit")
    1244              :     {
    1245            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1246              :                   snapdev::timespec_ex("@34.506")
    1247              :                 , snapdev::syntax_error
    1248              :                 , Catch::Matchers::ExceptionMessage(
    1249              :                           "timespec_ex_exception: number of seconds must include at least one digit, even if '0'."));
    1250              :     }
    1251            5 :     CATCH_END_SECTION()
    1252              : 
    1253            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: seconds overflow")
    1254              :     {
    1255            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1256              :                   snapdev::timespec_ex("-9223372036854775808.506")
    1257              :                 , snapdev::overflow
    1258              :                 , Catch::Matchers::ExceptionMessage(
    1259              :                           "timespec_ex_exception: number of seconds is too large."));
    1260              :     }
    1261            5 :     CATCH_END_SECTION()
    1262              : 
    1263            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: bad unit")
    1264              :     {
    1265            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1266              :                   snapdev::timespec_ex("-2036854775808.506sec")
    1267              :                 , snapdev::syntax_error
    1268              :                 , Catch::Matchers::ExceptionMessage(
    1269              :                           "timespec_ex_exception: number include unexpected characters (ec)."));
    1270              :     }
    1271            5 :     CATCH_END_SECTION()
    1272              : 
    1273            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: bad unit")
    1274              :     {
    1275            1 :         snapdev::timespec_ex t;
    1276            1 :         t.tv_sec = 34471;
    1277            1 :         t.tv_nsec = 1000000000;
    1278            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1279              :                   t.to_string("%s.%N")
    1280              :                 , snapdev::overflow
    1281              :                 , Catch::Matchers::ExceptionMessage(
    1282              :                           "timespec_ex_exception: tv_nsec is 1 billion or more, which is invalid."));
    1283              :     }
    1284            5 :     CATCH_END_SECTION()
    1285              : 
    1286            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: output too long")
    1287              :     {
    1288            1 :         snapdev::timespec_ex t;
    1289            1 :         t.tv_sec = rand();
    1290            1 :         t.tv_nsec = rand() % 1000000000;
    1291            1 :         std::string const format(
    1292              :                 "If we use the format string as a string too, then we can end up with a string that's just way way way too long."
    1293              :                 " Our current limit is 256 characters so that limits our message. The truth is that you limit yourself"
    1294              :                 " to just a date and time string rather than a whole message in this format string."
    1295              :                 " It's probably easier to handle too... The date is: %D. The time is: %T."
    1296            3 :                 " And we also have %F, %c, %r, %X, %EX for the alternative format, etc.");
    1297            2 :         CATCH_REQUIRE_THROWS_MATCHES(
    1298              :                   t.to_string(format)
    1299              :                 , snapdev::overflow
    1300              :                 , Catch::Matchers::ExceptionMessage(
    1301              :                           "timespec_ex_exception: the specified strftime() format \""
    1302              :                         + format
    1303              :                         + "\" failed."));
    1304            1 :     }
    1305            5 :     CATCH_END_SECTION()
    1306            5 : }
    1307              : 
    1308              : 
    1309              : 
    1310              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions