LCOV - code coverage report
Current view: top level - tests - catch_timespec_ex.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.0 % 805 797
Test Date: 2026-10-03 11:58:06 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 effects if not February
     647              :             //
     648         1000 :             if(t.tm_mon != 1)
     649              :             {
     650          937 :                 struct tm year(t);
     651          937 :                 year.tm_year += 100;
     652          937 :                 CATCH_REQUIRE(snapdev::is_canonical_tm(year));
     653              :             }
     654              : 
     655              :             // tweak wday, no effect
     656              :             //
     657         1000 :             struct tm wday(t);
     658         1000 :             wday.tm_wday += 100;
     659         1000 :             CATCH_REQUIRE(snapdev::is_canonical_tm(wday));
     660              : 
     661              :             // tweak yday, no effect
     662              :             //
     663         1000 :             struct tm yday(t);
     664         1000 :             yday.tm_yday += 100;
     665         1000 :             CATCH_REQUIRE(snapdev::is_canonical_tm(yday));
     666              :         }
     667              :     }
     668            2 :     CATCH_END_SECTION()
     669              : 
     670            2 :     CATCH_START_SECTION("timespec_ex_tm: compare tm")
     671              :     {
     672         1001 :         for(int count(0); count < 1'000; ++count)
     673              :         {
     674         1000 :             time_t const a(snapdev::random(-1'000'000LL, 2'000'000'000LL));
     675         1000 :             time_t const b(snapdev::random(-1'000'000LL, 2'000'000'000LL));
     676              : 
     677              :             // canonical values
     678              :             //
     679         1000 :             struct tm ta = {};
     680         1000 :             struct tm tb = {};
     681              : 
     682         1000 :             gmtime_r(&a, &ta);
     683         1000 :             gmtime_r(&b, &tb);
     684              : 
     685              :             // tweak second
     686              :             //
     687         1000 :             struct tm ta_sec(ta);
     688         1000 :             ta_sec.tm_sec += 100;
     689         1000 :             struct tm tb_sec(tb);
     690         1000 :             tb_sec.tm_sec += 100;
     691              : 
     692              :             // tweak minute
     693              :             //
     694         1000 :             struct tm ta_min(ta);
     695         1000 :             ta_min.tm_min += 100;
     696         1000 :             struct tm tb_min(tb);
     697         1000 :             tb_min.tm_min += 100;
     698              : 
     699              :             // tweak hour
     700              :             //
     701         1000 :             struct tm ta_hour(ta);
     702         1000 :             ta_hour.tm_hour += 100;
     703         1000 :             struct tm tb_hour(tb);
     704         1000 :             tb_hour.tm_hour += 100;
     705              : 
     706              :             // tweak mday
     707              :             //
     708         1000 :             struct tm ta_mday(ta);
     709         1000 :             ta_mday.tm_mday += 100;
     710         1000 :             struct tm tb_mday(tb);
     711         1000 :             tb_mday.tm_mday += 100;
     712              : 
     713              :             // tweak month
     714              :             //
     715         1000 :             struct tm ta_mon(ta);
     716         1000 :             ta_mon.tm_mon += 100;
     717         1000 :             struct tm tb_mon(tb);
     718         1000 :             tb_mon.tm_mon += 100;
     719              : 
     720         1000 :             if(a < b)
     721              :             {
     722          517 :                 CATCH_REQUIRE(snapdev::compare_tm(ta, tb) == -1);
     723          517 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_sec, tb_sec) == -1);
     724          517 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_min, tb_min) == -1);
     725          517 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_hour, tb_hour) == -1);
     726          517 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mday, tb_mday) == -1);
     727          517 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mon, tb_mon) == -1);
     728              : 
     729          517 :                 CATCH_REQUIRE(ta < tb);
     730          517 :                 CATCH_REQUIRE(ta <= tb);
     731          517 :                 CATCH_REQUIRE_FALSE(ta > tb);
     732          517 :                 CATCH_REQUIRE_FALSE(ta >= tb);
     733          517 :                 CATCH_REQUIRE_FALSE(ta == tb);
     734          517 :                 CATCH_REQUIRE(ta != tb);
     735              :             }
     736          483 :             else if(a > b)
     737              :             {
     738          483 :                 CATCH_REQUIRE(snapdev::compare_tm(ta, tb) == 1);
     739          483 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_sec, tb_sec) == 1);
     740          483 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_min, tb_min) == 1);
     741          483 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_hour, tb_hour) == 1);
     742          483 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mday, tb_mday) == 1);
     743          483 :                 CATCH_REQUIRE(snapdev::compare_tm(ta_mon, tb_mon) == 1);
     744              : 
     745          483 :                 CATCH_REQUIRE_FALSE(ta < tb);
     746          483 :                 CATCH_REQUIRE_FALSE(ta <= tb);
     747          483 :                 CATCH_REQUIRE(ta > tb);
     748          483 :                 CATCH_REQUIRE(ta >= tb);
     749          483 :                 CATCH_REQUIRE_FALSE(ta == tb);
     750          483 :                 CATCH_REQUIRE(ta != tb);
     751              :             }
     752              :             // else -- we handle equality below anyway...
     753              : 
     754              :             // verify equality
     755              :             //
     756         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta, ta) == 0);
     757         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_sec, ta_sec) == 0);
     758         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_min, ta_min) == 0);
     759         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_hour, ta_hour) == 0);
     760         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_mday, ta_mday) == 0);
     761         1000 :             CATCH_REQUIRE(snapdev::compare_tm(ta_mon, ta_mon) == 0);
     762              : 
     763         1000 :             CATCH_REQUIRE_FALSE(ta < ta);
     764         1000 :             CATCH_REQUIRE(ta <= ta);
     765         1000 :             CATCH_REQUIRE_FALSE(ta > ta);
     766         1000 :             CATCH_REQUIRE(ta >= ta);
     767         1000 :             CATCH_REQUIRE(ta == ta);
     768         1000 :             CATCH_REQUIRE_FALSE(ta != ta);
     769              : 
     770         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb, tb) == 0);
     771         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_sec, tb_sec) == 0);
     772         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_min, tb_min) == 0);
     773         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_hour, tb_hour) == 0);
     774         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_mday, tb_mday) == 0);
     775         1000 :             CATCH_REQUIRE(snapdev::compare_tm(tb_mon, tb_mon) == 0);
     776              : 
     777         1000 :             CATCH_REQUIRE_FALSE(tb < tb);
     778         1000 :             CATCH_REQUIRE(tb <= tb);
     779         1000 :             CATCH_REQUIRE_FALSE(tb > tb);
     780         1000 :             CATCH_REQUIRE(tb >= tb);
     781         1000 :             CATCH_REQUIRE(tb == tb);
     782         1000 :             CATCH_REQUIRE_FALSE(tb != tb);
     783              : 
     784              :             // to get full coverage, we need to also make sure the
     785              :             // seconds, minutes, hours, etc. are equal on both sides
     786              :             //
     787              :             {
     788         1000 :                 tm tb_same(tb);
     789              : 
     790         1000 :                 auto compare = [a, ta, &tb_same]()
     791              :                 {
     792         6000 :                     tm cb(tb_same);
     793         6000 :                     time_t const b_same(timegm(&cb));
     794         6000 :                     if(a < b_same)
     795              :                     {
     796         2579 :                         CATCH_REQUIRE(snapdev::compare_tm(ta, tb_same, false) == -1);
     797              : 
     798         2579 :                         CATCH_REQUIRE(ta < tb_same);
     799         2579 :                         CATCH_REQUIRE(a < tb_same);
     800         2579 :                         CATCH_REQUIRE(ta < b_same);
     801         2579 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) < tb_same);
     802         2579 :                         CATCH_REQUIRE(ta < snapdev::timespec_ex(tb_same));
     803              : 
     804         2579 :                         CATCH_REQUIRE(ta <= tb_same);
     805         2579 :                         CATCH_REQUIRE(a <= tb_same);
     806         2579 :                         CATCH_REQUIRE(ta <= b_same);
     807         2579 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) <= tb_same);
     808         2579 :                         CATCH_REQUIRE(ta <= snapdev::timespec_ex(tb_same));
     809              : 
     810         2579 :                         CATCH_REQUIRE_FALSE(ta > tb_same);
     811         2579 :                         CATCH_REQUIRE_FALSE(a > tb_same);
     812         2579 :                         CATCH_REQUIRE_FALSE(ta > b_same);
     813         2579 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) > tb_same);
     814         2579 :                         CATCH_REQUIRE_FALSE(ta > snapdev::timespec_ex(tb_same));
     815              : 
     816         2579 :                         CATCH_REQUIRE_FALSE(ta >= tb_same);
     817         2579 :                         CATCH_REQUIRE_FALSE(a >= tb_same);
     818         2579 :                         CATCH_REQUIRE_FALSE(ta >= b_same);
     819         2579 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) >= tb_same);
     820         2579 :                         CATCH_REQUIRE_FALSE(ta >= snapdev::timespec_ex(tb_same));
     821              : 
     822         2579 :                         CATCH_REQUIRE_FALSE(ta == tb_same);
     823         2579 :                         CATCH_REQUIRE_FALSE(a == tb_same);
     824         2579 :                         CATCH_REQUIRE_FALSE(ta == b_same);
     825         2579 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) == tb_same);
     826         2579 :                         CATCH_REQUIRE_FALSE(ta == snapdev::timespec_ex(tb_same));
     827              : 
     828         2579 :                         CATCH_REQUIRE(ta != tb_same);
     829         2579 :                         CATCH_REQUIRE(a != tb_same);
     830         2579 :                         CATCH_REQUIRE(ta != b_same);
     831         2579 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) != tb_same);
     832         2579 :                         CATCH_REQUIRE(ta != snapdev::timespec_ex(tb_same));
     833              :                     }
     834         3421 :                     else if(a > b_same)
     835              :                     {
     836         2413 :                         CATCH_REQUIRE(snapdev::compare_tm(ta, tb_same, false) == 1);
     837              : 
     838         2413 :                         CATCH_REQUIRE_FALSE(ta < tb_same);
     839         2413 :                         CATCH_REQUIRE_FALSE(a < tb_same);
     840         2413 :                         CATCH_REQUIRE_FALSE(ta < b_same);
     841         2413 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) < tb_same);
     842         2413 :                         CATCH_REQUIRE_FALSE(ta < snapdev::timespec_ex(tb_same));
     843              : 
     844         2413 :                         CATCH_REQUIRE_FALSE(ta <= tb_same);
     845         2413 :                         CATCH_REQUIRE_FALSE(a <= tb_same);
     846         2413 :                         CATCH_REQUIRE_FALSE(ta <= b_same);
     847         2413 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) <= tb_same);
     848         2413 :                         CATCH_REQUIRE_FALSE(ta <= snapdev::timespec_ex(tb_same));
     849              : 
     850         2413 :                         CATCH_REQUIRE(ta > tb_same);
     851         2413 :                         CATCH_REQUIRE(a > tb_same);
     852         2413 :                         CATCH_REQUIRE(ta > b_same);
     853         2413 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) > tb_same);
     854         2413 :                         CATCH_REQUIRE(ta > snapdev::timespec_ex(tb_same));
     855              : 
     856         2413 :                         CATCH_REQUIRE(ta >= tb_same);
     857         2413 :                         CATCH_REQUIRE(a >= tb_same);
     858         2413 :                         CATCH_REQUIRE(ta >= b_same);
     859         2413 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) >= tb_same);
     860         2413 :                         CATCH_REQUIRE(ta >= snapdev::timespec_ex(tb_same));
     861              : 
     862         2413 :                         CATCH_REQUIRE_FALSE(ta == tb_same);
     863         2413 :                         CATCH_REQUIRE_FALSE(a == tb_same);
     864         2413 :                         CATCH_REQUIRE_FALSE(ta == b_same);
     865         2413 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) == tb_same);
     866         2413 :                         CATCH_REQUIRE_FALSE(ta == snapdev::timespec_ex(tb_same));
     867              : 
     868         2413 :                         CATCH_REQUIRE(ta != tb_same);
     869         2413 :                         CATCH_REQUIRE(a != tb_same);
     870         2413 :                         CATCH_REQUIRE(ta != b_same);
     871         2413 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) != tb_same);
     872         2413 :                         CATCH_REQUIRE(ta != snapdev::timespec_ex(tb_same));
     873              :                     }
     874              :                     else
     875              :                     {
     876         1008 :                         CATCH_REQUIRE(snapdev::compare_tm(ta, tb_same, false) == 0);
     877              : 
     878         1008 :                         CATCH_REQUIRE_FALSE(ta < tb_same);
     879         1008 :                         CATCH_REQUIRE_FALSE(a < tb_same);
     880         1008 :                         CATCH_REQUIRE_FALSE(ta < b_same);
     881         1008 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) < tb_same);
     882         1008 :                         CATCH_REQUIRE_FALSE(ta < snapdev::timespec_ex(tb_same));
     883              : 
     884         1008 :                         CATCH_REQUIRE(ta <= tb_same);
     885         1008 :                         CATCH_REQUIRE(a <= tb_same);
     886         1008 :                         CATCH_REQUIRE(ta <= b_same);
     887         1008 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) <= tb_same);
     888         1008 :                         CATCH_REQUIRE(ta <= snapdev::timespec_ex(tb_same));
     889              : 
     890         1008 :                         CATCH_REQUIRE_FALSE(ta > tb_same);
     891         1008 :                         CATCH_REQUIRE_FALSE(a > tb_same);
     892         1008 :                         CATCH_REQUIRE_FALSE(ta > b_same);
     893         1008 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) > tb_same);
     894         1008 :                         CATCH_REQUIRE_FALSE(ta > snapdev::timespec_ex(tb_same));
     895              : 
     896         1008 :                         CATCH_REQUIRE(ta >= tb_same);
     897         1008 :                         CATCH_REQUIRE(a >= tb_same);
     898         1008 :                         CATCH_REQUIRE(ta >= b_same);
     899         1008 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) >= tb_same);
     900         1008 :                         CATCH_REQUIRE(ta >= snapdev::timespec_ex(tb_same));
     901              : 
     902         1008 :                         CATCH_REQUIRE(ta == tb_same);
     903         1008 :                         CATCH_REQUIRE(a == tb_same);
     904         1008 :                         CATCH_REQUIRE(ta == b_same);
     905         1008 :                         CATCH_REQUIRE(snapdev::timespec_ex(ta) == tb_same);
     906         1008 :                         CATCH_REQUIRE(ta == snapdev::timespec_ex(tb_same));
     907              : 
     908         1008 :                         CATCH_REQUIRE_FALSE(ta != tb_same);
     909         1008 :                         CATCH_REQUIRE_FALSE(a != tb_same);
     910         1008 :                         CATCH_REQUIRE_FALSE(ta != b_same);
     911         1008 :                         CATCH_REQUIRE_FALSE(snapdev::timespec_ex(ta) != tb_same);
     912         1008 :                         CATCH_REQUIRE_FALSE(ta != snapdev::timespec_ex(tb_same));
     913              :                     }
     914         6000 :                 };
     915              : 
     916         1000 :                 tb_same.tm_sec = ta.tm_sec;
     917         1000 :                 compare();
     918         1000 :                 tb_same.tm_min = ta.tm_min;
     919         1000 :                 compare();
     920         1000 :                 tb_same.tm_hour = ta.tm_hour;
     921         1000 :                 compare();
     922         1000 :                 tb_same.tm_mday = ta.tm_mday;
     923         1000 :                 compare();
     924         1000 :                 tb_same.tm_mon = ta.tm_mon;
     925         1000 :                 compare();
     926         1000 :                 tb_same.tm_year = ta.tm_year;
     927         1000 :                 compare();
     928              :             }
     929              : 
     930              :             // same with the largest value equal first
     931              :             //
     932              :             // note that we have to make sure mday is not too large because
     933              :             // here we first change the month, then mday so it could go over
     934              :             // (as far as canonicalization is concerned and its important
     935              :             // to make sure the test is a full coverage test)
     936              :             {
     937         1000 :                 tm ta_same(ta);
     938         1000 :                 tm tb_same(tb);
     939              : 
     940         1000 :                 if(ta_same.tm_mday > 28 || tb_same.tm_mday > 28)
     941              :                 {
     942          165 :                     ta_same.tm_mday = 15;
     943          165 :                     tb_same.tm_mday = 15;
     944              :                 }
     945              : 
     946         1000 :                 time_t const a_same(timegm(&ta_same));
     947              : 
     948         1000 :                 auto compare = [a_same, ta_same, &tb_same]()
     949              :                 {
     950         6000 :                     CATCH_REQUIRE(snapdev::is_canonical_tm(ta_same));
     951         6000 :                     CATCH_REQUIRE(snapdev::is_canonical_tm(tb_same));
     952              : 
     953         6000 :                     tm cb(tb_same);
     954         6000 :                     time_t const b_same(timegm(&cb));
     955              : 
     956         6000 :                     if(a_same < b_same)
     957              :                     {
     958         2523 :                         CATCH_REQUIRE(snapdev::compare_tm(ta_same, tb_same, false) == -1);
     959              : 
     960         2523 :                         CATCH_REQUIRE(ta_same < tb_same);
     961         2523 :                         CATCH_REQUIRE(ta_same <= tb_same);
     962         2523 :                         CATCH_REQUIRE_FALSE(ta_same > tb_same);
     963         2523 :                         CATCH_REQUIRE_FALSE(ta_same >= tb_same);
     964         2523 :                         CATCH_REQUIRE_FALSE(ta_same == tb_same);
     965         2523 :                         CATCH_REQUIRE(ta_same != tb_same);
     966              :                     }
     967         3477 :                     else if(a_same > b_same)
     968              :                     {
     969         2455 :                         CATCH_REQUIRE(snapdev::compare_tm(ta_same, tb_same, false) == 1);
     970              : 
     971         2455 :                         CATCH_REQUIRE_FALSE(ta_same < tb_same);
     972         2455 :                         CATCH_REQUIRE_FALSE(ta_same <= tb_same);
     973         2455 :                         CATCH_REQUIRE(ta_same > tb_same);
     974         2455 :                         CATCH_REQUIRE(ta_same >= tb_same);
     975         2455 :                         CATCH_REQUIRE_FALSE(ta_same == tb_same);
     976         2455 :                         CATCH_REQUIRE(ta_same != tb_same);
     977              :                     }
     978              :                     else
     979              :                     {
     980         1022 :                         CATCH_REQUIRE(snapdev::compare_tm(ta_same, tb_same, false) == 0);
     981              : 
     982         1022 :                         CATCH_REQUIRE_FALSE(ta_same < tb_same);
     983         1022 :                         CATCH_REQUIRE(ta_same <= tb_same);
     984         1022 :                         CATCH_REQUIRE_FALSE(ta_same > tb_same);
     985         1022 :                         CATCH_REQUIRE(ta_same >= tb_same);
     986         1022 :                         CATCH_REQUIRE(ta_same == tb_same);
     987         1022 :                         CATCH_REQUIRE_FALSE(ta_same != tb_same);
     988              :                     }
     989         6000 :                 };
     990              : 
     991         1000 :                 tb_same.tm_year = ta_same.tm_year;
     992         1000 :                 compare();
     993         1000 :                 tb_same.tm_mon = ta_same.tm_mon;
     994         1000 :                 compare();
     995         1000 :                 tb_same.tm_mday = ta_same.tm_mday;
     996         1000 :                 compare();
     997         1000 :                 tb_same.tm_hour = ta_same.tm_hour;
     998         1000 :                 compare();
     999         1000 :                 tb_same.tm_min = ta_same.tm_min;
    1000         1000 :                 compare();
    1001         1000 :                 tb_same.tm_sec = ta_same.tm_sec;
    1002         1000 :                 compare();
    1003              :             }
    1004              : 
    1005              :         }
    1006              :     }
    1007            2 :     CATCH_END_SECTION()
    1008            2 : }
    1009              : 
    1010              : 
    1011            7 : CATCH_TEST_CASE("timespec_ex_string", "[time][string]")
    1012              : {
    1013            7 :     CATCH_START_SECTION("timespec_ex_string: ostream")
    1014              :     {
    1015              :         // 4511.913788345
    1016            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1017            1 :         snapdev::timespec_ex b;
    1018              : 
    1019            1 :         std::stringstream ss;
    1020            1 :         ss << a;
    1021              : 
    1022            1 :         CATCH_REQUIRE(ss.str() == "4511.913788345");
    1023            1 :         CATCH_REQUIRE(a.to_timestamp() == "4511.913788345");
    1024            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "4511.913788345");
    1025              : 
    1026            1 :         b.set(ss.str());
    1027            1 :         CATCH_REQUIRE(a == b);
    1028              : 
    1029            3 :         b = "4511.913788345144763"; // ignore digits after 9 decimals
    1030            1 :         CATCH_REQUIRE(a == b);
    1031              : 
    1032            3 :         b = "4511.91378834598771"; // try again with an odd number of digits
    1033            1 :         CATCH_REQUIRE(a == b);
    1034              : 
    1035              :         // 83207.000000000
    1036            1 :         ss.str(std::string());
    1037            1 :         a.tv_sec = 83207L;
    1038            1 :         a.tv_nsec = 0;
    1039            1 :         ss << a;
    1040              : 
    1041            1 :         CATCH_REQUIRE(ss.str() == "83207");
    1042            1 :         CATCH_REQUIRE(a.to_timestamp() == "83207.000000000");
    1043            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "83207");
    1044              : 
    1045            1 :         b.set(ss.str());
    1046            1 :         CATCH_REQUIRE(a == b);
    1047            3 :         b.set("   83207.000000000  ");
    1048            1 :         CATCH_REQUIRE(a == b);
    1049            3 :         b.set("   83207.0  ");
    1050            1 :         CATCH_REQUIRE(a == b);
    1051            3 :         b.set("   83207.  ");
    1052            1 :         CATCH_REQUIRE(a == b);
    1053            3 :         b.set("   83207  ");
    1054            1 :         CATCH_REQUIRE(a == b);
    1055            3 :         b.set("   +83207s  ");
    1056            1 :         CATCH_REQUIRE(a == b);
    1057            3 :         b.set("   83207.s  ");
    1058            1 :         CATCH_REQUIRE(a == b);
    1059            3 :         b.set("   83207.0s  ");
    1060            1 :         CATCH_REQUIRE(a == b);
    1061              : 
    1062            3 :         snapdev::timespec_ex c("+83207.0s");
    1063            1 :         CATCH_REQUIRE(a == c);
    1064              : 
    1065              :         // 0.000000101
    1066            1 :         ss.str(std::string());
    1067            1 :         a.tv_sec = 0L;
    1068            1 :         a.tv_nsec = 101;
    1069            1 :         ss << a;
    1070              : 
    1071            1 :         CATCH_REQUIRE(ss.str() == "0.000000101");
    1072            1 :         CATCH_REQUIRE(a.to_timestamp() == "0.000000101");
    1073            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "0.000000101");
    1074              : 
    1075            1 :         b.set(ss.str());
    1076            1 :         CATCH_REQUIRE(a == b);
    1077              : 
    1078              :         // 1000000000.781200000
    1079            1 :         ss.str(std::string());
    1080            1 :         a.tv_sec = 1000000000L;
    1081            1 :         a.tv_nsec = 781200000;
    1082            1 :         ss << snapdev::setremovetrailingzeroes(false) << a;
    1083              : 
    1084            1 :         CATCH_REQUIRE(ss.str() == "1000000000.781200000");
    1085            1 :         CATCH_REQUIRE(a.to_timestamp() == "1000000000.781200000");
    1086            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "1000000000.7812");
    1087              : 
    1088            1 :         b.set(ss.str());
    1089            1 :         CATCH_REQUIRE(a == b);
    1090              : 
    1091              :         // -259.900000000
    1092            1 :         ss.str(std::string());
    1093            1 :         a.tv_sec = -259L;
    1094            1 :         a.tv_nsec = 900000000;
    1095            1 :         ss << a;
    1096              : 
    1097            1 :         CATCH_REQUIRE(ss.str() == "-259.900000000");
    1098            1 :         CATCH_REQUIRE(a.to_timestamp() == "-259.900000000");
    1099            1 :         CATCH_REQUIRE(a.to_timestamp(true) == "-259.9");
    1100              : 
    1101            1 :         b.set(ss.str());
    1102            1 :         CATCH_REQUIRE(a == b);
    1103            3 :         b.set("    -259.900000000   ");
    1104            1 :         CATCH_REQUIRE(a == b);
    1105            1 :     }
    1106            7 :     CATCH_END_SECTION()
    1107              : 
    1108            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string")
    1109              :     {
    1110            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1111              : 
    1112              : //        struct tm date_and_time = {};
    1113              : //        localtime_r(&a.tv_sec, &date_and_time);
    1114              : //std::cerr << "a.tv_sec = " << a.tv_sec << " but localtime.tm_sec = " << date_and_time.tm_sec;
    1115              : //        gmtime_r(&a.tv_sec, &date_and_time);
    1116              : //std::cerr << " and gmtime.tm_sec = " << date_and_time.tm_sec << "\n";
    1117              : 
    1118              :         // for these tests, we change the timezone to UTC so localtime_r()
    1119              :         // and gmtime_r() both return the same thing
    1120              :         //
    1121            3 :         std::string out(a.to_string("%s.%N", true));
    1122            1 :         CATCH_REQUIRE(out == "4511.913788345");
    1123              : 
    1124            3 :         out = a.to_string("%s.%N", false);
    1125            1 :         CATCH_REQUIRE(out == "4511.913788345");
    1126              : 
    1127            1 :         out = a.to_string();
    1128              : //std::cerr << "seconds misplaced in: " << out << "?\n";
    1129            1 :         CATCH_REQUIRE(out.find("11.913788345") != std::string::npos);
    1130              : 
    1131            1 :         a.tv_nsec = 913'000'000;
    1132            3 :         out = a.to_string("%s.%N");
    1133            1 :         CATCH_REQUIRE(out == "4511.913000000");
    1134            3 :         out = a.to_string("%s.%EN");
    1135            1 :         CATCH_REQUIRE(out == "4511.913");
    1136              : 
    1137            1 :         a.tv_nsec = 0;
    1138            3 :         out = a.to_string("%s.%N");
    1139            1 :         CATCH_REQUIRE(out == "4511.000000000");
    1140            3 :         out = a.to_string("%s.%EN");
    1141            1 :         CATCH_REQUIRE(out == "4511.0");
    1142            1 :     }
    1143            7 :     CATCH_END_SECTION()
    1144              : 
    1145            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string when nl_langinfo(\"D_T_FMT\") returns \"\".")
    1146              :     {
    1147            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1148              : 
    1149            1 :         time_t date(4511L);
    1150            1 :         struct tm t = *gmtime(&date);
    1151            1 :         char expected[256];
    1152            1 :         strftime(expected, sizeof(expected), "%a %b %e %H:%M:%S.913788345 %Y", &t);
    1153              : 
    1154            1 :         g_state = 0;
    1155            1 :         g_error = 0;
    1156            1 :         std::string out(a.to_string<wrap_nl_langinfo>());
    1157            1 :         CATCH_REQUIRE(g_error == 0);
    1158            1 :         CATCH_REQUIRE(out == expected);
    1159              : 
    1160            1 :     }
    1161            7 :     CATCH_END_SECTION()
    1162              : 
    1163            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string when nl_langinfo(\"D_T_FMT\") returns \"%T\".")
    1164              :     {
    1165            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1166              : 
    1167            1 :         time_t date(4511L);
    1168            1 :         struct tm t = *gmtime(&date);
    1169            1 :         char expected[256];
    1170            1 :         strftime(expected, sizeof(expected), "%T.913788345", &t);
    1171              : 
    1172            1 :         g_state = 10;
    1173            1 :         g_error = 0;
    1174            1 :         std::string out(a.to_string<wrap_nl_langinfo>());
    1175            1 :         CATCH_REQUIRE(g_error == 0);
    1176            1 :         CATCH_REQUIRE(out == expected);
    1177              : 
    1178            1 :     }
    1179            7 :     CATCH_END_SECTION()
    1180              : 
    1181            7 :     CATCH_START_SECTION("timespec_ex_string: convert to string when nl_langinfo(\"D_T_FMT\") returns \"%r %X %EX\".")
    1182              :     {
    1183            1 :         snapdev::timespec_ex a(timespec{ 4511L, 913'788'345L });
    1184              : 
    1185            1 :         time_t date(4511L);
    1186            1 :         struct tm t = *gmtime(&date);
    1187            1 :         char expected[256];
    1188            1 :         strftime(
    1189              :               expected
    1190              :             , sizeof(expected)
    1191              :             , "%I:%M:%S.913788345 %p"       // %r
    1192              :               " %H:%M:%S.913788345"         // %X
    1193              :               " %H.%M.%S.913788345"         // %EX
    1194              :             , &t);
    1195              : 
    1196            1 :         g_state = 20;
    1197            1 :         g_error = 0;
    1198            1 :         std::string out(a.to_string<wrap_nl_langinfo>());
    1199            1 :         CATCH_REQUIRE(g_error == 0);
    1200            1 :         CATCH_REQUIRE(out == expected);
    1201            1 :     }
    1202            7 :     CATCH_END_SECTION()
    1203              : 
    1204            7 :     CATCH_START_SECTION("timespec_ex_string: to and from string.")
    1205              :     {
    1206            1 :         snapdev::timespec_ex a(timespec{ rand(), rand() % 1'000'000'000 });
    1207            1 :         snapdev::timespec_ex b;
    1208              : 
    1209            3 :         std::string const format("%D %T");
    1210            1 :         std::string const s(a.to_string(format));
    1211            1 :         b.from_string(s, format);
    1212              : 
    1213              :         // we do not have support for %N just yet in the "from_string()"
    1214              :         // so here we instead just test the seconds
    1215              :         //
    1216            1 :         int const diff(labs(a.tv_sec - b.tv_sec));
    1217            1 :         bool const result(diff == 0 || diff == 3600);
    1218            1 :         CATCH_REQUIRE(result); // this is not correct, many countries are "off" the timezone by 30 or 15 min.
    1219            1 :     }
    1220            7 :     CATCH_END_SECTION()
    1221              : 
    1222            7 :     CATCH_START_SECTION("timespec_ex_string: to and from string with %N.")
    1223              :     {
    1224            1 :         snapdev::timespec_ex a(timespec{ rand(), rand() % 1'000'000'000 });
    1225            1 :         snapdev::timespec_ex b;
    1226              : 
    1227            3 :         std::string format("%D %T.%N");
    1228            1 :         std::string s(a.to_string(format));
    1229              :         //b.from_string(s, format);
    1230            3 :         CATCH_REQUIRE_THROWS_MATCHES(
    1231              :                   b.from_string(s, format)
    1232              :                 , libexcept::fixme
    1233              :                 , Catch::Matchers::ExceptionMessage(
    1234              :                           "fixme: the from_string() %N extension is not yet implemented."));
    1235              : 
    1236              :         // once it works:
    1237              :         //
    1238              :         //CATCH_REQUIRE(a == b);
    1239            1 :     }
    1240            7 :     CATCH_END_SECTION()
    1241            7 : }
    1242              : 
    1243              : 
    1244            5 : CATCH_TEST_CASE("timespec_ex_from_string_error", "[time][string][error]")
    1245              : {
    1246            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: string does not start with a sign or digit")
    1247              :     {
    1248            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1249              :                   snapdev::timespec_ex("@34.506")
    1250              :                 , snapdev::syntax_error
    1251              :                 , Catch::Matchers::ExceptionMessage(
    1252              :                           "timespec_ex_exception: number of seconds must include at least one digit, even if '0'."));
    1253              :     }
    1254            5 :     CATCH_END_SECTION()
    1255              : 
    1256            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: seconds overflow")
    1257              :     {
    1258            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1259              :                   snapdev::timespec_ex("-9223372036854775808.506")
    1260              :                 , snapdev::overflow
    1261              :                 , Catch::Matchers::ExceptionMessage(
    1262              :                           "timespec_ex_exception: number of seconds is too large."));
    1263              :     }
    1264            5 :     CATCH_END_SECTION()
    1265              : 
    1266            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: bad unit")
    1267              :     {
    1268            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1269              :                   snapdev::timespec_ex("-2036854775808.506sec")
    1270              :                 , snapdev::syntax_error
    1271              :                 , Catch::Matchers::ExceptionMessage(
    1272              :                           "timespec_ex_exception: number include unexpected characters (ec)."));
    1273              :     }
    1274            5 :     CATCH_END_SECTION()
    1275              : 
    1276            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: bad unit")
    1277              :     {
    1278            1 :         snapdev::timespec_ex t;
    1279            1 :         t.tv_sec = 34471;
    1280            1 :         t.tv_nsec = 1000000000;
    1281            8 :         CATCH_REQUIRE_THROWS_MATCHES(
    1282              :                   t.to_string("%s.%N")
    1283              :                 , snapdev::overflow
    1284              :                 , Catch::Matchers::ExceptionMessage(
    1285              :                           "timespec_ex_exception: tv_nsec is 1 billion or more, which is invalid."));
    1286              :     }
    1287            5 :     CATCH_END_SECTION()
    1288              : 
    1289            5 :     CATCH_START_SECTION("timespec_ex_from_string_error: output too long")
    1290              :     {
    1291            1 :         snapdev::timespec_ex t;
    1292            1 :         t.tv_sec = rand();
    1293            1 :         t.tv_nsec = rand() % 1000000000;
    1294            1 :         std::string const format(
    1295              :                 "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."
    1296              :                 " Our current limit is 256 characters so that limits our message. The truth is that you limit yourself"
    1297              :                 " to just a date and time string rather than a whole message in this format string."
    1298              :                 " It's probably easier to handle too... The date is: %D. The time is: %T."
    1299            3 :                 " And we also have %F, %c, %r, %X, %EX for the alternative format, etc.");
    1300            2 :         CATCH_REQUIRE_THROWS_MATCHES(
    1301              :                   t.to_string(format)
    1302              :                 , snapdev::overflow
    1303              :                 , Catch::Matchers::ExceptionMessage(
    1304              :                           "timespec_ex_exception: the specified strftime() format \""
    1305              :                         + format
    1306              :                         + "\" failed."));
    1307            1 :     }
    1308            5 :     CATCH_END_SECTION()
    1309            5 : }
    1310              : 
    1311              : 
    1312              : 
    1313              : // vim: ts=4 sw=4 et
        

Generated by: LCOV version 2.0-1

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