LCOV - code coverage report
Current view: top level - test - test_time.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 119 119 100.0 %
Date: 2026-07-14 21:28:07 Functions: 7 7 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 31 52 59.6 %

           Branch data     Line data    Source code
       1                 :            : // Copyright 2011-2025 David Robillard <d@drobilla.net>
       2                 :            : // SPDX-License-Identifier: ISC
       3                 :            : 
       4                 :            : #undef NDEBUG
       5                 :            : 
       6                 :            : #include "double_test_utils.h"
       7                 :            : #include "int_test_utils.h"
       8                 :            : #include "time_test_utils.h"
       9                 :            : 
      10                 :            : #include <exess/exess.h>
      11                 :            : 
      12                 :            : #include <assert.h>
      13                 :            : #include <stdbool.h>
      14                 :            : #include <stdint.h>
      15                 :            : #include <string.h>
      16                 :            : 
      17                 :            : static const ExessTime nozone   = {EXESS_TIMEZONE_LOCAL, 0, 0, 0, 0};
      18                 :            : static const ExessTime utc      = {EXESS_TIMEZONE_UTC, 12, 15, 1, 250000000};
      19                 :            : static const ExessTime zoned    = {INIT_ZONE(11, 30), 23, 59, 59, 1000000};
      20                 :            : static const ExessTime high     = {INIT_ZONE(11, 30), 23, 59, 59, 999999999};
      21                 :            : static const ExessTime garbage1 = {INIT_ZONE(11, 30), 0, 0, 0, 1000000000};
      22                 :            : static const ExessTime garbage2 = {INIT_ZONE(11, 30), 0, 0, 60, 0};
      23                 :            : static const ExessTime garbage3 = {INIT_ZONE(11, 30), 0, 60, 0, 0};
      24                 :            : static const ExessTime garbage4 = {INIT_ZONE(11, 30), 24, 0, 0, 1};
      25                 :            : static const ExessTime garbage5 = {INIT_ZONE(11, 30), 24, 0, 1, 0};
      26                 :            : static const ExessTime garbage6 = {INIT_ZONE(11, 30), 24, 1, 0, 0};
      27                 :            : static const ExessTime garbage7 = {INIT_ZONE(11, 30), 25, 0, 0, 0};
      28                 :            : static const ExessTime garbage8 = {INIT_ZONE(11, 30), 24, 0, 0, 0};
      29                 :            : 
      30                 :            : static void
      31                 :         40 : check_read(const char* const string,
      32                 :            :            const ExessStatus expected_status,
      33                 :            :            const uint8_t     expected_hour,
      34                 :            :            const uint8_t     expected_minute,
      35                 :            :            const uint8_t     expected_second,
      36                 :            :            const uint32_t    expected_nanosecond,
      37                 :            :            const int8_t      expected_tz_hour,
      38                 :            :            const int8_t      expected_tz_minute,
      39                 :            :            const bool        expected_tz_is_present,
      40                 :            :            const size_t      expected_count)
      41                 :            : {
      42                 :         40 :   ExessTime value = {EXESS_TIMEZONE_LOCAL, 0, 0, 0, 0};
      43                 :            : 
      44                 :         40 :   const ExessResult r = exess_read_time(string, &value);
      45         [ -  + ]:         40 :   assert(r.status == expected_status);
      46         [ -  + ]:         40 :   assert(r.count == expected_count);
      47         [ -  + ]:         40 :   assert(value.hour == expected_hour);
      48         [ -  + ]:         40 :   assert(value.minute == expected_minute);
      49         [ -  + ]:         40 :   assert(value.second == expected_second);
      50         [ -  + ]:         40 :   assert(value.nanosecond == expected_nanosecond);
      51   [ +  +  +  +  :         40 :   assert((!expected_tz_is_present && value.zone == EXESS_TIMEZONE_LOCAL) ||
                   -  + ]
      52                 :            :          value.zone == (4 * expected_tz_hour) + (expected_tz_minute / 15));
      53                 :         40 : }
      54                 :            : 
      55                 :            : static void
      56                 :          1 : test_read_time(void)
      57                 :            : {
      58                 :            :   // No value
      59                 :          1 :   check_read("", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, 0, false, 0);
      60                 :          1 :   check_read("\t\n\r ", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, 0, false, 4);
      61                 :            : 
      62                 :            :   // Good values
      63                 :          1 :   check_read("13:20:00", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, false, 8);
      64                 :          1 :   check_read(
      65                 :            :     "13:20:30.5555", EXESS_SUCCESS, 13, 20, 30, 555500000, 0, 0, false, 13);
      66                 :          1 :   check_read("13:20:00-05:00", EXESS_SUCCESS, 13, 20, 0, 0, -5, 0, true, 14);
      67                 :          1 :   check_read("13:20:00Z", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, true, 9);
      68                 :          1 :   check_read("00:00:00", EXESS_SUCCESS, 0, 0, 0, 0, 0, 0, false, 8);
      69                 :          1 :   check_read("24:00:00", EXESS_SUCCESS, 0, 0, 0, 0, 0, 0, false, 8);
      70                 :          1 :   check_read("21:32:52", EXESS_SUCCESS, 21, 32, 52, 0, 0, 0, false, 8);
      71                 :          1 :   check_read("21:32:52+02:00", EXESS_SUCCESS, 21, 32, 52, 0, 2, 0, true, 14);
      72                 :          1 :   check_read("19:32:52Z", EXESS_SUCCESS, 19, 32, 52, 0, 0, 0, true, 9);
      73                 :          1 :   check_read("19:32:52+00:00", EXESS_SUCCESS, 19, 32, 52, 0, 0, 0, true, 14);
      74                 :          1 :   check_read(
      75                 :            :     "21:32:52.12679", EXESS_SUCCESS, 21, 32, 52, 126790000, 0, 0, false, 14);
      76                 :            : 
      77                 :            :   // Longest possible string
      78                 :          1 :   check_read("23:59:59.000000001-14:00",
      79                 :            :              EXESS_SUCCESS,
      80                 :            :              23,
      81                 :            :              59,
      82                 :            :              59,
      83                 :            :              1,
      84                 :            :              -14,
      85                 :            :              0,
      86                 :            :              true,
      87                 :            :              EXESS_MAX_TIME_LENGTH);
      88                 :            : 
      89                 :            :   // Non-canonical form
      90                 :          1 :   check_read("\t\n\r 13:20:00 ", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, false, 12);
      91                 :            : 
      92                 :            :   // Trailing whitespace
      93                 :          1 :   check_read("13:20:45 ", EXESS_SUCCESS, 13, 20, 45, 0, 0, 0, false, 8);
      94                 :          1 :   check_read("13:20:45\f", EXESS_SUCCESS, 13, 20, 45, 0, 0, 0, false, 8);
      95                 :          1 :   check_read("13:20:45\n", EXESS_SUCCESS, 13, 20, 45, 0, 0, 0, false, 8);
      96                 :          1 :   check_read("13:20:45\r", EXESS_SUCCESS, 13, 20, 45, 0, 0, 0, false, 8);
      97                 :          1 :   check_read("13:20:45\t", EXESS_SUCCESS, 13, 20, 45, 0, 0, 0, false, 8);
      98                 :          1 :   check_read("13:20:45\v", EXESS_SUCCESS, 13, 20, 45, 0, 0, 0, false, 8);
      99                 :            : 
     100                 :            :   // Trailing garbage
     101                 :          1 :   check_read("13:20:00junk", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, false, 8);
     102                 :          1 :   check_read("13:20:00Zjunk", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, true, 9);
     103                 :          1 :   check_read("13:20:00A", EXESS_SUCCESS, 13, 20, 0, 0, 0, 0, false, 8);
     104                 :            : 
     105                 :            :   // Too many nanosecond digits (expecting a timezone after 9 digits)
     106                 :          1 :   check_read("01:02:03.123456789123",
     107                 :            :              EXESS_OUT_OF_RANGE,
     108                 :            :              1,
     109                 :            :              2,
     110                 :            :              3,
     111                 :            :              123456789,
     112                 :            :              0,
     113                 :            :              0,
     114                 :            :              false,
     115                 :            :              18);
     116                 :            : 
     117                 :            :   // Garbage
     118                 :          1 :   check_read("13.20.00", EXESS_EXPECTED_COLON, 13, 0, 0, 0, 0, 0, false, 2);
     119                 :          1 :   check_read("13:20:", EXESS_EXPECTED_DIGIT, 13, 20, 0, 0, 0, 0, false, 6);
     120                 :          1 :   check_read("5:20:00", EXESS_EXPECTED_DIGIT, 5, 0, 0, 0, 0, 0, false, 1);
     121                 :          1 :   check_read("13:20", EXESS_EXPECTED_COLON, 13, 20, 0, 0, 0, 0, false, 5);
     122                 :          1 :   check_read("13:20.5:00", EXESS_EXPECTED_COLON, 13, 20, 0, 0, 0, 0, false, 5);
     123                 :          1 :   check_read("13:65:00", EXESS_OUT_OF_RANGE, 13, 65, 0, 0, 0, 0, false, 5);
     124                 :          1 :   check_read("21:32", EXESS_EXPECTED_COLON, 21, 32, 0, 0, 0, 0, false, 5);
     125                 :          1 :   check_read("25:25:10", EXESS_OUT_OF_RANGE, 25, 0, 0, 0, 0, 0, false, 2);
     126                 :          1 :   check_read("-10:00:00", EXESS_EXPECTED_DIGIT, 0, 0, 0, 0, 0, 0, false, 0);
     127                 :          1 :   check_read("1:20:10", EXESS_EXPECTED_DIGIT, 1, 0, 0, 0, 0, 0, false, 1);
     128                 :          1 :   check_read("13:20:00.", EXESS_EXPECTED_DIGIT, 13, 20, 0, 0, 0, 0, false, 9);
     129                 :          1 :   check_read("24:01:00", EXESS_OUT_OF_RANGE, 24, 1, 0, 0, 0, 0, false, 5);
     130                 :          1 :   check_read("24:00:01", EXESS_OUT_OF_RANGE, 24, 0, 1, 0, 0, 0, false, 8);
     131                 :          1 :   check_read(
     132                 :            :     "24:00:00.000000001", EXESS_OUT_OF_RANGE, 24, 0, 0, 1, 0, 0, false, 18);
     133                 :          1 :   check_read("24:00:00.000000001+02:00",
     134                 :            :              EXESS_OUT_OF_RANGE,
     135                 :            :              24,
     136                 :            :              0,
     137                 :            :              0,
     138                 :            :              1,
     139                 :            :              0,
     140                 :            :              0,
     141                 :            :              false,
     142                 :            :              18);
     143                 :          1 : }
     144                 :            : 
     145                 :            : static void
     146                 :         17 : check_write(const ExessTime   value,
     147                 :            :             const ExessStatus expected_status,
     148                 :            :             const size_t      buf_size,
     149                 :            :             const char* const expected_string)
     150                 :            : {
     151                 :         17 :   char buf[EXESS_MAX_TIME_LENGTH + 1] = {1,  2,  3,  4,  5,  6,  7,  8,  9,
     152                 :            :                                          10, 11, 12, 13, 14, 15, 16, 17, 18,
     153                 :            :                                          19, 20, 21, 22, 23, 24, 25};
     154                 :            : 
     155         [ -  + ]:         17 :   assert(buf_size <= sizeof(buf));
     156                 :            : 
     157                 :         17 :   const ExessResult r = exess_write_time(value, buf_size, buf);
     158         [ -  + ]:         17 :   assert(r.status == expected_status);
     159         [ +  + ]:         17 :   if (!r.status) {
     160         [ -  + ]:          4 :     assert(!strcmp(buf, expected_string));
     161         [ -  + ]:          4 :     assert(r.count == strlen(buf));
     162         [ -  + ]:          4 :     assert(exess_write_time(value, 0, NULL).count == r.count);
     163                 :            :   }
     164                 :         17 : }
     165                 :            : 
     166                 :            : static void
     167                 :          1 : test_write_time(void)
     168                 :            : {
     169                 :          1 :   check_write(nozone, EXESS_SUCCESS, 9, "00:00:00");
     170                 :          1 :   check_write(utc, EXESS_SUCCESS, 13, "12:15:01.25Z");
     171                 :          1 :   check_write(zoned, EXESS_SUCCESS, 19, "23:59:59.001+11:30");
     172                 :          1 :   check_write(high, EXESS_SUCCESS, 25, "23:59:59.999999999+11:30");
     173                 :            : 
     174                 :          1 :   check_write(garbage1, EXESS_BAD_VALUE, 19, "");
     175                 :          1 :   check_write(garbage2, EXESS_BAD_VALUE, 19, "");
     176                 :          1 :   check_write(garbage3, EXESS_BAD_VALUE, 19, "");
     177                 :          1 :   check_write(garbage4, EXESS_BAD_VALUE, 19, "");
     178                 :          1 :   check_write(garbage5, EXESS_BAD_VALUE, 19, "");
     179                 :          1 :   check_write(garbage6, EXESS_BAD_VALUE, 19, "");
     180                 :          1 :   check_write(garbage7, EXESS_BAD_VALUE, 19, "");
     181                 :          1 :   check_write(garbage8, EXESS_BAD_VALUE, 19, "");
     182                 :            : 
     183                 :          1 :   check_write(nozone, EXESS_NO_SPACE, 8, "");
     184                 :          1 :   check_write(utc, EXESS_NO_SPACE, 12, "");
     185                 :          1 :   check_write(zoned, EXESS_NO_SPACE, 18, "");
     186                 :          1 :   check_write(zoned, EXESS_NO_SPACE, 12, "");
     187                 :          1 :   check_write(high, EXESS_NO_SPACE, 14, "");
     188                 :            : 
     189                 :            :   // Check that nothing is written when there isn't enough space
     190                 :          1 :   char              c = 42;
     191                 :          1 :   const ExessResult r = exess_write_time(nozone, 0, &c);
     192         [ -  + ]:          1 :   assert(c == 42);
     193         [ -  + ]:          1 :   assert(r.status == EXESS_NO_SPACE);
     194         [ -  + ]:          1 :   assert(r.count == 8);
     195                 :          1 : }
     196                 :            : 
     197                 :            : static void
     198                 :       5760 : check_round_trip(const ExessTime value)
     199                 :            : {
     200                 :       5760 :   char buf[EXESS_MAX_TIME_LENGTH + 1] = {1,  2,  3,  4,  5,  6,  7,  8,  9,
     201                 :            :                                          10, 11, 12, 13, 14, 15, 16, 17, 18,
     202                 :            :                                          19, 20, 21, 22, 23, 24, 25};
     203                 :            : 
     204                 :       5760 :   ExessTime parsed_value = {EXESS_TIMEZONE_LOCAL, 0, 0, 0, 0};
     205         [ -  + ]:       5760 :   assert(!exess_write_time(value, sizeof(buf), buf).status);
     206         [ -  + ]:       5760 :   assert(!exess_read_time(buf, &parsed_value).status);
     207         [ -  + ]:       5760 :   assert(parsed_value.hour == value.hour);
     208         [ -  + ]:       5760 :   assert(parsed_value.minute == value.minute);
     209         [ -  + ]:       5760 :   assert(double_matches(parsed_value.second, value.second));
     210         [ -  + ]:       5760 :   assert(parsed_value.zone == value.zone);
     211                 :       5760 : }
     212                 :            : 
     213                 :            : static void
     214                 :          1 : test_round_trip(void)
     215                 :            : {
     216                 :          1 :   uint32_t rng = 0;
     217         [ +  + ]:         25 :   for (uint8_t h = 0; h < 24; ++h) {
     218         [ +  + ]:       1464 :     for (uint8_t m = 0; m < 60; ++m) {
     219                 :       1440 :       rng = lcg32(rng);
     220                 :            : 
     221                 :       1440 :       const uint32_t ns = rng % 1000000000U;
     222                 :            : 
     223                 :       1440 :       rng = lcg32(rng);
     224                 :            : 
     225                 :       1440 :       const uint8_t   s            = (uint8_t)(rng % 60U);
     226                 :       1440 :       const ExessTime no_zone      = {EXESS_TIMEZONE_LOCAL, h, m, s, ns};
     227                 :       1440 :       const ExessTime lowest_zone  = {INIT_ZONE(-14, 0), h, m, s, ns};
     228                 :       1440 :       const ExessTime highest_zone = {INIT_ZONE(14, 0), h, m, s, ns};
     229                 :            : 
     230                 :       1440 :       check_round_trip(no_zone);
     231                 :       1440 :       check_round_trip(lowest_zone);
     232                 :       1440 :       check_round_trip(highest_zone);
     233                 :            : 
     234                 :       1440 :       const ExessTime value = {random_timezone(&rng), h, m, s, ns};
     235                 :       1440 :       check_round_trip(value);
     236                 :            :     }
     237                 :            :   }
     238                 :          1 : }
     239                 :            : 
     240                 :            : int
     241                 :          1 : main(void)
     242                 :            : {
     243                 :          1 :   test_read_time();
     244                 :          1 :   test_write_time();
     245                 :          1 :   test_round_trip();
     246                 :            : 
     247                 :          1 :   return 0;
     248                 :            : }

Generated by: LCOV version 1.16