LCOV - code coverage report
Current view: top level - test - test_timezone.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 106 106 100.0 %
Date: 2026-07-14 21:28:07 Functions: 8 8 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 40 66 60.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 "time_test_utils.h"
       7                 :            : 
       8                 :            : #include <exess/exess.h>
       9                 :            : 
      10                 :            : #include <assert.h>
      11                 :            : #include <stdbool.h>
      12                 :            : #include <stdint.h>
      13                 :            : #include <string.h>
      14                 :            : 
      15                 :            : static const ExessTimezone missing  = {EXESS_TIMEZONE_LOCAL};
      16                 :            : static const ExessTimezone utc      = INIT_ZONE(0, 0);
      17                 :            : static const ExessTimezone plus     = INIT_ZONE(11, 30);
      18                 :            : static const ExessTimezone minus    = INIT_ZONE(-11, -30);
      19                 :            : static const ExessTimezone slight   = INIT_ZONE(0, 30);
      20                 :            : static const ExessTimezone lowest   = INIT_ZONE(-14, 0);
      21                 :            : static const ExessTimezone highest  = INIT_ZONE(14, 0);
      22                 :            : static const ExessTimezone garbage1 = INIT_ZONE(-14, -15);
      23                 :            : static const ExessTimezone garbage2 = INIT_ZONE(14, 15);
      24                 :            : static const ExessTimezone garbage3 = INIT_ZONE(-15, 0);
      25                 :            : static const ExessTimezone garbage4 = INIT_ZONE(15, 0);
      26                 :            : 
      27                 :            : static void
      28                 :          1 : test_construct(void)
      29                 :            : {
      30                 :            :   // Out of bounds
      31         [ -  + ]:          1 :   assert(exess_timezone(-15, 00) == EXESS_TIMEZONE_LOCAL);
      32         [ -  + ]:          1 :   assert(exess_timezone(15, 00) == EXESS_TIMEZONE_LOCAL);
      33                 :            : 
      34                 :            :   // Extremes
      35         [ -  + ]:          1 :   assert(exess_timezone(-14, -45) == -59);
      36         [ -  + ]:          1 :   assert(exess_timezone(14, 45) == 59);
      37                 :            : 
      38                 :            :   // Bad minutes
      39         [ -  + ]:          1 :   assert(exess_timezone(12, 20) == EXESS_TIMEZONE_LOCAL);
      40         [ -  + ]:          1 :   assert(exess_timezone(-12, -20) == EXESS_TIMEZONE_LOCAL);
      41                 :            : 
      42                 :            :   // Bad signs
      43         [ -  + ]:          1 :   assert(exess_timezone(12, -30) == EXESS_TIMEZONE_LOCAL);
      44         [ -  + ]:          1 :   assert(exess_timezone(-12, 30) == EXESS_TIMEZONE_LOCAL);
      45                 :            : 
      46                 :            :   // All valid negative cases
      47                 :          1 :   ExessTimezone z = -59;
      48         [ +  + ]:         16 :   for (int8_t h = -14; h < 1; ++h) {
      49         [ +  + ]:         75 :     for (int8_t m = -45; m < 15; m = (int8_t)(m + 15)) {
      50         [ -  + ]:         60 :       assert(exess_timezone(h, m) == z);
      51                 :         60 :       ++z;
      52                 :            :     }
      53                 :            :   }
      54                 :            : 
      55         [ -  + ]:          1 :   assert(z == 1);
      56                 :            : 
      57                 :            :   // All valid non-negative cases
      58                 :          1 :   z = 0;
      59         [ +  + ]:         16 :   for (int8_t h = 0; h < 15; ++h) {
      60         [ +  + ]:         75 :     for (int8_t m = 0; m < 60; m = (int8_t)(m + 15)) {
      61         [ -  + ]:         60 :       assert(exess_timezone(h, m) == z);
      62                 :         60 :       ++z;
      63                 :            :     }
      64                 :            :   }
      65                 :          1 : }
      66                 :            : 
      67                 :            : static void
      68                 :         26 : check_read(const char* const string,
      69                 :            :            const ExessStatus expected_status,
      70                 :            :            const int8_t      expected_hour,
      71                 :            :            const int8_t      expected_minute,
      72                 :            :            const bool        expected_is_present,
      73                 :            :            const size_t      expected_count)
      74                 :            : {
      75                 :            :   // The timezone interface is not public, so we test it via time
      76                 :         26 :   char time_string[] = "12:00:00XXXXXX";
      77                 :         26 :   strncpy(time_string + 8, string, sizeof(time_string) - 9);
      78                 :            : 
      79                 :         26 :   ExessTime         value = {EXESS_TIMEZONE_LOCAL, 0, 0, 0, 0};
      80                 :         26 :   const ExessResult r     = exess_read_time(time_string, &value);
      81                 :            : 
      82         [ -  + ]:         26 :   assert(r.status == expected_status);
      83         [ -  + ]:         26 :   assert(r.count == 8 + expected_count);
      84   [ +  +  -  +  :         26 :   assert((!expected_is_present && value.zone == EXESS_TIMEZONE_LOCAL) ||
                   -  + ]
      85                 :            :          value.zone == (4 * expected_hour) + (expected_minute / 15));
      86                 :         26 : }
      87                 :            : 
      88                 :            : static void
      89                 :          1 : test_read_timezone(void)
      90                 :            : {
      91                 :            :   // Basic values
      92                 :          1 :   check_read("Z", EXESS_SUCCESS, 0, 0, true, 1);
      93                 :          1 :   check_read("-05:00", EXESS_SUCCESS, -5, 0, true, 6);
      94                 :          1 :   check_read("+02:00", EXESS_SUCCESS, 2, 0, true, 6);
      95                 :          1 :   check_read("+00:00", EXESS_SUCCESS, 0, 0, true, 6);
      96                 :          1 :   check_read("-00:00", EXESS_SUCCESS, 0, 0, true, 6);
      97                 :            : 
      98                 :            :   // Limits
      99                 :          1 :   check_read("-14:00", EXESS_SUCCESS, -14, 0, true, 6);
     100                 :          1 :   check_read("+14:00", EXESS_SUCCESS, 14, 0, true, 6);
     101                 :          1 :   check_read("-13:45", EXESS_SUCCESS, -13, -45, true, 6);
     102                 :          1 :   check_read("+13:45", EXESS_SUCCESS, 13, 45, true, 6);
     103                 :            : 
     104                 :            :   // Out of range
     105                 :          1 :   check_read("-14:15", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
     106                 :          1 :   check_read("+14:15", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
     107                 :          1 :   check_read("-15:00", EXESS_OUT_OF_RANGE, 0, 0, false, 3);
     108                 :          1 :   check_read("+15:00", EXESS_OUT_OF_RANGE, 0, 0, false, 3);
     109                 :          1 :   check_read("-13:60", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
     110                 :          1 :   check_read("+13:60", EXESS_OUT_OF_RANGE, 0, 0, false, 6);
     111                 :            : 
     112                 :            :   // Trailing garbage
     113                 :          1 :   check_read("05:00", EXESS_SUCCESS, 0, 0, false, 0);
     114                 :            : 
     115                 :            :   // Garbage
     116                 :          1 :   check_read("+05:01", EXESS_UNSUPPORTED, 0, 0, false, 6);
     117                 :          1 :   check_read("+5:00", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
     118                 :          1 :   check_read("+5:0", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
     119                 :          1 :   check_read("+5:", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
     120                 :          1 :   check_read("+:0", EXESS_EXPECTED_DIGIT, 0, 0, false, 1);
     121                 :          1 :   check_read("+A5:00", EXESS_EXPECTED_DIGIT, 0, 0, false, 1);
     122                 :          1 :   check_read("+0A:00", EXESS_EXPECTED_DIGIT, 0, 0, false, 2);
     123                 :          1 :   check_read("+05A00", EXESS_EXPECTED_COLON, 0, 0, false, 3);
     124                 :          1 :   check_read("+05:A0", EXESS_EXPECTED_DIGIT, 0, 0, false, 4);
     125                 :          1 :   check_read("+05:0A", EXESS_EXPECTED_DIGIT, 0, 0, false, 5);
     126                 :          1 : }
     127                 :            : 
     128                 :            : static void
     129                 :         13 : check_write(const ExessTimezone value,
     130                 :            :             const ExessStatus   expected_status,
     131                 :            :             const size_t        buf_size,
     132                 :            :             const char* const   expected_string)
     133                 :            : {
     134                 :            :   // The timezone interface is not public, so we test it via time
     135                 :         13 :   char buf[EXESS_MAX_TIME_LENGTH + 1] = {1,  2,  3,  4,  5,  6,  7,  8,  9,
     136                 :            :                                          10, 11, 12, 13, 14, 15, 16, 17, 18,
     137                 :            :                                          19, 20, 21, 22, 23, 24, 25};
     138                 :            : 
     139         [ -  + ]:         13 :   assert(buf_size <= sizeof(buf) - 8);
     140                 :            : 
     141                 :         13 :   const ExessTime   time = {value, 12, 0, 0, 0};
     142                 :         13 :   const ExessResult r    = exess_write_time(time, 8 + buf_size, buf);
     143         [ -  + ]:         13 :   assert(r.status == expected_status);
     144         [ +  + ]:         13 :   if (!r.status) {
     145         [ -  + ]:          7 :     assert((r.count == strlen(buf)));
     146   [ -  +  -  -  :          7 :     assert((!buf[0] && !expected_string[0]) ||
                   -  + ]
     147                 :            :            !strcmp(buf + 8, expected_string));
     148         [ -  + ]:          7 :     assert(exess_write_time(time, 0, NULL).count == r.count);
     149                 :            :   }
     150                 :         13 : }
     151                 :            : 
     152                 :            : static void
     153                 :          1 : test_write_timezone(void)
     154                 :            : {
     155                 :          1 :   check_write(missing, EXESS_SUCCESS, 1, "");
     156                 :          1 :   check_write(utc, EXESS_SUCCESS, 2, "Z");
     157                 :          1 :   check_write(plus, EXESS_SUCCESS, 7, "+11:30");
     158                 :          1 :   check_write(minus, EXESS_SUCCESS, 7, "-11:30");
     159                 :          1 :   check_write(slight, EXESS_SUCCESS, 7, "+00:30");
     160                 :          1 :   check_write(lowest, EXESS_SUCCESS, 7, "-14:00");
     161                 :          1 :   check_write(highest, EXESS_SUCCESS, 7, "+14:00");
     162                 :            : 
     163                 :          1 :   check_write(garbage1, EXESS_BAD_VALUE, 7, "");
     164                 :          1 :   check_write(garbage2, EXESS_BAD_VALUE, 7, "");
     165                 :          1 :   check_write(garbage3, EXESS_BAD_VALUE, 7, "");
     166                 :          1 :   check_write(garbage4, EXESS_BAD_VALUE, 7, "");
     167                 :            : 
     168                 :          1 :   check_write(utc, EXESS_NO_SPACE, 1, "");
     169                 :          1 :   check_write(plus, EXESS_NO_SPACE, 6, "");
     170                 :          1 : }
     171                 :            : 
     172                 :            : static void
     173                 :        106 : check_round_trip(const ExessTimezone value)
     174                 :            : {
     175                 :        106 :   char buf[EXESS_MAX_TIME_LENGTH + 1] = {1,  2,  3,  4,  5,  6,  7,  8,  9,
     176                 :            :                                          10, 11, 12, 13, 14, 15, 16, 17, 18,
     177                 :            :                                          19, 20, 21, 22, 23, 24, 25};
     178                 :            : 
     179                 :        106 :   const ExessTime time        = {value, 12, 0, 0, 0};
     180                 :        106 :   ExessTime       parsed_time = {EXESS_TIMEZONE_LOCAL, 0, 0, 0, 0};
     181                 :            : 
     182         [ -  + ]:        106 :   assert(!exess_write_time(time, sizeof(buf), buf).status);
     183         [ -  + ]:        106 :   assert(!exess_read_time(buf, &parsed_time).status);
     184         [ -  + ]:        106 :   assert(!memcmp(&parsed_time.zone, &value, sizeof(ExessTimezone)));
     185                 :        106 : }
     186                 :            : 
     187                 :            : static void
     188                 :          1 : test_round_trip(void)
     189                 :            : {
     190                 :          1 :   check_round_trip(lowest);
     191                 :          1 :   check_round_trip(highest);
     192                 :            : 
     193         [ +  + ]:         27 :   for (int8_t h = -13; h < 13; ++h) {
     194         [ +  + ]:        130 :     for (int8_t q = 0; q < 4; ++q) {
     195                 :        104 :       const ExessTimezone value = {(int8_t)((4 * h) + q)};
     196                 :            : 
     197                 :        104 :       check_round_trip(value);
     198                 :            :     }
     199                 :            :   }
     200                 :          1 : }
     201                 :            : 
     202                 :            : int
     203                 :          1 : main(void)
     204                 :            : {
     205                 :          1 :   test_construct();
     206                 :          1 :   test_read_timezone();
     207                 :          1 :   test_write_timezone();
     208                 :          1 :   test_round_trip();
     209                 :            : 
     210                 :          1 :   return 0;
     211                 :            : }

Generated by: LCOV version 1.16